@qwickapps/server 1.4.0 → 1.5.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 (271) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +12 -2
  4. package/dist/index.js.map +1 -1
  5. package/dist/plugins/bans/bans-plugin.d.ts.map +1 -1
  6. package/dist/plugins/bans/bans-plugin.js +12 -3
  7. package/dist/plugins/bans/bans-plugin.js.map +1 -1
  8. package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts +11 -0
  9. package/dist/plugins/devices/__tests__/devices-plugin.test.d.ts.map +1 -0
  10. package/dist/plugins/devices/__tests__/devices-plugin.test.js +410 -0
  11. package/dist/plugins/devices/__tests__/devices-plugin.test.js.map +1 -0
  12. package/dist/plugins/devices/__tests__/token-utils.test.d.ts +7 -0
  13. package/dist/plugins/devices/__tests__/token-utils.test.d.ts.map +1 -0
  14. package/dist/plugins/devices/__tests__/token-utils.test.js +197 -0
  15. package/dist/plugins/devices/__tests__/token-utils.test.js.map +1 -0
  16. package/dist/plugins/devices/adapters/compute-adapter.d.ts +36 -0
  17. package/dist/plugins/devices/adapters/compute-adapter.d.ts.map +1 -0
  18. package/dist/plugins/devices/adapters/compute-adapter.js +100 -0
  19. package/dist/plugins/devices/adapters/compute-adapter.js.map +1 -0
  20. package/dist/plugins/devices/adapters/index.d.ts +12 -0
  21. package/dist/plugins/devices/adapters/index.d.ts.map +1 -0
  22. package/dist/plugins/devices/adapters/index.js +10 -0
  23. package/dist/plugins/devices/adapters/index.js.map +1 -0
  24. package/dist/plugins/devices/adapters/mobile-adapter.d.ts +41 -0
  25. package/dist/plugins/devices/adapters/mobile-adapter.d.ts.map +1 -0
  26. package/dist/plugins/devices/adapters/mobile-adapter.js +131 -0
  27. package/dist/plugins/devices/adapters/mobile-adapter.js.map +1 -0
  28. package/dist/plugins/devices/devices-plugin.d.ts +70 -0
  29. package/dist/plugins/devices/devices-plugin.d.ts.map +1 -0
  30. package/dist/plugins/devices/devices-plugin.js +453 -0
  31. package/dist/plugins/devices/devices-plugin.js.map +1 -0
  32. package/dist/plugins/devices/index.d.ts +18 -0
  33. package/dist/plugins/devices/index.d.ts.map +1 -0
  34. package/dist/plugins/devices/index.js +18 -0
  35. package/dist/plugins/devices/index.js.map +1 -0
  36. package/dist/plugins/devices/stores/index.d.ts +9 -0
  37. package/dist/plugins/devices/stores/index.d.ts.map +1 -0
  38. package/dist/plugins/devices/stores/index.js +9 -0
  39. package/dist/plugins/devices/stores/index.js.map +1 -0
  40. package/dist/plugins/devices/stores/postgres-store.d.ts +26 -0
  41. package/dist/plugins/devices/stores/postgres-store.d.ts.map +1 -0
  42. package/dist/plugins/devices/stores/postgres-store.js +199 -0
  43. package/dist/plugins/devices/stores/postgres-store.js.map +1 -0
  44. package/dist/plugins/devices/token-utils.d.ts +100 -0
  45. package/dist/plugins/devices/token-utils.d.ts.map +1 -0
  46. package/dist/plugins/devices/token-utils.js +162 -0
  47. package/dist/plugins/devices/token-utils.js.map +1 -0
  48. package/dist/plugins/devices/types.d.ts +307 -0
  49. package/dist/plugins/devices/types.d.ts.map +1 -0
  50. package/dist/plugins/devices/types.js +10 -0
  51. package/dist/plugins/devices/types.js.map +1 -0
  52. package/dist/plugins/index.d.ts +14 -2
  53. package/dist/plugins/index.d.ts.map +1 -1
  54. package/dist/plugins/index.js +13 -1
  55. package/dist/plugins/index.js.map +1 -1
  56. package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts +5 -0
  57. package/dist/plugins/notifications/__tests__/notifications-manager.test.d.ts.map +1 -0
  58. package/dist/plugins/notifications/__tests__/notifications-manager.test.js +470 -0
  59. package/dist/plugins/notifications/__tests__/notifications-manager.test.js.map +1 -0
  60. package/dist/plugins/notifications/index.d.ts +71 -0
  61. package/dist/plugins/notifications/index.d.ts.map +1 -0
  62. package/dist/plugins/notifications/index.js +72 -0
  63. package/dist/plugins/notifications/index.js.map +1 -0
  64. package/dist/plugins/notifications/notifications-manager.d.ts +182 -0
  65. package/dist/plugins/notifications/notifications-manager.d.ts.map +1 -0
  66. package/dist/plugins/notifications/notifications-manager.js +610 -0
  67. package/dist/plugins/notifications/notifications-manager.js.map +1 -0
  68. package/dist/plugins/notifications/notifications-plugin.d.ts +83 -0
  69. package/dist/plugins/notifications/notifications-plugin.d.ts.map +1 -0
  70. package/dist/plugins/notifications/notifications-plugin.js +337 -0
  71. package/dist/plugins/notifications/notifications-plugin.js.map +1 -0
  72. package/dist/plugins/notifications/types.d.ts +164 -0
  73. package/dist/plugins/notifications/types.d.ts.map +1 -0
  74. package/dist/plugins/notifications/types.js +9 -0
  75. package/dist/plugins/notifications/types.js.map +1 -0
  76. package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts +12 -0
  77. package/dist/plugins/parental/__tests__/parental-plugin.test.d.ts.map +1 -0
  78. package/dist/plugins/parental/__tests__/parental-plugin.test.js +349 -0
  79. package/dist/plugins/parental/__tests__/parental-plugin.test.js.map +1 -0
  80. package/dist/plugins/parental/adapters/index.d.ts +8 -0
  81. package/dist/plugins/parental/adapters/index.d.ts.map +1 -0
  82. package/dist/plugins/parental/adapters/index.js +7 -0
  83. package/dist/plugins/parental/adapters/index.js.map +1 -0
  84. package/dist/plugins/parental/adapters/kids-adapter.d.ts +24 -0
  85. package/dist/plugins/parental/adapters/kids-adapter.d.ts.map +1 -0
  86. package/dist/plugins/parental/adapters/kids-adapter.js +174 -0
  87. package/dist/plugins/parental/adapters/kids-adapter.js.map +1 -0
  88. package/dist/plugins/parental/index.d.ts +14 -0
  89. package/dist/plugins/parental/index.d.ts.map +1 -0
  90. package/dist/plugins/parental/index.js +15 -0
  91. package/dist/plugins/parental/index.js.map +1 -0
  92. package/dist/plugins/parental/parental-plugin.d.ts +88 -0
  93. package/dist/plugins/parental/parental-plugin.d.ts.map +1 -0
  94. package/dist/plugins/parental/parental-plugin.js +666 -0
  95. package/dist/plugins/parental/parental-plugin.js.map +1 -0
  96. package/dist/plugins/parental/stores/index.d.ts +7 -0
  97. package/dist/plugins/parental/stores/index.d.ts.map +1 -0
  98. package/dist/plugins/parental/stores/index.js +7 -0
  99. package/dist/plugins/parental/stores/index.js.map +1 -0
  100. package/dist/plugins/parental/stores/postgres-store.d.ts +10 -0
  101. package/dist/plugins/parental/stores/postgres-store.d.ts.map +1 -0
  102. package/dist/plugins/parental/stores/postgres-store.js +209 -0
  103. package/dist/plugins/parental/stores/postgres-store.js.map +1 -0
  104. package/dist/plugins/parental/types.d.ts +154 -0
  105. package/dist/plugins/parental/types.d.ts.map +1 -0
  106. package/dist/plugins/parental/types.js +10 -0
  107. package/dist/plugins/parental/types.js.map +1 -0
  108. package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts +11 -0
  109. package/dist/plugins/profiles/__tests__/profiles-plugin.test.d.ts.map +1 -0
  110. package/dist/plugins/profiles/__tests__/profiles-plugin.test.js +243 -0
  111. package/dist/plugins/profiles/__tests__/profiles-plugin.test.js.map +1 -0
  112. package/dist/plugins/profiles/index.d.ts +12 -0
  113. package/dist/plugins/profiles/index.d.ts.map +1 -0
  114. package/dist/plugins/profiles/index.js +13 -0
  115. package/dist/plugins/profiles/index.js.map +1 -0
  116. package/dist/plugins/profiles/profiles-plugin.d.ts +71 -0
  117. package/dist/plugins/profiles/profiles-plugin.d.ts.map +1 -0
  118. package/dist/plugins/profiles/profiles-plugin.js +481 -0
  119. package/dist/plugins/profiles/profiles-plugin.js.map +1 -0
  120. package/dist/plugins/profiles/stores/index.d.ts +9 -0
  121. package/dist/plugins/profiles/stores/index.d.ts.map +1 -0
  122. package/dist/plugins/profiles/stores/index.js +9 -0
  123. package/dist/plugins/profiles/stores/index.js.map +1 -0
  124. package/dist/plugins/profiles/stores/postgres-store.d.ts +18 -0
  125. package/dist/plugins/profiles/stores/postgres-store.d.ts.map +1 -0
  126. package/dist/plugins/profiles/stores/postgres-store.js +310 -0
  127. package/dist/plugins/profiles/stores/postgres-store.js.map +1 -0
  128. package/dist/plugins/profiles/types.d.ts +289 -0
  129. package/dist/plugins/profiles/types.d.ts.map +1 -0
  130. package/dist/plugins/profiles/types.js +10 -0
  131. package/dist/plugins/profiles/types.js.map +1 -0
  132. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts +11 -0
  133. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.d.ts.map +1 -0
  134. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js +305 -0
  135. package/dist/plugins/subscriptions/__tests__/subscriptions-plugin.test.js.map +1 -0
  136. package/dist/plugins/subscriptions/index.d.ts +12 -0
  137. package/dist/plugins/subscriptions/index.d.ts.map +1 -0
  138. package/dist/plugins/subscriptions/index.js +13 -0
  139. package/dist/plugins/subscriptions/index.js.map +1 -0
  140. package/dist/plugins/subscriptions/stores/index.d.ts +9 -0
  141. package/dist/plugins/subscriptions/stores/index.d.ts.map +1 -0
  142. package/dist/plugins/subscriptions/stores/index.js +9 -0
  143. package/dist/plugins/subscriptions/stores/index.js.map +1 -0
  144. package/dist/plugins/subscriptions/stores/postgres-store.d.ts +14 -0
  145. package/dist/plugins/subscriptions/stores/postgres-store.d.ts.map +1 -0
  146. package/dist/plugins/subscriptions/stores/postgres-store.js +359 -0
  147. package/dist/plugins/subscriptions/stores/postgres-store.js.map +1 -0
  148. package/dist/plugins/subscriptions/subscriptions-plugin.d.ts +82 -0
  149. package/dist/plugins/subscriptions/subscriptions-plugin.d.ts.map +1 -0
  150. package/dist/plugins/subscriptions/subscriptions-plugin.js +449 -0
  151. package/dist/plugins/subscriptions/subscriptions-plugin.js.map +1 -0
  152. package/dist/plugins/subscriptions/types.d.ts +308 -0
  153. package/dist/plugins/subscriptions/types.d.ts.map +1 -0
  154. package/dist/plugins/subscriptions/types.js +10 -0
  155. package/dist/plugins/subscriptions/types.js.map +1 -0
  156. package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts +11 -0
  157. package/dist/plugins/usage/__tests__/usage-plugin.test.d.ts.map +1 -0
  158. package/dist/plugins/usage/__tests__/usage-plugin.test.js +218 -0
  159. package/dist/plugins/usage/__tests__/usage-plugin.test.js.map +1 -0
  160. package/dist/plugins/usage/index.d.ts +12 -0
  161. package/dist/plugins/usage/index.d.ts.map +1 -0
  162. package/dist/plugins/usage/index.js +13 -0
  163. package/dist/plugins/usage/index.js.map +1 -0
  164. package/dist/plugins/usage/stores/index.d.ts +9 -0
  165. package/dist/plugins/usage/stores/index.d.ts.map +1 -0
  166. package/dist/plugins/usage/stores/index.js +9 -0
  167. package/dist/plugins/usage/stores/index.js.map +1 -0
  168. package/dist/plugins/usage/stores/postgres-store.d.ts +14 -0
  169. package/dist/plugins/usage/stores/postgres-store.d.ts.map +1 -0
  170. package/dist/plugins/usage/stores/postgres-store.js +146 -0
  171. package/dist/plugins/usage/stores/postgres-store.js.map +1 -0
  172. package/dist/plugins/usage/types.d.ts +195 -0
  173. package/dist/plugins/usage/types.d.ts.map +1 -0
  174. package/dist/plugins/usage/types.js +10 -0
  175. package/dist/plugins/usage/types.js.map +1 -0
  176. package/dist/plugins/usage/usage-plugin.d.ts +51 -0
  177. package/dist/plugins/usage/usage-plugin.d.ts.map +1 -0
  178. package/dist/plugins/usage/usage-plugin.js +412 -0
  179. package/dist/plugins/usage/usage-plugin.js.map +1 -0
  180. package/dist/plugins/users/__tests__/postgres-store.test.d.ts +10 -0
  181. package/dist/plugins/users/__tests__/postgres-store.test.d.ts.map +1 -0
  182. package/dist/plugins/users/__tests__/postgres-store.test.js +229 -0
  183. package/dist/plugins/users/__tests__/postgres-store.test.js.map +1 -0
  184. package/dist/plugins/users/__tests__/users-plugin.test.js +3 -0
  185. package/dist/plugins/users/__tests__/users-plugin.test.js.map +1 -1
  186. package/dist/plugins/users/index.d.ts +2 -2
  187. package/dist/plugins/users/index.d.ts.map +1 -1
  188. package/dist/plugins/users/index.js +1 -1
  189. package/dist/plugins/users/index.js.map +1 -1
  190. package/dist/plugins/users/stores/postgres-store.d.ts.map +1 -1
  191. package/dist/plugins/users/stores/postgres-store.js +76 -0
  192. package/dist/plugins/users/stores/postgres-store.js.map +1 -1
  193. package/dist/plugins/users/types.d.ts +74 -6
  194. package/dist/plugins/users/types.d.ts.map +1 -1
  195. package/dist/plugins/users/users-plugin.d.ts +15 -1
  196. package/dist/plugins/users/users-plugin.d.ts.map +1 -1
  197. package/dist/plugins/users/users-plugin.js +29 -0
  198. package/dist/plugins/users/users-plugin.js.map +1 -1
  199. package/dist-ui/assets/index-CynOqPkb.js +469 -0
  200. package/dist-ui/assets/index-CynOqPkb.js.map +1 -0
  201. package/dist-ui/index.html +1 -1
  202. package/dist-ui-lib/api/controlPanelApi.d.ts +46 -0
  203. package/dist-ui-lib/components/StatCard.d.ts +16 -0
  204. package/dist-ui-lib/dashboard/widgets/NotificationsStatsWidget.d.ts +12 -0
  205. package/dist-ui-lib/dashboard/widgets/index.d.ts +1 -0
  206. package/dist-ui-lib/index.js +1822 -1611
  207. package/dist-ui-lib/index.js.map +1 -1
  208. package/dist-ui-lib/pages/NotificationsPage.d.ts +9 -0
  209. package/dist-ui-lib/utils/formatters.d.ts +19 -0
  210. package/package.json +1 -1
  211. package/src/index.ts +178 -0
  212. package/src/plugins/bans/bans-plugin.ts +15 -3
  213. package/src/plugins/devices/__tests__/devices-plugin.test.ts +551 -0
  214. package/src/plugins/devices/__tests__/token-utils.test.ts +264 -0
  215. package/src/plugins/devices/adapters/compute-adapter.ts +139 -0
  216. package/src/plugins/devices/adapters/index.ts +13 -0
  217. package/src/plugins/devices/adapters/mobile-adapter.ts +179 -0
  218. package/src/plugins/devices/devices-plugin.ts +538 -0
  219. package/src/plugins/devices/index.ts +69 -0
  220. package/src/plugins/devices/stores/index.ts +9 -0
  221. package/src/plugins/devices/stores/postgres-store.ts +304 -0
  222. package/src/plugins/devices/token-utils.ts +213 -0
  223. package/src/plugins/devices/types.ts +351 -0
  224. package/src/plugins/index.ts +218 -0
  225. package/src/plugins/notifications/__tests__/notifications-manager.test.ts +637 -0
  226. package/src/plugins/notifications/index.ts +91 -0
  227. package/src/plugins/notifications/notifications-manager.ts +773 -0
  228. package/src/plugins/notifications/notifications-plugin.ts +398 -0
  229. package/src/plugins/notifications/types.ts +207 -0
  230. package/src/plugins/parental/__tests__/parental-plugin.test.ts +465 -0
  231. package/src/plugins/parental/adapters/index.ts +8 -0
  232. package/src/plugins/parental/adapters/kids-adapter.ts +206 -0
  233. package/src/plugins/parental/index.ts +55 -0
  234. package/src/plugins/parental/parental-plugin.ts +759 -0
  235. package/src/plugins/parental/stores/index.ts +7 -0
  236. package/src/plugins/parental/stores/postgres-store.ts +304 -0
  237. package/src/plugins/parental/types.ts +180 -0
  238. package/src/plugins/profiles/__tests__/profiles-plugin.test.ts +321 -0
  239. package/src/plugins/profiles/index.ts +49 -0
  240. package/src/plugins/profiles/profiles-plugin.ts +546 -0
  241. package/src/plugins/profiles/stores/index.ts +9 -0
  242. package/src/plugins/profiles/stores/postgres-store.ts +439 -0
  243. package/src/plugins/profiles/types.ts +338 -0
  244. package/src/plugins/subscriptions/__tests__/subscriptions-plugin.test.ts +404 -0
  245. package/src/plugins/subscriptions/index.ts +51 -0
  246. package/src/plugins/subscriptions/stores/index.ts +9 -0
  247. package/src/plugins/subscriptions/stores/postgres-store.ts +482 -0
  248. package/src/plugins/subscriptions/subscriptions-plugin.ts +530 -0
  249. package/src/plugins/subscriptions/types.ts +355 -0
  250. package/src/plugins/usage/__tests__/usage-plugin.test.ts +288 -0
  251. package/src/plugins/usage/index.ts +39 -0
  252. package/src/plugins/usage/stores/index.ts +9 -0
  253. package/src/plugins/usage/stores/postgres-store.ts +213 -0
  254. package/src/plugins/usage/types.ts +222 -0
  255. package/src/plugins/usage/usage-plugin.ts +484 -0
  256. package/src/plugins/users/__tests__/postgres-store.test.ts +326 -0
  257. package/src/plugins/users/__tests__/users-plugin.test.ts +3 -0
  258. package/src/plugins/users/index.ts +6 -0
  259. package/src/plugins/users/stores/postgres-store.ts +104 -0
  260. package/src/plugins/users/types.ts +82 -6
  261. package/src/plugins/users/users-plugin.ts +37 -0
  262. package/ui/src/App.tsx +5 -1
  263. package/ui/src/api/controlPanelApi.ts +103 -6
  264. package/ui/src/components/StatCard.tsx +58 -0
  265. package/ui/src/dashboard/builtInWidgets.tsx +3 -1
  266. package/ui/src/dashboard/widgets/NotificationsStatsWidget.tsx +167 -0
  267. package/ui/src/dashboard/widgets/index.ts +1 -0
  268. package/ui/src/pages/NotificationsPage.tsx +417 -0
  269. package/ui/src/utils/formatters.ts +33 -0
  270. package/dist-ui/assets/index-D7DoZ9rL.js +0 -478
  271. package/dist-ui/assets/index-D7DoZ9rL.js.map +0 -1
