@happyvertical/smrt-subscriptions 0.34.9 → 0.35.1

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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/__smrt-register__.ts","../src/utils.ts","../src/models/SubscriptionPlan.ts","../src/collections/SubscriptionPlanCollection.ts","../src/models/TenantSubscription.ts","../src/collections/TenantSubscriptionCollection.ts","../src/models/TenantUsageMetric.ts","../src/collections/TenantUsageMetricCollection.ts","../src/services/threshold-evaluator.ts","../src/services/usage-meter.ts","../src/services/subscription-resolver.ts"],"sourcesContent":["import { ObjectRegistry } from '@happyvertical/smrt-core';\n\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","import type {\n JsonObject,\n PlanFeatureGrant,\n PlanThreshold,\n Subscriber,\n SubscriberKind,\n ThresholdEnforcement,\n ThresholdWindow,\n UsageWindow,\n} from './types.js';\n\nconst THRESHOLD_WINDOWS: ThresholdWindow[] = [\n 'day',\n 'week',\n 'month',\n 'year',\n 'rolling',\n];\nconst THRESHOLD_ENFORCEMENTS: ThresholdEnforcement[] = [\n 'observe',\n 'warn',\n 'block',\n];\n\nexport function parseJsonArray<T>(value: string, fallback: T[] = []): T[] {\n if (!value) {\n return fallback;\n }\n\n try {\n const parsed = JSON.parse(value) as unknown;\n return Array.isArray(parsed) ? (parsed as T[]) : fallback;\n } catch {\n return fallback;\n }\n}\n\nexport function parseJsonObject<T extends JsonObject>(\n value: string,\n fallback: T,\n): T {\n if (!value) {\n return fallback;\n }\n\n try {\n const parsed = JSON.parse(value) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as T)\n : fallback;\n } catch {\n return fallback;\n }\n}\n\nexport function stringifyJson(value: unknown): string {\n return JSON.stringify(value ?? null);\n}\n\nexport function normalizeFeatureGrants(\n grants: Array<string | PlanFeatureGrant>,\n): PlanFeatureGrant[] {\n return grants.map((grant) =>\n typeof grant === 'string' ? { featureKey: grant, enabled: true } : grant,\n );\n}\n\n/**\n * Coerce inputs from the legacy tenant-only API into a {@link Subscriber}.\n *\n * Existing callers that pass only `tenantId` (with optional `subscriberKind` /\n * `subscriberExternalId` fields, e.g. on `RecordUsageOptions`) get normalized\n * into the discriminated union exactly once at the boundary. This is the only\n * place that contains \"if kind is omitted, default to tenant\" logic — every\n * other site works with a typed `Subscriber`.\n *\n * Throws when:\n * - `subscriberKind === 'external'` is requested without a non-empty\n * `subscriberExternalId` (the XOR invariant is the whole point), or\n * - the input carries a non-empty `subscriberExternalId` but the kind resolves\n * to `'tenant'` (silent re-scoping would write buyer-scoped usage as tenant\n * usage, which then disappears from external summaries).\n */\nexport function normalizeSubscriber(input: {\n tenantId: string;\n subscriberKind?: SubscriberKind | null;\n subscriberExternalId?: string | null;\n}): Subscriber {\n // Treat null/undefined external ids as \"absent\" — but only after the\n // discriminator-derived branch decides what to do. We never coerce null\n // into the persisted column because the invariant on the model is\n // re-checked at save time via assertSubscriberInvariant.\n const rawExternalId = input.subscriberExternalId;\n const externalIdAbsent =\n rawExternalId === null || rawExternalId === undefined;\n const externalId = externalIdAbsent ? '' : String(rawExternalId);\n\n const rawKind = input.subscriberKind;\n const kind: SubscriberKind =\n rawKind === null || rawKind === undefined ? 'tenant' : rawKind;\n\n if (kind !== 'tenant' && kind !== 'external') {\n throw new Error(\n `subscriberKind must be \"tenant\" or \"external\" (got ${JSON.stringify(rawKind)})`,\n );\n }\n\n if (kind === 'tenant') {\n if (externalId !== '') {\n throw new Error(\n 'subscriberExternalId is set but subscriberKind is \"tenant\"; ' +\n 'set subscriberKind=\"external\" explicitly or omit subscriberExternalId',\n );\n }\n return { kind: 'tenant', tenantId: input.tenantId };\n }\n if (externalId === '') {\n throw new Error(\n 'subscriberKind=\"external\" requires a non-empty subscriberExternalId',\n );\n }\n return {\n kind: 'external',\n tenantId: input.tenantId,\n externalId,\n };\n}\n\n/**\n * Project a {@link Subscriber} back onto column values for persistence or query\n * filters. Counterpart to {@link normalizeSubscriber}.\n */\nexport function subscriberToColumns(subscriber: Subscriber): {\n tenantId: string;\n subscriberKind: SubscriberKind;\n subscriberExternalId: string;\n} {\n if (subscriber.kind === 'tenant') {\n return {\n tenantId: subscriber.tenantId,\n subscriberKind: 'tenant',\n subscriberExternalId: '',\n };\n }\n return {\n tenantId: subscriber.tenantId,\n subscriberKind: 'external',\n subscriberExternalId: subscriber.externalId,\n };\n}\n\n/**\n * Enforce the subscriber XOR invariant on a row's columns. Used by both the\n * model constructors (catches construction-time mistakes) and the\n * `validateBeforeSave` override (catches mutations applied via the\n * generated PUT/PATCH update path that bypass the constructor).\n *\n * Defensive about types because both call sites can receive JSON or untyped\n * fixture data via the generated REST/CLI surface — `null` arriving in place\n * of `''` would otherwise slip past an `=== ''` check, land in the\n * `(tenant_id, subscriber_kind, subscriber_external_id)` conflict key, and\n * (on Postgres) not collide with other NULLs while `findCurrentForSubscriber`\n * keeps querying by a string external id.\n *\n * @param modelName - Prepended to error messages so callers can tell whether\n * the failure originated in `TenantSubscription` or `TenantUsageMetric`.\n */\nexport function assertSubscriberInvariant(\n modelName: string,\n fields: {\n subscriberKind: unknown;\n subscriberExternalId: unknown;\n },\n): void {\n if (\n fields.subscriberKind !== 'tenant' &&\n fields.subscriberKind !== 'external'\n ) {\n throw new Error(\n `${modelName}: subscriberKind must be \"tenant\" or \"external\" (got ${describe(\n fields.subscriberKind,\n )})`,\n );\n }\n if (typeof fields.subscriberExternalId !== 'string') {\n throw new Error(\n `${modelName}: subscriberExternalId must be a string (got ${describe(\n fields.subscriberExternalId,\n )})`,\n );\n }\n\n if (\n fields.subscriberKind === 'tenant' &&\n fields.subscriberExternalId !== ''\n ) {\n throw new Error(\n `${modelName}: subscriberExternalId must be empty when subscriberKind is \"tenant\"`,\n );\n }\n if (\n fields.subscriberKind === 'external' &&\n fields.subscriberExternalId === ''\n ) {\n throw new Error(\n `${modelName}: subscriberKind=\"external\" requires a non-empty subscriberExternalId`,\n );\n }\n}\n\nfunction describe(value: unknown): string {\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n if (typeof value === 'string') return `\"${value}\"`;\n return String(value);\n}\n\nexport function getWindowForThreshold(\n thresholdWindow: ThresholdWindow,\n now = new Date(),\n): UsageWindow {\n switch (thresholdWindow) {\n case 'day':\n return utcWindow(now, 'day');\n case 'week':\n return utcWindow(now, 'week');\n case 'month':\n return utcWindow(now, 'month');\n case 'year':\n return utcWindow(now, 'year');\n case 'rolling':\n return {\n start: new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000),\n end: now,\n };\n }\n}\n\nexport function getWindowKey(window: UsageWindow): string {\n return `${window.start.toISOString()}..${window.end.toISOString()}`;\n}\n\nexport function isValidThreshold(threshold: PlanThreshold): boolean {\n return (\n typeof threshold.metricKey === 'string' &&\n threshold.metricKey.length > 0 &&\n Number.isFinite(threshold.limit) &&\n threshold.limit >= 0 &&\n isThresholdWindow(threshold.window) &&\n isThresholdEnforcement(threshold.enforcement) &&\n (threshold.warningRatio === undefined ||\n (Number.isFinite(threshold.warningRatio) &&\n threshold.warningRatio >= 0 &&\n threshold.warningRatio <= 1))\n );\n}\n\nfunction isThresholdWindow(value: unknown): value is ThresholdWindow {\n return (\n typeof value === 'string' &&\n THRESHOLD_WINDOWS.includes(value as ThresholdWindow)\n );\n}\n\nfunction isThresholdEnforcement(value: unknown): value is ThresholdEnforcement {\n return (\n typeof value === 'string' &&\n THRESHOLD_ENFORCEMENTS.includes(value as ThresholdEnforcement)\n );\n}\n\nfunction utcWindow(now: Date, unit: 'day' | 'week' | 'month' | 'year') {\n const start = new Date(\n Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()),\n );\n\n if (unit === 'week') {\n const day = start.getUTCDay();\n const mondayOffset = day === 0 ? -6 : 1 - day;\n start.setUTCDate(start.getUTCDate() + mondayOffset);\n }\n\n if (unit === 'month') {\n start.setUTCDate(1);\n }\n\n if (unit === 'year') {\n start.setUTCMonth(0, 1);\n }\n\n const end = new Date(start);\n switch (unit) {\n case 'day':\n end.setUTCDate(end.getUTCDate() + 1);\n break;\n case 'week':\n end.setUTCDate(end.getUTCDate() + 7);\n break;\n case 'month':\n end.setUTCMonth(end.getUTCMonth() + 1);\n break;\n case 'year':\n end.setUTCFullYear(end.getUTCFullYear() + 1);\n break;\n }\n\n return { start, end };\n}\n","import { SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n BillingInterval,\n JsonObject,\n PlanFeatureGrant,\n PlanThreshold,\n SubscriptionPlanStatus,\n} from '../types.js';\nimport {\n normalizeFeatureGrants,\n parseJsonArray,\n parseJsonObject,\n stringifyJson,\n} from '../utils.js';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: '_smrt_subscription_plans',\n api: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n mcp: { include: ['list', 'get'] },\n conflictColumns: ['plan_key'],\n})\nexport class SubscriptionPlan extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n planKey: string = '';\n name: string = '';\n description: string = '';\n status: SubscriptionPlanStatus = 'active';\n sortOrder: number = 0;\n priceAmount: number = 0.0;\n currency: string = 'USD';\n billingInterval: BillingInterval = 'month';\n externalProvider: string = 'stripe';\n stripeProductId: string = '';\n stripePriceId: string = '';\n features: string = '[]';\n thresholds: string = '[]';\n metadata: string = '{}';\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.planKey !== undefined) this.planKey = options.planKey;\n if (options.name !== undefined) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.status !== undefined) this.status = options.status;\n if (options.sortOrder !== undefined) this.sortOrder = options.sortOrder;\n if (options.priceAmount !== undefined)\n this.priceAmount = options.priceAmount;\n if (options.currency !== undefined) this.currency = options.currency;\n if (options.billingInterval !== undefined) {\n this.billingInterval = options.billingInterval;\n }\n if (options.externalProvider !== undefined) {\n this.externalProvider = options.externalProvider;\n }\n if (options.stripeProductId !== undefined) {\n this.stripeProductId = options.stripeProductId;\n }\n if (options.stripePriceId !== undefined) {\n this.stripePriceId = options.stripePriceId;\n }\n if (options.features !== undefined) this.features = options.features;\n if (options.thresholds !== undefined) this.thresholds = options.thresholds;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n isActive(): boolean {\n return this.status === 'active';\n }\n\n getFeatureGrants(): PlanFeatureGrant[] {\n return normalizeFeatureGrants(\n parseJsonArray<string | PlanFeatureGrant>(this.features),\n );\n }\n\n setFeatureGrants(grants: Array<string | PlanFeatureGrant>): void {\n this.features = stringifyJson(normalizeFeatureGrants(grants));\n }\n\n getFeatureKeys(): string[] {\n return this.getFeatureGrants()\n .filter((grant) => grant.enabled !== false)\n .map((grant) => grant.featureKey);\n }\n\n getThresholds(): PlanThreshold[] {\n return parseJsonArray<PlanThreshold>(this.thresholds).filter(\n (threshold) => threshold.metricKey,\n );\n }\n\n setThresholds(thresholds: PlanThreshold[]): void {\n this.thresholds = stringifyJson(thresholds);\n }\n\n getMetadata(): JsonObject {\n return parseJsonObject(this.metadata, {});\n }\n\n setMetadata(metadata: JsonObject): void {\n this.metadata = stringifyJson(metadata);\n }\n}\n\nexport default SubscriptionPlan;\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { SubscriptionPlan } from '../models/SubscriptionPlan.js';\n\nexport class SubscriptionPlanCollection extends SmrtCollection<SubscriptionPlan> {\n static readonly _itemClass = SubscriptionPlan;\n\n async findByPlanKey(planKey: string): Promise<SubscriptionPlan | null> {\n const plans = await this.list({ where: { planKey }, limit: 1 });\n return plans[0] ?? null;\n }\n\n async findActive(): Promise<SubscriptionPlan[]> {\n return this.list({\n where: { status: 'active' },\n orderBy: 'sortOrder ASC',\n });\n }\n\n async findByStripePriceId(\n stripePriceId: string,\n ): Promise<SubscriptionPlan | null> {\n const plans = await this.list({ where: { stripePriceId }, limit: 1 });\n return plans[0] ?? null;\n }\n}\n","import { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n JsonObject,\n Subscriber,\n SubscriberKind,\n SubscriptionStatus,\n} from '../types.js';\nimport {\n assertSubscriberInvariant,\n parseJsonObject,\n stringifyJson,\n} from '../utils.js';\n\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: '_smrt_tenant_subscriptions',\n api: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n mcp: { include: ['list', 'get'] },\n // The conflict key includes the subscriber discriminator so an issuing tenant\n // can host many distinct external subscribers without collisions on the\n // unique index:\n // - tenant-kind rows always have subscriber_external_id = '' → uniqueness\n // reduces to (tenant_id, 'tenant', '') = at most one tenant-kind row per\n // tenant (preserves the pre-polymorphic invariant).\n // - external-kind rows are unique on (tenant_id, 'external', externalId) =\n // at most one active subscription per (issuer, external subscriber).\n //\n // UPGRADE NOTE: databases created against the pre-#1454 conflict key\n // ['tenant_id'] still carry the legacy `_smrt_tenant_subscriptions_tenant_id_idx`.\n // `smrt db:migrate` does not sweep orphan indexes by default, so the legacy\n // index persists and continues to reject external subscriptions until it's\n // dropped. Run scripts/migrate-1454-drop-legacy-conflict-index.ts once on\n // upgrade, or pass `--drop-indexes` to `db:migrate`. Fresh databases need\n // neither — the generated schema already reflects the new key.\n conflictColumns: ['tenant_id', 'subscriber_kind', 'subscriber_external_id'],\n})\nexport class TenantSubscription extends SmrtObject {\n @tenantId()\n tenantId?: string;\n\n /**\n * Discriminator for the subscriber identity.\n *\n * - `'tenant'` (default): the subscriber IS the owning `tenantId` — the\n * pre-polymorphic shape. All existing rows continue to behave this way.\n * - `'external'`: the subscriber is `subscriberExternalId`, scoped under the\n * issuing `tenantId`. Used for B2C buyers, anonymous-email subscribers,\n * agent identities, and any other caller-defined identity.\n */\n subscriberKind: SubscriberKind = 'tenant';\n\n /**\n * Caller-namespaced opaque identifier for the subscriber when\n * `subscriberKind === 'external'`. Empty string when kind is `'tenant'`.\n *\n * The package treats this as opaque — no FK, no inferred semantics. Callers\n * are expected to namespace (e.g. `buyer-contact:abc123`,\n * `agent:hermes-7`, `email:foo@example.com`).\n */\n subscriberExternalId: string = '';\n\n @foreignKey('SubscriptionPlan')\n planId: string = '';\n\n status: SubscriptionStatus = 'incomplete';\n startedAt: Date = new Date();\n currentPeriodStart: Date | null = null;\n currentPeriodEnd: Date | null = null;\n trialEndsAt: Date | null = null;\n cancelAtPeriodEnd: boolean = false;\n canceledAt: Date | null = null;\n externalProvider: string = 'stripe';\n stripeCustomerId: string = '';\n stripeSubscriptionId: string = '';\n stripeCheckoutSessionId: string = '';\n metadata: string = '{}';\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.subscriberKind !== undefined)\n this.subscriberKind = options.subscriberKind;\n if (options.subscriberExternalId !== undefined)\n this.subscriberExternalId = options.subscriberExternalId;\n // Enforce the subscriber XOR invariant at construction time so callers\n // catch mistakes early. The same check also runs in `validateBeforeSave`\n // below so the generated PUT/PATCH update path — which mutates an existing\n // instance via `Object.assign()` and bypasses this constructor — can't\n // sneak a malformed row past us either.\n assertSubscriberInvariant('TenantSubscription', {\n subscriberKind: this.subscriberKind,\n subscriberExternalId: this.subscriberExternalId,\n });\n if (options.planId !== undefined) this.planId = options.planId;\n if (options.status !== undefined) this.status = options.status;\n if (options.startedAt !== undefined) this.startedAt = options.startedAt;\n if (options.currentPeriodStart !== undefined) {\n this.currentPeriodStart = options.currentPeriodStart;\n }\n if (options.currentPeriodEnd !== undefined) {\n this.currentPeriodEnd = options.currentPeriodEnd;\n }\n if (options.trialEndsAt !== undefined)\n this.trialEndsAt = options.trialEndsAt;\n if (options.cancelAtPeriodEnd !== undefined) {\n this.cancelAtPeriodEnd = options.cancelAtPeriodEnd;\n }\n if (options.canceledAt !== undefined) this.canceledAt = options.canceledAt;\n if (options.externalProvider !== undefined) {\n this.externalProvider = options.externalProvider;\n }\n if (options.stripeCustomerId !== undefined) {\n this.stripeCustomerId = options.stripeCustomerId;\n }\n if (options.stripeSubscriptionId !== undefined) {\n this.stripeSubscriptionId = options.stripeSubscriptionId;\n }\n if (options.stripeCheckoutSessionId !== undefined) {\n this.stripeCheckoutSessionId = options.stripeCheckoutSessionId;\n }\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n /**\n * Validates the subscriber XOR invariant before every save. Critical for the\n * generated update path: `SmrtCollection.update()` mutates the existing\n * instance via `Object.assign()` and calls `save()` directly — the\n * constructor never re-runs, so without this hook a partial update like\n * `{ subscriberExternalId: 'buyer:alice' }` on a tenant-kind row would\n * persist a malformed conflict key and let two tenant-kind rows coexist\n * under the same tenant.\n */\n protected async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n assertSubscriberInvariant('TenantSubscription', {\n subscriberKind: this.subscriberKind,\n subscriberExternalId: this.subscriberExternalId,\n });\n }\n\n isEntitled(now = new Date()): boolean {\n if (this.status !== 'active' && this.status !== 'trialing') {\n return false;\n }\n if (\n this.currentPeriodEnd &&\n this.currentPeriodEnd.getTime() < now.getTime()\n ) {\n return false;\n }\n return true;\n }\n\n /**\n * Project this row's polymorphic subscriber columns onto the\n * {@link Subscriber} discriminated union. Returns `null` when the owning\n * tenant is absent — that's an invalid row that should not be acted on.\n */\n getSubscriber(): Subscriber | null {\n if (!this.tenantId) {\n return null;\n }\n if (this.subscriberKind === 'external') {\n if (!this.subscriberExternalId) {\n return null;\n }\n return {\n kind: 'external',\n tenantId: this.tenantId,\n externalId: this.subscriberExternalId,\n };\n }\n return { kind: 'tenant', tenantId: this.tenantId };\n }\n\n getMetadata(): JsonObject {\n return parseJsonObject(this.metadata, {});\n }\n\n setMetadata(metadata: JsonObject): void {\n this.metadata = stringifyJson(metadata);\n }\n}\n\nexport default TenantSubscription;\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { TenantSubscription } from '../models/TenantSubscription.js';\nimport type { Subscriber } from '../types.js';\n\nexport class TenantSubscriptionCollection extends SmrtCollection<TenantSubscription> {\n static readonly _itemClass = TenantSubscription;\n\n /**\n * List subscriptions whose owning tenant scope is `tenantId`. Includes both\n * `'tenant'`-kind (subscriber == owner) and `'external'`-kind (subscriber is\n * an external identity scoped under this tenant) rows.\n */\n async findByTenant(tenantId: string): Promise<TenantSubscription[]> {\n return this.list({\n where: { tenantId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Legacy single-tenant accessor: returns the current subscription where the\n * tenant IS the subscriber (`subscriberKind = 'tenant'`). External-kind rows\n * scoped under this tenant are intentionally excluded so existing callers\n * are not surprised by buyer/agent subscriptions.\n */\n async findCurrentForTenant(\n tenantId: string,\n now = new Date(),\n ): Promise<TenantSubscription | null> {\n return this.findCurrentForSubscriber({ kind: 'tenant', tenantId }, now);\n }\n\n /**\n * Polymorphic current-subscription accessor.\n *\n * For `'tenant'` subscribers we match rows with `subscriberKind = 'tenant'`\n * under the same `tenantId`. For `'external'` subscribers we match by\n * `(tenantId, subscriberExternalId)` with `subscriberKind = 'external'`.\n */\n async findCurrentForSubscriber(\n subscriber: Subscriber,\n now = new Date(),\n ): Promise<TenantSubscription | null> {\n const where: Record<string, unknown> = {\n tenantId: subscriber.tenantId,\n subscriberKind: subscriber.kind,\n };\n if (subscriber.kind === 'external') {\n where.subscriberExternalId = subscriber.externalId;\n }\n\n const subscriptions = await this.list({\n where,\n orderBy: 'created_at DESC',\n });\n return (\n subscriptions.find((subscription) => subscription.isEntitled(now)) ??\n subscriptions[0] ??\n null\n );\n }\n\n async findByStripeSubscriptionId(\n stripeSubscriptionId: string,\n ): Promise<TenantSubscription | null> {\n const subscriptions = await this.list({\n where: { stripeSubscriptionId },\n limit: 1,\n });\n return subscriptions[0] ?? null;\n }\n}\n","import { SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { JsonObject, Subscriber, SubscriberKind } from '../types.js';\nimport {\n assertSubscriberInvariant,\n parseJsonObject,\n stringifyJson,\n} from '../utils.js';\n\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: '_smrt_tenant_usage_metrics',\n api: { include: ['list', 'get', 'create'] },\n cli: true,\n mcp: { include: ['list', 'get'] },\n})\nexport class TenantUsageMetric extends SmrtObject {\n @tenantId()\n tenantId?: string;\n\n /**\n * Discriminator for which subscriber identity recorded this usage.\n * `'tenant'` (default) preserves the legacy shape; `'external'` indicates\n * the subscriber is `subscriberExternalId` under the issuing `tenantId`.\n */\n subscriberKind: SubscriberKind = 'tenant';\n\n /**\n * Opaque caller-namespaced subscriber id when `subscriberKind === 'external'`.\n * Empty string when kind is `'tenant'`.\n */\n subscriberExternalId: string = '';\n\n metricKey: string = '';\n quantity: number = 0.0;\n windowStart: Date = new Date();\n windowEnd: Date = new Date();\n source: string = '';\n sourceId: string = '';\n dimensions: string = '{}';\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.subscriberKind !== undefined)\n this.subscriberKind = options.subscriberKind;\n if (options.subscriberExternalId !== undefined)\n this.subscriberExternalId = options.subscriberExternalId;\n // Construction-time guard; the same invariant is re-checked in\n // `validateBeforeSave` so the generated update path can't bypass it.\n assertSubscriberInvariant('TenantUsageMetric', {\n subscriberKind: this.subscriberKind,\n subscriberExternalId: this.subscriberExternalId,\n });\n if (options.metricKey !== undefined) this.metricKey = options.metricKey;\n if (options.quantity !== undefined) this.quantity = options.quantity;\n if (options.windowStart !== undefined)\n this.windowStart = options.windowStart;\n if (options.windowEnd !== undefined) this.windowEnd = options.windowEnd;\n if (options.source !== undefined) this.source = options.source;\n if (options.sourceId !== undefined) this.sourceId = options.sourceId;\n if (options.dimensions !== undefined) this.dimensions = options.dimensions;\n }\n\n /** See `TenantSubscription.validateBeforeSave` for the rationale. */\n protected async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n assertSubscriberInvariant('TenantUsageMetric', {\n subscriberKind: this.subscriberKind,\n subscriberExternalId: this.subscriberExternalId,\n });\n }\n\n /**\n * Project this row's polymorphic subscriber columns onto the\n * {@link Subscriber} discriminated union.\n */\n getSubscriber(): Subscriber | null {\n if (!this.tenantId) {\n return null;\n }\n if (this.subscriberKind === 'external') {\n if (!this.subscriberExternalId) {\n return null;\n }\n return {\n kind: 'external',\n tenantId: this.tenantId,\n externalId: this.subscriberExternalId,\n };\n }\n return { kind: 'tenant', tenantId: this.tenantId };\n }\n\n getDimensions(): JsonObject {\n return parseJsonObject(this.dimensions, {});\n }\n\n setDimensions(dimensions: JsonObject): void {\n this.dimensions = stringifyJson(dimensions);\n }\n}\n\nexport default TenantUsageMetric;\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { TenantUsageMetric } from '../models/TenantUsageMetric.js';\nimport type {\n AiUsageSummary,\n RecordUsageOptions,\n SummarizeAiUsageOptions,\n SummarizeUsageBatchOptions,\n SummarizeUsageOptions,\n UsageSummary,\n} from '../types.js';\nimport {\n normalizeSubscriber,\n stringifyJson,\n subscriberToColumns,\n} from '../utils.js';\n\nexport class TenantUsageMetricCollection extends SmrtCollection<TenantUsageMetric> {\n static readonly _itemClass = TenantUsageMetric;\n\n async recordUsage(options: RecordUsageOptions): Promise<TenantUsageMetric> {\n const subscriber = normalizeSubscriber({\n tenantId: options.tenantId,\n subscriberKind: options.subscriberKind,\n subscriberExternalId: options.subscriberExternalId,\n });\n const columns = subscriberToColumns(subscriber);\n const metric = await this.create({\n tenantId: columns.tenantId,\n subscriberKind: columns.subscriberKind,\n subscriberExternalId: columns.subscriberExternalId,\n metricKey: options.metricKey,\n quantity: options.quantity,\n windowStart: options.windowStart,\n windowEnd: options.windowEnd,\n source: options.source ?? '',\n sourceId: options.sourceId ?? '',\n dimensions: stringifyJson(options.dimensions ?? {}),\n });\n return metric;\n }\n\n /**\n * Legacy accessor — finds metrics for `subscriberKind = 'tenant'` under the\n * given tenant. External-kind rows scoped to the same tenant are excluded.\n */\n async findByTenantAndMetric(\n tenantId: string,\n metricKey: string,\n ): Promise<TenantUsageMetric[]> {\n return this.list({\n where: { tenantId, metricKey, subscriberKind: 'tenant' },\n orderBy: 'windowStart DESC',\n });\n }\n\n async summarizeUsage(options: SummarizeUsageOptions): Promise<UsageSummary> {\n const subscriber = normalizeSubscriber({\n tenantId: options.tenantId,\n subscriberKind: options.subscriberKind,\n subscriberExternalId: options.subscriberExternalId,\n });\n const columns = subscriberToColumns(subscriber);\n\n const where: Record<string, unknown> = {\n tenantId: columns.tenantId,\n subscriberKind: columns.subscriberKind,\n metricKey: options.metricKey,\n 'windowStart <': options.window.end.toISOString(),\n 'windowEnd >': options.window.start.toISOString(),\n };\n if (subscriber.kind === 'external') {\n where.subscriberExternalId = columns.subscriberExternalId;\n }\n\n const metrics = await this.list({ where });\n\n const baseSummary: UsageSummary = {\n tenantId: columns.tenantId,\n metricKey: options.metricKey,\n quantity: metrics.reduce((sum, metric) => sum + metric.quantity, 0),\n windowStart: options.window.start,\n windowEnd: options.window.end,\n };\n if (subscriber.kind === 'external') {\n return {\n ...baseSummary,\n subscriberKind: 'external',\n subscriberExternalId: subscriber.externalId,\n };\n }\n return baseSummary;\n }\n\n async summarizeUsageBatch(\n options: SummarizeUsageBatchOptions,\n ): Promise<UsageSummary[]> {\n const metricKeys = Array.from(new Set(options.metricKeys));\n if (metricKeys.length === 0) {\n return [];\n }\n\n const subscriber = normalizeSubscriber({\n tenantId: options.tenantId,\n subscriberKind: options.subscriberKind,\n subscriberExternalId: options.subscriberExternalId,\n });\n const columns = subscriberToColumns(subscriber);\n\n const metricPlaceholders = metricKeys.map(() => '?').join(', ');\n const params: unknown[] = [\n columns.tenantId,\n columns.subscriberKind,\n ...metricKeys,\n options.window.end.toISOString(),\n options.window.start.toISOString(),\n ];\n let subscriberSql = '';\n if (subscriber.kind === 'external') {\n subscriberSql = 'AND subscriber_external_id = ?';\n params.splice(2, 0, columns.subscriberExternalId);\n }\n\n const result = await this.db.query(\n `SELECT\n metric_key,\n COALESCE(SUM(quantity), 0) AS quantity\n FROM _smrt_tenant_usage_metrics\n WHERE tenant_id = ?\n AND subscriber_kind = ?\n ${subscriberSql}\n AND metric_key IN (${metricPlaceholders})\n AND window_start < ?\n AND window_end > ?\n GROUP BY metric_key`,\n ...params,\n );\n\n const quantityByMetric = new Map<string, number>();\n for (const row of resultRows(result)) {\n const metricKey = String(row.metric_key ?? row.metricKey ?? '');\n if (metricKey) {\n quantityByMetric.set(metricKey, numberFromRow(row, 'quantity'));\n }\n }\n\n return metricKeys.map((metricKey) => {\n const baseSummary: UsageSummary = {\n tenantId: columns.tenantId,\n metricKey,\n quantity: quantityByMetric.get(metricKey) ?? 0,\n windowStart: options.window.start,\n windowEnd: options.window.end,\n };\n if (subscriber.kind === 'external') {\n return {\n ...baseSummary,\n subscriberKind: 'external',\n subscriberExternalId: subscriber.externalId,\n };\n }\n return baseSummary;\n });\n }\n\n /**\n * Summarize persisted AI usage for a tenant within a window.\n *\n * Reads the framework `_smrt_ai_usage` system table via a raw aggregate\n * query. This deliberately uses `this.db.query` rather than the inherited\n * `query()`: those rows are framework usage records, not `TenantUsageMetric`\n * instances, so model hydration would discard the aggregate columns. Tenant\n * scoping is applied explicitly through the `tenant_id` filter.\n *\n * Named distinctly from the inherited `SmrtClass.summarizeAiUsage` (which\n * returns grouped stats across all tenants) to avoid overriding it.\n */\n async summarizeTenantAiUsage(\n options: SummarizeAiUsageOptions,\n ): Promise<AiUsageSummary> {\n const result = await this.db.query(\n `SELECT\n COALESCE(SUM(prompt_tokens), 0) AS prompt_tokens,\n COALESCE(SUM(completion_tokens), 0) AS completion_tokens,\n COALESCE(SUM(total_tokens), 0) AS total_tokens,\n COALESCE(SUM(estimated_cost), 0) AS estimated_cost,\n COUNT(*) AS request_count\n FROM _smrt_ai_usage\n WHERE tenant_id = ?\n AND created_at >= ?\n AND created_at < ?`,\n options.tenantId,\n options.window.start.toISOString(),\n options.window.end.toISOString(),\n );\n const row = firstRow(result);\n\n return {\n tenantId: options.tenantId,\n promptTokens: numberFromRow(row, 'prompt_tokens'),\n completionTokens: numberFromRow(row, 'completion_tokens'),\n totalTokens: numberFromRow(row, 'total_tokens'),\n estimatedCost: numberFromRow(row, 'estimated_cost'),\n requestCount: numberFromRow(row, 'request_count'),\n windowStart: options.window.start,\n windowEnd: options.window.end,\n };\n }\n}\n\nfunction firstRow(result: unknown): Record<string, unknown> {\n const rows = resultRows(result);\n return (rows[0] ?? {}) as Record<string, unknown>;\n}\n\nfunction resultRows(result: unknown): Record<string, unknown>[] {\n return Array.isArray(result)\n ? (result as Record<string, unknown>[])\n : ((result as { rows?: Record<string, unknown>[] })?.rows ?? []);\n}\n\nfunction numberFromRow(row: Record<string, unknown>, key: string): number {\n const value = row[key];\n if (typeof value === 'number') {\n return value;\n }\n if (typeof value === 'bigint') {\n return Number(value);\n }\n if (typeof value === 'string') {\n return Number.parseFloat(value) || 0;\n }\n return 0;\n}\n","import type {\n PlanThreshold,\n ThresholdEvaluation,\n UsageSummary,\n} from '../types.js';\n\nexport function evaluateThreshold(\n threshold: PlanThreshold,\n usage: UsageSummary,\n): ThresholdEvaluation {\n const ratio =\n threshold.limit === 0\n ? usage.quantity > 0\n ? Infinity\n : 0\n : usage.quantity / threshold.limit;\n const warningRatio = threshold.warningRatio ?? 0.8;\n const blocked =\n threshold.enforcement === 'block' &&\n (threshold.limit === 0\n ? usage.quantity > 0\n : usage.quantity >= threshold.limit);\n const warned = !blocked && ratio >= warningRatio;\n\n return {\n threshold,\n usage,\n ratio,\n state: blocked ? 'blocked' : warned ? 'warn' : 'ok',\n allowed: !blocked,\n remaining: Math.max(0, threshold.limit - usage.quantity),\n };\n}\n\nexport function evaluateThresholds(\n thresholds: PlanThreshold[],\n usage: UsageSummary[],\n): ThresholdEvaluation[] {\n const usageByMetric = new Map(\n usage.map((summary) => [summary.metricKey, summary]),\n );\n\n return thresholds.map((threshold) => {\n const summary =\n usageByMetric.get(threshold.metricKey) ??\n emptyUsageSummary(threshold.metricKey);\n return evaluateThreshold(threshold, summary);\n });\n}\n\nfunction emptyUsageSummary(metricKey: string): UsageSummary {\n const now = new Date();\n return {\n tenantId: '',\n metricKey,\n quantity: 0,\n windowStart: now,\n windowEnd: now,\n };\n}\n","import type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { TenantUsageMetricCollection } from '../collections/TenantUsageMetricCollection.js';\nimport type {\n AiUsageSummary,\n RecordUsageOptions,\n SummarizeAiUsageOptions,\n SummarizeUsageBatchOptions,\n SummarizeUsageOptions,\n UsageSummary,\n} from '../types.js';\nimport { normalizeSubscriber } from '../utils.js';\n\nexport class TenantUsageMeter {\n constructor(\n private readonly metrics: TenantUsageMetricCollection,\n private readonly classOptions: SmrtClassOptions = {},\n ) {}\n\n static async create(\n classOptions: SmrtClassOptions = {},\n ): Promise<TenantUsageMeter> {\n const metrics = await TenantUsageMetricCollection.create(classOptions);\n return new TenantUsageMeter(metrics, classOptions);\n }\n\n /**\n * Record one usage row.\n *\n * Accepts the polymorphic subscriber fields directly on the options object\n * (`subscriberKind`/`subscriberExternalId`); when omitted defaults to a\n * `'tenant'`-kind row keyed off `tenantId`. The collection layer enforces\n * the XOR invariant — passing `subscriberKind: 'external'` without a\n * non-empty `subscriberExternalId` throws.\n */\n async record(options: RecordUsageOptions): Promise<void> {\n await this.metrics.recordUsage(options);\n }\n\n /**\n * Summarize usage over a window for a given subscriber.\n *\n * The `ai.*` short-circuit only fires for `'tenant'`-kind subscribers since\n * the `_smrt_ai_usage` system table is tenant-scoped; external subscribers\n * fall through to the normal `_smrt_tenant_usage_metrics` aggregation.\n */\n async summarize(options: SummarizeUsageOptions): Promise<UsageSummary> {\n // Normalize at the boundary so the `ai.*` short-circuit and the standard\n // `summarizeUsage` path both refuse the same XOR violations (e.g. caller\n // passes subscriberExternalId without subscriberKind: 'external'). Without\n // this, the AI short-circuit would silently re-scope to tenant-wide\n // _smrt_ai_usage totals.\n normalizeSubscriber({\n tenantId: options.tenantId,\n subscriberKind: options.subscriberKind,\n subscriberExternalId: options.subscriberExternalId,\n });\n\n const aiSummary = await this.trySummarizeAiMetric(options);\n if (aiSummary) {\n return aiSummary;\n }\n\n return this.metrics.summarizeUsage(options);\n }\n\n async summarizeBatch(\n options: SummarizeUsageBatchOptions,\n ): Promise<UsageSummary[]> {\n const subscriber = normalizeSubscriber({\n tenantId: options.tenantId,\n subscriberKind: options.subscriberKind,\n subscriberExternalId: options.subscriberExternalId,\n });\n const metricKeys = Array.from(new Set(options.metricKeys));\n if (metricKeys.length === 0) {\n return [];\n }\n\n const useTenantAiSummary = subscriber.kind === 'tenant';\n const aiMetricKeys = useTenantAiSummary\n ? metricKeys.filter((metricKey) => metricKey.startsWith('ai.'))\n : [];\n const persistedMetricKeys = useTenantAiSummary\n ? metricKeys.filter((metricKey) => !metricKey.startsWith('ai.'))\n : metricKeys;\n\n const summaries = new Map<string, UsageSummary>();\n if (aiMetricKeys.length > 0) {\n const aiSummary = await this.summarizeAiUsage({\n tenantId: options.tenantId,\n window: options.window,\n });\n const quantityByMetric = aiQuantityByMetric(aiSummary);\n for (const metricKey of aiMetricKeys) {\n summaries.set(metricKey, {\n tenantId: options.tenantId,\n metricKey,\n quantity: quantityByMetric[metricKey] ?? 0,\n windowStart: options.window.start,\n windowEnd: options.window.end,\n });\n }\n }\n\n const persistedSummaries = await this.metrics.summarizeUsageBatch({\n ...options,\n metricKeys: persistedMetricKeys,\n });\n for (const summary of persistedSummaries) {\n summaries.set(summary.metricKey, summary);\n }\n\n return metricKeys.map(\n (metricKey) =>\n summaries.get(metricKey) ??\n emptyUsageSummary(subscriber, metricKey, options.window),\n );\n }\n\n async summarizeAiUsage(\n options: SummarizeAiUsageOptions,\n ): Promise<AiUsageSummary> {\n return this.metrics.summarizeTenantAiUsage(options);\n }\n\n getOptions(): SmrtClassOptions {\n return this.classOptions;\n }\n\n private async trySummarizeAiMetric(\n options: SummarizeUsageOptions,\n ): Promise<UsageSummary | null> {\n if (!options.metricKey.startsWith('ai.')) {\n return null;\n }\n // _smrt_ai_usage is tenant-scoped only; external subscribers must use the\n // standard usage path. Skip the short-circuit so the caller's recorded\n // usage rows are summed instead.\n const kind = options.subscriberKind ?? 'tenant';\n if (kind !== 'tenant') {\n return null;\n }\n\n const summary = await this.summarizeAiUsage({\n tenantId: options.tenantId,\n window: options.window,\n });\n\n const quantityByMetric: Record<string, number> = {\n ...aiQuantityByMetric(summary),\n };\n\n return {\n tenantId: options.tenantId,\n metricKey: options.metricKey,\n quantity: quantityByMetric[options.metricKey] ?? 0,\n windowStart: options.window.start,\n windowEnd: options.window.end,\n };\n }\n}\n\nfunction aiQuantityByMetric(summary: AiUsageSummary): Record<string, number> {\n return {\n 'ai.tokens.prompt': summary.promptTokens,\n 'ai.tokens.completion': summary.completionTokens,\n 'ai.tokens.total': summary.totalTokens,\n 'ai.cost.estimated': summary.estimatedCost,\n 'ai.requests': summary.requestCount,\n };\n}\n\nfunction emptyUsageSummary(\n subscriber: ReturnType<typeof normalizeSubscriber>,\n metricKey: string,\n window: SummarizeUsageBatchOptions['window'],\n): UsageSummary {\n const summary: UsageSummary = {\n tenantId: subscriber.tenantId,\n metricKey,\n quantity: 0,\n windowStart: window.start,\n windowEnd: window.end,\n };\n if (subscriber.kind === 'external') {\n return {\n ...summary,\n subscriberKind: 'external',\n subscriberExternalId: subscriber.externalId,\n };\n }\n return summary;\n}\n","import { SubscriptionPlanCollection } from '../collections/SubscriptionPlanCollection.js';\nimport { TenantSubscriptionCollection } from '../collections/TenantSubscriptionCollection.js';\nimport type { SubscriptionPlan } from '../models/SubscriptionPlan.js';\nimport type { TenantSubscription } from '../models/TenantSubscription.js';\nimport type {\n EntitlementResolution,\n EntitlementResolutionContext,\n PlanThreshold,\n SmrtClassOptions,\n Subscriber,\n SubscriptionResolverOptions,\n ThresholdEvaluation,\n UsageSummary,\n UsageWindow,\n} from '../types.js';\nimport {\n getWindowForThreshold,\n getWindowKey,\n isValidThreshold,\n} from '../utils.js';\nimport { evaluateThreshold } from './threshold-evaluator.js';\nimport { TenantUsageMeter } from './usage-meter.js';\n\nexport interface SubscriptionPlanReader {\n get(criteria: { id: string }): Promise<SubscriptionPlan | null>;\n}\n\n/**\n * Reader contract for finding the current subscription for a subscriber.\n *\n * The legacy `findCurrentForTenant(tenantId)` signature stays for backward\n * compatibility with the original tenant-only resolver. New implementations\n * should provide `findCurrentForSubscriber(subscriber)` — it's preferred when\n * present and lets the resolver work with both `'tenant'` and `'external'`\n * subscribers without callers having to pre-narrow the discriminator.\n */\nexport interface TenantSubscriptionReader {\n findCurrentForTenant(\n tenantId: string,\n now?: Date,\n ): Promise<TenantSubscription | null>;\n findCurrentForSubscriber?(\n subscriber: Subscriber,\n now?: Date,\n ): Promise<TenantSubscription | null>;\n}\n\n/**\n * Reader contract for summarizing usage.\n *\n * `summarize(options)` already accepts the polymorphic\n * `subscriberKind`/`subscriberExternalId` fields. When omitted the reader\n * defaults to `'tenant'`, which is what existing callers got before this\n * change — no behavior shift for them. The discriminator type is derived\n * from `Subscriber['kind']` rather than inlined so any future additions to\n * the union stay consistent across the package.\n */\nexport interface UsageSummaryReader {\n summarize(options: {\n tenantId: string;\n subscriberKind?: Subscriber['kind'];\n subscriberExternalId?: string;\n metricKey: string;\n window: UsageWindow;\n }): Promise<UsageSummary>;\n summarizeBatch?(options: {\n tenantId: string;\n subscriberKind?: Subscriber['kind'];\n subscriberExternalId?: string;\n metricKeys: string[];\n window: UsageWindow;\n }): Promise<UsageSummary[]>;\n}\n\nexport interface SubscriptionResolverReaders {\n plans: SubscriptionPlanReader;\n subscriptions: TenantSubscriptionReader;\n usage: UsageSummaryReader;\n}\n\nexport class SubscriptionResolver {\n constructor(private readonly readers: SubscriptionResolverReaders) {}\n\n /**\n * Build the default resolver readers once for a request or app lifecycle and\n * reuse the returned resolver across entitlement checks.\n */\n static async create(\n classOptions: SmrtClassOptions = {},\n ): Promise<SubscriptionResolver> {\n const [plans, subscriptions, usage] = await Promise.all([\n SubscriptionPlanCollection.create(classOptions),\n TenantSubscriptionCollection.create(classOptions),\n TenantUsageMeter.create(classOptions),\n ]);\n return new SubscriptionResolver({ plans, subscriptions, usage });\n }\n\n /**\n * Load the subscription/plan pair used by entitlement resolution. Callers\n * that need both the entitlement snapshot and the backing records can load\n * this once, then pass it back via `options.context`.\n */\n async loadEntitlementContext(\n subscriberOrTenantId: Subscriber | string,\n options: SubscriptionResolverOptions = {},\n ): Promise<EntitlementResolutionContext> {\n const subscriber = toSubscriber(subscriberOrTenantId);\n const now = options.now ?? new Date();\n const subscription = await this.resolveSubscription(\n subscriber,\n now,\n options.context,\n );\n const plan = await this.resolvePlan(subscription, options.context);\n return { subscription, plan };\n }\n\n /**\n * Polymorphic entitlement resolution. Works for both `'tenant'`-kind and\n * `'external'`-kind subscribers and is the preferred surface — the\n * `resolveTenantEntitlements(tenantId)` method below is a thin wrapper.\n */\n async resolveEntitlements(\n subscriber: Subscriber,\n options: SubscriptionResolverOptions = {},\n ): Promise<EntitlementResolution> {\n const now = options.now ?? new Date();\n const { subscription, plan } = await this.loadEntitlementContext(\n subscriber,\n { ...options, now },\n );\n\n if (!subscription) {\n return emptyResolution(subscriber);\n }\n\n if (!plan || !plan.isActive() || !subscription.isEntitled(now)) {\n return {\n ...emptyResolution(subscriber),\n planId: plan?.id ?? subscription.planId ?? null,\n planKey: plan?.planKey ?? null,\n subscriptionId: subscription.id ?? null,\n status: subscription.status,\n };\n }\n\n const thresholds = plan.getThresholds().filter(isValidThreshold);\n const thresholdEvaluations = await this.resolveThresholdEvaluations(\n subscriber,\n thresholds,\n now,\n options,\n );\n\n return {\n tenantId: subscriber.tenantId,\n subscriber,\n planId: plan.id ?? null,\n planKey: plan.planKey,\n subscriptionId: subscription.id ?? null,\n status: subscription.status,\n featureKeys: plan.getFeatureKeys(),\n thresholds,\n thresholdEvaluations,\n allowed: thresholdEvaluations.every((evaluation) => evaluation.allowed),\n };\n }\n\n /**\n * Legacy single-tenant wrapper around {@link resolveEntitlements}. Kept so\n * existing tenant-only callers don't need to update their call sites.\n */\n async resolveTenantEntitlements(\n tenantId: string,\n options: SubscriptionResolverOptions = {},\n ): Promise<EntitlementResolution> {\n return this.resolveEntitlements({ kind: 'tenant', tenantId }, options);\n }\n\n async isFeatureEnabled(\n subscriberOrTenantId: Subscriber | string,\n featureKey: string,\n options: SubscriptionResolverOptions = {},\n ): Promise<boolean> {\n const resolution = await this.resolveEntitlements(\n toSubscriber(subscriberOrTenantId),\n options,\n );\n return resolution.featureKeys.includes(featureKey);\n }\n\n async assertWithinThresholds(\n subscriberOrTenantId: Subscriber | string,\n options: SubscriptionResolverOptions = {},\n ): Promise<void> {\n const subscriber = toSubscriber(subscriberOrTenantId);\n const resolution = await this.resolveEntitlements(subscriber, options);\n const blocked = resolution.thresholdEvaluations.find(\n (evaluation) => !evaluation.allowed,\n );\n\n if (blocked) {\n const subject =\n subscriber.kind === 'external'\n ? `external:${subscriber.externalId}`\n : `Tenant ${subscriber.tenantId}`;\n throw new Error(\n `${subject} exceeded subscription threshold ${blocked.threshold.metricKey}`,\n );\n }\n }\n\n private async resolveSubscription(\n subscriber: Subscriber,\n now: Date,\n context?: EntitlementResolutionContext,\n ): Promise<TenantSubscription | null> {\n if (hasContextValue(context, 'subscription')) {\n const subscription = context?.subscription ?? null;\n assertSubscriptionMatchesSubscriber(subscription, subscriber);\n return subscription;\n }\n return this.findCurrentSubscription(subscriber, now);\n }\n\n private async resolvePlan(\n subscription: TenantSubscription | null,\n context?: EntitlementResolutionContext,\n ): Promise<SubscriptionPlan | null> {\n if (!subscription?.planId) {\n return null;\n }\n if (hasContextValue(context, 'plan')) {\n const plan = context?.plan ?? null;\n assertPlanMatchesSubscription(plan, subscription);\n return plan;\n }\n return this.readers.plans.get({ id: subscription.planId });\n }\n\n /**\n * Bridge the legacy and polymorphic reader contracts.\n *\n * Prefers `findCurrentForSubscriber` when the reader provides it (the\n * preferred shape). For `'tenant'` subscribers we fall back to the legacy\n * `findCurrentForTenant`. For `'external'` subscribers the reader MUST\n * implement `findCurrentForSubscriber` — otherwise we have no way to scope\n * the lookup and we throw rather than silently returning the tenant's\n * primary subscription.\n */\n private async findCurrentSubscription(\n subscriber: Subscriber,\n now: Date,\n ): Promise<TenantSubscription | null> {\n if (this.readers.subscriptions.findCurrentForSubscriber) {\n return this.readers.subscriptions.findCurrentForSubscriber(\n subscriber,\n now,\n );\n }\n if (subscriber.kind === 'tenant') {\n return this.readers.subscriptions.findCurrentForTenant(\n subscriber.tenantId,\n now,\n );\n }\n throw new Error(\n 'External-subscriber resolution requires a TenantSubscriptionReader ' +\n 'that implements findCurrentForSubscriber()',\n );\n }\n\n private async resolveThresholdEvaluations(\n subscriber: Subscriber,\n thresholds: PlanThreshold[],\n now: Date,\n options: SubscriptionResolverOptions,\n ): Promise<ThresholdEvaluation[]> {\n if (thresholds.length === 0) {\n return [];\n }\n\n if (!this.readers.usage.summarizeBatch) {\n const evaluations: ThresholdEvaluation[] = [];\n for (const threshold of thresholds) {\n const window =\n options.usageWindows?.[threshold.window] ??\n getWindowForThreshold(threshold.window, now);\n const usage = await this.readers.usage.summarize({\n tenantId: subscriber.tenantId,\n subscriberKind: subscriber.kind,\n subscriberExternalId:\n subscriber.kind === 'external' ? subscriber.externalId : undefined,\n metricKey: threshold.metricKey,\n window,\n });\n evaluations.push(evaluateThreshold(threshold, usage));\n }\n return evaluations;\n }\n\n const groups = new Map<\n string,\n {\n window: UsageWindow;\n entries: Array<{ index: number; threshold: PlanThreshold }>;\n }\n >();\n\n thresholds.forEach((threshold, index) => {\n const window =\n options.usageWindows?.[threshold.window] ??\n getWindowForThreshold(threshold.window, now);\n const key = getWindowKey(window);\n const group = groups.get(key) ?? { window, entries: [] };\n group.entries.push({ index, threshold });\n groups.set(key, group);\n });\n\n const evaluations: ThresholdEvaluation[] = new Array(thresholds.length);\n await Promise.all(\n Array.from(groups.values()).map(async ({ window, entries }) => {\n const metricKeys = uniqueMetricKeys(\n entries.map((entry) => entry.threshold.metricKey),\n );\n const summaries = await this.readers.usage.summarizeBatch?.({\n tenantId: subscriber.tenantId,\n subscriberKind: subscriber.kind,\n subscriberExternalId:\n subscriber.kind === 'external' ? subscriber.externalId : undefined,\n metricKeys,\n window,\n });\n const summaryByMetric = new Map(\n (summaries ?? []).map((summary) => [summary.metricKey, summary]),\n );\n\n for (const { index, threshold } of entries) {\n const usage =\n summaryByMetric.get(threshold.metricKey) ??\n emptyUsageSummary(subscriber, threshold.metricKey, window);\n evaluations[index] = evaluateThreshold(threshold, usage);\n }\n }),\n );\n\n return evaluations;\n }\n}\n\nfunction toSubscriber(input: Subscriber | string): Subscriber {\n if (typeof input === 'string') {\n return { kind: 'tenant', tenantId: input };\n }\n return input;\n}\n\nfunction emptyResolution(subscriber: Subscriber): EntitlementResolution {\n return {\n tenantId: subscriber.tenantId,\n subscriber,\n planId: null,\n planKey: null,\n subscriptionId: null,\n status: 'none',\n featureKeys: [],\n thresholds: [],\n thresholdEvaluations: [],\n allowed: false,\n };\n}\n\nfunction hasContextValue<K extends keyof EntitlementResolutionContext>(\n context: EntitlementResolutionContext | undefined,\n key: K,\n): boolean {\n return context?.[key] !== undefined;\n}\n\nfunction assertSubscriptionMatchesSubscriber(\n subscription: TenantSubscription | null,\n subscriber: Subscriber,\n): void {\n if (!subscription) {\n return;\n }\n const subscriptionSubscriber = subscription.getSubscriber();\n if (\n !subscriptionSubscriber ||\n !sameSubscriber(subscriptionSubscriber, subscriber)\n ) {\n throw new Error(\n 'Provided entitlement context subscription does not match requested subscriber',\n );\n }\n}\n\nfunction assertPlanMatchesSubscription(\n plan: SubscriptionPlan | null,\n subscription: TenantSubscription,\n): void {\n if (!plan) {\n return;\n }\n if (!plan.id || plan.id !== subscription.planId) {\n throw new Error(\n 'Provided entitlement context plan does not match subscription.planId',\n );\n }\n}\n\nfunction sameSubscriber(left: Subscriber, right: Subscriber): boolean {\n if (left.kind !== right.kind || left.tenantId !== right.tenantId) {\n return false;\n }\n if (left.kind === 'tenant') {\n return true;\n }\n return right.kind === 'external' && left.externalId === right.externalId;\n}\n\nfunction uniqueMetricKeys(metricKeys: string[]): string[] {\n return Array.from(new Set(metricKeys));\n}\n\nfunction emptyUsageSummary(\n subscriber: Subscriber,\n metricKey: string,\n window: UsageWindow,\n): UsageSummary {\n const summary: UsageSummary = {\n tenantId: subscriber.tenantId,\n metricKey,\n quantity: 0,\n windowStart: window.start,\n windowEnd: window.end,\n };\n if (subscriber.kind === 'external') {\n return {\n ...summary,\n subscriberKind: 'external',\n subscriberExternalId: subscriber.externalId,\n };\n }\n return summary;\n}\n\nexport type { PlanThreshold };\n"],"names":["__decorateClass","tenantId","emptyUsageSummary","evaluations"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/__smrt-register__.ts","../src/utils.ts","../src/models/SubscriptionPlan.ts","../src/collections/SubscriptionPlanCollection.ts","../src/models/TenantSubscription.ts","../src/collections/TenantSubscriptionCollection.ts","../src/models/TenantUsageMetric.ts","../src/collections/TenantUsageMetricCollection.ts","../src/services/threshold-evaluator.ts","../src/services/usage-meter.ts","../src/services/subscription-resolver.ts"],"sourcesContent":["import { ObjectRegistry } from '@happyvertical/smrt-core';\n\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","import type {\n JsonObject,\n PlanFeatureGrant,\n PlanThreshold,\n Subscriber,\n SubscriberKind,\n ThresholdEnforcement,\n ThresholdWindow,\n UsageWindow,\n} from './types.js';\n\nconst THRESHOLD_WINDOWS: ThresholdWindow[] = [\n 'day',\n 'week',\n 'month',\n 'year',\n 'rolling',\n];\nconst THRESHOLD_ENFORCEMENTS: ThresholdEnforcement[] = [\n 'observe',\n 'warn',\n 'block',\n];\n\nexport function parseJsonArray<T>(value: string, fallback: T[] = []): T[] {\n if (!value) {\n return fallback;\n }\n\n try {\n const parsed = JSON.parse(value) as unknown;\n return Array.isArray(parsed) ? (parsed as T[]) : fallback;\n } catch {\n return fallback;\n }\n}\n\nexport function parseJsonObject<T extends JsonObject>(\n value: string,\n fallback: T,\n): T {\n if (!value) {\n return fallback;\n }\n\n try {\n const parsed = JSON.parse(value) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as T)\n : fallback;\n } catch {\n return fallback;\n }\n}\n\nexport function stringifyJson(value: unknown): string {\n return JSON.stringify(value ?? null);\n}\n\nexport function normalizeFeatureGrants(\n grants: Array<string | PlanFeatureGrant>,\n): PlanFeatureGrant[] {\n return grants.map((grant) =>\n typeof grant === 'string' ? { featureKey: grant, enabled: true } : grant,\n );\n}\n\n/**\n * Coerce inputs from the legacy tenant-only API into a {@link Subscriber}.\n *\n * Existing callers that pass only `tenantId` (with optional `subscriberKind` /\n * `subscriberExternalId` fields, e.g. on `RecordUsageOptions`) get normalized\n * into the discriminated union exactly once at the boundary. This is the only\n * place that contains \"if kind is omitted, default to tenant\" logic — every\n * other site works with a typed `Subscriber`.\n *\n * Throws when:\n * - `subscriberKind === 'external'` is requested without a non-empty\n * `subscriberExternalId` (the XOR invariant is the whole point), or\n * - the input carries a non-empty `subscriberExternalId` but the kind resolves\n * to `'tenant'` (silent re-scoping would write buyer-scoped usage as tenant\n * usage, which then disappears from external summaries).\n */\nexport function normalizeSubscriber(input: {\n tenantId: string;\n subscriberKind?: SubscriberKind | null;\n subscriberExternalId?: string | null;\n}): Subscriber {\n // Treat null/undefined external ids as \"absent\" — but only after the\n // discriminator-derived branch decides what to do. We never coerce null\n // into the persisted column because the invariant on the model is\n // re-checked at save time via assertSubscriberInvariant.\n const rawExternalId = input.subscriberExternalId;\n const externalIdAbsent =\n rawExternalId === null || rawExternalId === undefined;\n const externalId = externalIdAbsent ? '' : String(rawExternalId);\n\n const rawKind = input.subscriberKind;\n const kind: SubscriberKind =\n rawKind === null || rawKind === undefined ? 'tenant' : rawKind;\n\n if (kind !== 'tenant' && kind !== 'external') {\n throw new Error(\n `subscriberKind must be \"tenant\" or \"external\" (got ${JSON.stringify(rawKind)})`,\n );\n }\n\n if (kind === 'tenant') {\n if (externalId !== '') {\n throw new Error(\n 'subscriberExternalId is set but subscriberKind is \"tenant\"; ' +\n 'set subscriberKind=\"external\" explicitly or omit subscriberExternalId',\n );\n }\n return { kind: 'tenant', tenantId: input.tenantId };\n }\n if (externalId === '') {\n throw new Error(\n 'subscriberKind=\"external\" requires a non-empty subscriberExternalId',\n );\n }\n return {\n kind: 'external',\n tenantId: input.tenantId,\n externalId,\n };\n}\n\n/**\n * Project a {@link Subscriber} back onto column values for persistence or query\n * filters. Counterpart to {@link normalizeSubscriber}.\n */\nexport function subscriberToColumns(subscriber: Subscriber): {\n tenantId: string;\n subscriberKind: SubscriberKind;\n subscriberExternalId: string;\n} {\n if (subscriber.kind === 'tenant') {\n return {\n tenantId: subscriber.tenantId,\n subscriberKind: 'tenant',\n subscriberExternalId: '',\n };\n }\n return {\n tenantId: subscriber.tenantId,\n subscriberKind: 'external',\n subscriberExternalId: subscriber.externalId,\n };\n}\n\n/**\n * Enforce the subscriber XOR invariant on a row's columns. Used by both the\n * model constructors (catches construction-time mistakes) and the\n * `validateBeforeSave` override (catches mutations applied via the\n * generated PUT/PATCH update path that bypass the constructor).\n *\n * Defensive about types because both call sites can receive JSON or untyped\n * fixture data via the generated REST/CLI surface — `null` arriving in place\n * of `''` would otherwise slip past an `=== ''` check, land in the\n * `(tenant_id, subscriber_kind, subscriber_external_id)` conflict key, and\n * (on Postgres) not collide with other NULLs while `findCurrentForSubscriber`\n * keeps querying by a string external id.\n *\n * @param modelName - Prepended to error messages so callers can tell whether\n * the failure originated in `TenantSubscription` or `TenantUsageMetric`.\n */\nexport function assertSubscriberInvariant(\n modelName: string,\n fields: {\n subscriberKind: unknown;\n subscriberExternalId: unknown;\n },\n): void {\n if (\n fields.subscriberKind !== 'tenant' &&\n fields.subscriberKind !== 'external'\n ) {\n throw new Error(\n `${modelName}: subscriberKind must be \"tenant\" or \"external\" (got ${describe(\n fields.subscriberKind,\n )})`,\n );\n }\n if (typeof fields.subscriberExternalId !== 'string') {\n throw new Error(\n `${modelName}: subscriberExternalId must be a string (got ${describe(\n fields.subscriberExternalId,\n )})`,\n );\n }\n\n if (\n fields.subscriberKind === 'tenant' &&\n fields.subscriberExternalId !== ''\n ) {\n throw new Error(\n `${modelName}: subscriberExternalId must be empty when subscriberKind is \"tenant\"`,\n );\n }\n if (\n fields.subscriberKind === 'external' &&\n fields.subscriberExternalId === ''\n ) {\n throw new Error(\n `${modelName}: subscriberKind=\"external\" requires a non-empty subscriberExternalId`,\n );\n }\n}\n\nfunction describe(value: unknown): string {\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n if (typeof value === 'string') return `\"${value}\"`;\n return String(value);\n}\n\nexport function getWindowForThreshold(\n thresholdWindow: ThresholdWindow,\n now = new Date(),\n): UsageWindow {\n switch (thresholdWindow) {\n case 'day':\n return utcWindow(now, 'day');\n case 'week':\n return utcWindow(now, 'week');\n case 'month':\n return utcWindow(now, 'month');\n case 'year':\n return utcWindow(now, 'year');\n case 'rolling':\n return {\n start: new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000),\n end: now,\n };\n }\n}\n\nexport function getWindowKey(window: UsageWindow): string {\n return `${window.start.toISOString()}..${window.end.toISOString()}`;\n}\n\nexport function isValidThreshold(threshold: PlanThreshold): boolean {\n return (\n typeof threshold.metricKey === 'string' &&\n threshold.metricKey.length > 0 &&\n Number.isFinite(threshold.limit) &&\n threshold.limit >= 0 &&\n isThresholdWindow(threshold.window) &&\n isThresholdEnforcement(threshold.enforcement) &&\n (threshold.warningRatio === undefined ||\n (Number.isFinite(threshold.warningRatio) &&\n threshold.warningRatio >= 0 &&\n threshold.warningRatio <= 1))\n );\n}\n\nfunction isThresholdWindow(value: unknown): value is ThresholdWindow {\n return (\n typeof value === 'string' &&\n THRESHOLD_WINDOWS.includes(value as ThresholdWindow)\n );\n}\n\nfunction isThresholdEnforcement(value: unknown): value is ThresholdEnforcement {\n return (\n typeof value === 'string' &&\n THRESHOLD_ENFORCEMENTS.includes(value as ThresholdEnforcement)\n );\n}\n\nfunction utcWindow(now: Date, unit: 'day' | 'week' | 'month' | 'year') {\n const start = new Date(\n Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()),\n );\n\n if (unit === 'week') {\n const day = start.getUTCDay();\n const mondayOffset = day === 0 ? -6 : 1 - day;\n start.setUTCDate(start.getUTCDate() + mondayOffset);\n }\n\n if (unit === 'month') {\n start.setUTCDate(1);\n }\n\n if (unit === 'year') {\n start.setUTCMonth(0, 1);\n }\n\n const end = new Date(start);\n switch (unit) {\n case 'day':\n end.setUTCDate(end.getUTCDate() + 1);\n break;\n case 'week':\n end.setUTCDate(end.getUTCDate() + 7);\n break;\n case 'month':\n end.setUTCMonth(end.getUTCMonth() + 1);\n break;\n case 'year':\n end.setUTCFullYear(end.getUTCFullYear() + 1);\n break;\n }\n\n return { start, end };\n}\n","import { SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n BillingInterval,\n JsonObject,\n PlanFeatureGrant,\n PlanThreshold,\n SubscriptionPlanOptions,\n SubscriptionPlanStatus,\n} from '../types.js';\nimport {\n normalizeFeatureGrants,\n parseJsonArray,\n parseJsonObject,\n stringifyJson,\n} from '../utils.js';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: '_smrt_subscription_plans',\n api: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n mcp: { include: ['list', 'get'] },\n conflictColumns: ['plan_key'],\n})\nexport class SubscriptionPlan extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n planKey: string = '';\n name: string = '';\n description: string = '';\n status: SubscriptionPlanStatus = 'active';\n sortOrder: number = 0;\n priceAmount: number = 0.0;\n currency: string = 'USD';\n billingInterval: BillingInterval = 'month';\n externalProvider: string = 'stripe';\n stripeProductId: string = '';\n stripePriceId: string = '';\n features: string = '[]';\n thresholds: string = '[]';\n metadata: string = '{}';\n\n constructor(options: SubscriptionPlanOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.planKey !== undefined) this.planKey = options.planKey;\n if (options.name !== undefined) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.status !== undefined) this.status = options.status;\n if (options.sortOrder !== undefined) this.sortOrder = options.sortOrder;\n if (options.priceAmount !== undefined)\n this.priceAmount = options.priceAmount;\n if (options.currency !== undefined) this.currency = options.currency;\n if (options.billingInterval !== undefined) {\n this.billingInterval = options.billingInterval;\n }\n if (options.externalProvider !== undefined) {\n this.externalProvider = options.externalProvider;\n }\n if (options.stripeProductId !== undefined) {\n this.stripeProductId = options.stripeProductId;\n }\n if (options.stripePriceId !== undefined) {\n this.stripePriceId = options.stripePriceId;\n }\n if (options.features !== undefined) this.features = options.features;\n if (options.thresholds !== undefined) this.thresholds = options.thresholds;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n isActive(): boolean {\n return this.status === 'active';\n }\n\n getFeatureGrants(): PlanFeatureGrant[] {\n return normalizeFeatureGrants(\n parseJsonArray<string | PlanFeatureGrant>(this.features),\n );\n }\n\n setFeatureGrants(grants: Array<string | PlanFeatureGrant>): void {\n this.features = stringifyJson(normalizeFeatureGrants(grants));\n }\n\n getFeatureKeys(): string[] {\n return this.getFeatureGrants()\n .filter((grant) => grant.enabled !== false)\n .map((grant) => grant.featureKey);\n }\n\n getThresholds(): PlanThreshold[] {\n return parseJsonArray<PlanThreshold>(this.thresholds).filter(\n (threshold) => threshold.metricKey,\n );\n }\n\n setThresholds(thresholds: PlanThreshold[]): void {\n this.thresholds = stringifyJson(thresholds);\n }\n\n getMetadata(): JsonObject {\n return parseJsonObject(this.metadata, {});\n }\n\n setMetadata(metadata: JsonObject): void {\n this.metadata = stringifyJson(metadata);\n }\n}\n\nexport default SubscriptionPlan;\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { SubscriptionPlan } from '../models/SubscriptionPlan.js';\n\nexport class SubscriptionPlanCollection extends SmrtCollection<SubscriptionPlan> {\n static readonly _itemClass = SubscriptionPlan;\n\n async findByPlanKey(planKey: string): Promise<SubscriptionPlan | null> {\n const plans = await this.list({ where: { planKey }, limit: 1 });\n return plans[0] ?? null;\n }\n\n async findActive(): Promise<SubscriptionPlan[]> {\n return this.list({\n where: { status: 'active' },\n orderBy: 'sortOrder ASC',\n });\n }\n\n async findByStripePriceId(\n stripePriceId: string,\n ): Promise<SubscriptionPlan | null> {\n const plans = await this.list({ where: { stripePriceId }, limit: 1 });\n return plans[0] ?? null;\n }\n}\n","import { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n JsonObject,\n Subscriber,\n SubscriberKind,\n SubscriptionStatus,\n TenantSubscriptionOptions,\n} from '../types.js';\nimport {\n assertSubscriberInvariant,\n parseJsonObject,\n stringifyJson,\n} from '../utils.js';\n\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: '_smrt_tenant_subscriptions',\n api: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n mcp: { include: ['list', 'get'] },\n // The conflict key includes the subscriber discriminator so an issuing tenant\n // can host many distinct external subscribers without collisions on the\n // unique index:\n // - tenant-kind rows always have subscriber_external_id = '' → uniqueness\n // reduces to (tenant_id, 'tenant', '') = at most one tenant-kind row per\n // tenant (preserves the pre-polymorphic invariant).\n // - external-kind rows are unique on (tenant_id, 'external', externalId) =\n // at most one active subscription per (issuer, external subscriber).\n //\n // UPGRADE NOTE: databases created against the pre-#1454 conflict key\n // ['tenant_id'] still carry the legacy `_smrt_tenant_subscriptions_tenant_id_idx`.\n // `smrt db:migrate` does not sweep orphan indexes by default, so the legacy\n // index persists and continues to reject external subscriptions until it's\n // dropped. Run scripts/migrate-1454-drop-legacy-conflict-index.ts once on\n // upgrade, or pass `--drop-indexes` to `db:migrate`. Fresh databases need\n // neither — the generated schema already reflects the new key.\n conflictColumns: ['tenant_id', 'subscriber_kind', 'subscriber_external_id'],\n})\nexport class TenantSubscription extends SmrtObject {\n @tenantId()\n tenantId?: string;\n\n /**\n * Discriminator for the subscriber identity.\n *\n * - `'tenant'` (default): the subscriber IS the owning `tenantId` — the\n * pre-polymorphic shape. All existing rows continue to behave this way.\n * - `'external'`: the subscriber is `subscriberExternalId`, scoped under the\n * issuing `tenantId`. Used for B2C buyers, anonymous-email subscribers,\n * agent identities, and any other caller-defined identity.\n */\n subscriberKind: SubscriberKind = 'tenant';\n\n /**\n * Caller-namespaced opaque identifier for the subscriber when\n * `subscriberKind === 'external'`. Empty string when kind is `'tenant'`.\n *\n * The package treats this as opaque — no FK, no inferred semantics. Callers\n * are expected to namespace (e.g. `buyer-contact:abc123`,\n * `agent:hermes-7`, `email:foo@example.com`).\n */\n subscriberExternalId: string = '';\n\n @foreignKey('SubscriptionPlan')\n planId: string = '';\n\n status: SubscriptionStatus = 'incomplete';\n startedAt: Date = new Date();\n currentPeriodStart: Date | null = null;\n currentPeriodEnd: Date | null = null;\n trialEndsAt: Date | null = null;\n cancelAtPeriodEnd: boolean = false;\n canceledAt: Date | null = null;\n externalProvider: string = 'stripe';\n stripeCustomerId: string = '';\n stripeSubscriptionId: string = '';\n stripeCheckoutSessionId: string = '';\n metadata: string = '{}';\n\n constructor(options: TenantSubscriptionOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.subscriberKind !== undefined)\n this.subscriberKind = options.subscriberKind;\n if (options.subscriberExternalId !== undefined)\n this.subscriberExternalId = options.subscriberExternalId;\n // Enforce the subscriber XOR invariant at construction time so callers\n // catch mistakes early. The same check also runs in `validateBeforeSave`\n // below so the generated PUT/PATCH update path — which mutates an existing\n // instance via `Object.assign()` and bypasses this constructor — can't\n // sneak a malformed row past us either.\n assertSubscriberInvariant('TenantSubscription', {\n subscriberKind: this.subscriberKind,\n subscriberExternalId: this.subscriberExternalId,\n });\n if (options.planId !== undefined) this.planId = options.planId;\n if (options.status !== undefined) this.status = options.status;\n if (options.startedAt !== undefined) this.startedAt = options.startedAt;\n if (options.currentPeriodStart !== undefined) {\n this.currentPeriodStart = options.currentPeriodStart;\n }\n if (options.currentPeriodEnd !== undefined) {\n this.currentPeriodEnd = options.currentPeriodEnd;\n }\n if (options.trialEndsAt !== undefined)\n this.trialEndsAt = options.trialEndsAt;\n if (options.cancelAtPeriodEnd !== undefined) {\n this.cancelAtPeriodEnd = options.cancelAtPeriodEnd;\n }\n if (options.canceledAt !== undefined) this.canceledAt = options.canceledAt;\n if (options.externalProvider !== undefined) {\n this.externalProvider = options.externalProvider;\n }\n if (options.stripeCustomerId !== undefined) {\n this.stripeCustomerId = options.stripeCustomerId;\n }\n if (options.stripeSubscriptionId !== undefined) {\n this.stripeSubscriptionId = options.stripeSubscriptionId;\n }\n if (options.stripeCheckoutSessionId !== undefined) {\n this.stripeCheckoutSessionId = options.stripeCheckoutSessionId;\n }\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n\n /**\n * Validates the subscriber XOR invariant before every save. Critical for the\n * generated update path: `SmrtCollection.update()` mutates the existing\n * instance via `Object.assign()` and calls `save()` directly — the\n * constructor never re-runs, so without this hook a partial update like\n * `{ subscriberExternalId: 'buyer:alice' }` on a tenant-kind row would\n * persist a malformed conflict key and let two tenant-kind rows coexist\n * under the same tenant.\n */\n protected async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n assertSubscriberInvariant('TenantSubscription', {\n subscriberKind: this.subscriberKind,\n subscriberExternalId: this.subscriberExternalId,\n });\n }\n\n isEntitled(now = new Date()): boolean {\n if (this.status !== 'active' && this.status !== 'trialing') {\n return false;\n }\n if (\n this.currentPeriodEnd &&\n this.currentPeriodEnd.getTime() < now.getTime()\n ) {\n return false;\n }\n return true;\n }\n\n /**\n * Project this row's polymorphic subscriber columns onto the\n * {@link Subscriber} discriminated union. Returns `null` when the owning\n * tenant is absent — that's an invalid row that should not be acted on.\n */\n getSubscriber(): Subscriber | null {\n if (!this.tenantId) {\n return null;\n }\n if (this.subscriberKind === 'external') {\n if (!this.subscriberExternalId) {\n return null;\n }\n return {\n kind: 'external',\n tenantId: this.tenantId,\n externalId: this.subscriberExternalId,\n };\n }\n return { kind: 'tenant', tenantId: this.tenantId };\n }\n\n getMetadata(): JsonObject {\n return parseJsonObject(this.metadata, {});\n }\n\n setMetadata(metadata: JsonObject): void {\n this.metadata = stringifyJson(metadata);\n }\n}\n\nexport default TenantSubscription;\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { TenantSubscription } from '../models/TenantSubscription.js';\nimport type { Subscriber } from '../types.js';\n\nexport class TenantSubscriptionCollection extends SmrtCollection<TenantSubscription> {\n static readonly _itemClass = TenantSubscription;\n\n /**\n * List subscriptions whose owning tenant scope is `tenantId`. Includes both\n * `'tenant'`-kind (subscriber == owner) and `'external'`-kind (subscriber is\n * an external identity scoped under this tenant) rows.\n */\n async findByTenant(tenantId: string): Promise<TenantSubscription[]> {\n return this.list({\n where: { tenantId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Legacy single-tenant accessor: returns the current subscription where the\n * tenant IS the subscriber (`subscriberKind = 'tenant'`). External-kind rows\n * scoped under this tenant are intentionally excluded so existing callers\n * are not surprised by buyer/agent subscriptions.\n */\n async findCurrentForTenant(\n tenantId: string,\n now = new Date(),\n ): Promise<TenantSubscription | null> {\n return this.findCurrentForSubscriber({ kind: 'tenant', tenantId }, now);\n }\n\n /**\n * Polymorphic current-subscription accessor.\n *\n * For `'tenant'` subscribers we match rows with `subscriberKind = 'tenant'`\n * under the same `tenantId`. For `'external'` subscribers we match by\n * `(tenantId, subscriberExternalId)` with `subscriberKind = 'external'`.\n */\n async findCurrentForSubscriber(\n subscriber: Subscriber,\n now = new Date(),\n ): Promise<TenantSubscription | null> {\n const where: Record<string, unknown> = {\n tenantId: subscriber.tenantId,\n subscriberKind: subscriber.kind,\n };\n if (subscriber.kind === 'external') {\n where.subscriberExternalId = subscriber.externalId;\n }\n\n const subscriptions = await this.list({\n where,\n orderBy: 'created_at DESC',\n });\n return (\n subscriptions.find((subscription) => subscription.isEntitled(now)) ??\n subscriptions[0] ??\n null\n );\n }\n\n async findByStripeSubscriptionId(\n stripeSubscriptionId: string,\n ): Promise<TenantSubscription | null> {\n const subscriptions = await this.list({\n where: { stripeSubscriptionId },\n limit: 1,\n });\n return subscriptions[0] ?? null;\n }\n}\n","import { SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n JsonObject,\n Subscriber,\n SubscriberKind,\n TenantUsageMetricOptions,\n} from '../types.js';\nimport {\n assertSubscriberInvariant,\n parseJsonObject,\n stringifyJson,\n} from '../utils.js';\n\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: '_smrt_tenant_usage_metrics',\n api: { include: ['list', 'get', 'create'] },\n cli: true,\n mcp: { include: ['list', 'get'] },\n})\nexport class TenantUsageMetric extends SmrtObject {\n @tenantId()\n tenantId?: string;\n\n /**\n * Discriminator for which subscriber identity recorded this usage.\n * `'tenant'` (default) preserves the legacy shape; `'external'` indicates\n * the subscriber is `subscriberExternalId` under the issuing `tenantId`.\n */\n subscriberKind: SubscriberKind = 'tenant';\n\n /**\n * Opaque caller-namespaced subscriber id when `subscriberKind === 'external'`.\n * Empty string when kind is `'tenant'`.\n */\n subscriberExternalId: string = '';\n\n metricKey: string = '';\n quantity: number = 0.0;\n windowStart: Date = new Date();\n windowEnd: Date = new Date();\n source: string = '';\n sourceId: string = '';\n dimensions: string = '{}';\n\n constructor(options: TenantUsageMetricOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.subscriberKind !== undefined)\n this.subscriberKind = options.subscriberKind;\n if (options.subscriberExternalId !== undefined)\n this.subscriberExternalId = options.subscriberExternalId;\n // Construction-time guard; the same invariant is re-checked in\n // `validateBeforeSave` so the generated update path can't bypass it.\n assertSubscriberInvariant('TenantUsageMetric', {\n subscriberKind: this.subscriberKind,\n subscriberExternalId: this.subscriberExternalId,\n });\n if (options.metricKey !== undefined) this.metricKey = options.metricKey;\n if (options.quantity !== undefined) this.quantity = options.quantity;\n if (options.windowStart !== undefined)\n this.windowStart = options.windowStart;\n if (options.windowEnd !== undefined) this.windowEnd = options.windowEnd;\n if (options.source !== undefined) this.source = options.source;\n if (options.sourceId !== undefined) this.sourceId = options.sourceId;\n if (options.dimensions !== undefined) this.dimensions = options.dimensions;\n }\n\n /** See `TenantSubscription.validateBeforeSave` for the rationale. */\n protected async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n assertSubscriberInvariant('TenantUsageMetric', {\n subscriberKind: this.subscriberKind,\n subscriberExternalId: this.subscriberExternalId,\n });\n }\n\n /**\n * Project this row's polymorphic subscriber columns onto the\n * {@link Subscriber} discriminated union.\n */\n getSubscriber(): Subscriber | null {\n if (!this.tenantId) {\n return null;\n }\n if (this.subscriberKind === 'external') {\n if (!this.subscriberExternalId) {\n return null;\n }\n return {\n kind: 'external',\n tenantId: this.tenantId,\n externalId: this.subscriberExternalId,\n };\n }\n return { kind: 'tenant', tenantId: this.tenantId };\n }\n\n getDimensions(): JsonObject {\n return parseJsonObject(this.dimensions, {});\n }\n\n setDimensions(dimensions: JsonObject): void {\n this.dimensions = stringifyJson(dimensions);\n }\n}\n\nexport default TenantUsageMetric;\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { TenantUsageMetric } from '../models/TenantUsageMetric.js';\nimport type {\n AiUsageSummary,\n RecordUsageOptions,\n SummarizeAiUsageOptions,\n SummarizeUsageBatchOptions,\n SummarizeUsageOptions,\n UsageSummary,\n} from '../types.js';\nimport {\n normalizeSubscriber,\n stringifyJson,\n subscriberToColumns,\n} from '../utils.js';\n\nexport class TenantUsageMetricCollection extends SmrtCollection<TenantUsageMetric> {\n static readonly _itemClass = TenantUsageMetric;\n\n async recordUsage(options: RecordUsageOptions): Promise<TenantUsageMetric> {\n const subscriber = normalizeSubscriber({\n tenantId: options.tenantId,\n subscriberKind: options.subscriberKind,\n subscriberExternalId: options.subscriberExternalId,\n });\n const columns = subscriberToColumns(subscriber);\n const metric = await this.create({\n tenantId: columns.tenantId,\n subscriberKind: columns.subscriberKind,\n subscriberExternalId: columns.subscriberExternalId,\n metricKey: options.metricKey,\n quantity: options.quantity,\n windowStart: options.windowStart,\n windowEnd: options.windowEnd,\n source: options.source ?? '',\n sourceId: options.sourceId ?? '',\n dimensions: stringifyJson(options.dimensions ?? {}),\n });\n return metric;\n }\n\n /**\n * Legacy accessor — finds metrics for `subscriberKind = 'tenant'` under the\n * given tenant. External-kind rows scoped to the same tenant are excluded.\n */\n async findByTenantAndMetric(\n tenantId: string,\n metricKey: string,\n ): Promise<TenantUsageMetric[]> {\n return this.list({\n where: { tenantId, metricKey, subscriberKind: 'tenant' },\n orderBy: 'windowStart DESC',\n });\n }\n\n async summarizeUsage(options: SummarizeUsageOptions): Promise<UsageSummary> {\n const subscriber = normalizeSubscriber({\n tenantId: options.tenantId,\n subscriberKind: options.subscriberKind,\n subscriberExternalId: options.subscriberExternalId,\n });\n const columns = subscriberToColumns(subscriber);\n\n const where: Record<string, unknown> = {\n tenantId: columns.tenantId,\n subscriberKind: columns.subscriberKind,\n metricKey: options.metricKey,\n 'windowStart <': options.window.end.toISOString(),\n 'windowEnd >': options.window.start.toISOString(),\n };\n if (subscriber.kind === 'external') {\n where.subscriberExternalId = columns.subscriberExternalId;\n }\n\n const metrics = await this.list({ where });\n\n const baseSummary: UsageSummary = {\n tenantId: columns.tenantId,\n metricKey: options.metricKey,\n quantity: metrics.reduce((sum, metric) => sum + metric.quantity, 0),\n windowStart: options.window.start,\n windowEnd: options.window.end,\n };\n if (subscriber.kind === 'external') {\n return {\n ...baseSummary,\n subscriberKind: 'external',\n subscriberExternalId: subscriber.externalId,\n };\n }\n return baseSummary;\n }\n\n async summarizeUsageBatch(\n options: SummarizeUsageBatchOptions,\n ): Promise<UsageSummary[]> {\n const metricKeys = Array.from(new Set(options.metricKeys));\n if (metricKeys.length === 0) {\n return [];\n }\n\n const subscriber = normalizeSubscriber({\n tenantId: options.tenantId,\n subscriberKind: options.subscriberKind,\n subscriberExternalId: options.subscriberExternalId,\n });\n const columns = subscriberToColumns(subscriber);\n\n const metricPlaceholders = metricKeys.map(() => '?').join(', ');\n const params: unknown[] = [\n columns.tenantId,\n columns.subscriberKind,\n ...metricKeys,\n options.window.end.toISOString(),\n options.window.start.toISOString(),\n ];\n let subscriberSql = '';\n if (subscriber.kind === 'external') {\n subscriberSql = 'AND subscriber_external_id = ?';\n params.splice(2, 0, columns.subscriberExternalId);\n }\n\n const result = await this.db.query(\n `SELECT\n metric_key,\n COALESCE(SUM(quantity), 0) AS quantity\n FROM _smrt_tenant_usage_metrics\n WHERE tenant_id = ?\n AND subscriber_kind = ?\n ${subscriberSql}\n AND metric_key IN (${metricPlaceholders})\n AND window_start < ?\n AND window_end > ?\n GROUP BY metric_key`,\n ...params,\n );\n\n const quantityByMetric = new Map<string, number>();\n for (const row of resultRows(result)) {\n const metricKey = String(row.metric_key ?? row.metricKey ?? '');\n if (metricKey) {\n quantityByMetric.set(metricKey, numberFromRow(row, 'quantity'));\n }\n }\n\n return metricKeys.map((metricKey) => {\n const baseSummary: UsageSummary = {\n tenantId: columns.tenantId,\n metricKey,\n quantity: quantityByMetric.get(metricKey) ?? 0,\n windowStart: options.window.start,\n windowEnd: options.window.end,\n };\n if (subscriber.kind === 'external') {\n return {\n ...baseSummary,\n subscriberKind: 'external',\n subscriberExternalId: subscriber.externalId,\n };\n }\n return baseSummary;\n });\n }\n\n /**\n * Summarize persisted AI usage for a tenant within a window.\n *\n * Reads the framework `_smrt_ai_usage` system table via a raw aggregate\n * query. This deliberately uses `this.db.query` rather than the inherited\n * `query()`: those rows are framework usage records, not `TenantUsageMetric`\n * instances, so model hydration would discard the aggregate columns. Tenant\n * scoping is applied explicitly through the `tenant_id` filter.\n *\n * Named distinctly from the inherited `SmrtClass.summarizeAiUsage` (which\n * returns grouped stats across all tenants) to avoid overriding it.\n */\n async summarizeTenantAiUsage(\n options: SummarizeAiUsageOptions,\n ): Promise<AiUsageSummary> {\n const result = await this.db.query(\n `SELECT\n COALESCE(SUM(prompt_tokens), 0) AS prompt_tokens,\n COALESCE(SUM(completion_tokens), 0) AS completion_tokens,\n COALESCE(SUM(total_tokens), 0) AS total_tokens,\n COALESCE(SUM(estimated_cost), 0) AS estimated_cost,\n COUNT(*) AS request_count\n FROM _smrt_ai_usage\n WHERE tenant_id = ?\n AND created_at >= ?\n AND created_at < ?`,\n options.tenantId,\n options.window.start.toISOString(),\n options.window.end.toISOString(),\n );\n const row = firstRow(result);\n\n return {\n tenantId: options.tenantId,\n promptTokens: numberFromRow(row, 'prompt_tokens'),\n completionTokens: numberFromRow(row, 'completion_tokens'),\n totalTokens: numberFromRow(row, 'total_tokens'),\n estimatedCost: numberFromRow(row, 'estimated_cost'),\n requestCount: numberFromRow(row, 'request_count'),\n windowStart: options.window.start,\n windowEnd: options.window.end,\n };\n }\n}\n\nfunction firstRow(result: unknown): Record<string, unknown> {\n const rows = resultRows(result);\n return (rows[0] ?? {}) as Record<string, unknown>;\n}\n\nfunction resultRows(result: unknown): Record<string, unknown>[] {\n return Array.isArray(result)\n ? (result as Record<string, unknown>[])\n : ((result as { rows?: Record<string, unknown>[] })?.rows ?? []);\n}\n\nfunction numberFromRow(row: Record<string, unknown>, key: string): number {\n const value = row[key];\n if (typeof value === 'number') {\n return value;\n }\n if (typeof value === 'bigint') {\n return Number(value);\n }\n if (typeof value === 'string') {\n return Number.parseFloat(value) || 0;\n }\n return 0;\n}\n","import type {\n PlanThreshold,\n ThresholdEvaluation,\n UsageSummary,\n} from '../types.js';\n\nexport function evaluateThreshold(\n threshold: PlanThreshold,\n usage: UsageSummary,\n): ThresholdEvaluation {\n const ratio =\n threshold.limit === 0\n ? usage.quantity > 0\n ? Infinity\n : 0\n : usage.quantity / threshold.limit;\n const warningRatio = threshold.warningRatio ?? 0.8;\n const blocked =\n threshold.enforcement === 'block' &&\n (threshold.limit === 0\n ? usage.quantity > 0\n : usage.quantity >= threshold.limit);\n const warned = !blocked && ratio >= warningRatio;\n\n return {\n threshold,\n usage,\n ratio,\n state: blocked ? 'blocked' : warned ? 'warn' : 'ok',\n allowed: !blocked,\n remaining: Math.max(0, threshold.limit - usage.quantity),\n };\n}\n\nexport function evaluateThresholds(\n thresholds: PlanThreshold[],\n usage: UsageSummary[],\n): ThresholdEvaluation[] {\n const usageByMetric = new Map(\n usage.map((summary) => [summary.metricKey, summary]),\n );\n\n return thresholds.map((threshold) => {\n const summary =\n usageByMetric.get(threshold.metricKey) ??\n emptyUsageSummary(threshold.metricKey);\n return evaluateThreshold(threshold, summary);\n });\n}\n\nfunction emptyUsageSummary(metricKey: string): UsageSummary {\n const now = new Date();\n return {\n tenantId: '',\n metricKey,\n quantity: 0,\n windowStart: now,\n windowEnd: now,\n };\n}\n","import type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { TenantUsageMetricCollection } from '../collections/TenantUsageMetricCollection.js';\nimport type {\n AiUsageSummary,\n RecordUsageOptions,\n SummarizeAiUsageOptions,\n SummarizeUsageBatchOptions,\n SummarizeUsageOptions,\n UsageSummary,\n} from '../types.js';\nimport { normalizeSubscriber } from '../utils.js';\n\nexport class TenantUsageMeter {\n constructor(\n private readonly metrics: TenantUsageMetricCollection,\n private readonly classOptions: SmrtClassOptions = {},\n ) {}\n\n static async create(\n classOptions: SmrtClassOptions = {},\n ): Promise<TenantUsageMeter> {\n const metrics = await TenantUsageMetricCollection.create(classOptions);\n return new TenantUsageMeter(metrics, classOptions);\n }\n\n /**\n * Record one usage row.\n *\n * Accepts the polymorphic subscriber fields directly on the options object\n * (`subscriberKind`/`subscriberExternalId`); when omitted defaults to a\n * `'tenant'`-kind row keyed off `tenantId`. The collection layer enforces\n * the XOR invariant — passing `subscriberKind: 'external'` without a\n * non-empty `subscriberExternalId` throws.\n */\n async record(options: RecordUsageOptions): Promise<void> {\n await this.metrics.recordUsage(options);\n }\n\n /**\n * Summarize usage over a window for a given subscriber.\n *\n * The `ai.*` short-circuit only fires for `'tenant'`-kind subscribers since\n * the `_smrt_ai_usage` system table is tenant-scoped; external subscribers\n * fall through to the normal `_smrt_tenant_usage_metrics` aggregation.\n */\n async summarize(options: SummarizeUsageOptions): Promise<UsageSummary> {\n // Normalize at the boundary so the `ai.*` short-circuit and the standard\n // `summarizeUsage` path both refuse the same XOR violations (e.g. caller\n // passes subscriberExternalId without subscriberKind: 'external'). Without\n // this, the AI short-circuit would silently re-scope to tenant-wide\n // _smrt_ai_usage totals.\n normalizeSubscriber({\n tenantId: options.tenantId,\n subscriberKind: options.subscriberKind,\n subscriberExternalId: options.subscriberExternalId,\n });\n\n const aiSummary = await this.trySummarizeAiMetric(options);\n if (aiSummary) {\n return aiSummary;\n }\n\n return this.metrics.summarizeUsage(options);\n }\n\n async summarizeBatch(\n options: SummarizeUsageBatchOptions,\n ): Promise<UsageSummary[]> {\n const subscriber = normalizeSubscriber({\n tenantId: options.tenantId,\n subscriberKind: options.subscriberKind,\n subscriberExternalId: options.subscriberExternalId,\n });\n const metricKeys = Array.from(new Set(options.metricKeys));\n if (metricKeys.length === 0) {\n return [];\n }\n\n const useTenantAiSummary = subscriber.kind === 'tenant';\n const aiMetricKeys = useTenantAiSummary\n ? metricKeys.filter((metricKey) => metricKey.startsWith('ai.'))\n : [];\n const persistedMetricKeys = useTenantAiSummary\n ? metricKeys.filter((metricKey) => !metricKey.startsWith('ai.'))\n : metricKeys;\n\n const summaries = new Map<string, UsageSummary>();\n if (aiMetricKeys.length > 0) {\n const aiSummary = await this.summarizeAiUsage({\n tenantId: options.tenantId,\n window: options.window,\n });\n const quantityByMetric = aiQuantityByMetric(aiSummary);\n for (const metricKey of aiMetricKeys) {\n summaries.set(metricKey, {\n tenantId: options.tenantId,\n metricKey,\n quantity: quantityByMetric[metricKey] ?? 0,\n windowStart: options.window.start,\n windowEnd: options.window.end,\n });\n }\n }\n\n const persistedSummaries = await this.metrics.summarizeUsageBatch({\n ...options,\n metricKeys: persistedMetricKeys,\n });\n for (const summary of persistedSummaries) {\n summaries.set(summary.metricKey, summary);\n }\n\n return metricKeys.map(\n (metricKey) =>\n summaries.get(metricKey) ??\n emptyUsageSummary(subscriber, metricKey, options.window),\n );\n }\n\n async summarizeAiUsage(\n options: SummarizeAiUsageOptions,\n ): Promise<AiUsageSummary> {\n return this.metrics.summarizeTenantAiUsage(options);\n }\n\n getOptions(): SmrtClassOptions {\n return this.classOptions;\n }\n\n private async trySummarizeAiMetric(\n options: SummarizeUsageOptions,\n ): Promise<UsageSummary | null> {\n if (!options.metricKey.startsWith('ai.')) {\n return null;\n }\n // _smrt_ai_usage is tenant-scoped only; external subscribers must use the\n // standard usage path. Skip the short-circuit so the caller's recorded\n // usage rows are summed instead.\n const kind = options.subscriberKind ?? 'tenant';\n if (kind !== 'tenant') {\n return null;\n }\n\n const summary = await this.summarizeAiUsage({\n tenantId: options.tenantId,\n window: options.window,\n });\n\n const quantityByMetric: Record<string, number> = {\n ...aiQuantityByMetric(summary),\n };\n\n return {\n tenantId: options.tenantId,\n metricKey: options.metricKey,\n quantity: quantityByMetric[options.metricKey] ?? 0,\n windowStart: options.window.start,\n windowEnd: options.window.end,\n };\n }\n}\n\nfunction aiQuantityByMetric(summary: AiUsageSummary): Record<string, number> {\n return {\n 'ai.tokens.prompt': summary.promptTokens,\n 'ai.tokens.completion': summary.completionTokens,\n 'ai.tokens.total': summary.totalTokens,\n 'ai.cost.estimated': summary.estimatedCost,\n 'ai.requests': summary.requestCount,\n };\n}\n\nfunction emptyUsageSummary(\n subscriber: ReturnType<typeof normalizeSubscriber>,\n metricKey: string,\n window: SummarizeUsageBatchOptions['window'],\n): UsageSummary {\n const summary: UsageSummary = {\n tenantId: subscriber.tenantId,\n metricKey,\n quantity: 0,\n windowStart: window.start,\n windowEnd: window.end,\n };\n if (subscriber.kind === 'external') {\n return {\n ...summary,\n subscriberKind: 'external',\n subscriberExternalId: subscriber.externalId,\n };\n }\n return summary;\n}\n","import { SubscriptionPlanCollection } from '../collections/SubscriptionPlanCollection.js';\nimport { TenantSubscriptionCollection } from '../collections/TenantSubscriptionCollection.js';\nimport type { SubscriptionPlan } from '../models/SubscriptionPlan.js';\nimport type { TenantSubscription } from '../models/TenantSubscription.js';\nimport type {\n EntitlementResolution,\n EntitlementResolutionContext,\n PlanThreshold,\n SmrtClassOptions,\n Subscriber,\n SubscriptionResolverOptions,\n ThresholdEvaluation,\n UsageSummary,\n UsageWindow,\n} from '../types.js';\nimport {\n getWindowForThreshold,\n getWindowKey,\n isValidThreshold,\n} from '../utils.js';\nimport { evaluateThreshold } from './threshold-evaluator.js';\nimport { TenantUsageMeter } from './usage-meter.js';\n\nexport interface SubscriptionPlanReader {\n get(criteria: { id: string }): Promise<SubscriptionPlan | null>;\n}\n\n/**\n * Reader contract for finding the current subscription for a subscriber.\n *\n * The legacy `findCurrentForTenant(tenantId)` signature stays for backward\n * compatibility with the original tenant-only resolver. New implementations\n * should provide `findCurrentForSubscriber(subscriber)` — it's preferred when\n * present and lets the resolver work with both `'tenant'` and `'external'`\n * subscribers without callers having to pre-narrow the discriminator.\n */\nexport interface TenantSubscriptionReader {\n findCurrentForTenant(\n tenantId: string,\n now?: Date,\n ): Promise<TenantSubscription | null>;\n findCurrentForSubscriber?(\n subscriber: Subscriber,\n now?: Date,\n ): Promise<TenantSubscription | null>;\n}\n\n/**\n * Reader contract for summarizing usage.\n *\n * `summarize(options)` already accepts the polymorphic\n * `subscriberKind`/`subscriberExternalId` fields. When omitted the reader\n * defaults to `'tenant'`, which is what existing callers got before this\n * change — no behavior shift for them. The discriminator type is derived\n * from `Subscriber['kind']` rather than inlined so any future additions to\n * the union stay consistent across the package.\n */\nexport interface UsageSummaryReader {\n summarize(options: {\n tenantId: string;\n subscriberKind?: Subscriber['kind'];\n subscriberExternalId?: string;\n metricKey: string;\n window: UsageWindow;\n }): Promise<UsageSummary>;\n summarizeBatch?(options: {\n tenantId: string;\n subscriberKind?: Subscriber['kind'];\n subscriberExternalId?: string;\n metricKeys: string[];\n window: UsageWindow;\n }): Promise<UsageSummary[]>;\n}\n\nexport interface SubscriptionResolverReaders {\n plans: SubscriptionPlanReader;\n subscriptions: TenantSubscriptionReader;\n usage: UsageSummaryReader;\n}\n\nexport class SubscriptionResolver {\n constructor(private readonly readers: SubscriptionResolverReaders) {}\n\n /**\n * Build the default resolver readers once for a request or app lifecycle and\n * reuse the returned resolver across entitlement checks.\n */\n static async create(\n classOptions: SmrtClassOptions = {},\n ): Promise<SubscriptionResolver> {\n const [plans, subscriptions, usage] = await Promise.all([\n SubscriptionPlanCollection.create(classOptions),\n TenantSubscriptionCollection.create(classOptions),\n TenantUsageMeter.create(classOptions),\n ]);\n return new SubscriptionResolver({ plans, subscriptions, usage });\n }\n\n /**\n * Load the subscription/plan pair used by entitlement resolution. Callers\n * that need both the entitlement snapshot and the backing records can load\n * this once, then pass it back via `options.context`.\n */\n async loadEntitlementContext(\n subscriberOrTenantId: Subscriber | string,\n options: SubscriptionResolverOptions = {},\n ): Promise<EntitlementResolutionContext> {\n const subscriber = toSubscriber(subscriberOrTenantId);\n const now = options.now ?? new Date();\n const subscription = await this.resolveSubscription(\n subscriber,\n now,\n options.context,\n );\n const plan = await this.resolvePlan(subscription, options.context);\n return { subscription, plan };\n }\n\n /**\n * Polymorphic entitlement resolution. Works for both `'tenant'`-kind and\n * `'external'`-kind subscribers and is the preferred surface — the\n * `resolveTenantEntitlements(tenantId)` method below is a thin wrapper.\n */\n async resolveEntitlements(\n subscriber: Subscriber,\n options: SubscriptionResolverOptions = {},\n ): Promise<EntitlementResolution> {\n const now = options.now ?? new Date();\n const { subscription, plan } = await this.loadEntitlementContext(\n subscriber,\n { ...options, now },\n );\n\n if (!subscription) {\n return emptyResolution(subscriber);\n }\n\n if (!plan || !plan.isActive() || !subscription.isEntitled(now)) {\n return {\n ...emptyResolution(subscriber),\n planId: plan?.id ?? subscription.planId ?? null,\n planKey: plan?.planKey ?? null,\n subscriptionId: subscription.id ?? null,\n status: subscription.status,\n };\n }\n\n const thresholds = plan.getThresholds().filter(isValidThreshold);\n const thresholdEvaluations = await this.resolveThresholdEvaluations(\n subscriber,\n thresholds,\n now,\n options,\n );\n\n return {\n tenantId: subscriber.tenantId,\n subscriber,\n planId: plan.id ?? null,\n planKey: plan.planKey,\n subscriptionId: subscription.id ?? null,\n status: subscription.status,\n featureKeys: plan.getFeatureKeys(),\n thresholds,\n thresholdEvaluations,\n allowed: thresholdEvaluations.every((evaluation) => evaluation.allowed),\n };\n }\n\n /**\n * Legacy single-tenant wrapper around {@link resolveEntitlements}. Kept so\n * existing tenant-only callers don't need to update their call sites.\n */\n async resolveTenantEntitlements(\n tenantId: string,\n options: SubscriptionResolverOptions = {},\n ): Promise<EntitlementResolution> {\n return this.resolveEntitlements({ kind: 'tenant', tenantId }, options);\n }\n\n async isFeatureEnabled(\n subscriberOrTenantId: Subscriber | string,\n featureKey: string,\n options: SubscriptionResolverOptions = {},\n ): Promise<boolean> {\n const resolution = await this.resolveEntitlements(\n toSubscriber(subscriberOrTenantId),\n options,\n );\n return resolution.featureKeys.includes(featureKey);\n }\n\n async assertWithinThresholds(\n subscriberOrTenantId: Subscriber | string,\n options: SubscriptionResolverOptions = {},\n ): Promise<void> {\n const subscriber = toSubscriber(subscriberOrTenantId);\n const resolution = await this.resolveEntitlements(subscriber, options);\n const blocked = resolution.thresholdEvaluations.find(\n (evaluation) => !evaluation.allowed,\n );\n\n if (blocked) {\n const subject =\n subscriber.kind === 'external'\n ? `external:${subscriber.externalId}`\n : `Tenant ${subscriber.tenantId}`;\n throw new Error(\n `${subject} exceeded subscription threshold ${blocked.threshold.metricKey}`,\n );\n }\n }\n\n private async resolveSubscription(\n subscriber: Subscriber,\n now: Date,\n context?: EntitlementResolutionContext,\n ): Promise<TenantSubscription | null> {\n if (hasContextValue(context, 'subscription')) {\n const subscription = context?.subscription ?? null;\n assertSubscriptionMatchesSubscriber(subscription, subscriber);\n return subscription;\n }\n return this.findCurrentSubscription(subscriber, now);\n }\n\n private async resolvePlan(\n subscription: TenantSubscription | null,\n context?: EntitlementResolutionContext,\n ): Promise<SubscriptionPlan | null> {\n if (!subscription?.planId) {\n return null;\n }\n if (hasContextValue(context, 'plan')) {\n const plan = context?.plan ?? null;\n assertPlanMatchesSubscription(plan, subscription);\n return plan;\n }\n return this.readers.plans.get({ id: subscription.planId });\n }\n\n /**\n * Bridge the legacy and polymorphic reader contracts.\n *\n * Prefers `findCurrentForSubscriber` when the reader provides it (the\n * preferred shape). For `'tenant'` subscribers we fall back to the legacy\n * `findCurrentForTenant`. For `'external'` subscribers the reader MUST\n * implement `findCurrentForSubscriber` — otherwise we have no way to scope\n * the lookup and we throw rather than silently returning the tenant's\n * primary subscription.\n */\n private async findCurrentSubscription(\n subscriber: Subscriber,\n now: Date,\n ): Promise<TenantSubscription | null> {\n if (this.readers.subscriptions.findCurrentForSubscriber) {\n return this.readers.subscriptions.findCurrentForSubscriber(\n subscriber,\n now,\n );\n }\n if (subscriber.kind === 'tenant') {\n return this.readers.subscriptions.findCurrentForTenant(\n subscriber.tenantId,\n now,\n );\n }\n throw new Error(\n 'External-subscriber resolution requires a TenantSubscriptionReader ' +\n 'that implements findCurrentForSubscriber()',\n );\n }\n\n private async resolveThresholdEvaluations(\n subscriber: Subscriber,\n thresholds: PlanThreshold[],\n now: Date,\n options: SubscriptionResolverOptions,\n ): Promise<ThresholdEvaluation[]> {\n if (thresholds.length === 0) {\n return [];\n }\n\n if (!this.readers.usage.summarizeBatch) {\n const evaluations: ThresholdEvaluation[] = [];\n for (const threshold of thresholds) {\n const window =\n options.usageWindows?.[threshold.window] ??\n getWindowForThreshold(threshold.window, now);\n const usage = await this.readers.usage.summarize({\n tenantId: subscriber.tenantId,\n subscriberKind: subscriber.kind,\n subscriberExternalId:\n subscriber.kind === 'external' ? subscriber.externalId : undefined,\n metricKey: threshold.metricKey,\n window,\n });\n evaluations.push(evaluateThreshold(threshold, usage));\n }\n return evaluations;\n }\n\n const groups = new Map<\n string,\n {\n window: UsageWindow;\n entries: Array<{ index: number; threshold: PlanThreshold }>;\n }\n >();\n\n thresholds.forEach((threshold, index) => {\n const window =\n options.usageWindows?.[threshold.window] ??\n getWindowForThreshold(threshold.window, now);\n const key = getWindowKey(window);\n const group = groups.get(key) ?? { window, entries: [] };\n group.entries.push({ index, threshold });\n groups.set(key, group);\n });\n\n const evaluations: ThresholdEvaluation[] = new Array(thresholds.length);\n await Promise.all(\n Array.from(groups.values()).map(async ({ window, entries }) => {\n const metricKeys = uniqueMetricKeys(\n entries.map((entry) => entry.threshold.metricKey),\n );\n const summaries = await this.readers.usage.summarizeBatch?.({\n tenantId: subscriber.tenantId,\n subscriberKind: subscriber.kind,\n subscriberExternalId:\n subscriber.kind === 'external' ? subscriber.externalId : undefined,\n metricKeys,\n window,\n });\n const summaryByMetric = new Map(\n (summaries ?? []).map((summary) => [summary.metricKey, summary]),\n );\n\n for (const { index, threshold } of entries) {\n const usage =\n summaryByMetric.get(threshold.metricKey) ??\n emptyUsageSummary(subscriber, threshold.metricKey, window);\n evaluations[index] = evaluateThreshold(threshold, usage);\n }\n }),\n );\n\n return evaluations;\n }\n}\n\nfunction toSubscriber(input: Subscriber | string): Subscriber {\n if (typeof input === 'string') {\n return { kind: 'tenant', tenantId: input };\n }\n return input;\n}\n\nfunction emptyResolution(subscriber: Subscriber): EntitlementResolution {\n return {\n tenantId: subscriber.tenantId,\n subscriber,\n planId: null,\n planKey: null,\n subscriptionId: null,\n status: 'none',\n featureKeys: [],\n thresholds: [],\n thresholdEvaluations: [],\n allowed: false,\n };\n}\n\nfunction hasContextValue<K extends keyof EntitlementResolutionContext>(\n context: EntitlementResolutionContext | undefined,\n key: K,\n): boolean {\n return context?.[key] !== undefined;\n}\n\nfunction assertSubscriptionMatchesSubscriber(\n subscription: TenantSubscription | null,\n subscriber: Subscriber,\n): void {\n if (!subscription) {\n return;\n }\n const subscriptionSubscriber = subscription.getSubscriber();\n if (\n !subscriptionSubscriber ||\n !sameSubscriber(subscriptionSubscriber, subscriber)\n ) {\n throw new Error(\n 'Provided entitlement context subscription does not match requested subscriber',\n );\n }\n}\n\nfunction assertPlanMatchesSubscription(\n plan: SubscriptionPlan | null,\n subscription: TenantSubscription,\n): void {\n if (!plan) {\n return;\n }\n if (!plan.id || plan.id !== subscription.planId) {\n throw new Error(\n 'Provided entitlement context plan does not match subscription.planId',\n );\n }\n}\n\nfunction sameSubscriber(left: Subscriber, right: Subscriber): boolean {\n if (left.kind !== right.kind || left.tenantId !== right.tenantId) {\n return false;\n }\n if (left.kind === 'tenant') {\n return true;\n }\n return right.kind === 'external' && left.externalId === right.externalId;\n}\n\nfunction uniqueMetricKeys(metricKeys: string[]): string[] {\n return Array.from(new Set(metricKeys));\n}\n\nfunction emptyUsageSummary(\n subscriber: Subscriber,\n metricKey: string,\n window: UsageWindow,\n): UsageSummary {\n const summary: UsageSummary = {\n tenantId: subscriber.tenantId,\n metricKey,\n quantity: 0,\n windowStart: window.start,\n windowEnd: window.end,\n };\n if (subscriber.kind === 'external') {\n return {\n ...summary,\n subscriberKind: 'external',\n subscriberExternalId: subscriber.externalId,\n };\n }\n return summary;\n}\n\nexport type { PlanThreshold };\n"],"names":["__decorateClass","tenantId","emptyUsageSummary","evaluations"],"mappings":";;AAEA,eAAe;AAAA,EACb,IAAA,IAAA,mBAAA,YAAA,GAAA;AACF;ACOA,MAAM,oBAAuC;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,MAAM,yBAAiD;AAAA,EACrD;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,eAAkB,OAAe,WAAgB,IAAS;AACxE,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,WAAO,MAAM,QAAQ,MAAM,IAAK,SAAiB;AAAA,EACnD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,gBACd,OACA,UACG;AACH,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,WAAO,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,IAC/D,SACD;AAAA,EACN,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,OAAwB;AACpD,SAAO,KAAK,UAAU,SAAS,IAAI;AACrC;AAEO,SAAS,uBACd,QACoB;AACpB,SAAO,OAAO;AAAA,IAAI,CAAC,UACjB,OAAO,UAAU,WAAW,EAAE,YAAY,OAAO,SAAS,SAAS;AAAA,EAAA;AAEvE;AAkBO,SAAS,oBAAoB,OAIrB;AAKb,QAAM,gBAAgB,MAAM;AAC5B,QAAM,mBACJ,kBAAkB,QAAQ,kBAAkB;AAC9C,QAAM,aAAa,mBAAmB,KAAK,OAAO,aAAa;AAE/D,QAAM,UAAU,MAAM;AACtB,QAAM,OACJ,YAAY,QAAQ,YAAY,SAAY,WAAW;AAEzD,MAAI,SAAS,YAAY,SAAS,YAAY;AAC5C,UAAM,IAAI;AAAA,MACR,sDAAsD,KAAK,UAAU,OAAO,CAAC;AAAA,IAAA;AAAA,EAEjF;AAEA,MAAI,SAAS,UAAU;AACrB,QAAI,eAAe,IAAI;AACrB,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAGJ;AACA,WAAO,EAAE,MAAM,UAAU,UAAU,MAAM,SAAA;AAAA,EAC3C;AACA,MAAI,eAAe,IAAI;AACrB,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU,MAAM;AAAA,IAChB;AAAA,EAAA;AAEJ;AAMO,SAAS,oBAAoB,YAIlC;AACA,MAAI,WAAW,SAAS,UAAU;AAChC,WAAO;AAAA,MACL,UAAU,WAAW;AAAA,MACrB,gBAAgB;AAAA,MAChB,sBAAsB;AAAA,IAAA;AAAA,EAE1B;AACA,SAAO;AAAA,IACL,UAAU,WAAW;AAAA,IACrB,gBAAgB;AAAA,IAChB,sBAAsB,WAAW;AAAA,EAAA;AAErC;AAkBO,SAAS,0BACd,WACA,QAIM;AACN,MACE,OAAO,mBAAmB,YAC1B,OAAO,mBAAmB,YAC1B;AACA,UAAM,IAAI;AAAA,MACR,GAAG,SAAS,wDAAwD;AAAA,QAClE,OAAO;AAAA,MAAA,CACR;AAAA,IAAA;AAAA,EAEL;AACA,MAAI,OAAO,OAAO,yBAAyB,UAAU;AACnD,UAAM,IAAI;AAAA,MACR,GAAG,SAAS,gDAAgD;AAAA,QAC1D,OAAO;AAAA,MAAA,CACR;AAAA,IAAA;AAAA,EAEL;AAEA,MACE,OAAO,mBAAmB,YAC1B,OAAO,yBAAyB,IAChC;AACA,UAAM,IAAI;AAAA,MACR,GAAG,SAAS;AAAA,IAAA;AAAA,EAEhB;AACA,MACE,OAAO,mBAAmB,cAC1B,OAAO,yBAAyB,IAChC;AACA,UAAM,IAAI;AAAA,MACR,GAAG,SAAS;AAAA,IAAA;AAAA,EAEhB;AACF;AAEA,SAAS,SAAS,OAAwB;AACxC,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,SAAU,QAAO,IAAI,KAAK;AAC/C,SAAO,OAAO,KAAK;AACrB;AAEO,SAAS,sBACd,iBACA,MAAM,oBAAI,QACG;AACb,UAAQ,iBAAA;AAAA,IACN,KAAK;AACH,aAAO,UAAU,KAAK,KAAK;AAAA,IAC7B,KAAK;AACH,aAAO,UAAU,KAAK,MAAM;AAAA,IAC9B,KAAK;AACH,aAAO,UAAU,KAAK,OAAO;AAAA,IAC/B,KAAK;AACH,aAAO,UAAU,KAAK,MAAM;AAAA,IAC9B,KAAK;AACH,aAAO;AAAA,QACL,OAAO,IAAI,KAAK,IAAI,QAAA,IAAY,KAAK,KAAK,KAAK,KAAK,GAAI;AAAA,QACxD,KAAK;AAAA,MAAA;AAAA,EACP;AAEN;AAEO,SAAS,aAAa,QAA6B;AACxD,SAAO,GAAG,OAAO,MAAM,YAAA,CAAa,KAAK,OAAO,IAAI,YAAA,CAAa;AACnE;AAEO,SAAS,iBAAiB,WAAmC;AAClE,SACE,OAAO,UAAU,cAAc,YAC/B,UAAU,UAAU,SAAS,KAC7B,OAAO,SAAS,UAAU,KAAK,KAC/B,UAAU,SAAS,KACnB,kBAAkB,UAAU,MAAM,KAClC,uBAAuB,UAAU,WAAW,MAC3C,UAAU,iBAAiB,UACzB,OAAO,SAAS,UAAU,YAAY,KACrC,UAAU,gBAAgB,KAC1B,UAAU,gBAAgB;AAElC;AAEA,SAAS,kBAAkB,OAA0C;AACnE,SACE,OAAO,UAAU,YACjB,kBAAkB,SAAS,KAAwB;AAEvD;AAEA,SAAS,uBAAuB,OAA+C;AAC7E,SACE,OAAO,UAAU,YACjB,uBAAuB,SAAS,KAA6B;AAEjE;AAEA,SAAS,UAAU,KAAW,MAAyC;AACrE,QAAM,QAAQ,IAAI;AAAA,IAChB,KAAK,IAAI,IAAI,eAAA,GAAkB,IAAI,YAAA,GAAe,IAAI,WAAA,CAAY;AAAA,EAAA;AAGpE,MAAI,SAAS,QAAQ;AACnB,UAAM,MAAM,MAAM,UAAA;AAClB,UAAM,eAAe,QAAQ,IAAI,KAAK,IAAI;AAC1C,UAAM,WAAW,MAAM,WAAA,IAAe,YAAY;AAAA,EACpD;AAEA,MAAI,SAAS,SAAS;AACpB,UAAM,WAAW,CAAC;AAAA,EACpB;AAEA,MAAI,SAAS,QAAQ;AACnB,UAAM,YAAY,GAAG,CAAC;AAAA,EACxB;AAEA,QAAM,MAAM,IAAI,KAAK,KAAK;AAC1B,UAAQ,MAAA;AAAA,IACN,KAAK;AACH,UAAI,WAAW,IAAI,WAAA,IAAe,CAAC;AACnC;AAAA,IACF,KAAK;AACH,UAAI,WAAW,IAAI,WAAA,IAAe,CAAC;AACnC;AAAA,IACF,KAAK;AACH,UAAI,YAAY,IAAI,YAAA,IAAgB,CAAC;AACrC;AAAA,IACF,KAAK;AACH,UAAI,eAAe,IAAI,eAAA,IAAmB,CAAC;AAC3C;AAAA,EAAA;AAGJ,SAAO,EAAE,OAAO,IAAA;AAClB;;;;;;;;;;;AC1RO,IAAM,mBAAN,cAA+B,WAAW;AAAA,EAE/C,WAA0B;AAAA,EAE1B,UAAkB;AAAA,EAClB,OAAe;AAAA,EACf,cAAsB;AAAA,EACtB,SAAiC;AAAA,EACjC,YAAoB;AAAA,EACpB,cAAsB;AAAA,EACtB,WAAmB;AAAA,EACnB,kBAAmC;AAAA,EACnC,mBAA2B;AAAA,EAC3B,kBAA0B;AAAA,EAC1B,gBAAwB;AAAA,EACxB,WAAmB;AAAA,EACnB,aAAqB;AAAA,EACrB,WAAmB;AAAA,EAEnB,YAAY,UAAmC,IAAI;AACjD,UAAM,OAAO;AACb,QAAI,QAAQ,aAAa,OAAW,MAAK,WAAW,QAAQ;AAC5D,QAAI,QAAQ,YAAY,OAAW,MAAK,UAAU,QAAQ;AAC1D,QAAI,QAAQ,SAAS,OAAW,MAAK,OAAO,QAAQ;AACpD,QAAI,QAAQ,gBAAgB;AAC1B,WAAK,cAAc,QAAQ;AAC7B,QAAI,QAAQ,WAAW,OAAW,MAAK,SAAS,QAAQ;AACxD,QAAI,QAAQ,cAAc,OAAW,MAAK,YAAY,QAAQ;AAC9D,QAAI,QAAQ,gBAAgB;AAC1B,WAAK,cAAc,QAAQ;AAC7B,QAAI,QAAQ,aAAa,OAAW,MAAK,WAAW,QAAQ;AAC5D,QAAI,QAAQ,oBAAoB,QAAW;AACzC,WAAK,kBAAkB,QAAQ;AAAA,IACjC;AACA,QAAI,QAAQ,qBAAqB,QAAW;AAC1C,WAAK,mBAAmB,QAAQ;AAAA,IAClC;AACA,QAAI,QAAQ,oBAAoB,QAAW;AACzC,WAAK,kBAAkB,QAAQ;AAAA,IACjC;AACA,QAAI,QAAQ,kBAAkB,QAAW;AACvC,WAAK,gBAAgB,QAAQ;AAAA,IAC/B;AACA,QAAI,QAAQ,aAAa,OAAW,MAAK,WAAW,QAAQ;AAC5D,QAAI,QAAQ,eAAe,OAAW,MAAK,aAAa,QAAQ;AAChE,QAAI,QAAQ,aAAa,OAAW,MAAK,WAAW,QAAQ;AAAA,EAC9D;AAAA,EAEA,WAAoB;AAClB,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA,EAEA,mBAAuC;AACrC,WAAO;AAAA,MACL,eAA0C,KAAK,QAAQ;AAAA,IAAA;AAAA,EAE3D;AAAA,EAEA,iBAAiB,QAAgD;AAC/D,SAAK,WAAW,cAAc,uBAAuB,MAAM,CAAC;AAAA,EAC9D;AAAA,EAEA,iBAA2B;AACzB,WAAO,KAAK,iBAAA,EACT,OAAO,CAAC,UAAU,MAAM,YAAY,KAAK,EACzC,IAAI,CAAC,UAAU,MAAM,UAAU;AAAA,EACpC;AAAA,EAEA,gBAAiC;AAC/B,WAAO,eAA8B,KAAK,UAAU,EAAE;AAAA,MACpD,CAAC,cAAc,UAAU;AAAA,IAAA;AAAA,EAE7B;AAAA,EAEA,cAAc,YAAmC;AAC/C,SAAK,aAAa,cAAc,UAAU;AAAA,EAC5C;AAAA,EAEA,cAA0B;AACxB,WAAO,gBAAgB,KAAK,UAAU,EAAE;AAAA,EAC1C;AAAA,EAEA,YAAY,UAA4B;AACtC,SAAK,WAAW,cAAc,QAAQ;AAAA,EACxC;AACF;AAnFEA,kBAAA;AAAA,EADC,SAAS,EAAE,UAAU,KAAA,CAAM;AAAA,GADjB,iBAEX,WAAA,YAAA,CAAA;AAFW,mBAANA,kBAAA;AAAA,EARN,aAAa,EAAE,MAAM,YAAY;AAAA,EACjC,KAAK;AAAA,IACJ,WAAW;AAAA,IACX,KAAK,EAAE,SAAS,CAAC,QAAQ,OAAO,UAAU,QAAQ,EAAA;AAAA,IAClD,KAAK;AAAA,IACL,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAA;AAAA,IAC9B,iBAAiB,CAAC,UAAU;AAAA,EAAA,CAC7B;AAAA,GACY,gBAAA;ACtBN,MAAM,mCAAmC,eAAiC;AAAA,EAC/E,OAAgB,aAAa;AAAA,EAE7B,MAAM,cAAc,SAAmD;AACrE,UAAM,QAAQ,MAAM,KAAK,KAAK,EAAE,OAAO,EAAE,QAAA,GAAW,OAAO,GAAG;AAC9D,WAAO,MAAM,CAAC,KAAK;AAAA,EACrB;AAAA,EAEA,MAAM,aAA0C;AAC9C,WAAO,KAAK,KAAK;AAAA,MACf,OAAO,EAAE,QAAQ,SAAA;AAAA,MACjB,SAAS;AAAA,IAAA,CACV;AAAA,EACH;AAAA,EAEA,MAAM,oBACJ,eACkC;AAClC,UAAM,QAAQ,MAAM,KAAK,KAAK,EAAE,OAAO,EAAE,cAAA,GAAiB,OAAO,GAAG;AACpE,WAAO,MAAM,CAAC,KAAK;AAAA,EACrB;AACF;;;;;;;;;;;ACeO,IAAM,qBAAN,cAAiC,WAAW;AAAA,EAEjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,iBAAiC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUjC,uBAA+B;AAAA,EAG/B,SAAiB;AAAA,EAEjB,SAA6B;AAAA,EAC7B,gCAAsB,KAAA;AAAA,EACtB,qBAAkC;AAAA,EAClC,mBAAgC;AAAA,EAChC,cAA2B;AAAA,EAC3B,oBAA6B;AAAA,EAC7B,aAA0B;AAAA,EAC1B,mBAA2B;AAAA,EAC3B,mBAA2B;AAAA,EAC3B,uBAA+B;AAAA,EAC/B,0BAAkC;AAAA,EAClC,WAAmB;AAAA,EAEnB,YAAY,UAAqC,IAAI;AACnD,UAAM,OAAO;AACb,QAAI,QAAQ,aAAa,OAAW,MAAK,WAAW,QAAQ;AAC5D,QAAI,QAAQ,mBAAmB;AAC7B,WAAK,iBAAiB,QAAQ;AAChC,QAAI,QAAQ,yBAAyB;AACnC,WAAK,uBAAuB,QAAQ;AAMtC,8BAA0B,sBAAsB;AAAA,MAC9C,gBAAgB,KAAK;AAAA,MACrB,sBAAsB,KAAK;AAAA,IAAA,CAC5B;AACD,QAAI,QAAQ,WAAW,OAAW,MAAK,SAAS,QAAQ;AACxD,QAAI,QAAQ,WAAW,OAAW,MAAK,SAAS,QAAQ;AACxD,QAAI,QAAQ,cAAc,OAAW,MAAK,YAAY,QAAQ;AAC9D,QAAI,QAAQ,uBAAuB,QAAW;AAC5C,WAAK,qBAAqB,QAAQ;AAAA,IACpC;AACA,QAAI,QAAQ,qBAAqB,QAAW;AAC1C,WAAK,mBAAmB,QAAQ;AAAA,IAClC;AACA,QAAI,QAAQ,gBAAgB;AAC1B,WAAK,cAAc,QAAQ;AAC7B,QAAI,QAAQ,sBAAsB,QAAW;AAC3C,WAAK,oBAAoB,QAAQ;AAAA,IACnC;AACA,QAAI,QAAQ,eAAe,OAAW,MAAK,aAAa,QAAQ;AAChE,QAAI,QAAQ,qBAAqB,QAAW;AAC1C,WAAK,mBAAmB,QAAQ;AAAA,IAClC;AACA,QAAI,QAAQ,qBAAqB,QAAW;AAC1C,WAAK,mBAAmB,QAAQ;AAAA,IAClC;AACA,QAAI,QAAQ,yBAAyB,QAAW;AAC9C,WAAK,uBAAuB,QAAQ;AAAA,IACtC;AACA,QAAI,QAAQ,4BAA4B,QAAW;AACjD,WAAK,0BAA0B,QAAQ;AAAA,IACzC;AACA,QAAI,QAAQ,aAAa,OAAW,MAAK,WAAW,QAAQ;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAgB,qBAAoC;AAClD,UAAM,MAAM,mBAAA;AACZ,8BAA0B,sBAAsB;AAAA,MAC9C,gBAAgB,KAAK;AAAA,MACrB,sBAAsB,KAAK;AAAA,IAAA,CAC5B;AAAA,EACH;AAAA,EAEA,WAAW,MAAM,oBAAI,QAAiB;AACpC,QAAI,KAAK,WAAW,YAAY,KAAK,WAAW,YAAY;AAC1D,aAAO;AAAA,IACT;AACA,QACE,KAAK,oBACL,KAAK,iBAAiB,YAAY,IAAI,WACtC;AACA,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAmC;AACjC,QAAI,CAAC,KAAK,UAAU;AAClB,aAAO;AAAA,IACT;AACA,QAAI,KAAK,mBAAmB,YAAY;AACtC,UAAI,CAAC,KAAK,sBAAsB;AAC9B,eAAO;AAAA,MACT;AACA,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,KAAK;AAAA,QACf,YAAY,KAAK;AAAA,MAAA;AAAA,IAErB;AACA,WAAO,EAAE,MAAM,UAAU,UAAU,KAAK,SAAA;AAAA,EAC1C;AAAA,EAEA,cAA0B;AACxB,WAAO,gBAAgB,KAAK,UAAU,EAAE;AAAA,EAC1C;AAAA,EAEA,YAAY,UAA4B;AACtC,SAAK,WAAW,cAAc,QAAQ;AAAA,EACxC;AACF;AAhJEA,kBAAA;AAAA,EADC,SAAA;AAAS,GADC,mBAEX,WAAA,YAAA,CAAA;AAwBAA,kBAAA;AAAA,EADC,WAAW,kBAAkB;AAAA,GAzBnB,mBA0BX,WAAA,UAAA,CAAA;AA1BW,qBAANA,kBAAA;AAAA,EAxBN,aAAa,EAAE,MAAM,YAAY;AAAA,EACjC,KAAK;AAAA,IACJ,WAAW;AAAA,IACX,KAAK,EAAE,SAAS,CAAC,QAAQ,OAAO,UAAU,QAAQ,EAAA;AAAA,IAClD,KAAK;AAAA,IACL,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAiB9B,iBAAiB,CAAC,aAAa,mBAAmB,wBAAwB;AAAA,EAAA,CAC3E;AAAA,GACY,kBAAA;ACnCN,MAAM,qCAAqC,eAAmC;AAAA,EACnF,OAAgB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO7B,MAAM,aAAaC,WAAiD;AAClE,WAAO,KAAK,KAAK;AAAA,MACf,OAAO,EAAE,UAAAA,UAAA;AAAA,MACT,SAAS;AAAA,IAAA,CACV;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,qBACJA,WACA,MAAM,oBAAI,QAC0B;AACpC,WAAO,KAAK,yBAAyB,EAAE,MAAM,UAAU,UAAAA,UAAA,GAAY,GAAG;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,yBACJ,YACA,MAAM,oBAAI,QAC0B;AACpC,UAAM,QAAiC;AAAA,MACrC,UAAU,WAAW;AAAA,MACrB,gBAAgB,WAAW;AAAA,IAAA;AAE7B,QAAI,WAAW,SAAS,YAAY;AAClC,YAAM,uBAAuB,WAAW;AAAA,IAC1C;AAEA,UAAM,gBAAgB,MAAM,KAAK,KAAK;AAAA,MACpC;AAAA,MACA,SAAS;AAAA,IAAA,CACV;AACD,WACE,cAAc,KAAK,CAAC,iBAAiB,aAAa,WAAW,GAAG,CAAC,KACjE,cAAc,CAAC,KACf;AAAA,EAEJ;AAAA,EAEA,MAAM,2BACJ,sBACoC;AACpC,UAAM,gBAAgB,MAAM,KAAK,KAAK;AAAA,MACpC,OAAO,EAAE,qBAAA;AAAA,MACT,OAAO;AAAA,IAAA,CACR;AACD,WAAO,cAAc,CAAC,KAAK;AAAA,EAC7B;AACF;;;;;;;;;;;AClDO,IAAM,oBAAN,cAAgC,WAAW;AAAA,EAEhD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAiC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMjC,uBAA+B;AAAA,EAE/B,YAAoB;AAAA,EACpB,WAAmB;AAAA,EACnB,kCAAwB,KAAA;AAAA,EACxB,gCAAsB,KAAA;AAAA,EACtB,SAAiB;AAAA,EACjB,WAAmB;AAAA,EACnB,aAAqB;AAAA,EAErB,YAAY,UAAoC,IAAI;AAClD,UAAM,OAAO;AACb,QAAI,QAAQ,aAAa,OAAW,MAAK,WAAW,QAAQ;AAC5D,QAAI,QAAQ,mBAAmB;AAC7B,WAAK,iBAAiB,QAAQ;AAChC,QAAI,QAAQ,yBAAyB;AACnC,WAAK,uBAAuB,QAAQ;AAGtC,8BAA0B,qBAAqB;AAAA,MAC7C,gBAAgB,KAAK;AAAA,MACrB,sBAAsB,KAAK;AAAA,IAAA,CAC5B;AACD,QAAI,QAAQ,cAAc,OAAW,MAAK,YAAY,QAAQ;AAC9D,QAAI,QAAQ,aAAa,OAAW,MAAK,WAAW,QAAQ;AAC5D,QAAI,QAAQ,gBAAgB;AAC1B,WAAK,cAAc,QAAQ;AAC7B,QAAI,QAAQ,cAAc,OAAW,MAAK,YAAY,QAAQ;AAC9D,QAAI,QAAQ,WAAW,OAAW,MAAK,SAAS,QAAQ;AACxD,QAAI,QAAQ,aAAa,OAAW,MAAK,WAAW,QAAQ;AAC5D,QAAI,QAAQ,eAAe,OAAW,MAAK,aAAa,QAAQ;AAAA,EAClE;AAAA;AAAA,EAGA,MAAgB,qBAAoC;AAClD,UAAM,MAAM,mBAAA;AACZ,8BAA0B,qBAAqB;AAAA,MAC7C,gBAAgB,KAAK;AAAA,MACrB,sBAAsB,KAAK;AAAA,IAAA,CAC5B;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAmC;AACjC,QAAI,CAAC,KAAK,UAAU;AAClB,aAAO;AAAA,IACT;AACA,QAAI,KAAK,mBAAmB,YAAY;AACtC,UAAI,CAAC,KAAK,sBAAsB;AAC9B,eAAO;AAAA,MACT;AACA,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,KAAK;AAAA,QACf,YAAY,KAAK;AAAA,MAAA;AAAA,IAErB;AACA,WAAO,EAAE,MAAM,UAAU,UAAU,KAAK,SAAA;AAAA,EAC1C;AAAA,EAEA,gBAA4B;AAC1B,WAAO,gBAAgB,KAAK,YAAY,EAAE;AAAA,EAC5C;AAAA,EAEA,cAAc,YAA8B;AAC1C,SAAK,aAAa,cAAc,UAAU;AAAA,EAC5C;AACF;AAnFE,gBAAA;AAAA,EADC,SAAA;AAAS,GADC,kBAEX,WAAA,YAAA,CAAA;AAFW,oBAAN,gBAAA;AAAA,EAPN,aAAa,EAAE,MAAM,YAAY;AAAA,EACjC,KAAK;AAAA,IACJ,WAAW;AAAA,IACX,KAAK,EAAE,SAAS,CAAC,QAAQ,OAAO,QAAQ,EAAA;AAAA,IACxC,KAAK;AAAA,IACL,KAAK,EAAE,SAAS,CAAC,QAAQ,KAAK,EAAA;AAAA,EAAE,CACjC;AAAA,GACY,iBAAA;ACLN,MAAM,oCAAoC,eAAkC;AAAA,EACjF,OAAgB,aAAa;AAAA,EAE7B,MAAM,YAAY,SAAyD;AACzE,UAAM,aAAa,oBAAoB;AAAA,MACrC,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,sBAAsB,QAAQ;AAAA,IAAA,CAC/B;AACD,UAAM,UAAU,oBAAoB,UAAU;AAC9C,UAAM,SAAS,MAAM,KAAK,OAAO;AAAA,MAC/B,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,sBAAsB,QAAQ;AAAA,MAC9B,WAAW,QAAQ;AAAA,MACnB,UAAU,QAAQ;AAAA,MAClB,aAAa,QAAQ;AAAA,MACrB,WAAW,QAAQ;AAAA,MACnB,QAAQ,QAAQ,UAAU;AAAA,MAC1B,UAAU,QAAQ,YAAY;AAAA,MAC9B,YAAY,cAAc,QAAQ,cAAc,CAAA,CAAE;AAAA,IAAA,CACnD;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,sBACJA,WACA,WAC8B;AAC9B,WAAO,KAAK,KAAK;AAAA,MACf,OAAO,EAAE,UAAAA,WAAU,WAAW,gBAAgB,SAAA;AAAA,MAC9C,SAAS;AAAA,IAAA,CACV;AAAA,EACH;AAAA,EAEA,MAAM,eAAe,SAAuD;AAC1E,UAAM,aAAa,oBAAoB;AAAA,MACrC,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,sBAAsB,QAAQ;AAAA,IAAA,CAC/B;AACD,UAAM,UAAU,oBAAoB,UAAU;AAE9C,UAAM,QAAiC;AAAA,MACrC,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,WAAW,QAAQ;AAAA,MACnB,iBAAiB,QAAQ,OAAO,IAAI,YAAA;AAAA,MACpC,eAAe,QAAQ,OAAO,MAAM,YAAA;AAAA,IAAY;AAElD,QAAI,WAAW,SAAS,YAAY;AAClC,YAAM,uBAAuB,QAAQ;AAAA,IACvC;AAEA,UAAM,UAAU,MAAM,KAAK,KAAK,EAAE,OAAO;AAEzC,UAAM,cAA4B;AAAA,MAChC,UAAU,QAAQ;AAAA,MAClB,WAAW,QAAQ;AAAA,MACnB,UAAU,QAAQ,OAAO,CAAC,KAAK,WAAW,MAAM,OAAO,UAAU,CAAC;AAAA,MAClE,aAAa,QAAQ,OAAO;AAAA,MAC5B,WAAW,QAAQ,OAAO;AAAA,IAAA;AAE5B,QAAI,WAAW,SAAS,YAAY;AAClC,aAAO;AAAA,QACL,GAAG;AAAA,QACH,gBAAgB;AAAA,QAChB,sBAAsB,WAAW;AAAA,MAAA;AAAA,IAErC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,oBACJ,SACyB;AACzB,UAAM,aAAa,MAAM,KAAK,IAAI,IAAI,QAAQ,UAAU,CAAC;AACzD,QAAI,WAAW,WAAW,GAAG;AAC3B,aAAO,CAAA;AAAA,IACT;AAEA,UAAM,aAAa,oBAAoB;AAAA,MACrC,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,sBAAsB,QAAQ;AAAA,IAAA,CAC/B;AACD,UAAM,UAAU,oBAAoB,UAAU;AAE9C,UAAM,qBAAqB,WAAW,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AAC9D,UAAM,SAAoB;AAAA,MACxB,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,GAAG;AAAA,MACH,QAAQ,OAAO,IAAI,YAAA;AAAA,MACnB,QAAQ,OAAO,MAAM,YAAA;AAAA,IAAY;AAEnC,QAAI,gBAAgB;AACpB,QAAI,WAAW,SAAS,YAAY;AAClC,sBAAgB;AAChB,aAAO,OAAO,GAAG,GAAG,QAAQ,oBAAoB;AAAA,IAClD;AAEA,UAAM,SAAS,MAAM,KAAK,GAAG;AAAA,MAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAMM,aAAa;AAAA,+BACM,kBAAkB;AAAA;AAAA;AAAA;AAAA,MAI3C,GAAG;AAAA,IAAA;AAGL,UAAM,uCAAuB,IAAA;AAC7B,eAAW,OAAO,WAAW,MAAM,GAAG;AACpC,YAAM,YAAY,OAAO,IAAI,cAAc,IAAI,aAAa,EAAE;AAC9D,UAAI,WAAW;AACb,yBAAiB,IAAI,WAAW,cAAc,KAAK,UAAU,CAAC;AAAA,MAChE;AAAA,IACF;AAEA,WAAO,WAAW,IAAI,CAAC,cAAc;AACnC,YAAM,cAA4B;AAAA,QAChC,UAAU,QAAQ;AAAA,QAClB;AAAA,QACA,UAAU,iBAAiB,IAAI,SAAS,KAAK;AAAA,QAC7C,aAAa,QAAQ,OAAO;AAAA,QAC5B,WAAW,QAAQ,OAAO;AAAA,MAAA;AAE5B,UAAI,WAAW,SAAS,YAAY;AAClC,eAAO;AAAA,UACL,GAAG;AAAA,UACH,gBAAgB;AAAA,UAChB,sBAAsB,WAAW;AAAA,QAAA;AAAA,MAErC;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,uBACJ,SACyB;AACzB,UAAM,SAAS,MAAM,KAAK,GAAG;AAAA,MAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAUA,QAAQ;AAAA,MACR,QAAQ,OAAO,MAAM,YAAA;AAAA,MACrB,QAAQ,OAAO,IAAI,YAAA;AAAA,IAAY;AAEjC,UAAM,MAAM,SAAS,MAAM;AAE3B,WAAO;AAAA,MACL,UAAU,QAAQ;AAAA,MAClB,cAAc,cAAc,KAAK,eAAe;AAAA,MAChD,kBAAkB,cAAc,KAAK,mBAAmB;AAAA,MACxD,aAAa,cAAc,KAAK,cAAc;AAAA,MAC9C,eAAe,cAAc,KAAK,gBAAgB;AAAA,MAClD,cAAc,cAAc,KAAK,eAAe;AAAA,MAChD,aAAa,QAAQ,OAAO;AAAA,MAC5B,WAAW,QAAQ,OAAO;AAAA,IAAA;AAAA,EAE9B;AACF;AAEA,SAAS,SAAS,QAA0C;AAC1D,QAAM,OAAO,WAAW,MAAM;AAC9B,SAAQ,KAAK,CAAC,KAAK,CAAA;AACrB;AAEA,SAAS,WAAW,QAA4C;AAC9D,SAAO,MAAM,QAAQ,MAAM,IACtB,SACC,QAAiD,QAAQ,CAAA;AACjE;AAEA,SAAS,cAAc,KAA8B,KAAqB;AACxE,QAAM,QAAQ,IAAI,GAAG;AACrB,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,OAAO,WAAW,KAAK,KAAK;AAAA,EACrC;AACA,SAAO;AACT;AClOO,SAAS,kBACd,WACA,OACqB;AACrB,QAAM,QACJ,UAAU,UAAU,IAChB,MAAM,WAAW,IACf,WACA,IACF,MAAM,WAAW,UAAU;AACjC,QAAM,eAAe,UAAU,gBAAgB;AAC/C,QAAM,UACJ,UAAU,gBAAgB,YACzB,UAAU,UAAU,IACjB,MAAM,WAAW,IACjB,MAAM,YAAY,UAAU;AAClC,QAAM,SAAS,CAAC,WAAW,SAAS;AAEpC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,UAAU,YAAY,SAAS,SAAS;AAAA,IAC/C,SAAS,CAAC;AAAA,IACV,WAAW,KAAK,IAAI,GAAG,UAAU,QAAQ,MAAM,QAAQ;AAAA,EAAA;AAE3D;AAEO,SAAS,mBACd,YACA,OACuB;AACvB,QAAM,gBAAgB,IAAI;AAAA,IACxB,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,WAAW,OAAO,CAAC;AAAA,EAAA;AAGrD,SAAO,WAAW,IAAI,CAAC,cAAc;AACnC,UAAM,UACJ,cAAc,IAAI,UAAU,SAAS,KACrCC,oBAAkB,UAAU,SAAS;AACvC,WAAO,kBAAkB,WAAW,OAAO;AAAA,EAC7C,CAAC;AACH;AAEA,SAASA,oBAAkB,WAAiC;AAC1D,QAAM,0BAAU,KAAA;AAChB,SAAO;AAAA,IACL,UAAU;AAAA,IACV;AAAA,IACA,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,EAAA;AAEf;AC/CO,MAAM,iBAAiB;AAAA,EAC5B,YACmB,SACA,eAAiC,IAClD;AAFiB,SAAA,UAAA;AACA,SAAA,eAAA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAGnB,aAAa,OACX,eAAiC,IACN;AAC3B,UAAM,UAAU,MAAM,4BAA4B,OAAO,YAAY;AACrE,WAAO,IAAI,iBAAiB,SAAS,YAAY;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,OAAO,SAA4C;AACvD,UAAM,KAAK,QAAQ,YAAY,OAAO;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,UAAU,SAAuD;AAMrE,wBAAoB;AAAA,MAClB,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,sBAAsB,QAAQ;AAAA,IAAA,CAC/B;AAED,UAAM,YAAY,MAAM,KAAK,qBAAqB,OAAO;AACzD,QAAI,WAAW;AACb,aAAO;AAAA,IACT;AAEA,WAAO,KAAK,QAAQ,eAAe,OAAO;AAAA,EAC5C;AAAA,EAEA,MAAM,eACJ,SACyB;AACzB,UAAM,aAAa,oBAAoB;AAAA,MACrC,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,sBAAsB,QAAQ;AAAA,IAAA,CAC/B;AACD,UAAM,aAAa,MAAM,KAAK,IAAI,IAAI,QAAQ,UAAU,CAAC;AACzD,QAAI,WAAW,WAAW,GAAG;AAC3B,aAAO,CAAA;AAAA,IACT;AAEA,UAAM,qBAAqB,WAAW,SAAS;AAC/C,UAAM,eAAe,qBACjB,WAAW,OAAO,CAAC,cAAc,UAAU,WAAW,KAAK,CAAC,IAC5D,CAAA;AACJ,UAAM,sBAAsB,qBACxB,WAAW,OAAO,CAAC,cAAc,CAAC,UAAU,WAAW,KAAK,CAAC,IAC7D;AAEJ,UAAM,gCAAgB,IAAA;AACtB,QAAI,aAAa,SAAS,GAAG;AAC3B,YAAM,YAAY,MAAM,KAAK,iBAAiB;AAAA,QAC5C,UAAU,QAAQ;AAAA,QAClB,QAAQ,QAAQ;AAAA,MAAA,CACjB;AACD,YAAM,mBAAmB,mBAAmB,SAAS;AACrD,iBAAW,aAAa,cAAc;AACpC,kBAAU,IAAI,WAAW;AAAA,UACvB,UAAU,QAAQ;AAAA,UAClB;AAAA,UACA,UAAU,iBAAiB,SAAS,KAAK;AAAA,UACzC,aAAa,QAAQ,OAAO;AAAA,UAC5B,WAAW,QAAQ,OAAO;AAAA,QAAA,CAC3B;AAAA,MACH;AAAA,IACF;AAEA,UAAM,qBAAqB,MAAM,KAAK,QAAQ,oBAAoB;AAAA,MAChE,GAAG;AAAA,MACH,YAAY;AAAA,IAAA,CACb;AACD,eAAW,WAAW,oBAAoB;AACxC,gBAAU,IAAI,QAAQ,WAAW,OAAO;AAAA,IAC1C;AAEA,WAAO,WAAW;AAAA,MAChB,CAAC,cACC,UAAU,IAAI,SAAS,KACvBA,oBAAkB,YAAY,WAAW,QAAQ,MAAM;AAAA,IAAA;AAAA,EAE7D;AAAA,EAEA,MAAM,iBACJ,SACyB;AACzB,WAAO,KAAK,QAAQ,uBAAuB,OAAO;AAAA,EACpD;AAAA,EAEA,aAA+B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,qBACZ,SAC8B;AAC9B,QAAI,CAAC,QAAQ,UAAU,WAAW,KAAK,GAAG;AACxC,aAAO;AAAA,IACT;AAIA,UAAM,OAAO,QAAQ,kBAAkB;AACvC,QAAI,SAAS,UAAU;AACrB,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,MAAM,KAAK,iBAAiB;AAAA,MAC1C,UAAU,QAAQ;AAAA,MAClB,QAAQ,QAAQ;AAAA,IAAA,CACjB;AAED,UAAM,mBAA2C;AAAA,MAC/C,GAAG,mBAAmB,OAAO;AAAA,IAAA;AAG/B,WAAO;AAAA,MACL,UAAU,QAAQ;AAAA,MAClB,WAAW,QAAQ;AAAA,MACnB,UAAU,iBAAiB,QAAQ,SAAS,KAAK;AAAA,MACjD,aAAa,QAAQ,OAAO;AAAA,MAC5B,WAAW,QAAQ,OAAO;AAAA,IAAA;AAAA,EAE9B;AACF;AAEA,SAAS,mBAAmB,SAAiD;AAC3E,SAAO;AAAA,IACL,oBAAoB,QAAQ;AAAA,IAC5B,wBAAwB,QAAQ;AAAA,IAChC,mBAAmB,QAAQ;AAAA,IAC3B,qBAAqB,QAAQ;AAAA,IAC7B,eAAe,QAAQ;AAAA,EAAA;AAE3B;AAEA,SAASA,oBACP,YACA,WACA,QACc;AACd,QAAM,UAAwB;AAAA,IAC5B,UAAU,WAAW;AAAA,IACrB;AAAA,IACA,UAAU;AAAA,IACV,aAAa,OAAO;AAAA,IACpB,WAAW,OAAO;AAAA,EAAA;AAEpB,MAAI,WAAW,SAAS,YAAY;AAClC,WAAO;AAAA,MACL,GAAG;AAAA,MACH,gBAAgB;AAAA,MAChB,sBAAsB,WAAW;AAAA,IAAA;AAAA,EAErC;AACA,SAAO;AACT;AChHO,MAAM,qBAAqB;AAAA,EAChC,YAA6B,SAAsC;AAAtC,SAAA,UAAA;AAAA,EAAuC;AAAA,EAAvC;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7B,aAAa,OACX,eAAiC,IACF;AAC/B,UAAM,CAAC,OAAO,eAAe,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,MACtD,2BAA2B,OAAO,YAAY;AAAA,MAC9C,6BAA6B,OAAO,YAAY;AAAA,MAChD,iBAAiB,OAAO,YAAY;AAAA,IAAA,CACrC;AACD,WAAO,IAAI,qBAAqB,EAAE,OAAO,eAAe,OAAO;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,uBACJ,sBACA,UAAuC,IACA;AACvC,UAAM,aAAa,aAAa,oBAAoB;AACpD,UAAM,MAAM,QAAQ,OAAO,oBAAI,KAAA;AAC/B,UAAM,eAAe,MAAM,KAAK;AAAA,MAC9B;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,IAAA;AAEV,UAAM,OAAO,MAAM,KAAK,YAAY,cAAc,QAAQ,OAAO;AACjE,WAAO,EAAE,cAAc,KAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,oBACJ,YACA,UAAuC,IACP;AAChC,UAAM,MAAM,QAAQ,OAAO,oBAAI,KAAA;AAC/B,UAAM,EAAE,cAAc,SAAS,MAAM,KAAK;AAAA,MACxC;AAAA,MACA,EAAE,GAAG,SAAS,IAAA;AAAA,IAAI;AAGpB,QAAI,CAAC,cAAc;AACjB,aAAO,gBAAgB,UAAU;AAAA,IACnC;AAEA,QAAI,CAAC,QAAQ,CAAC,KAAK,SAAA,KAAc,CAAC,aAAa,WAAW,GAAG,GAAG;AAC9D,aAAO;AAAA,QACL,GAAG,gBAAgB,UAAU;AAAA,QAC7B,QAAQ,MAAM,MAAM,aAAa,UAAU;AAAA,QAC3C,SAAS,MAAM,WAAW;AAAA,QAC1B,gBAAgB,aAAa,MAAM;AAAA,QACnC,QAAQ,aAAa;AAAA,MAAA;AAAA,IAEzB;AAEA,UAAM,aAAa,KAAK,cAAA,EAAgB,OAAO,gBAAgB;AAC/D,UAAM,uBAAuB,MAAM,KAAK;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAGF,WAAO;AAAA,MACL,UAAU,WAAW;AAAA,MACrB;AAAA,MACA,QAAQ,KAAK,MAAM;AAAA,MACnB,SAAS,KAAK;AAAA,MACd,gBAAgB,aAAa,MAAM;AAAA,MACnC,QAAQ,aAAa;AAAA,MACrB,aAAa,KAAK,eAAA;AAAA,MAClB;AAAA,MACA;AAAA,MACA,SAAS,qBAAqB,MAAM,CAAC,eAAe,WAAW,OAAO;AAAA,IAAA;AAAA,EAE1E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,0BACJD,WACA,UAAuC,IACP;AAChC,WAAO,KAAK,oBAAoB,EAAE,MAAM,UAAU,UAAAA,UAAA,GAAY,OAAO;AAAA,EACvE;AAAA,EAEA,MAAM,iBACJ,sBACA,YACA,UAAuC,CAAA,GACrB;AAClB,UAAM,aAAa,MAAM,KAAK;AAAA,MAC5B,aAAa,oBAAoB;AAAA,MACjC;AAAA,IAAA;AAEF,WAAO,WAAW,YAAY,SAAS,UAAU;AAAA,EACnD;AAAA,EAEA,MAAM,uBACJ,sBACA,UAAuC,IACxB;AACf,UAAM,aAAa,aAAa,oBAAoB;AACpD,UAAM,aAAa,MAAM,KAAK,oBAAoB,YAAY,OAAO;AACrE,UAAM,UAAU,WAAW,qBAAqB;AAAA,MAC9C,CAAC,eAAe,CAAC,WAAW;AAAA,IAAA;AAG9B,QAAI,SAAS;AACX,YAAM,UACJ,WAAW,SAAS,aAChB,YAAY,WAAW,UAAU,KACjC,UAAU,WAAW,QAAQ;AACnC,YAAM,IAAI;AAAA,QACR,GAAG,OAAO,oCAAoC,QAAQ,UAAU,SAAS;AAAA,MAAA;AAAA,IAE7E;AAAA,EACF;AAAA,EAEA,MAAc,oBACZ,YACA,KACA,SACoC;AACpC,QAAI,gBAAgB,SAAS,cAAc,GAAG;AAC5C,YAAM,eAAe,SAAS,gBAAgB;AAC9C,0CAAoC,cAAc,UAAU;AAC5D,aAAO;AAAA,IACT;AACA,WAAO,KAAK,wBAAwB,YAAY,GAAG;AAAA,EACrD;AAAA,EAEA,MAAc,YACZ,cACA,SACkC;AAClC,QAAI,CAAC,cAAc,QAAQ;AACzB,aAAO;AAAA,IACT;AACA,QAAI,gBAAgB,SAAS,MAAM,GAAG;AACpC,YAAM,OAAO,SAAS,QAAQ;AAC9B,oCAA8B,MAAM,YAAY;AAChD,aAAO;AAAA,IACT;AACA,WAAO,KAAK,QAAQ,MAAM,IAAI,EAAE,IAAI,aAAa,QAAQ;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,wBACZ,YACA,KACoC;AACpC,QAAI,KAAK,QAAQ,cAAc,0BAA0B;AACvD,aAAO,KAAK,QAAQ,cAAc;AAAA,QAChC;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AACA,QAAI,WAAW,SAAS,UAAU;AAChC,aAAO,KAAK,QAAQ,cAAc;AAAA,QAChC,WAAW;AAAA,QACX;AAAA,MAAA;AAAA,IAEJ;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAGJ;AAAA,EAEA,MAAc,4BACZ,YACA,YACA,KACA,SACgC;AAChC,QAAI,WAAW,WAAW,GAAG;AAC3B,aAAO,CAAA;AAAA,IACT;AAEA,QAAI,CAAC,KAAK,QAAQ,MAAM,gBAAgB;AACtC,YAAME,eAAqC,CAAA;AAC3C,iBAAW,aAAa,YAAY;AAClC,cAAM,SACJ,QAAQ,eAAe,UAAU,MAAM,KACvC,sBAAsB,UAAU,QAAQ,GAAG;AAC7C,cAAM,QAAQ,MAAM,KAAK,QAAQ,MAAM,UAAU;AAAA,UAC/C,UAAU,WAAW;AAAA,UACrB,gBAAgB,WAAW;AAAA,UAC3B,sBACE,WAAW,SAAS,aAAa,WAAW,aAAa;AAAA,UAC3D,WAAW,UAAU;AAAA,UACrB;AAAA,QAAA,CACD;AACDA,qBAAY,KAAK,kBAAkB,WAAW,KAAK,CAAC;AAAA,MACtD;AACA,aAAOA;AAAAA,IACT;AAEA,UAAM,6BAAa,IAAA;AAQnB,eAAW,QAAQ,CAAC,WAAW,UAAU;AACvC,YAAM,SACJ,QAAQ,eAAe,UAAU,MAAM,KACvC,sBAAsB,UAAU,QAAQ,GAAG;AAC7C,YAAM,MAAM,aAAa,MAAM;AAC/B,YAAM,QAAQ,OAAO,IAAI,GAAG,KAAK,EAAE,QAAQ,SAAS,GAAC;AACrD,YAAM,QAAQ,KAAK,EAAE,OAAO,WAAW;AACvC,aAAO,IAAI,KAAK,KAAK;AAAA,IACvB,CAAC;AAED,UAAM,cAAqC,IAAI,MAAM,WAAW,MAAM;AACtE,UAAM,QAAQ;AAAA,MACZ,MAAM,KAAK,OAAO,OAAA,CAAQ,EAAE,IAAI,OAAO,EAAE,QAAQ,cAAc;AAC7D,cAAM,aAAa;AAAA,UACjB,QAAQ,IAAI,CAAC,UAAU,MAAM,UAAU,SAAS;AAAA,QAAA;AAElD,cAAM,YAAY,MAAM,KAAK,QAAQ,MAAM,iBAAiB;AAAA,UAC1D,UAAU,WAAW;AAAA,UACrB,gBAAgB,WAAW;AAAA,UAC3B,sBACE,WAAW,SAAS,aAAa,WAAW,aAAa;AAAA,UAC3D;AAAA,UACA;AAAA,QAAA,CACD;AACD,cAAM,kBAAkB,IAAI;AAAA,WACzB,aAAa,CAAA,GAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,WAAW,OAAO,CAAC;AAAA,QAAA;AAGjE,mBAAW,EAAE,OAAO,UAAA,KAAe,SAAS;AAC1C,gBAAM,QACJ,gBAAgB,IAAI,UAAU,SAAS,KACvC,kBAAkB,YAAY,UAAU,WAAW,MAAM;AAC3D,sBAAY,KAAK,IAAI,kBAAkB,WAAW,KAAK;AAAA,QACzD;AAAA,MACF,CAAC;AAAA,IAAA;AAGH,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,OAAwC;AAC5D,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,EAAE,MAAM,UAAU,UAAU,MAAA;AAAA,EACrC;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,YAA+C;AACtE,SAAO;AAAA,IACL,UAAU,WAAW;AAAA,IACrB;AAAA,IACA,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,aAAa,CAAA;AAAA,IACb,YAAY,CAAA;AAAA,IACZ,sBAAsB,CAAA;AAAA,IACtB,SAAS;AAAA,EAAA;AAEb;AAEA,SAAS,gBACP,SACA,KACS;AACT,SAAO,UAAU,GAAG,MAAM;AAC5B;AAEA,SAAS,oCACP,cACA,YACM;AACN,MAAI,CAAC,cAAc;AACjB;AAAA,EACF;AACA,QAAM,yBAAyB,aAAa,cAAA;AAC5C,MACE,CAAC,0BACD,CAAC,eAAe,wBAAwB,UAAU,GAClD;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACF;AAEA,SAAS,8BACP,MACA,cACM;AACN,MAAI,CAAC,MAAM;AACT;AAAA,EACF;AACA,MAAI,CAAC,KAAK,MAAM,KAAK,OAAO,aAAa,QAAQ;AAC/C,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACF;AAEA,SAAS,eAAe,MAAkB,OAA4B;AACpE,MAAI,KAAK,SAAS,MAAM,QAAQ,KAAK,aAAa,MAAM,UAAU;AAChE,WAAO;AAAA,EACT;AACA,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AACA,SAAO,MAAM,SAAS,cAAc,KAAK,eAAe,MAAM;AAChE;AAEA,SAAS,iBAAiB,YAAgC;AACxD,SAAO,MAAM,KAAK,IAAI,IAAI,UAAU,CAAC;AACvC;AAEA,SAAS,kBACP,YACA,WACA,QACc;AACd,QAAM,UAAwB;AAAA,IAC5B,UAAU,WAAW;AAAA,IACrB;AAAA,IACA,UAAU;AAAA,IACV,aAAa,OAAO;AAAA,IACpB,WAAW,OAAO;AAAA,EAAA;AAEpB,MAAI,WAAW,SAAS,YAAY;AAClC,WAAO;AAAA,MACL,GAAG;AAAA,MACH,gBAAgB;AAAA,MAChB,sBAAsB,WAAW;AAAA,IAAA;AAAA,EAErC;AACA,SAAO;AACT;"}
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": "1.0.0",
3
- "timestamp": 1782329190298,
3
+ "timestamp": 1782335238107,
4
4
  "packageName": "@happyvertical/smrt-subscriptions",
5
- "packageVersion": "0.34.9",
5
+ "packageVersion": "0.35.1",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-subscriptions:SubscriptionPlanCollection": {
8
8
  "name": "subscriptionplancollection",
@@ -1,5 +1,5 @@
1
1
  import { SmrtObject } from '@happyvertical/smrt-core';
2
- import { BillingInterval, JsonObject, PlanFeatureGrant, PlanThreshold, SubscriptionPlanStatus } from '../types.js';
2
+ import { BillingInterval, JsonObject, PlanFeatureGrant, PlanThreshold, SubscriptionPlanOptions, SubscriptionPlanStatus } from '../types.js';
3
3
  export declare class SubscriptionPlan extends SmrtObject {
4
4
  tenantId: string | null;
5
5
  planKey: string;
@@ -16,7 +16,7 @@ export declare class SubscriptionPlan extends SmrtObject {
16
16
  features: string;
17
17
  thresholds: string;
18
18
  metadata: string;
19
- constructor(options?: any);
19
+ constructor(options?: SubscriptionPlanOptions);
20
20
  isActive(): boolean;
21
21
  getFeatureGrants(): PlanFeatureGrant[];
22
22
  setFeatureGrants(grants: Array<string | PlanFeatureGrant>): void;
@@ -1 +1 @@
1
- {"version":3,"file":"SubscriptionPlan.d.ts","sourceRoot":"","sources":["../../src/models/SubscriptionPlan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAE5D,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAQrB,qBAQa,gBAAiB,SAAQ,UAAU;IAE9C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE/B,OAAO,EAAE,MAAM,CAAM;IACrB,IAAI,EAAE,MAAM,CAAM;IAClB,WAAW,EAAE,MAAM,CAAM;IACzB,MAAM,EAAE,sBAAsB,CAAY;IAC1C,SAAS,EAAE,MAAM,CAAK;IACtB,WAAW,EAAE,MAAM,CAAO;IAC1B,QAAQ,EAAE,MAAM,CAAS;IACzB,eAAe,EAAE,eAAe,CAAW;IAC3C,gBAAgB,EAAE,MAAM,CAAY;IACpC,eAAe,EAAE,MAAM,CAAM;IAC7B,aAAa,EAAE,MAAM,CAAM;IAC3B,QAAQ,EAAE,MAAM,CAAQ;IACxB,UAAU,EAAE,MAAM,CAAQ;IAC1B,QAAQ,EAAE,MAAM,CAAQ;gBAEZ,OAAO,GAAE,GAAQ;IA6B7B,QAAQ,IAAI,OAAO;IAInB,gBAAgB,IAAI,gBAAgB,EAAE;IAMtC,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,gBAAgB,CAAC,GAAG,IAAI;IAIhE,cAAc,IAAI,MAAM,EAAE;IAM1B,aAAa,IAAI,aAAa,EAAE;IAMhC,aAAa,CAAC,UAAU,EAAE,aAAa,EAAE,GAAG,IAAI;IAIhD,WAAW,IAAI,UAAU;IAIzB,WAAW,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI;CAGxC;AAED,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"SubscriptionPlan.d.ts","sourceRoot":"","sources":["../../src/models/SubscriptionPlan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAE5D,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,uBAAuB,EACvB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAQrB,qBAQa,gBAAiB,SAAQ,UAAU;IAE9C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE/B,OAAO,EAAE,MAAM,CAAM;IACrB,IAAI,EAAE,MAAM,CAAM;IAClB,WAAW,EAAE,MAAM,CAAM;IACzB,MAAM,EAAE,sBAAsB,CAAY;IAC1C,SAAS,EAAE,MAAM,CAAK;IACtB,WAAW,EAAE,MAAM,CAAO;IAC1B,QAAQ,EAAE,MAAM,CAAS;IACzB,eAAe,EAAE,eAAe,CAAW;IAC3C,gBAAgB,EAAE,MAAM,CAAY;IACpC,eAAe,EAAE,MAAM,CAAM;IAC7B,aAAa,EAAE,MAAM,CAAM;IAC3B,QAAQ,EAAE,MAAM,CAAQ;IACxB,UAAU,EAAE,MAAM,CAAQ;IAC1B,QAAQ,EAAE,MAAM,CAAQ;gBAEZ,OAAO,GAAE,uBAA4B;IA6BjD,QAAQ,IAAI,OAAO;IAInB,gBAAgB,IAAI,gBAAgB,EAAE;IAMtC,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,gBAAgB,CAAC,GAAG,IAAI;IAIhE,cAAc,IAAI,MAAM,EAAE;IAM1B,aAAa,IAAI,aAAa,EAAE;IAMhC,aAAa,CAAC,UAAU,EAAE,aAAa,EAAE,GAAG,IAAI;IAIhD,WAAW,IAAI,UAAU;IAIzB,WAAW,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI;CAGxC;AAED,eAAe,gBAAgB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { SmrtObject } from '@happyvertical/smrt-core';
2
- import { JsonObject, Subscriber, SubscriberKind, SubscriptionStatus } from '../types.js';
2
+ import { JsonObject, Subscriber, SubscriberKind, SubscriptionStatus, TenantSubscriptionOptions } from '../types.js';
3
3
  export declare class TenantSubscription extends SmrtObject {
4
4
  tenantId?: string;
5
5
  /**
@@ -34,7 +34,7 @@ export declare class TenantSubscription extends SmrtObject {
34
34
  stripeSubscriptionId: string;
35
35
  stripeCheckoutSessionId: string;
36
36
  metadata: string;
37
- constructor(options?: any);
37
+ constructor(options?: TenantSubscriptionOptions);
38
38
  /**
39
39
  * Validates the subscriber XOR invariant before every save. Critical for the
40
40
  * generated update path: `SmrtCollection.update()` mutates the existing
@@ -1 +1 @@
1
- {"version":3,"file":"TenantSubscription.d.ts","sourceRoot":"","sources":["../../src/models/TenantSubscription.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAOrB,qBAwBa,kBAAmB,SAAQ,UAAU;IAEhD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;;;;;OAQG;IACH,cAAc,EAAE,cAAc,CAAY;IAE1C;;;;;;;OAOG;IACH,oBAAoB,EAAE,MAAM,CAAM;IAGlC,MAAM,EAAE,MAAM,CAAM;IAEpB,MAAM,EAAE,kBAAkB,CAAgB;IAC1C,SAAS,EAAE,IAAI,CAAc;IAC7B,kBAAkB,EAAE,IAAI,GAAG,IAAI,CAAQ;IACvC,gBAAgB,EAAE,IAAI,GAAG,IAAI,CAAQ;IACrC,WAAW,EAAE,IAAI,GAAG,IAAI,CAAQ;IAChC,iBAAiB,EAAE,OAAO,CAAS;IACnC,UAAU,EAAE,IAAI,GAAG,IAAI,CAAQ;IAC/B,gBAAgB,EAAE,MAAM,CAAY;IACpC,gBAAgB,EAAE,MAAM,CAAM;IAC9B,oBAAoB,EAAE,MAAM,CAAM;IAClC,uBAAuB,EAAE,MAAM,CAAM;IACrC,QAAQ,EAAE,MAAM,CAAQ;gBAEZ,OAAO,GAAE,GAAQ;IA8C7B;;;;;;;;OAQG;cACa,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQnD,UAAU,CAAC,GAAG,OAAa,GAAG,OAAO;IAarC;;;;OAIG;IACH,aAAa,IAAI,UAAU,GAAG,IAAI;IAiBlC,WAAW,IAAI,UAAU;IAIzB,WAAW,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI;CAGxC;AAED,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"TenantSubscription.d.ts","sourceRoot":"","sources":["../../src/models/TenantSubscription.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,yBAAyB,EAC1B,MAAM,aAAa,CAAC;AAOrB,qBAwBa,kBAAmB,SAAQ,UAAU;IAEhD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;;;;;OAQG;IACH,cAAc,EAAE,cAAc,CAAY;IAE1C;;;;;;;OAOG;IACH,oBAAoB,EAAE,MAAM,CAAM;IAGlC,MAAM,EAAE,MAAM,CAAM;IAEpB,MAAM,EAAE,kBAAkB,CAAgB;IAC1C,SAAS,EAAE,IAAI,CAAc;IAC7B,kBAAkB,EAAE,IAAI,GAAG,IAAI,CAAQ;IACvC,gBAAgB,EAAE,IAAI,GAAG,IAAI,CAAQ;IACrC,WAAW,EAAE,IAAI,GAAG,IAAI,CAAQ;IAChC,iBAAiB,EAAE,OAAO,CAAS;IACnC,UAAU,EAAE,IAAI,GAAG,IAAI,CAAQ;IAC/B,gBAAgB,EAAE,MAAM,CAAY;IACpC,gBAAgB,EAAE,MAAM,CAAM;IAC9B,oBAAoB,EAAE,MAAM,CAAM;IAClC,uBAAuB,EAAE,MAAM,CAAM;IACrC,QAAQ,EAAE,MAAM,CAAQ;gBAEZ,OAAO,GAAE,yBAA8B;IA8CnD;;;;;;;;OAQG;cACa,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQnD,UAAU,CAAC,GAAG,OAAa,GAAG,OAAO;IAarC;;;;OAIG;IACH,aAAa,IAAI,UAAU,GAAG,IAAI;IAiBlC,WAAW,IAAI,UAAU;IAIzB,WAAW,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI;CAGxC;AAED,eAAe,kBAAkB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { SmrtObject } from '@happyvertical/smrt-core';
2
- import { JsonObject, Subscriber, SubscriberKind } from '../types.js';
2
+ import { JsonObject, Subscriber, SubscriberKind, TenantUsageMetricOptions } from '../types.js';
3
3
  export declare class TenantUsageMetric extends SmrtObject {
4
4
  tenantId?: string;
5
5
  /**
@@ -20,7 +20,7 @@ export declare class TenantUsageMetric extends SmrtObject {
20
20
  source: string;
21
21
  sourceId: string;
22
22
  dimensions: string;
23
- constructor(options?: any);
23
+ constructor(options?: TenantUsageMetricOptions);
24
24
  /** See `TenantSubscription.validateBeforeSave` for the rationale. */
25
25
  protected validateBeforeSave(): Promise<void>;
26
26
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"TenantUsageMetric.d.ts","sourceRoot":"","sources":["../../src/models/TenantUsageMetric.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAE5D,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAO1E,qBAOa,iBAAkB,SAAQ,UAAU;IAE/C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAAY;IAE1C;;;OAGG;IACH,oBAAoB,EAAE,MAAM,CAAM;IAElC,SAAS,EAAE,MAAM,CAAM;IACvB,QAAQ,EAAE,MAAM,CAAO;IACvB,WAAW,EAAE,IAAI,CAAc;IAC/B,SAAS,EAAE,IAAI,CAAc;IAC7B,MAAM,EAAE,MAAM,CAAM;IACpB,QAAQ,EAAE,MAAM,CAAM;IACtB,UAAU,EAAE,MAAM,CAAQ;gBAEd,OAAO,GAAE,GAAQ;IAuB7B,qEAAqE;cACrD,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQnD;;;OAGG;IACH,aAAa,IAAI,UAAU,GAAG,IAAI;IAiBlC,aAAa,IAAI,UAAU;IAI3B,aAAa,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;CAG5C;AAED,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"TenantUsageMetric.d.ts","sourceRoot":"","sources":["../../src/models/TenantUsageMetric.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAE5D,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,wBAAwB,EACzB,MAAM,aAAa,CAAC;AAOrB,qBAOa,iBAAkB,SAAQ,UAAU;IAE/C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAAY;IAE1C;;;OAGG;IACH,oBAAoB,EAAE,MAAM,CAAM;IAElC,SAAS,EAAE,MAAM,CAAM;IACvB,QAAQ,EAAE,MAAM,CAAO;IACvB,WAAW,EAAE,IAAI,CAAc;IAC/B,SAAS,EAAE,IAAI,CAAc;IAC7B,MAAM,EAAE,MAAM,CAAM;IACpB,QAAQ,EAAE,MAAM,CAAM;IACtB,UAAU,EAAE,MAAM,CAAQ;gBAEd,OAAO,GAAE,wBAA6B;IAuBlD,qEAAqE;cACrD,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQnD;;;OAGG;IACH,aAAa,IAAI,UAAU,GAAG,IAAI;IAiBlC,aAAa,IAAI,UAAU;IAI3B,aAAa,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;CAG5C;AAED,eAAe,iBAAiB,CAAC"}
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-06-24T19:26:31.126Z",
3
+ "generatedAt": "2026-06-24T21:07:18.478Z",
4
4
  "packageName": "@happyvertical/smrt-subscriptions",
5
- "packageVersion": "0.34.9",
5
+ "packageVersion": "0.35.1",
6
6
  "sourceManifestPath": "dist/manifest.json",
7
7
  "agentDocPath": "AGENTS.md",
8
8
  "sourceHashes": {
9
- "manifest": "f41d1082c6d41b0a7570fae81994b74cb987dfc311227751ab7a554bdf6ec34f",
10
- "packageJson": "4fa885b95a097568e8856f4065ce41fcfb6767c09aa18ee1348b33fe0c9c2b9c",
9
+ "manifest": "8bc1d9e87bdbacd13132cc17d697e9d66ac71c1e014d421a76110bf4a5746738",
10
+ "packageJson": "c28ddde7cc221944b77c1f8e439b35c6b57f1fe72616b82f912fd6765b61c9bc",
11
11
  "agents": "97061be30df384ee234135e176165d3ecc6303146e596d3dd64c58ce508ae3b4"
12
12
  },
13
13
  "exports": [
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { SmrtClassOptions } from '@happyvertical/smrt-core';
1
+ import { SmrtClassOptions, SmrtObjectOptions } from '@happyvertical/smrt-core';
2
2
  import { SubscriptionPlan } from './models/SubscriptionPlan.js';
3
3
  import { TenantSubscription } from './models/TenantSubscription.js';
4
4
  export type SubscriptionStatus = 'active' | 'canceled' | 'incomplete' | 'past_due' | 'trialing' | 'unpaid';
@@ -171,5 +171,67 @@ export interface SummarizeAiUsageOptions {
171
171
  window: UsageWindow;
172
172
  }
173
173
  export type JsonObject = Record<string, unknown>;
174
+ /**
175
+ * Constructor options for {@link SubscriptionPlan}. Each field mirrors a
176
+ * persisted column and is optional; unspecified values fall back to the class
177
+ * initializers.
178
+ */
179
+ export interface SubscriptionPlanOptions extends SmrtObjectOptions {
180
+ tenantId?: string | null;
181
+ planKey?: string;
182
+ name?: string;
183
+ description?: string;
184
+ status?: SubscriptionPlanStatus;
185
+ sortOrder?: number;
186
+ priceAmount?: number;
187
+ currency?: string;
188
+ billingInterval?: BillingInterval;
189
+ externalProvider?: string;
190
+ stripeProductId?: string;
191
+ stripePriceId?: string;
192
+ features?: string;
193
+ thresholds?: string;
194
+ metadata?: string;
195
+ }
196
+ /**
197
+ * Constructor options for {@link TenantSubscription}. Each field mirrors a
198
+ * persisted column and is optional; unspecified values fall back to the class
199
+ * initializers.
200
+ */
201
+ export interface TenantSubscriptionOptions extends SmrtObjectOptions {
202
+ tenantId?: string;
203
+ subscriberKind?: SubscriberKind;
204
+ subscriberExternalId?: string;
205
+ planId?: string;
206
+ status?: SubscriptionStatus;
207
+ startedAt?: Date;
208
+ currentPeriodStart?: Date | null;
209
+ currentPeriodEnd?: Date | null;
210
+ trialEndsAt?: Date | null;
211
+ cancelAtPeriodEnd?: boolean;
212
+ canceledAt?: Date | null;
213
+ externalProvider?: string;
214
+ stripeCustomerId?: string;
215
+ stripeSubscriptionId?: string;
216
+ stripeCheckoutSessionId?: string;
217
+ metadata?: string;
218
+ }
219
+ /**
220
+ * Constructor options for {@link TenantUsageMetric}. Each field mirrors a
221
+ * persisted column and is optional; unspecified values fall back to the class
222
+ * initializers.
223
+ */
224
+ export interface TenantUsageMetricOptions extends SmrtObjectOptions {
225
+ tenantId?: string;
226
+ subscriberKind?: SubscriberKind;
227
+ subscriberExternalId?: string;
228
+ metricKey?: string;
229
+ quantity?: number;
230
+ windowStart?: Date;
231
+ windowEnd?: Date;
232
+ source?: string;
233
+ sourceId?: string;
234
+ dimensions?: string;
235
+ }
174
236
  export type { SmrtClassOptions };
175
237
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEzE,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,UAAU,GACV,YAAY,GACZ,UAAU,GACV,UAAU,GACV,QAAQ,CAAC;AAEb,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;AAErE,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEhE,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEhE,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5E,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,eAAe,CAAC;IACxB,WAAW,EAAE,oBAAoB,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,IAAI,CAAC;IACZ,GAAG,EAAE,IAAI,CAAC;CACX;AAED,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,IAAI,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,YAAY;IAC3B,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,gDAAgD;IAChD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,IAAI,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,IAAI,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,aAAa,CAAC;IACzB,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAAC;IACpC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,oBAAoB,EAAE,mBAAmB,EAAE,CAAC;IAC5C,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,4BAA4B;IAC3C;;;OAGG;IACH,YAAY,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACzC;;;OAGG;IACH,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,2BAA2B;IAC1C,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC,CAAC;IAC7D;;;OAGG;IACH,OAAO,CAAC,EAAE,4BAA4B,CAAC;CACxC;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAED,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;CAAG;AAEhE,MAAM,WAAW,qBAAqB;IACpC,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,qDAAqD;IACrD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,0BAA0B;IACzC,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,qDAAqD;IACrD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD;;;;GAIG;AACH,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,sBAAsB,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAA0B,SAAQ,iBAAiB;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,kBAAkB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACjC,gBAAgB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC/B,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAyB,SAAQ,iBAAiB;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-subscriptions",
3
- "version": "0.34.9",
3
+ "version": "0.35.1",
4
4
  "description": "Tenant subscriptions, entitlement resolution, usage thresholds, and subscription UI for SMRT",
5
5
  "type": "module",
6
6
  "smrtRawPrimitives": "strict",
@@ -27,9 +27,9 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@happyvertical/sql": "^0.74.7",
30
- "@happyvertical/smrt-core": "0.34.9",
31
- "@happyvertical/smrt-tenancy": "0.34.9",
32
- "@happyvertical/smrt-ui": "0.34.9"
30
+ "@happyvertical/smrt-tenancy": "0.35.1",
31
+ "@happyvertical/smrt-ui": "0.35.1",
32
+ "@happyvertical/smrt-core": "0.35.1"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "svelte": "^5.18.0"
@@ -47,7 +47,7 @@
47
47
  "typescript": "^5.9.3",
48
48
  "vite": "^7.3.1",
49
49
  "vitest": "^4.0.17",
50
- "@happyvertical/smrt-vitest": "0.34.9"
50
+ "@happyvertical/smrt-vitest": "0.35.1"
51
51
  },
52
52
  "keywords": [
53
53
  "smrt",