@agentlensai/server 0.10.0 → 0.11.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.
Files changed (185) hide show
  1. package/dist/cloud/auth/api-key-middleware.d.ts +66 -0
  2. package/dist/cloud/auth/api-key-middleware.d.ts.map +1 -0
  3. package/dist/cloud/auth/api-key-middleware.js +147 -0
  4. package/dist/cloud/auth/api-key-middleware.js.map +1 -0
  5. package/dist/cloud/auth/api-keys.d.ts +90 -0
  6. package/dist/cloud/auth/api-keys.d.ts.map +1 -0
  7. package/dist/cloud/auth/api-keys.js +162 -0
  8. package/dist/cloud/auth/api-keys.js.map +1 -0
  9. package/dist/cloud/auth/audit-log.d.ts +66 -0
  10. package/dist/cloud/auth/audit-log.d.ts.map +1 -0
  11. package/dist/cloud/auth/audit-log.js +92 -0
  12. package/dist/cloud/auth/audit-log.js.map +1 -0
  13. package/dist/cloud/auth/auth-service.d.ts +77 -0
  14. package/dist/cloud/auth/auth-service.d.ts.map +1 -0
  15. package/dist/cloud/auth/auth-service.js +229 -0
  16. package/dist/cloud/auth/auth-service.js.map +1 -0
  17. package/dist/cloud/auth/brute-force.d.ts +36 -0
  18. package/dist/cloud/auth/brute-force.d.ts.map +1 -0
  19. package/dist/cloud/auth/brute-force.js +67 -0
  20. package/dist/cloud/auth/brute-force.js.map +1 -0
  21. package/dist/cloud/auth/index.d.ts +11 -0
  22. package/dist/cloud/auth/index.d.ts.map +1 -0
  23. package/dist/cloud/auth/index.js +11 -0
  24. package/dist/cloud/auth/index.js.map +1 -0
  25. package/dist/cloud/auth/jwt.d.ts +34 -0
  26. package/dist/cloud/auth/jwt.d.ts.map +1 -0
  27. package/dist/cloud/auth/jwt.js +68 -0
  28. package/dist/cloud/auth/jwt.js.map +1 -0
  29. package/dist/cloud/auth/oauth.d.ts +37 -0
  30. package/dist/cloud/auth/oauth.d.ts.map +1 -0
  31. package/dist/cloud/auth/oauth.js +120 -0
  32. package/dist/cloud/auth/oauth.js.map +1 -0
  33. package/dist/cloud/auth/passwords.d.ts +25 -0
  34. package/dist/cloud/auth/passwords.d.ts.map +1 -0
  35. package/dist/cloud/auth/passwords.js +50 -0
  36. package/dist/cloud/auth/passwords.js.map +1 -0
  37. package/dist/cloud/auth/rbac.d.ts +51 -0
  38. package/dist/cloud/auth/rbac.d.ts.map +1 -0
  39. package/dist/cloud/auth/rbac.js +89 -0
  40. package/dist/cloud/auth/rbac.js.map +1 -0
  41. package/dist/cloud/auth/tokens.d.ts +18 -0
  42. package/dist/cloud/auth/tokens.d.ts.map +1 -0
  43. package/dist/cloud/auth/tokens.js +29 -0
  44. package/dist/cloud/auth/tokens.js.map +1 -0
  45. package/dist/cloud/billing/billing-service.d.ts +44 -0
  46. package/dist/cloud/billing/billing-service.d.ts.map +1 -0
  47. package/dist/cloud/billing/billing-service.js +153 -0
  48. package/dist/cloud/billing/billing-service.js.map +1 -0
  49. package/dist/cloud/billing/index.d.ts +11 -0
  50. package/dist/cloud/billing/index.d.ts.map +1 -0
  51. package/dist/cloud/billing/index.js +11 -0
  52. package/dist/cloud/billing/index.js.map +1 -0
  53. package/dist/cloud/billing/invoice-service.d.ts +57 -0
  54. package/dist/cloud/billing/invoice-service.d.ts.map +1 -0
  55. package/dist/cloud/billing/invoice-service.js +123 -0
  56. package/dist/cloud/billing/invoice-service.js.map +1 -0
  57. package/dist/cloud/billing/plan-management.d.ts +46 -0
  58. package/dist/cloud/billing/plan-management.d.ts.map +1 -0
  59. package/dist/cloud/billing/plan-management.js +157 -0
  60. package/dist/cloud/billing/plan-management.js.map +1 -0
  61. package/dist/cloud/billing/quota-enforcement.d.ts +53 -0
  62. package/dist/cloud/billing/quota-enforcement.d.ts.map +1 -0
  63. package/dist/cloud/billing/quota-enforcement.js +143 -0
  64. package/dist/cloud/billing/quota-enforcement.js.map +1 -0
  65. package/dist/cloud/billing/stripe-client.d.ts +142 -0
  66. package/dist/cloud/billing/stripe-client.d.ts.map +1 -0
  67. package/dist/cloud/billing/stripe-client.js +169 -0
  68. package/dist/cloud/billing/stripe-client.js.map +1 -0
  69. package/dist/cloud/billing/trial-service.d.ts +47 -0
  70. package/dist/cloud/billing/trial-service.d.ts.map +1 -0
  71. package/dist/cloud/billing/trial-service.js +104 -0
  72. package/dist/cloud/billing/trial-service.js.map +1 -0
  73. package/dist/cloud/billing/usage-metering.d.ts +83 -0
  74. package/dist/cloud/billing/usage-metering.d.ts.map +1 -0
  75. package/dist/cloud/billing/usage-metering.js +174 -0
  76. package/dist/cloud/billing/usage-metering.js.map +1 -0
  77. package/dist/cloud/ingestion/backpressure.d.ts +107 -0
  78. package/dist/cloud/ingestion/backpressure.d.ts.map +1 -0
  79. package/dist/cloud/ingestion/backpressure.js +134 -0
  80. package/dist/cloud/ingestion/backpressure.js.map +1 -0
  81. package/dist/cloud/ingestion/batch-writer.d.ts +115 -0
  82. package/dist/cloud/ingestion/batch-writer.d.ts.map +1 -0
  83. package/dist/cloud/ingestion/batch-writer.js +319 -0
  84. package/dist/cloud/ingestion/batch-writer.js.map +1 -0
  85. package/dist/cloud/ingestion/dlq-manager.d.ts +116 -0
  86. package/dist/cloud/ingestion/dlq-manager.d.ts.map +1 -0
  87. package/dist/cloud/ingestion/dlq-manager.js +244 -0
  88. package/dist/cloud/ingestion/dlq-manager.js.map +1 -0
  89. package/dist/cloud/ingestion/event-queue.d.ts +105 -0
  90. package/dist/cloud/ingestion/event-queue.d.ts.map +1 -0
  91. package/dist/cloud/ingestion/event-queue.js +185 -0
  92. package/dist/cloud/ingestion/event-queue.js.map +1 -0
  93. package/dist/cloud/ingestion/gateway.d.ts +68 -0
  94. package/dist/cloud/ingestion/gateway.d.ts.map +1 -0
  95. package/dist/cloud/ingestion/gateway.js +198 -0
  96. package/dist/cloud/ingestion/gateway.js.map +1 -0
  97. package/dist/cloud/ingestion/index.d.ts +7 -0
  98. package/dist/cloud/ingestion/index.d.ts.map +1 -0
  99. package/dist/cloud/ingestion/index.js +7 -0
  100. package/dist/cloud/ingestion/index.js.map +1 -0
  101. package/dist/cloud/ingestion/rate-limiter.d.ts +73 -0
  102. package/dist/cloud/ingestion/rate-limiter.d.ts.map +1 -0
  103. package/dist/cloud/ingestion/rate-limiter.js +153 -0
  104. package/dist/cloud/ingestion/rate-limiter.js.map +1 -0
  105. package/dist/cloud/migrate.d.ts +45 -0
  106. package/dist/cloud/migrate.d.ts.map +1 -0
  107. package/dist/cloud/migrate.js +147 -0
  108. package/dist/cloud/migrate.js.map +1 -0
  109. package/dist/cloud/migration/export-import.d.ts +56 -0
  110. package/dist/cloud/migration/export-import.d.ts.map +1 -0
  111. package/dist/cloud/migration/export-import.js +289 -0
  112. package/dist/cloud/migration/export-import.js.map +1 -0
  113. package/dist/cloud/migration/index.d.ts +5 -0
  114. package/dist/cloud/migration/index.d.ts.map +1 -0
  115. package/dist/cloud/migration/index.js +5 -0
  116. package/dist/cloud/migration/index.js.map +1 -0
  117. package/dist/cloud/org-service.d.ts +68 -0
  118. package/dist/cloud/org-service.d.ts.map +1 -0
  119. package/dist/cloud/org-service.js +169 -0
  120. package/dist/cloud/org-service.js.map +1 -0
  121. package/dist/cloud/partition-maintenance.d.ts +29 -0
  122. package/dist/cloud/partition-maintenance.d.ts.map +1 -0
  123. package/dist/cloud/partition-maintenance.js +96 -0
  124. package/dist/cloud/partition-maintenance.js.map +1 -0
  125. package/dist/cloud/retention/index.d.ts +7 -0
  126. package/dist/cloud/retention/index.d.ts.map +1 -0
  127. package/dist/cloud/retention/index.js +7 -0
  128. package/dist/cloud/retention/index.js.map +1 -0
  129. package/dist/cloud/retention/partition-management.d.ts +61 -0
  130. package/dist/cloud/retention/partition-management.d.ts.map +1 -0
  131. package/dist/cloud/retention/partition-management.js +167 -0
  132. package/dist/cloud/retention/partition-management.js.map +1 -0
  133. package/dist/cloud/retention/retention-job.d.ts +70 -0
  134. package/dist/cloud/retention/retention-job.d.ts.map +1 -0
  135. package/dist/cloud/retention/retention-job.js +160 -0
  136. package/dist/cloud/retention/retention-job.js.map +1 -0
  137. package/dist/cloud/retention/retention-policy.d.ts +27 -0
  138. package/dist/cloud/retention/retention-policy.d.ts.map +1 -0
  139. package/dist/cloud/retention/retention-policy.js +36 -0
  140. package/dist/cloud/retention/retention-policy.js.map +1 -0
  141. package/dist/cloud/routes/api-key-routes.d.ts +38 -0
  142. package/dist/cloud/routes/api-key-routes.d.ts.map +1 -0
  143. package/dist/cloud/routes/api-key-routes.js +84 -0
  144. package/dist/cloud/routes/api-key-routes.js.map +1 -0
  145. package/dist/cloud/routes/audit-routes.d.ts +36 -0
  146. package/dist/cloud/routes/audit-routes.d.ts.map +1 -0
  147. package/dist/cloud/routes/audit-routes.js +47 -0
  148. package/dist/cloud/routes/audit-routes.js.map +1 -0
  149. package/dist/cloud/routes/billing-routes.d.ts +51 -0
  150. package/dist/cloud/routes/billing-routes.d.ts.map +1 -0
  151. package/dist/cloud/routes/billing-routes.js +114 -0
  152. package/dist/cloud/routes/billing-routes.js.map +1 -0
  153. package/dist/cloud/routes/onboarding-routes.d.ts +34 -0
  154. package/dist/cloud/routes/onboarding-routes.d.ts.map +1 -0
  155. package/dist/cloud/routes/onboarding-routes.js +58 -0
  156. package/dist/cloud/routes/onboarding-routes.js.map +1 -0
  157. package/dist/cloud/routes/org-routes.d.ts +80 -0
  158. package/dist/cloud/routes/org-routes.d.ts.map +1 -0
  159. package/dist/cloud/routes/org-routes.js +153 -0
  160. package/dist/cloud/routes/org-routes.js.map +1 -0
  161. package/dist/cloud/routes/usage-routes.d.ts +18 -0
  162. package/dist/cloud/routes/usage-routes.d.ts.map +1 -0
  163. package/dist/cloud/routes/usage-routes.js +66 -0
  164. package/dist/cloud/routes/usage-routes.js.map +1 -0
  165. package/dist/cloud/storage/adapter.d.ts +102 -0
  166. package/dist/cloud/storage/adapter.d.ts.map +1 -0
  167. package/dist/cloud/storage/adapter.js +21 -0
  168. package/dist/cloud/storage/adapter.js.map +1 -0
  169. package/dist/cloud/storage/index.d.ts +8 -0
  170. package/dist/cloud/storage/index.d.ts.map +1 -0
  171. package/dist/cloud/storage/index.js +7 -0
  172. package/dist/cloud/storage/index.js.map +1 -0
  173. package/dist/cloud/storage/postgres-adapter.d.ts +34 -0
  174. package/dist/cloud/storage/postgres-adapter.d.ts.map +1 -0
  175. package/dist/cloud/storage/postgres-adapter.js +544 -0
  176. package/dist/cloud/storage/postgres-adapter.js.map +1 -0
  177. package/dist/cloud/storage/sqlite-adapter.d.ts +29 -0
  178. package/dist/cloud/storage/sqlite-adapter.d.ts.map +1 -0
  179. package/dist/cloud/storage/sqlite-adapter.js +176 -0
  180. package/dist/cloud/storage/sqlite-adapter.js.map +1 -0
  181. package/dist/cloud/tenant-pool.d.ts +49 -0
  182. package/dist/cloud/tenant-pool.d.ts.map +1 -0
  183. package/dist/cloud/tenant-pool.js +61 -0
  184. package/dist/cloud/tenant-pool.js.map +1 -0
  185. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cloud/billing/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EACtB,KAAK,QAAQ,EACb,WAAW,EACX,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,cAAc,EAAE,KAAK,kBAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAEnG,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,eAAe,GACrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,aAAa,EACb,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,YAAY,GAClB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Billing Module Index (S-6.1 through S-6.6)