@@ -0,0 +1,404 @@
1
+ /**
2
+ * Subscriptions Plugin Tests
3
+ *
4
+ * Unit tests for the subscriptions plugin including:
5
+ * - Plugin lifecycle
6
+ * - Tier management
7
+ * - Entitlement checks
8
+ * - User subscription management
9
+ */
10
+
11
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
12
+ import {
13
+ createSubscriptionsPlugin,
14
+ getSubscriptionsStore,
15
+ getTierById,
16
+ getTierBySlug,
17
+ listTiers,
18
+ createTier,
19
+ getUserSubscription,
20
+ createUserSubscription,
21
+ getFeatureLimit,
22
+ hasFeature,
23
+ } from '../subscriptions-plugin.js';
24
+ import type {
25
+ SubscriptionsStore,
26
+ SubscriptionTier,
27
+ UserSubscription,
28
+ SubscriptionEntitlement,
29
+ } from '../types.js';
30
+ import type { PluginRegistry } from '../../../core/plugin-registry.js';
31
+
32
+ describe('Subscriptions Plugin', () => {
33
+ // Mock tier data
34
+ const mockTier: SubscriptionTier = {
35
+ id: 'tier-id-123',
36
+ slug: 'premium',
37
+ name: 'Premium Plan',
38
+ description: 'Full access to all features',
39
+ price_monthly_cents: 999,
40
+ price_yearly_cents: 9990,
41
+ stripe_price_id_monthly: 'price_monthly_123',
42
+ stripe_price_id_yearly: 'price_yearly_123',
43
+ is_active: true,
44
+ sort_order: 1,
45
+ metadata: {},
46
+ created_at: new Date(),
47
+ updated_at: new Date(),
48
+ };
49
+
50
+ const mockEntitlement: SubscriptionEntitlement = {
51
+ id: 'entitlement-id-123',
52
+ tier_id: mockTier.id,
53
+ feature_code: 'ai_messages_daily',
54
+ limit_value: -1, // unlimited
55
+ metadata: {},
56
+ };
57
+
58
+ const mockSubscription: UserSubscription = {
59
+ id: 'subscription-id-123',
60
+ user_id: 'user-123',
61
+ tier_id: mockTier.id,
62
+ stripe_customer_id: 'cus_123',
63
+ stripe_subscription_id: 'sub_123',
64
+ status: 'active',
65
+ current_period_start: new Date(),
66
+ current_period_end: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
67
+ cancel_at_period_end: false,
68
+ metadata: {},
69
+ created_at: new Date(),
70
+ updated_at: new Date(),
71
+ };
72
+
73
+ // Mock store factory
74
+ const createMockStore = (): SubscriptionsStore => ({
75
+ name: 'mock',
76
+ initialize: vi.fn().mockResolvedValue(undefined),
77
+ // Tiers
78
+ createTier: vi.fn().mockResolvedValue(mockTier),
79
+ getTierById: vi.fn().mockResolvedValue(mockTier),
80
+ getTierBySlug: vi.fn().mockResolvedValue(mockTier),
81
+ listTiers: vi.fn().mockResolvedValue([mockTier]),
82
+ updateTier: vi.fn().mockResolvedValue(mockTier),
83
+ deleteTier: vi.fn().mockResolvedValue(true),
84
+ // Entitlements
85
+ createEntitlement: vi.fn().mockResolvedValue(mockEntitlement),
86
+ getEntitlementsByTier: vi.fn().mockResolvedValue([mockEntitlement]),
87
+ updateEntitlement: vi.fn().mockResolvedValue(mockEntitlement),
88
+ deleteEntitlement: vi.fn().mockResolvedValue(true),
89
+ setTierEntitlements: vi.fn().mockResolvedValue(undefined),
90
+ // User subscriptions
91
+ createUserSubscription: vi.fn().mockResolvedValue(mockSubscription),
92
+ getUserSubscriptionById: vi.fn().mockResolvedValue(mockSubscription),
93
+ getActiveSubscription: vi.fn().mockResolvedValue({
94
+ ...mockSubscription,
95
+ tier: mockTier,
96
+ }),
97
+ getByStripeSubscriptionId: vi.fn().mockResolvedValue(mockSubscription),
98
+ updateUserSubscription: vi.fn().mockResolvedValue(mockSubscription),
99
+ cancelSubscription: vi.fn().mockResolvedValue(true),
100
+ getFeatureLimit: vi.fn().mockResolvedValue(-1), // unlimited
101
+ hasFeature: vi.fn().mockResolvedValue(true),
102
+ shutdown: vi.fn().mockResolvedValue(undefined),
103
+ });
104
+
105
+ // Mock registry factory
106
+ const createMockRegistry = (): PluginRegistry =>
107
+ ({
108
+ hasPlugin: vi.fn().mockReturnValue(false),
109
+ getPlugin: vi.fn().mockReturnValue(null),
110
+ listPlugins: vi.fn().mockReturnValue([]),
111
+ addRoute: vi.fn(),
112
+ addMenuItem: vi.fn(),
113
+ addPage: vi.fn(),
114
+ addWidget: vi.fn(),
115
+ getRoutes: vi.fn().mockReturnValue([]),
116
+ getMenuItems: vi.fn().mockReturnValue([]),
117
+ getPages: vi.fn().mockReturnValue([]),
118
+ getWidgets: vi.fn().mockReturnValue([]),
119
+ getConfig: vi.fn().mockReturnValue({}),
120
+ setConfig: vi.fn().mockResolvedValue(undefined),
121
+ subscribe: vi.fn().mockReturnValue(() => {}),
122
+ emit: vi.fn(),
123
+ registerHealthCheck: vi.fn(),
124
+ getApp: vi.fn().mockReturnValue({} as any),
125
+ getRouter: vi.fn().mockReturnValue({} as any),
126
+ getLogger: vi.fn().mockReturnValue({
127
+ debug: vi.fn(),
128
+ info: vi.fn(),
129
+ warn: vi.fn(),
130
+ error: vi.fn(),
131
+ }),
132
+ }) as unknown as PluginRegistry;
133
+
134
+ let mockStore: SubscriptionsStore;
135
+ let mockRegistry: PluginRegistry;
136
+
137
+ beforeEach(() => {
138
+ vi.clearAllMocks();
139
+ mockStore = createMockStore();
140
+ mockRegistry = createMockRegistry();
141
+ });
142
+
143
+ afterEach(async () => {
144
+ // Clean up
145
+ const store = getSubscriptionsStore();
146
+ if (store) {
147
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
148
+ await plugin.onStop?.();
149
+ }
150
+ });
151
+
152
+ describe('createSubscriptionsPlugin', () => {
153
+ it('should create a plugin with correct id', () => {
154
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
155
+ expect(plugin.id).toBe('subscriptions');
156
+ });
157
+
158
+ it('should create a plugin with correct name', () => {
159
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
160
+ expect(plugin.name).toBe('Subscriptions');
161
+ });
162
+
163
+ it('should create a plugin with version', () => {
164
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
165
+ expect(plugin.version).toBe('1.0.0');
166
+ });
167
+ });
168
+
169
+ describe('onStart', () => {
170
+ it('should initialize store on start', async () => {
171
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
172
+ await plugin.onStart({}, mockRegistry);
173
+
174
+ expect(mockStore.initialize).toHaveBeenCalled();
175
+ });
176
+
177
+ it('should register health check', async () => {
178
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
179
+ await plugin.onStart({}, mockRegistry);
180
+
181
+ expect(mockRegistry.registerHealthCheck).toHaveBeenCalledWith(
182
+ expect.objectContaining({
183
+ name: 'subscriptions-store',
184
+ type: 'custom',
185
+ })
186
+ );
187
+ });
188
+ });
189
+
190
+ describe('onStop', () => {
191
+ it('should shutdown store', async () => {
192
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
193
+ await plugin.onStart({}, mockRegistry);
194
+ await plugin.onStop?.();
195
+
196
+ expect(mockStore.shutdown).toHaveBeenCalled();
197
+ });
198
+
199
+ it('should clear store reference', async () => {
200
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
201
+ await plugin.onStart({}, mockRegistry);
202
+ await plugin.onStop?.();
203
+
204
+ expect(getSubscriptionsStore()).toBeNull();
205
+ });
206
+ });
207
+
208
+ describe('helper functions', () => {
209
+ describe('getSubscriptionsStore', () => {
210
+ it('should return null when plugin not started', () => {
211
+ expect(getSubscriptionsStore()).toBeNull();
212
+ });
213
+
214
+ it('should return store when plugin started', async () => {
215
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
216
+ await plugin.onStart({}, mockRegistry);
217
+
218
+ expect(getSubscriptionsStore()).toBe(mockStore);
219
+ });
220
+ });
221
+
222
+ describe('getTierById', () => {
223
+ it('should throw when plugin not initialized', async () => {
224
+ await expect(getTierById('tier-id')).rejects.toThrow(
225
+ 'Subscriptions plugin not initialized'
226
+ );
227
+ });
228
+
229
+ it('should return tier when found', async () => {
230
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
231
+ await plugin.onStart({}, mockRegistry);
232
+
233
+ const result = await getTierById(mockTier.id);
234
+ expect(result).toEqual(mockTier);
235
+ });
236
+ });
237
+
238
+ describe('getTierBySlug', () => {
239
+ it('should throw when plugin not initialized', async () => {
240
+ await expect(getTierBySlug('premium')).rejects.toThrow(
241
+ 'Subscriptions plugin not initialized'
242
+ );
243
+ });
244
+
245
+ it('should return tier when found', async () => {
246
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
247
+ await plugin.onStart({}, mockRegistry);
248
+
249
+ const result = await getTierBySlug('premium');
250
+ expect(result).toEqual(mockTier);
251
+ });
252
+ });
253
+
254
+ describe('listTiers', () => {
255
+ it('should throw when plugin not initialized', async () => {
256
+ await expect(listTiers()).rejects.toThrow(
257
+ 'Subscriptions plugin not initialized'
258
+ );
259
+ });
260
+
261
+ it('should return all tiers', async () => {
262
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
263
+ await plugin.onStart({}, mockRegistry);
264
+
265
+ const result = await listTiers();
266
+
267
+ expect(result).toHaveLength(1);
268
+ expect(result[0]).toEqual(mockTier);
269
+ });
270
+ });
271
+
272
+ describe('createTier', () => {
273
+ it('should throw when plugin not initialized', async () => {
274
+ await expect(
275
+ createTier({ slug: 'new', name: 'New Tier' })
276
+ ).rejects.toThrow('Subscriptions plugin not initialized');
277
+ });
278
+
279
+ it('should create tier', async () => {
280
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
281
+ await plugin.onStart({}, mockRegistry);
282
+
283
+ const result = await createTier({ slug: 'new', name: 'New Tier' });
284
+
285
+ expect(result).toEqual(mockTier);
286
+ expect(mockStore.createTier).toHaveBeenCalled();
287
+ });
288
+ });
289
+
290
+ describe('getUserSubscription', () => {
291
+ it('should throw when plugin not initialized', async () => {
292
+ await expect(getUserSubscription('user-id')).rejects.toThrow(
293
+ 'Subscriptions plugin not initialized'
294
+ );
295
+ });
296
+
297
+ it('should return user subscription with tier', async () => {
298
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
299
+ await plugin.onStart({}, mockRegistry);
300
+
301
+ const result = await getUserSubscription('user-123');
302
+
303
+ expect(result?.user_id).toBe('user-123');
304
+ expect(result?.tier).toBeDefined();
305
+ });
306
+
307
+ it('should return null when no subscription', async () => {
308
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
309
+ await plugin.onStart({}, mockRegistry);
310
+
311
+ (mockStore.getActiveSubscription as any).mockResolvedValue(null);
312
+
313
+ const result = await getUserSubscription('user-no-sub');
314
+ expect(result).toBeNull();
315
+ });
316
+ });
317
+
318
+ describe('createUserSubscription', () => {
319
+ it('should throw when plugin not initialized', async () => {
320
+ await expect(
321
+ createUserSubscription({
322
+ user_id: 'user-123',
323
+ tier_id: mockTier.id,
324
+ })
325
+ ).rejects.toThrow('Subscriptions plugin not initialized');
326
+ });
327
+
328
+ it('should create subscription', async () => {
329
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
330
+ await plugin.onStart({}, mockRegistry);
331
+
332
+ const result = await createUserSubscription({
333
+ user_id: 'user-123',
334
+ tier_id: mockTier.id,
335
+ });
336
+
337
+ expect(result).toEqual(mockSubscription);
338
+ expect(mockStore.createUserSubscription).toHaveBeenCalled();
339
+ });
340
+ });
341
+
342
+ describe('getFeatureLimit', () => {
343
+ it('should throw when plugin not initialized', async () => {
344
+ await expect(
345
+ getFeatureLimit('user-id', 'ai_messages_daily')
346
+ ).rejects.toThrow('Subscriptions plugin not initialized');
347
+ });
348
+
349
+ it('should return -1 for unlimited feature', async () => {
350
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
351
+ await plugin.onStart({}, mockRegistry);
352
+
353
+ const result = await getFeatureLimit('user-123', 'ai_messages_daily');
354
+ expect(result).toBe(-1);
355
+ });
356
+
357
+ it('should return limit value', async () => {
358
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
359
+ await plugin.onStart({}, mockRegistry);
360
+
361
+ (mockStore.getFeatureLimit as any).mockResolvedValue(100);
362
+
363
+ const result = await getFeatureLimit('user-123', 'vision_calls_daily');
364
+ expect(result).toBe(100);
365
+ });
366
+
367
+ it('should return null when no subscription', async () => {
368
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
369
+ await plugin.onStart({}, mockRegistry);
370
+
371
+ (mockStore.getFeatureLimit as any).mockResolvedValue(null);
372
+
373
+ const result = await getFeatureLimit('user-no-sub', 'ai_messages_daily');
374
+ expect(result).toBeNull();
375
+ });
376
+ });
377
+
378
+ describe('hasFeature', () => {
379
+ it('should throw when plugin not initialized', async () => {
380
+ await expect(
381
+ hasFeature('user-id', 'ai_messages_daily')
382
+ ).rejects.toThrow('Subscriptions plugin not initialized');
383
+ });
384
+
385
+ it('should return true when user has feature', async () => {
386
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
387
+ await plugin.onStart({}, mockRegistry);
388
+
389
+ const result = await hasFeature('user-123', 'ai_messages_daily');
390
+ expect(result).toBe(true);
391
+ });
392
+
393
+ it('should return false when user lacks feature', async () => {
394
+ const plugin = createSubscriptionsPlugin({ store: mockStore });
395
+ await plugin.onStart({}, mockRegistry);
396
+
397
+ (mockStore.hasFeature as any).mockResolvedValue(false);
398
+
399
+ const result = await hasFeature('user-123', 'premium_feature');
400
+ expect(result).toBe(false);
401
+ });
402
+ });
403
+ });
404
+ });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Subscriptions Plugin
3
+ *
4
+ * Subscription tier and entitlement management.
5
+ * Exports all subscription-related functionality.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ // Main plugin
11
+ export {
12
+ createSubscriptionsPlugin,
13
+ getSubscriptionsStore,
14
+ createTier,
15
+ getTierBySlug,
16
+ getTierById,
17
+ listTiers,
18
+ getTierEntitlements,
19
+ setTierEntitlements,
20
+ getUserSubscription,
21
+ createUserSubscription,
22
+ updateUserSubscription,
23
+ cancelSubscription,
24
+ getUserTierSlug,
25
+ getFeatureLimit,
26
+ hasFeature,
27
+ checkFeatureLimit,
28
+ ensureUserSubscription,
29
+ } from './subscriptions-plugin.js';
30
+
31
+ // Types
32
+ export type {
33
+ SubscriptionTier,
34
+ SubscriptionEntitlement,
35
+ UserSubscription,
36
+ UserSubscriptionWithTier,
37
+ SubscriptionStatus,
38
+ FeatureLimitResult,
39
+ CreateTierInput,
40
+ UpdateTierInput,
41
+ CreateEntitlementInput,
42
+ CreateUserSubscriptionInput,
43
+ UpdateUserSubscriptionInput,
44
+ SubscriptionsStore,
45
+ SubscriptionsPluginConfig,
46
+ SubscriptionsApiConfig,
47
+ PostgresSubscriptionsStoreConfig,
48
+ } from './types.js';
49
+
50
+ // Stores
51
+ export { postgresSubscriptionsStore } from './stores/index.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Subscriptions Stores
3
+ *
4
+ * Export all subscriptions storage implementations.
5
+ *
6
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
7
+ */
8
+
9
+ export { postgresSubscriptionsStore } from './postgres-store.js';