@talkpilot/core-db 1.3.84 → 1.3.86

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 (229) hide show
  1. package/.cursor/rules/development.mdc +65 -65
  2. package/AGENTS.md +49 -49
  3. package/DEVELOPMENT.md +141 -141
  4. package/README.md +847 -847
  5. package/dist/talkpilot/flows/flows.schema.d.ts +9 -0
  6. package/dist/talkpilot/flows/flows.schema.d.ts.map +1 -1
  7. package/dist/talkpilot/flows/flows.schema.js +7 -0
  8. package/dist/talkpilot/flows/flows.schema.js.map +1 -1
  9. package/dist/talkpilot/flows/flows.types.d.ts +3 -0
  10. package/dist/talkpilot/flows/flows.types.d.ts.map +1 -1
  11. package/dist/talkpilot/subscriptions/subscriptions.utils.d.ts +4 -0
  12. package/dist/talkpilot/subscriptions/subscriptions.utils.d.ts.map +1 -0
  13. package/dist/talkpilot/subscriptions/subscriptions.utils.js +20 -0
  14. package/dist/talkpilot/subscriptions/subscriptions.utils.js.map +1 -0
  15. package/jest.config.js +20 -20
  16. package/package.json +46 -46
  17. package/src/__tests__/setup.ts +20 -20
  18. package/src/bulkWrite/__tests__/bulkWrite.spec.ts +65 -65
  19. package/src/bulkWrite/bulkWrite.ops.ts +45 -45
  20. package/src/bulkWrite/bulkWrite.types.ts +4 -4
  21. package/src/bulkWrite/index.ts +2 -2
  22. package/src/config.ts +9 -9
  23. package/src/configuration/index.ts +17 -17
  24. package/src/configuration/internalModels/index.ts +3 -3
  25. package/src/configuration/internalModels/internalModels.constants.ts +1 -1
  26. package/src/configuration/internalModels/internalModels.getters.ts +33 -33
  27. package/src/configuration/internalModels/internalModels.types.ts +12 -12
  28. package/src/configuration/models/__tests__/models.getters.spec.ts +67 -67
  29. package/src/configuration/models/index.ts +27 -27
  30. package/src/configuration/models/models.constants.ts +20 -20
  31. package/src/configuration/models/models.getters.ts +71 -71
  32. package/src/configuration/models/models.types.ts +80 -80
  33. package/src/configuration/mongodb-client.ts +62 -62
  34. package/src/configuration/prompts/index.ts +2 -2
  35. package/src/configuration/prompts/prompts.getters.ts +49 -49
  36. package/src/configuration/prompts/prompts.types.ts +11 -11
  37. package/src/connection.ts +81 -81
  38. package/src/index.ts +48 -48
  39. package/src/municipal/__tests__/validation.spec.ts +62 -62
  40. package/src/municipal/cities/cities.getters.ts +50 -50
  41. package/src/municipal/cities/cities.types.ts +11 -11
  42. package/src/municipal/cities/index.ts +2 -2
  43. package/src/municipal/departmentsSubjects/__tests__/departmentsSubjects.spec.ts +272 -272
  44. package/src/municipal/departmentsSubjects/departmentsSubjects.getters.ts +317 -317
  45. package/src/municipal/departmentsSubjects/departmentsSubjects.types.ts +73 -73
  46. package/src/municipal/departmentsSubjects/index.ts +9 -9
  47. package/src/municipal/index.ts +23 -23
  48. package/src/municipal/mongodb-client.ts +61 -61
  49. package/src/municipal/muniIssues/__tests__/muniIssues.getters.spec.ts +87 -87
  50. package/src/municipal/muniIssues/__tests__/muniIssues.setters.spec.ts +219 -219
  51. package/src/municipal/muniIssues/__tests__/muniIssues.setup.spec.ts +93 -93
  52. package/src/municipal/muniIssues/index.ts +24 -24
  53. package/src/municipal/muniIssues/muniIssues.constants.ts +27 -27
  54. package/src/municipal/muniIssues/muniIssues.getters.ts +73 -73
  55. package/src/municipal/muniIssues/muniIssues.schema.ts +85 -85
  56. package/src/municipal/muniIssues/muniIssues.setters.ts +87 -87
  57. package/src/municipal/muniIssues/muniIssues.setup.ts +65 -65
  58. package/src/municipal/muniIssues/muniIssues.types.ts +67 -67
  59. package/src/municipal/residents/__tests__/residents.spec.ts +92 -92
  60. package/src/municipal/residents/index.ts +4 -4
  61. package/src/municipal/residents/residents.constants.ts +2 -2
  62. package/src/municipal/residents/residents.getters.ts +42 -42
  63. package/src/municipal/residents/residents.schema.ts +28 -28
  64. package/src/municipal/residents/residents.types.ts +17 -17
  65. package/src/municipal/streets/__tests__/streets.spec.ts +253 -253
  66. package/src/municipal/streets/index.ts +2 -2
  67. package/src/municipal/streets/streets.getters.ts +140 -140
  68. package/src/municipal/streets/streets.types.ts +19 -19
  69. package/src/municipal/systemInstructions/__tests__/getters.spec.ts +113 -113
  70. package/src/municipal/systemInstructions/__tests__/setters.spec.ts +274 -274
  71. package/src/municipal/systemInstructions/index.ts +7 -7
  72. package/src/municipal/systemInstructions/instructions.getters.ts +57 -57
  73. package/src/municipal/systemInstructions/instructions.setters.ts +119 -119
  74. package/src/municipal/systemInstructions/instructions.types.ts +30 -30
  75. package/src/municipal/tickets/__tests__/tickets.getters.spec.ts +30 -30
  76. package/src/municipal/tickets/__tests__/tickets.statistics.spec.ts +99 -99
  77. package/src/municipal/tickets/index.ts +9 -9
  78. package/src/municipal/tickets/tickets.constants.ts +8 -8
  79. package/src/municipal/tickets/tickets.getters.ts +121 -121
  80. package/src/municipal/tickets/tickets.statistics.aggregation.ts +110 -110
  81. package/src/municipal/tickets/tickets.statistics.getters.ts +145 -145
  82. package/src/municipal/tickets/tickets.types.ts +54 -54
  83. package/src/municipal/utils/types.ts +11 -11
  84. package/src/talkpilot/__tests__/db.spec.ts +38 -38
  85. package/src/talkpilot/__tests__/mongodb-client.spec.ts +18 -18
  86. package/src/talkpilot/__tests__/validation.spec.ts +68 -68
  87. package/src/talkpilot/agents/__tests__/agents.getters.spec.ts +29 -29
  88. package/src/talkpilot/agents/agents.getters.ts +34 -34
  89. package/src/talkpilot/agents/agents.types.ts +14 -14
  90. package/src/talkpilot/agents/index.ts +2 -2
  91. package/src/talkpilot/backgroundToolResults/__tests__/backgroundToolResults.getters.spec.ts +147 -147
  92. package/src/talkpilot/backgroundToolResults/backgroundToolResults.getters.ts +65 -65
  93. package/src/talkpilot/backgroundToolResults/backgroundToolResults.types.ts +23 -23
  94. package/src/talkpilot/backgroundToolResults/index.ts +2 -2
  95. package/src/talkpilot/callTokenUsage/__tests__/callTokenUsage.getters.spec.ts +57 -57
  96. package/src/talkpilot/callTokenUsage/callTokenUsage.getters.ts +42 -42
  97. package/src/talkpilot/callTokenUsage/callTokenUsage.types.ts +28 -28
  98. package/src/talkpilot/callTokenUsage/index.ts +2 -2
  99. package/src/talkpilot/calls/__tests__/callStats.utils.spec.ts +128 -128
  100. package/src/talkpilot/calls/__tests__/calls.dashboard.spec.ts +229 -229
  101. package/src/talkpilot/calls/__tests__/calls.spec.ts +519 -519
  102. package/src/talkpilot/calls/__tests__/calls.statistics.spec.ts +483 -483
  103. package/src/talkpilot/calls/calls.constants.ts +57 -57
  104. package/src/talkpilot/calls/calls.getters.ts +295 -295
  105. package/src/talkpilot/calls/calls.statistics.getters.ts +668 -668
  106. package/src/talkpilot/calls/calls.statistics.types.ts +48 -48
  107. package/src/talkpilot/calls/calls.types.ts +168 -168
  108. package/src/talkpilot/calls/dashboard/calls.dashboard.ts +295 -295
  109. package/src/talkpilot/calls/dashboard/calls.dashboard.types.ts +57 -57
  110. package/src/talkpilot/calls/index.ts +6 -6
  111. package/src/talkpilot/clientAudioBuffers/__tests__/clientAudioBuffer.getters.spec.ts +160 -160
  112. package/src/talkpilot/clientAudioBuffers/clientAudioBuffer.getters.ts +117 -117
  113. package/src/talkpilot/clientAudioBuffers/clientsAudioBuffers.types.ts +25 -25
  114. package/src/talkpilot/clientAudioBuffers/index.ts +2 -2
  115. package/src/talkpilot/clients/__tests__/clients.quota.spec.ts +123 -123
  116. package/src/talkpilot/clients/clients.getters.ts +19 -19
  117. package/src/talkpilot/clients/clients.quota.getters.ts +119 -119
  118. package/src/talkpilot/clients/clients.quota.types.ts +27 -27
  119. package/src/talkpilot/clients/clients.types.ts +18 -18
  120. package/src/talkpilot/clients/index.ts +4 -4
  121. package/src/talkpilot/clientsConfig/__tests__/clientsConfig.getters.spec.ts +423 -423
  122. package/src/talkpilot/clientsConfig/__tests__/clientsConfig.spec.ts +212 -212
  123. package/src/talkpilot/clientsConfig/clientsConfig.constants.ts +2 -2
  124. package/src/talkpilot/clientsConfig/clientsConfig.getters.ts +247 -247
  125. package/src/talkpilot/clientsConfig/clientsConfig.types.ts +137 -137
  126. package/src/talkpilot/clientsConfig/index.ts +3 -3
  127. package/src/talkpilot/contextNotes/__tests__/contextNotes.getters.spec.ts +217 -217
  128. package/src/talkpilot/contextNotes/contextNotes.getters.ts +85 -85
  129. package/src/talkpilot/contextNotes/contextNotes.types.ts +20 -20
  130. package/src/talkpilot/contextNotes/index.ts +2 -2
  131. package/src/talkpilot/flows/__tests__/flows.schema.spec.ts +163 -152
  132. package/src/talkpilot/flows/flows.getter.ts +34 -34
  133. package/src/talkpilot/flows/flows.schema.ts +203 -196
  134. package/src/talkpilot/flows/flows.types.ts +221 -220
  135. package/src/talkpilot/flows/index.ts +7 -7
  136. package/src/talkpilot/groups/__tests__/groups.spec.ts +90 -90
  137. package/src/talkpilot/groups/__tests__/phone.utils.spec.ts +32 -32
  138. package/src/talkpilot/groups/groups.getters.ts +30 -30
  139. package/src/talkpilot/groups/groups.types.ts +29 -29
  140. package/src/talkpilot/groups/index.ts +3 -3
  141. package/src/talkpilot/groups/phone.utils.ts +46 -46
  142. package/src/talkpilot/index.ts +32 -32
  143. package/src/talkpilot/leads/index.ts +2 -2
  144. package/src/talkpilot/leads/leads.getter.ts +6 -6
  145. package/src/talkpilot/leads/leads.schema.ts +33 -33
  146. package/src/talkpilot/leads/leads.types.ts +20 -20
  147. package/src/talkpilot/mongodb-client.ts +78 -78
  148. package/src/talkpilot/phone_numbers/__tests__/phone_numbers.spec.ts +530 -530
  149. package/src/talkpilot/phone_numbers/index.ts +2 -2
  150. package/src/talkpilot/phone_numbers/phone_numbers.getter.ts +240 -240
  151. package/src/talkpilot/phone_numbers/phone_numbers.schema.ts +17 -17
  152. package/src/talkpilot/phone_numbers/phone_numbers.types.ts +30 -30
  153. package/src/talkpilot/phone_numbers/phone_numbers.utils.ts +9 -9
  154. package/src/talkpilot/plans/__tests__/plans.spec.ts +70 -70
  155. package/src/talkpilot/plans/index.ts +2 -2
  156. package/src/talkpilot/plans/plans.getters.ts +132 -132
  157. package/src/talkpilot/plans/plans.types.ts +89 -89
  158. package/src/talkpilot/products/__tests__/products.getters.spec.ts +44 -44
  159. package/src/talkpilot/products/index.ts +2 -2
  160. package/src/talkpilot/products/products.getters.ts +12 -12
  161. package/src/talkpilot/products/products.types.ts +9 -9
  162. package/src/talkpilot/results/index.ts +7 -7
  163. package/src/talkpilot/results/results.getter.ts +39 -39
  164. package/src/talkpilot/results/results.schema.ts +25 -25
  165. package/src/talkpilot/results/results.types.ts +34 -34
  166. package/src/talkpilot/retry_analyze/__tests__/retryAnalyze.getters.spec.ts +156 -156
  167. package/src/talkpilot/retry_analyze/index.ts +2 -2
  168. package/src/talkpilot/retry_analyze/retryAnalyze.getters.ts +84 -84
  169. package/src/talkpilot/retry_analyze/retryAnalyze.types.ts +13 -13
  170. package/src/talkpilot/sessions/__tests__/sessions.spec.ts +147 -147
  171. package/src/talkpilot/sessions/index.ts +2 -2
  172. package/src/talkpilot/sessions/sessions.getter.ts +92 -92
  173. package/src/talkpilot/sessions/sessions.schema.ts +34 -34
  174. package/src/talkpilot/sessions/sessions.types.ts +30 -30
  175. package/src/talkpilot/subscriptions/__tests__/subscriptions.getters.utils.spec.ts +45 -45
  176. package/src/talkpilot/subscriptions/index.ts +3 -3
  177. package/src/talkpilot/subscriptions/subscriptions.getters.ts +146 -146
  178. package/src/talkpilot/subscriptions/subscriptions.getters.utils.ts +33 -33
  179. package/src/talkpilot/subscriptions/subscriptions.types.ts +66 -66
  180. package/src/talkpilot/utils/__tests__/query.utils.spec.ts +49 -49
  181. package/src/talkpilot/utils/query.utils.ts +21 -21
  182. package/src/test-utils/db-utils.ts +33 -33
  183. package/src/test-utils/factories/index.ts +19 -19
  184. package/src/test-utils/factories/municipal/cities.ts +16 -16
  185. package/src/test-utils/factories/municipal/departmentsSubjects.ts +37 -37
  186. package/src/test-utils/factories/municipal/muniIssues.ts +32 -32
  187. package/src/test-utils/factories/municipal/residents.ts +20 -20
  188. package/src/test-utils/factories/municipal/streets.ts +22 -22
  189. package/src/test-utils/factories/municipal/tickets.ts +39 -39
  190. package/src/test-utils/factories/talkpilot/agents.ts +19 -19
  191. package/src/test-utils/factories/talkpilot/calls.ts +37 -37
  192. package/src/test-utils/factories/talkpilot/clientAudioBuffers.ts +20 -20
  193. package/src/test-utils/factories/talkpilot/clientsConfig.ts +18 -18
  194. package/src/test-utils/factories/talkpilot/contextNotes.ts +40 -40
  195. package/src/test-utils/factories/talkpilot/flows.ts +33 -33
  196. package/src/test-utils/factories/talkpilot/groups.ts +33 -33
  197. package/src/test-utils/factories/talkpilot/phone_numbers.ts +22 -22
  198. package/src/test-utils/factories/talkpilot/sessions.ts +35 -35
  199. package/src/test-utils/factories/websitalk/knowledgeDocuments.ts +29 -29
  200. package/src/test-utils/factories/websitalk/scanResources.ts +26 -26
  201. package/src/test-utils/factories/websitalk/scans.ts +30 -30
  202. package/src/test-utils/factories/websitalk/websiteUrls.ts +17 -17
  203. package/src/utils/date.utils.ts +126 -126
  204. package/src/utils/shared.types.ts +4 -4
  205. package/src/utils/validation.ts +23 -23
  206. package/src/websitalk/index.ts +18 -18
  207. package/src/websitalk/knowledgeDocuments/__tests__/knowledgeDocuments.spec.ts +238 -238
  208. package/src/websitalk/knowledgeDocuments/index.ts +3 -3
  209. package/src/websitalk/knowledgeDocuments/knowledgeDocuments.constants.ts +26 -26
  210. package/src/websitalk/knowledgeDocuments/knowledgeDocuments.getters.ts +95 -95
  211. package/src/websitalk/knowledgeDocuments/knowledgeDocuments.types.ts +75 -75
  212. package/src/websitalk/mongodb-client.ts +61 -61
  213. package/src/websitalk/scanResources/__tests__/scanResources.spec.ts +267 -267
  214. package/src/websitalk/scanResources/index.ts +4 -4
  215. package/src/websitalk/scanResources/scanResources.constants.ts +60 -60
  216. package/src/websitalk/scanResources/scanResources.getters.ts +128 -128
  217. package/src/websitalk/scanResources/scanResources.types.ts +105 -105
  218. package/src/websitalk/scanResources/scanResources.utils.ts +31 -31
  219. package/src/websitalk/scans/__tests__/scans.spec.ts +362 -362
  220. package/src/websitalk/scans/index.ts +4 -4
  221. package/src/websitalk/scans/scans.constants.ts +18 -18
  222. package/src/websitalk/scans/scans.getters.ts +172 -172
  223. package/src/websitalk/scans/scans.types.ts +58 -58
  224. package/src/websitalk/scans/scans.utils.ts +5 -5
  225. package/src/websitalk/websiteUrls/__tests__/websiteUrls.spec.ts +77 -77
  226. package/src/websitalk/websiteUrls/index.ts +2 -2
  227. package/src/websitalk/websiteUrls/websiteUrls.getters.ts +65 -65
  228. package/src/websitalk/websiteUrls/websiteUrls.types.ts +13 -13
  229. package/tsconfig.json +23 -23
