@qwickapps/server 1.4.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/CHANGELOG.md +507 -0
  2. package/README.md +9 -0
  3. package/dist/index.d.ts +2 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +12 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/plugins/bans/bans-plugin.d.ts.map +1 -1
  8. package/dist/plugins/bans/bans-plugin.js +12 -3
  9. package/dist/plugins/bans/bans-plugin.js.map +1 -1
  10. package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts +11 -0
  11. package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts.map +1 -0
  12. package/dist/plugins/devices/__tests__/devices-plugin.test.js +410 -0
  13. package/dist/plugins/devices/__tests__/devices-plugin.test.js.map +1 -0
  14. package/dist/plugins/devices/__tests__/token-utils.test.d.ts +7 -0
  15. package/dist/plugins/devices/__tests__/token-utils.test.d.ts.map +1 -0
  16. package/dist/plugins/devices/__tests__/token-utils.test.js +197 -0
  17. package/dist/plugins/devices/__tests__/token-utils.test.js.map +1 -0
  18. package/dist/plugins/devices/adapters/compute-adapter.d.ts +36 -0
  19. package/dist/plugins/devices/adapters/compute-adapter.d.ts.map +1 -0
  20. package/dist/plugins/devices/adapters/compute-adapter.js +100 -0
  21. package/dist/plugins/devices/adapters/compute-adapter.js.map +1 -0
  22. package/dist/plugins/devices/adapters/index.d.ts +12 -0
  23. package/dist/plugins/devices/adapters/index.d.ts.map +1 -0
  24. package/dist/plugins/devices/adapters/index.js +10 -0
  25. package/dist/plugins/devices/adapters/index.js.map +1 -0
  26. package/dist/plugins/devices/adapters/mobile-adapter.d.ts +41 -0
  27. package/dist/plugins/devices/adapters/mobile-adapter.d.ts.map +1 -0
  28. package/dist/plugins/devices/adapters/mobile-adapter.js +131 -0
  29. package/dist/plugins/devices/adapters/mobile-adapter.js.map +1 -0
  30. package/dist/plugins/devices/devices-plugin.d.ts +70 -0
  31. package/dist/plugins/devices/devices-plugin.d.ts.map +1 -0
  32. package/dist/plugins/devices/devices-plugin.js +453 -0
  33. package/dist/plugins/devices/devices-plugin.js.map +1 -0
  34. package/dist/plugins/devices/index.d.ts +18 -0
  35. package/dist/plugins/devices/index.d.ts.map +1 -0
  36. package/dist/plugins/devices/index.js +18 -0
  37. package/dist/plugins/devices/index.js.map +1 -0
  38. package/dist/plugins/devices/stores/index.d.ts +9 -0
  39. package/dist/plugins/devices/stores/index.d.ts.map +1 -0
  40. package/dist/plugins/devices/stores/index.js +9 -0
  41. package/dist/plugins/devices/stores/index.js.map +1 -0
  42. package/dist/plugins/devices/stores/postgres-store.d.ts +26 -0
  43. package/dist/plugins/devices/stores/postgres-store.d.ts.map +1 -0
  44. package/dist/plugins/devices/stores/postgres-store.js +199 -0
  45. package/dist/plugins/devices/stores/postgres-store.js.map +1 -0
  46. package/dist/plugins/devices/token-utils.d.ts +100 -0
  47. package/dist/plugins/devices/token-utils.d.ts.map +1 -0
  48. package/dist/plugins/devices/token-utils.js +162 -0
  49. package/dist/plugins/devices/token-utils.js.map +1 -0
  50. package/dist/plugins/devices/types.d.ts +307 -0
  51. package/dist/plugins/devices/types.d.ts.map +1 -0
  52. package/dist/plugins/devices/types.js +10 -0
  53. package/dist/plugins/devices/types.js.map +1 -0
  54. package/dist/plugins/index.d.ts +14 -2
  55. package/dist/plugins/index.d.ts.map +1 -1
  56. package/dist/plugins/index.js +13 -1
  57. package/dist/plugins/index.js.map +1 -1
  58. package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts +5 -0
  59. package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts.map +1 -0
  60. package/dist/plugins/notifications/__tests__/notifications-manager.test.js +470 -0
  61. package/dist/plugins/notifications/__tests__/notifications-manager.test.js.map +1 -0
  62. package/dist/plugins/notifications/index.d.ts +71 -0
  63. package/dist/plugins/notifications/index.d.ts.map +1 -0
  64. package/dist/plugins/notifications/index.js +72 -0
  65. package/dist/plugins/notifications/index.js.map +1 -0
  66. package/dist/plugins/notifications/notifications-manager.d.ts +182 -0
  67. package/dist/plugins/notifications/notifications-manager.d.ts.map +1 -0
  68. package/dist/plugins/notifications/notifications-manager.js +610 -0
  69. package/dist/plugins/notifications/notifications-manager.js.map +1 -0
  70. package/dist/plugins/notifications/notifications-plugin.d.ts +83 -0
  71. package/dist/plugins/notifications/notifications-plugin.d.ts.map +1 -0
  72. package/dist/plugins/notifications/notifications-plugin.js +337 -0
  73. package/dist/plugins/notifications/notifications-plugin.js.map +1 -0
  74. package/dist/plugins/notifications/types.d.ts +164 -0
  75. package/dist/plugins/notifications/types.d.ts.map +1 -0
  76. package/dist/plugins/notifications/types.js +9 -0
  77. package/dist/plugins/notifications/types.js.map +1 -0
  78. package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts +12 -0
  79. package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts.map +1 -0
  80. package/dist/plugins/parental/__tests__/parental-plugin.test.js +349 -0
  81. package/dist/plugins/parental/__tests__/parental-plugin.test.js.map +1 -0
  82. package/dist/plugins/parental/adapters/index.d.ts +8 -0
  83. package/dist/plugins/parental/adapters/index.d.ts.map +1 -0
  84. package/dist/plugins/parental/adapters/index.js +7 -0
  85. package/dist/plugins/parental/adapters/index.js.map +1 -0
  86. package/dist/plugins/parental/adapters/kids-adapter.d.ts +24 -0
  87. package/dist/plugins/parental/adapters/kids-adapter.d.ts.map +1 -0
  88. package/dist/plugins/parental/adapters/kids-adapter.js +174 -0
  89. package/dist/plugins/parental/adapters/kids-adapter.js.map +1 -0
  90. package/dist/plugins/parental/index.d.ts +14 -0
  91. package/dist/plugins/parental/index.d.ts.map +1 -0
  92. package/dist/plugins/parental/index.js +15 -0
  93. package/dist/plugins/parental/index.js.map +1 -0
  94. package/dist/plugins/parental/parental-plugin.d.ts +88 -0
  95. package/dist/plugins/parental/parental-plugin.d.ts.map +1 -0
  96. package/dist/plugins/parental/parental-plugin.js +666 -0
  97. package/dist/plugins/parental/parental-plugin.js.map +1 -0
  98. package/dist/plugins/parental/stores/index.d.ts +7 -0
  99. package/dist/plugins/parental/stores/index.d.ts.map +1 -0
  100. package/dist/plugins/parental/stores/index.js +7 -0
  101. package/dist/plugins/parental/stores/index.js.map +1 -0
  102. package/dist/plugins/parental/stores/postgres-store.d.ts +10 -0
  103. package/dist/plugins/parental/stores/postgres-store.d.ts.map +1 -0
  104. package/dist/plugins/parental/stores/postgres-store.js +209 -0
  105. package/dist/plugins/parental/stores/postgres-store.js.map +1 -0
  106. package/dist/plugins/parental/types.d.ts +154 -0
  107. package/dist/plugins/parental/types.d.ts.map +1 -0
  108. package/dist/plugins/parental/types.js +10 -0
  109. package/dist/plugins/parental/types.js.map +1 -0
  110. package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts +11 -0
  111. package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts.map +1 -0
  112. package/dist/plugins/profiles/__tests__/profiles-plugin.test.js +243 -0
  113. package/dist/plugins/profiles/__tests__/profiles-plugin.test.js.map +1 -0
  114. package/dist/plugins/profiles/index.d.ts +12 -0
  115. package/dist/plugins/profiles/index.d.ts.map +1 -0
  116. package/dist/plugins/profiles/index.js +13 -0
  117. package/dist/plugins/profiles/index.js.map +1 -0
  118. package/dist/plugins/profiles/profiles-plugin.d.ts +71 -0
  119. package/dist/plugins/profiles/profiles-plugin.d.ts.map +1 -0
  120. package/dist/plugins/profiles/profiles-plugin.js +481 -0
  121. package/dist/plugins/profiles/profiles-plugin.js.map +1 -0
  122. package/dist/plugins/profiles/stores/index.d.ts +9 -0
  123. package/dist/plugins/profiles/stores/index.d.ts.map +1 -0
  124. package/dist/plugins/profiles/stores/index.js +9 -0
  125. package/dist/plugins/profiles/stores/index.js.map +1 -0
  126. package/dist/plugins/profiles/stores/postgres-store.d.ts +18 -0
  127. package/dist/plugins/profiles/stores/postgres-store.d.ts.map +1 -0
  128. package/dist/plugins/profiles/stores/postgres-store.js +310 -0
  129. package/dist/plugins/profiles/stores/postgres-store.js.map +1 -0
  130. package/dist/plugins/profiles/types.d.ts +289 -0
  131. package/dist/plugins/profiles/types.d.ts.map +1 -0
  132. package/dist/plugins/profiles/types.js +10 -0
  133. package/dist/plugins/profiles/types.js.map +1 -0
  134. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts +11 -0
  135. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts.map +1 -0
  136. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js +305 -0
  137. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js.map +1 -0
  138. package/dist/plugins/subscriptions/index.d.ts +12 -0
  139. package/dist/plugins/subscriptions/index.d.ts.map +1 -0
  140. package/dist/plugins/subscriptions/index.js +13 -0
  141. package/dist/plugins/subscriptions/index.js.map +1 -0
  142. package/dist/plugins/subscriptions/stores/index.d.ts +9 -0
  143. package/dist/plugins/subscriptions/stores/index.d.ts.map +1 -0
  144. package/dist/plugins/subscriptions/stores/index.js +9 -0
  145. package/dist/plugins/subscriptions/stores/index.js.map +1 -0
  146. package/dist/plugins/subscriptions/stores/postgres-store.d.ts +14 -0
  147. package/dist/plugins/subscriptions/stores/postgres-store.d.ts.map +1 -0
  148. package/dist/plugins/subscriptions/stores/postgres-store.js +359 -0
  149. package/dist/plugins/subscriptions/stores/postgres-store.js.map +1 -0
  150. package/dist/plugins/subscriptions/subscriptions-plugin.d.ts +82 -0
  151. package/dist/plugins/subscriptions/subscriptions-plugin.d.ts.map +1 -0
  152. package/dist/plugins/subscriptions/subscriptions-plugin.js +449 -0
  153. package/dist/plugins/subscriptions/subscriptions-plugin.js.map +1 -0
  154. package/dist/plugins/subscriptions/types.d.ts +308 -0
  155. package/dist/plugins/subscriptions/types.d.ts.map +1 -0
  156. package/dist/plugins/subscriptions/types.js +10 -0
  157. package/dist/plugins/subscriptions/types.js.map +1 -0
  158. package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts +11 -0
  159. package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts.map +1 -0
  160. package/dist/plugins/usage/__tests__/usage-plugin.test.js +218 -0
  161. package/dist/plugins/usage/__tests__/usage-plugin.test.js.map +1 -0
  162. package/dist/plugins/usage/index.d.ts +12 -0
  163. package/dist/plugins/usage/index.d.ts.map +1 -0
  164. package/dist/plugins/usage/index.js +13 -0
  165. package/dist/plugins/usage/index.js.map +1 -0
  166. package/dist/plugins/usage/stores/index.d.ts +9 -0
  167. package/dist/plugins/usage/stores/index.d.ts.map +1 -0
  168. package/dist/plugins/usage/stores/index.js +9 -0
  169. package/dist/plugins/usage/stores/index.js.map +1 -0
  170. package/dist/plugins/usage/stores/postgres-store.d.ts +14 -0
  171. package/dist/plugins/usage/stores/postgres-store.d.ts.map +1 -0
  172. package/dist/plugins/usage/stores/postgres-store.js +146 -0
  173. package/dist/plugins/usage/stores/postgres-store.js.map +1 -0
  174. package/dist/plugins/usage/types.d.ts +195 -0
  175. package/dist/plugins/usage/types.d.ts.map +1 -0
  176. package/dist/plugins/usage/types.js +10 -0
  177. package/dist/plugins/usage/types.js.map +1 -0
  178. package/dist/plugins/usage/usage-plugin.d.ts +51 -0
  179. package/dist/plugins/usage/usage-plugin.d.ts.map +1 -0
  180. package/dist/plugins/usage/usage-plugin.js +412 -0
  181. package/dist/plugins/usage/usage-plugin.js.map +1 -0
  182. package/dist/plugins/users/__tests__/postgres-store.test.d.ts +10 -0
  183. package/dist/plugins/users/__tests__/postgres-store.test.d.ts.map +1 -0
  184. package/dist/plugins/users/__tests__/postgres-store.test.js +229 -0
  185. package/dist/plugins/users/__tests__/postgres-store.test.js.map +1 -0
  186. package/dist/plugins/users/__tests__/users-plugin.test.js +3 -0
  187. package/dist/plugins/users/__tests__/users-plugin.test.js.map +1 -1
  188. package/dist/plugins/users/index.d.ts +2 -2
  189. package/dist/plugins/users/index.d.ts.map +1 -1
  190. package/dist/plugins/users/index.js +1 -1
  191. package/dist/plugins/users/index.js.map +1 -1
  192. package/dist/plugins/users/stores/postgres-store.d.ts.map +1 -1
  193. package/dist/plugins/users/stores/postgres-store.js +76 -0
  194. package/dist/plugins/users/stores/postgres-store.js.map +1 -1
  195. package/dist/plugins/users/types.d.ts +74 -6
  196. package/dist/plugins/users/types.d.ts.map +1 -1
  197. package/dist/plugins/users/users-plugin.d.ts +15 -1
  198. package/dist/plugins/users/users-plugin.d.ts.map +1 -1
  199. package/dist/plugins/users/users-plugin.js +29 -0
  200. package/dist/plugins/users/users-plugin.js.map +1 -1
  201. package/dist-ui/assets/index-CynOqPkb.js +469 -0
  202. package/dist-ui/assets/index-CynOqPkb.js.map +1 -0
  203. package/dist-ui/index.html +1 -1
  204. package/dist-ui-lib/api/controlPanelApi.d.ts +46 -0
  205. package/dist-ui-lib/components/StatCard.d.ts +16 -0
  206. package/dist-ui-lib/dashboard/widgets/NotificationsStatsWidget.d.ts +12 -0
  207. package/dist-ui-lib/dashboard/widgets/index.d.ts +1 -0
  208. package/dist-ui-lib/index.js +1822 -1611
  209. package/dist-ui-lib/index.js.map +1 -1
  210. package/dist-ui-lib/pages/NotificationsPage.d.ts +9 -0
  211. package/dist-ui-lib/utils/formatters.d.ts +19 -0
  212. package/package.json +3 -2
  213. package/src/index.ts +178 -0
  214. package/src/plugins/bans/bans-plugin.ts +15 -3
  215. package/src/plugins/devices/__tests__/devices-plugin.test.ts +551 -0
  216. package/src/plugins/devices/__tests__/token-utils.test.ts +264 -0
  217. package/src/plugins/devices/adapters/compute-adapter.ts +139 -0
  218. package/src/plugins/devices/adapters/index.ts +13 -0
  219. package/src/plugins/devices/adapters/mobile-adapter.ts +179 -0
  220. package/src/plugins/devices/devices-plugin.ts +538 -0
  221. package/src/plugins/devices/index.ts +69 -0
  222. package/src/plugins/devices/stores/index.ts +9 -0
  223. package/src/plugins/devices/stores/postgres-store.ts +304 -0
  224. package/src/plugins/devices/token-utils.ts +213 -0
  225. package/src/plugins/devices/types.ts +351 -0
  226. package/src/plugins/index.ts +218 -0
  227. package/src/plugins/notifications/__tests__/notifications-manager.test.ts +637 -0
  228. package/src/plugins/notifications/index.ts +91 -0
  229. package/src/plugins/notifications/notifications-manager.ts +773 -0
  230. package/src/plugins/notifications/notifications-plugin.ts +398 -0
  231. package/src/plugins/notifications/types.ts +207 -0
  232. package/src/plugins/parental/__tests__/parental-plugin.test.ts +465 -0
  233. package/src/plugins/parental/adapters/index.ts +8 -0
  234. package/src/plugins/parental/adapters/kids-adapter.ts +206 -0
  235. package/src/plugins/parental/index.ts +55 -0
  236. package/src/plugins/parental/parental-plugin.ts +759 -0
  237. package/src/plugins/parental/stores/index.ts +7 -0
  238. package/src/plugins/parental/stores/postgres-store.ts +304 -0
  239. package/src/plugins/parental/types.ts +180 -0
  240. package/src/plugins/profiles/__tests__/profiles-plugin.test.ts +321 -0
  241. package/src/plugins/profiles/index.ts +49 -0
  242. package/src/plugins/profiles/profiles-plugin.ts +546 -0
  243. package/src/plugins/profiles/stores/index.ts +9 -0
  244. package/src/plugins/profiles/stores/postgres-store.ts +439 -0
  245. package/src/plugins/profiles/types.ts +338 -0
  246. package/src/plugins/subscriptions/__tests__/subscriptions-plugin.test.ts +404 -0
  247. package/src/plugins/subscriptions/index.ts +51 -0
  248. package/src/plugins/subscriptions/stores/index.ts +9 -0
  249. package/src/plugins/subscriptions/stores/postgres-store.ts +482 -0
  250. package/src/plugins/subscriptions/subscriptions-plugin.ts +530 -0
  251. package/src/plugins/subscriptions/types.ts +355 -0
  252. package/src/plugins/usage/__tests__/usage-plugin.test.ts +288 -0
  253. package/src/plugins/usage/index.ts +39 -0
  254. package/src/plugins/usage/stores/index.ts +9 -0
  255. package/src/plugins/usage/stores/postgres-store.ts +213 -0
  256. package/src/plugins/usage/types.ts +222 -0
  257. package/src/plugins/usage/usage-plugin.ts +484 -0
  258. package/src/plugins/users/__tests__/postgres-store.test.ts +326 -0
  259. package/src/plugins/users/__tests__/users-plugin.test.ts +3 -0
  260. package/src/plugins/users/index.ts +6 -0
  261. package/src/plugins/users/stores/postgres-store.ts +104 -0
  262. package/src/plugins/users/types.ts +82 -6
  263. package/src/plugins/users/users-plugin.ts +37 -0
  264. package/ui/src/App.tsx +5 -1
  265. package/ui/src/api/controlPanelApi.ts +103 -6
  266. package/ui/src/components/StatCard.tsx +58 -0
  267. package/ui/src/dashboard/builtInWidgets.tsx +3 -1
  268. package/ui/src/dashboard/widgets/NotificationsStatsWidget.tsx +167 -0
  269. package/ui/src/dashboard/widgets/index.ts +1 -0
  270. package/ui/src/pages/NotificationsPage.tsx +417 -0
  271. package/ui/src/utils/formatters.ts +33 -0
  272. package/dist-ui/assets/index-D7DoZ9rL.js +0 -478
  273. package/dist-ui/assets/index-D7DoZ9rL.js.map +0 -1
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Subscriptions Plugin Types
3
+ *
4
+ * Type definitions for subscription tier management and feature entitlements.
5
+ * Supports Stripe integration for paid subscriptions.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ /**
11
+ * Subscription status
12
+ */
13
+ export type SubscriptionStatus = 'active' | 'canceled' | 'past_due' | 'trialing' | 'inactive';
14
+
15
+ /**
16
+ * Subscription tier definition (admin-managed)
17
+ */
18
+ export interface SubscriptionTier {
19
+ /** Primary key - UUID */
20
+ id: string;
21
+ /** URL-safe identifier (e.g., 'free', 'premium', 'family') */
22
+ slug: string;
23
+ /** Display name */
24
+ name: string;
25
+ /** Description */
26
+ description?: string;
27
+ /** Monthly price in cents (null = free) */
28
+ price_monthly_cents?: number;
29
+ /** Yearly price in cents (null = free or monthly only) */
30
+ price_yearly_cents?: number;
31
+ /** Stripe price ID for monthly billing */
32
+ stripe_price_id_monthly?: string;
33
+ /** Stripe price ID for yearly billing */
34
+ stripe_price_id_yearly?: string;
35
+ /** Whether this tier is active (available for new subscriptions) */
36
+ is_active: boolean;
37
+ /** Sort order for display */
38
+ sort_order: number;
39
+ /** Additional metadata */
40
+ metadata: Record<string, unknown>;
41
+ /** When the tier was created */
42
+ created_at: Date;
43
+ /** When the tier was last updated */
44
+ updated_at: Date;
45
+ }
46
+
47
+ /**
48
+ * Feature entitlement per tier
49
+ */
50
+ export interface SubscriptionEntitlement {
51
+ /** Primary key - UUID */
52
+ id: string;
53
+ /** Tier this entitlement belongs to */
54
+ tier_id: string;
55
+ /** Feature code (e.g., 'ai_messages_daily', 'vision_calls_daily') */
56
+ feature_code: string;
57
+ /** Limit value (-1 = unlimited, null = disabled, positive = limit) */
58
+ limit_value?: number;
59
+ /** Additional metadata */
60
+ metadata: Record<string, unknown>;
61
+ }
62
+
63
+ /**
64
+ * User subscription record
65
+ */
66
+ export interface UserSubscription {
67
+ /** Primary key - UUID */
68
+ id: string;
69
+ /** User ID */
70
+ user_id: string;
71
+ /** Tier ID */
72
+ tier_id: string;
73
+ /** Stripe customer ID */
74
+ stripe_customer_id?: string;
75
+ /** Stripe subscription ID */
76
+ stripe_subscription_id?: string;
77
+ /** Subscription status */
78
+ status: SubscriptionStatus;
79
+ /** Current billing period start */
80
+ current_period_start?: Date;
81
+ /** Current billing period end */
82
+ current_period_end?: Date;
83
+ /** Whether to cancel at period end */
84
+ cancel_at_period_end: boolean;
85
+ /** Additional metadata */
86
+ metadata: Record<string, unknown>;
87
+ /** When the subscription was created */
88
+ created_at: Date;
89
+ /** When the subscription was last updated */
90
+ updated_at: Date;
91
+ }
92
+
93
+ /**
94
+ * User subscription with tier details
95
+ */
96
+ export interface UserSubscriptionWithTier extends UserSubscription {
97
+ tier: SubscriptionTier;
98
+ }
99
+
100
+ /**
101
+ * Feature limit check result
102
+ */
103
+ export interface FeatureLimitResult {
104
+ /** Whether the feature is available */
105
+ available: boolean;
106
+ /** Limit value (-1 = unlimited) */
107
+ limit?: number;
108
+ /** Current usage (if tracked) */
109
+ current?: number;
110
+ /** Remaining quota (if tracked) */
111
+ remaining?: number;
112
+ }
113
+
114
+ // ═══════════════════════════════════════════════════════════════════════════
115
+ // Input Types
116
+ // ═══════════════════════════════════════════════════════════════════════════
117
+
118
+ /**
119
+ * Tier creation payload
120
+ */
121
+ export interface CreateTierInput {
122
+ slug: string;
123
+ name: string;
124
+ description?: string;
125
+ price_monthly_cents?: number;
126
+ price_yearly_cents?: number;
127
+ stripe_price_id_monthly?: string;
128
+ stripe_price_id_yearly?: string;
129
+ is_active?: boolean;
130
+ sort_order?: number;
131
+ metadata?: Record<string, unknown>;
132
+ }
133
+
134
+ /**
135
+ * Tier update payload
136
+ */
137
+ export interface UpdateTierInput {
138
+ name?: string;
139
+ description?: string;
140
+ price_monthly_cents?: number;
141
+ price_yearly_cents?: number;
142
+ stripe_price_id_monthly?: string;
143
+ stripe_price_id_yearly?: string;
144
+ is_active?: boolean;
145
+ sort_order?: number;
146
+ metadata?: Record<string, unknown>;
147
+ }
148
+
149
+ /**
150
+ * Entitlement creation payload
151
+ */
152
+ export interface CreateEntitlementInput {
153
+ tier_id: string;
154
+ feature_code: string;
155
+ limit_value?: number;
156
+ metadata?: Record<string, unknown>;
157
+ }
158
+
159
+ /**
160
+ * User subscription creation payload
161
+ */
162
+ export interface CreateUserSubscriptionInput {
163
+ user_id: string;
164
+ tier_id: string;
165
+ stripe_customer_id?: string;
166
+ stripe_subscription_id?: string;
167
+ status?: SubscriptionStatus;
168
+ current_period_start?: Date;
169
+ current_period_end?: Date;
170
+ metadata?: Record<string, unknown>;
171
+ }
172
+
173
+ /**
174
+ * User subscription update payload
175
+ */
176
+ export interface UpdateUserSubscriptionInput {
177
+ tier_id?: string;
178
+ stripe_customer_id?: string;
179
+ stripe_subscription_id?: string;
180
+ status?: SubscriptionStatus;
181
+ current_period_start?: Date;
182
+ current_period_end?: Date;
183
+ cancel_at_period_end?: boolean;
184
+ metadata?: Record<string, unknown>;
185
+ }
186
+
187
+ // ═══════════════════════════════════════════════════════════════════════════
188
+ // Store Interface
189
+ // ═══════════════════════════════════════════════════════════════════════════
190
+
191
+ /**
192
+ * Subscriptions store interface
193
+ */
194
+ export interface SubscriptionsStore {
195
+ /** Store name (e.g., 'postgres') */
196
+ name: string;
197
+
198
+ /**
199
+ * Initialize the store (create tables, etc.)
200
+ */
201
+ initialize(): Promise<void>;
202
+
203
+ // Tiers
204
+ /**
205
+ * Create a subscription tier
206
+ */
207
+ createTier(input: CreateTierInput): Promise<SubscriptionTier>;
208
+
209
+ /**
210
+ * Get tier by ID
211
+ */
212
+ getTierById(id: string): Promise<SubscriptionTier | null>;
213
+
214
+ /**
215
+ * Get tier by slug
216
+ */
217
+ getTierBySlug(slug: string): Promise<SubscriptionTier | null>;
218
+
219
+ /**
220
+ * List all tiers
221
+ */
222
+ listTiers(activeOnly?: boolean): Promise<SubscriptionTier[]>;
223
+
224
+ /**
225
+ * Update a tier
226
+ */
227
+ updateTier(id: string, input: UpdateTierInput): Promise<SubscriptionTier | null>;
228
+
229
+ /**
230
+ * Delete a tier (soft delete)
231
+ */
232
+ deleteTier(id: string): Promise<boolean>;
233
+
234
+ // Entitlements
235
+ /**
236
+ * Create an entitlement
237
+ */
238
+ createEntitlement(input: CreateEntitlementInput): Promise<SubscriptionEntitlement>;
239
+
240
+ /**
241
+ * Get entitlements for a tier
242
+ */
243
+ getEntitlementsByTier(tierId: string): Promise<SubscriptionEntitlement[]>;
244
+
245
+ /**
246
+ * Update an entitlement
247
+ */
248
+ updateEntitlement(id: string, limitValue: number | null): Promise<SubscriptionEntitlement | null>;
249
+
250
+ /**
251
+ * Delete an entitlement
252
+ */
253
+ deleteEntitlement(id: string): Promise<boolean>;
254
+
255
+ /**
256
+ * Set multiple entitlements for a tier (upsert)
257
+ */
258
+ setTierEntitlements(tierId: string, entitlements: Array<{ feature_code: string; limit_value?: number }>): Promise<void>;
259
+
260
+ // User Subscriptions
261
+ /**
262
+ * Create a user subscription
263
+ */
264
+ createUserSubscription(input: CreateUserSubscriptionInput): Promise<UserSubscription>;
265
+
266
+ /**
267
+ * Get user subscription by ID
268
+ */
269
+ getUserSubscriptionById(id: string): Promise<UserSubscription | null>;
270
+
271
+ /**
272
+ * Get active subscription for a user
273
+ */
274
+ getActiveSubscription(userId: string): Promise<UserSubscriptionWithTier | null>;
275
+
276
+ /**
277
+ * Get subscription by Stripe subscription ID
278
+ */
279
+ getByStripeSubscriptionId(stripeSubId: string): Promise<UserSubscription | null>;
280
+
281
+ /**
282
+ * Update user subscription
283
+ */
284
+ updateUserSubscription(id: string, input: UpdateUserSubscriptionInput): Promise<UserSubscription | null>;
285
+
286
+ /**
287
+ * Cancel subscription (sets cancel_at_period_end)
288
+ */
289
+ cancelSubscription(id: string): Promise<boolean>;
290
+
291
+ /**
292
+ * Get feature limit for a user
293
+ */
294
+ getFeatureLimit(userId: string, featureCode: string): Promise<number | null>;
295
+
296
+ /**
297
+ * Check if user has a feature
298
+ */
299
+ hasFeature(userId: string, featureCode: string): Promise<boolean>;
300
+
301
+ /**
302
+ * Shutdown the store
303
+ */
304
+ shutdown(): Promise<void>;
305
+ }
306
+
307
+ // ═══════════════════════════════════════════════════════════════════════════
308
+ // Configuration Types
309
+ // ═══════════════════════════════════════════════════════════════════════════
310
+
311
+ /**
312
+ * PostgreSQL subscriptions store configuration
313
+ */
314
+ export interface PostgresSubscriptionsStoreConfig {
315
+ /** PostgreSQL pool instance or a function that returns one */
316
+ pool: unknown | (() => unknown);
317
+ /** Tiers table name (default: 'subscription_tiers') */
318
+ tiersTable?: string;
319
+ /** Entitlements table name (default: 'subscription_entitlements') */
320
+ entitlementsTable?: string;
321
+ /** User subscriptions table name (default: 'user_subscriptions') */
322
+ userSubscriptionsTable?: string;
323
+ /** Schema name (default: 'public') */
324
+ schema?: string;
325
+ /** Auto-create tables on init (default: true) */
326
+ autoCreateTables?: boolean;
327
+ }
328
+
329
+ /**
330
+ * API configuration
331
+ */
332
+ export interface SubscriptionsApiConfig {
333
+ /** API route prefix (default: '/subscriptions') */
334
+ prefix?: string;
335
+ /** Enable tier management endpoints */
336
+ tierManagement?: boolean;
337
+ /** Enable user subscription endpoints */
338
+ userSubscriptions?: boolean;
339
+ }
340
+
341
+ /**
342
+ * Subscriptions plugin configuration
343
+ */
344
+ export interface SubscriptionsPluginConfig {
345
+ /** Subscriptions storage backend */
346
+ store: SubscriptionsStore;
347
+ /** Default tier slug for new users (default: 'free') */
348
+ defaultTierSlug?: string;
349
+ /** Whether to auto-create default subscription for new users */
350
+ autoCreateDefaultSubscription?: boolean;
351
+ /** API configuration */
352
+ api?: SubscriptionsApiConfig;
353
+ /** Enable debug logging */
354
+ debug?: boolean;
355
+ }
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Usage Plugin Tests
3
+ *
4
+ * Unit tests for the usage tracking plugin including:
5
+ * - Plugin lifecycle
6
+ * - Daily/monthly usage tracking
7
+ * - Increment and check limits
8
+ * - Cleanup operations
9
+ */
10
+
11
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
12
+ import {
13
+ createUsagePlugin,
14
+ getUsageStore,
15
+ getDailyUsage,
16
+ incrementUsage,
17
+ checkUsageLimit,
18
+ getDailyUsageSummary,
19
+ resetUsage,
20
+ } from '../usage-plugin.js';
21
+ import type { UsageStore, DailyUsage, MonthlyUsage } from '../types.js';
22
+ import type { PluginRegistry } from '../../../core/plugin-registry.js';
23
+
24
+ // Mock the subscriptions plugin (usage plugin depends on it for limits)
25
+ vi.mock('../../subscriptions/subscriptions-plugin.js', () => ({
26
+ getFeatureLimit: vi.fn().mockResolvedValue(-1), // unlimited by default
27
+ hasFeature: vi.fn().mockResolvedValue(true),
28
+ }));
29
+
30
+ import { getFeatureLimit } from '../../subscriptions/subscriptions-plugin.js';
31
+
32
+ describe('Usage Plugin', () => {
33
+ // Mock usage data
34
+ const mockDailyUsage: DailyUsage = {
35
+ id: 'usage-id-123',
36
+ user_id: 'user-123',
37
+ feature_code: 'ai_messages',
38
+ date: new Date().toISOString().split('T')[0],
39
+ count: 10,
40
+ metadata: {},
41
+ created_at: new Date(),
42
+ updated_at: new Date(),
43
+ };
44
+
45
+ const mockMonthlyUsage: MonthlyUsage = {
46
+ id: 'monthly-id-123',
47
+ user_id: 'user-123',
48
+ feature_code: 'ai_messages',
49
+ year_month: new Date().toISOString().substring(0, 7),
50
+ count: 300,
51
+ metadata: {},
52
+ created_at: new Date(),
53
+ updated_at: new Date(),
54
+ };
55
+
56
+ // Mock store factory
57
+ const createMockStore = (): UsageStore => ({
58
+ name: 'mock',
59
+ initialize: vi.fn().mockResolvedValue(undefined),
60
+ getDailyUsage: vi.fn().mockResolvedValue(mockDailyUsage),
61
+ getMonthlyUsage: vi.fn().mockResolvedValue(mockMonthlyUsage),
62
+ incrementDaily: vi.fn().mockResolvedValue(11),
63
+ getAllDailyUsage: vi.fn().mockResolvedValue([mockDailyUsage]),
64
+ getAllMonthlyUsage: vi.fn().mockResolvedValue([mockMonthlyUsage]),
65
+ resetDailyUsage: vi.fn().mockResolvedValue(undefined),
66
+ cleanupOldDaily: vi.fn().mockResolvedValue(100),
67
+ cleanupOldMonthly: vi.fn().mockResolvedValue(12),
68
+ shutdown: vi.fn().mockResolvedValue(undefined),
69
+ });
70
+
71
+ // Mock registry factory
72
+ const createMockRegistry = (): PluginRegistry =>
73
+ ({
74
+ hasPlugin: vi.fn().mockReturnValue(false),
75
+ getPlugin: vi.fn().mockReturnValue(null),
76
+ listPlugins: vi.fn().mockReturnValue([]),
77
+ addRoute: vi.fn(),
78
+ addMenuItem: vi.fn(),
79
+ addPage: vi.fn(),
80
+ addWidget: vi.fn(),
81
+ getRoutes: vi.fn().mockReturnValue([]),
82
+ getMenuItems: vi.fn().mockReturnValue([]),
83
+ getPages: vi.fn().mockReturnValue([]),
84
+ getWidgets: vi.fn().mockReturnValue([]),
85
+ getConfig: vi.fn().mockReturnValue({}),
86
+ setConfig: vi.fn().mockResolvedValue(undefined),
87
+ subscribe: vi.fn().mockReturnValue(() => {}),
88
+ emit: vi.fn(),
89
+ registerHealthCheck: vi.fn(),
90
+ getApp: vi.fn().mockReturnValue({} as any),
91
+ getRouter: vi.fn().mockReturnValue({} as any),
92
+ getLogger: vi.fn().mockReturnValue({
93
+ debug: vi.fn(),
94
+ info: vi.fn(),
95
+ warn: vi.fn(),
96
+ error: vi.fn(),
97
+ }),
98
+ }) as unknown as PluginRegistry;
99
+
100
+ let mockStore: UsageStore;
101
+ let mockRegistry: PluginRegistry;
102
+
103
+ beforeEach(() => {
104
+ vi.clearAllMocks();
105
+ mockStore = createMockStore();
106
+ mockRegistry = createMockRegistry();
107
+ });
108
+
109
+ afterEach(async () => {
110
+ // Clean up
111
+ const store = getUsageStore();
112
+ if (store) {
113
+ const plugin = createUsagePlugin({ store: mockStore });
114
+ await plugin.onStop?.();
115
+ }
116
+ });
117
+
118
+ describe('createUsagePlugin', () => {
119
+ it('should create a plugin with correct id', () => {
120
+ const plugin = createUsagePlugin({ store: mockStore });
121
+ expect(plugin.id).toBe('usage');
122
+ });
123
+
124
+ it('should create a plugin with correct name', () => {
125
+ const plugin = createUsagePlugin({ store: mockStore });
126
+ expect(plugin.name).toBe('Usage');
127
+ });
128
+
129
+ it('should create a plugin with version', () => {
130
+ const plugin = createUsagePlugin({ store: mockStore });
131
+ expect(plugin.version).toBe('1.0.0');
132
+ });
133
+ });
134
+
135
+ describe('onStart', () => {
136
+ it('should initialize store on start', async () => {
137
+ const plugin = createUsagePlugin({ store: mockStore });
138
+ await plugin.onStart({}, mockRegistry);
139
+
140
+ expect(mockStore.initialize).toHaveBeenCalled();
141
+ });
142
+
143
+ it('should register health check', async () => {
144
+ const plugin = createUsagePlugin({ store: mockStore });
145
+ await plugin.onStart({}, mockRegistry);
146
+
147
+ expect(mockRegistry.registerHealthCheck).toHaveBeenCalledWith(
148
+ expect.objectContaining({
149
+ name: 'usage-store',
150
+ type: 'custom',
151
+ })
152
+ );
153
+ });
154
+ });
155
+
156
+ describe('onStop', () => {
157
+ it('should shutdown store', async () => {
158
+ const plugin = createUsagePlugin({ store: mockStore });
159
+ await plugin.onStart({}, mockRegistry);
160
+ await plugin.onStop?.();
161
+
162
+ expect(mockStore.shutdown).toHaveBeenCalled();
163
+ });
164
+
165
+ it('should clear store reference', async () => {
166
+ const plugin = createUsagePlugin({ store: mockStore });
167
+ await plugin.onStart({}, mockRegistry);
168
+ await plugin.onStop?.();
169
+
170
+ expect(getUsageStore()).toBeNull();
171
+ });
172
+ });
173
+
174
+ describe('helper functions', () => {
175
+ describe('getUsageStore', () => {
176
+ it('should return null when plugin not started', () => {
177
+ expect(getUsageStore()).toBeNull();
178
+ });
179
+
180
+ it('should return store when plugin started', async () => {
181
+ const plugin = createUsagePlugin({ store: mockStore });
182
+ await plugin.onStart({}, mockRegistry);
183
+
184
+ expect(getUsageStore()).toBe(mockStore);
185
+ });
186
+ });
187
+
188
+ describe('getDailyUsage', () => {
189
+ it('should throw when plugin not initialized', async () => {
190
+ await expect(
191
+ getDailyUsage('user-id', 'ai_messages')
192
+ ).rejects.toThrow('Usage plugin not initialized');
193
+ });
194
+
195
+ it('should return daily usage count', async () => {
196
+ const plugin = createUsagePlugin({ store: mockStore });
197
+ await plugin.onStart({}, mockRegistry);
198
+
199
+ const result = await getDailyUsage('user-123', 'ai_messages');
200
+ expect(result).toBe(10); // count from mockDailyUsage
201
+ });
202
+
203
+ it('should return 0 when no usage', async () => {
204
+ const plugin = createUsagePlugin({ store: mockStore });
205
+ await plugin.onStart({}, mockRegistry);
206
+
207
+ (mockStore.getDailyUsage as any).mockResolvedValue(null);
208
+
209
+ const result = await getDailyUsage('user-123', 'unused_feature');
210
+ expect(result).toBe(0);
211
+ });
212
+ });
213
+
214
+ describe('incrementUsage', () => {
215
+ it('should throw when plugin not initialized', async () => {
216
+ await expect(
217
+ incrementUsage('user-id', 'ai_messages')
218
+ ).rejects.toThrow('Usage plugin not initialized');
219
+ });
220
+
221
+ it('should increment and return result', async () => {
222
+ const plugin = createUsagePlugin({ store: mockStore });
223
+ await plugin.onStart({}, mockRegistry);
224
+
225
+ const result = await incrementUsage('user-123', 'ai_messages');
226
+
227
+ expect(result.allowed).toBe(true);
228
+ expect(result.current_count).toBeDefined();
229
+ expect(mockStore.incrementDaily).toHaveBeenCalled();
230
+ });
231
+
232
+ it('should increment by custom amount', async () => {
233
+ const plugin = createUsagePlugin({ store: mockStore });
234
+ await plugin.onStart({}, mockRegistry);
235
+
236
+ (mockStore.incrementDaily as any).mockResolvedValue(15);
237
+
238
+ const result = await incrementUsage('user-123', 'ai_messages', 5);
239
+
240
+ expect(result.allowed).toBe(true);
241
+ expect(result.current_count).toBe(15);
242
+ });
243
+ });
244
+
245
+ describe('checkUsageLimit', () => {
246
+ it('should throw when plugin not initialized', async () => {
247
+ await expect(
248
+ checkUsageLimit('user-id', 'ai_messages', 50)
249
+ ).rejects.toThrow('Usage plugin not initialized');
250
+ });
251
+
252
+ it('should return usage limit result', async () => {
253
+ const plugin = createUsagePlugin({ store: mockStore });
254
+ await plugin.onStart({}, mockRegistry);
255
+
256
+ // Reset mock to return -1 (unlimited)
257
+ (getFeatureLimit as any).mockResolvedValue(-1);
258
+
259
+ const result = await checkUsageLimit('user-123', 'ai_messages', -1);
260
+
261
+ // Should return a result object with usage info
262
+ expect(result).toHaveProperty('allowed');
263
+ expect(result).toHaveProperty('current_count');
264
+ expect(result).toHaveProperty('limit');
265
+ });
266
+ });
267
+
268
+ describe('resetUsage', () => {
269
+ it('should throw when plugin not initialized', async () => {
270
+ await expect(
271
+ resetUsage('user-id', 'ai_messages')
272
+ ).rejects.toThrow('Usage plugin not initialized');
273
+ });
274
+
275
+ it('should reset usage', async () => {
276
+ const plugin = createUsagePlugin({ store: mockStore });
277
+ await plugin.onStart({}, mockRegistry);
278
+
279
+ await resetUsage('user-123', 'ai_messages');
280
+
281
+ expect(mockStore.resetDailyUsage).toHaveBeenCalledWith(
282
+ 'user-123',
283
+ 'ai_messages'
284
+ );
285
+ });
286
+ });
287
+ });
288
+ });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Usage Plugin
3
+ *
4
+ * Usage tracking with daily/monthly counters.
5
+ * Exports all usage-related functionality.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ // Main plugin
11
+ export {
12
+ createUsagePlugin,
13
+ getUsageStore,
14
+ getDailyUsage,
15
+ incrementUsage,
16
+ checkUsageLimit,
17
+ getFeatureUsageStatus,
18
+ getDailyUsageSummary,
19
+ resetUsage,
20
+ getRemainingQuota,
21
+ canUseFeature,
22
+ } from './usage-plugin.js';
23
+
24
+ // Types
25
+ export type {
26
+ DailyUsage,
27
+ MonthlyUsage,
28
+ UsageIncrementResult,
29
+ UsageStatus,
30
+ UsageSummary,
31
+ UsageStore,
32
+ UsagePluginConfig,
33
+ UsageApiConfig,
34
+ UsageCleanupConfig,
35
+ PostgresUsageStoreConfig,
36
+ } from './types.js';
37
+
38
+ // Stores
39
+ export { postgresUsageStore } from './stores/index.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Usage Stores
3
+ *
4
+ * Export all usage storage implementations.
5
+ *
6
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
7
+ */
8
+
9
+ export { postgresUsageStore } from './postgres-store.js';