@happyvertical/smrt-subscriptions 0.43.9 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +15 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +200 -31
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/services/commercial.d.ts +25 -2
- package/dist/services/commercial.d.ts.map +1 -1
- package/dist/services/index.d.ts +2 -2
- package/dist/services/index.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +5 -5
- package/package.json +5 -5
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["evaluations"],"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/migrations/moneyMinorUnits.ts","../src/models/commercial.ts","../src/services/commercial.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 async function deterministicUuid(parts: string[]): Promise<string> {\n const bytes = new TextEncoder().encode(JSON.stringify(parts));\n const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));\n const uuid = digest.slice(0, 16);\n uuid[6] = (uuid[6] & 0x0f) | 0x50;\n uuid[8] = (uuid[8] & 0x3f) | 0x80;\n const hex = Array.from(uuid, (byte) =>\n byte.toString(16).padStart(2, '0'),\n ).join('');\n return [\n hex.slice(0, 8),\n hex.slice(8, 12),\n hex.slice(12, 16),\n hex.slice(16, 20),\n hex.slice(20),\n ].join('-');\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 /**\n * Plan price in **integer minor units** of {@link currency} — `$19.99` is\n * `1999`. The `= 0` initializer is load-bearing: SMRT maps an integer literal\n * to INTEGER and a decimal literal to DECIMAL, and money is exact (#2401).\n */\n priceAmount: number = 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: { include: ['list', 'get', 'create'] },\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 projectId: string = '';\n workRefType: string = '';\n workRefId: string = '';\n provider: 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.projectId !== undefined) this.projectId = options.projectId;\n if (options.workRefType !== undefined)\n this.workRefType = options.workRefType;\n if (options.workRefId !== undefined) this.workRefId = options.workRefId;\n if (options.provider !== undefined) this.provider = options.provider;\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 if (this.id) {\n const row = await this.db.get(this.tableName, { id: this.id });\n if (row) {\n const persisted = [\n row.tenant_id,\n row.subscriber_kind,\n row.subscriber_external_id,\n row.metric_key,\n row.quantity,\n row.window_start,\n row.window_end,\n row.source,\n row.source_id,\n row.project_id,\n row.work_ref_type,\n row.work_ref_id,\n row.provider,\n row.dimensions,\n ];\n const current = [\n this.tenantId,\n this.subscriberKind,\n this.subscriberExternalId,\n this.metricKey,\n this.quantity,\n this.windowStart,\n this.windowEnd,\n this.source,\n this.sourceId,\n this.projectId,\n this.workRefType,\n this.workRefId,\n this.provider,\n this.dimensions,\n ];\n if (\n persisted.some(\n (value, index) =>\n normalizeEvidence(value) !== normalizeEvidence(current[index]),\n )\n ) {\n throw new Error(\n 'Usage events are immutable; record a new event instead.',\n );\n }\n }\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\nfunction normalizeEvidence(value: unknown): string {\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'string') {\n const timestamp = Date.parse(value);\n if (/^\\d{4}-\\d{2}-\\d{2}[T ]/.test(value) && Number.isFinite(timestamp)) {\n return new Date(timestamp).toISOString();\n }\n }\n return String(value ?? '');\n}\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 deterministicUuid,\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 sourcedId =\n options.source && options.sourceId\n ? await deterministicUuid([\n 'tenant-usage',\n columns.tenantId,\n columns.subscriberKind,\n columns.subscriberExternalId,\n options.metricKey,\n options.source,\n options.sourceId,\n ])\n : null;\n if (options.source && options.sourceId) {\n const existing = await this.get(sourcedId as string);\n if (existing) return existing;\n }\n try {\n return await this.create({\n ...(sourcedId ? { id: sourcedId } : {}),\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 projectId: options.projectId ?? '',\n workRefType: options.workRefType ?? '',\n workRefId: options.workRefId ?? '',\n provider: options.provider ?? '',\n dimensions: stringifyJson(options.dimensions ?? {}),\n _insertOnly: Boolean(sourcedId),\n });\n } catch (error) {\n if (sourcedId) {\n const existing = await this.get(sourcedId);\n if (existing) return existing;\n }\n throw error;\n }\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","/**\n * Subscriptions money columns: floating-point major units → integer minor units.\n *\n * Ships as an explicit, opt-in migration rather than a schema-differ upgrade\n * because REAL→INTEGER is not information-preserving: an automatic\n * `ALTER … TYPE integer` would truncate `19.99` to `19`. The values have to be\n * rescaled by the currency's minor-unit factor during the type change (#2401).\n *\n * ```ts\n * console.log((await preflightSubscriptionsMoneyMinorUnits(db)).summary);\n * await migrateSubscriptionsMoneyToMinorUnits(db);\n * ```\n *\n * On SQLite the values are rescaled but the declared type cannot change in\n * place; those columns come back in `declaredTypeChangePending` and need the\n * table-rebuild path (#2370).\n *\n * **Pricing rules are not migrated.** `PricingRule.terms` is a JSON blob whose\n * `unitPrice` / `overageUnitPrice` / `amount` / tier prices are now read as\n * *minor units per unit of usage*. They are deliberately left fractional (a\n * per-token rate is routinely a fraction of a cent), so there is no type to\n * change — but every existing rule's terms are in major units and must be\n * rescaled by the operator, who alone knows which keys in a given rule are\n * prices and which are quantities or ratios.\n */\n\nimport {\n type DatabaseInterface,\n type MinorUnitsPreflightResult,\n type MinorUnitsRescaleResult,\n type MoneyColumnTarget,\n preflightMinorUnitsRescale,\n rescaleMoneyColumnsToMinorUnits,\n} from '@happyvertical/smrt-core/migrations';\n\n/** Marker recorded in `_smrt_backfills` once the rescale has run. */\nexport const SUBSCRIPTIONS_MONEY_MINOR_UNITS_BACKFILL =\n '@happyvertical/smrt-subscriptions:money-minor-units:v1';\n\n/**\n * Every subscriptions money column that held major units before #2401.\n *\n * `quantity` columns are absent on purpose: metered quantities are genuinely\n * fractional and stay DECIMAL.\n */\nexport const SUBSCRIPTIONS_MONEY_COLUMNS: MoneyColumnTarget[] = [\n { table: '_smrt_subscription_plans', columns: ['price_amount'] },\n { table: '_smrt_client_charges', columns: ['amount'] },\n { table: '_smrt_billing_adjustments', columns: ['amount'] },\n { table: '_smrt_spending_policies', columns: ['limit_amount'] },\n];\n\n/**\n * Report what {@link migrateSubscriptionsMoneyToMinorUnits} would do, without\n * writing anything.\n */\nexport function preflightSubscriptionsMoneyMinorUnits(\n db: DatabaseInterface,\n options: { scale?: number; engineHint?: string } = {},\n): Promise<MinorUnitsPreflightResult> {\n return preflightMinorUnitsRescale(db, SUBSCRIPTIONS_MONEY_COLUMNS, options);\n}\n\n/**\n * Convert every subscriptions money column from floating-point major units to\n * integer minor units. Idempotent via a `_smrt_backfills` marker.\n *\n * @throws `MinorUnitsPreflightError` when a row would be rounded or would\n * overflow `int4` and `force` was not set.\n */\nexport function migrateSubscriptionsMoneyToMinorUnits(\n db: DatabaseInterface,\n options: { scale?: number; engineHint?: string; force?: boolean } = {},\n): Promise<MinorUnitsRescaleResult> {\n return rescaleMoneyColumnsToMinorUnits(db, SUBSCRIPTIONS_MONEY_COLUMNS, {\n ...options,\n backfillName: SUBSCRIPTIONS_MONEY_MINOR_UNITS_BACKFILL,\n packageName: '@happyvertical/smrt-subscriptions',\n });\n}\n","import {\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\nexport type PricingStrategy =\n | 'fixed_unit'\n | 'cost_plus'\n | 'multiplier'\n | 'tiered'\n | 'included_overage'\n | 'flat'\n | 'custom';\nexport type SpendingPolicyBehavior =\n | 'observe'\n | 'warn'\n | 'block'\n | 'approval_required';\nexport type SpendingPeriod = 'day' | 'week' | 'month' | 'year' | 'rolling';\n\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: '_smrt_pricing_rules',\n api: true,\n cli: true,\n mcp: true,\n})\nexport class PricingRule extends SmrtObject {\n @tenantId() tenantId?: string;\n ruleKey: string = '';\n metricKey: string = '';\n serviceKey: string = '';\n strategy: PricingStrategy = 'fixed_unit';\n currency: string = 'USD';\n effectiveFrom: Date = new Date();\n effectiveTo: Date | null = null;\n priority: number = 0;\n terms: string = '{}';\n active: boolean = true;\n\n getTerms(): Record<string, unknown> {\n try {\n return JSON.parse(this.terms) as Record<string, unknown>;\n } catch {\n return {};\n }\n }\n setTerms(value: Record<string, unknown>): void {\n this.terms = JSON.stringify(value);\n }\n}\n\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: '_smrt_client_charges',\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n conflictColumns: ['usage_event_id'],\n})\nexport class ClientCharge extends SmrtObject {\n @tenantId() tenantId?: string;\n @foreignKey('TenantUsageMetric')\n usageEventId: string = '';\n subscriberKind: string = 'tenant';\n subscriberExternalId: string = '';\n projectId: string = '';\n workRefType: string = '';\n workRefId: string = '';\n provider: string = '';\n serviceKey: string = '';\n metricKey: string = '';\n /**\n * Metered quantity — genuinely fractional (`duration.seconds`, token counts\n * scaled by a rate), so it stays DECIMAL.\n */\n quantity: number = 0.0;\n /**\n * Charge in **integer minor units** of {@link currency} (#2401).\n *\n * Summed against {@link BillingAdjustment.amount} and compared against\n * {@link SpendingPolicy.limitAmount}, so all three carry the same unit —\n * `CommercialService.calculateAmount()` rounds to a whole minor unit at the\n * one boundary where the fractional pricing terms meet money.\n */\n amount: number = 0;\n currency: string = 'USD';\n @foreignKey('PricingRule')\n pricingRuleId: string = '';\n pricingSnapshot: string = '{}';\n status: 'draft' | 'approved' | 'adjusted' = 'draft';\n approvedAt: Date | null = null;\n\n getPricingSnapshot(): Record<string, unknown> {\n try {\n return JSON.parse(this.pricingSnapshot) as Record<string, unknown>;\n } catch {\n return {};\n }\n }\n\n protected async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n if (!this.id) return;\n const row = await this.db.get(this.tableName, { id: this.id });\n if (!row || (row.status !== 'approved' && row.status !== 'adjusted'))\n return;\n if (\n (row.status === 'approved' &&\n this.status !== 'approved' &&\n this.status !== 'adjusted') ||\n (row.status === 'adjusted' && this.status !== 'adjusted')\n ) {\n throw new Error(\n 'Approved client charges cannot be reverted to an editable status.',\n );\n }\n const persisted = [\n row.tenant_id,\n row.usage_event_id,\n row.subscriber_kind,\n row.subscriber_external_id,\n row.project_id,\n row.work_ref_type,\n row.work_ref_id,\n row.provider,\n row.service_key,\n row.metric_key,\n row.quantity,\n row.amount,\n row.currency,\n row.pricing_rule_id,\n row.pricing_snapshot,\n row.approved_at,\n ];\n const current = [\n this.tenantId,\n this.usageEventId,\n this.subscriberKind,\n this.subscriberExternalId,\n this.projectId,\n this.workRefType,\n this.workRefId,\n this.provider,\n this.serviceKey,\n this.metricKey,\n this.quantity,\n this.amount,\n this.currency,\n this.pricingRuleId,\n this.pricingSnapshot,\n this.approvedAt,\n ];\n if (\n persisted.some(\n (value, index) =>\n normalizeSnapshot(value) !== normalizeSnapshot(current[index]),\n )\n ) {\n throw new Error(\n 'Approved client charges are immutable; append a BillingAdjustment instead.',\n );\n }\n }\n}\n\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: '_smrt_billing_adjustments',\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n})\nexport class BillingAdjustment extends SmrtObject {\n @tenantId() tenantId?: string;\n @foreignKey('ClientCharge')\n clientChargeId: string = '';\n /**\n * Signed correction in **integer minor units**, same unit as\n * {@link ClientCharge.amount} it adjusts. Negative values are legitimate\n * (a credit) — that is why the non-negative guard in\n * `CommercialService.calculateAmount()` does not apply here (#2401).\n */\n amount: number = 0;\n currency: string = 'USD';\n reason: string = '';\n source: string = '';\n sourceId: string = '';\n createdBy: string = '';\n\n protected async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n if (!this.id) return;\n const row = await this.db.get(this.tableName, { id: this.id });\n if (row) {\n throw new Error(\n 'Billing adjustments are append-only; create a new adjustment instead.',\n );\n }\n }\n}\n\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: '_smrt_spending_policies',\n conflictColumns: [\n 'tenant_id',\n 'subscriber_kind',\n 'subscriber_external_id',\n 'project_id',\n 'service_key',\n 'metric_key',\n 'period',\n 'name',\n ],\n api: true,\n cli: true,\n mcp: true,\n})\nexport class SpendingPolicy extends SmrtObject {\n @tenantId() tenantId?: string;\n name: string = '';\n subscriberKind: string = '';\n subscriberExternalId: string = '';\n projectId: string = '';\n serviceKey: string = '';\n metricKey: string = '';\n period: SpendingPeriod = 'month';\n rollingSeconds: number = 0;\n /**\n * Spending cap in **integer minor units** of {@link currency}. Compared\n * directly against the sum of `ClientCharge.amount` + `BillingAdjustment`s\n * plus the caller's estimate, so it must carry the same unit (#2401).\n */\n limitAmount: number = 0;\n currency: string = 'USD';\n behavior: SpendingPolicyBehavior = 'observe';\n priority: number = 0;\n active: boolean = true;\n\n protected async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n if (this.subscriberExternalId && !this.subscriberKind) {\n throw new Error(\n 'Spending policies with subscriberExternalId require subscriberKind.',\n );\n }\n if (\n this.period === 'rolling' &&\n (!Number.isFinite(this.rollingSeconds) || this.rollingSeconds <= 0)\n ) {\n throw new Error(\n 'Rolling spending policies require rollingSeconds greater than zero.',\n );\n }\n }\n}\n\nexport class PricingRuleCollection extends SmrtCollection<PricingRule> {\n static readonly _itemClass = PricingRule;\n}\nexport class ClientChargeCollection extends SmrtCollection<ClientCharge> {\n static readonly _itemClass = ClientCharge;\n}\nexport class BillingAdjustmentCollection extends SmrtCollection<BillingAdjustment> {\n static readonly _itemClass = BillingAdjustment;\n}\nexport class SpendingPolicyCollection extends SmrtCollection<SpendingPolicy> {\n static readonly _itemClass = SpendingPolicy;\n}\n\nfunction normalizeSnapshot(value: unknown): string {\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'string' && /^\\d{4}-\\d{2}-\\d{2}[T ]/.test(value)) {\n const timestamp = Date.parse(value);\n if (Number.isFinite(timestamp)) return new Date(timestamp).toISOString();\n }\n return String(value ?? '');\n}\n","import type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { TenantUsageMetricCollection } from '../collections/TenantUsageMetricCollection.js';\nimport {\n BillingAdjustmentCollection,\n type ClientCharge,\n ClientChargeCollection,\n type PricingRule,\n PricingRuleCollection,\n type PricingStrategy,\n type SpendingPolicy,\n SpendingPolicyCollection,\n} from '../models/commercial.js';\nimport type { RecordUsageOptions, SubscriberKind } from '../types.js';\nimport {\n deterministicUuid,\n normalizeSubscriber,\n subscriberToColumns,\n} from '../utils.js';\n\nexport interface PriceUsageOptions {\n usageEventId: string;\n approved?: boolean;\n at?: Date;\n}\nexport interface CustomPricingContext {\n usage: { quantity: number; dimensions: Record<string, unknown> };\n rule: PricingRule;\n terms: Record<string, unknown>;\n}\nexport type CustomPricingStrategy = (\n context: CustomPricingContext,\n) => number | Promise<number>;\n\nexport class CommercialUsageService {\n private readonly customStrategies = new Map<string, CustomPricingStrategy>();\n constructor(\n private readonly usage: TenantUsageMetricCollection,\n private readonly rules: PricingRuleCollection,\n private readonly charges: ClientChargeCollection,\n private readonly adjustments: BillingAdjustmentCollection,\n ) {}\n\n static async create(\n options: SmrtClassOptions = {},\n ): Promise<CommercialUsageService> {\n const usage = await TenantUsageMetricCollection.create(options);\n const sharedOptions = { ...options, db: usage.db };\n return new CommercialUsageService(\n usage,\n await PricingRuleCollection.create(sharedOptions),\n await ClientChargeCollection.create(sharedOptions),\n await BillingAdjustmentCollection.create(sharedOptions),\n );\n }\n\n registerCustomStrategy(key: string, strategy: CustomPricingStrategy): void {\n this.customStrategies.set(key, strategy);\n }\n\n async record(options: RecordUsageOptions) {\n return this.usage.recordUsage(options);\n }\n\n async price(options: PriceUsageOptions): Promise<ClientCharge> {\n const existing = await this.charges.list({\n where: { usageEventId: options.usageEventId },\n limit: 1,\n });\n if (existing[0]) {\n return this.approveCharge(existing[0], options.approved);\n }\n const usage = await this.usage.get(options.usageEventId);\n if (!usage)\n throw new Error(`Usage event ${options.usageEventId} was not found.`);\n const at = options.at ?? usage.windowStart;\n const dimensions = usage.getDimensions();\n const rules = await this.rules.list({\n where: {\n tenantId: usage.tenantId,\n metricKey: usage.metricKey,\n active: true,\n },\n });\n const rule = selectRule(rules, at, String(dimensions.serviceKey ?? ''));\n if (!rule)\n throw new Error(\n `No effective pricing rule for metric '${usage.metricKey}'.`,\n );\n if (!usage.id)\n throw new Error(\n `Usage event ${options.usageEventId} has no persisted id.`,\n );\n if (!rule.id)\n throw new Error(`Pricing rule '${rule.ruleKey}' has no persisted id.`);\n const amount = await this.calculateAmount(rule, usage.quantity, dimensions);\n const chargeId = await deterministicUuid([\n 'client-charge',\n String(usage.tenantId),\n String(usage.id),\n ]);\n try {\n return await this.charges.create({\n id: chargeId,\n tenantId: usage.tenantId,\n usageEventId: String(usage.id),\n subscriberKind: usage.subscriberKind,\n subscriberExternalId: usage.subscriberExternalId,\n projectId: usage.projectId,\n workRefType: usage.workRefType,\n workRefId: usage.workRefId,\n provider: usage.provider,\n serviceKey: String(dimensions.serviceKey ?? ''),\n metricKey: usage.metricKey,\n quantity: usage.quantity,\n amount,\n currency: rule.currency,\n pricingRuleId: String(rule.id),\n pricingSnapshot: JSON.stringify({\n ruleKey: rule.ruleKey,\n strategy: rule.strategy,\n terms: rule.getTerms(),\n effectiveFrom: rule.effectiveFrom,\n }),\n status: options.approved ? 'approved' : 'draft',\n approvedAt: options.approved ? new Date() : null,\n _insertOnly: true,\n });\n } catch (error) {\n const concurrent = await this.charges.get(chargeId);\n if (!concurrent) throw error;\n return this.approveCharge(concurrent, options.approved);\n }\n }\n\n private async approveCharge(\n charge: ClientCharge,\n approved = false,\n ): Promise<ClientCharge> {\n if (approved && charge.status === 'draft') {\n charge.status = 'approved';\n charge.approvedAt = new Date();\n await charge.save();\n }\n return charge;\n }\n\n /**\n * Append a signed correction to an approved charge.\n *\n * @param amount - Signed correction in **integer minor units**, same unit as\n * the charge (#2401). Negative is legitimate — that is a credit. A\n * fractional value is rejected rather than silently rounded, because it\n * almost always means the caller passed major units.\n */\n async adjust(\n chargeId: string,\n amount: number,\n reason: string,\n source = '',\n sourceId = '',\n ) {\n if (!Number.isInteger(amount)) {\n throw new Error(\n `Billing adjustment amount must be an integer number of minor units ` +\n `(cents) — got ${amount}. Money is exact: -$0.25 is -25, not -0.25.`,\n );\n }\n const charge = await this.charges.get(chargeId);\n if (!charge) throw new Error(`Client charge ${chargeId} was not found.`);\n if (charge.status !== 'approved' && charge.status !== 'adjusted') {\n throw new Error(\n `Client charge ${chargeId} must be approved before it can be adjusted.`,\n );\n }\n const sourcedId =\n source && sourceId\n ? await deterministicUuid([\n 'billing-adjustment',\n String(charge.tenantId),\n chargeId,\n source,\n sourceId,\n ])\n : null;\n let adjustment = sourcedId\n ? await this.adjustments.get(sourcedId)\n : undefined;\n if (!adjustment) {\n try {\n adjustment = await this.adjustments.create({\n ...(sourcedId ? { id: sourcedId } : {}),\n tenantId: charge.tenantId,\n clientChargeId: chargeId,\n amount,\n currency: charge.currency,\n reason,\n source,\n sourceId,\n _insertOnly: Boolean(sourcedId),\n });\n } catch (error) {\n if (sourcedId) adjustment = await this.adjustments.get(sourcedId);\n if (!adjustment) throw error;\n }\n }\n if (charge.status === 'approved') {\n charge.status = 'adjusted';\n await charge.save();\n }\n return adjustment;\n }\n\n /**\n * Price one usage event, in **integer minor units** (#2401).\n *\n * The pricing terms stay fractional on purpose — a per-token rate is\n * routinely a fraction of a cent, and an integer `unitPrice` would truncate\n * it to zero — so terms are read as *minor units per unit of usage* and only\n * the result is rounded. This is the single boundary in the package where a\n * rate meets money, which is what lets `evaluateSpending()` compare sums\n * against `SpendingPolicy.limitAmount` exactly.\n *\n * Replaces the old micro-unit rounding (`Math.round(amount * 1e6) / 1e6`),\n * which kept six sub-cent digits in a column that is now integral.\n */\n async calculateAmount(\n rule: PricingRule,\n quantity: number,\n dimensions: Record<string, unknown> = {},\n ): Promise<number> {\n const terms = rule.getTerms();\n const number = (key: string, fallback = 0) =>\n Number(terms[key] ?? fallback);\n let amount: number;\n switch (rule.strategy as PricingStrategy) {\n case 'fixed_unit':\n amount = quantity * number('unitPrice');\n break;\n case 'cost_plus':\n amount =\n Number(dimensions.providerCost ?? 0) +\n number('fixedMarkup') +\n Number(dimensions.providerCost ?? 0) * number('markupRatio');\n break;\n case 'multiplier':\n amount =\n Number(dimensions.providerCost ?? quantity) * number('multiplier', 1);\n break;\n case 'flat':\n amount = number('amount');\n break;\n case 'included_overage':\n amount =\n Math.max(0, quantity - number('includedQuantity')) *\n number('overageUnitPrice');\n break;\n case 'tiered':\n amount = priceTiers(\n quantity,\n Array.isArray(terms.tiers) ? terms.tiers : [],\n );\n break;\n case 'custom': {\n const custom = this.customStrategies.get(\n String(terms.strategyKey ?? ''),\n );\n if (!custom)\n throw new Error(\n `Custom pricing strategy '${String(terms.strategyKey ?? '')}' is not registered.`,\n );\n amount = await custom({ usage: { quantity, dimensions }, rule, terms });\n break;\n }\n }\n if (!Number.isFinite(amount) || amount < 0)\n throw new Error('Pricing produced an invalid amount.');\n return Math.round(amount);\n }\n}\n\nexport interface SpendingDecision {\n allowed: boolean;\n approvalRequired: boolean;\n state: 'ok' | 'observed' | 'warned' | 'blocked' | 'approval_required';\n /**\n * Already-spent plus estimated, in **integer minor units** — the same unit as\n * `SpendingPolicy.limitAmount` it is compared against (#2401).\n */\n projectedAmount: number;\n matchedPolicyId: string | null;\n}\n\ninterface SpendingEvaluationInput {\n tenantId: string;\n subscriberKind?: SubscriberKind;\n subscriberExternalId?: string;\n projectId?: string;\n serviceKey?: string;\n metricKey: string;\n /** Estimated cost of the pending call, in integer minor units (#2401). */\n estimatedAmount: number;\n currency: string;\n at?: Date;\n}\n\nexport class SpendingPolicyEvaluator {\n constructor(\n private readonly policies: SpendingPolicyCollection,\n private readonly charges: ClientChargeCollection,\n private readonly adjustments: BillingAdjustmentCollection,\n ) {}\n static async create(options: SmrtClassOptions = {}) {\n const policies = await SpendingPolicyCollection.create(options);\n const sharedOptions = { ...options, db: policies.db };\n return new SpendingPolicyEvaluator(\n policies,\n await ClientChargeCollection.create(sharedOptions),\n await BillingAdjustmentCollection.create(sharedOptions),\n );\n }\n\n async evaluate(input: SpendingEvaluationInput): Promise<SpendingDecision> {\n const at = input.at ?? new Date();\n const normalizedInput = {\n ...input,\n ...subscriberToColumns(\n normalizeSubscriber({\n tenantId: input.tenantId,\n subscriberKind: input.subscriberKind,\n subscriberExternalId: input.subscriberExternalId,\n }),\n ),\n };\n const candidates = await this.policies.list({\n where: {\n tenantId: input.tenantId,\n currency: input.currency,\n active: true,\n },\n });\n const matchingPolicies = selectPolicies(candidates, normalizedInput);\n if (matchingPolicies.length === 0)\n return {\n allowed: true,\n approvalRequired: false,\n state: 'ok',\n projectedAmount: input.estimatedAmount,\n matchedPolicyId: null,\n };\n const decisions = await Promise.all(\n matchingPolicies.map((policy) =>\n this.evaluatePolicy(policy, normalizedInput, at),\n ),\n );\n return decisions.reduce((mostRestrictive, decision) =>\n decisionRank(decision) > decisionRank(mostRestrictive)\n ? decision\n : mostRestrictive,\n );\n }\n\n private async evaluatePolicy(\n policy: SpendingPolicy,\n input: SpendingEvaluationInput,\n at: Date,\n ): Promise<SpendingDecision> {\n const [start, end] = policyWindow(policy, at);\n const chargeWhere: Record<string, unknown> = {\n tenantId: input.tenantId,\n currency: policy.currency,\n status: ['approved', 'adjusted'],\n 'approvedAt >=': start.toISOString(),\n 'approvedAt <': end.toISOString(),\n };\n if (policy.metricKey) chargeWhere.metricKey = policy.metricKey;\n if (policy.projectId) chargeWhere.projectId = policy.projectId;\n if (policy.serviceKey) chargeWhere.serviceKey = policy.serviceKey;\n if (policy.subscriberKind)\n chargeWhere.subscriberKind = policy.subscriberKind;\n if (policy.subscriberExternalId)\n chargeWhere.subscriberExternalId = policy.subscriberExternalId;\n const rows = await this.charges.list({ where: chargeWhere });\n const scopedCharges = rows.filter(\n (charge) =>\n (charge.status === 'approved' || charge.status === 'adjusted') &&\n matchesChargeScope(policy, charge),\n );\n const chargeIds = scopedCharges\n .map((charge) => charge.id)\n .filter((id): id is string => Boolean(id));\n const chargeIdSet = new Set(chargeIds);\n const adjustmentRows =\n chargeIds.length > 0\n ? await this.adjustments.list({\n where: {\n tenantId: input.tenantId,\n currency: policy.currency,\n clientChargeId: chargeIds,\n },\n })\n : [];\n const spent = scopedCharges.reduce((sum, charge) => sum + charge.amount, 0);\n const correctedSpent =\n spent +\n adjustmentRows\n .filter((adjustment) => chargeIdSet.has(adjustment.clientChargeId))\n .reduce((sum, adjustment) => sum + adjustment.amount, 0);\n const projectedAmount = correctedSpent + input.estimatedAmount;\n const exceeded = projectedAmount > policy.limitAmount;\n if (!exceeded || policy.behavior === 'observe')\n return {\n allowed: true,\n approvalRequired: false,\n state: exceeded ? 'observed' : 'ok',\n projectedAmount,\n matchedPolicyId: policy.id ?? null,\n };\n if (policy.behavior === 'warn')\n return {\n allowed: true,\n approvalRequired: false,\n state: 'warned',\n projectedAmount,\n matchedPolicyId: policy.id ?? null,\n };\n if (policy.behavior === 'approval_required')\n return {\n allowed: false,\n approvalRequired: true,\n state: 'approval_required',\n projectedAmount,\n matchedPolicyId: policy.id ?? null,\n };\n return {\n allowed: false,\n approvalRequired: false,\n state: 'blocked',\n projectedAmount,\n matchedPolicyId: policy.id ?? null,\n };\n }\n}\n\nfunction selectRule(\n rules: PricingRule[],\n at: Date,\n serviceKey: string,\n): PricingRule | undefined {\n return rules\n .filter(\n (rule) =>\n rule.effectiveFrom <= at &&\n (!rule.effectiveTo || rule.effectiveTo > at) &&\n (!rule.serviceKey || rule.serviceKey === serviceKey),\n )\n .sort(\n (a, b) =>\n Number(Boolean(b.serviceKey)) - Number(Boolean(a.serviceKey)) ||\n b.priority - a.priority ||\n b.effectiveFrom.getTime() - a.effectiveFrom.getTime(),\n )[0];\n}\nfunction priceTiers(quantity: number, tiers: unknown[]): number {\n let remaining = quantity,\n prior = 0,\n amount = 0;\n for (const raw of tiers as Array<Record<string, unknown>>) {\n const upTo = raw.upTo == null ? Infinity : Number(raw.upTo);\n const units = Math.max(0, Math.min(remaining, upTo - prior));\n amount += units * Number(raw.unitPrice ?? 0);\n remaining -= units;\n prior = upTo;\n if (remaining <= 0) break;\n }\n return amount;\n}\nfunction selectPolicies(\n policies: SpendingPolicy[],\n input: Record<string, unknown>,\n): SpendingPolicy[] {\n return policies\n .filter(\n (p) =>\n hasValidSubscriberScope(p) &&\n (!p.metricKey || p.metricKey === input.metricKey) &&\n p.currency === input.currency &&\n (!p.projectId || p.projectId === input.projectId) &&\n (!p.serviceKey || p.serviceKey === input.serviceKey) &&\n (!p.subscriberKind ||\n (p.subscriberKind === (input.subscriberKind ?? 'tenant') &&\n (!p.subscriberExternalId ||\n p.subscriberExternalId === input.subscriberExternalId))),\n )\n .sort((a, b) => scopeScore(b) - scopeScore(a) || b.priority - a.priority);\n}\n\nfunction decisionRank(decision: SpendingDecision): number {\n return {\n ok: 0,\n observed: 1,\n warned: 2,\n approval_required: 3,\n blocked: 4,\n }[decision.state];\n}\nfunction scopeScore(p: SpendingPolicy): number {\n return (\n Number(Boolean(p.subscriberKind)) +\n Number(Boolean(p.subscriberExternalId)) +\n Number(Boolean(p.projectId)) +\n Number(Boolean(p.serviceKey)) +\n Number(Boolean(p.metricKey))\n );\n}\nfunction matchesChargeScope(p: SpendingPolicy, c: ClientCharge): boolean {\n return (\n hasValidSubscriberScope(p) &&\n (!p.projectId || p.projectId === c.projectId) &&\n (!p.serviceKey || p.serviceKey === c.serviceKey) &&\n (!p.metricKey || p.metricKey === c.metricKey) &&\n (!p.subscriberKind ||\n (p.subscriberKind === c.subscriberKind &&\n (!p.subscriberExternalId ||\n p.subscriberExternalId === c.subscriberExternalId)))\n );\n}\nfunction hasValidSubscriberScope(p: SpendingPolicy): boolean {\n return !p.subscriberExternalId || Boolean(p.subscriberKind);\n}\nfunction policyWindow(policy: SpendingPolicy, at: Date): [Date, Date] {\n const end = new Date(at);\n const start = new Date(at);\n if (policy.period === 'rolling')\n start.setTime(at.getTime() - policy.rollingSeconds * 1000);\n else if (policy.period === 'day') start.setUTCHours(0, 0, 0, 0);\n else if (policy.period === 'week') {\n start.setUTCHours(0, 0, 0, 0);\n start.setUTCDate(start.getUTCDate() - ((start.getUTCDay() + 6) % 7));\n } else if (policy.period === 'month') {\n start.setUTCDate(1);\n start.setUTCHours(0, 0, 0, 0);\n } else {\n start.setUTCMonth(0, 1);\n start.setUTCHours(0, 0, 0, 0);\n }\n return [start, end];\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 let fallbackAiMetricKeys: string[] = [];\n if (aiMetricKeys.length > 0) {\n if (!(await this.hasAiUsageTable())) {\n fallbackAiMetricKeys = aiMetricKeys;\n } else {\n try {\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 } catch (error) {\n if (!isMissingAiUsageTableError(error)) {\n throw error;\n }\n fallbackAiMetricKeys = aiMetricKeys;\n }\n }\n }\n\n const persistedSummaries = await this.metrics.summarizeUsageBatch({\n ...options,\n metricKeys: [...persistedMetricKeys, ...fallbackAiMetricKeys],\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 if (!(await this.hasAiUsageTable())) {\n return null;\n }\n\n let summary: AiUsageSummary;\n try {\n summary = await this.summarizeAiUsage({\n tenantId: options.tenantId,\n window: options.window,\n });\n } catch (error) {\n if (isMissingAiUsageTableError(error)) {\n return null;\n }\n throw error;\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 private async hasAiUsageTable(): Promise<boolean> {\n return this.metrics.db.tableExists('_smrt_ai_usage');\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 isMissingAiUsageTableError(\n error: unknown,\n seen = new Set<unknown>(),\n): boolean {\n if (!error) {\n return false;\n }\n if (typeof error !== 'object') {\n return isMissingAiUsageTableMessage(String(error));\n }\n if (seen.has(error)) {\n return false;\n }\n seen.add(error);\n\n const err = error as {\n code?: unknown;\n message?: unknown;\n cause?: unknown;\n context?: Record<string, unknown>;\n };\n const codes = [err?.code, err?.context?.code, err?.context?.errorCode];\n if (codes.some((code) => code === '42P01')) {\n return true;\n }\n\n const messages = [\n err?.message,\n err?.context?.originalError,\n err?.context?.error,\n err?.context?.details,\n ];\n for (const candidate of messages) {\n if (\n candidate &&\n typeof candidate === 'object' &&\n isMissingAiUsageTableError(candidate, seen)\n ) {\n return true;\n }\n if (isMissingAiUsageTableMessage(String(candidate ?? ''))) {\n return true;\n }\n }\n\n return err?.cause ? isMissingAiUsageTableError(err.cause, seen) : false;\n}\n\nfunction isMissingAiUsageTableMessage(message: string): boolean {\n const normalized = message.toLowerCase();\n return (\n normalized.includes('_smrt_ai_usage') &&\n (normalized.includes('no such table') ||\n normalized.includes('does not exist') ||\n normalized.includes('undefined table'))\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?.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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["adapterType","evaluations"],"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/migrations/moneyMinorUnits.ts","../src/models/commercial.ts","../src/services/commercial.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 async function deterministicUuid(parts: string[]): Promise<string> {\n const bytes = new TextEncoder().encode(JSON.stringify(parts));\n const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));\n const uuid = digest.slice(0, 16);\n uuid[6] = (uuid[6] & 0x0f) | 0x50;\n uuid[8] = (uuid[8] & 0x3f) | 0x80;\n const hex = Array.from(uuid, (byte) =>\n byte.toString(16).padStart(2, '0'),\n ).join('');\n return [\n hex.slice(0, 8),\n hex.slice(8, 12),\n hex.slice(12, 16),\n hex.slice(16, 20),\n hex.slice(20),\n ].join('-');\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 /**\n * Plan price in **integer minor units** of {@link currency} — `$19.99` is\n * `1999`. The `= 0` initializer is load-bearing: SMRT maps an integer literal\n * to INTEGER and a decimal literal to DECIMAL, and money is exact (#2401).\n */\n priceAmount: number = 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: { include: ['list', 'get', 'create'] },\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 projectId: string = '';\n workRefType: string = '';\n workRefId: string = '';\n provider: 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.projectId !== undefined) this.projectId = options.projectId;\n if (options.workRefType !== undefined)\n this.workRefType = options.workRefType;\n if (options.workRefId !== undefined) this.workRefId = options.workRefId;\n if (options.provider !== undefined) this.provider = options.provider;\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 if (this.id) {\n const row = await this.db.get(this.tableName, { id: this.id });\n if (row) {\n const persisted = [\n row.tenant_id,\n row.subscriber_kind,\n row.subscriber_external_id,\n row.metric_key,\n row.quantity,\n row.window_start,\n row.window_end,\n row.source,\n row.source_id,\n row.project_id,\n row.work_ref_type,\n row.work_ref_id,\n row.provider,\n row.dimensions,\n ];\n const current = [\n this.tenantId,\n this.subscriberKind,\n this.subscriberExternalId,\n this.metricKey,\n this.quantity,\n this.windowStart,\n this.windowEnd,\n this.source,\n this.sourceId,\n this.projectId,\n this.workRefType,\n this.workRefId,\n this.provider,\n this.dimensions,\n ];\n if (\n persisted.some(\n (value, index) =>\n normalizeEvidence(value) !== normalizeEvidence(current[index]),\n )\n ) {\n throw new Error(\n 'Usage events are immutable; record a new event instead.',\n );\n }\n }\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\nfunction normalizeEvidence(value: unknown): string {\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'string') {\n const timestamp = Date.parse(value);\n if (/^\\d{4}-\\d{2}-\\d{2}[T ]/.test(value) && Number.isFinite(timestamp)) {\n return new Date(timestamp).toISOString();\n }\n }\n return String(value ?? '');\n}\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 deterministicUuid,\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 sourcedId =\n options.source && options.sourceId\n ? await deterministicUuid([\n 'tenant-usage',\n columns.tenantId,\n columns.subscriberKind,\n columns.subscriberExternalId,\n options.metricKey,\n options.source,\n options.sourceId,\n ])\n : null;\n if (options.source && options.sourceId) {\n const existing = await this.get(sourcedId as string);\n if (existing) return existing;\n }\n try {\n return await this.create({\n ...(sourcedId ? { id: sourcedId } : {}),\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 projectId: options.projectId ?? '',\n workRefType: options.workRefType ?? '',\n workRefId: options.workRefId ?? '',\n provider: options.provider ?? '',\n dimensions: stringifyJson(options.dimensions ?? {}),\n _insertOnly: Boolean(sourcedId),\n });\n } catch (error) {\n if (sourcedId) {\n const existing = await this.get(sourcedId);\n if (existing) return existing;\n }\n throw error;\n }\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","/**\n * Subscriptions money columns: floating-point major units → integer minor units.\n *\n * Ships as an explicit, opt-in migration rather than a schema-differ upgrade\n * because REAL→INTEGER is not information-preserving: an automatic\n * `ALTER … TYPE integer` would truncate `19.99` to `19`. The values have to be\n * rescaled by the currency's minor-unit factor during the type change (#2401).\n *\n * ```ts\n * console.log((await preflightSubscriptionsMoneyMinorUnits(db)).summary);\n * await migrateSubscriptionsMoneyToMinorUnits(db);\n * ```\n *\n * On SQLite the values are rescaled but the declared type cannot change in\n * place; those columns come back in `declaredTypeChangePending` and need the\n * table-rebuild path (#2370).\n *\n * **Pricing rules are not migrated.** `PricingRule.terms` is a JSON blob whose\n * `unitPrice` / `overageUnitPrice` / `amount` / tier prices are now read as\n * *minor units per unit of usage*. They are deliberately left fractional (a\n * per-token rate is routinely a fraction of a cent), so there is no type to\n * change — but every existing rule's terms are in major units and must be\n * rescaled by the operator, who alone knows which keys in a given rule are\n * prices and which are quantities or ratios.\n */\n\nimport {\n type DatabaseInterface,\n type MinorUnitsPreflightResult,\n type MinorUnitsRescaleResult,\n type MoneyColumnTarget,\n preflightMinorUnitsRescale,\n rescaleMoneyColumnsToMinorUnits,\n} from '@happyvertical/smrt-core/migrations';\n\n/** Marker recorded in `_smrt_backfills` once the rescale has run. */\nexport const SUBSCRIPTIONS_MONEY_MINOR_UNITS_BACKFILL =\n '@happyvertical/smrt-subscriptions:money-minor-units:v1';\n\n/**\n * Every subscriptions money column that held major units before #2401.\n *\n * `quantity` columns are absent on purpose: metered quantities are genuinely\n * fractional and stay DECIMAL.\n */\nexport const SUBSCRIPTIONS_MONEY_COLUMNS: MoneyColumnTarget[] = [\n { table: '_smrt_subscription_plans', columns: ['price_amount'] },\n { table: '_smrt_client_charges', columns: ['amount'] },\n { table: '_smrt_billing_adjustments', columns: ['amount'] },\n { table: '_smrt_spending_policies', columns: ['limit_amount'] },\n];\n\n/**\n * Report what {@link migrateSubscriptionsMoneyToMinorUnits} would do, without\n * writing anything.\n */\nexport function preflightSubscriptionsMoneyMinorUnits(\n db: DatabaseInterface,\n options: { scale?: number; engineHint?: string } = {},\n): Promise<MinorUnitsPreflightResult> {\n return preflightMinorUnitsRescale(db, SUBSCRIPTIONS_MONEY_COLUMNS, options);\n}\n\n/**\n * Convert every subscriptions money column from floating-point major units to\n * integer minor units. Idempotent via a `_smrt_backfills` marker.\n *\n * @throws `MinorUnitsPreflightError` when a row would be rounded or would\n * overflow `int4` and `force` was not set.\n */\nexport function migrateSubscriptionsMoneyToMinorUnits(\n db: DatabaseInterface,\n options: { scale?: number; engineHint?: string; force?: boolean } = {},\n): Promise<MinorUnitsRescaleResult> {\n return rescaleMoneyColumnsToMinorUnits(db, SUBSCRIPTIONS_MONEY_COLUMNS, {\n ...options,\n backfillName: SUBSCRIPTIONS_MONEY_MINOR_UNITS_BACKFILL,\n packageName: '@happyvertical/smrt-subscriptions',\n });\n}\n","import {\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\nexport type PricingStrategy =\n | 'fixed_unit'\n | 'cost_plus'\n | 'multiplier'\n | 'tiered'\n | 'included_overage'\n | 'flat'\n | 'custom';\nexport type SpendingPolicyBehavior =\n | 'observe'\n | 'warn'\n | 'block'\n | 'approval_required';\nexport type SpendingPeriod = 'day' | 'week' | 'month' | 'year' | 'rolling';\n\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: '_smrt_pricing_rules',\n api: true,\n cli: true,\n mcp: true,\n})\nexport class PricingRule extends SmrtObject {\n @tenantId() tenantId?: string;\n ruleKey: string = '';\n metricKey: string = '';\n serviceKey: string = '';\n strategy: PricingStrategy = 'fixed_unit';\n currency: string = 'USD';\n effectiveFrom: Date = new Date();\n effectiveTo: Date | null = null;\n priority: number = 0;\n terms: string = '{}';\n active: boolean = true;\n\n getTerms(): Record<string, unknown> {\n try {\n return JSON.parse(this.terms) as Record<string, unknown>;\n } catch {\n return {};\n }\n }\n setTerms(value: Record<string, unknown>): void {\n this.terms = JSON.stringify(value);\n }\n}\n\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: '_smrt_client_charges',\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n conflictColumns: ['usage_event_id'],\n})\nexport class ClientCharge extends SmrtObject {\n @tenantId() tenantId?: string;\n @foreignKey('TenantUsageMetric')\n usageEventId: string = '';\n subscriberKind: string = 'tenant';\n subscriberExternalId: string = '';\n projectId: string = '';\n workRefType: string = '';\n workRefId: string = '';\n provider: string = '';\n serviceKey: string = '';\n metricKey: string = '';\n /**\n * Metered quantity — genuinely fractional (`duration.seconds`, token counts\n * scaled by a rate), so it stays DECIMAL.\n */\n quantity: number = 0.0;\n /**\n * Charge in **integer minor units** of {@link currency} (#2401).\n *\n * Summed against {@link BillingAdjustment.amount} and compared against\n * {@link SpendingPolicy.limitAmount}, so all three carry the same unit —\n * `CommercialService.calculateAmount()` rounds to a whole minor unit at the\n * one boundary where the fractional pricing terms meet money.\n */\n amount: number = 0;\n currency: string = 'USD';\n @foreignKey('PricingRule')\n pricingRuleId: string = '';\n pricingSnapshot: string = '{}';\n status: 'draft' | 'approved' | 'adjusted' = 'draft';\n approvedAt: Date | null = null;\n\n getPricingSnapshot(): Record<string, unknown> {\n try {\n return JSON.parse(this.pricingSnapshot) as Record<string, unknown>;\n } catch {\n return {};\n }\n }\n\n protected async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n if (!this.id) return;\n const row = await this.getCanonicalPersistedRow({ id: this.id });\n if (!row || (row.status !== 'approved' && row.status !== 'adjusted'))\n return;\n if (\n (row.status === 'approved' &&\n this.status !== 'approved' &&\n this.status !== 'adjusted') ||\n (row.status === 'adjusted' && this.status !== 'adjusted')\n ) {\n throw new Error(\n 'Approved client charges cannot be reverted to an editable status.',\n );\n }\n const persisted = [\n row.tenant_id,\n row.usage_event_id,\n row.subscriber_kind,\n row.subscriber_external_id,\n row.project_id,\n row.work_ref_type,\n row.work_ref_id,\n row.provider,\n row.service_key,\n row.metric_key,\n row.quantity,\n row.amount,\n row.currency,\n row.pricing_rule_id,\n row.pricing_snapshot,\n row.approved_at,\n ];\n const current = [\n this.tenantId,\n this.usageEventId,\n this.subscriberKind,\n this.subscriberExternalId,\n this.projectId,\n this.workRefType,\n this.workRefId,\n this.provider,\n this.serviceKey,\n this.metricKey,\n this.quantity,\n this.amount,\n this.currency,\n this.pricingRuleId,\n this.pricingSnapshot,\n this.approvedAt,\n ];\n if (\n persisted.some(\n (value, index) =>\n normalizeSnapshot(value) !== normalizeSnapshot(current[index]),\n )\n ) {\n throw new Error(\n 'Approved client charges are immutable; append a BillingAdjustment instead.',\n );\n }\n }\n}\n\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: '_smrt_billing_adjustments',\n api: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n})\nexport class BillingAdjustment extends SmrtObject {\n @tenantId() tenantId?: string;\n @foreignKey('ClientCharge')\n clientChargeId: string = '';\n /**\n * Signed correction in **integer minor units**, same unit as\n * {@link ClientCharge.amount} it adjusts. Negative values are legitimate\n * (a credit) — that is why the non-negative guard in\n * `CommercialService.calculateAmount()` does not apply here (#2401).\n */\n amount: number = 0;\n currency: string = 'USD';\n reason: string = '';\n source: string = '';\n sourceId: string = '';\n createdBy: string = '';\n\n protected async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n if (!this.id) return;\n const row = await this.db.get(this.tableName, { id: this.id });\n if (row) {\n throw new Error(\n 'Billing adjustments are append-only; create a new adjustment instead.',\n );\n }\n }\n}\n\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: '_smrt_spending_policies',\n conflictColumns: [\n 'tenant_id',\n 'subscriber_kind',\n 'subscriber_external_id',\n 'project_id',\n 'service_key',\n 'metric_key',\n 'period',\n 'name',\n ],\n api: true,\n cli: true,\n mcp: true,\n})\nexport class SpendingPolicy extends SmrtObject {\n @tenantId() tenantId?: string;\n name: string = '';\n subscriberKind: string = '';\n subscriberExternalId: string = '';\n projectId: string = '';\n serviceKey: string = '';\n metricKey: string = '';\n period: SpendingPeriod = 'month';\n rollingSeconds: number = 0;\n /**\n * Spending cap in **integer minor units** of {@link currency}. Compared\n * directly against the sum of `ClientCharge.amount` + `BillingAdjustment`s\n * plus the caller's estimate, so it must carry the same unit (#2401).\n */\n limitAmount: number = 0;\n currency: string = 'USD';\n behavior: SpendingPolicyBehavior = 'observe';\n priority: number = 0;\n active: boolean = true;\n\n protected async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n if (this.subscriberExternalId && !this.subscriberKind) {\n throw new Error(\n 'Spending policies with subscriberExternalId require subscriberKind.',\n );\n }\n if (\n this.period === 'rolling' &&\n (!Number.isFinite(this.rollingSeconds) || this.rollingSeconds <= 0)\n ) {\n throw new Error(\n 'Rolling spending policies require rollingSeconds greater than zero.',\n );\n }\n }\n}\n\nexport class PricingRuleCollection extends SmrtCollection<PricingRule> {\n static readonly _itemClass = PricingRule;\n}\nexport class ClientChargeCollection extends SmrtCollection<ClientCharge> {\n static readonly _itemClass = ClientCharge;\n}\nexport class BillingAdjustmentCollection extends SmrtCollection<BillingAdjustment> {\n static readonly _itemClass = BillingAdjustment;\n}\nexport class SpendingPolicyCollection extends SmrtCollection<SpendingPolicy> {\n static readonly _itemClass = SpendingPolicy;\n}\n\nfunction normalizeSnapshot(value: unknown): string {\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'string' && /^\\d{4}-\\d{2}-\\d{2}[T ]/.test(value)) {\n const timestamp = Date.parse(value);\n if (Number.isFinite(timestamp)) return new Date(timestamp).toISOString();\n }\n return String(value ?? '');\n}\n","import {\n type DatabaseConfig,\n isDatabaseInterface,\n isEmbeddedDatabase,\n type SmrtClassOptions,\n withEmbeddedWriteTransaction,\n} from '@happyvertical/smrt-core';\nimport type { SqlAdapterType } from '@happyvertical/sql';\nimport { TenantUsageMetricCollection } from '../collections/TenantUsageMetricCollection.js';\nimport {\n BillingAdjustmentCollection,\n type ClientCharge,\n ClientChargeCollection,\n type PricingRule,\n PricingRuleCollection,\n type PricingStrategy,\n type SpendingPolicy,\n SpendingPolicyCollection,\n} from '../models/commercial.js';\nimport type { RecordUsageOptions, SubscriberKind } from '../types.js';\nimport {\n deterministicUuid,\n normalizeSubscriber,\n subscriberToColumns,\n} from '../utils.js';\n\nexport interface PriceUsageOptions {\n usageEventId: string;\n approved?: boolean;\n at?: Date;\n}\nexport interface CustomPricingContext {\n usage: { quantity: number; dimensions: Record<string, unknown> };\n rule: PricingRule;\n terms: Record<string, unknown>;\n}\nexport type CustomPricingStrategy = (\n context: CustomPricingContext,\n) => number | Promise<number>;\n\nexport interface CommercialBillingStorage {\n adapterType: SqlAdapterType;\n writeStrategy?: 'immediate' | 'manual' | 'none';\n}\n\nexport interface CommercialUsageServiceOptions extends SmrtClassOptions {\n /**\n * Required when `db` is an already-resolved database handle, whose public\n * interface does not expose its adapter/write-back configuration.\n */\n billingStorage?: CommercialBillingStorage;\n}\n\nconst embeddedAdjustmentLocks = new Map<string | object, Promise<void>>();\n\nasync function withEmbeddedSourcedAdjustmentLock<T>(\n db: { url?: string },\n storage: CommercialBillingStorage,\n sourced: boolean,\n operation: () => Promise<T>,\n): Promise<T> {\n if (!sourced || storage.adapterType === 'postgres') return operation();\n\n const key = db.url ? `${storage.adapterType}:${db.url}` : (db as object);\n const previous = embeddedAdjustmentLocks.get(key) ?? Promise.resolve();\n let release!: () => void;\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const queued = previous.then(() => current);\n embeddedAdjustmentLocks.set(key, queued);\n await previous;\n try {\n return await operation();\n } finally {\n release();\n if (embeddedAdjustmentLocks.get(key) === queued) {\n embeddedAdjustmentLocks.delete(key);\n }\n }\n}\n\nexport class UnsupportedCommercialBillingStorageError extends Error {\n readonly code = 'UNSUPPORTED_COMMERCIAL_BILLING_STORAGE';\n\n constructor(storage: CommercialBillingStorage) {\n super(\n `Commercial billing does not support ${storage.adapterType} ` +\n `with immediate JSON write-back because exported files are not ` +\n `transactionally rolled back. Use PostgreSQL, SQLite, ordinary ` +\n `DuckDB, or a non-immediate write strategy.`,\n );\n this.name = 'UnsupportedCommercialBillingStorageError';\n }\n}\n\nexport class CommercialBillingStorageConfigurationError extends Error {\n readonly code = 'COMMERCIAL_BILLING_STORAGE_CONFIGURATION_REQUIRED';\n\n constructor(message?: string) {\n super(\n message ??\n 'Commercial billing requires billingStorage when db is an already-resolved handle because adapter write-back capabilities are not public on that interface.',\n );\n this.name = 'CommercialBillingStorageConfigurationError';\n }\n}\n\nfunction assertValidCommercialBillingWriteStrategy(\n writeStrategy: unknown,\n): asserts writeStrategy is CommercialBillingStorage['writeStrategy'] {\n if (\n writeStrategy !== undefined &&\n writeStrategy !== 'immediate' &&\n writeStrategy !== 'manual' &&\n writeStrategy !== 'none'\n ) {\n throw new CommercialBillingStorageConfigurationError(\n 'Commercial billing database writeStrategy must be immediate, manual, or none.',\n );\n }\n}\n\nfunction snapshotCommercialBillingStorage(\n storage: CommercialBillingStorage,\n): CommercialBillingStorage {\n const adapterType: unknown = storage.adapterType;\n if (\n adapterType !== 'sqlite' &&\n adapterType !== 'postgres' &&\n adapterType !== 'duckdb' &&\n adapterType !== 'json'\n ) {\n throw new CommercialBillingStorageConfigurationError(\n 'Commercial billing storage adapterType must be sqlite, postgres, duckdb, or json.',\n );\n }\n const writeStrategy: unknown = storage.writeStrategy;\n assertValidCommercialBillingWriteStrategy(writeStrategy);\n return { adapterType, writeStrategy };\n}\n\nfunction snapshotCommercialUsageServiceOptions(\n options: CommercialUsageServiceOptions,\n): CommercialUsageServiceOptions {\n const snapshot = { ...options };\n if (snapshot.billingStorage) {\n snapshot.billingStorage = snapshotCommercialBillingStorage(\n snapshot.billingStorage,\n );\n }\n for (const key of ['db', 'persistence'] as const) {\n const database = snapshot[key];\n if (\n database &&\n typeof database !== 'string' &&\n !isDatabaseInterface(database)\n ) {\n snapshot[key] = { ...database };\n }\n }\n return snapshot;\n}\n\nexport function assertCommercialBillingStorageSupported(\n storage: CommercialBillingStorage,\n): void {\n const snapshot = snapshotCommercialBillingStorage(storage);\n const writeStrategy =\n snapshot.adapterType === 'json'\n ? (snapshot.writeStrategy ?? 'immediate')\n : snapshot.writeStrategy;\n if (\n (snapshot.adapterType === 'json' || snapshot.adapterType === 'duckdb') &&\n writeStrategy === 'immediate'\n ) {\n throw new UnsupportedCommercialBillingStorageError({\n ...snapshot,\n writeStrategy,\n });\n }\n}\n\nfunction inferAdapterType(url: string): SqlAdapterType | undefined {\n if (/^postgres(?:ql)?:/iu.test(url)) return 'postgres';\n if (/^(?:https?|libsql):\\/\\//iu.test(url)) return 'sqlite';\n if (/\\.duckdb(?:$|\\?)/iu.test(url)) return 'duckdb';\n if (url === ':memory:' || url.startsWith('file:')) return 'sqlite';\n return undefined;\n}\n\nfunction environmentAdapterType(): SqlAdapterType | undefined {\n const type = process.env.HAVE_SQL_TYPE;\n if (type === undefined || type === '') return undefined;\n if (\n type === 'sqlite' ||\n type === 'postgres' ||\n type === 'duckdb' ||\n type === 'json'\n ) {\n return type;\n }\n throw new CommercialBillingStorageConfigurationError(\n 'Commercial billing HAVE_SQL_TYPE must be sqlite, postgres, duckdb, or json.',\n );\n}\n\nfunction environmentWriteStrategy(): CommercialBillingStorage['writeStrategy'] {\n const strategy = process.env.HAVE_SQL_WRITE_STRATEGY;\n if (strategy === undefined || strategy === '') return undefined;\n if (\n strategy === 'immediate' ||\n strategy === 'manual' ||\n strategy === 'none'\n ) {\n return strategy;\n }\n throw new CommercialBillingStorageConfigurationError(\n 'Commercial billing HAVE_SQL_WRITE_STRATEGY must be immediate, manual, or none.',\n );\n}\n\nfunction billingStorageFromConfig(\n config: DatabaseConfig | undefined,\n declared?: CommercialBillingStorage,\n): CommercialBillingStorage {\n // Validate a configured environment value even when an explicit config type\n // wins adapter precedence. Otherwise normalization below could hide an SDK\n // configuration error by replacing the invalid environment value.\n const environmentType = environmentAdapterType();\n if (config === undefined) {\n if (environmentType)\n return {\n adapterType: environmentType,\n writeStrategy: environmentWriteStrategy(),\n };\n return { adapterType: 'sqlite' };\n }\n if (isDatabaseInterface(config)) {\n throw new CommercialBillingStorageConfigurationError();\n }\n if (typeof config === 'string') {\n const adapterType =\n environmentType ?? inferAdapterType(config) ?? declared?.adapterType;\n if (adapterType)\n return {\n adapterType,\n writeStrategy: environmentWriteStrategy() ?? declared?.writeStrategy,\n };\n throw new Error(\n 'Commercial billing requires an explicit billingStorage adapter contract for ambiguous database URLs.',\n );\n }\n const adapterType =\n config.type ??\n environmentType ??\n inferAdapterType(String(config.url ?? '')) ??\n declared?.adapterType;\n if (!adapterType) {\n throw new Error(\n 'Commercial billing requires an explicit database type or billingStorage adapter contract.',\n );\n }\n const configuredWriteStrategy = config.writeStrategy;\n assertValidCommercialBillingWriteStrategy(configuredWriteStrategy);\n const writeStrategy =\n configuredWriteStrategy === 'immediate' ||\n configuredWriteStrategy === 'manual' ||\n configuredWriteStrategy === 'none'\n ? configuredWriteStrategy\n : (environmentWriteStrategy() ?? declared?.writeStrategy);\n return { adapterType, writeStrategy };\n}\n\nfunction effectiveWriteStrategy(\n storage: CommercialBillingStorage,\n): CommercialBillingStorage['writeStrategy'] {\n if (storage.writeStrategy) return storage.writeStrategy;\n if (storage.adapterType === 'json') return 'immediate';\n if (storage.adapterType === 'duckdb') return 'none';\n return undefined;\n}\n\nfunction resolveCommercialBillingStorage(\n options: CommercialUsageServiceOptions,\n): CommercialBillingStorage {\n // Environment declarations are configuration contracts, even when explicit\n // options win precedence. Reject invalid nonempty values before setup.\n environmentAdapterType();\n environmentWriteStrategy();\n const configuredDatabase = options.db ?? options.persistence;\n if (isDatabaseInterface(configuredDatabase)) {\n if (!options.billingStorage) {\n throw new CommercialBillingStorageConfigurationError();\n }\n return options.billingStorage;\n }\n const configured = billingStorageFromConfig(\n configuredDatabase,\n options.billingStorage,\n );\n if (\n options.billingStorage &&\n (options.billingStorage.adapterType !== configured.adapterType ||\n effectiveWriteStrategy(options.billingStorage) !==\n effectiveWriteStrategy(configured))\n ) {\n throw new CommercialBillingStorageConfigurationError(\n 'Commercial billingStorage must match the configured database adapter and write strategy.',\n );\n }\n return configured;\n}\n\nfunction normalizedCommercialClassOptions(\n options: CommercialUsageServiceOptions,\n billingStorage: CommercialBillingStorage,\n): SmrtClassOptions {\n const { billingStorage: _billingStorage, ...classOptions } = options;\n const configuredDatabase = options.db ?? options.persistence;\n if (configuredDatabase && !isDatabaseInterface(configuredDatabase)) {\n const normalizedDatabase =\n typeof configuredDatabase === 'string'\n ? {\n type: billingStorage.adapterType,\n url: configuredDatabase,\n writeStrategy: billingStorage.writeStrategy,\n }\n : {\n ...configuredDatabase,\n type: billingStorage.adapterType,\n writeStrategy: billingStorage.writeStrategy,\n };\n if (options.db !== undefined) classOptions.db = normalizedDatabase;\n else classOptions.persistence = normalizedDatabase;\n }\n return classOptions;\n}\n\nexport class CommercialUsageService {\n private readonly customStrategies = new Map<string, CustomPricingStrategy>();\n constructor(\n private readonly usage: TenantUsageMetricCollection,\n private readonly rules: PricingRuleCollection,\n private readonly charges: ClientChargeCollection,\n private readonly adjustments: BillingAdjustmentCollection,\n private readonly billingStorage: CommercialBillingStorage,\n ) {\n if (!billingStorage) {\n throw new CommercialBillingStorageConfigurationError();\n }\n assertCommercialBillingStorageSupported(billingStorage);\n }\n\n static async create(\n options: CommercialUsageServiceOptions = {},\n ): Promise<CommercialUsageService> {\n const optionSnapshot = snapshotCommercialUsageServiceOptions(options);\n const billingStorage = resolveCommercialBillingStorage(optionSnapshot);\n assertCommercialBillingStorageSupported(billingStorage);\n const classOptions = normalizedCommercialClassOptions(\n optionSnapshot,\n billingStorage,\n );\n const usage = await TenantUsageMetricCollection.create(classOptions);\n const sharedOptions = { ...classOptions, db: usage.db };\n return new CommercialUsageService(\n usage,\n await PricingRuleCollection.create(sharedOptions),\n await ClientChargeCollection.create(sharedOptions),\n await BillingAdjustmentCollection.create(sharedOptions),\n billingStorage,\n );\n }\n\n registerCustomStrategy(key: string, strategy: CustomPricingStrategy): void {\n this.customStrategies.set(key, strategy);\n }\n\n async record(options: RecordUsageOptions) {\n return this.usage.recordUsage(options);\n }\n\n async price(options: PriceUsageOptions): Promise<ClientCharge> {\n const existing = await this.charges.list({\n where: { usageEventId: options.usageEventId },\n limit: 1,\n });\n if (existing[0]) {\n return this.approveCharge(existing[0], options.approved);\n }\n const usage = await this.usage.get(options.usageEventId);\n if (!usage)\n throw new Error(`Usage event ${options.usageEventId} was not found.`);\n const at = options.at ?? usage.windowStart;\n const dimensions = usage.getDimensions();\n const rules = await this.rules.list({\n where: {\n tenantId: usage.tenantId,\n metricKey: usage.metricKey,\n active: true,\n },\n });\n const rule = selectRule(rules, at, String(dimensions.serviceKey ?? ''));\n if (!rule)\n throw new Error(\n `No effective pricing rule for metric '${usage.metricKey}'.`,\n );\n if (!usage.id)\n throw new Error(\n `Usage event ${options.usageEventId} has no persisted id.`,\n );\n if (!rule.id)\n throw new Error(`Pricing rule '${rule.ruleKey}' has no persisted id.`);\n const amount = await this.calculateAmount(rule, usage.quantity, dimensions);\n const chargeId = await deterministicUuid([\n 'client-charge',\n String(usage.tenantId),\n String(usage.id),\n ]);\n try {\n return await this.charges.create({\n id: chargeId,\n tenantId: usage.tenantId,\n usageEventId: String(usage.id),\n subscriberKind: usage.subscriberKind,\n subscriberExternalId: usage.subscriberExternalId,\n projectId: usage.projectId,\n workRefType: usage.workRefType,\n workRefId: usage.workRefId,\n provider: usage.provider,\n serviceKey: String(dimensions.serviceKey ?? ''),\n metricKey: usage.metricKey,\n quantity: usage.quantity,\n amount,\n currency: rule.currency,\n pricingRuleId: String(rule.id),\n pricingSnapshot: JSON.stringify({\n ruleKey: rule.ruleKey,\n strategy: rule.strategy,\n terms: rule.getTerms(),\n effectiveFrom: rule.effectiveFrom,\n }),\n status: options.approved ? 'approved' : 'draft',\n approvedAt: options.approved ? new Date() : null,\n _insertOnly: true,\n });\n } catch (error) {\n const concurrent = await this.charges.get(chargeId);\n if (!concurrent) throw error;\n return this.approveCharge(concurrent, options.approved);\n }\n }\n\n private async approveCharge(\n charge: ClientCharge,\n approved = false,\n ): Promise<ClientCharge> {\n if (approved && charge.status === 'draft') {\n charge.status = 'approved';\n charge.approvedAt = new Date();\n await charge.save();\n }\n return charge;\n }\n\n /**\n * Append a signed correction to an approved charge.\n *\n * @param amount - Signed correction in **integer minor units**, same unit as\n * the charge (#2401). Negative is legitimate — that is a credit. A\n * fractional value is rejected rather than silently rounded, because it\n * almost always means the caller passed major units.\n */\n async adjust(\n chargeId: string,\n amount: number,\n reason: string,\n source = '',\n sourceId = '',\n ) {\n if (!Number.isInteger(amount)) {\n throw new Error(\n `Billing adjustment amount must be an integer number of minor units ` +\n `(cents) — got ${amount}. Money is exact: -$0.25 is -25, not -0.25.`,\n );\n }\n const sourced = Boolean(source && sourceId);\n return withEmbeddedSourcedAdjustmentLock(\n this.charges.db,\n this.billingStorage,\n sourced,\n () => this.adjustWithinLock(chargeId, amount, reason, source, sourceId),\n );\n }\n\n private async adjustWithinLock(\n chargeId: string,\n amount: number,\n reason: string,\n source: string,\n sourceId: string,\n ) {\n const initialCharge = await this.charges.get(chargeId);\n if (!initialCharge)\n throw new Error(`Client charge ${chargeId} was not found.`);\n if (\n initialCharge.status !== 'approved' &&\n initialCharge.status !== 'adjusted'\n ) {\n throw new Error(\n `Client charge ${chargeId} must be approved before it can be adjusted.`,\n );\n }\n const sourcedId =\n source && sourceId\n ? await deterministicUuid([\n 'billing-adjustment',\n String(initialCharge.tenantId),\n chargeId,\n source,\n sourceId,\n ])\n : null;\n const db = this.charges.db;\n if (!db.transaction) {\n throw new Error('Atomic billing adjustment requires transaction support');\n }\n try {\n return await withEmbeddedWriteTransaction(\n db,\n isEmbeddedDatabase(db),\n async (transaction) => {\n const options = { db: transaction };\n const charges = await ClientChargeCollection.create(options);\n const adjustments = await BillingAdjustmentCollection.create(options);\n const charge = await charges.get(chargeId);\n if (!charge)\n throw new Error(`Client charge ${chargeId} was not found.`);\n if (charge.status !== 'approved' && charge.status !== 'adjusted') {\n throw new Error(\n `Client charge ${chargeId} must be approved before it can be adjusted.`,\n );\n }\n let adjustment = sourcedId\n ? await adjustments.get(sourcedId)\n : undefined;\n if (!adjustment) {\n adjustment = await adjustments.create({\n ...(sourcedId ? { id: sourcedId } : {}),\n tenantId: charge.tenantId,\n clientChargeId: chargeId,\n amount,\n currency: charge.currency,\n reason,\n source,\n sourceId,\n _insertOnly: Boolean(sourcedId),\n });\n }\n if (charge.status === 'approved') {\n charge.status = 'adjusted';\n await charge.save();\n }\n return adjustment;\n },\n );\n } catch (error) {\n // A concurrent sourced adjustment may win the deterministic insert. Its\n // transaction also owns the charge-state transition, so it is safe to\n // return only after both durable records are visible.\n if (sourcedId) {\n // Embedded adapters multiplex one native handle. Keep recovery reads\n // sequential just like the transaction flow protected above.\n const adjustment = await this.adjustments.get(sourcedId);\n const charge = await this.charges.get(chargeId);\n if (adjustment && charge?.status === 'adjusted') return adjustment;\n }\n throw error;\n }\n }\n\n /**\n * Price one usage event, in **integer minor units** (#2401).\n *\n * The pricing terms stay fractional on purpose — a per-token rate is\n * routinely a fraction of a cent, and an integer `unitPrice` would truncate\n * it to zero — so terms are read as *minor units per unit of usage* and only\n * the result is rounded. This is the single boundary in the package where a\n * rate meets money, which is what lets `evaluateSpending()` compare sums\n * against `SpendingPolicy.limitAmount` exactly.\n *\n * Replaces the old micro-unit rounding (`Math.round(amount * 1e6) / 1e6`),\n * which kept six sub-cent digits in a column that is now integral.\n */\n async calculateAmount(\n rule: PricingRule,\n quantity: number,\n dimensions: Record<string, unknown> = {},\n ): Promise<number> {\n const terms = rule.getTerms();\n const number = (key: string, fallback = 0) =>\n Number(terms[key] ?? fallback);\n let amount: number;\n switch (rule.strategy as PricingStrategy) {\n case 'fixed_unit':\n amount = quantity * number('unitPrice');\n break;\n case 'cost_plus':\n amount =\n Number(dimensions.providerCost ?? 0) +\n number('fixedMarkup') +\n Number(dimensions.providerCost ?? 0) * number('markupRatio');\n break;\n case 'multiplier':\n amount =\n Number(dimensions.providerCost ?? quantity) * number('multiplier', 1);\n break;\n case 'flat':\n amount = number('amount');\n break;\n case 'included_overage':\n amount =\n Math.max(0, quantity - number('includedQuantity')) *\n number('overageUnitPrice');\n break;\n case 'tiered':\n amount = priceTiers(\n quantity,\n Array.isArray(terms.tiers) ? terms.tiers : [],\n );\n break;\n case 'custom': {\n const custom = this.customStrategies.get(\n String(terms.strategyKey ?? ''),\n );\n if (!custom)\n throw new Error(\n `Custom pricing strategy '${String(terms.strategyKey ?? '')}' is not registered.`,\n );\n amount = await custom({ usage: { quantity, dimensions }, rule, terms });\n break;\n }\n }\n if (!Number.isFinite(amount) || amount < 0)\n throw new Error('Pricing produced an invalid amount.');\n return Math.round(amount);\n }\n}\n\nexport interface SpendingDecision {\n allowed: boolean;\n approvalRequired: boolean;\n state: 'ok' | 'observed' | 'warned' | 'blocked' | 'approval_required';\n /**\n * Already-spent plus estimated, in **integer minor units** — the same unit as\n * `SpendingPolicy.limitAmount` it is compared against (#2401).\n */\n projectedAmount: number;\n matchedPolicyId: string | null;\n}\n\ninterface SpendingEvaluationInput {\n tenantId: string;\n subscriberKind?: SubscriberKind;\n subscriberExternalId?: string;\n projectId?: string;\n serviceKey?: string;\n metricKey: string;\n /** Estimated cost of the pending call, in integer minor units (#2401). */\n estimatedAmount: number;\n currency: string;\n at?: Date;\n}\n\nexport class SpendingPolicyEvaluator {\n constructor(\n private readonly policies: SpendingPolicyCollection,\n private readonly charges: ClientChargeCollection,\n private readonly adjustments: BillingAdjustmentCollection,\n ) {}\n static async create(options: SmrtClassOptions = {}) {\n const policies = await SpendingPolicyCollection.create(options);\n const sharedOptions = { ...options, db: policies.db };\n return new SpendingPolicyEvaluator(\n policies,\n await ClientChargeCollection.create(sharedOptions),\n await BillingAdjustmentCollection.create(sharedOptions),\n );\n }\n\n async evaluate(input: SpendingEvaluationInput): Promise<SpendingDecision> {\n const at = input.at ?? new Date();\n const normalizedInput = {\n ...input,\n ...subscriberToColumns(\n normalizeSubscriber({\n tenantId: input.tenantId,\n subscriberKind: input.subscriberKind,\n subscriberExternalId: input.subscriberExternalId,\n }),\n ),\n };\n const candidates = await this.policies.list({\n where: {\n tenantId: input.tenantId,\n currency: input.currency,\n active: true,\n },\n });\n const matchingPolicies = selectPolicies(candidates, normalizedInput);\n if (matchingPolicies.length === 0)\n return {\n allowed: true,\n approvalRequired: false,\n state: 'ok',\n projectedAmount: input.estimatedAmount,\n matchedPolicyId: null,\n };\n const decisions = await Promise.all(\n matchingPolicies.map((policy) =>\n this.evaluatePolicy(policy, normalizedInput, at),\n ),\n );\n return decisions.reduce((mostRestrictive, decision) =>\n decisionRank(decision) > decisionRank(mostRestrictive)\n ? decision\n : mostRestrictive,\n );\n }\n\n private async evaluatePolicy(\n policy: SpendingPolicy,\n input: SpendingEvaluationInput,\n at: Date,\n ): Promise<SpendingDecision> {\n const [start, end] = policyWindow(policy, at);\n const chargeWhere: Record<string, unknown> = {\n tenantId: input.tenantId,\n currency: policy.currency,\n status: ['approved', 'adjusted'],\n 'approvedAt >=': start.toISOString(),\n 'approvedAt <': end.toISOString(),\n };\n if (policy.metricKey) chargeWhere.metricKey = policy.metricKey;\n if (policy.projectId) chargeWhere.projectId = policy.projectId;\n if (policy.serviceKey) chargeWhere.serviceKey = policy.serviceKey;\n if (policy.subscriberKind)\n chargeWhere.subscriberKind = policy.subscriberKind;\n if (policy.subscriberExternalId)\n chargeWhere.subscriberExternalId = policy.subscriberExternalId;\n const rows = await this.charges.list({ where: chargeWhere });\n const scopedCharges = rows.filter(\n (charge) =>\n (charge.status === 'approved' || charge.status === 'adjusted') &&\n matchesChargeScope(policy, charge),\n );\n const chargeIds = scopedCharges\n .map((charge) => charge.id)\n .filter((id): id is string => Boolean(id));\n const chargeIdSet = new Set(chargeIds);\n const adjustmentRows =\n chargeIds.length > 0\n ? await this.adjustments.list({\n where: {\n tenantId: input.tenantId,\n currency: policy.currency,\n clientChargeId: chargeIds,\n },\n })\n : [];\n const spent = scopedCharges.reduce((sum, charge) => sum + charge.amount, 0);\n const correctedSpent =\n spent +\n adjustmentRows\n .filter((adjustment) => chargeIdSet.has(adjustment.clientChargeId))\n .reduce((sum, adjustment) => sum + adjustment.amount, 0);\n const projectedAmount = correctedSpent + input.estimatedAmount;\n const exceeded = projectedAmount > policy.limitAmount;\n if (!exceeded || policy.behavior === 'observe')\n return {\n allowed: true,\n approvalRequired: false,\n state: exceeded ? 'observed' : 'ok',\n projectedAmount,\n matchedPolicyId: policy.id ?? null,\n };\n if (policy.behavior === 'warn')\n return {\n allowed: true,\n approvalRequired: false,\n state: 'warned',\n projectedAmount,\n matchedPolicyId: policy.id ?? null,\n };\n if (policy.behavior === 'approval_required')\n return {\n allowed: false,\n approvalRequired: true,\n state: 'approval_required',\n projectedAmount,\n matchedPolicyId: policy.id ?? null,\n };\n return {\n allowed: false,\n approvalRequired: false,\n state: 'blocked',\n projectedAmount,\n matchedPolicyId: policy.id ?? null,\n };\n }\n}\n\nfunction selectRule(\n rules: PricingRule[],\n at: Date,\n serviceKey: string,\n): PricingRule | undefined {\n return rules\n .filter(\n (rule) =>\n rule.effectiveFrom <= at &&\n (!rule.effectiveTo || rule.effectiveTo > at) &&\n (!rule.serviceKey || rule.serviceKey === serviceKey),\n )\n .sort(\n (a, b) =>\n Number(Boolean(b.serviceKey)) - Number(Boolean(a.serviceKey)) ||\n b.priority - a.priority ||\n b.effectiveFrom.getTime() - a.effectiveFrom.getTime(),\n )[0];\n}\nfunction priceTiers(quantity: number, tiers: unknown[]): number {\n let remaining = quantity,\n prior = 0,\n amount = 0;\n for (const raw of tiers as Array<Record<string, unknown>>) {\n const upTo = raw.upTo == null ? Infinity : Number(raw.upTo);\n const units = Math.max(0, Math.min(remaining, upTo - prior));\n amount += units * Number(raw.unitPrice ?? 0);\n remaining -= units;\n prior = upTo;\n if (remaining <= 0) break;\n }\n return amount;\n}\nfunction selectPolicies(\n policies: SpendingPolicy[],\n input: Record<string, unknown>,\n): SpendingPolicy[] {\n return policies\n .filter(\n (p) =>\n hasValidSubscriberScope(p) &&\n (!p.metricKey || p.metricKey === input.metricKey) &&\n p.currency === input.currency &&\n (!p.projectId || p.projectId === input.projectId) &&\n (!p.serviceKey || p.serviceKey === input.serviceKey) &&\n (!p.subscriberKind ||\n (p.subscriberKind === (input.subscriberKind ?? 'tenant') &&\n (!p.subscriberExternalId ||\n p.subscriberExternalId === input.subscriberExternalId))),\n )\n .sort((a, b) => scopeScore(b) - scopeScore(a) || b.priority - a.priority);\n}\n\nfunction decisionRank(decision: SpendingDecision): number {\n return {\n ok: 0,\n observed: 1,\n warned: 2,\n approval_required: 3,\n blocked: 4,\n }[decision.state];\n}\nfunction scopeScore(p: SpendingPolicy): number {\n return (\n Number(Boolean(p.subscriberKind)) +\n Number(Boolean(p.subscriberExternalId)) +\n Number(Boolean(p.projectId)) +\n Number(Boolean(p.serviceKey)) +\n Number(Boolean(p.metricKey))\n );\n}\nfunction matchesChargeScope(p: SpendingPolicy, c: ClientCharge): boolean {\n return (\n hasValidSubscriberScope(p) &&\n (!p.projectId || p.projectId === c.projectId) &&\n (!p.serviceKey || p.serviceKey === c.serviceKey) &&\n (!p.metricKey || p.metricKey === c.metricKey) &&\n (!p.subscriberKind ||\n (p.subscriberKind === c.subscriberKind &&\n (!p.subscriberExternalId ||\n p.subscriberExternalId === c.subscriberExternalId)))\n );\n}\nfunction hasValidSubscriberScope(p: SpendingPolicy): boolean {\n return !p.subscriberExternalId || Boolean(p.subscriberKind);\n}\nfunction policyWindow(policy: SpendingPolicy, at: Date): [Date, Date] {\n const end = new Date(at);\n const start = new Date(at);\n if (policy.period === 'rolling')\n start.setTime(at.getTime() - policy.rollingSeconds * 1000);\n else if (policy.period === 'day') start.setUTCHours(0, 0, 0, 0);\n else if (policy.period === 'week') {\n start.setUTCHours(0, 0, 0, 0);\n start.setUTCDate(start.getUTCDate() - ((start.getUTCDay() + 6) % 7));\n } else if (policy.period === 'month') {\n start.setUTCDate(1);\n start.setUTCHours(0, 0, 0, 0);\n } else {\n start.setUTCMonth(0, 1);\n start.setUTCHours(0, 0, 0, 0);\n }\n return [start, end];\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 let fallbackAiMetricKeys: string[] = [];\n if (aiMetricKeys.length > 0) {\n if (!(await this.hasAiUsageTable())) {\n fallbackAiMetricKeys = aiMetricKeys;\n } else {\n try {\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 } catch (error) {\n if (!isMissingAiUsageTableError(error)) {\n throw error;\n }\n fallbackAiMetricKeys = aiMetricKeys;\n }\n }\n }\n\n const persistedSummaries = await this.metrics.summarizeUsageBatch({\n ...options,\n metricKeys: [...persistedMetricKeys, ...fallbackAiMetricKeys],\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 if (!(await this.hasAiUsageTable())) {\n return null;\n }\n\n let summary: AiUsageSummary;\n try {\n summary = await this.summarizeAiUsage({\n tenantId: options.tenantId,\n window: options.window,\n });\n } catch (error) {\n if (isMissingAiUsageTableError(error)) {\n return null;\n }\n throw error;\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 private async hasAiUsageTable(): Promise<boolean> {\n return this.metrics.db.tableExists('_smrt_ai_usage');\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 isMissingAiUsageTableError(\n error: unknown,\n seen = new Set<unknown>(),\n): boolean {\n if (!error) {\n return false;\n }\n if (typeof error !== 'object') {\n return isMissingAiUsageTableMessage(String(error));\n }\n if (seen.has(error)) {\n return false;\n }\n seen.add(error);\n\n const err = error as {\n code?: unknown;\n message?: unknown;\n cause?: unknown;\n context?: Record<string, unknown>;\n };\n const codes = [err?.code, err?.context?.code, err?.context?.errorCode];\n if (codes.some((code) => code === '42P01')) {\n return true;\n }\n\n const messages = [\n err?.message,\n err?.context?.originalError,\n err?.context?.error,\n err?.context?.details,\n ];\n for (const candidate of messages) {\n if (\n candidate &&\n typeof candidate === 'object' &&\n isMissingAiUsageTableError(candidate, seen)\n ) {\n return true;\n }\n if (isMissingAiUsageTableMessage(String(candidate ?? ''))) {\n return true;\n }\n }\n\n return err?.cause ? isMissingAiUsageTableError(err.cause, seen) : false;\n}\n\nfunction isMissingAiUsageTableMessage(message: string): boolean {\n const normalized = message.toLowerCase();\n return (\n normalized.includes('_smrt_ai_usage') &&\n (normalized.includes('no such table') ||\n normalized.includes('does not exist') ||\n normalized.includes('undefined table'))\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?.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"],"mappings":"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"}
|