@intlayer/backend 3.5.5 → 3.5.6

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 (244) hide show
  1. package/dist/cjs/controllers/ai.controller.cjs +159 -7
  2. package/dist/cjs/controllers/ai.controller.cjs.map +1 -1
  3. package/dist/cjs/controllers/dictionary.controller.cjs +50 -17
  4. package/dist/cjs/controllers/dictionary.controller.cjs.map +1 -1
  5. package/dist/cjs/controllers/project.controller.cjs +2 -6
  6. package/dist/cjs/controllers/project.controller.cjs.map +1 -1
  7. package/dist/cjs/controllers/tag.controller.cjs +235 -0
  8. package/dist/cjs/controllers/tag.controller.cjs.map +1 -0
  9. package/dist/cjs/controllers/user.controller.cjs +1 -0
  10. package/dist/cjs/controllers/user.controller.cjs.map +1 -1
  11. package/dist/cjs/export.cjs.map +1 -1
  12. package/dist/cjs/index.cjs +2 -0
  13. package/dist/cjs/index.cjs.map +1 -1
  14. package/dist/cjs/models/tag.model.cjs +31 -0
  15. package/dist/cjs/models/tag.model.cjs.map +1 -0
  16. package/dist/cjs/routes/ai.routes.cjs +31 -4
  17. package/dist/cjs/routes/ai.routes.cjs.map +1 -1
  18. package/dist/cjs/routes/dictionary.routes.cjs +4 -4
  19. package/dist/cjs/routes/dictionary.routes.cjs.map +1 -1
  20. package/dist/cjs/routes/tags.routes.cjs +60 -0
  21. package/dist/cjs/routes/tags.routes.cjs.map +1 -0
  22. package/dist/cjs/schemas/dictionary.schema.cjs +31 -6
  23. package/dist/cjs/schemas/dictionary.schema.cjs.map +1 -1
  24. package/dist/cjs/schemas/project.schema.cjs +5 -0
  25. package/dist/cjs/schemas/project.schema.cjs.map +1 -1
  26. package/dist/cjs/schemas/tag.schema.cjs +64 -0
  27. package/dist/cjs/schemas/tag.schema.cjs.map +1 -0
  28. package/dist/cjs/services/dictionary.service.cjs +139 -27
  29. package/dist/cjs/services/dictionary.service.cjs.map +1 -1
  30. package/dist/cjs/services/project.service.cjs +14 -5
  31. package/dist/cjs/services/project.service.cjs.map +1 -1
  32. package/dist/cjs/services/tag.service.cjs +91 -0
  33. package/dist/cjs/services/tag.service.cjs.map +1 -0
  34. package/dist/cjs/types/dictionary.types.cjs.map +1 -1
  35. package/dist/cjs/types/tag.types.cjs +17 -0
  36. package/dist/cjs/types/tag.types.cjs.map +1 -0
  37. package/dist/cjs/utils/{audit → auditDictionary}/PROMPT.md +6 -1
  38. package/dist/cjs/utils/{audit → auditDictionary}/index.cjs +10 -8
  39. package/dist/cjs/utils/auditDictionary/index.cjs.map +1 -0
  40. package/dist/cjs/utils/auditDictionaryField/PROMPT.md +115 -0
  41. package/dist/cjs/utils/auditDictionaryField/index.cjs +81 -0
  42. package/dist/cjs/utils/auditDictionaryField/index.cjs.map +1 -0
  43. package/dist/cjs/utils/auditDictionaryMetadata/PROMPT.md +76 -0
  44. package/dist/cjs/utils/auditDictionaryMetadata/index.cjs +78 -0
  45. package/dist/cjs/utils/auditDictionaryMetadata/index.cjs.map +1 -0
  46. package/dist/cjs/utils/auditTag/PROMPT.md +34 -0
  47. package/dist/cjs/utils/auditTag/index.cjs +74 -0
  48. package/dist/cjs/utils/auditTag/index.cjs.map +1 -0
  49. package/dist/cjs/utils/ensureArrayQueryFilter.cjs +39 -0
  50. package/dist/cjs/utils/ensureArrayQueryFilter.cjs.map +1 -0
  51. package/dist/cjs/utils/ensureMongoDocumentToObject.cjs +35 -0
  52. package/dist/cjs/utils/ensureMongoDocumentToObject.cjs.map +1 -0
  53. package/dist/cjs/utils/errors/errorCodes.cjs +91 -0
  54. package/dist/cjs/utils/errors/errorCodes.cjs.map +1 -1
  55. package/dist/cjs/utils/filtersAndPagination/getDictionaryFiltersAndPagination.cjs +10 -11
  56. package/dist/cjs/utils/filtersAndPagination/getDictionaryFiltersAndPagination.cjs.map +1 -1
  57. package/dist/cjs/utils/filtersAndPagination/getOrganizationFiltersAndPagination.cjs +6 -10
  58. package/dist/cjs/utils/filtersAndPagination/getOrganizationFiltersAndPagination.cjs.map +1 -1
  59. package/dist/cjs/utils/filtersAndPagination/getProjectFiltersAndPagination.cjs +6 -10
  60. package/dist/cjs/utils/filtersAndPagination/getProjectFiltersAndPagination.cjs.map +1 -1
  61. package/dist/cjs/utils/filtersAndPagination/getTagFiltersAndPagination.cjs +51 -0
  62. package/dist/cjs/utils/filtersAndPagination/getTagFiltersAndPagination.cjs.map +1 -0
  63. package/dist/cjs/utils/filtersAndPagination/getUserFiltersAndPagination.cjs +2 -9
  64. package/dist/cjs/utils/filtersAndPagination/getUserFiltersAndPagination.cjs.map +1 -1
  65. package/dist/cjs/utils/mapper/dictionary.cjs +8 -4
  66. package/dist/cjs/utils/mapper/dictionary.cjs.map +1 -1
  67. package/dist/cjs/utils/mapper/organization.cjs +2 -4
  68. package/dist/cjs/utils/mapper/organization.cjs.map +1 -1
  69. package/dist/cjs/utils/mapper/project.cjs +2 -4
  70. package/dist/cjs/utils/mapper/project.cjs.map +1 -1
  71. package/dist/cjs/utils/mapper/tag.cjs +37 -0
  72. package/dist/cjs/utils/mapper/tag.cjs.map +1 -0
  73. package/dist/cjs/utils/mapper/user.cjs +2 -4
  74. package/dist/cjs/utils/mapper/user.cjs.map +1 -1
  75. package/dist/cjs/utils/removeObjectKeys.cjs +35 -0
  76. package/dist/cjs/utils/removeObjectKeys.cjs.map +1 -0
  77. package/dist/cjs/utils/validation/validateProject.cjs +17 -10
  78. package/dist/cjs/utils/validation/validateProject.cjs.map +1 -1
  79. package/dist/cjs/utils/validation/validateTag.cjs +93 -0
  80. package/dist/cjs/utils/validation/validateTag.cjs.map +1 -0
  81. package/dist/esm/controllers/ai.controller.mjs +155 -6
  82. package/dist/esm/controllers/ai.controller.mjs.map +1 -1
  83. package/dist/esm/controllers/dictionary.controller.mjs +50 -17
  84. package/dist/esm/controllers/dictionary.controller.mjs.map +1 -1
  85. package/dist/esm/controllers/project.controller.mjs +2 -6
  86. package/dist/esm/controllers/project.controller.mjs.map +1 -1
  87. package/dist/esm/controllers/tag.controller.mjs +203 -0
  88. package/dist/esm/controllers/tag.controller.mjs.map +1 -0
  89. package/dist/esm/controllers/user.controller.mjs +1 -0
  90. package/dist/esm/controllers/user.controller.mjs.map +1 -1
  91. package/dist/esm/export.mjs.map +1 -1
  92. package/dist/esm/index.mjs +2 -0
  93. package/dist/esm/index.mjs.map +1 -1
  94. package/dist/esm/models/tag.model.mjs +7 -0
  95. package/dist/esm/models/tag.model.mjs.map +1 -0
  96. package/dist/esm/routes/ai.routes.mjs +37 -5
  97. package/dist/esm/routes/ai.routes.mjs.map +1 -1
  98. package/dist/esm/routes/dictionary.routes.mjs +4 -4
  99. package/dist/esm/routes/dictionary.routes.mjs.map +1 -1
  100. package/dist/esm/routes/tags.routes.mjs +40 -0
  101. package/dist/esm/routes/tags.routes.mjs.map +1 -0
  102. package/dist/esm/schemas/dictionary.schema.mjs +31 -6
  103. package/dist/esm/schemas/dictionary.schema.mjs.map +1 -1
  104. package/dist/esm/schemas/project.schema.mjs +5 -0
  105. package/dist/esm/schemas/project.schema.mjs.map +1 -1
  106. package/dist/esm/schemas/tag.schema.mjs +45 -0
  107. package/dist/esm/schemas/tag.schema.mjs.map +1 -0
  108. package/dist/esm/services/dictionary.service.mjs +137 -27
  109. package/dist/esm/services/dictionary.service.mjs.map +1 -1
  110. package/dist/esm/services/project.service.mjs +14 -5
  111. package/dist/esm/services/project.service.mjs.map +1 -1
  112. package/dist/esm/services/tag.service.mjs +61 -0
  113. package/dist/esm/services/tag.service.mjs.map +1 -0
  114. package/dist/esm/types/tag.types.mjs +1 -0
  115. package/dist/esm/types/tag.types.mjs.map +1 -0
  116. package/dist/esm/utils/{audit → auditDictionary}/PROMPT.md +6 -1
  117. package/dist/esm/utils/{audit → auditDictionary}/index.mjs +6 -4
  118. package/dist/esm/utils/auditDictionary/index.mjs.map +1 -0
  119. package/dist/esm/utils/auditDictionaryField/PROMPT.md +115 -0
  120. package/dist/esm/utils/auditDictionaryField/index.mjs +56 -0
  121. package/dist/esm/utils/auditDictionaryField/index.mjs.map +1 -0
  122. package/dist/esm/utils/auditDictionaryMetadata/PROMPT.md +76 -0
  123. package/dist/esm/utils/auditDictionaryMetadata/index.mjs +53 -0
  124. package/dist/esm/utils/auditDictionaryMetadata/index.mjs.map +1 -0
  125. package/dist/esm/utils/auditTag/PROMPT.md +34 -0
  126. package/dist/esm/utils/auditTag/index.mjs +49 -0
  127. package/dist/esm/utils/auditTag/index.mjs.map +1 -0
  128. package/dist/esm/utils/ensureArrayQueryFilter.mjs +15 -0
  129. package/dist/esm/utils/ensureArrayQueryFilter.mjs.map +1 -0
  130. package/dist/esm/utils/ensureMongoDocumentToObject.mjs +11 -0
  131. package/dist/esm/utils/ensureMongoDocumentToObject.mjs.map +1 -0
  132. package/dist/esm/utils/errors/errorCodes.mjs +91 -0
  133. package/dist/esm/utils/errors/errorCodes.mjs.map +1 -1
  134. package/dist/esm/utils/filtersAndPagination/getDictionaryFiltersAndPagination.mjs +10 -11
  135. package/dist/esm/utils/filtersAndPagination/getDictionaryFiltersAndPagination.mjs.map +1 -1
  136. package/dist/esm/utils/filtersAndPagination/getOrganizationFiltersAndPagination.mjs +6 -10
  137. package/dist/esm/utils/filtersAndPagination/getOrganizationFiltersAndPagination.mjs.map +1 -1
  138. package/dist/esm/utils/filtersAndPagination/getProjectFiltersAndPagination.mjs +6 -10
  139. package/dist/esm/utils/filtersAndPagination/getProjectFiltersAndPagination.mjs.map +1 -1
  140. package/dist/esm/utils/filtersAndPagination/getTagFiltersAndPagination.mjs +29 -0
  141. package/dist/esm/utils/filtersAndPagination/getTagFiltersAndPagination.mjs.map +1 -0
  142. package/dist/esm/utils/filtersAndPagination/getUserFiltersAndPagination.mjs +2 -9
  143. package/dist/esm/utils/filtersAndPagination/getUserFiltersAndPagination.mjs.map +1 -1
  144. package/dist/esm/utils/mapper/dictionary.mjs +8 -4
  145. package/dist/esm/utils/mapper/dictionary.mjs.map +1 -1
  146. package/dist/esm/utils/mapper/organization.mjs +2 -4
  147. package/dist/esm/utils/mapper/organization.mjs.map +1 -1
  148. package/dist/esm/utils/mapper/project.mjs +2 -4
  149. package/dist/esm/utils/mapper/project.mjs.map +1 -1
  150. package/dist/esm/utils/mapper/tag.mjs +12 -0
  151. package/dist/esm/utils/mapper/tag.mjs.map +1 -0
  152. package/dist/esm/utils/mapper/user.mjs +2 -4
  153. package/dist/esm/utils/mapper/user.mjs.map +1 -1
  154. package/dist/esm/utils/removeObjectKeys.mjs +11 -0
  155. package/dist/esm/utils/removeObjectKeys.mjs.map +1 -0
  156. package/dist/esm/utils/validation/validateProject.mjs +17 -10
  157. package/dist/esm/utils/validation/validateProject.mjs.map +1 -1
  158. package/dist/esm/utils/validation/validateTag.mjs +65 -0
  159. package/dist/esm/utils/validation/validateTag.mjs.map +1 -0
  160. package/dist/types/controllers/ai.controller.d.ts +45 -4
  161. package/dist/types/controllers/ai.controller.d.ts.map +1 -1
  162. package/dist/types/controllers/dictionary.controller.d.ts +6 -3
  163. package/dist/types/controllers/dictionary.controller.d.ts.map +1 -1
  164. package/dist/types/controllers/project.controller.d.ts +2 -4
  165. package/dist/types/controllers/project.controller.d.ts.map +1 -1
  166. package/dist/types/controllers/tag.controller.d.ts +39 -0
  167. package/dist/types/controllers/tag.controller.d.ts.map +1 -0
  168. package/dist/types/controllers/user.controller.d.ts.map +1 -1
  169. package/dist/types/export.d.ts +2 -0
  170. package/dist/types/export.d.ts.map +1 -1
  171. package/dist/types/index.d.ts.map +1 -1
  172. package/dist/types/models/dictionary.model.d.ts +1 -0
  173. package/dist/types/models/dictionary.model.d.ts.map +1 -1
  174. package/dist/types/models/tag.model.d.ts +3 -0
  175. package/dist/types/models/tag.model.d.ts.map +1 -0
  176. package/dist/types/routes/ai.routes.d.ts +16 -1
  177. package/dist/types/routes/ai.routes.d.ts.map +1 -1
  178. package/dist/types/routes/dictionary.routes.d.ts +6 -2
  179. package/dist/types/routes/dictionary.routes.d.ts.map +1 -1
  180. package/dist/types/routes/tags.routes.d.ts +29 -0
  181. package/dist/types/routes/tags.routes.d.ts.map +1 -0
  182. package/dist/types/schemas/dictionary.schema.d.ts +1 -0
  183. package/dist/types/schemas/dictionary.schema.d.ts.map +1 -1
  184. package/dist/types/schemas/project.schema.d.ts.map +1 -1
  185. package/dist/types/schemas/tag.schema.d.ts +16 -0
  186. package/dist/types/schemas/tag.schema.d.ts.map +1 -0
  187. package/dist/types/services/dictionary.service.d.ts +10 -2
  188. package/dist/types/services/dictionary.service.d.ts.map +1 -1
  189. package/dist/types/services/project.service.d.ts.map +1 -1
  190. package/dist/types/services/tag.service.d.ts +45 -0
  191. package/dist/types/services/tag.service.d.ts.map +1 -0
  192. package/dist/types/types/dictionary.types.d.ts +14 -4
  193. package/dist/types/types/dictionary.types.d.ts.map +1 -1
  194. package/dist/types/types/tag.types.d.ts +26 -0
  195. package/dist/types/types/tag.types.d.ts.map +1 -0
  196. package/dist/types/utils/auditDictionary/index.d.ts +24 -0
  197. package/dist/types/utils/auditDictionary/index.d.ts.map +1 -0
  198. package/dist/types/utils/auditDictionaryField/index.d.ts +24 -0
  199. package/dist/types/utils/auditDictionaryField/index.d.ts.map +1 -0
  200. package/dist/types/utils/auditDictionaryMetadata/index.d.ts +21 -0
  201. package/dist/types/utils/auditDictionaryMetadata/index.d.ts.map +1 -0
  202. package/dist/types/utils/auditTag/index.d.ts +27 -0
  203. package/dist/types/utils/auditTag/index.d.ts.map +1 -0
  204. package/dist/types/utils/ensureArrayQueryFilter.d.ts +2 -0
  205. package/dist/types/utils/ensureArrayQueryFilter.d.ts.map +1 -0
  206. package/dist/types/utils/ensureMongoDocumentToObject.d.ts +8 -0
  207. package/dist/types/utils/ensureMongoDocumentToObject.d.ts.map +1 -0
  208. package/dist/types/utils/errors/errorCodes.d.ts +91 -0
  209. package/dist/types/utils/errors/errorCodes.d.ts.map +1 -1
  210. package/dist/types/utils/filtersAndPagination/getDictionaryFiltersAndPagination.d.ts +2 -0
  211. package/dist/types/utils/filtersAndPagination/getDictionaryFiltersAndPagination.d.ts.map +1 -1
  212. package/dist/types/utils/filtersAndPagination/getOrganizationFiltersAndPagination.d.ts.map +1 -1
  213. package/dist/types/utils/filtersAndPagination/getProjectFiltersAndPagination.d.ts.map +1 -1
  214. package/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts +24 -0
  215. package/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts.map +1 -0
  216. package/dist/types/utils/filtersAndPagination/getUserFiltersAndPagination.d.ts.map +1 -1
  217. package/dist/types/utils/mapper/dictionary.d.ts +1 -1
  218. package/dist/types/utils/mapper/dictionary.d.ts.map +1 -1
  219. package/dist/types/utils/mapper/organization.d.ts.map +1 -1
  220. package/dist/types/utils/mapper/project.d.ts.map +1 -1
  221. package/dist/types/utils/mapper/tag.d.ts +16 -0
  222. package/dist/types/utils/mapper/tag.d.ts.map +1 -0
  223. package/dist/types/utils/mapper/user.d.ts +2 -2
  224. package/dist/types/utils/mapper/user.d.ts.map +1 -1
  225. package/dist/types/utils/removeObjectKeys.d.ts +2 -0
  226. package/dist/types/utils/removeObjectKeys.d.ts.map +1 -0
  227. package/dist/types/utils/validation/validateProject.d.ts.map +1 -1
  228. package/dist/types/utils/validation/validateTag.d.ts +16 -0
  229. package/dist/types/utils/validation/validateTag.d.ts.map +1 -0
  230. package/package.json +2 -3
  231. package/dist/cjs/utils/audit/index.cjs.map +0 -1
  232. package/dist/esm/utils/audit/index.mjs.map +0 -1
  233. /package/dist/cjs/utils/{audit → auditDictionary}/CJS_FORMAT.md +0 -0
  234. /package/dist/cjs/utils/{audit → auditDictionary}/JSON_FORMAT.md +0 -0
  235. /package/dist/cjs/utils/{audit → auditDictionary}/JSX_FORMAT.md +0 -0
  236. /package/dist/cjs/utils/{audit → auditDictionary}/MJS_FORMAT.md +0 -0
  237. /package/dist/cjs/utils/{audit → auditDictionary}/TSX_FORMAT.md +0 -0
  238. /package/dist/cjs/utils/{audit → auditDictionary}/TS_FORMAT.md +0 -0
  239. /package/dist/esm/utils/{audit → auditDictionary}/CJS_FORMAT.md +0 -0
  240. /package/dist/esm/utils/{audit → auditDictionary}/JSON_FORMAT.md +0 -0
  241. /package/dist/esm/utils/{audit → auditDictionary}/JSX_FORMAT.md +0 -0
  242. /package/dist/esm/utils/{audit → auditDictionary}/MJS_FORMAT.md +0 -0
  243. /package/dist/esm/utils/{audit → auditDictionary}/TSX_FORMAT.md +0 -0
  244. /package/dist/esm/utils/{audit → auditDictionary}/TS_FORMAT.md +0 -0
