{"version":3,"file":"config.cjs","names":[],"sources":["../../src/google-play/config.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport ms from 'ms';\nimport type { IsUnique, Metadata } from '../types';\n\nconst addMethod = Symbol('addMethod');\n\nclass Subscription<\n  NS extends Lowercase<string> = Lowercase<string>,\n  PE extends string = string,\n  BP extends string = string,\n  PI extends string = never,\n  I extends Lowercase<string> = never,\n> {\n  readonly productId: string;\n  readonly config: GooglePlayConfig<NS, PE, BP, PI>;\n  readonly plans: Map<string, Metadata & { plan?: PE; billingPeriod?: BP }>;\n  readonly defaultPlans: Set<string>;\n\n  constructor(config: GooglePlayConfig<NS, PE, BP, PI>, productId: string) {\n    this.config = config;\n    this.productId = productId;\n    this.plans = new Map();\n    this.defaultPlans = new Set();\n  }\n\n  /**\n   * Non-default base plans stay declared on purpose: they keep resolving for\n   * grandfathered subscribers and serve as price-experiment arms. Never sell\n   * a legacy base plan to new users unless that is intentional.\n   */\n  basePlan = <K extends Lowercase<string>>(\n    planId: K extends I ? never : K,\n    plan: PE,\n    billingPeriod: BP,\n    metadata: Metadata = { credits: 0, expiresIn: '0' }\n  ): Subscription<NS, PE, BP, PI, I | K> => {\n    invariant(!this.plans.has(planId), `Duplicate base plan ${planId}`);\n    this.plans.set(planId, { ...metadata, plan, billingPeriod });\n    return this;\n  };\n\n  default = <const T extends readonly [I, ...I[]]>(\n    planIds: T & (IsUnique<T> extends true ? unknown : never)\n  ) => {\n    planIds.forEach((planId) => {\n      invariant(this.plans.has(planId), `Default base plan ${planId} is not declared`);\n      this.defaultPlans.add(planId);\n    });\n    this.config[addMethod](this);\n    return this.config;\n  };\n}\n\ntype Options = { package: string; audience: string };\n\nexport class GooglePlayConfig<\n  NS extends Lowercase<string> = Lowercase<string>,\n  PE extends string = string,\n  BP extends string = string,\n  PI extends string = never,\n> {\n  readonly package: string;\n  readonly audience: string;\n  private readonly onetimes: Set<string>;\n  private readonly subscriptions: Map<string, Subscription<NS, PE, BP, PI>>;\n  private readonly products: Map<string, (Metadata & { plan?: PE; billingPeriod?: BP }) | null>;\n\n  // runtime mirror of the type-level guards: generic accumulation only\n  // protects a single fluent chain, not statement-style re-entry or casts\n  [addMethod] = (subscription: Subscription<NS, PE, BP, PI>) => {\n    invariant(\n      !this.subscriptions.has(subscription.productId),\n      `Duplicate subscription ${subscription.productId}`\n    );\n    subscription.plans.forEach((metadata, planId) => {\n      const id = this.getId(subscription.productId, planId);\n      invariant(!this.products.has(id), `Duplicate product ${id}`);\n      this.products.set(id, metadata);\n    });\n    this.subscriptions.set(subscription.productId, subscription);\n    return this;\n  };\n\n  private readonly getId = (productId: string, planId?: string) => {\n    return planId ? `${productId}:${planId}` : productId;\n  };\n\n  private constructor(options: Options) {\n    this.package = options.package;\n    this.audience = options.audience;\n    this.products = new Map();\n    this.onetimes = new Set();\n    this.subscriptions = new Map();\n  }\n\n  static create = <\n    NS extends Lowercase<string>,\n    PE extends string = string,\n    BP extends string = string,\n  >(\n    options: Options\n  ) => {\n    return new GooglePlayConfig<NS, PE, BP>(options);\n  };\n\n  subscription = <K extends `${NS}.${Lowercase<string>}`>(productId: K extends PI ? never : K) => {\n    return new Subscription<NS, PE, BP, PI | K>(this, productId);\n  };\n\n  onetime = <K extends `${NS}.${Lowercase<string>}`>(\n    productId: K extends PI ? never : K,\n    metadata: Metadata = { credits: 0, expiresIn: '0' }\n  ) => {\n    invariant(!this.products.has(productId), `Duplicate product ${productId}`);\n    this.onetimes.add(productId);\n    this.products.set(productId, metadata);\n    return this as GooglePlayConfig<NS, PE, BP, PI | K>;\n  };\n\n  /** Base plan ids that new subscribers should purchase for the product. */\n  getDefaultPlanIds = (productId: string): string[] => {\n    const subscription = this.subscriptions.get(productId);\n    invariant(subscription, `Subscription not found for ${productId}`);\n    return Array.from(subscription.defaultPlans);\n  };\n\n  getPlan = (productId: string, planId: string): PE => {\n    const id = this.getId(productId, planId);\n    const metadata = this.products.get(id);\n    invariant(metadata, `Product not found for ${id}`);\n    invariant(metadata.plan, `Plan not found for ${id}`);\n    return metadata.plan;\n  };\n\n  getBillingPeriod = (productId: string, planId: string): BP => {\n    const id = this.getId(productId, planId);\n    const metadata = this.products.get(id);\n    invariant(metadata, `Product not found for ${id}`);\n    invariant(metadata.billingPeriod, `Billing period not found for ${id}`);\n    return metadata.billingPeriod;\n  };\n\n  getMode = (productId: string): 'payment' | 'subscription' => {\n    if (this.onetimes.has(productId)) return 'payment';\n    if (this.subscriptions.has(productId)) return 'subscription';\n    throw new Error(`Mode not found for product ${productId}`);\n  };\n\n  getCreditAmount = (productId: string, planId?: string): number => {\n    const id = this.getId(productId, planId);\n    const metadata = this.products.get(id);\n    invariant(metadata, `Product not found for ${id}`);\n    return metadata.credits;\n  };\n\n  private readonly getCreditExpiresIn = (productId: string, planId?: string): number => {\n    const id = this.getId(productId, planId);\n    const metadata = this.products.get(id);\n    invariant(metadata, `Product not found for ${id}`);\n    return ms(metadata.expiresIn);\n  };\n\n  getCreditExpiresAt = (productId: string, planId?: string): string => {\n    const expiresIn = this.getCreditExpiresIn(productId, planId);\n    return new Date(Date.now() + expiresIn).toISOString();\n  };\n}\n"],"mappings":"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"}