3
+ */
4
+ export { TIER_CONFIG, MockStripeClient, createStripeClient, } from './stripe-client.js';
5
+ export { BillingService } from './billing-service.js';
6
+ export { UsageAccumulator, UsageQuery, reportOverageToStripe, } from './usage-metering.js';
7
+ export { QuotaEnforcer, } from './quota-enforcement.js';
8
+ export { PlanManager, ANNUAL_PRICE_IDS, } from './plan-management.js';
9
+ export { InvoiceService, ANNUAL_DISCOUNT, calculateAnnualPrice, } from './invoice-service.js';
10
+ export { TrialService, TRIAL_DURATION_DAYS, } from './trial-service.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cloud/billing/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAUL,WAAW,EACX,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,cAAc,EAA+C,MAAM,sBAAsB,CAAC;AAEnG,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,qBAAqB,GAItB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,aAAa,GAId,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,WAAW,EACX,gBAAgB,GAGjB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,cAAc,EACd,eAAe,EACf,oBAAoB,GAIrB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,YAAY,EACZ,mBAAmB,GAGpB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Invoice Generation & Annual Billing (S-6.5)
3
+ *
4
+ * - Monthly invoices via Stripe with itemized usage (base + overage)
5
+ * - Annual billing with 20% discount
6
+ * - Invoices table synced from Stripe webhooks
7
+ * - Invoice list queryable via API
8
+ */
9
+ import type { IStripeClient, StripeWebhookEvent } from './stripe-client.js';
10
+ import type { MigrationClient } from '../migrate.js';
11
+ export interface InvoiceServiceDeps {
12
+ stripe: IStripeClient;
13
+ db: MigrationClient;
14
+ }
15
+ export interface InvoiceRecord {
16
+ id: string;
17
+ org_id: string;
18
+ stripe_invoice_id: string;
19
+ period_start: string;
20
+ period_end: string;
21
+ base_amount_cents: number;
22
+ overage_amount_cents: number;
23
+ total_amount_cents: number;
24
+ status: 'draft' | 'open' | 'paid' | 'void' | 'uncollectible';
25
+ billing_interval: 'monthly' | 'annual';
26
+ line_items: InvoiceLineItem[];
27
+ created_at: string;
28
+ }
29
+ export interface InvoiceLineItem {
30
+ description: string;
31
+ amount_cents: number;
32
+ quantity: number;
33
+ }
34
+ /** Annual pricing = monthly × 12 × 0.8 (20% discount) */
35
+ export declare const ANNUAL_DISCOUNT = 0.2;
36
+ export declare function calculateAnnualPrice(monthlyPriceCents: number): number;
37
+ export declare class InvoiceService {
38
+ private deps;
39
+ constructor(deps: InvoiceServiceDeps);
40
+ /**
41
+ * Sync an invoice from a Stripe webhook event into the invoices table.
42
+ */
43
+ syncInvoiceFromWebhook(event: StripeWebhookEvent): Promise<InvoiceRecord | null>;
44
+ /**
45
+ * List invoices for an org, newest first.
46
+ */
47
+ listInvoices(orgId: string, limit?: number, offset?: number): Promise<InvoiceRecord[]>;
48
+ /**
49
+ * Calculate overage charges for an org's current period.
50
+ */
51
+ calculateOverageCharges(orgId: string): Promise<{
52
+ overage_events: number;
53
+ overage_cost_cents: number;
54
+ rate_per_1k_cents: number;
55
+ }>;
56
+ }
57
+ //# sourceMappingURL=invoice-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoice-service.d.ts","sourceRoot":"","sources":["../../../src/cloud/billing/invoice-service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAY,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAEtF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,aAAa,CAAC;IACtB,EAAE,EAAE,eAAe,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,eAAe,CAAC;IAC7D,gBAAgB,EAAE,SAAS,GAAG,QAAQ,CAAC;IACvC,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,yDAAyD;AACzD,eAAO,MAAM,eAAe,MAAO,CAAC;AAEpC,wBAAgB,oBAAoB,CAAC,iBAAiB,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,qBAAa,cAAc;IACb,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,kBAAkB;IAE5C;;OAEG;IACG,sBAAsB,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IA2EtF;;OAEG;IACG,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAK,EAAE,MAAM,SAAI,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IASnF;;OAEG;IACG,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QACpD,cAAc,EAAE,MAAM,CAAC;QACvB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;CAkCH"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Invoice Generation & Annual Billing (S-6.5)
3
+ *
4
+ * - Monthly invoices via Stripe with itemized usage (base + overage)
5
+ * - Annual billing with 20% discount
6
+ * - Invoices table synced from Stripe webhooks
7
+ * - Invoice list queryable via API
8
+ */
9
+ import { TIER_CONFIG } from './stripe-client.js';
10
+ /** Annual pricing = monthly × 12 × 0.8 (20% discount) */
11
+ export const ANNUAL_DISCOUNT = 0.20;
12
+ export function calculateAnnualPrice(monthlyPriceCents) {
13
+ return Math.round(monthlyPriceCents * 12 * (1 - ANNUAL_DISCOUNT));
14
+ }
15
+ export class InvoiceService {
16
+ deps;
17
+ constructor(deps) {
18
+ this.deps = deps;
19
+ }
20
+ /**
21
+ * Sync an invoice from a Stripe webhook event into the invoices table.
22
+ */
23
+ async syncInvoiceFromWebhook(event) {
24
+ const invoice = event.data.object;
25
+ const stripeInvoiceId = invoice.id;
26
+ const customerId = invoice.customer;
27
+ const status = invoice.status;
28
+ const amountDue = invoice.amount_due ?? 0;
29
+ const amountPaid = invoice.amount_paid ?? 0;
30
+ const periodStart = invoice.period_start;
31
+ const periodEnd = invoice.period_end;
32
+ // Extract line items
33
+ const lines = invoice.lines?.data ?? [];
34
+ const lineItems = lines.map((line) => ({
35
+ description: line.description ?? 'Subscription',
36
+ amount_cents: line.amount ?? 0,
37
+ quantity: line.quantity ?? 1,
38
+ }));
39
+ // Calculate base vs overage from line items
40
+ let baseAmount = 0;
41
+ let overageAmount = 0;
42
+ for (const line of lineItems) {
43
+ if ((line.description ?? '').toLowerCase().includes('overage')) {
44
+ overageAmount += line.amount_cents;
45
+ }
46
+ else {
47
+ baseAmount += line.amount_cents;
48
+ }
49
+ }
50
+ const totalAmount = status === 'paid' ? amountPaid : amountDue;
51
+ // Find org
52
+ const orgResult = await this.deps.db.query(`SELECT id FROM orgs WHERE stripe_customer_id = $1`, [customerId]);
53
+ const org = orgResult.rows[0];
54
+ if (!org)
55
+ return null;
56
+ // Determine billing interval from period length
57
+ const periodDays = (periodEnd - periodStart) / 86400;
58
+ const billingInterval = periodDays > 60 ? 'annual' : 'monthly';
59
+ // Upsert invoice
60
+ await this.deps.db.query(`INSERT INTO invoices (org_id, stripe_invoice_id, period_start, period_end,
61
+ base_amount_cents, overage_amount_cents, total_amount_cents, status,
62
+ billing_interval, line_items)
63
+ VALUES ($1, $2, to_timestamp($3), to_timestamp($4), $5, $6, $7, $8, $9, $10::jsonb)
64
+ ON CONFLICT (stripe_invoice_id)
65
+ DO UPDATE SET status = $8, total_amount_cents = $7, overage_amount_cents = $6,
66
+ line_items = $10::jsonb`, [
67
+ org.id, stripeInvoiceId, periodStart, periodEnd,
68
+ baseAmount, overageAmount, totalAmount, status,
69
+ billingInterval, JSON.stringify(lineItems),
70
+ ]);
71
+ return {
72
+ id: stripeInvoiceId,
73
+ org_id: org.id,
74
+ stripe_invoice_id: stripeInvoiceId,
75
+ period_start: new Date(periodStart * 1000).toISOString(),
76
+ period_end: new Date(periodEnd * 1000).toISOString(),
77
+ base_amount_cents: baseAmount,
78
+ overage_amount_cents: overageAmount,
79
+ total_amount_cents: totalAmount,
80
+ status: status,
81
+ billing_interval: billingInterval,
82
+ line_items: lineItems,
83
+ created_at: new Date().toISOString(),
84
+ };
85
+ }
86
+ /**
87
+ * List invoices for an org, newest first.
88
+ */
89
+ async listInvoices(orgId, limit = 20, offset = 0) {
90
+ const result = await this.deps.db.query(`SELECT * FROM invoices WHERE org_id = $1
91
+ ORDER BY period_start DESC LIMIT $2 OFFSET $3`, [orgId, limit, offset]);
92
+ return result.rows;
93
+ }
94
+ /**
95
+ * Calculate overage charges for an org's current period.
96
+ */
97
+ async calculateOverageCharges(orgId) {
98
+ // Get org plan and usage
99
+ const orgResult = await this.deps.db.query(`SELECT plan, event_quota FROM orgs WHERE id = $1`, [orgId]);
100
+ const org = orgResult.rows[0];
101
+ if (!org)
102
+ throw new Error(`Org ${orgId} not found`);
103
+ const plan = org.plan;
104
+ const quota = org.event_quota;
105
+ const rate = TIER_CONFIG[plan]?.overage_rate_per_1k_cents ?? 0;
106
+ // Get current month usage
107
+ const now = new Date();
108
+ const month = `${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, '0')}`;
109
+ const monthStart = `${month}-01T00:00:00Z`;
110
+ const nextMonth = new Date(now.getUTCFullYear(), now.getUTCMonth() + 1, 1);
111
+ const usageResult = await this.deps.db.query(`SELECT COALESCE(SUM(event_count), 0)::int as total
112
+ FROM usage_records WHERE org_id = $1 AND hour >= $2 AND hour < $3`, [orgId, monthStart, nextMonth.toISOString()]);
113
+ const totalEvents = usageResult.rows[0]?.total ?? 0;
114
+ const overageEvents = Math.max(0, totalEvents - quota);
115
+ const overageCostCents = Math.ceil(overageEvents / 1000) * rate;
116
+ return {
117
+ overage_events: overageEvents,
118
+ overage_cost_cents: overageCostCents,
119
+ rate_per_1k_cents: rate,
120
+ };
121
+ }
122
+ }
123
+ //# sourceMappingURL=invoice-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoice-service.js","sourceRoot":"","sources":["../../../src/cloud/billing/invoice-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Plan Upgrade/Downgrade (S-6.4)
3
+ *
4
+ * - Upgrade: immediate, with proration
5
+ * - Downgrade: at end of billing period
6
+ * - Enforces tier limits (keys, members, orgs)
7
+ */
8
+ import type { IStripeClient, TierName } from './stripe-client.js';
9
+ import type { MigrationClient } from '../migrate.js';
10
+ export interface PlanManagementDeps {
11
+ stripe: IStripeClient;
12
+ db: MigrationClient;
13
+ }
14
+ export interface PlanChangeResult {
15
+ success: boolean;
16
+ action: 'upgraded' | 'downgraded' | 'scheduled_downgrade';
17
+ previousPlan: TierName;
18
+ newPlan: TierName;
19
+ effectiveAt: 'immediate' | 'end_of_period';
20
+ prorationApplied?: boolean;
21
+ }
22
+ /** Annual pricing: 20% discount applied to monthly × 12 */
23
+ export declare const ANNUAL_PRICE_IDS: Partial<Record<TierName, string>>;
24
+ export declare class PlanManager {
25
+ private deps;
26
+ constructor(deps: PlanManagementDeps);
27
+ /**
28
+ * Change an org's plan. Upgrades are immediate with proration;
29
+ * downgrades take effect at end of billing period.
30
+ */
31
+ changePlan(orgId: string, newTier: TierName, billing?: 'monthly' | 'annual'): Promise<PlanChangeResult>;
32
+ /**
33
+ * Immediate upgrade with proration.
34
+ */
35
+ private handleUpgrade;
36
+ /**
37
+ * Downgrade at end of billing period.
38
+ */
39
+ private handleDowngrade;
40
+ /**
41
+ * Apply a pending downgrade (called from webhook when subscription period ends).
42
+ */
43
+ applyPendingDowngrade(orgId: string): Promise<void>;
44
+ private getOrg;
45
+ }
46
+ //# sourceMappingURL=plan-management.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-management.d.ts","sourceRoot":"","sources":["../../../src/cloud/billing/plan-management.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAElE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,aAAa,CAAC;IACtB,EAAE,EAAE,eAAe,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,UAAU,GAAG,YAAY,GAAG,qBAAqB,CAAC;IAC1D,YAAY,EAAE,QAAQ,CAAC;IACvB,OAAO,EAAE,QAAQ,CAAC;IAClB,WAAW,EAAE,WAAW,GAAG,eAAe,CAAC;IAC3C,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAUD,2DAA2D;AAC3D,eAAO,MAAM,gBAAgB,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAG9D,CAAC;AAEF,qBAAa,WAAW;IACV,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,kBAAkB;IAE5C;;;OAGG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAE,SAAS,GAAG,QAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqBxH;;OAEG;YACW,aAAa;IAmD3B;;OAEG;YACW,eAAe;IAmC7B;;OAEG;IACG,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAqC3C,MAAM;CASrB"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Plan Upgrade/Downgrade (S-6.4)
3
+ *
4
+ * - Upgrade: immediate, with proration
5
+ * - Downgrade: at end of billing period
6
+ * - Enforces tier limits (keys, members, orgs)
7
+ */
8
+ import { TIER_CONFIG } from './stripe-client.js';
9
+ /** Tier ordering for upgrade/downgrade detection */
10
+ const TIER_ORDER = {
11
+ free: 0,
12
+ pro: 1,
13
+ team: 2,
14
+ enterprise: 3,
15
+ };
16
+ /** Annual pricing: 20% discount applied to monthly × 12 */
17
+ export const ANNUAL_PRICE_IDS = {
18
+ pro: 'price_pro_annual',
19
+ team: 'price_team_annual',
20
+ };
21
+ export class PlanManager {
22
+ deps;
23
+ constructor(deps) {
24
+ this.deps = deps;
25
+ }
26
+ /**
27
+ * Change an org's plan. Upgrades are immediate with proration;
28
+ * downgrades take effect at end of billing period.
29
+ */
30
+ async changePlan(orgId, newTier, billing = 'monthly') {
31
+ const org = await this.getOrg(orgId);
32
+ const currentTier = org.plan;
33
+ if (currentTier === newTier) {
34
+ throw new Error(`Org is already on the ${newTier} plan.`);
35
+ }
36
+ if (!org.stripe_customer_id) {
37
+ throw new Error('Org has no Stripe customer. Call createCustomerForOrg first.');
38
+ }
39
+ const isUpgrade = TIER_ORDER[newTier] > TIER_ORDER[currentTier];
40
+ if (isUpgrade) {
41
+ return this.handleUpgrade(org, newTier, billing);
42
+ }
43
+ else {
44
+ return this.handleDowngrade(org, newTier);
45
+ }
46
+ }
47
+ /**
48
+ * Immediate upgrade with proration.
49
+ */
50
+ async handleUpgrade(org, newTier, billing) {
51
+ const previousPlan = org.plan;
52
+ const tierConfig = TIER_CONFIG[newTier];
53
+ // Build subscription items for the new tier
54
+ const priceId = billing === 'annual' && ANNUAL_PRICE_IDS[newTier]
55
+ ? ANNUAL_PRICE_IDS[newTier]
56
+ : tierConfig.price_id;
57
+ const items = [{ price: priceId }];
58
+ if (tierConfig.overage_price_id) {
59
+ items.push({ price: tierConfig.overage_price_id });
60
+ }
61
+ // Atomically update existing subscription (proration) instead of cancel+recreate
62
+ let subscription;
63
+ if (org.stripe_subscription_id) {
64
+ subscription = await this.deps.stripe.updateSubscription(org.stripe_subscription_id, items);
65
+ }
66
+ else {
67
+ subscription = await this.deps.stripe.createSubscription({
68
+ customer: org.stripe_customer_id,
69
+ items,
70
+ });
71
+ }
72
+ // Update org immediately
73
+ await this.deps.db.query(`UPDATE orgs SET plan = $1, stripe_subscription_id = $2, event_quota = $3,
74
+ settings = jsonb_set(
75
+ jsonb_set(COALESCE(settings, '{}'), '{billing_interval}', $5::jsonb),
76
+ '{pending_downgrade}', 'null'::jsonb
77
+ ),
78
+ updated_at = now()
79
+ WHERE id = $4`, [newTier, subscription.id, tierConfig.event_quota, org.id, JSON.stringify(billing)]);
80
+ return {
81
+ success: true,
82
+ action: 'upgraded',
83
+ previousPlan,
84
+ newPlan: newTier,
85
+ effectiveAt: 'immediate',
86
+ prorationApplied: !!org.stripe_subscription_id, // proration if replacing existing sub
87
+ };
88
+ }
89
+ /**
90
+ * Downgrade at end of billing period.
91
+ */
92
+ async handleDowngrade(org, newTier) {
93
+ const previousPlan = org.plan;
94
+ if (newTier === 'free') {
95
+ // Cancel subscription at period end
96
+ if (org.stripe_subscription_id) {
97
+ await this.deps.stripe.cancelSubscription(org.stripe_subscription_id, true);
98
+ }
99
+ }
100
+ else {
101
+ // Downgrading to a lower paid tier — schedule change at period end
102
+ if (org.stripe_subscription_id) {
103
+ await this.deps.stripe.cancelSubscription(org.stripe_subscription_id, true);
104
+ }
105
+ }
106
+ // Record pending downgrade
107
+ await this.deps.db.query(`UPDATE orgs SET settings = jsonb_set(COALESCE(settings, '{}'), '{pending_downgrade}', $1::jsonb),
108
+ updated_at = now()
109
+ WHERE id = $2`, [JSON.stringify(newTier), org.id]);
110
+ return {
111
+ success: true,
112
+ action: 'scheduled_downgrade',
113
+ previousPlan,
114
+ newPlan: newTier,
115
+ effectiveAt: 'end_of_period',
116
+ };
117
+ }
118
+ /**
119
+ * Apply a pending downgrade (called from webhook when subscription period ends).
120
+ */
121
+ async applyPendingDowngrade(orgId) {
122
+ const org = await this.getOrg(orgId);
123
+ const pendingTier = org.settings?.pending_downgrade;
124
+ if (!pendingTier)
125
+ return;
126
+ const tierConfig = TIER_CONFIG[pendingTier];
127
+ if (pendingTier === 'free') {
128
+ await this.deps.db.query(`UPDATE orgs SET plan = 'free', stripe_subscription_id = NULL,
129
+ event_quota = $1, settings = settings - 'pending_downgrade',
130
+ updated_at = now()
131
+ WHERE id = $2`, [tierConfig.event_quota, orgId]);
132
+ }
133
+ else {
134
+ // Downgrade to lower paid tier — create new subscription
135
+ const items = [{ price: tierConfig.price_id }];
136
+ if (tierConfig.overage_price_id) {
137
+ items.push({ price: tierConfig.overage_price_id });
138
+ }
139
+ const subscription = await this.deps.stripe.createSubscription({
140
+ customer: org.stripe_customer_id,
141
+ items,
142
+ });
143
+ await this.deps.db.query(`UPDATE orgs SET plan = $1, stripe_subscription_id = $2,
144
+ event_quota = $3, settings = settings - 'pending_downgrade',
145
+ updated_at = now()
146
+ WHERE id = $4`, [pendingTier, subscription.id, tierConfig.event_quota, orgId]);
147
+ }
148
+ }
149
+ async getOrg(orgId) {
150
+ const result = await this.deps.db.query(`SELECT id, plan, stripe_customer_id, stripe_subscription_id, settings FROM orgs WHERE id = $1`, [orgId]);
151
+ const org = result.rows[0];
152
+ if (!org)
153
+ throw new Error(`Org ${orgId} not found`);
154
+ return org;
155
+ }
156
+ }
157
+ //# sourceMappingURL=plan-management.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-management.js","sourceRoot":"","sources":["../../../src/cloud/billing/plan-management.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Quota Enforcement (S-6.3)
3
+ *
4
+ * Checks usage against tier limits on every ingestion request.
5
+ * - Free tier: hard block at 10K events/month (402)
6
+ * - Pro/Team: allow overage up to cap (default 2x)
7
+ * - 80% soft warning
8
+ * - Grace period for paid tiers
9
+ */
10
+ import type { TierName } from './stripe-client.js';
11
+ import type { RedisUsageStore } from './usage-metering.js';
12
+ import type { MigrationClient } from '../migrate.js';
13
+ export interface QuotaCheckResult {
14
+ allowed: boolean;
15
+ status: 'ok' | 'warning' | 'overage' | 'blocked';
16
+ usage: number;
17
+ quota: number;
18
+ usage_pct: number;
19
+ message?: string;
20
+ upgrade_url?: string;
21
+ }
22
+ export interface QuotaEnforcerDeps {
23
+ db: MigrationClient;
24
+ redis?: RedisUsageStore;
25
+ }
26
+ export interface OrgQuotaInfo {
27
+ plan: TierName;
28
+ event_quota: number;
29
+ overage_cap_multiplier: number;
30
+ }
31
+ export declare class QuotaEnforcer {
32
+ private deps;
33
+ private orgCache;
34
+ private cacheTtlMs;
35
+ constructor(deps: QuotaEnforcerDeps, config?: {
36
+ cacheTtlMs?: number;
37
+ });
38
+ /**
39
+ * Check if an org can ingest events.
40
+ * Designed to be < 5ms via Redis lookup.
41
+ */
42
+ checkQuota(orgId: string): Promise<QuotaCheckResult>;
43
+ /**
44
+ * Check if an org should see a warning notification.
45
+ * Returns the warning level or null.
46
+ */
47
+ getWarningLevel(orgId: string): Promise<'80pct' | '100pct' | null>;
48
+ private getOrgInfo;
49
+ private getCurrentUsage;
50
+ /** Clear cache (for testing) */
51
+ clearCache(): void;
52
+ }
53
+ //# sourceMappingURL=quota-enforcement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota-enforcement.d.ts","sourceRoot":"","sources":["../../../src/cloud/billing/quota-enforcement.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAMrD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,IAAI,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,eAAe,CAAC;IACpB,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAMD,qBAAa,aAAa;IAKtB,OAAO,CAAC,IAAI;IAJd,OAAO,CAAC,QAAQ,CAA8D;IAC9E,OAAO,CAAC,UAAU,CAAS;gBAGjB,IAAI,EAAE,iBAAiB,EAC/B,MAAM,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAKlC;;;OAGG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+D1D;;;OAGG;IACG,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC;YAa1D,UAAU;YAsBV,eAAe;IA4B7B,gCAAgC;IAChC,UAAU,IAAI,IAAI;CAGnB"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Quota Enforcement (S-6.3)
3
+ *
4
+ * Checks usage against tier limits on every ingestion request.
5
+ * - Free tier: hard block at 10K events/month (402)
6
+ * - Pro/Team: allow overage up to cap (default 2x)
7
+ * - 80% soft warning
8
+ * - Grace period for paid tiers
9
+ */
10
+ import { TIER_CONFIG } from './stripe-client.js';
11
+ // ═══════════════════════════════════════════
12
+ // Quota Enforcer
13
+ // ═══════════════════════════════════════════
14
+ export class QuotaEnforcer {
15
+ deps;
16
+ orgCache = new Map();
17
+ cacheTtlMs;
18
+ constructor(deps, config) {
19
+ this.deps = deps;
20
+ this.cacheTtlMs = config?.cacheTtlMs ?? 60_000; // 1 min
21
+ }
22
+ /**
23
+ * Check if an org can ingest events.
24
+ * Designed to be < 5ms via Redis lookup.
25
+ */
26
+ async checkQuota(orgId) {
27
+ const orgInfo = await this.getOrgInfo(orgId);
28
+ const usage = await this.getCurrentUsage(orgId);
29
+ const quota = orgInfo.event_quota;
30
+ const usagePct = quota > 0 ? (usage / quota) * 100 : 0;
31
+ // Under 80% — all good
32
+ if (usagePct < 80) {
33
+ return { allowed: true, status: 'ok', usage, quota, usage_pct: round2(usagePct) };
34
+ }
35
+ // 80-100% — soft warning
36
+ if (usage < quota) {
37
+ return {
38
+ allowed: true,
39
+ status: 'warning',
40
+ usage,
41
+ quota,
42
+ usage_pct: round2(usagePct),
43
+ message: `You've used ${Math.round(usagePct)}% of your monthly event quota.`,
44
+ };
45
+ }
46
+ // At or over quota
47
+ const plan = orgInfo.plan;
48
+ // Free tier: hard block
49
+ if (plan === 'free') {
50
+ return {
51
+ allowed: false,
52
+ status: 'blocked',
53
+ usage,
54
+ quota,
55
+ usage_pct: round2(usagePct),
56
+ message: 'Monthly event quota exceeded. Upgrade to continue ingesting events.',
57
+ upgrade_url: '/billing/upgrade',
58
+ };
59
+ }
60
+ // Paid tiers: check overage cap
61
+ const overageCap = quota * orgInfo.overage_cap_multiplier;
62
+ if (usage >= overageCap) {
63
+ return {
64
+ allowed: false,
65
+ status: 'blocked',
66
+ usage,
67
+ quota,
68
+ usage_pct: round2(usagePct),
69
+ message: `Overage cap reached (${orgInfo.overage_cap_multiplier}x quota). Contact support to increase.`,
70
+ };
71
+ }
72
+ // Paid tier, within overage cap — allow with overage billing
73
+ return {
74
+ allowed: true,
75
+ status: 'overage',
76
+ usage,
77
+ quota,
78
+ usage_pct: round2(usagePct),
79
+ message: `You're now incurring overage charges at ${TIER_CONFIG[plan]?.overage_rate_per_1k_cents ?? 0}¢ per 1K events.`,
80
+ };
81
+ }
82
+ /**
83
+ * Check if an org should see a warning notification.
84
+ * Returns the warning level or null.
85
+ */
86
+ async getWarningLevel(orgId) {
87
+ const orgInfo = await this.getOrgInfo(orgId);
88
+ const usage = await this.getCurrentUsage(orgId);
89
+ const quota = orgInfo.event_quota;
90
+ const pct = quota > 0 ? (usage / quota) * 100 : 0;
91
+ if (pct >= 100)
92
+ return '100pct';
93
+ if (pct >= 80)
94
+ return '80pct';
95
+ return null;
96
+ }
97
+ // ── Internals ──
98
+ async getOrgInfo(orgId) {
99
+ // Check cache
100
+ const cached = this.orgCache.get(orgId);
101
+ if (cached && cached.expires > Date.now()) {
102
+ return cached.info;
103
+ }
104
+ const result = await this.deps.db.query(`SELECT plan, event_quota, overage_cap_multiplier FROM orgs WHERE id = $1`, [orgId]);
105
+ const row = result.rows[0];
106
+ const info = {
107
+ plan: (row?.plan ?? 'free'),
108
+ event_quota: row?.event_quota ?? TIER_CONFIG.free.event_quota,
109
+ overage_cap_multiplier: row?.overage_cap_multiplier ?? 2.0,
110
+ };
111
+ this.orgCache.set(orgId, { info, expires: Date.now() + this.cacheTtlMs });
112
+ return info;
113
+ }
114
+ async getCurrentUsage(orgId) {
115
+ // Try Redis first (fast path, < 1ms)
116
+ if (this.deps.redis) {
117
+ const now = new Date();
118
+ const month = `${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, '0')}`;
119
+ const redisKey = `usage:${orgId}:${month}`;
120
+ const val = await this.deps.redis.get(redisKey);
121
+ if (val !== null) {
122
+ return parseInt(val, 10);
123
+ }
124
+ }
125
+ // Fallback to DB
126
+ const now = new Date();
127
+ const month = `${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, '0')}`;
128
+ const monthStart = `${month}-01T00:00:00Z`;
129
+ const nextMonth = new Date(now.getUTCFullYear(), now.getUTCMonth() + 1, 1);
130
+ const result = await this.deps.db.query(`SELECT COALESCE(SUM(event_count), 0)::int as total
131
+ FROM usage_records
132
+ WHERE org_id = $1 AND hour >= $2 AND hour < $3`, [orgId, monthStart, nextMonth.toISOString()]);
133
+ return result.rows[0]?.total ?? 0;
134
+ }
135
+ /** Clear cache (for testing) */
136
+ clearCache() {
137
+ this.orgCache.clear();
138
+ }
139
+ }
140
+ function round2(n) {
141
+ return Math.round(n * 100) / 100;
142
+ }
143
+ //# sourceMappingURL=quota-enforcement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota-enforcement.js","sourceRoot":"","sources":["../../../src/cloud/billing/quota-enforcement.ts"],"names":[],"mappings":"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"}