@@ -1,34 +1,95 @@
1
1
  import { DictionaryModel } from './../models/dictionary.model.mjs';
2
+ import { ensureMongoDocumentToObject } from './../utils/ensureMongoDocumentToObject.mjs';
2
3
  import { GenericError } from './../utils/errors/index.mjs';
4
+ import { removeObjectKeys } from './../utils/removeObjectKeys.mjs';
3
5
  import {
4
6
  validateDictionary
5
7
  } from './../utils/validation/validateDictionary.mjs';
6
- const findDictionaries = async (filters, skip = 0, limit = 100) => await DictionaryModel.find(filters).skip(skip).limit(limit);
8
+ const findDictionaries = async (filters, skip = 0, limit = 100) => {
9
+ try {
10
+ const dictionaries = await DictionaryModel.aggregate([
11
+ // Stage 1: Match the filters
12
+ { $match: filters },
13
+ // Stage 2: Skip for pagination
14
+ { $skip: skip },
15
+ // Stage 3: Limit the number of documents
16
+ { $limit: limit },
17
+ // Stage 4: Add the 'availableVersions' field
18
+ {
19
+ $addFields: {
20
+ availableVersions: {
21
+ $map: {
22
+ input: { $objectToArray: "$content" },
23
+ as: "version",
24
+ in: "$$version.k"
25
+ }
26
+ }
27
+ }
28
+ }
29
+ // (Optional) Stage 5: Project the fields you want to include/exclude
30
+ // For example, to exclude the entire 'content' field and keep only 'availableVersions'
31
+ // {
32
+ // $project: {
33
+ // content: 0 // Exclude the 'content' field
34
+ // }
35
+ // }
36
+ ]);
37
+ return dictionaries;
38
+ } catch (error) {
39
+ console.error("Error fetching dictionaries:", error);
40
+ throw error;
41
+ }
42
+ };
7
43
  const getDictionaryById = async (dictionaryId) => {
8
- const dictionary = await DictionaryModel.findById(dictionaryId);
9
- if (!dictionary) {
44
+ const dictionary = await DictionaryModel.aggregate([
45
+ // Stage 1: Match the document by ID
46
+ { $match: { _id: dictionaryId } },
47
+ // Stage 2: Add the 'availableVersions' field
48
+ {
49
+ $addFields: {
50
+ availableVersions: {
51
+ $map: {
52
+ input: { $objectToArray: "$content" },
53
+ as: "version",
54
+ in: "$$version.k"
55
+ }
56
+ }
57
+ }
58
+ }
59
+ ]);
60
+ if (!dictionary.length) {
10
61
  throw new GenericError("DICTIONARY_NOT_FOUND", { dictionaryId });
11
62
  }
12
- return dictionary;
63
+ return dictionary[0];
13
64
  };
14
65
  const getDictionaryByKey = async (dictionaryKey, projectId) => {
15
- const dictionary = await DictionaryModel.findOne({
16
- key: dictionaryKey,
17
- projectIds: projectId
18
- });
19
- if (!dictionary) {
66
+ const dictionaries = await getDictionariesByKeys([dictionaryKey], projectId);
67
+ console.log("dictionaries", dictionaries);
68
+ return dictionaries[0];
69
+ };
70
+ const getDictionariesByKeys = async (dictionaryKeys, projectId) => {
71
+ const dictionaries = await DictionaryModel.aggregate([
72
+ // Stage 1: Match the document by key
73
+ { $match: { key: { $in: dictionaryKeys }, projectIds: projectId } },
74
+ // Stage 2: Add the 'availableVersions' field
75
+ {
76
+ $addFields: {
77
+ availableVersions: {
78
+ $map: {
79
+ input: { $objectToArray: "$content" },
80
+ as: "version",
81
+ in: "$$version.k"
82
+ }
83
+ }
84
+ }
85
+ }
86
+ ]);
87
+ if (!dictionaries) {
20
88
  throw new GenericError("DICTIONARY_NOT_FOUND", {
21
- dictionaryKey,
89
+ dictionaryKeys,
22
90
  projectId
23
91
  });
24
92
  }
25
- return dictionary;
26
- };
27
- const getDictionariesByKeys = async (dictionaryKey, projectId) => {
28
- const dictionaries = await DictionaryModel.find({
29
- key: dictionaryKey,
30
- projectIds: projectId
31
- });
32
93
  return dictionaries;
33
94
  };
34
95
  const getDictionariesKeys = async (projectId) => {
@@ -37,6 +98,30 @@ const getDictionariesKeys = async (projectId) => {
37
98
  }).select("key");
38
99
  return dictionaries.map((dictionary) => dictionary.key);
39
100
  };
101
+ const getDictionariesByTags = async (tags, projectId) => {
102
+ const dictionaries = await DictionaryModel.aggregate([
103
+ // Stage 1: Match the document by tags
104
+ {
105
+ $match: {
106
+ tags: { $in: tags },
107
+ projectIds: projectId
108
+ }
109
+ },
110
+ // Stage 2: Add the 'availableVersions' field
111
+ {
112
+ $addFields: {
113
+ availableVersions: {
114
+ $map: {
115
+ input: { $objectToArray: "$content" },
116
+ as: "version",
117
+ in: "$$version.k"
118
+ }
119
+ }
120
+ }
121
+ }
122
+ ]);
123
+ return dictionaries;
124
+ };
40
125
  const countDictionaries = async (filters) => {
41
126
  const result = await DictionaryModel.countDocuments(filters);
42
127
  if (typeof result === "undefined") {
@@ -73,27 +158,31 @@ const getExistingDictionaryKey = async (dictionariesKeys, projectId) => {
73
158
  return { existingDictionariesKey, newDictionariesKey };
74
159
  };
75
160
  const updateDictionaryById = async (dictionaryId, dictionary) => {
76
- const updatedKeys = Object.keys(dictionary);
77
- const errors = validateDictionary(dictionary, updatedKeys);
161
+ const dictionaryObject = ensureMongoDocumentToObject(dictionary);
162
+ const dictionaryToUpdate = removeObjectKeys(dictionaryObject, ["_id"]);
163
+ const updatedKeys = Object.keys(dictionaryToUpdate);
164
+ const errors = await validateDictionary(dictionaryToUpdate, updatedKeys);
78
165
  if (Object.keys(errors).length > 0) {
79
166
  throw new GenericError("DICTIONARY_INVALID_FIELDS", {
80
167
  dictionaryId,
81
168
  errors
82
169
  });
83
170
  }
84
- const existingDictionary = await getDictionaryById(dictionaryId);
85
171
  const result = await DictionaryModel.updateOne(
86
172
  { _id: dictionaryId },
87
- { ...dictionary, content: [existingDictionary.content, dictionary.content] }
173
+ dictionaryToUpdate
88
174
  );
89
175
  if (result.matchedCount === 0) {
90
176
  throw new GenericError("DICTIONARY_UPDATE_FAILED", { dictionaryId });
91
177
  }
92
- return await getDictionaryById(dictionaryId);
178
+ const updatedDictionary = await getDictionaryById(dictionaryId);
179
+ return updatedDictionary;
93
180
  };
94
181
  const updateDictionaryByKey = async (dictionaryKey, dictionary, projectId) => {
95
- const updatedKeys = Object.keys(dictionary);
96
- const errors = validateDictionary(dictionary, updatedKeys);
182
+ const dictionaryObject = ensureMongoDocumentToObject(dictionary);
183
+ const dictionaryToUpdate = removeObjectKeys(dictionaryObject, ["_id"]);
184
+ const updatedKeys = Object.keys(dictionaryToUpdate);
185
+ const errors = await validateDictionary(dictionaryToUpdate, updatedKeys);
97
186
  if (Object.keys(errors).length > 0) {
98
187
  throw new GenericError("DICTIONARY_INVALID_FIELDS", {
99
188
  dictionaryKey,
@@ -101,15 +190,15 @@ const updateDictionaryByKey = async (dictionaryKey, dictionary, projectId) => {
101
190
  errors
102
191
  });
103
192
  }
104
- const existingDictionary = await getDictionaryByKey(dictionaryKey, projectId);
105
193
  const result = await DictionaryModel.updateOne(
106
194
  { key: dictionaryKey, projectIds: projectId },
107
- { ...dictionary, content: [existingDictionary.content, dictionary.content] }
195
+ dictionaryToUpdate
108
196
  );
109
197
  if (result.matchedCount === 0) {
110
198
  throw new GenericError("DICTIONARY_UPDATE_FAILED", { dictionaryKey });
111
199
  }
112
- return await getDictionaryByKey(dictionaryKey, projectId);
200
+ const updatedDictionary = await getDictionaryByKey(dictionaryKey, projectId);
201
+ return updatedDictionary;
113
202
  };
114
203
  const deleteDictionaryById = async (dictionaryId) => {
115
204
  const dictionary = await DictionaryModel.findByIdAndDelete(dictionaryId);
@@ -118,16 +207,37 @@ const deleteDictionaryById = async (dictionaryId) => {
118
207
  }
119
208
  return dictionary;
120
209
  };
210
+ const incrementVersion = (dictionary) => {
211
+ const VERSION_PREFIX = "v";
212
+ const availableVersions = dictionary.availableVersions ?? [];
213
+ const currentVersion = availableVersions.length > 0 ? availableVersions[availableVersions.length - 1] : "v1";
214
+ const getVersionNumber = (version) => {
215
+ const match = version.match(/^v(\d+)$/);
216
+ if (!match) {
217
+ throw new Error(`Invalid version format: ${version}`);
218
+ }
219
+ return parseInt(match[1], 10);
220
+ };
221
+ let newNumber = getVersionNumber(currentVersion) + 1;
222
+ let newVersion = `${VERSION_PREFIX}${newNumber}`;
223
+ while (availableVersions.includes(newVersion)) {
224
+ newNumber += 1;
225
+ newVersion = `${VERSION_PREFIX}${newNumber}`;
226
+ }
227
+ return newVersion;
228
+ };
121
229
  export {
122
230
  countDictionaries,
123
231
  createDictionary,
124
232
  deleteDictionaryById,
125
233
  findDictionaries,
126
234
  getDictionariesByKeys,
235
+ getDictionariesByTags,
127
236
  getDictionariesKeys,
128
237
  getDictionaryById,
129
238
  getDictionaryByKey,
130
239
  getExistingDictionaryKey,
240
+ incrementVersion,
131
241
  updateDictionaryById,
132
242
  updateDictionaryByKey
133
243
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/services/dictionary.service.ts"],"sourcesContent":["import { DictionaryModel } from '@models/dictionary.model';\nimport { GenericError } from '@utils/errors';\nimport type { DictionaryFilters } from '@utils/filtersAndPagination/getDictionaryFiltersAndPagination';\nimport {\n type DictionaryFields,\n validateDictionary,\n} from '@utils/validation/validateDictionary';\nimport type { ObjectId } from 'mongoose';\nimport type {\n Dictionary,\n DictionaryData,\n DictionaryDocument,\n} from '@/types/dictionary.types';\n\n/**\n * Finds dictionaries based on filters and pagination options.\n * @param filters - MongoDB filter query.\n * @param skip - Number of documents to skip.\n * @param limit - Number of documents to limit.\n * @returns List of dictionaries matching the filters.\n */\nexport const findDictionaries = async (\n filters: DictionaryFilters,\n skip = 0,\n limit = 100\n): Promise<DictionaryDocument[]> =>\n await DictionaryModel.find(filters).skip(skip).limit(limit);\n\n/**\n * Finds a dictionary by its ID.\n * @param dictionaryId - The ID of the dictionary to find.\n * @returns The dictionary matching the ID.\n */\nexport const getDictionaryById = async (\n dictionaryId: string | ObjectId\n): Promise<DictionaryDocument> => {\n const dictionary = await DictionaryModel.findById(dictionaryId);\n\n if (!dictionary) {\n throw new GenericError('DICTIONARY_NOT_FOUND', { dictionaryId });\n }\n\n return dictionary;\n};\n\n/**\n * Finds a dictionary by its ID.\n * @param dictionaryKey - The ID of the dictionary to find.\n * @returns The dictionary matching the ID.\n */\nexport const getDictionaryByKey = async (\n dictionaryKey: string,\n projectId: string | ObjectId\n): Promise<DictionaryDocument> => {\n const dictionary = await DictionaryModel.findOne({\n key: dictionaryKey,\n projectIds: projectId,\n });\n\n if (!dictionary) {\n throw new GenericError('DICTIONARY_NOT_FOUND', {\n dictionaryKey,\n projectId,\n });\n }\n\n return dictionary;\n};\n\nexport const getDictionariesByKeys = async (\n dictionaryKey: string[],\n projectId: string | ObjectId\n): Promise<DictionaryDocument[]> => {\n const dictionaries = await DictionaryModel.find({\n key: dictionaryKey,\n projectIds: projectId,\n });\n\n return dictionaries;\n};\n\nexport const getDictionariesKeys = async (\n projectId: string | ObjectId\n): Promise<string[]> => {\n const dictionaries = await DictionaryModel.find({\n projectIds: projectId,\n }).select('key');\n\n return dictionaries.map((dictionary) => dictionary.key);\n};\n\n/**\n * Counts the total number of dictionaries that match the filters.\n * @param filters - MongoDB filter query.\n * @returns Total number of dictionaries.\n */\nexport const countDictionaries = async (\n filters: DictionaryFilters\n): Promise<number> => {\n const result = await DictionaryModel.countDocuments(filters);\n\n if (typeof result === 'undefined') {\n throw new GenericError('DICTIONARY_COUNT_FAILED', { filters });\n }\n\n return result;\n};\n\n/**\n * Creates a new dictionary in the database.\n * @param dictionary - The dictionary data to create.\n * @returns The created dictionary.\n */\nexport const createDictionary = async (\n dictionary: DictionaryData\n): Promise<DictionaryDocument> => {\n const errors = await validateDictionary(dictionary);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('DICTIONARY_INVALID_FIELDS', {\n errors,\n });\n }\n\n return await DictionaryModel.create(dictionary);\n};\n\ntype GetExistingDictionaryResult = {\n existingDictionariesKey: string[];\n newDictionariesKey: string[];\n};\n\n/**\n * Gets the existing dictionaries from the provided list of keys.\n * @param dictionariesKeys - List of dictionary keys to check.\n * @param projectId - The ID of the project to check the dictionaries against.\n * @returns The existing dictionaries and the new dictionaries.\n */\nexport const getExistingDictionaryKey = async (\n dictionariesKeys: string[],\n projectId: string | ObjectId\n): Promise<GetExistingDictionaryResult> => {\n // Fetch dictionaries from the database where the key is in the provided list\n const existingDictionaries = await DictionaryModel.find({\n key: { $in: dictionariesKeys },\n projectIds: projectId,\n });\n\n // Map existing dictionaries to a LocalDictionary object\n const existingDictionariesKey: string[] = [];\n const newDictionariesKey: string[] = [];\n\n for (const key of dictionariesKeys) {\n const isDictionaryExist = existingDictionaries.some(\n (dictionary) => dictionary.key === key\n );\n\n if (isDictionaryExist) {\n existingDictionariesKey.push(key);\n } else {\n newDictionariesKey.push(key);\n }\n }\n\n return { existingDictionariesKey, newDictionariesKey };\n};\n\n/**\n * Updates an existing dictionary in the database by its ID.\n * @param dictionaryId - The ID of the dictionary to update.\n * @param dictionary - The updated dictionary data.\n * @returns The updated dictionary.\n */\nexport const updateDictionaryById = async (\n dictionaryId: string | ObjectId,\n dictionary: Partial<Dictionary>\n): Promise<DictionaryDocument> => {\n const updatedKeys = Object.keys(dictionary) as DictionaryFields;\n const errors = validateDictionary(dictionary, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('DICTIONARY_INVALID_FIELDS', {\n dictionaryId,\n errors,\n });\n }\n\n const existingDictionary = await getDictionaryById(dictionaryId);\n\n const result = await DictionaryModel.updateOne(\n { _id: dictionaryId },\n { ...dictionary, content: [existingDictionary.content, dictionary.content] }\n );\n\n if (result.matchedCount === 0) {\n throw new GenericError('DICTIONARY_UPDATE_FAILED', { dictionaryId });\n }\n\n return await getDictionaryById(dictionaryId);\n};\n\n/**\n * Updates an existing dictionary in the database by its key.\n * @param dictionaryKey - The ID of the dictionary to update.\n * @param dictionary - The updated dictionary data.\n * @returns The updated dictionary.\n */\nexport const updateDictionaryByKey = async (\n dictionaryKey: string,\n dictionary: Partial<Dictionary>,\n projectId: string | ObjectId\n): Promise<DictionaryDocument> => {\n const updatedKeys = Object.keys(dictionary) as DictionaryFields;\n const errors = validateDictionary(dictionary, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('DICTIONARY_INVALID_FIELDS', {\n dictionaryKey,\n projectId,\n errors,\n });\n }\n\n const existingDictionary = await getDictionaryByKey(dictionaryKey, projectId);\n\n const result = await DictionaryModel.updateOne(\n { key: dictionaryKey, projectIds: projectId },\n { ...dictionary, content: [existingDictionary.content, dictionary.content] }\n );\n\n if (result.matchedCount === 0) {\n throw new GenericError('DICTIONARY_UPDATE_FAILED', { dictionaryKey });\n }\n\n return await getDictionaryByKey(dictionaryKey, projectId);\n};\n\n/**\n * Deletes a dictionary from the database by its ID.\n * @param dictionaryId - The ID of the dictionary to delete.\n * @returns The result of the deletion operation.\n */\nexport const deleteDictionaryById = async (\n dictionaryId: string\n): Promise<DictionaryDocument> => {\n const dictionary = await DictionaryModel.findByIdAndDelete(dictionaryId);\n\n if (!dictionary) {\n throw new GenericError('DICTIONARY_NOT_FOUND', { dictionaryId });\n }\n\n return dictionary;\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/services/dictionary.service.ts"],"sourcesContent":["import { DictionaryModel } from '@models/dictionary.model';\nimport { ensureMongoDocumentToObject } from '@utils/ensureMongoDocumentToObject';\nimport { GenericError } from '@utils/errors';\nimport type { DictionaryFilters } from '@utils/filtersAndPagination/getDictionaryFiltersAndPagination';\nimport { removeObjectKeys } from '@utils/removeObjectKeys';\nimport {\n type DictionaryFields,\n validateDictionary,\n} from '@utils/validation/validateDictionary';\nimport { ObjectId } from 'mongoose';\nimport type {\n Dictionary,\n DictionaryData,\n DictionaryDocument,\n} from '@/types/dictionary.types';\nimport type { Project } from '@/types/project.types';\n\n/**\n * Finds dictionaries based on filters and pagination options.\n * @param filters - MongoDB filter query.\n * @param skip - Number of documents to skip.\n * @param limit - Number of documents to limit.\n * @returns List of dictionaries matching the filters.\n */\nexport const findDictionaries = async (\n filters: DictionaryFilters,\n skip = 0,\n limit = 100\n): Promise<DictionaryDocument[]> => {\n try {\n const dictionaries = await DictionaryModel.aggregate<DictionaryDocument>([\n // Stage 1: Match the filters\n { $match: filters },\n\n // Stage 2: Skip for pagination\n { $skip: skip },\n\n // Stage 3: Limit the number of documents\n { $limit: limit },\n\n // Stage 4: Add the 'availableVersions' field\n {\n $addFields: {\n availableVersions: {\n $map: {\n input: { $objectToArray: '$content' },\n as: 'version',\n in: '$$version.k',\n },\n },\n },\n },\n\n // (Optional) Stage 5: Project the fields you want to include/exclude\n // For example, to exclude the entire 'content' field and keep only 'availableVersions'\n // {\n // $project: {\n // content: 0 // Exclude the 'content' field\n // }\n // }\n ]);\n\n return dictionaries;\n } catch (error) {\n console.error('Error fetching dictionaries:', error);\n throw error;\n }\n};\n\n/**\n * Finds a dictionary by its ID.\n * @param dictionaryId - The ID of the dictionary to find.\n * @returns The dictionary matching the ID.\n */\n/**\n * Finds a dictionary by its ID and includes the 'availableVersions' field.\n * @param dictionaryId - The ID of the dictionary to find.\n * @returns The dictionary matching the ID with available versions.\n */\nexport const getDictionaryById = async (\n dictionaryId: string | ObjectId\n): Promise<DictionaryDocument> => {\n const dictionary = await DictionaryModel.aggregate<DictionaryDocument>([\n // Stage 1: Match the document by ID\n { $match: { _id: dictionaryId } },\n\n // Stage 2: Add the 'availableVersions' field\n {\n $addFields: {\n availableVersions: {\n $map: {\n input: { $objectToArray: '$content' },\n as: 'version',\n in: '$$version.k',\n },\n },\n },\n },\n ]);\n\n if (!dictionary.length) {\n throw new GenericError('DICTIONARY_NOT_FOUND', { dictionaryId });\n }\n\n return dictionary[0];\n};\n\n/**\n * Finds a dictionary by its ID.\n * @param dictionaryKey - The ID of the dictionary to find.\n * @returns The dictionary matching the ID.\n */\nexport const getDictionaryByKey = async (\n dictionaryKey: string,\n projectId: string | ObjectId\n): Promise<DictionaryDocument> => {\n const dictionaries = await getDictionariesByKeys([dictionaryKey], projectId);\n\n console.log('dictionaries', dictionaries);\n\n return dictionaries[0];\n};\n\nexport const getDictionariesByKeys = async (\n dictionaryKeys: string[],\n projectId: string | ObjectId\n): Promise<DictionaryDocument[]> => {\n const dictionaries = await DictionaryModel.aggregate<DictionaryDocument>([\n // Stage 1: Match the document by key\n { $match: { key: { $in: dictionaryKeys }, projectIds: projectId } },\n\n // Stage 2: Add the 'availableVersions' field\n {\n $addFields: {\n availableVersions: {\n $map: {\n input: { $objectToArray: '$content' },\n as: 'version',\n in: '$$version.k',\n },\n },\n },\n },\n ]);\n\n if (!dictionaries) {\n throw new GenericError('DICTIONARY_NOT_FOUND', {\n dictionaryKeys,\n projectId,\n });\n }\n\n return dictionaries;\n};\n\nexport const getDictionariesKeys = async (\n projectId: string | ObjectId\n): Promise<string[]> => {\n const dictionaries = await DictionaryModel.find({\n projectIds: projectId,\n }).select('key');\n\n return dictionaries.map((dictionary) => dictionary.key);\n};\n\nexport const getDictionariesByTags = async (\n tags: string[],\n projectId: string | Project['_id']\n): Promise<DictionaryDocument[]> => {\n const dictionaries = await DictionaryModel.aggregate<DictionaryDocument>([\n // Stage 1: Match the document by tags\n {\n $match: {\n tags: { $in: tags },\n projectIds: projectId,\n },\n },\n\n // Stage 2: Add the 'availableVersions' field\n {\n $addFields: {\n availableVersions: {\n $map: {\n input: { $objectToArray: '$content' },\n as: 'version',\n in: '$$version.k',\n },\n },\n },\n },\n ]);\n\n return dictionaries;\n};\n\n/**\n * Counts the total number of dictionaries that match the filters.\n * @param filters - MongoDB filter query.\n * @returns Total number of dictionaries.\n */\nexport const countDictionaries = async (\n filters: DictionaryFilters\n): Promise<number> => {\n const result = await DictionaryModel.countDocuments(filters);\n\n if (typeof result === 'undefined') {\n throw new GenericError('DICTIONARY_COUNT_FAILED', { filters });\n }\n\n return result;\n};\n\n/**\n * Creates a new dictionary in the database.\n * @param dictionary - The dictionary data to create.\n * @returns The created dictionary.\n */\nexport const createDictionary = async (\n dictionary: DictionaryData\n): Promise<DictionaryDocument> => {\n const errors = await validateDictionary(dictionary);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('DICTIONARY_INVALID_FIELDS', {\n errors,\n });\n }\n\n return await DictionaryModel.create(dictionary);\n};\n\ntype GetExistingDictionaryResult = {\n existingDictionariesKey: string[];\n newDictionariesKey: string[];\n};\n\n/**\n * Gets the existing dictionaries from the provided list of keys.\n * @param dictionariesKeys - List of dictionary keys to check.\n * @param projectId - The ID of the project to check the dictionaries against.\n * @returns The existing dictionaries and the new dictionaries.\n */\nexport const getExistingDictionaryKey = async (\n dictionariesKeys: string[],\n projectId: string | ObjectId\n): Promise<GetExistingDictionaryResult> => {\n // Fetch dictionaries from the database where the key is in the provided list\n const existingDictionaries = await DictionaryModel.find({\n key: { $in: dictionariesKeys },\n projectIds: projectId,\n });\n\n // Map existing dictionaries to a LocalDictionary object\n const existingDictionariesKey: string[] = [];\n const newDictionariesKey: string[] = [];\n\n for (const key of dictionariesKeys) {\n const isDictionaryExist = existingDictionaries.some(\n (dictionary) => dictionary.key === key\n );\n\n if (isDictionaryExist) {\n existingDictionariesKey.push(key);\n } else {\n newDictionariesKey.push(key);\n }\n }\n\n return { existingDictionariesKey, newDictionariesKey };\n};\n\n/**\n * Updates an existing dictionary in the database by its ID.\n * @param dictionaryId - The ID of the dictionary to update.\n * @param dictionary - The updated dictionary data.\n * @returns The updated dictionary.\n */\nexport const updateDictionaryById = async (\n dictionaryId: string | ObjectId,\n dictionary: Partial<Dictionary>\n): Promise<DictionaryDocument> => {\n const dictionaryObject = ensureMongoDocumentToObject(dictionary);\n const dictionaryToUpdate = removeObjectKeys(dictionaryObject, ['_id']);\n\n const updatedKeys = Object.keys(dictionaryToUpdate) as DictionaryFields;\n const errors = await validateDictionary(dictionaryToUpdate, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('DICTIONARY_INVALID_FIELDS', {\n dictionaryId,\n errors,\n });\n }\n\n const result = await DictionaryModel.updateOne(\n { _id: dictionaryId },\n dictionaryToUpdate\n );\n\n if (result.matchedCount === 0) {\n throw new GenericError('DICTIONARY_UPDATE_FAILED', { dictionaryId });\n }\n\n const updatedDictionary = await getDictionaryById(dictionaryId);\n\n return updatedDictionary;\n};\n\n/**\n * Updates an existing dictionary in the database by its key.\n * @param dictionaryKey - The ID of the dictionary to update.\n * @param dictionary - The updated dictionary data.\n * @returns The updated dictionary.\n */\nexport const updateDictionaryByKey = async (\n dictionaryKey: string,\n dictionary: Partial<Dictionary>,\n projectId: string | ObjectId\n): Promise<DictionaryDocument> => {\n const dictionaryObject = ensureMongoDocumentToObject(dictionary);\n\n const dictionaryToUpdate = removeObjectKeys(dictionaryObject, ['_id']);\n\n const updatedKeys = Object.keys(dictionaryToUpdate) as DictionaryFields;\n const errors = await validateDictionary(dictionaryToUpdate, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('DICTIONARY_INVALID_FIELDS', {\n dictionaryKey,\n projectId,\n errors,\n });\n }\n\n const result = await DictionaryModel.updateOne(\n { key: dictionaryKey, projectIds: projectId },\n dictionaryToUpdate\n );\n\n if (result.matchedCount === 0) {\n throw new GenericError('DICTIONARY_UPDATE_FAILED', { dictionaryKey });\n }\n\n const updatedDictionary = await getDictionaryByKey(dictionaryKey, projectId);\n\n return updatedDictionary;\n};\n\n/**\n * Deletes a dictionary from the database by its ID.\n * @param dictionaryId - The ID of the dictionary to delete.\n * @returns The result of the deletion operation.\n */\nexport const deleteDictionaryById = async (\n dictionaryId: string\n): Promise<DictionaryDocument> => {\n const dictionary = await DictionaryModel.findByIdAndDelete(dictionaryId);\n\n if (!dictionary) {\n throw new GenericError('DICTIONARY_NOT_FOUND', { dictionaryId });\n }\n\n return dictionary;\n};\n\nexport const incrementVersion = (dictionary: Dictionary): string => {\n const VERSION_PREFIX = 'v';\n const availableVersions = dictionary.availableVersions ?? [];\n\n // Get the current version from the version list, default to 'v1' if not present\n const currentVersion =\n availableVersions.length > 0\n ? availableVersions[availableVersions.length - 1]\n : 'v1';\n\n // Function to extract the numeric part of the version\n const getVersionNumber = (version: string): number => {\n const match = version.match(/^v(\\d+)$/);\n if (!match) {\n throw new Error(`Invalid version format: ${version}`);\n }\n return parseInt(match[1], 10);\n };\n\n // Start with the next version number\n let newNumber = getVersionNumber(currentVersion) + 1;\n let newVersion = `${VERSION_PREFIX}${newNumber}`;\n\n // Loop until a unique version is found\n while (availableVersions.includes(newVersion)) {\n newNumber += 1;\n newVersion = `${VERSION_PREFIX}${newNumber}`;\n }\n\n return newVersion;\n};\n"],"mappings":"AAAA,SAAS,uBAAuB;AAChC,SAAS,mCAAmC;AAC5C,SAAS,oBAAoB;AAE7B,SAAS,wBAAwB;AACjC;AAAA,EAEE;AAAA,OACK;AAgBA,MAAM,mBAAmB,OAC9B,SACA,OAAO,GACP,QAAQ,QAC0B;AAClC,MAAI;AACF,UAAM,eAAe,MAAM,gBAAgB,UAA8B;AAAA;AAAA,MAEvE,EAAE,QAAQ,QAAQ;AAAA;AAAA,MAGlB,EAAE,OAAO,KAAK;AAAA;AAAA,MAGd,EAAE,QAAQ,MAAM;AAAA;AAAA,MAGhB;AAAA,QACE,YAAY;AAAA,UACV,mBAAmB;AAAA,YACjB,MAAM;AAAA,cACJ,OAAO,EAAE,gBAAgB,WAAW;AAAA,cACpC,IAAI;AAAA,cACJ,IAAI;AAAA,YACN;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASF,CAAC;AAED,WAAO;AAAA,EACT,SAAS,OAAO;AACd,YAAQ,MAAM,gCAAgC,KAAK;AACnD,UAAM;AAAA,EACR;AACF;AAYO,MAAM,oBAAoB,OAC/B,iBACgC;AAChC,QAAM,aAAa,MAAM,gBAAgB,UAA8B;AAAA;AAAA,IAErE,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE;AAAA;AAAA,IAGhC;AAAA,MACE,YAAY;AAAA,QACV,mBAAmB;AAAA,UACjB,MAAM;AAAA,YACJ,OAAO,EAAE,gBAAgB,WAAW;AAAA,YACpC,IAAI;AAAA,YACJ,IAAI;AAAA,UACN;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAED,MAAI,CAAC,WAAW,QAAQ;AACtB,UAAM,IAAI,aAAa,wBAAwB,EAAE,aAAa,CAAC;AAAA,EACjE;AAEA,SAAO,WAAW,CAAC;AACrB;AAOO,MAAM,qBAAqB,OAChC,eACA,cACgC;AAChC,QAAM,eAAe,MAAM,sBAAsB,CAAC,aAAa,GAAG,SAAS;AAE3E,UAAQ,IAAI,gBAAgB,YAAY;AAExC,SAAO,aAAa,CAAC;AACvB;AAEO,MAAM,wBAAwB,OACnC,gBACA,cACkC;AAClC,QAAM,eAAe,MAAM,gBAAgB,UAA8B;AAAA;AAAA,IAEvE,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,eAAe,GAAG,YAAY,UAAU,EAAE;AAAA;AAAA,IAGlE;AAAA,MACE,YAAY;AAAA,QACV,mBAAmB;AAAA,UACjB,MAAM;AAAA,YACJ,OAAO,EAAE,gBAAgB,WAAW;AAAA,YACpC,IAAI;AAAA,YACJ,IAAI;AAAA,UACN;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAED,MAAI,CAAC,cAAc;AACjB,UAAM,IAAI,aAAa,wBAAwB;AAAA,MAC7C;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEO,MAAM,sBAAsB,OACjC,cACsB;AACtB,QAAM,eAAe,MAAM,gBAAgB,KAAK;AAAA,IAC9C,YAAY;AAAA,EACd,CAAC,EAAE,OAAO,KAAK;AAEf,SAAO,aAAa,IAAI,CAAC,eAAe,WAAW,GAAG;AACxD;AAEO,MAAM,wBAAwB,OACnC,MACA,cACkC;AAClC,QAAM,eAAe,MAAM,gBAAgB,UAA8B;AAAA;AAAA,IAEvE;AAAA,MACE,QAAQ;AAAA,QACN,MAAM,EAAE,KAAK,KAAK;AAAA,QAClB,YAAY;AAAA,MACd;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,YAAY;AAAA,QACV,mBAAmB;AAAA,UACjB,MAAM;AAAA,YACJ,OAAO,EAAE,gBAAgB,WAAW;AAAA,YACpC,IAAI;AAAA,YACJ,IAAI;AAAA,UACN;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAOO,MAAM,oBAAoB,OAC/B,YACoB;AACpB,QAAM,SAAS,MAAM,gBAAgB,eAAe,OAAO;AAE3D,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,IAAI,aAAa,2BAA2B,EAAE,QAAQ,CAAC;AAAA,EAC/D;AAEA,SAAO;AACT;AAOO,MAAM,mBAAmB,OAC9B,eACgC;AAChC,QAAM,SAAS,MAAM,mBAAmB,UAAU;AAElD,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,aAAa,6BAA6B;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,MAAM,gBAAgB,OAAO,UAAU;AAChD;AAaO,MAAM,2BAA2B,OACtC,kBACA,cACyC;AAEzC,QAAM,uBAAuB,MAAM,gBAAgB,KAAK;AAAA,IACtD,KAAK,EAAE,KAAK,iBAAiB;AAAA,IAC7B,YAAY;AAAA,EACd,CAAC;AAGD,QAAM,0BAAoC,CAAC;AAC3C,QAAM,qBAA+B,CAAC;AAEtC,aAAW,OAAO,kBAAkB;AAClC,UAAM,oBAAoB,qBAAqB;AAAA,MAC7C,CAAC,eAAe,WAAW,QAAQ;AAAA,IACrC;AAEA,QAAI,mBAAmB;AACrB,8BAAwB,KAAK,GAAG;AAAA,IAClC,OAAO;AACL,yBAAmB,KAAK,GAAG;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO,EAAE,yBAAyB,mBAAmB;AACvD;AAQO,MAAM,uBAAuB,OAClC,cACA,eACgC;AAChC,QAAM,mBAAmB,4BAA4B,UAAU;AAC/D,QAAM,qBAAqB,iBAAiB,kBAAkB,CAAC,KAAK,CAAC;AAErE,QAAM,cAAc,OAAO,KAAK,kBAAkB;AAClD,QAAM,SAAS,MAAM,mBAAmB,oBAAoB,WAAW;AAEvE,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,aAAa,6BAA6B;AAAA,MAClD;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,MAAM,gBAAgB;AAAA,IACnC,EAAE,KAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAEA,MAAI,OAAO,iBAAiB,GAAG;AAC7B,UAAM,IAAI,aAAa,4BAA4B,EAAE,aAAa,CAAC;AAAA,EACrE;AAEA,QAAM,oBAAoB,MAAM,kBAAkB,YAAY;AAE9D,SAAO;AACT;AAQO,MAAM,wBAAwB,OACnC,eACA,YACA,cACgC;AAChC,QAAM,mBAAmB,4BAA4B,UAAU;AAE/D,QAAM,qBAAqB,iBAAiB,kBAAkB,CAAC,KAAK,CAAC;AAErE,QAAM,cAAc,OAAO,KAAK,kBAAkB;AAClD,QAAM,SAAS,MAAM,mBAAmB,oBAAoB,WAAW;AAEvE,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,aAAa,6BAA6B;AAAA,MAClD;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,MAAM,gBAAgB;AAAA,IACnC,EAAE,KAAK,eAAe,YAAY,UAAU;AAAA,IAC5C;AAAA,EACF;AAEA,MAAI,OAAO,iBAAiB,GAAG;AAC7B,UAAM,IAAI,aAAa,4BAA4B,EAAE,cAAc,CAAC;AAAA,EACtE;AAEA,QAAM,oBAAoB,MAAM,mBAAmB,eAAe,SAAS;AAE3E,SAAO;AACT;AAOO,MAAM,uBAAuB,OAClC,iBACgC;AAChC,QAAM,aAAa,MAAM,gBAAgB,kBAAkB,YAAY;AAEvE,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,aAAa,wBAAwB,EAAE,aAAa,CAAC;AAAA,EACjE;AAEA,SAAO;AACT;AAEO,MAAM,mBAAmB,CAAC,eAAmC;AAClE,QAAM,iBAAiB;AACvB,QAAM,oBAAoB,WAAW,qBAAqB,CAAC;AAG3D,QAAM,iBACJ,kBAAkB,SAAS,IACvB,kBAAkB,kBAAkB,SAAS,CAAC,IAC9C;AAGN,QAAM,mBAAmB,CAAC,YAA4B;AACpD,UAAM,QAAQ,QAAQ,MAAM,UAAU;AACtC,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,2BAA2B,OAAO,EAAE;AAAA,IACtD;AACA,WAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AAAA,EAC9B;AAGA,MAAI,YAAY,iBAAiB,cAAc,IAAI;AACnD,MAAI,aAAa,GAAG,cAAc,GAAG,SAAS;AAG9C,SAAO,kBAAkB,SAAS,UAAU,GAAG;AAC7C,iBAAa;AACb,iBAAa,GAAG,cAAc,GAAG,SAAS;AAAA,EAC5C;AAEA,SAAO;AACT;","names":[]}
@@ -1,5 +1,7 @@
1
1
  import { ProjectModel } from './../models/project.model.mjs';
2
+ import { ensureMongoDocumentToObject } from './../utils/ensureMongoDocumentToObject.mjs';
2
3
  import { GenericError } from './../utils/errors/index.mjs';
4
+ import { removeObjectKeys } from './../utils/removeObjectKeys.mjs';
3
5
  import {
4
6
  validateProject
5
7
  } from './../utils/validation/validateProject.mjs';
@@ -29,10 +31,13 @@ const createProject = async (project) => {
29
31
  return await ProjectModel.create(project);
30
32
  };
31
33
  const updateProjectById = async (projectId, project) => {
32
- if (project.oAuth2Access) {
33
- delete project.oAuth2Access;
34
- }
35
- const updatedKeys = Object.keys(project);
34
+ const projectObject = ensureMongoDocumentToObject(project);
35
+ const projectToUpdate = removeObjectKeys(projectObject, [
36
+ "_id",
37
+ "oAuth2Access",
38
+ "organizationId"
39
+ ]);
40
+ const updatedKeys = Object.keys(projectToUpdate);
36
41
  const errors = validateProject(project, updatedKeys);
37
42
  if (Object.keys(errors).length > 0) {
38
43
  throw new GenericError("PROJECT_INVALID_FIELDS", {
@@ -40,7 +45,11 @@ const updateProjectById = async (projectId, project) => {
40
45
  errors
41
46
  });
42
47
  }
43
- const result = await ProjectModel.updateOne({ _id: projectId }, project);
48
+ console.log("projectToUpdate", projectToUpdate);
49
+ const result = await ProjectModel.updateOne(
50
+ { _id: projectId },
51
+ projectToUpdate
52
+ );
44
53
  if (result.matchedCount === 0) {
45
54
  throw new GenericError("PROJECT_UPDATE_FAILED", { projectId });
46
55
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/services/project.service.ts"],"sourcesContent":["import { ProjectModel } from '@models/project.model';\nimport { GenericError } from '@utils/errors';\nimport type { ProjectFilters } from '@utils/filtersAndPagination/getProjectFiltersAndPagination';\nimport {\n type ProjectFields,\n validateProject,\n} from '@utils/validation/validateProject';\nimport type { ObjectId } from 'mongoose';\nimport type {\n Project,\n ProjectData,\n ProjectDocument,\n} from '@/types/project.types';\n\n/**\n * Finds projects based on filters and pagination options.\n * @param filters - MongoDB filter query.\n * @param skip - Number of documents to skip.\n * @param limit - Number of documents to limit.\n * @returns List of projects matching the filters.\n */\nexport const findProjects = async (\n filters: ProjectFilters,\n skip = 0,\n limit = 100\n): Promise<ProjectDocument[]> =>\n await ProjectModel.find(filters).skip(skip).limit(limit);\n\n/**\n * Finds a project by its ID.\n * @param projectId - The ID of the project to find.\n * @returns The project matching the ID.\n */\nexport const getProjectById = async (\n projectId: string | ObjectId\n): Promise<ProjectDocument> => {\n const project = await ProjectModel.findById(projectId);\n\n if (!project) {\n throw new GenericError('PROJECT_NOT_DEFINED', { projectId });\n }\n\n return project;\n};\n\n/**\n * Counts the total number of projects that match the filters.\n * @param filters - MongoDB filter query.\n * @returns Total number of projects.\n */\nexport const countProjects = async (\n filters: ProjectFilters\n): Promise<number> => {\n const result = await ProjectModel.countDocuments(filters);\n\n if (typeof result === 'undefined') {\n throw new GenericError('PROJECT_COUNT_FAILED', { filters });\n }\n\n return result;\n};\n\n/**\n * Creates a new project in the database.\n * @param project - The project data to create.\n * @returns The created project.\n */\nexport const createProject = async (\n project: ProjectData\n): Promise<ProjectDocument> => {\n if ((project as Partial<Project>).oAuth2Access) {\n delete (project as Partial<Project>).oAuth2Access;\n }\n\n const errors = await validateProject(project, ['name']);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('PROJECT_INVALID_FIELDS', { errors });\n }\n\n return await ProjectModel.create(project);\n};\n\n/**\n * Updates an existing project in the database by its ID.\n * @param projectId - The ID of the project to update.\n * @param project - The updated project data.\n * @returns The updated project.\n */\nexport const updateProjectById = async (\n projectId: string | ObjectId,\n project: Partial<Project>\n): Promise<ProjectDocument> => {\n if (project.oAuth2Access) {\n delete project.oAuth2Access;\n }\n\n const updatedKeys = Object.keys(project) as ProjectFields;\n\n const errors = validateProject(project, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('PROJECT_INVALID_FIELDS', {\n projectId,\n errors,\n });\n }\n\n const result = await ProjectModel.updateOne({ _id: projectId }, project);\n\n if (result.matchedCount === 0) {\n throw new GenericError('PROJECT_UPDATE_FAILED', { projectId });\n }\n\n return await getProjectById(projectId);\n};\n\n/**\n * Deletes a project from the database by its ID.\n * @param projectId - The ID of the project to delete.\n * @returns The result of the deletion operation.\n */\nexport const deleteProjectById = async (\n projectId: string | ObjectId\n): Promise<ProjectDocument> => {\n const project = await ProjectModel.findByIdAndDelete(projectId);\n\n if (!project) {\n throw new GenericError('PROJECT_NOT_DEFINED', { projectId });\n }\n\n return project;\n};\n"],"mappings":"AAAA,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAE7B;AAAA,EAEE;AAAA,OACK;AAeA,MAAM,eAAe,OAC1B,SACA,OAAO,GACP,QAAQ,QAER,MAAM,aAAa,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,KAAK;AAOlD,MAAM,iBAAiB,OAC5B,cAC6B;AAC7B,QAAM,UAAU,MAAM,aAAa,SAAS,SAAS;AAErD,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,aAAa,uBAAuB,EAAE,UAAU,CAAC;AAAA,EAC7D;AAEA,SAAO;AACT;AAOO,MAAM,gBAAgB,OAC3B,YACoB;AACpB,QAAM,SAAS,MAAM,aAAa,eAAe,OAAO;AAExD,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,IAAI,aAAa,wBAAwB,EAAE,QAAQ,CAAC;AAAA,EAC5D;AAEA,SAAO;AACT;AAOO,MAAM,gBAAgB,OAC3B,YAC6B;AAC7B,MAAK,QAA6B,cAAc;AAC9C,WAAQ,QAA6B;AAAA,EACvC;AAEA,QAAM,SAAS,MAAM,gBAAgB,SAAS,CAAC,MAAM,CAAC;AAEtD,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,aAAa,0BAA0B,EAAE,OAAO,CAAC;AAAA,EAC7D;AAEA,SAAO,MAAM,aAAa,OAAO,OAAO;AAC1C;AAQO,MAAM,oBAAoB,OAC/B,WACA,YAC6B;AAC7B,MAAI,QAAQ,cAAc;AACxB,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,cAAc,OAAO,KAAK,OAAO;AAEvC,QAAM,SAAS,gBAAgB,SAAS,WAAW;AAEnD,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,aAAa,0BAA0B;AAAA,MAC/C;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,MAAM,aAAa,UAAU,EAAE,KAAK,UAAU,GAAG,OAAO;AAEvE,MAAI,OAAO,iBAAiB,GAAG;AAC7B,UAAM,IAAI,aAAa,yBAAyB,EAAE,UAAU,CAAC;AAAA,EAC/D;AAEA,SAAO,MAAM,eAAe,SAAS;AACvC;AAOO,MAAM,oBAAoB,OAC/B,cAC6B;AAC7B,QAAM,UAAU,MAAM,aAAa,kBAAkB,SAAS;AAE9D,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,aAAa,uBAAuB,EAAE,UAAU,CAAC;AAAA,EAC7D;AAEA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../../src/services/project.service.ts"],"sourcesContent":["import { ProjectModel } from '@models/project.model';\nimport { ensureMongoDocumentToObject } from '@utils/ensureMongoDocumentToObject';\nimport { GenericError } from '@utils/errors';\nimport type { ProjectFilters } from '@utils/filtersAndPagination/getProjectFiltersAndPagination';\nimport { removeObjectKeys } from '@utils/removeObjectKeys';\nimport {\n type ProjectFields,\n validateProject,\n} from '@utils/validation/validateProject';\nimport type { ObjectId } from 'mongoose';\nimport type {\n Project,\n ProjectData,\n ProjectDocument,\n} from '@/types/project.types';\n\n/**\n * Finds projects based on filters and pagination options.\n * @param filters - MongoDB filter query.\n * @param skip - Number of documents to skip.\n * @param limit - Number of documents to limit.\n * @returns List of projects matching the filters.\n */\nexport const findProjects = async (\n filters: ProjectFilters,\n skip = 0,\n limit = 100\n): Promise<ProjectDocument[]> =>\n await ProjectModel.find(filters).skip(skip).limit(limit);\n\n/**\n * Finds a project by its ID.\n * @param projectId - The ID of the project to find.\n * @returns The project matching the ID.\n */\nexport const getProjectById = async (\n projectId: string | ObjectId\n): Promise<ProjectDocument> => {\n const project = await ProjectModel.findById(projectId);\n\n if (!project) {\n throw new GenericError('PROJECT_NOT_DEFINED', { projectId });\n }\n\n return project;\n};\n\n/**\n * Counts the total number of projects that match the filters.\n * @param filters - MongoDB filter query.\n * @returns Total number of projects.\n */\nexport const countProjects = async (\n filters: ProjectFilters\n): Promise<number> => {\n const result = await ProjectModel.countDocuments(filters);\n\n if (typeof result === 'undefined') {\n throw new GenericError('PROJECT_COUNT_FAILED', { filters });\n }\n\n return result;\n};\n\n/**\n * Creates a new project in the database.\n * @param project - The project data to create.\n * @returns The created project.\n */\nexport const createProject = async (\n project: ProjectData\n): Promise<ProjectDocument> => {\n if ((project as Partial<Project>).oAuth2Access) {\n delete (project as Partial<Project>).oAuth2Access;\n }\n\n const errors = await validateProject(project, ['name']);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('PROJECT_INVALID_FIELDS', { errors });\n }\n\n return await ProjectModel.create(project);\n};\n\n/**\n * Updates an existing project in the database by its ID.\n * @param projectId - The ID of the project to update.\n * @param project - The updated project data.\n * @returns The updated project.\n */\nexport const updateProjectById = async (\n projectId: string | ObjectId,\n project: Partial<Project>\n): Promise<ProjectDocument> => {\n const projectObject = ensureMongoDocumentToObject(project);\n const projectToUpdate = removeObjectKeys(projectObject, [\n '_id',\n 'oAuth2Access',\n 'organizationId',\n ]);\n\n const updatedKeys = Object.keys(projectToUpdate) as ProjectFields;\n\n const errors = validateProject(project, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('PROJECT_INVALID_FIELDS', {\n projectId,\n errors,\n });\n }\n\n console.log('projectToUpdate', projectToUpdate);\n\n const result = await ProjectModel.updateOne(\n { _id: projectId },\n projectToUpdate\n );\n\n if (result.matchedCount === 0) {\n throw new GenericError('PROJECT_UPDATE_FAILED', { projectId });\n }\n\n return await getProjectById(projectId);\n};\n\n/**\n * Deletes a project from the database by its ID.\n * @param projectId - The ID of the project to delete.\n * @returns The result of the deletion operation.\n */\nexport const deleteProjectById = async (\n projectId: string | ObjectId\n): Promise<ProjectDocument> => {\n const project = await ProjectModel.findByIdAndDelete(projectId);\n\n if (!project) {\n throw new GenericError('PROJECT_NOT_DEFINED', { projectId });\n }\n\n return project;\n};\n"],"mappings":"AAAA,SAAS,oBAAoB;AAC7B,SAAS,mCAAmC;AAC5C,SAAS,oBAAoB;AAE7B,SAAS,wBAAwB;AACjC;AAAA,EAEE;AAAA,OACK;AAeA,MAAM,eAAe,OAC1B,SACA,OAAO,GACP,QAAQ,QAER,MAAM,aAAa,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,KAAK;AAOlD,MAAM,iBAAiB,OAC5B,cAC6B;AAC7B,QAAM,UAAU,MAAM,aAAa,SAAS,SAAS;AAErD,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,aAAa,uBAAuB,EAAE,UAAU,CAAC;AAAA,EAC7D;AAEA,SAAO;AACT;AAOO,MAAM,gBAAgB,OAC3B,YACoB;AACpB,QAAM,SAAS,MAAM,aAAa,eAAe,OAAO;AAExD,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,IAAI,aAAa,wBAAwB,EAAE,QAAQ,CAAC;AAAA,EAC5D;AAEA,SAAO;AACT;AAOO,MAAM,gBAAgB,OAC3B,YAC6B;AAC7B,MAAK,QAA6B,cAAc;AAC9C,WAAQ,QAA6B;AAAA,EACvC;AAEA,QAAM,SAAS,MAAM,gBAAgB,SAAS,CAAC,MAAM,CAAC;AAEtD,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,aAAa,0BAA0B,EAAE,OAAO,CAAC;AAAA,EAC7D;AAEA,SAAO,MAAM,aAAa,OAAO,OAAO;AAC1C;AAQO,MAAM,oBAAoB,OAC/B,WACA,YAC6B;AAC7B,QAAM,gBAAgB,4BAA4B,OAAO;AACzD,QAAM,kBAAkB,iBAAiB,eAAe;AAAA,IACtD;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,cAAc,OAAO,KAAK,eAAe;AAE/C,QAAM,SAAS,gBAAgB,SAAS,WAAW;AAEnD,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,aAAa,0BAA0B;AAAA,MAC/C;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,UAAQ,IAAI,mBAAmB,eAAe;AAE9C,QAAM,SAAS,MAAM,aAAa;AAAA,IAChC,EAAE,KAAK,UAAU;AAAA,IACjB;AAAA,EACF;AAEA,MAAI,OAAO,iBAAiB,GAAG;AAC7B,UAAM,IAAI,aAAa,yBAAyB,EAAE,UAAU,CAAC;AAAA,EAC/D;AAEA,SAAO,MAAM,eAAe,SAAS;AACvC;AAOO,MAAM,oBAAoB,OAC/B,cAC6B;AAC7B,QAAM,UAAU,MAAM,aAAa,kBAAkB,SAAS;AAE9D,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,aAAa,uBAAuB,EAAE,UAAU,CAAC;AAAA,EAC7D;AAEA,SAAO;AACT;","names":[]}
@@ -0,0 +1,61 @@
1
+ import { TagModel } from './../models/tag.model.mjs';
2
+ import { GenericError } from './../utils/errors/index.mjs';
3
+ import { validateTag } from './../utils/validation/validateTag.mjs';
4
+ const findTags = async (filters, skip = 0, limit = 100) => await TagModel.find(filters).skip(skip).limit(limit);
5
+ const getTagById = async (tagId) => {
6
+ const tag = await TagModel.findById(tagId);
7
+ if (!tag) {
8
+ throw new GenericError("TAG_NOT_FOUND", { tagId });
9
+ }
10
+ return tag;
11
+ };
12
+ const getTagsByKeys = async (keys, organizationId) => {
13
+ const tags = await TagModel.find({ key: { $in: keys }, organizationId });
14
+ return tags;
15
+ };
16
+ const countTags = async (filters) => {
17
+ const result = await TagModel.countDocuments(filters);
18
+ if (typeof result === "undefined") {
19
+ throw new GenericError("TAG_COUNT_FAILED", { filters });
20
+ }
21
+ return result;
22
+ };
23
+ const createTag = async (tag) => {
24
+ const errors = await validateTag(tag, ["key"]);
25
+ if (Object.keys(errors).length > 0) {
26
+ throw new GenericError("TAG_INVALID_FIELDS", { errors });
27
+ }
28
+ return await TagModel.create(tag);
29
+ };
30
+ const updateTagById = async (tagId, tag) => {
31
+ const updatedKeys = Object.keys(tag);
32
+ const errors = validateTag(tag, updatedKeys);
33
+ if (Object.keys(errors).length > 0) {
34
+ throw new GenericError("TAG_INVALID_FIELDS", {
35
+ tagId,
36
+ errors
37
+ });
38
+ }
39
+ const result = await TagModel.updateOne({ _id: tagId }, tag);
40
+ if (result.matchedCount === 0) {
41
+ throw new GenericError("TAG_UPDATE_FAILED", { tagId });
42
+ }
43
+ return await getTagById(tagId);
44
+ };
45
+ const deleteTagById = async (tagId) => {
46
+ const tag = await TagModel.findByIdAndDelete(tagId);
47
+ if (!tag) {
48
+ throw new GenericError("TAG_NOT_FOUND", { tagId });
49
+ }
50
+ return tag;
51
+ };
52
+ export {
53
+ countTags,
54
+ createTag,
55
+ deleteTagById,
56
+ findTags,
57
+ getTagById,
58
+ getTagsByKeys,
59
+ updateTagById
60
+ };
61
+ //# sourceMappingURL=tag.service.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/services/tag.service.ts"],"sourcesContent":["import { TagModel } from '@models/tag.model';\nimport { GenericError } from '@utils/errors';\nimport type { TagFilters } from '@utils/filtersAndPagination/getTagFiltersAndPagination';\nimport { type TagFields, validateTag } from '@utils/validation/validateTag';\nimport type { ObjectId } from 'mongoose';\nimport { Organization } from '@/export';\nimport type { Tag, TagData, TagDocument } from '@/types/tag.types';\n\n/**\n * Finds tags based on filters and pagination options.\n * @param filters - MongoDB filter query.\n * @param skip - Number of documents to skip.\n * @param limit - Number of documents to limit.\n * @returns List of tags matching the filters.\n */\nexport const findTags = async (\n filters: TagFilters,\n skip = 0,\n limit = 100\n): Promise<TagDocument[]> =>\n await TagModel.find(filters).skip(skip).limit(limit);\n\n/**\n * Finds a tag by its ID.\n * @param tagId - The ID of the tag to find.\n * @returns The tag matching the ID.\n */\nexport const getTagById = async (\n tagId: string | ObjectId\n): Promise<TagDocument> => {\n const tag = await TagModel.findById(tagId);\n\n if (!tag) {\n throw new GenericError('TAG_NOT_FOUND', { tagId });\n }\n\n return tag;\n};\n\nexport const getTagsByKeys = async (\n keys: string[],\n organizationId: string | Organization['_id']\n): Promise<TagDocument[]> => {\n const tags = await TagModel.find({ key: { $in: keys }, organizationId });\n\n return tags;\n};\n\n/**\n * Counts the total number of tags that match the filters.\n * @param filters - MongoDB filter query.\n * @returns Total number of tags.\n */\nexport const countTags = async (filters: TagFilters): Promise<number> => {\n const result = await TagModel.countDocuments(filters);\n\n if (typeof result === 'undefined') {\n throw new GenericError('TAG_COUNT_FAILED', { filters });\n }\n\n return result;\n};\n\n/**\n * Creates a new tag in the database.\n * @param tag - The tag data to create.\n * @returns The created tag.\n */\nexport const createTag = async (tag: TagData): Promise<TagDocument> => {\n const errors = await validateTag(tag, ['key']);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('TAG_INVALID_FIELDS', { errors });\n }\n\n return await TagModel.create(tag);\n};\n\n/**\n * Updates an existing tag in the database by its ID.\n * @param tagId - The ID of the tag to update.\n * @param tag - The updated tag data.\n * @returns The updated tag.\n */\nexport const updateTagById = async (\n tagId: string | ObjectId,\n tag: Partial<Tag>\n): Promise<TagDocument> => {\n const updatedKeys = Object.keys(tag) as TagFields;\n\n const errors = validateTag(tag, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('TAG_INVALID_FIELDS', {\n tagId,\n errors,\n });\n }\n\n const result = await TagModel.updateOne({ _id: tagId }, tag);\n\n if (result.matchedCount === 0) {\n throw new GenericError('TAG_UPDATE_FAILED', { tagId });\n }\n\n return await getTagById(tagId);\n};\n\n/**\n * Deletes a tag from the database by its ID.\n * @param tagId - The ID of the tag to delete.\n * @returns The result of the deletion operation.\n */\nexport const deleteTagById = async (\n tagId: string | ObjectId\n): Promise<TagDocument> => {\n const tag = await TagModel.findByIdAndDelete(tagId);\n\n if (!tag) {\n throw new GenericError('TAG_NOT_FOUND', { tagId });\n }\n\n return tag;\n};\n"],"mappings":"AAAA,SAAS,gBAAgB;AACzB,SAAS,oBAAoB;AAE7B,SAAyB,mBAAmB;AAYrC,MAAM,WAAW,OACtB,SACA,OAAO,GACP,QAAQ,QAER,MAAM,SAAS,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,KAAK;AAO9C,MAAM,aAAa,OACxB,UACyB;AACzB,QAAM,MAAM,MAAM,SAAS,SAAS,KAAK;AAEzC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,aAAa,iBAAiB,EAAE,MAAM,CAAC;AAAA,EACnD;AAEA,SAAO;AACT;AAEO,MAAM,gBAAgB,OAC3B,MACA,mBAC2B;AAC3B,QAAM,OAAO,MAAM,SAAS,KAAK,EAAE,KAAK,EAAE,KAAK,KAAK,GAAG,eAAe,CAAC;AAEvE,SAAO;AACT;AAOO,MAAM,YAAY,OAAO,YAAyC;AACvE,QAAM,SAAS,MAAM,SAAS,eAAe,OAAO;AAEpD,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,IAAI,aAAa,oBAAoB,EAAE,QAAQ,CAAC;AAAA,EACxD;AAEA,SAAO;AACT;AAOO,MAAM,YAAY,OAAO,QAAuC;AACrE,QAAM,SAAS,MAAM,YAAY,KAAK,CAAC,KAAK,CAAC;AAE7C,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,aAAa,sBAAsB,EAAE,OAAO,CAAC;AAAA,EACzD;AAEA,SAAO,MAAM,SAAS,OAAO,GAAG;AAClC;AAQO,MAAM,gBAAgB,OAC3B,OACA,QACyB;AACzB,QAAM,cAAc,OAAO,KAAK,GAAG;AAEnC,QAAM,SAAS,YAAY,KAAK,WAAW;AAE3C,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,aAAa,sBAAsB;AAAA,MAC3C;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,MAAM,SAAS,UAAU,EAAE,KAAK,MAAM,GAAG,GAAG;AAE3D,MAAI,OAAO,iBAAiB,GAAG;AAC7B,UAAM,IAAI,aAAa,qBAAqB,EAAE,MAAM,CAAC;AAAA,EACvD;AAEA,SAAO,MAAM,WAAW,KAAK;AAC/B;AAOO,MAAM,gBAAgB,OAC3B,UACyB;AACzB,QAAM,MAAM,MAAM,SAAS,kBAAkB,KAAK;AAElD,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,aAAa,iBAAiB,EAAE,MAAM,CAAC;AAAA,EACnD;AAEA,SAAO;AACT;","names":[]}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=tag.types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,4 +1,4 @@
1
- You are an expert in internationalization and content management. Your task is to audit the content declaration files in the project and identify any potential issues or inconsistencies. Provide a detailed report of any issues found, including the file path, line number, and a brief explanation of the issue.
1
+ You are an expert in internationalization, copy writing and content management. Your task is to audit the content declaration files in the project and identify any potential issues or inconsistencies. Provide a detailed report of any issues found, including the file path, line number, and a brief explanation of the issue.
2
2
 
3
3
  **Instructions:**
4
4
 
@@ -33,6 +33,7 @@ You are an expert in internationalization and content management. Your task is t
33
33
  - **Manage Localizations:** If the required languages contains similar languages, as `en` and `en-GB`, consider `en` as English US and `en-GB` as English UK, and insert or review both translations to maintain clarity and correctness.
34
34
  - **Order Translations:** If the translations are not in the same order as the required languages list, consider reordering the translations to maintain clarity and correctness.
35
35
  - **Escape Special Characters:** If the translations contain special characters, escape them using the appropriate escape sequence.
36
+ - **Respect the tags instructions:** If the tags instructions are provided, ensure that the audited file adheres to them.
36
37
 
37
38
  6. **Example Scenario:**
38
39
 
@@ -137,6 +138,10 @@ You are an expert in internationalization and content management. Your task is t
137
138
  - The import of the `t` function was imported from `react-intlayer` instead of `intlayer`.
138
139
  - A type `DeclarationContent` was added to the file to strengthen the content declaration.
139
140
 
141
+ **Tags Instructions:**
142
+
143
+ {{tagsInstructions}}
144
+
140
145
  **File to Audit:**
141
146
 
142
147
  {{fileContent}}
@@ -24,14 +24,16 @@ const formatLocaleWithName = (locale) => {
24
24
  const localeName = getLocaleName(locale);
25
25
  return `${locale}: ${localeName}`;
26
26
  };
27
- const auditFile = async ({
27
+ const formatTagInstructions = (tags = []) => tags.map((tag) => `- ${tag.key}: ${tag.instructions}`).join("\n\n");
28
+ const auditDictionary = async ({
28
29
  fileContent,
29
30
  filePath,
30
31
  model,
31
32
  openAiApiKey,
32
33
  customPrompt,
33
34
  locales,
34
- defaultLocale
35
+ defaultLocale,
36
+ tags
35
37
  }) => {
36
38
  try {
37
39
  const openai = new OpenAI({
@@ -48,7 +50,7 @@ const auditFile = async ({
48
50
  ).replace(
49
51
  "{{declarationsContentTemplate}}",
50
52
  FILE_TEMPLATE[fileExtension]
51
- ).replace("{{fileContent}}", fileContent);
53
+ ).replace("{{fileContent}}", fileContent).replace("{{tagsInstructions}}", formatTagInstructions(tags));
52
54
  const chatCompletion = await openai.chat.completions.create({
53
55
  model: model ?? "gpt-4o-mini",
54
56
  messages: [{ role: "system", content: prompt }]
@@ -66,6 +68,6 @@ const auditFile = async ({
66
68
  }
67
69
  };
68
70
  export {
69
- auditFile
71
+ auditDictionary
70
72
  };
71
73
  //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/utils/auditDictionary/index.ts"],"sourcesContent":["import { readFileSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { getLocaleName } from '@intlayer/core';\nimport { logger } from '@logger';\nimport { Locales } from 'intlayer';\nimport { OpenAI } from 'openai';\nimport { Tag } from '@/types/tag.types';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport type AuditOptions = {\n locales: Locales[];\n defaultLocale: Locales;\n fileContent: string;\n filePath?: string;\n model?: string;\n openAiApiKey: string;\n customPrompt?: string;\n tags?: Tag[];\n};\nexport type AuditFileResultData = { fileContent: string; tokenUsed: number };\n\n/**\n * Reads the content of a file synchronously.\n *\n * @function\n * @param relativeFilePath - The relative or absolute path to the target file.\n * @returns The entire contents of the specified file as a UTF-8 encoded string.\n */\nconst getFileContent = (relativeFilePath: string): string => {\n const absolutePath = join(__dirname, relativeFilePath);\n const fileContent = readFileSync(absolutePath, 'utf-8');\n return fileContent;\n};\n\nconst FILE_TEMPLATE: Record<string, string> = {\n ts: getFileContent('./TS_FORMAT.md'),\n tsx: getFileContent('./TSX_FORMAT.md'),\n js: getFileContent('./MJS_FORMAT.md'),\n mjs: getFileContent('./MJS_FORMAT.md'),\n cjs: getFileContent('./CJS_FORMAT.md'),\n jsx: getFileContent('./JSX_FORMAT.md'),\n json: getFileContent('./JSON_FORMAT.md'),\n};\n\n// The prompt template to send to ChatGPT, requesting an audit of content declaration files.\nconst CHAT_GPT_PROMPT = getFileContent('./PROMPT.md');\n\n/**\n * Formats a locale with its full name and returns a string representation.\n *\n * @function\n * @param locale - A locale from the project's configuration (e.g., 'en-US', 'fr-FR').\n * @returns A formatted string combining the locale's name and code. Example: \"English (US): en-US\".\n */\nconst formatLocaleWithName = (locale: Locales): string => {\n // getLocaleName returns a human-readable name for the locale.\n const localeName = getLocaleName(locale);\n\n // Concatenate both the readable name and the locale code.\n return `${locale}: ${localeName}`;\n};\n\n/**\n * Formats an array of tags with their keys and instructions.\n *\n * @function\n * @param tags - An array of tags from the project's configuration.\n * @returns A string representation of the tags, with their keys and instructions.\n */\nconst formatTagInstructions = (tags: Tag[] = []) =>\n tags.map((tag) => `- ${tag.key}: ${tag.instructions}`).join('\\n\\n');\n\n/**\n * Audits a content declaration file by constructing a prompt for ChatGPT.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies. It prints the prompt for each file,\n * and could be adapted to send requests to the ChatGPT model.\n */\nexport const auditDictionary = async ({\n fileContent,\n filePath,\n model,\n openAiApiKey,\n customPrompt,\n locales,\n defaultLocale,\n tags,\n}: AuditOptions): Promise<AuditFileResultData | undefined> => {\n try {\n // Optionally, you could initialize and configure the OpenAI client here, if you intend to make API calls.\n // Uncomment and configure the following lines if you have `openai` installed and want to call the API:\n\n const openai = new OpenAI({\n apiKey: openAiApiKey,\n });\n\n // Read the file's content.\n const splitted = (filePath ?? '.json').split('.');\n const fileExtension = splitted[splitted.length - 1];\n\n // Prepare the prompt for ChatGPT by replacing placeholders with actual values.\n const prompt =\n customPrompt ??\n CHAT_GPT_PROMPT.replace('{{filePath}}', filePath ?? 'Not provided')\n .replace(\n '{{defaultLocale}}',\n `{${formatLocaleWithName(defaultLocale)}}`\n )\n .replace(\n '{{otherLocales}}',\n `{${locales.map(formatLocaleWithName).join(', ')}}`\n )\n .replace(\n '{{declarationsContentTemplate}}',\n FILE_TEMPLATE[fileExtension]\n )\n .replace('{{fileContent}}', fileContent)\n .replace('{{tagsInstructions}}', formatTagInstructions(tags));\n\n // Example of how you might request a completion from ChatGPT:\n const chatCompletion = await openai.chat.completions.create({\n model: model ?? 'gpt-4o-mini',\n messages: [{ role: 'system', content: prompt }],\n });\n\n const newContent = chatCompletion.choices[0].message?.content;\n\n logger.info(\n `${chatCompletion.usage?.total_tokens} tokens used in the request`\n );\n\n return {\n fileContent: newContent ?? '',\n tokenUsed: chatCompletion.usage?.total_tokens ?? 0,\n };\n } catch (error) {\n console.error(error);\n }\n};\n"],"mappings":"AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,cAAc;AAEvB,SAAS,cAAc;AAGvB,MAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAqBxD,MAAM,iBAAiB,CAAC,qBAAqC;AAC3D,QAAM,eAAe,KAAK,WAAW,gBAAgB;AACrD,QAAM,cAAc,aAAa,cAAc,OAAO;AACtD,SAAO;AACT;AAEA,MAAM,gBAAwC;AAAA,EAC5C,IAAI,eAAe,gBAAgB;AAAA,EACnC,KAAK,eAAe,iBAAiB;AAAA,EACrC,IAAI,eAAe,iBAAiB;AAAA,EACpC,KAAK,eAAe,iBAAiB;AAAA,EACrC,KAAK,eAAe,iBAAiB;AAAA,EACrC,KAAK,eAAe,iBAAiB;AAAA,EACrC,MAAM,eAAe,kBAAkB;AACzC;AAGA,MAAM,kBAAkB,eAAe,aAAa;AASpD,MAAM,uBAAuB,CAAC,WAA4B;AAExD,QAAM,aAAa,cAAc,MAAM;AAGvC,SAAO,GAAG,MAAM,KAAK,UAAU;AACjC;AASA,MAAM,wBAAwB,CAAC,OAAc,CAAC,MAC5C,KAAK,IAAI,CAAC,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,YAAY,EAAE,EAAE,KAAK,MAAM;AAQ7D,MAAM,kBAAkB,OAAO;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAA8D;AAC5D,MAAI;AAIF,UAAM,SAAS,IAAI,OAAO;AAAA,MACxB,QAAQ;AAAA,IACV,CAAC;AAGD,UAAM,YAAY,YAAY,SAAS,MAAM,GAAG;AAChD,UAAM,gBAAgB,SAAS,SAAS,SAAS,CAAC;AAGlD,UAAM,SACJ,gBACA,gBAAgB,QAAQ,gBAAgB,YAAY,cAAc,EAC/D;AAAA,MACC;AAAA,MACA,IAAI,qBAAqB,aAAa,CAAC;AAAA,IACzC,EACC;AAAA,MACC;AAAA,MACA,IAAI,QAAQ,IAAI,oBAAoB,EAAE,KAAK,IAAI,CAAC;AAAA,IAClD,EACC;AAAA,MACC;AAAA,MACA,cAAc,aAAa;AAAA,IAC7B,EACC,QAAQ,mBAAmB,WAAW,EACtC,QAAQ,wBAAwB,sBAAsB,IAAI,CAAC;AAGhE,UAAM,iBAAiB,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,MAC1D,OAAO,SAAS;AAAA,MAChB,UAAU,CAAC,EAAE,MAAM,UAAU,SAAS,OAAO,CAAC;AAAA,IAChD,CAAC;AAED,UAAM,aAAa,eAAe,QAAQ,CAAC,EAAE,SAAS;AAEtD,WAAO;AAAA,MACL,GAAG,eAAe,OAAO,YAAY;AAAA,IACvC;AAEA,WAAO;AAAA,MACL,aAAa,cAAc;AAAA,MAC3B,WAAW,eAAe,OAAO,gBAAgB;AAAA,IACnD;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,KAAK;AAAA,EACrB;AACF;","names":[]}
@@ -0,0 +1,115 @@
1
+ You are an expert in internationalization, copy writing and content management. Your task is to audit the given element into the content declaration file. If the element is not declared, review it. If this element is missing, write it by considering the given context of the content declaration, and by following instructions.
2
+
3
+ **Instructions:**
4
+
5
+ 1. **Terminology:**
6
+
7
+ - **Content Declaration:** A content declaration is a is a file (.ts, .js or .json) that contains the multilingual declaration related to a specific content. A content declaration file is usually related to a specific component, or page or section of a website.
8
+ - **Tag:** A tag is attached to a content declaration and is used to group content declaration and harmonize them.
9
+ - **KeyPath** The KeyPath correspond to to the path to retrieve the targeted element from the `content` key of the content declaration file.
10
+
11
+ 2. **Locales:**
12
+
13
+ - Required Locales: {{otherLocales}}
14
+
15
+ 3. **Audit Requirements:**
16
+
17
+ - **Consistency:** Ensure that all keys have translations for all specified locales.
18
+ - **Missing Content:** Identify any missing translations and specify the expected content.
19
+ - **Misplaced Content:** Detect if any translations are placed under incorrect keys.
20
+ - **Type Compliance:** Verify that the content types match the declarations (e.g., strings, string arrays).
21
+
22
+ 4. **Modification Guidelines:**
23
+
24
+ - **Return Only the Targeted Content:** Provide the updated targeted content as plain text without any markdown, additional comments or explanations.
25
+ - **Consider the locale context:** If the targeted field correspond to a specific language, contains similar languages, as `zh` or `en-GB`, consider return the content in this specified language.
26
+ - **Escape Special Characters:** If the translations contain special characters, escape them using the appropriate escape sequence.
27
+ - **Respect the tags instructions:** If the tags instructions are provided, ensure that the audited file adheres to them.
28
+
29
+ 5. **Example Scenario:**
30
+
31
+ - **Example 1:**
32
+
33
+ - **Input File:**
34
+
35
+ ```typescript
36
+ import { t, type DeclarationContent } from "intlayer";
37
+
38
+ export default {
39
+ key: "creative-work-structured-data",
40
+ content: {
41
+ audienceType: t({
42
+ en: "Developers, Content Managers",
43
+ fr: "Développeurs, Responsables de contenu",
44
+ es: "",
45
+ }),
46
+ },
47
+ } satisfies DeclarationContent;
48
+ ```
49
+
50
+ - **Input keyPath:**
51
+
52
+ `[{type: "object", key: "audienceType"},{type: "translation", key: "es"}]`
53
+
54
+ - **Expected Output:**
55
+
56
+ Desarrolladores, Gestores de Contenido
57
+
58
+ - **Example 2:**
59
+
60
+ - **Input File:**
61
+
62
+ ```typescript
63
+ import { t, enu, type DeclarationContent } from "intlayer";
64
+
65
+ const content = {
66
+ key: "creative-work-structured-data",
67
+ content: {
68
+ en: {
69
+ audienceType: t({
70
+ en: "Developers, Content Managers",
71
+ fr: "Développeurs, Responsables de contenu",
72
+ es: "Desarrolladores, Gestores de Contenido",
73
+ }),
74
+ },
75
+ audienceType: {
76
+ object1: enu({
77
+ "1": t({
78
+ en: "Developers, Content Managers",
79
+ es: "Desarrolladores, Gestores de Contenido",
80
+ fr: "Développeurs, Responsables de contenu",
81
+ }),
82
+ ">2": t({
83
+ en: "Developers, Content Managers",
84
+ es: "Desarrolladores, Gestores de Contenido",
85
+ fr: "Here content thst make no sense and should be replaced",
86
+ }),
87
+ }),
88
+ },
89
+ },
90
+ } satisfies DeclarationContent;
91
+ ```
92
+
93
+ - **Input target:**
94
+
95
+ `[{type: "object", key: "audienceType"},{type: "enumeration", key: ">2"},{type: "object", key: "audienceType"},{type: "translation", key: "es"}]`
96
+
97
+ - **Expected Output:**
98
+
99
+ Développeurs, Responsables de contenu
100
+
101
+ **Tags Instructions:**
102
+
103
+ {{tagsInstructions}}
104
+
105
+ **File to Audit:**
106
+
107
+ {{fileContent}}
108
+
109
+ **KeyPath:**
110
+
111
+ {{keyPath}}
112
+
113
+ **Expected Response:**
114
+
115
+ After auditing, provide only the targeted content as plain text without any Markdown or code block formatting. If no changes are needed, return the targeted content