fresh-squeezy 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,7 +25,7 @@ The official [`@lemonsqueezy/lemonsqueezy.js`](https://github.com/lmsqueezy/lemo
25
25
 
26
26
  Most teams already use the official SDK or plain `fetch` for API calls. They don't need another wrapper — they need **fewer silent misconfigurations**. `fresh-squeezy` is intentionally thin:
27
27
 
28
- - 4 validators (`connection`, `store`, `product`, `webhook`) that return the same `ValidationResult` shape every time
28
+ - 7 validators (`connection`, `store`, `product`, `webhook`, `discount`, `licenseKey`, `subscriptionPlan`) that return the same `ValidationResult` shape every time
29
29
  - `doctor()` composes them into one report
30
30
  - A raw `request()` escape hatch so you never hit a wall when the platform adds something we haven't wrapped yet
31
31
  - Static, reviewed support manifest — no live scraping in runtime code
@@ -47,6 +47,9 @@ This project is MIT-licensed and deliberately scoped to stay small. The goal is
47
47
  | `store` | Wrong store ID, store owned by a different account |
48
48
  | `product` | Unpublished product, product on the wrong store, missing or all-draft variants, missing buy URL |
49
49
  | `webhook` | Webhook URL not registered, missing recommended events (order lifecycle, subscription lifecycle, refunds), missing optional newer events |
50
+ | `discount` | Draft discounts, expired or not-yet-active windows, invalid amounts (≤0 or >100%), store ownership mismatch |
51
+ | `licenseKey` | Disabled keys, expired keys, keys at activation limit, store ownership mismatch |
52
+ | `subscriptionPlan` | Non-subscription variants, invalid billing intervals, zero-price plans, inconsistent trial settings, draft variants, store ownership mismatch |
50
53
 
51
54
  Every validator returns the same `ValidationResult` — stable public contract, switchable by `issue.code`.
52
55
 
@@ -178,7 +181,10 @@ client.validateConnection()
178
181
  client.validateStore(id)
179
182
  client.validateProduct({ productId, expectedStoreId? })
180
183
  client.validateWebhook({ storeId, url })
181
- client.doctor({ storeId?, productId?, webhookUrl? })
184
+ client.validateDiscount({ storeId, discountId })
185
+ client.validateLicenseKey({ storeId, licenseKeyId })
186
+ client.validateSubscriptionPlan({ storeId, variantId })
187
+ client.doctor({ storeId?, productId?, webhookUrl?, discountId?, licenseKeyId?, variantId? })
182
188
  ```
183
189
 
184
190
  ### `ValidationResult<T>`
package/dist/cli.js CHANGED
@@ -1118,7 +1118,10 @@ async function runDoctorCommand(options) {
1118
1118
  (storeId) => client.doctor({
1119
1119
  storeId,
1120
1120
  productId: options.productId,
1121
- webhookUrl: options.webhookUrl
1121
+ webhookUrl: options.webhookUrl,
1122
+ discountId: options.discountId,
1123
+ licenseKeyId: options.licenseKeyId,
1124
+ variantId: options.variantId
1122
1125
  })
1123
1126
  )
1124
1127
  );
@@ -1333,13 +1336,16 @@ dotenv.config();
1333
1336
  var isInteractive = Boolean(process.stdin.isTTY);
1334
1337
  var program = new Command();
1335
1338
  program.name("fresh-squeezy").description("Validator-first Lemon Squeezy setup doctor").version("0.1.0");
1336
- program.command("doctor").description("Run every configured validator and emit a report").option("-m, --mode <mode>", "test or live", parseMode).option("--store-ids <ids>", "Comma-separated store IDs (e.g. 1,2,3)", parseCsv).option("--all-stores", "Run against every reachable store, no prompt").option("--product-id <id>", "Product to validate").option("--webhook-url <url>", "Webhook URL to validate").option("--json", "Emit machine-readable JSON").action(async (opts) => {
1339
+ program.command("doctor").description("Run every configured validator and emit a report").option("-m, --mode <mode>", "test or live", parseMode).option("--store-ids <ids>", "Comma-separated store IDs (e.g. 1,2,3)", parseCsv).option("--all-stores", "Run against every reachable store, no prompt").option("--product-id <id>", "Product to validate").option("--webhook-url <url>", "Webhook URL to validate").option("--discount-id <id>", "Discount to validate").option("--license-key-id <id>", "License key to validate").option("--variant-id <id>", "Subscription plan variant to validate").option("--json", "Emit machine-readable JSON").action(async (opts) => {
1337
1340
  const code = await runDoctorCommand({
1338
1341
  mode: opts.mode,
1339
1342
  storeIds: opts.storeIds,
1340
1343
  allStores: Boolean(opts.allStores),
1341
1344
  productId: opts.productId,
1342
1345
  webhookUrl: opts.webhookUrl,
1346
+ discountId: opts.discountId,
1347
+ licenseKeyId: opts.licenseKeyId,
1348
+ variantId: opts.variantId,
1343
1349
  json: Boolean(opts.json),
1344
1350
  isInteractive
1345
1351
  });
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/main.ts","../src/core/errors.ts","../src/core/config.ts","../src/core/http.ts","../src/resources/users.ts","../src/resources/stores.ts","../src/validate/rules.ts","../src/validate/connection.ts","../src/validate/store.ts","../src/resources/products.ts","../src/resources/variants.ts","../src/validate/product.ts","../src/resources/webhooks.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/cli/render.ts","../src/cli/prompts.ts","../src/cli/resolveStores.ts","../src/cli/commands/doctor.ts","../src/cli/commands/validate.ts","../src/cli/commands/init.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport dotenv from \"dotenv\";\nimport type { Mode } from \"../core/types.js\";\nimport { runDoctorCommand } from \"./commands/doctor.js\";\nimport { runValidateCommand, type ValidateTarget } from \"./commands/validate.js\";\nimport { runInitCommand } from \"./commands/init.js\";\n\n/**\n * CLI entry. Wires commander subcommands to their handlers. Each handler\n * returns an exit code; the wrapper below forwards it to `process.exit`.\n *\n * Store resolution is a CLI concern: `--store-ids 1,2,3` (CSV) for scripts,\n * `--all-stores` for \"run against every reachable store\", or interactive\n * multi-select when stdin is a TTY and neither flag is supplied. The library\n * API deliberately stays single-store-per-call.\n */\n\ndotenv.config({ path: \".env.local\" });\ndotenv.config();\n\nconst isInteractive = Boolean(process.stdin.isTTY);\n\nconst program = new Command();\n\nprogram\n .name(\"fresh-squeezy\")\n .description(\"Validator-first Lemon Squeezy setup doctor\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"doctor\")\n .description(\"Run every configured validator and emit a report\")\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--store-ids <ids>\", \"Comma-separated store IDs (e.g. 1,2,3)\", parseCsv)\n .option(\"--all-stores\", \"Run against every reachable store, no prompt\")\n .option(\"--product-id <id>\", \"Product to validate\")\n .option(\"--webhook-url <url>\", \"Webhook URL to validate\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: DoctorCliOpts) => {\n const code = await runDoctorCommand({\n mode: opts.mode,\n storeIds: opts.storeIds,\n allStores: Boolean(opts.allStores),\n productId: opts.productId,\n webhookUrl: opts.webhookUrl,\n json: Boolean(opts.json),\n isInteractive,\n });\n process.exit(code);\n });\n\nconst validate = program.command(\"validate\").description(\"Run a single validator\");\n\nvalidate\n .command(\"connection\")\n .description(\"Check that the API key authenticates\")\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"connection\", opts));\n\nvalidate\n .command(\"store\")\n .description(\"Check one or more stores are reachable\")\n .option(\"--store-ids <ids>\", \"Comma-separated store IDs\", parseCsv)\n .option(\"--all-stores\", \"Run against every reachable store\")\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"store\", opts));\n\nvalidate\n .command(\"product\")\n .description(\"Check a product is published with at least one variant\")\n .requiredOption(\"--product-id <id>\", \"Product ID to validate\")\n .option(\"--store-ids <ids>\", \"Expected owning store IDs (first is used for cross-check)\", parseCsv)\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"product\", opts));\n\nvalidate\n .command(\"webhook\")\n .description(\"Check a webhook is registered with the recommended events\")\n .requiredOption(\"--webhook-url <url>\", \"Public webhook URL\")\n .option(\"--store-ids <ids>\", \"Comma-separated store IDs\", parseCsv)\n .option(\"--all-stores\", \"Run against every reachable store\")\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"webhook\", opts));\n\nvalidate\n .command(\"discount\")\n .description(\"Check a discount code is valid and redeemable\")\n .requiredOption(\"--discount-id <id>\", \"Discount ID to validate\")\n .option(\"--store-ids <ids>\", \"Store ID for ownership check (first ID used)\", parseCsv)\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"discount\", opts));\n\nvalidate\n .command(\"license-key\")\n .description(\"Check a license key is active and not at its activation limit\")\n .requiredOption(\"--license-key-id <id>\", \"License key ID to validate\")\n .option(\"--store-ids <ids>\", \"Store ID for ownership check (first ID used)\", parseCsv)\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"license-key\", opts));\n\nvalidate\n .command(\"subscription-plan\")\n .description(\"Check a subscription plan variant has valid billing interval and trial config\")\n .requiredOption(\"--variant-id <id>\", \"Variant ID of the subscription plan\")\n .option(\"--store-ids <ids>\", \"Store ID for ownership check (first ID used)\", parseCsv)\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"subscription-plan\", opts));\n\nprogram\n .command(\"init\")\n .description(\"Interactive setup: ask for credentials, pick a store, run doctor\")\n .option(\"--env-file <path>\", \"Where to write credentials (default: .env.local)\")\n .action(async (opts: { envFile?: string }) => {\n const code = await runInitCommand({ envFile: opts.envFile });\n process.exit(code);\n });\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`fresh-squeezy: ${message}\\n`);\n process.exit(2);\n});\n\nfunction parseMode(value: string): Mode {\n if (value === \"test\" || value === \"live\") return value;\n throw new Error(`Mode must be \"test\" or \"live\", got \"${value}\"`);\n}\n\nfunction parseCsv(value: string): string[] {\n return value\n .split(\",\")\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0);\n}\n\ninterface DoctorCliOpts {\n mode?: Mode;\n storeIds?: string[];\n allStores?: boolean;\n productId?: string;\n webhookUrl?: string;\n json?: boolean;\n}\n\ninterface ValidateCliOpts {\n mode?: Mode;\n storeIds?: string[];\n allStores?: boolean;\n productId?: string;\n webhookUrl?: string;\n discountId?: string;\n licenseKeyId?: string;\n variantId?: string;\n json?: boolean;\n}\n\nasync function runValidate(target: ValidateTarget, opts: ValidateCliOpts): Promise<void> {\n const code = await runValidateCommand(target, {\n mode: opts.mode,\n storeIds: opts.storeIds,\n allStores: Boolean(opts.allStores),\n productId: opts.productId,\n webhookUrl: opts.webhookUrl,\n discountId: opts.discountId,\n licenseKeyId: opts.licenseKeyId,\n variantId: opts.variantId,\n json: Boolean(opts.json),\n isInteractive,\n });\n process.exit(code);\n}\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 JSON:API collection and return its `data` array.\n * Pagination is the caller's responsibility — use `meta.page` on the raw\n * request for multi-page traversal.\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 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\";\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 {\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 \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.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 {\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\nexport async function listStores(http: HttpClient): Promise<JsonApiResource<StoreAttributes>[]> {\n return http.getCollection<StoreAttributes>(\"/v1/stores\");\n}\n","import type { ValidationIssue, ValidationResult, ValidationSeverity } 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): 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 return result;\n}\n","import { FreshSqueezyError } from \"../core/errors.js\";\nimport type { HttpClient } from \"../core/http.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 { 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(toConnectionIssue(err));\n return buildResult(\"connection\", mode, issues);\n }\n}\n\n/**\n * Map the boolean `meta.test_mode` flag to our `Mode` type. Returns\n * `undefined` when the field is absent so older accounts / proxies that\n * don't surface it don't produce spurious MODE_MISMATCH failures.\n */\nfunction resolveActualMode(testMode: boolean | undefined): Mode | undefined {\n if (testMode === true) return \"test\";\n if (testMode === false) return \"live\";\n return undefined;\n}\n\nfunction toConnectionIssue(err: unknown): ValidationIssue {\n if (err instanceof FreshSqueezyError) {\n if (err.code === \"UNAUTHORIZED\") {\n return issue(ISSUE_CODES.AUTH_FAILED, \"error\", \"API key rejected by Lemon Squeezy.\", {\n suggestedFix: \"Regenerate the key at https://app.lemonsqueezy.com/settings/api.\",\n context: { status: err.status ?? null },\n });\n }\n if (err.code === \"NETWORK_ERROR\") {\n return issue(ISSUE_CODES.NETWORK_ERROR, \"error\", `Could not reach Lemon Squeezy: ${err.message}`);\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","import { FreshSqueezyError } from \"../core/errors.js\";\nimport type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { getStore, type StoreAttributes } from \"../resources/stores.js\";\nimport { ISSUE_CODES, buildResult, issue } 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 try {\n const store = await getStore(http, storeId);\n return buildResult(\"store\", mode, issues, store.attributes);\n } catch (err) {\n if (err instanceof FreshSqueezyError && err.status === 404) {\n issues.push(\n issue(\n ISSUE_CODES.STORE_NOT_FOUND,\n \"error\",\n `Store ${storeId} not found with the current API key.`,\n {\n suggestedFix:\n \"Check the store ID and confirm the API key belongs to the account that owns it.\",\n context: { storeId: String(storeId) },\n }\n )\n );\n return buildResult(\"store\", mode, issues);\n }\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(\"store\", mode, issues);\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"error\", message));\n return buildResult(\"store\", mode, issues);\n }\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.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 {\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\nexport async function listProducts(\n http: HttpClient,\n storeId: string | number\n): Promise<JsonApiResource<ProductAttributes>[]> {\n return http.getCollection<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\";\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 {\n product_id: number;\n name: string;\n slug: string;\n description?: string | null;\n status: \"pending\" | \"draft\" | \"published\";\n is_subscription?: boolean;\n interval?: string | null;\n interval_count?: number | null;\n has_license_keys?: boolean;\n created_at?: string;\n updated_at?: string;\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\nexport async function listVariantsForProduct(\n http: HttpClient,\n productId: string | number\n): Promise<JsonApiResource<VariantAttributes>[]> {\n return http.getCollection<VariantAttributes>(\"/v1/variants\", {\n \"filter[product_id]\": String(productId),\n });\n}\n","import { FreshSqueezyError } from \"../core/errors.js\";\nimport 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 { 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 let product;\n try {\n product = await getProduct(http, options.productId);\n } catch (err) {\n if (err instanceof FreshSqueezyError && err.status === 404) {\n issues.push(\n issue(\n ISSUE_CODES.PRODUCT_NOT_FOUND,\n \"error\",\n `Product ${options.productId} not found.`,\n {\n suggestedFix: \"Verify the product ID in the Lemon Squeezy dashboard.\",\n context: { productId: String(options.productId) },\n }\n )\n );\n return buildResult(\"product\", mode, issues);\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"error\", message));\n return buildResult(\"product\", mode, issues);\n }\n\n const attrs = product.attributes;\n\n if (options.expectedStoreId !== undefined) {\n const expected = String(options.expectedStoreId);\n const actual = String(attrs.store_id);\n if (expected !== actual) {\n issues.push(\n issue(\n ISSUE_CODES.PRODUCT_WRONG_STORE,\n \"error\",\n `Product belongs to store ${actual}, expected ${expected}.`,\n {\n suggestedFix:\n \"Either use the correct store ID or the correct product ID — IDs should not cross stores.\",\n context: { expectedStoreId: expected, actualStoreId: actual },\n }\n )\n );\n }\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}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.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 {\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\nexport async function listWebhooksForStore(\n http: HttpClient,\n storeId: string | number\n): Promise<JsonApiResource<WebhookAttributes>[]> {\n return http.getCollection<WebhookAttributes>(\"/v1/webhooks\", {\n \"filter[store_id]\": String(storeId),\n });\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 * The plan deliberately favors a static, reviewed manifest over live changelog\n * scraping (see plan.md §Non-goals). When the platform adds new resources,\n * fields, or webhook events, bump the entries below and re-snapshot the\n * changelog page with `npm run check:changelog -- --update`.\n *\n * Changelog source: https://docs.lemonsqueezy.com/api/getting-started/changelog\n * Last reviewed: 2026-04-24\n */\n\n/**\n * Resources fresh-squeezy wraps today. Anything outside this list is still\n * reachable via the raw `request()` escape hatch but has no dedicated\n * validator.\n */\nexport const SUPPORTED_RESOURCES = [\n \"users\",\n \"stores\",\n \"products\",\n \"variants\",\n \"webhooks\",\n] as const;\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 \"Not wrapped — reachable via client.request('/v1/subscriptions/:id'). Add a validator only if a real integration needs it.\",\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-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] as const;\n\nexport type RecommendedEvent = (typeof RECOMMENDED_WEBHOOK_EVENTS)[number];\nexport type OptionalEvent = (typeof OPTIONAL_WEBHOOK_EVENTS)[number];\n","import { 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(\"webhook\", mode, issues);\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"error\", message));\n return buildResult(\"webhook\", mode, issues);\n }\n\n const match = webhooks.find((webhook) => normalizeUrl(webhook.attributes.url) === normalizeUrl(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(\"webhook\", mode, issues);\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}\n\n/**\n * Compare webhook URLs without being tripped up by trailing slashes.\n * Lemon Squeezy strips trailing slashes on save; users often pass them in.\n */\nfunction normalizeUrl(raw: string): string {\n return raw.replace(/\\/+$/, \"\").toLowerCase();\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.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 {\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 { FreshSqueezyError } from \"../core/errors.js\";\nimport type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { getDiscount, type DiscountAttributes } from \"../resources/discounts.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 let discount;\n try {\n discount = await getDiscount(http, options.discountId);\n } catch (err) {\n if (err instanceof FreshSqueezyError && err.status === 404) {\n issues.push(\n issue(ISSUE_CODES.DISCOUNT_NOT_FOUND, \"error\", `Discount ${options.discountId} not found.`, {\n suggestedFix: \"Verify the discount ID in the Lemon Squeezy dashboard.\",\n context: { discountId: String(options.discountId) },\n })\n );\n return buildResult(\"discount\", mode, issues);\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"error\", message));\n return buildResult(\"discount\", mode, issues);\n }\n\n const attrs = discount.attributes;\n\n const expectedStore = String(options.storeId);\n const actualStore = String(attrs.store_id);\n if (expectedStore !== actualStore) {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_STORE_MISMATCH,\n \"error\",\n `Discount belongs to store ${actualStore}, expected ${expectedStore}.`,\n {\n suggestedFix: \"Use the correct store ID or discount ID — discounts should not cross stores.\",\n context: { expectedStoreId: expectedStore, actualStoreId: actualStore },\n }\n )\n );\n }\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}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.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 {\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 { FreshSqueezyError } from \"../core/errors.js\";\nimport type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { getLicenseKey, type LicenseKeyAttributes } from \"../resources/licenseKeys.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 let licenseKey;\n try {\n licenseKey = await getLicenseKey(http, options.licenseKeyId);\n } catch (err) {\n if (err instanceof FreshSqueezyError && err.status === 404) {\n issues.push(\n issue(\n ISSUE_CODES.LICENSE_KEY_NOT_FOUND,\n \"error\",\n `License key ${options.licenseKeyId} not found.`,\n {\n suggestedFix: \"Verify the license key ID in the Lemon Squeezy dashboard.\",\n context: { licenseKeyId: String(options.licenseKeyId) },\n }\n )\n );\n return buildResult(\"licenseKey\", mode, issues);\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"error\", message));\n return buildResult(\"licenseKey\", mode, issues);\n }\n\n const attrs = licenseKey.attributes;\n\n const expectedStore = String(options.storeId);\n const actualStore = String(attrs.store_id);\n if (expectedStore !== actualStore) {\n issues.push(\n issue(\n ISSUE_CODES.LICENSE_KEY_STORE_MISMATCH,\n \"error\",\n `License key belongs to store ${actualStore}, expected ${expectedStore}.`,\n {\n suggestedFix: \"Use the correct store ID or license key ID — keys should not cross stores.\",\n context: { expectedStoreId: expectedStore, actualStoreId: actualStore },\n }\n )\n );\n }\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}\n","import { FreshSqueezyError } from \"../core/errors.js\";\nimport 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 { 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 let variant;\n try {\n variant = await getVariant<SubscriptionVariantAttributes>(http, options.variantId);\n } catch (err) {\n if (err instanceof FreshSqueezyError && err.status === 404) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_VARIANT_NOT_FOUND,\n \"error\",\n `Variant ${options.variantId} not found.`,\n {\n suggestedFix: \"Verify the variant ID in the Lemon Squeezy dashboard.\",\n context: { variantId: String(options.variantId) },\n }\n )\n );\n return buildResult(\"subscriptionPlan\", mode, issues);\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"error\", message));\n return buildResult(\"subscriptionPlan\", mode, issues);\n }\n\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 expectedStore = String(options.storeId);\n const actualStore = String(product.attributes.store_id);\n if (expectedStore !== actualStore) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_STORE_MISMATCH,\n \"error\",\n `Subscription variant belongs to store ${actualStore} (via product ${attrs.product_id}), expected ${expectedStore}.`,\n {\n suggestedFix: \"Use the correct store ID or variant ID — plans should not cross stores.\",\n context: { expectedStoreId: expectedStore, actualStoreId: actualStore, productId: String(attrs.product_id) },\n }\n )\n );\n }\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}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { DoctorReport, Mode, ValidationResult } from \"../core/types.js\";\nimport { validateConnection } from \"./connection.js\";\nimport { validateStore } from \"./store.js\";\nimport { validateProduct } from \"./product.js\";\nimport { validateWebhook } from \"./webhook.js\";\nimport { validateDiscount } from \"./discount.js\";\nimport { validateLicenseKey } from \"./licenseKey.js\";\nimport { validateSubscriptionPlan } from \"./subscriptionPlan.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 webhookUrl?: string;\n discountId?: string | number;\n licenseKeyId?: string | number;\n variantId?: string | number;\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 */\nexport async function doctor(\n http: HttpClient,\n mode: Mode,\n options: DoctorOptions = {}\n): Promise<DoctorReport> {\n const results: ValidationResult[] = [];\n\n const connection = await validateConnection(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 validateStore(http, mode, options.storeId));\n }\n\n if (options.productId !== undefined) {\n results.push(\n await validateProduct(http, mode, {\n productId: options.productId,\n expectedStoreId: options.storeId,\n })\n );\n }\n\n if (options.storeId !== undefined && options.webhookUrl !== undefined) {\n results.push(\n await validateWebhook(http, mode, {\n storeId: options.storeId,\n url: options.webhookUrl,\n })\n );\n }\n\n if (options.storeId !== undefined && options.discountId !== undefined) {\n results.push(\n await validateDiscount(http, mode, {\n storeId: options.storeId,\n discountId: options.discountId,\n })\n );\n }\n\n if (options.storeId !== undefined && options.licenseKeyId !== undefined) {\n results.push(\n await validateLicenseKey(http, mode, {\n storeId: options.storeId,\n licenseKeyId: options.licenseKeyId,\n })\n );\n }\n\n if (options.storeId !== undefined && options.variantId !== undefined) {\n results.push(\n await validateSubscriptionPlan(http, mode, {\n storeId: options.storeId,\n variantId: options.variantId,\n })\n );\n }\n\n const ok = results.every((result) => result.ok);\n return { ok, mode, results };\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 webhookUrl: options?.webhookUrl,\n discountId: options?.discountId,\n licenseKeyId: options?.licenseKeyId,\n variantId: options?.variantId,\n }),\n };\n}\n","import chalk from \"chalk\";\nimport type { DoctorReport, ValidationIssue, ValidationResult } from \"../core/types.js\";\n\n/**\n * Human-readable pretty-printer for a validation result. Keeps color logic in\n * one place so doctor/validate commands share formatting and consumers can\n * redirect stdout without ANSI codes leaking through (chalk auto-detects TTY).\n */\nexport function renderResult(result: ValidationResult): string {\n const lines: string[] = [];\n const badge = result.ok ? chalk.green(\"PASS\") : chalk.red(\"FAIL\");\n const mode = chalk.dim(`[${result.mode}]`);\n lines.push(`${badge} ${mode} ${chalk.bold(result.name)}`);\n\n for (const issue of result.issues) {\n lines.push(` ${renderIssueLine(issue)}`);\n if (issue.suggestedFix) {\n lines.push(` ${chalk.dim(\"fix:\")} ${issue.suggestedFix}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nexport function renderReport(report: DoctorReport): string {\n const header = report.ok\n ? chalk.green.bold(\"fresh-squeezy doctor: OK\")\n : chalk.red.bold(\"fresh-squeezy doctor: FAILED\");\n const body = report.results.map(renderResult).join(\"\\n\\n\");\n return `${header} ${chalk.dim(`(mode: ${report.mode})`)}\\n\\n${body}`;\n}\n\nfunction renderIssueLine(issue: ValidationIssue): string {\n const label =\n issue.severity === \"error\"\n ? chalk.red(\"✗ error\")\n : issue.severity === \"warning\"\n ? chalk.yellow(\"! warn \")\n : chalk.blue(\"i info \");\n return `${label} ${chalk.gray(`[${issue.code}]`)} ${issue.message}`;\n}\n","import inquirer from \"inquirer\";\nimport type { Mode } from \"../core/types.js\";\n\n/**\n * Interactive prompts used by `fresh-squeezy init`.\n *\n * Isolated from the command handler so the command stays focused on the flow\n * (ask → detect → confirm → write) and prompts can be unit-tested by mocking\n * inquirer without pulling in the full commander program.\n */\n\nexport interface InitAnswers {\n apiKey: string;\n mode: Mode;\n}\n\nexport async function askForCredentials(): Promise<InitAnswers> {\n const answers = await inquirer.prompt<InitAnswers>([\n {\n type: \"password\",\n name: \"apiKey\",\n message: \"Paste your Lemon Squeezy API key:\",\n mask: \"*\",\n validate: (value: string) => (value.trim().length > 0 ? true : \"API key is required.\"),\n },\n {\n type: \"list\",\n name: \"mode\",\n message: \"Which mode does this key belong to?\",\n choices: [\n { name: \"test — sandbox / development\", value: \"test\" },\n { name: \"live — production charges\", value: \"live\" },\n ],\n default: \"test\",\n },\n ]);\n return answers;\n}\n\nexport async function pickStore(\n choices: { id: string; name: string; slug: string }[]\n): Promise<string> {\n const { storeId } = await inquirer.prompt<{ storeId: string }>([\n {\n type: \"list\",\n name: \"storeId\",\n message: \"Pick a store to validate against:\",\n choices: choices.map((entry) => ({\n name: `${entry.name} (${entry.slug}) — id ${entry.id}`,\n value: entry.id,\n })),\n },\n ]);\n return storeId;\n}\n\n/**\n * Multi-select store picker used by `doctor` and `validate` when no\n * `--store-ids` / `--all-stores` flag is supplied and stdin is a TTY.\n * The first store is pre-checked so hitting Enter without toggling still\n * picks something — callers enforce the \"at least one\" rule.\n */\nexport async function pickStores(\n choices: { id: string; name: string; slug: string }[]\n): Promise<string[]> {\n const { storeIds } = await inquirer.prompt<{ storeIds: string[] }>([\n {\n type: \"checkbox\",\n name: \"storeIds\",\n message: \"Pick one or more stores (space to toggle, enter to confirm):\",\n choices: choices.map((entry, index) => ({\n name: `${entry.name} (${entry.slug}) — id ${entry.id}`,\n value: entry.id,\n checked: index === 0,\n })),\n validate: (values: unknown) =>\n Array.isArray(values) && values.length > 0 ? true : \"Pick at least one store.\",\n },\n ]);\n return storeIds;\n}\n\nexport async function confirmWriteEnvFile(path: string): Promise<boolean> {\n const { confirm } = await inquirer.prompt<{ confirm: boolean }>([\n {\n type: \"confirm\",\n name: \"confirm\",\n message: `Write these values to ${path}?`,\n default: true,\n },\n ]);\n return confirm;\n}\n","import type { FreshSqueezyClient } from \"../createFreshSqueezy.js\";\nimport { FreshSqueezyError } from \"../core/errors.js\";\nimport { pickStores } from \"./prompts.js\";\n\n/**\n * Inputs the CLI has when it needs to figure out which stores to validate.\n * Kept flag-only + TTY — the CLI deliberately does not read\n * `LEMON_SQUEEZY_STORE_ID`. Library consumers still can (via `createFreshSqueezy`).\n */\nexport interface ResolveStoresInput {\n storeIds?: string[];\n allStores?: boolean;\n /** Whether stdin is attached to a TTY so we can prompt. */\n isInteractive: boolean;\n}\n\nexport interface ResolveStoresOutput {\n storeIds: string[];\n /**\n * True when the resolver could not pick any store AND was not asked to.\n * The caller should fall back to a connection-only run in that case.\n */\n skipped: boolean;\n}\n\n/**\n * Decide which store IDs a doctor/validate run should cover.\n *\n * Resolution order (highest → lowest):\n * 1. `--store-ids 1,2,3` flag\n * 2. `--all-stores` flag (fetch every reachable store)\n * 3. TTY + inquirer multi-select\n * 4. Non-TTY, no flag → `skipped: true`\n *\n * `skipped` lets the caller decide whether to run only connection-level checks\n * or fail loudly — different subcommands want different behavior.\n */\nexport async function resolveStores(\n client: FreshSqueezyClient,\n input: ResolveStoresInput\n): Promise<ResolveStoresOutput> {\n if (input.storeIds && input.storeIds.length > 0) {\n return { storeIds: dedupe(input.storeIds), skipped: false };\n }\n\n if (input.allStores) {\n const ids = await fetchReachableStoreIds(client);\n if (ids.length === 0) {\n throw new FreshSqueezyError({\n code: \"NO_STORES\",\n message: \"No stores reachable with this API key.\",\n });\n }\n return { storeIds: ids, skipped: false };\n }\n\n if (!input.isInteractive) {\n return { storeIds: [], skipped: true };\n }\n\n const ids = await fetchReachableStoreIds(client);\n if (ids.length === 0) {\n throw new FreshSqueezyError({\n code: \"NO_STORES\",\n message: \"No stores reachable with this API key.\",\n });\n }\n if (ids.length === 1) {\n return { storeIds: ids, skipped: false };\n }\n\n const detailed = await Promise.all(\n ids.map(async (id) => {\n const result = await client.validateStore(id);\n return {\n id,\n name: result.resource?.name ?? \"(unnamed)\",\n slug: result.resource?.slug ?? \"\",\n };\n })\n );\n\n const picked = await pickStores(detailed);\n if (picked.length === 0) {\n throw new FreshSqueezyError({\n code: \"NO_SELECTION\",\n message: \"No store selected. Pick at least one to continue.\",\n });\n }\n return { storeIds: picked, skipped: false };\n}\n\n/**\n * Fetch every store reachable with the current key. Errors from the underlying\n * request propagate — callers handle them at the command boundary.\n */\nasync function fetchReachableStoreIds(client: FreshSqueezyClient): Promise<string[]> {\n const connection = await client.validateConnection();\n if (!connection.ok) {\n const authIssue = connection.issues.find((entry) => entry.code === \"AUTH_FAILED\");\n if (authIssue) {\n throw new FreshSqueezyError({\n code: \"AUTH_FAILED\",\n message: authIssue.message,\n });\n }\n }\n return connection.resource?.storeIds ?? [];\n}\n\nfunction dedupe(values: string[]): string[] {\n return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)));\n}\n","import { createFreshSqueezy, type FreshSqueezyClient } from \"../../createFreshSqueezy.js\";\nimport { FreshSqueezyError } from \"../../core/errors.js\";\nimport type { DoctorReport, Mode } from \"../../core/types.js\";\nimport { renderReport } from \"../render.js\";\nimport { resolveStores } from \"../resolveStores.js\";\n\nexport interface DoctorCommandOptions {\n mode?: Mode;\n storeIds?: string[];\n allStores?: boolean;\n productId?: string;\n webhookUrl?: string;\n json?: boolean;\n isInteractive?: boolean;\n}\n\n/**\n * Aggregate payload emitted when `--json` is set. When a single store is\n * resolved, `reports` still contains one entry — consumers always see an\n * array so JSON parsers don't need two code paths.\n */\nexport interface DoctorJsonOutput {\n ok: boolean;\n mode: Mode;\n reports: DoctorReport[];\n}\n\n/**\n * `fresh-squeezy doctor` — run every validator across each resolved store and\n * emit one combined exit code. Store resolution (flag → --all-stores → TTY\n * prompt → connection-only) lives in resolveStores so validate commands can\n * reuse it.\n */\nexport async function runDoctorCommand(options: DoctorCommandOptions): Promise<number> {\n try {\n const client = createFreshSqueezy({ mode: options.mode });\n const resolved = await resolveStores(client, {\n storeIds: options.storeIds,\n allStores: options.allStores,\n isInteractive: options.isInteractive ?? false,\n });\n\n if (resolved.skipped) {\n return await runConnectionOnly(client, options);\n }\n\n const reports = await Promise.all(\n resolved.storeIds.map((storeId) =>\n client.doctor({\n storeId,\n productId: options.productId,\n webhookUrl: options.webhookUrl,\n })\n )\n );\n\n const ok = reports.every((report) => report.ok);\n const payload: DoctorJsonOutput = { ok, mode: client.mode, reports };\n\n if (options.json) {\n process.stdout.write(`${JSON.stringify(payload, null, 2)}\\n`);\n } else {\n for (const report of reports) {\n process.stdout.write(`${renderReport(report)}\\n\\n`);\n }\n }\n\n return ok ? 0 : 1;\n } catch (err) {\n writeFatal(err, options.json ?? false);\n return 2;\n }\n}\n\n/**\n * Fallback when no store could be resolved and we are not interactive.\n * Running connection-only gives CI a useful signal (\"key works\") without\n * silently pretending everything is fine.\n */\nasync function runConnectionOnly(\n client: FreshSqueezyClient,\n options: DoctorCommandOptions\n): Promise<number> {\n const connection = await client.validateConnection();\n const report: DoctorReport = {\n ok: connection.ok,\n mode: client.mode,\n results: [connection],\n };\n const payload: DoctorJsonOutput = { ok: connection.ok, mode: client.mode, reports: [report] };\n\n if (options.json) {\n process.stdout.write(`${JSON.stringify(payload, null, 2)}\\n`);\n } else {\n process.stderr.write(\n \"fresh-squeezy: no --store-ids or --all-stores and stdin is not a TTY; running connection-only.\\n\"\n );\n process.stdout.write(`${renderReport(report)}\\n`);\n }\n return connection.ok ? 0 : 1;\n}\n\nfunction writeFatal(err: unknown, asJson: boolean): void {\n if (asJson) {\n const payload =\n err instanceof FreshSqueezyError\n ? { ok: false, error: { code: err.code, message: err.message, status: err.status ?? null } }\n : { ok: false, error: { code: \"UNKNOWN\", message: err instanceof Error ? err.message : String(err) } };\n process.stderr.write(`${JSON.stringify(payload)}\\n`);\n return;\n }\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`fresh-squeezy: ${message}\\n`);\n}\n","import { createFreshSqueezy, type FreshSqueezyClient } from \"../../createFreshSqueezy.js\";\nimport { FreshSqueezyError } from \"../../core/errors.js\";\nimport type { Mode, ValidationResult } from \"../../core/types.js\";\nimport { renderResult } from \"../render.js\";\nimport { resolveStores } from \"../resolveStores.js\";\n\nexport interface ValidateCommandOptions {\n mode?: Mode;\n storeIds?: string[];\n allStores?: boolean;\n productId?: string;\n webhookUrl?: string;\n discountId?: string;\n licenseKeyId?: string;\n variantId?: string;\n json?: boolean;\n isInteractive?: boolean;\n}\n\nexport type ValidateTarget = \"connection\" | \"store\" | \"product\" | \"webhook\" | \"discount\" | \"license-key\" | \"subscription-plan\";\n\n/**\n * `fresh-squeezy validate <target>` — run one validator. Store-scoped targets\n * reuse the same store resolution the `doctor` command uses, so a single\n * `--store-ids` flag or interactive pick works across every subcommand.\n *\n * Connection runs once (no stores needed). Store/webhook loop per resolved\n * store. Product runs once because a product ID identifies a single resource;\n * the `--store-ids` value (if any) is used for the cross-store ownership check.\n */\nexport async function runValidateCommand(\n target: ValidateTarget,\n options: ValidateCommandOptions\n): Promise<number> {\n try {\n const client = createFreshSqueezy({ mode: options.mode });\n\n if (target === \"connection\") {\n return emit(await client.validateConnection(), options);\n }\n\n if (target === \"product\") {\n return emit(await runProduct(client, options), options);\n }\n\n if (target === \"discount\") {\n return emit(await runDiscount(client, options), options);\n }\n\n if (target === \"license-key\") {\n return emit(await runLicenseKey(client, options), options);\n }\n\n if (target === \"subscription-plan\") {\n return emit(await runSubscriptionPlan(client, options), options);\n }\n\n const storeIds = await resolveStoresForTarget(client, target, options);\n const results = await runPerStore(client, target, storeIds, options);\n\n if (options.json) {\n process.stdout.write(`${JSON.stringify(results, null, 2)}\\n`);\n } else {\n for (const result of results) {\n process.stdout.write(`${renderResult(result)}\\n\\n`);\n }\n }\n\n return results.every((result) => result.ok) ? 0 : 1;\n } catch (err) {\n const message =\n err instanceof FreshSqueezyError\n ? err.message\n : err instanceof Error\n ? err.message\n : String(err);\n process.stderr.write(`fresh-squeezy: ${message}\\n`);\n return 2;\n }\n}\n\nasync function resolveStoresForTarget(\n client: FreshSqueezyClient,\n target: ValidateTarget,\n options: ValidateCommandOptions\n): Promise<string[]> {\n const resolved = await resolveStores(client, {\n storeIds: options.storeIds,\n allStores: options.allStores,\n isInteractive: options.isInteractive ?? false,\n });\n if (resolved.skipped) {\n throw new FreshSqueezyError({\n code: \"MISSING_ARG\",\n message: `--store-ids or --all-stores is required for \\`validate ${target}\\` in non-interactive mode.`,\n });\n }\n return resolved.storeIds;\n}\n\nasync function runPerStore(\n client: FreshSqueezyClient,\n target: ValidateTarget,\n storeIds: string[],\n options: ValidateCommandOptions\n): Promise<ValidationResult[]> {\n if (target === \"store\") {\n return Promise.all(storeIds.map((id) => client.validateStore(id)));\n }\n if (target === \"webhook\") {\n const url = required(options.webhookUrl, \"--webhook-url is required for `validate webhook`.\");\n return Promise.all(\n storeIds.map((storeId) => client.validateWebhook({ storeId, url }))\n );\n }\n return [];\n}\n\nasync function runProduct(\n client: FreshSqueezyClient,\n options: ValidateCommandOptions\n): Promise<ValidationResult> {\n const productId = required(options.productId, \"--product-id is required for `validate product`.\");\n const expected = options.storeIds?.[0];\n return client.validateProduct({\n productId,\n expectedStoreId: expected,\n });\n}\n\nasync function runDiscount(\n client: FreshSqueezyClient,\n options: ValidateCommandOptions\n): Promise<ValidationResult> {\n const discountId = required(options.discountId, \"--discount-id is required for `validate discount`.\");\n const storeId = required(options.storeIds?.[0], \"--store-ids is required for `validate discount`.\");\n return client.validateDiscount({ storeId, discountId });\n}\n\nasync function runLicenseKey(\n client: FreshSqueezyClient,\n options: ValidateCommandOptions\n): Promise<ValidationResult> {\n const licenseKeyId = required(options.licenseKeyId, \"--license-key-id is required for `validate license-key`.\");\n const storeId = required(options.storeIds?.[0], \"--store-ids is required for `validate license-key`.\");\n return client.validateLicenseKey({ storeId, licenseKeyId });\n}\n\nasync function runSubscriptionPlan(\n client: FreshSqueezyClient,\n options: ValidateCommandOptions\n): Promise<ValidationResult> {\n const variantId = required(options.variantId, \"--variant-id is required for `validate subscription-plan`.\");\n const storeId = required(options.storeIds?.[0], \"--store-ids is required for `validate subscription-plan`.\");\n return client.validateSubscriptionPlan({ storeId, variantId });\n}\n\nfunction emit(result: ValidationResult, options: ValidateCommandOptions): number {\n if (options.json) {\n process.stdout.write(`${JSON.stringify(result, null, 2)}\\n`);\n } else {\n process.stdout.write(`${renderResult(result)}\\n`);\n }\n return result.ok ? 0 : 1;\n}\n\nfunction required<T>(value: T | undefined, message: string): T {\n if (value === undefined || value === null || value === \"\") {\n throw new FreshSqueezyError({ code: \"MISSING_ARG\", message });\n }\n return value;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport chalk from \"chalk\";\nimport { createFreshSqueezy } from \"../../createFreshSqueezy.js\";\nimport { ENV_KEYS } from \"../../core/config.js\";\nimport { renderReport } from \"../render.js\";\nimport { askForCredentials, confirmWriteEnvFile, pickStore } from \"../prompts.js\";\n\nexport interface InitCommandOptions {\n envFile?: string;\n}\n\n/**\n * `fresh-squeezy init` — interactive onboarding. Walks the user through the\n * fastest path from \"I have an API key\" to \"my integration is verified\":\n *\n * 1. Ask for API key + mode.\n * 2. List reachable stores via `/v1/stores`, let the user pick one.\n * 3. Optionally persist credentials to `.env.local`.\n * 4. Run `doctor()` against the chosen config and print the report.\n *\n * Returns an exit code so the CLI wrapper can forward it to `process.exit`.\n */\nexport async function runInitCommand(options: InitCommandOptions = {}): Promise<number> {\n const answers = await askForCredentials();\n const client = createFreshSqueezy({ apiKey: answers.apiKey, mode: answers.mode });\n\n const connection = await client.validateConnection();\n if (!connection.ok) {\n process.stdout.write(`${renderReport({ ok: false, mode: answers.mode, results: [connection] })}\\n`);\n return 1;\n }\n\n const storeIds = connection.resource?.storeIds ?? [];\n if (storeIds.length === 0) {\n process.stdout.write(\n chalk.yellow(\"No stores reachable with this key. Create a store in Lemon Squeezy and retry.\\n\")\n );\n return 1;\n }\n\n const stores = await Promise.all(storeIds.map((id) => client.validateStore(id)));\n const pickable = stores\n .filter((entry) => entry.ok && entry.resource)\n .map((entry, index) => ({\n id: storeIds[index] ?? \"\",\n name: entry.resource?.name ?? \"(unnamed)\",\n slug: entry.resource?.slug ?? \"\",\n }))\n .filter((entry) => entry.id !== \"\");\n\n const storeId = await pickStore(pickable);\n\n const envPath = path.resolve(process.cwd(), options.envFile ?? \".env.local\");\n const shouldWrite = await confirmWriteEnvFile(envPath);\n if (shouldWrite) {\n await writeEnvFile(envPath, { apiKey: answers.apiKey, mode: answers.mode, storeId });\n process.stdout.write(chalk.green(`Wrote ${envPath}\\n`));\n }\n\n process.stdout.write(chalk.dim(\"\\nRunning doctor...\\n\\n\"));\n const report = await client.doctor({ storeId });\n process.stdout.write(`${renderReport(report)}\\n`);\n\n return report.ok ? 0 : 1;\n}\n\nasync function writeEnvFile(\n envPath: string,\n values: { apiKey: string; mode: string; storeId: string }\n): Promise<void> {\n const lines = [\n `${ENV_KEYS.apiKey}=${values.apiKey}`,\n `${ENV_KEYS.storeId}=${values.storeId}`,\n `${ENV_KEYS.mode}=${values.mode}`,\n \"\",\n ];\n await fs.writeFile(envPath, lines.join(\"\\n\"), { encoding: \"utf8\" });\n}\n"],"mappings":"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","names":["path","issue","path","ids","chalk","chalk"]}
1
+ {"version":3,"sources":["../src/cli/main.ts","../src/core/errors.ts","../src/core/config.ts","../src/core/http.ts","../src/resources/users.ts","../src/resources/stores.ts","../src/validate/rules.ts","../src/validate/connection.ts","../src/validate/store.ts","../src/resources/products.ts","../src/resources/variants.ts","../src/validate/product.ts","../src/resources/webhooks.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/cli/render.ts","../src/cli/prompts.ts","../src/cli/resolveStores.ts","../src/cli/commands/doctor.ts","../src/cli/commands/validate.ts","../src/cli/commands/init.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport dotenv from \"dotenv\";\nimport type { Mode } from \"../core/types.js\";\nimport { runDoctorCommand } from \"./commands/doctor.js\";\nimport { runValidateCommand, type ValidateTarget } from \"./commands/validate.js\";\nimport { runInitCommand } from \"./commands/init.js\";\n\n/**\n * CLI entry. Wires commander subcommands to their handlers. Each handler\n * returns an exit code; the wrapper below forwards it to `process.exit`.\n *\n * Store resolution is a CLI concern: `--store-ids 1,2,3` (CSV) for scripts,\n * `--all-stores` for \"run against every reachable store\", or interactive\n * multi-select when stdin is a TTY and neither flag is supplied. The library\n * API deliberately stays single-store-per-call.\n */\n\ndotenv.config({ path: \".env.local\" });\ndotenv.config();\n\nconst isInteractive = Boolean(process.stdin.isTTY);\n\nconst program = new Command();\n\nprogram\n .name(\"fresh-squeezy\")\n .description(\"Validator-first Lemon Squeezy setup doctor\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"doctor\")\n .description(\"Run every configured validator and emit a report\")\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--store-ids <ids>\", \"Comma-separated store IDs (e.g. 1,2,3)\", parseCsv)\n .option(\"--all-stores\", \"Run against every reachable store, no prompt\")\n .option(\"--product-id <id>\", \"Product to validate\")\n .option(\"--webhook-url <url>\", \"Webhook URL to validate\")\n .option(\"--discount-id <id>\", \"Discount to validate\")\n .option(\"--license-key-id <id>\", \"License key to validate\")\n .option(\"--variant-id <id>\", \"Subscription plan variant to validate\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: DoctorCliOpts) => {\n const code = await runDoctorCommand({\n mode: opts.mode,\n storeIds: opts.storeIds,\n allStores: Boolean(opts.allStores),\n productId: opts.productId,\n webhookUrl: opts.webhookUrl,\n discountId: opts.discountId,\n licenseKeyId: opts.licenseKeyId,\n variantId: opts.variantId,\n json: Boolean(opts.json),\n isInteractive,\n });\n process.exit(code);\n });\n\nconst validate = program.command(\"validate\").description(\"Run a single validator\");\n\nvalidate\n .command(\"connection\")\n .description(\"Check that the API key authenticates\")\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"connection\", opts));\n\nvalidate\n .command(\"store\")\n .description(\"Check one or more stores are reachable\")\n .option(\"--store-ids <ids>\", \"Comma-separated store IDs\", parseCsv)\n .option(\"--all-stores\", \"Run against every reachable store\")\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"store\", opts));\n\nvalidate\n .command(\"product\")\n .description(\"Check a product is published with at least one variant\")\n .requiredOption(\"--product-id <id>\", \"Product ID to validate\")\n .option(\"--store-ids <ids>\", \"Expected owning store IDs (first is used for cross-check)\", parseCsv)\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"product\", opts));\n\nvalidate\n .command(\"webhook\")\n .description(\"Check a webhook is registered with the recommended events\")\n .requiredOption(\"--webhook-url <url>\", \"Public webhook URL\")\n .option(\"--store-ids <ids>\", \"Comma-separated store IDs\", parseCsv)\n .option(\"--all-stores\", \"Run against every reachable store\")\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"webhook\", opts));\n\nvalidate\n .command(\"discount\")\n .description(\"Check a discount code is valid and redeemable\")\n .requiredOption(\"--discount-id <id>\", \"Discount ID to validate\")\n .option(\"--store-ids <ids>\", \"Store ID for ownership check (first ID used)\", parseCsv)\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"discount\", opts));\n\nvalidate\n .command(\"license-key\")\n .description(\"Check a license key is active and not at its activation limit\")\n .requiredOption(\"--license-key-id <id>\", \"License key ID to validate\")\n .option(\"--store-ids <ids>\", \"Store ID for ownership check (first ID used)\", parseCsv)\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"license-key\", opts));\n\nvalidate\n .command(\"subscription-plan\")\n .description(\"Check a subscription plan variant has valid billing interval and trial config\")\n .requiredOption(\"--variant-id <id>\", \"Variant ID of the subscription plan\")\n .option(\"--store-ids <ids>\", \"Store ID for ownership check (first ID used)\", parseCsv)\n .option(\"-m, --mode <mode>\", \"test or live\", parseMode)\n .option(\"--json\", \"Emit machine-readable JSON\")\n .action(async (opts: ValidateCliOpts) => runValidate(\"subscription-plan\", opts));\n\nprogram\n .command(\"init\")\n .description(\"Interactive setup: ask for credentials, pick a store, run doctor\")\n .option(\"--env-file <path>\", \"Where to write credentials (default: .env.local)\")\n .action(async (opts: { envFile?: string }) => {\n const code = await runInitCommand({ envFile: opts.envFile });\n process.exit(code);\n });\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`fresh-squeezy: ${message}\\n`);\n process.exit(2);\n});\n\nfunction parseMode(value: string): Mode {\n if (value === \"test\" || value === \"live\") return value;\n throw new Error(`Mode must be \"test\" or \"live\", got \"${value}\"`);\n}\n\nfunction parseCsv(value: string): string[] {\n return value\n .split(\",\")\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0);\n}\n\ninterface DoctorCliOpts {\n mode?: Mode;\n storeIds?: string[];\n allStores?: boolean;\n productId?: string;\n webhookUrl?: string;\n discountId?: string;\n licenseKeyId?: string;\n variantId?: string;\n json?: boolean;\n}\n\ninterface ValidateCliOpts {\n mode?: Mode;\n storeIds?: string[];\n allStores?: boolean;\n productId?: string;\n webhookUrl?: string;\n discountId?: string;\n licenseKeyId?: string;\n variantId?: string;\n json?: boolean;\n}\n\nasync function runValidate(target: ValidateTarget, opts: ValidateCliOpts): Promise<void> {\n const code = await runValidateCommand(target, {\n mode: opts.mode,\n storeIds: opts.storeIds,\n allStores: Boolean(opts.allStores),\n productId: opts.productId,\n webhookUrl: opts.webhookUrl,\n discountId: opts.discountId,\n licenseKeyId: opts.licenseKeyId,\n variantId: opts.variantId,\n json: Boolean(opts.json),\n isInteractive,\n });\n process.exit(code);\n}\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 JSON:API collection and return its `data` array.\n * Pagination is the caller's responsibility — use `meta.page` on the raw\n * request for multi-page traversal.\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 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\";\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 {\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 \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.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 {\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\nexport async function listStores(http: HttpClient): Promise<JsonApiResource<StoreAttributes>[]> {\n return http.getCollection<StoreAttributes>(\"/v1/stores\");\n}\n","import type { ValidationIssue, ValidationResult, ValidationSeverity } 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): 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 return result;\n}\n","import { FreshSqueezyError } from \"../core/errors.js\";\nimport type { HttpClient } from \"../core/http.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 { 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(toConnectionIssue(err));\n return buildResult(\"connection\", mode, issues);\n }\n}\n\n/**\n * Map the boolean `meta.test_mode` flag to our `Mode` type. Returns\n * `undefined` when the field is absent so older accounts / proxies that\n * don't surface it don't produce spurious MODE_MISMATCH failures.\n */\nfunction resolveActualMode(testMode: boolean | undefined): Mode | undefined {\n if (testMode === true) return \"test\";\n if (testMode === false) return \"live\";\n return undefined;\n}\n\nfunction toConnectionIssue(err: unknown): ValidationIssue {\n if (err instanceof FreshSqueezyError) {\n if (err.code === \"UNAUTHORIZED\") {\n return issue(ISSUE_CODES.AUTH_FAILED, \"error\", \"API key rejected by Lemon Squeezy.\", {\n suggestedFix: \"Regenerate the key at https://app.lemonsqueezy.com/settings/api.\",\n context: { status: err.status ?? null },\n });\n }\n if (err.code === \"NETWORK_ERROR\") {\n return issue(ISSUE_CODES.NETWORK_ERROR, \"error\", `Could not reach Lemon Squeezy: ${err.message}`);\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","import { FreshSqueezyError } from \"../core/errors.js\";\nimport type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { getStore, type StoreAttributes } from \"../resources/stores.js\";\nimport { ISSUE_CODES, buildResult, issue } 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 try {\n const store = await getStore(http, storeId);\n return buildResult(\"store\", mode, issues, store.attributes);\n } catch (err) {\n if (err instanceof FreshSqueezyError && err.status === 404) {\n issues.push(\n issue(\n ISSUE_CODES.STORE_NOT_FOUND,\n \"error\",\n `Store ${storeId} not found with the current API key.`,\n {\n suggestedFix:\n \"Check the store ID and confirm the API key belongs to the account that owns it.\",\n context: { storeId: String(storeId) },\n }\n )\n );\n return buildResult(\"store\", mode, issues);\n }\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(\"store\", mode, issues);\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"error\", message));\n return buildResult(\"store\", mode, issues);\n }\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.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 {\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\nexport async function listProducts(\n http: HttpClient,\n storeId: string | number\n): Promise<JsonApiResource<ProductAttributes>[]> {\n return http.getCollection<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\";\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 {\n product_id: number;\n name: string;\n slug: string;\n description?: string | null;\n status: \"pending\" | \"draft\" | \"published\";\n is_subscription?: boolean;\n interval?: string | null;\n interval_count?: number | null;\n has_license_keys?: boolean;\n created_at?: string;\n updated_at?: string;\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\nexport async function listVariantsForProduct(\n http: HttpClient,\n productId: string | number\n): Promise<JsonApiResource<VariantAttributes>[]> {\n return http.getCollection<VariantAttributes>(\"/v1/variants\", {\n \"filter[product_id]\": String(productId),\n });\n}\n","import { FreshSqueezyError } from \"../core/errors.js\";\nimport 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 { 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 let product;\n try {\n product = await getProduct(http, options.productId);\n } catch (err) {\n if (err instanceof FreshSqueezyError && err.status === 404) {\n issues.push(\n issue(\n ISSUE_CODES.PRODUCT_NOT_FOUND,\n \"error\",\n `Product ${options.productId} not found.`,\n {\n suggestedFix: \"Verify the product ID in the Lemon Squeezy dashboard.\",\n context: { productId: String(options.productId) },\n }\n )\n );\n return buildResult(\"product\", mode, issues);\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"error\", message));\n return buildResult(\"product\", mode, issues);\n }\n\n const attrs = product.attributes;\n\n if (options.expectedStoreId !== undefined) {\n const expected = String(options.expectedStoreId);\n const actual = String(attrs.store_id);\n if (expected !== actual) {\n issues.push(\n issue(\n ISSUE_CODES.PRODUCT_WRONG_STORE,\n \"error\",\n `Product belongs to store ${actual}, expected ${expected}.`,\n {\n suggestedFix:\n \"Either use the correct store ID or the correct product ID — IDs should not cross stores.\",\n context: { expectedStoreId: expected, actualStoreId: actual },\n }\n )\n );\n }\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}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.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 {\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\nexport async function listWebhooksForStore(\n http: HttpClient,\n storeId: string | number\n): Promise<JsonApiResource<WebhookAttributes>[]> {\n return http.getCollection<WebhookAttributes>(\"/v1/webhooks\", {\n \"filter[store_id]\": String(storeId),\n });\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 * The plan deliberately favors a static, reviewed manifest over live changelog\n * scraping (see plan.md §Non-goals). When the platform adds new resources,\n * fields, or webhook events, bump the entries below and re-snapshot the\n * changelog page with `npm run check:changelog -- --update`.\n *\n * Changelog source: https://docs.lemonsqueezy.com/api/getting-started/changelog\n * Last reviewed: 2026-04-24\n */\n\n/**\n * Resources fresh-squeezy wraps today. Anything outside this list is still\n * reachable via the raw `request()` escape hatch but has no dedicated\n * validator.\n */\nexport const SUPPORTED_RESOURCES = [\n \"users\",\n \"stores\",\n \"products\",\n \"variants\",\n \"webhooks\",\n] as const;\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 \"Not wrapped — reachable via client.request('/v1/subscriptions/:id'). Add a validator only if a real integration needs it.\",\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-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] as const;\n\nexport type RecommendedEvent = (typeof RECOMMENDED_WEBHOOK_EVENTS)[number];\nexport type OptionalEvent = (typeof OPTIONAL_WEBHOOK_EVENTS)[number];\n","import { 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(\"webhook\", mode, issues);\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"error\", message));\n return buildResult(\"webhook\", mode, issues);\n }\n\n const match = webhooks.find((webhook) => normalizeUrl(webhook.attributes.url) === normalizeUrl(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(\"webhook\", mode, issues);\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}\n\n/**\n * Compare webhook URLs without being tripped up by trailing slashes.\n * Lemon Squeezy strips trailing slashes on save; users often pass them in.\n */\nfunction normalizeUrl(raw: string): string {\n return raw.replace(/\\/+$/, \"\").toLowerCase();\n}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.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 {\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 { FreshSqueezyError } from \"../core/errors.js\";\nimport type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { getDiscount, type DiscountAttributes } from \"../resources/discounts.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 let discount;\n try {\n discount = await getDiscount(http, options.discountId);\n } catch (err) {\n if (err instanceof FreshSqueezyError && err.status === 404) {\n issues.push(\n issue(ISSUE_CODES.DISCOUNT_NOT_FOUND, \"error\", `Discount ${options.discountId} not found.`, {\n suggestedFix: \"Verify the discount ID in the Lemon Squeezy dashboard.\",\n context: { discountId: String(options.discountId) },\n })\n );\n return buildResult(\"discount\", mode, issues);\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"error\", message));\n return buildResult(\"discount\", mode, issues);\n }\n\n const attrs = discount.attributes;\n\n const expectedStore = String(options.storeId);\n const actualStore = String(attrs.store_id);\n if (expectedStore !== actualStore) {\n issues.push(\n issue(\n ISSUE_CODES.DISCOUNT_STORE_MISMATCH,\n \"error\",\n `Discount belongs to store ${actualStore}, expected ${expectedStore}.`,\n {\n suggestedFix: \"Use the correct store ID or discount ID — discounts should not cross stores.\",\n context: { expectedStoreId: expectedStore, actualStoreId: actualStore },\n }\n )\n );\n }\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}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { JsonApiResource } from \"../core/types.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 {\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 { FreshSqueezyError } from \"../core/errors.js\";\nimport type { HttpClient } from \"../core/http.js\";\nimport type { Mode, ValidationIssue, ValidationResult } from \"../core/types.js\";\nimport { getLicenseKey, type LicenseKeyAttributes } from \"../resources/licenseKeys.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 let licenseKey;\n try {\n licenseKey = await getLicenseKey(http, options.licenseKeyId);\n } catch (err) {\n if (err instanceof FreshSqueezyError && err.status === 404) {\n issues.push(\n issue(\n ISSUE_CODES.LICENSE_KEY_NOT_FOUND,\n \"error\",\n `License key ${options.licenseKeyId} not found.`,\n {\n suggestedFix: \"Verify the license key ID in the Lemon Squeezy dashboard.\",\n context: { licenseKeyId: String(options.licenseKeyId) },\n }\n )\n );\n return buildResult(\"licenseKey\", mode, issues);\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"error\", message));\n return buildResult(\"licenseKey\", mode, issues);\n }\n\n const attrs = licenseKey.attributes;\n\n const expectedStore = String(options.storeId);\n const actualStore = String(attrs.store_id);\n if (expectedStore !== actualStore) {\n issues.push(\n issue(\n ISSUE_CODES.LICENSE_KEY_STORE_MISMATCH,\n \"error\",\n `License key belongs to store ${actualStore}, expected ${expectedStore}.`,\n {\n suggestedFix: \"Use the correct store ID or license key ID — keys should not cross stores.\",\n context: { expectedStoreId: expectedStore, actualStoreId: actualStore },\n }\n )\n );\n }\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}\n","import { FreshSqueezyError } from \"../core/errors.js\";\nimport 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 { 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 let variant;\n try {\n variant = await getVariant<SubscriptionVariantAttributes>(http, options.variantId);\n } catch (err) {\n if (err instanceof FreshSqueezyError && err.status === 404) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_VARIANT_NOT_FOUND,\n \"error\",\n `Variant ${options.variantId} not found.`,\n {\n suggestedFix: \"Verify the variant ID in the Lemon Squeezy dashboard.\",\n context: { variantId: String(options.variantId) },\n }\n )\n );\n return buildResult(\"subscriptionPlan\", mode, issues);\n }\n const message = err instanceof Error ? err.message : \"Unknown error\";\n issues.push(issue(ISSUE_CODES.UNKNOWN, \"error\", message));\n return buildResult(\"subscriptionPlan\", mode, issues);\n }\n\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 expectedStore = String(options.storeId);\n const actualStore = String(product.attributes.store_id);\n if (expectedStore !== actualStore) {\n issues.push(\n issue(\n ISSUE_CODES.PLAN_STORE_MISMATCH,\n \"error\",\n `Subscription variant belongs to store ${actualStore} (via product ${attrs.product_id}), expected ${expectedStore}.`,\n {\n suggestedFix: \"Use the correct store ID or variant ID — plans should not cross stores.\",\n context: { expectedStoreId: expectedStore, actualStoreId: actualStore, productId: String(attrs.product_id) },\n }\n )\n );\n }\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}\n","import type { HttpClient } from \"../core/http.js\";\nimport type { DoctorReport, Mode, ValidationResult } from \"../core/types.js\";\nimport { validateConnection } from \"./connection.js\";\nimport { validateStore } from \"./store.js\";\nimport { validateProduct } from \"./product.js\";\nimport { validateWebhook } from \"./webhook.js\";\nimport { validateDiscount } from \"./discount.js\";\nimport { validateLicenseKey } from \"./licenseKey.js\";\nimport { validateSubscriptionPlan } from \"./subscriptionPlan.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 webhookUrl?: string;\n discountId?: string | number;\n licenseKeyId?: string | number;\n variantId?: string | number;\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 */\nexport async function doctor(\n http: HttpClient,\n mode: Mode,\n options: DoctorOptions = {}\n): Promise<DoctorReport> {\n const results: ValidationResult[] = [];\n\n const connection = await validateConnection(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 validateStore(http, mode, options.storeId));\n }\n\n if (options.productId !== undefined) {\n results.push(\n await validateProduct(http, mode, {\n productId: options.productId,\n expectedStoreId: options.storeId,\n })\n );\n }\n\n if (options.storeId !== undefined && options.webhookUrl !== undefined) {\n results.push(\n await validateWebhook(http, mode, {\n storeId: options.storeId,\n url: options.webhookUrl,\n })\n );\n }\n\n if (options.storeId !== undefined && options.discountId !== undefined) {\n results.push(\n await validateDiscount(http, mode, {\n storeId: options.storeId,\n discountId: options.discountId,\n })\n );\n }\n\n if (options.storeId !== undefined && options.licenseKeyId !== undefined) {\n results.push(\n await validateLicenseKey(http, mode, {\n storeId: options.storeId,\n licenseKeyId: options.licenseKeyId,\n })\n );\n }\n\n if (options.storeId !== undefined && options.variantId !== undefined) {\n results.push(\n await validateSubscriptionPlan(http, mode, {\n storeId: options.storeId,\n variantId: options.variantId,\n })\n );\n }\n\n const ok = results.every((result) => result.ok);\n return { ok, mode, results };\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 webhookUrl: options?.webhookUrl,\n discountId: options?.discountId,\n licenseKeyId: options?.licenseKeyId,\n variantId: options?.variantId,\n }),\n };\n}\n","import chalk from \"chalk\";\nimport type { DoctorReport, ValidationIssue, ValidationResult } from \"../core/types.js\";\n\n/**\n * Human-readable pretty-printer for a validation result. Keeps color logic in\n * one place so doctor/validate commands share formatting and consumers can\n * redirect stdout without ANSI codes leaking through (chalk auto-detects TTY).\n */\nexport function renderResult(result: ValidationResult): string {\n const lines: string[] = [];\n const badge = result.ok ? chalk.green(\"PASS\") : chalk.red(\"FAIL\");\n const mode = chalk.dim(`[${result.mode}]`);\n lines.push(`${badge} ${mode} ${chalk.bold(result.name)}`);\n\n for (const issue of result.issues) {\n lines.push(` ${renderIssueLine(issue)}`);\n if (issue.suggestedFix) {\n lines.push(` ${chalk.dim(\"fix:\")} ${issue.suggestedFix}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nexport function renderReport(report: DoctorReport): string {\n const header = report.ok\n ? chalk.green.bold(\"fresh-squeezy doctor: OK\")\n : chalk.red.bold(\"fresh-squeezy doctor: FAILED\");\n const body = report.results.map(renderResult).join(\"\\n\\n\");\n return `${header} ${chalk.dim(`(mode: ${report.mode})`)}\\n\\n${body}`;\n}\n\nfunction renderIssueLine(issue: ValidationIssue): string {\n const label =\n issue.severity === \"error\"\n ? chalk.red(\"✗ error\")\n : issue.severity === \"warning\"\n ? chalk.yellow(\"! warn \")\n : chalk.blue(\"i info \");\n return `${label} ${chalk.gray(`[${issue.code}]`)} ${issue.message}`;\n}\n","import inquirer from \"inquirer\";\nimport type { Mode } from \"../core/types.js\";\n\n/**\n * Interactive prompts used by `fresh-squeezy init`.\n *\n * Isolated from the command handler so the command stays focused on the flow\n * (ask → detect → confirm → write) and prompts can be unit-tested by mocking\n * inquirer without pulling in the full commander program.\n */\n\nexport interface InitAnswers {\n apiKey: string;\n mode: Mode;\n}\n\nexport async function askForCredentials(): Promise<InitAnswers> {\n const answers = await inquirer.prompt<InitAnswers>([\n {\n type: \"password\",\n name: \"apiKey\",\n message: \"Paste your Lemon Squeezy API key:\",\n mask: \"*\",\n validate: (value: string) => (value.trim().length > 0 ? true : \"API key is required.\"),\n },\n {\n type: \"list\",\n name: \"mode\",\n message: \"Which mode does this key belong to?\",\n choices: [\n { name: \"test — sandbox / development\", value: \"test\" },\n { name: \"live — production charges\", value: \"live\" },\n ],\n default: \"test\",\n },\n ]);\n return answers;\n}\n\nexport async function pickStore(\n choices: { id: string; name: string; slug: string }[]\n): Promise<string> {\n const { storeId } = await inquirer.prompt<{ storeId: string }>([\n {\n type: \"list\",\n name: \"storeId\",\n message: \"Pick a store to validate against:\",\n choices: choices.map((entry) => ({\n name: `${entry.name} (${entry.slug}) — id ${entry.id}`,\n value: entry.id,\n })),\n },\n ]);\n return storeId;\n}\n\n/**\n * Multi-select store picker used by `doctor` and `validate` when no\n * `--store-ids` / `--all-stores` flag is supplied and stdin is a TTY.\n * The first store is pre-checked so hitting Enter without toggling still\n * picks something — callers enforce the \"at least one\" rule.\n */\nexport async function pickStores(\n choices: { id: string; name: string; slug: string }[]\n): Promise<string[]> {\n const { storeIds } = await inquirer.prompt<{ storeIds: string[] }>([\n {\n type: \"checkbox\",\n name: \"storeIds\",\n message: \"Pick one or more stores (space to toggle, enter to confirm):\",\n choices: choices.map((entry, index) => ({\n name: `${entry.name} (${entry.slug}) — id ${entry.id}`,\n value: entry.id,\n checked: index === 0,\n })),\n validate: (values: unknown) =>\n Array.isArray(values) && values.length > 0 ? true : \"Pick at least one store.\",\n },\n ]);\n return storeIds;\n}\n\nexport async function confirmWriteEnvFile(path: string): Promise<boolean> {\n const { confirm } = await inquirer.prompt<{ confirm: boolean }>([\n {\n type: \"confirm\",\n name: \"confirm\",\n message: `Write these values to ${path}?`,\n default: true,\n },\n ]);\n return confirm;\n}\n","import type { FreshSqueezyClient } from \"../createFreshSqueezy.js\";\nimport { FreshSqueezyError } from \"../core/errors.js\";\nimport { pickStores } from \"./prompts.js\";\n\n/**\n * Inputs the CLI has when it needs to figure out which stores to validate.\n * Kept flag-only + TTY — the CLI deliberately does not read\n * `LEMON_SQUEEZY_STORE_ID`. Library consumers still can (via `createFreshSqueezy`).\n */\nexport interface ResolveStoresInput {\n storeIds?: string[];\n allStores?: boolean;\n /** Whether stdin is attached to a TTY so we can prompt. */\n isInteractive: boolean;\n}\n\nexport interface ResolveStoresOutput {\n storeIds: string[];\n /**\n * True when the resolver could not pick any store AND was not asked to.\n * The caller should fall back to a connection-only run in that case.\n */\n skipped: boolean;\n}\n\n/**\n * Decide which store IDs a doctor/validate run should cover.\n *\n * Resolution order (highest → lowest):\n * 1. `--store-ids 1,2,3` flag\n * 2. `--all-stores` flag (fetch every reachable store)\n * 3. TTY + inquirer multi-select\n * 4. Non-TTY, no flag → `skipped: true`\n *\n * `skipped` lets the caller decide whether to run only connection-level checks\n * or fail loudly — different subcommands want different behavior.\n */\nexport async function resolveStores(\n client: FreshSqueezyClient,\n input: ResolveStoresInput\n): Promise<ResolveStoresOutput> {\n if (input.storeIds && input.storeIds.length > 0) {\n return { storeIds: dedupe(input.storeIds), skipped: false };\n }\n\n if (input.allStores) {\n const ids = await fetchReachableStoreIds(client);\n if (ids.length === 0) {\n throw new FreshSqueezyError({\n code: \"NO_STORES\",\n message: \"No stores reachable with this API key.\",\n });\n }\n return { storeIds: ids, skipped: false };\n }\n\n if (!input.isInteractive) {\n return { storeIds: [], skipped: true };\n }\n\n const ids = await fetchReachableStoreIds(client);\n if (ids.length === 0) {\n throw new FreshSqueezyError({\n code: \"NO_STORES\",\n message: \"No stores reachable with this API key.\",\n });\n }\n if (ids.length === 1) {\n return { storeIds: ids, skipped: false };\n }\n\n const detailed = await Promise.all(\n ids.map(async (id) => {\n const result = await client.validateStore(id);\n return {\n id,\n name: result.resource?.name ?? \"(unnamed)\",\n slug: result.resource?.slug ?? \"\",\n };\n })\n );\n\n const picked = await pickStores(detailed);\n if (picked.length === 0) {\n throw new FreshSqueezyError({\n code: \"NO_SELECTION\",\n message: \"No store selected. Pick at least one to continue.\",\n });\n }\n return { storeIds: picked, skipped: false };\n}\n\n/**\n * Fetch every store reachable with the current key. Errors from the underlying\n * request propagate — callers handle them at the command boundary.\n */\nasync function fetchReachableStoreIds(client: FreshSqueezyClient): Promise<string[]> {\n const connection = await client.validateConnection();\n if (!connection.ok) {\n const authIssue = connection.issues.find((entry) => entry.code === \"AUTH_FAILED\");\n if (authIssue) {\n throw new FreshSqueezyError({\n code: \"AUTH_FAILED\",\n message: authIssue.message,\n });\n }\n }\n return connection.resource?.storeIds ?? [];\n}\n\nfunction dedupe(values: string[]): string[] {\n return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)));\n}\n","import { createFreshSqueezy, type FreshSqueezyClient } from \"../../createFreshSqueezy.js\";\nimport { FreshSqueezyError } from \"../../core/errors.js\";\nimport type { DoctorReport, Mode } from \"../../core/types.js\";\nimport { renderReport } from \"../render.js\";\nimport { resolveStores } from \"../resolveStores.js\";\n\nexport interface DoctorCommandOptions {\n mode?: Mode;\n storeIds?: string[];\n allStores?: boolean;\n productId?: string;\n webhookUrl?: string;\n discountId?: string;\n licenseKeyId?: string;\n variantId?: string;\n json?: boolean;\n isInteractive?: boolean;\n}\n\n/**\n * Aggregate payload emitted when `--json` is set. When a single store is\n * resolved, `reports` still contains one entry — consumers always see an\n * array so JSON parsers don't need two code paths.\n */\nexport interface DoctorJsonOutput {\n ok: boolean;\n mode: Mode;\n reports: DoctorReport[];\n}\n\n/**\n * `fresh-squeezy doctor` — run every validator across each resolved store and\n * emit one combined exit code. Store resolution (flag → --all-stores → TTY\n * prompt → connection-only) lives in resolveStores so validate commands can\n * reuse it.\n */\nexport async function runDoctorCommand(options: DoctorCommandOptions): Promise<number> {\n try {\n const client = createFreshSqueezy({ mode: options.mode });\n const resolved = await resolveStores(client, {\n storeIds: options.storeIds,\n allStores: options.allStores,\n isInteractive: options.isInteractive ?? false,\n });\n\n if (resolved.skipped) {\n return await runConnectionOnly(client, options);\n }\n\n const reports = await Promise.all(\n resolved.storeIds.map((storeId) =>\n client.doctor({\n storeId,\n productId: options.productId,\n webhookUrl: options.webhookUrl,\n discountId: options.discountId,\n licenseKeyId: options.licenseKeyId,\n variantId: options.variantId,\n })\n )\n );\n\n const ok = reports.every((report) => report.ok);\n const payload: DoctorJsonOutput = { ok, mode: client.mode, reports };\n\n if (options.json) {\n process.stdout.write(`${JSON.stringify(payload, null, 2)}\\n`);\n } else {\n for (const report of reports) {\n process.stdout.write(`${renderReport(report)}\\n\\n`);\n }\n }\n\n return ok ? 0 : 1;\n } catch (err) {\n writeFatal(err, options.json ?? false);\n return 2;\n }\n}\n\n/**\n * Fallback when no store could be resolved and we are not interactive.\n * Running connection-only gives CI a useful signal (\"key works\") without\n * silently pretending everything is fine.\n */\nasync function runConnectionOnly(\n client: FreshSqueezyClient,\n options: DoctorCommandOptions\n): Promise<number> {\n const connection = await client.validateConnection();\n const report: DoctorReport = {\n ok: connection.ok,\n mode: client.mode,\n results: [connection],\n };\n const payload: DoctorJsonOutput = { ok: connection.ok, mode: client.mode, reports: [report] };\n\n if (options.json) {\n process.stdout.write(`${JSON.stringify(payload, null, 2)}\\n`);\n } else {\n process.stderr.write(\n \"fresh-squeezy: no --store-ids or --all-stores and stdin is not a TTY; running connection-only.\\n\"\n );\n process.stdout.write(`${renderReport(report)}\\n`);\n }\n return connection.ok ? 0 : 1;\n}\n\nfunction writeFatal(err: unknown, asJson: boolean): void {\n if (asJson) {\n const payload =\n err instanceof FreshSqueezyError\n ? { ok: false, error: { code: err.code, message: err.message, status: err.status ?? null } }\n : { ok: false, error: { code: \"UNKNOWN\", message: err instanceof Error ? err.message : String(err) } };\n process.stderr.write(`${JSON.stringify(payload)}\\n`);\n return;\n }\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`fresh-squeezy: ${message}\\n`);\n}\n","import { createFreshSqueezy, type FreshSqueezyClient } from \"../../createFreshSqueezy.js\";\nimport { FreshSqueezyError } from \"../../core/errors.js\";\nimport type { Mode, ValidationResult } from \"../../core/types.js\";\nimport { renderResult } from \"../render.js\";\nimport { resolveStores } from \"../resolveStores.js\";\n\nexport interface ValidateCommandOptions {\n mode?: Mode;\n storeIds?: string[];\n allStores?: boolean;\n productId?: string;\n webhookUrl?: string;\n discountId?: string;\n licenseKeyId?: string;\n variantId?: string;\n json?: boolean;\n isInteractive?: boolean;\n}\n\nexport type ValidateTarget = \"connection\" | \"store\" | \"product\" | \"webhook\" | \"discount\" | \"license-key\" | \"subscription-plan\";\n\n/**\n * `fresh-squeezy validate <target>` — run one validator. Store-scoped targets\n * reuse the same store resolution the `doctor` command uses, so a single\n * `--store-ids` flag or interactive pick works across every subcommand.\n *\n * Connection runs once (no stores needed). Store/webhook loop per resolved\n * store. Product runs once because a product ID identifies a single resource;\n * the `--store-ids` value (if any) is used for the cross-store ownership check.\n */\nexport async function runValidateCommand(\n target: ValidateTarget,\n options: ValidateCommandOptions\n): Promise<number> {\n try {\n const client = createFreshSqueezy({ mode: options.mode });\n\n if (target === \"connection\") {\n return emit(await client.validateConnection(), options);\n }\n\n if (target === \"product\") {\n return emit(await runProduct(client, options), options);\n }\n\n if (target === \"discount\") {\n return emit(await runDiscount(client, options), options);\n }\n\n if (target === \"license-key\") {\n return emit(await runLicenseKey(client, options), options);\n }\n\n if (target === \"subscription-plan\") {\n return emit(await runSubscriptionPlan(client, options), options);\n }\n\n const storeIds = await resolveStoresForTarget(client, target, options);\n const results = await runPerStore(client, target, storeIds, options);\n\n if (options.json) {\n process.stdout.write(`${JSON.stringify(results, null, 2)}\\n`);\n } else {\n for (const result of results) {\n process.stdout.write(`${renderResult(result)}\\n\\n`);\n }\n }\n\n return results.every((result) => result.ok) ? 0 : 1;\n } catch (err) {\n const message =\n err instanceof FreshSqueezyError\n ? err.message\n : err instanceof Error\n ? err.message\n : String(err);\n process.stderr.write(`fresh-squeezy: ${message}\\n`);\n return 2;\n }\n}\n\nasync function resolveStoresForTarget(\n client: FreshSqueezyClient,\n target: ValidateTarget,\n options: ValidateCommandOptions\n): Promise<string[]> {\n const resolved = await resolveStores(client, {\n storeIds: options.storeIds,\n allStores: options.allStores,\n isInteractive: options.isInteractive ?? false,\n });\n if (resolved.skipped) {\n throw new FreshSqueezyError({\n code: \"MISSING_ARG\",\n message: `--store-ids or --all-stores is required for \\`validate ${target}\\` in non-interactive mode.`,\n });\n }\n return resolved.storeIds;\n}\n\nasync function runPerStore(\n client: FreshSqueezyClient,\n target: ValidateTarget,\n storeIds: string[],\n options: ValidateCommandOptions\n): Promise<ValidationResult[]> {\n if (target === \"store\") {\n return Promise.all(storeIds.map((id) => client.validateStore(id)));\n }\n if (target === \"webhook\") {\n const url = required(options.webhookUrl, \"--webhook-url is required for `validate webhook`.\");\n return Promise.all(\n storeIds.map((storeId) => client.validateWebhook({ storeId, url }))\n );\n }\n return [];\n}\n\nasync function runProduct(\n client: FreshSqueezyClient,\n options: ValidateCommandOptions\n): Promise<ValidationResult> {\n const productId = required(options.productId, \"--product-id is required for `validate product`.\");\n const expected = options.storeIds?.[0];\n return client.validateProduct({\n productId,\n expectedStoreId: expected,\n });\n}\n\nasync function runDiscount(\n client: FreshSqueezyClient,\n options: ValidateCommandOptions\n): Promise<ValidationResult> {\n const discountId = required(options.discountId, \"--discount-id is required for `validate discount`.\");\n const storeId = required(options.storeIds?.[0], \"--store-ids is required for `validate discount`.\");\n return client.validateDiscount({ storeId, discountId });\n}\n\nasync function runLicenseKey(\n client: FreshSqueezyClient,\n options: ValidateCommandOptions\n): Promise<ValidationResult> {\n const licenseKeyId = required(options.licenseKeyId, \"--license-key-id is required for `validate license-key`.\");\n const storeId = required(options.storeIds?.[0], \"--store-ids is required for `validate license-key`.\");\n return client.validateLicenseKey({ storeId, licenseKeyId });\n}\n\nasync function runSubscriptionPlan(\n client: FreshSqueezyClient,\n options: ValidateCommandOptions\n): Promise<ValidationResult> {\n const variantId = required(options.variantId, \"--variant-id is required for `validate subscription-plan`.\");\n const storeId = required(options.storeIds?.[0], \"--store-ids is required for `validate subscription-plan`.\");\n return client.validateSubscriptionPlan({ storeId, variantId });\n}\n\nfunction emit(result: ValidationResult, options: ValidateCommandOptions): number {\n if (options.json) {\n process.stdout.write(`${JSON.stringify(result, null, 2)}\\n`);\n } else {\n process.stdout.write(`${renderResult(result)}\\n`);\n }\n return result.ok ? 0 : 1;\n}\n\nfunction required<T>(value: T | undefined, message: string): T {\n if (value === undefined || value === null || value === \"\") {\n throw new FreshSqueezyError({ code: \"MISSING_ARG\", message });\n }\n return value;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport chalk from \"chalk\";\nimport { createFreshSqueezy } from \"../../createFreshSqueezy.js\";\nimport { ENV_KEYS } from \"../../core/config.js\";\nimport { renderReport } from \"../render.js\";\nimport { askForCredentials, confirmWriteEnvFile, pickStore } from \"../prompts.js\";\n\nexport interface InitCommandOptions {\n envFile?: string;\n}\n\n/**\n * `fresh-squeezy init` — interactive onboarding. Walks the user through the\n * fastest path from \"I have an API key\" to \"my integration is verified\":\n *\n * 1. Ask for API key + mode.\n * 2. List reachable stores via `/v1/stores`, let the user pick one.\n * 3. Optionally persist credentials to `.env.local`.\n * 4. Run `doctor()` against the chosen config and print the report.\n *\n * Returns an exit code so the CLI wrapper can forward it to `process.exit`.\n */\nexport async function runInitCommand(options: InitCommandOptions = {}): Promise<number> {\n const answers = await askForCredentials();\n const client = createFreshSqueezy({ apiKey: answers.apiKey, mode: answers.mode });\n\n const connection = await client.validateConnection();\n if (!connection.ok) {\n process.stdout.write(`${renderReport({ ok: false, mode: answers.mode, results: [connection] })}\\n`);\n return 1;\n }\n\n const storeIds = connection.resource?.storeIds ?? [];\n if (storeIds.length === 0) {\n process.stdout.write(\n chalk.yellow(\"No stores reachable with this key. Create a store in Lemon Squeezy and retry.\\n\")\n );\n return 1;\n }\n\n const stores = await Promise.all(storeIds.map((id) => client.validateStore(id)));\n const pickable = stores\n .filter((entry) => entry.ok && entry.resource)\n .map((entry, index) => ({\n id: storeIds[index] ?? \"\",\n name: entry.resource?.name ?? \"(unnamed)\",\n slug: entry.resource?.slug ?? \"\",\n }))\n .filter((entry) => entry.id !== \"\");\n\n const storeId = await pickStore(pickable);\n\n const envPath = path.resolve(process.cwd(), options.envFile ?? \".env.local\");\n const shouldWrite = await confirmWriteEnvFile(envPath);\n if (shouldWrite) {\n await writeEnvFile(envPath, { apiKey: answers.apiKey, mode: answers.mode, storeId });\n process.stdout.write(chalk.green(`Wrote ${envPath}\\n`));\n }\n\n process.stdout.write(chalk.dim(\"\\nRunning doctor...\\n\\n\"));\n const report = await client.doctor({ storeId });\n process.stdout.write(`${renderReport(report)}\\n`);\n\n return report.ok ? 0 : 1;\n}\n\nasync function writeEnvFile(\n envPath: string,\n values: { apiKey: string; mode: string; storeId: string }\n): Promise<void> {\n const lines = [\n `${ENV_KEYS.apiKey}=${values.apiKey}`,\n `${ENV_KEYS.storeId}=${values.storeId}`,\n `${ENV_KEYS.mode}=${values.mode}`,\n \"\",\n ];\n await fs.writeFile(envPath, lines.join(\"\\n\"), { encoding: \"utf8\" });\n}\n"],"mappings":"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","names":["path","issue","path","ids","chalk","chalk"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fresh-squeezy",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Validator-first Lemon Squeezy setup toolkit. Verify your integration before it hits production.",
5
5
  "keywords": [
6
6
  "lemon-squeezy",