@@ -1,146 +1,146 @@
1
- import { getDb } from "../index";
2
- import {
3
- SimpleSubscriptionFilter,
4
- Subscription,
5
- SubscriptionDoc,
6
- SubscriptionFilter,
7
- SubscriptionQueryOptions,
8
- } from "./subscriptions.types";
9
- import { Filter, ObjectId } from "mongodb";
10
- import { buildSimpleQuery } from "./subscriptions.getters.utils";
11
- import { applyQueryOptions } from "../utils/query.utils";
12
-
13
- export const getSubscriptionsCollection = () => {
14
- return getDb().collection<Subscription>("subscriptions");
15
- };
16
-
17
- /**
18
- * A simple filter for Subscription: each field may be a value or a MongoDB operator expression
19
- */
20
-
21
- export const findSubscriptions = async (
22
- filter?: SubscriptionFilter,
23
- options?: SubscriptionQueryOptions,
24
- ): Promise<SubscriptionDoc[]> => {
25
- let query: Filter<Subscription> = {};
26
-
27
- if (filter) {
28
- const simpleFilter: SimpleSubscriptionFilter = {
29
- _id: filter._id,
30
- clientId: filter.clientId,
31
- planId: filter.planId,
32
- startDate: filter.startDate,
33
- endDate: filter.endDate,
34
- isActive: filter.isActive,
35
- createdAt: filter.createdAt,
36
- nextRenewalDate: filter.nextRenewalDate,
37
- };
38
- const baseQuery = buildSimpleQuery(simpleFilter);
39
- if (Object.keys(baseQuery).length > 0) {
40
- query = baseQuery;
41
- }
42
- if (filter.or && filter.or.length > 0) {
43
- query.$or = filter.or.map(buildSimpleQuery);
44
- }
45
- if (filter.and && filter.and.length > 0) {
46
- query.$and = filter.and.map(buildSimpleQuery);
47
- }
48
- }
49
-
50
- const cursor = getSubscriptionsCollection().find(query);
51
- return await applyQueryOptions(cursor, options).toArray();
52
- };
53
-
54
- export const countSubscriptions = async (
55
- query: Filter<Subscription>,
56
- ): Promise<number> => {
57
- return getSubscriptionsCollection().countDocuments(query);
58
- };
59
-
60
- export const createSubscriptionDoc = async (
61
- subscriptionData: Omit<Subscription, "createdAt" | "updatedAt">,
62
- ): Promise<SubscriptionDoc> => {
63
- const now = new Date();
64
- const result = await getSubscriptionsCollection().insertOne({
65
- ...subscriptionData,
66
- createdAt: now,
67
- updatedAt: now,
68
- });
69
-
70
- const subscriptions = await findSubscriptions({ _id: result.insertedId });
71
- if (!subscriptions[0])
72
- throw new Error("Failed to retrieve created subscription");
73
- return subscriptions[0];
74
- };
75
-
76
- export const updateSubscriptionDoc = async (
77
- subscriptionId: ObjectId,
78
- updates: Partial<Subscription>,
79
- ): Promise<SubscriptionDoc | null> => {
80
- return await getSubscriptionsCollection().findOneAndUpdate(
81
- { _id: subscriptionId },
82
- {
83
- $set: {
84
- ...updates,
85
- updatedAt: new Date(),
86
- },
87
- },
88
- {
89
- returnDocument: "after",
90
- },
91
- );
92
- };
93
-
94
- export const incrementSubscriptionUsage = async (
95
- subscriptionId: ObjectId,
96
- amount: number = 1,
97
- ): Promise<SubscriptionDoc | null> => {
98
- const fieldToIncrement = "cycles.$[activeCycle].usage.usedCalls";
99
- return await getSubscriptionsCollection().findOneAndUpdate(
100
- { _id: subscriptionId, "cycles.isActive": true },
101
- {
102
- $inc: { [fieldToIncrement]: amount },
103
- $set: { updatedAt: new Date() },
104
- },
105
- {
106
- arrayFilters: [{ "activeCycle.isActive": true }],
107
- returnDocument: "after",
108
- },
109
- );
110
- };
111
-
112
- export const deactivateCurrentSubscriptionCycle = async (
113
- subscriptionId: ObjectId,
114
- nextRenewalDate: Date,
115
- ): Promise<void> => {
116
- await getSubscriptionsCollection().updateOne(
117
- { _id: subscriptionId, "cycles.isActive": true },
118
- {
119
- $set: {
120
- "cycles.$[activeCycle].isActive": false,
121
- nextRenewalDate: nextRenewalDate,
122
- updatedAt: new Date(),
123
- },
124
- },
125
- {
126
- arrayFilters: [{ "activeCycle.isActive": true }],
127
- },
128
- );
129
- };
130
-
131
- export const pushNewSubscriptionCycle = async (
132
- subscriptionId: ObjectId,
133
- newCycle: Subscription["cycles"][number],
134
- ): Promise<SubscriptionDoc | null> => {
135
- return await getSubscriptionsCollection().findOneAndUpdate(
136
- { _id: subscriptionId },
137
- {
138
- $push: {
139
- cycles: newCycle,
140
- },
141
- },
142
- {
143
- returnDocument: "after",
144
- },
145
- );
146
- };
1
+ import { getDb } from "../index";
2
+ import {
3
+ SimpleSubscriptionFilter,
4
+ Subscription,
5
+ SubscriptionDoc,
6
+ SubscriptionFilter,
7
+ SubscriptionQueryOptions,
8
+ } from "./subscriptions.types";
9
+ import { Filter, ObjectId } from "mongodb";
10
+ import { buildSimpleQuery } from "./subscriptions.getters.utils";
11
+ import { applyQueryOptions } from "../utils/query.utils";
12
+
13
+ export const getSubscriptionsCollection = () => {
14
+ return getDb().collection<Subscription>("subscriptions");
15
+ };
16
+
17
+ /**
18
+ * A simple filter for Subscription: each field may be a value or a MongoDB operator expression
19
+ */
20
+
21
+ export const findSubscriptions = async (
22
+ filter?: SubscriptionFilter,
23
+ options?: SubscriptionQueryOptions,
24
+ ): Promise<SubscriptionDoc[]> => {
25
+ let query: Filter<Subscription> = {};
26
+
27
+ if (filter) {
28
+ const simpleFilter: SimpleSubscriptionFilter = {
29
+ _id: filter._id,
30
+ clientId: filter.clientId,
31
+ planId: filter.planId,
32
+ startDate: filter.startDate,
33
+ endDate: filter.endDate,
34
+ isActive: filter.isActive,
35
+ createdAt: filter.createdAt,
36
+ nextRenewalDate: filter.nextRenewalDate,
37
+ };
38
+ const baseQuery = buildSimpleQuery(simpleFilter);
39
+ if (Object.keys(baseQuery).length > 0) {
40
+ query = baseQuery;
41
+ }
42
+ if (filter.or && filter.or.length > 0) {
43
+ query.$or = filter.or.map(buildSimpleQuery);
44
+ }
45
+ if (filter.and && filter.and.length > 0) {
46
+ query.$and = filter.and.map(buildSimpleQuery);
47
+ }
48
+ }
49
+
50
+ const cursor = getSubscriptionsCollection().find(query);
51
+ return await applyQueryOptions(cursor, options).toArray();
52
+ };
53
+
54
+ export const countSubscriptions = async (
55
+ query: Filter<Subscription>,
56
+ ): Promise<number> => {
57
+ return getSubscriptionsCollection().countDocuments(query);
58
+ };
59
+
60
+ export const createSubscriptionDoc = async (
61
+ subscriptionData: Omit<Subscription, "createdAt" | "updatedAt">,
62
+ ): Promise<SubscriptionDoc> => {
63
+ const now = new Date();
64
+ const result = await getSubscriptionsCollection().insertOne({
65
+ ...subscriptionData,
66
+ createdAt: now,
67
+ updatedAt: now,
68
+ });
69
+
70
+ const subscriptions = await findSubscriptions({ _id: result.insertedId });
71
+ if (!subscriptions[0])
72
+ throw new Error("Failed to retrieve created subscription");
73
+ return subscriptions[0];
74
+ };
75
+
76
+ export const updateSubscriptionDoc = async (
77
+ subscriptionId: ObjectId,
78
+ updates: Partial<Subscription>,
79
+ ): Promise<SubscriptionDoc | null> => {
80
+ return await getSubscriptionsCollection().findOneAndUpdate(
81
+ { _id: subscriptionId },
82
+ {
83
+ $set: {
84
+ ...updates,
85
+ updatedAt: new Date(),
86
+ },
87
+ },
88
+ {
89
+ returnDocument: "after",
90
+ },
91
+ );
92
+ };
93
+
94
+ export const incrementSubscriptionUsage = async (
95
+ subscriptionId: ObjectId,
96
+ amount: number = 1,
97
+ ): Promise<SubscriptionDoc | null> => {
98
+ const fieldToIncrement = "cycles.$[activeCycle].usage.usedCalls";
99
+ return await getSubscriptionsCollection().findOneAndUpdate(
100
+ { _id: subscriptionId, "cycles.isActive": true },
101
+ {
102
+ $inc: { [fieldToIncrement]: amount },
103
+ $set: { updatedAt: new Date() },
104
+ },
105
+ {
106
+ arrayFilters: [{ "activeCycle.isActive": true }],
107
+ returnDocument: "after",
108
+ },
109
+ );
110
+ };
111
+
112
+ export const deactivateCurrentSubscriptionCycle = async (
113
+ subscriptionId: ObjectId,
114
+ nextRenewalDate: Date,
115
+ ): Promise<void> => {
116
+ await getSubscriptionsCollection().updateOne(
117
+ { _id: subscriptionId, "cycles.isActive": true },
118
+ {
119
+ $set: {
120
+ "cycles.$[activeCycle].isActive": false,
121
+ nextRenewalDate: nextRenewalDate,
122
+ updatedAt: new Date(),
123
+ },
124
+ },
125
+ {
126
+ arrayFilters: [{ "activeCycle.isActive": true }],
127
+ },
128
+ );
129
+ };
130
+
131
+ export const pushNewSubscriptionCycle = async (
132
+ subscriptionId: ObjectId,
133
+ newCycle: Subscription["cycles"][number],
134
+ ): Promise<SubscriptionDoc | null> => {
135
+ return await getSubscriptionsCollection().findOneAndUpdate(
136
+ { _id: subscriptionId },
137
+ {
138
+ $push: {
139
+ cycles: newCycle,
140
+ },
141
+ },
142
+ {
143
+ returnDocument: "after",
144
+ },
145
+ );
146
+ };
@@ -1,33 +1,33 @@
1
- import { ObjectId, Filter } from "mongodb";
2
- import {
3
- Subscription,
4
- SimpleSubscriptionFilter,
5
- simpleFields,
6
- SimpleField,
7
- } from "../index";
8
-
9
- export const buildSimpleQuery = (
10
- filter: SimpleSubscriptionFilter,
11
- ): Filter<Subscription> => {
12
- const query: Filter<Subscription> = {};
13
-
14
- if (filter._id) {
15
- query._id = new ObjectId(filter._id);
16
- }
17
-
18
- simpleFields.forEach((field: SimpleField) => {
19
- const val = filter[field];
20
- if (val !== undefined) {
21
- query[field] = val as any;
22
- }
23
- });
24
-
25
- return query;
26
- };
27
-
28
- export const buildDateRangeFilter = (from: Date, to: Date) => {
29
- return {
30
- $gte: from,
31
- $lt: to,
32
- };
33
- };
1
+ import { ObjectId, Filter } from "mongodb";
2
+ import {
3
+ Subscription,
4
+ SimpleSubscriptionFilter,
5
+ simpleFields,
6
+ SimpleField,
7
+ } from "../index";
8
+
9
+ export const buildSimpleQuery = (
10
+ filter: SimpleSubscriptionFilter,
11
+ ): Filter<Subscription> => {
12
+ const query: Filter<Subscription> = {};
13
+
14
+ if (filter._id) {
15
+ query._id = new ObjectId(filter._id);
16
+ }
17
+
18
+ simpleFields.forEach((field: SimpleField) => {
19
+ const val = filter[field];
20
+ if (val !== undefined) {
21
+ query[field] = val as any;
22
+ }
23
+ });
24
+
25
+ return query;
26
+ };
27
+
28
+ export const buildDateRangeFilter = (from: Date, to: Date) => {
29
+ return {
30
+ $gte: from,
31
+ $lt: to,
32
+ };
33
+ };
@@ -1,66 +1,66 @@
1
- import { Filter, ObjectId, WithId, Sort } from "mongodb";
2
- import { PlanDoc } from "../plans";
3
-
4
- export type SubscriptionCycle = {
5
- usage: {
6
- usedCalls: number;
7
- };
8
- startDate: Date;
9
- endDate: Date;
10
- cycleNumber: number;
11
- isActive: boolean;
12
- };
13
-
14
- export type Subscription = {
15
- clientId: string;
16
- planId: string;
17
- planSnapshot: PlanDoc;
18
- startDate: Date;
19
- endDate?: Date; // optional, if we want to predefine the end date
20
- isActive: boolean;
21
- createdAt: Date;
22
- updatedAt: Date;
23
- nextRenewalDate: Date;
24
- cycles: SubscriptionCycle[];
25
- };
26
-
27
- export type SubscriptionDoc = WithId<Subscription>;
28
- export type Subscriptions = SubscriptionDoc[];
29
-
30
- // Define which Subscription fields we allow in a simple filter
31
- export const simpleFields = [
32
- "clientId",
33
- "planId",
34
- "startDate",
35
- "endDate",
36
- "isActive",
37
- "createdAt",
38
- "nextRenewalDate",
39
- ] as const;
40
- export type SimpleField = (typeof simpleFields)[number];
41
-
42
- /**
43
- * A simple filter for Subscription: each field may be a literal or a MongoDB operator
44
- */
45
- export type SimpleSubscriptionFilter = {
46
- _id?: ObjectId | string;
47
- } & {
48
- [K in keyof Subscription]?: Filter<Subscription>[K];
49
- };
50
-
51
- /**
52
- * Composite filter allowing nested OR/AND arrays
53
- */
54
- export type SubscriptionFilter = SimpleSubscriptionFilter & {
55
- or?: SimpleSubscriptionFilter[];
56
- and?: SimpleSubscriptionFilter[];
57
- };
58
-
59
- /**
60
- * Query options for sorting and limiting results
61
- */
62
- export type SubscriptionQueryOptions = {
63
- sort?: Sort;
64
- skip?: number;
65
- limit?: number;
66
- };
1
+ import { Filter, ObjectId, WithId, Sort } from "mongodb";
2
+ import { PlanDoc } from "../plans";
3
+
4
+ export type SubscriptionCycle = {
5
+ usage: {
6
+ usedCalls: number;
7
+ };
8
+ startDate: Date;
9
+ endDate: Date;
10
+ cycleNumber: number;
11
+ isActive: boolean;
12
+ };
13
+
14
+ export type Subscription = {
15
+ clientId: string;
16
+ planId: string;
17
+ planSnapshot: PlanDoc;
18
+ startDate: Date;
19
+ endDate?: Date; // optional, if we want to predefine the end date
20
+ isActive: boolean;
21
+ createdAt: Date;
22
+ updatedAt: Date;
23
+ nextRenewalDate: Date;
24
+ cycles: SubscriptionCycle[];
25
+ };
26
+
27
+ export type SubscriptionDoc = WithId<Subscription>;
28
+ export type Subscriptions = SubscriptionDoc[];
29
+
30
+ // Define which Subscription fields we allow in a simple filter
31
+ export const simpleFields = [
32
+ "clientId",
33
+ "planId",
34
+ "startDate",
35
+ "endDate",
36
+ "isActive",
37
+ "createdAt",
38
+ "nextRenewalDate",
39
+ ] as const;
40
+ export type SimpleField = (typeof simpleFields)[number];
41
+
42
+ /**
43
+ * A simple filter for Subscription: each field may be a literal or a MongoDB operator
44
+ */
45
+ export type SimpleSubscriptionFilter = {
46
+ _id?: ObjectId | string;
47
+ } & {
48
+ [K in keyof Subscription]?: Filter<Subscription>[K];
49
+ };
50
+
51
+ /**
52
+ * Composite filter allowing nested OR/AND arrays
53
+ */
54
+ export type SubscriptionFilter = SimpleSubscriptionFilter & {
55
+ or?: SimpleSubscriptionFilter[];
56
+ and?: SimpleSubscriptionFilter[];
57
+ };
58
+
59
+ /**
60
+ * Query options for sorting and limiting results
61
+ */
62
+ export type SubscriptionQueryOptions = {
63
+ sort?: Sort;
64
+ skip?: number;
65
+ limit?: number;
66
+ };
@@ -1,49 +1,49 @@
1
- import { applyQueryOptions } from "../query.utils";
2
-
3
- describe("query.utils", () => {
4
- describe("applyQueryOptions()", () => {
5
- let mockCursor: any;
6
-
7
- beforeEach(() => {
8
- mockCursor = {
9
- sort: jest.fn().mockReturnThis(),
10
- skip: jest.fn().mockReturnThis(),
11
- limit: jest.fn().mockReturnThis(),
12
- };
13
- });
14
-
15
- it("should return cursor if no options provided", () => {
16
- const result = applyQueryOptions(mockCursor);
17
- expect(result).toBe(mockCursor);
18
- expect(mockCursor.sort).not.toHaveBeenCalled();
19
- });
20
-
21
- it("should apply sort, skip, and limit", () => {
22
- const options = {
23
- sort: { name: 1 },
24
- skip: 10,
25
- limit: 5,
26
- };
27
-
28
- const result = applyQueryOptions(mockCursor, options);
29
-
30
- expect(result).toBe(mockCursor);
31
- expect(mockCursor.sort).toHaveBeenCalledWith({ name: 1 });
32
- expect(mockCursor.skip).toHaveBeenCalledWith(10);
33
- expect(mockCursor.limit).toHaveBeenCalledWith(5);
34
- });
35
-
36
- it("should apply only sort if skip and limit are missing", () => {
37
- const options = {
38
- sort: { name: 1 },
39
- };
40
-
41
- const result = applyQueryOptions(mockCursor, options);
42
-
43
- expect(result).toBe(mockCursor);
44
- expect(mockCursor.sort).toHaveBeenCalledWith({ name: 1 });
45
- expect(mockCursor.skip).not.toHaveBeenCalled();
46
- expect(mockCursor.limit).not.toHaveBeenCalled();
47
- });
48
- });
49
- });
1
+ import { applyQueryOptions } from "../query.utils";
2
+
3
+ describe("query.utils", () => {
4
+ describe("applyQueryOptions()", () => {
5
+ let mockCursor: any;
6
+
7
+ beforeEach(() => {
8
+ mockCursor = {
9
+ sort: jest.fn().mockReturnThis(),
10
+ skip: jest.fn().mockReturnThis(),
11
+ limit: jest.fn().mockReturnThis(),
12
+ };
13
+ });
14
+
15
+ it("should return cursor if no options provided", () => {
16
+ const result = applyQueryOptions(mockCursor);
17
+ expect(result).toBe(mockCursor);
18
+ expect(mockCursor.sort).not.toHaveBeenCalled();
19
+ });
20
+
21
+ it("should apply sort, skip, and limit", () => {
22
+ const options = {
23
+ sort: { name: 1 },
24
+ skip: 10,
25
+ limit: 5,
26
+ };
27
+
28
+ const result = applyQueryOptions(mockCursor, options);
29
+
30
+ expect(result).toBe(mockCursor);
31
+ expect(mockCursor.sort).toHaveBeenCalledWith({ name: 1 });
32
+ expect(mockCursor.skip).toHaveBeenCalledWith(10);
33
+ expect(mockCursor.limit).toHaveBeenCalledWith(5);
34
+ });
35
+
36
+ it("should apply only sort if skip and limit are missing", () => {
37
+ const options = {
38
+ sort: { name: 1 },
39
+ };
40
+
41
+ const result = applyQueryOptions(mockCursor, options);
42
+
43
+ expect(result).toBe(mockCursor);
44
+ expect(mockCursor.sort).toHaveBeenCalledWith({ name: 1 });
45
+ expect(mockCursor.skip).not.toHaveBeenCalled();
46
+ expect(mockCursor.limit).not.toHaveBeenCalled();
47
+ });
48
+ });
49
+ });
@@ -1,21 +1,21 @@
1
- import { FindCursor } from "mongodb";
2
-
3
- export type QueryOptions = {
4
- sort?: any;
5
- skip?: number;
6
- limit?: number;
7
- };
8
-
9
- export const applyQueryOptions = <T>(
10
- cursor: FindCursor<T>,
11
- options?: QueryOptions,
12
- ): FindCursor<T> => {
13
- if (!options) return cursor;
14
-
15
- let result = cursor;
16
- if (options.sort) result = result.sort(options.sort);
17
- if (options.skip) result = result.skip(options.skip);
18
- if (options.limit) result = result.limit(options.limit);
19
-
20
- return result;
21
- };
1
+ import { FindCursor } from "mongodb";
2
+
3
+ export type QueryOptions = {
4
+ sort?: any;
5
+ skip?: number;
6
+ limit?: number;
7
+ };
8
+
9
+ export const applyQueryOptions = <T>(
10
+ cursor: FindCursor<T>,
11
+ options?: QueryOptions,
12
+ ): FindCursor<T> => {
13
+ if (!options) return cursor;
14
+
15
+ let result = cursor;
16
+ if (options.sort) result = result.sort(options.sort);
17
+ if (options.skip) result = result.skip(options.skip);
18
+ if (options.limit) result = result.limit(options.limit);
19
+
20
+ return result;
21
+ };