@solidxai/core 0.1.13-beta.2 → 0.1.13-beta.21

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 (517) hide show
  1. package/CHANGELOG.md +10107 -0
  2. package/CLAUDE.md +5 -6
  3. package/README.md +19 -0
  4. package/dist/commands/run-tests.command.d.ts +9 -1
  5. package/dist/commands/run-tests.command.d.ts.map +1 -1
  6. package/dist/commands/run-tests.command.js +69 -6
  7. package/dist/commands/run-tests.command.js.map +1 -1
  8. package/dist/commands/seed.command.d.ts +2 -0
  9. package/dist/commands/seed.command.d.ts.map +1 -1
  10. package/dist/commands/seed.command.js +21 -0
  11. package/dist/commands/seed.command.js.map +1 -1
  12. package/dist/config/cache.options.d.ts.map +1 -1
  13. package/dist/config/cache.options.js +2 -2
  14. package/dist/config/cache.options.js.map +1 -1
  15. package/dist/constants/chatter-message.constants.d.ts +1 -0
  16. package/dist/constants/chatter-message.constants.d.ts.map +1 -1
  17. package/dist/constants/chatter-message.constants.js +3 -1
  18. package/dist/constants/chatter-message.constants.js.map +1 -1
  19. package/dist/constants/draft-publish-fields.d.ts +2 -0
  20. package/dist/constants/draft-publish-fields.d.ts.map +1 -0
  21. package/dist/constants/draft-publish-fields.js +5 -0
  22. package/dist/constants/draft-publish-fields.js.map +1 -0
  23. package/dist/constants/error-messages.d.ts +7 -0
  24. package/dist/constants/error-messages.d.ts.map +1 -1
  25. package/dist/constants/error-messages.js +7 -0
  26. package/dist/constants/error-messages.js.map +1 -1
  27. package/dist/constants/media-file-types.d.ts +12 -0
  28. package/dist/constants/media-file-types.d.ts.map +1 -1
  29. package/dist/constants/media-file-types.js +66 -1
  30. package/dist/constants/media-file-types.js.map +1 -1
  31. package/dist/constants/success-messages.d.ts +3 -0
  32. package/dist/constants/success-messages.d.ts.map +1 -1
  33. package/dist/constants/success-messages.js +3 -0
  34. package/dist/constants/success-messages.js.map +1 -1
  35. package/dist/constants/user.constants.d.ts +2 -0
  36. package/dist/constants/user.constants.d.ts.map +1 -0
  37. package/dist/constants/user.constants.js +5 -0
  38. package/dist/constants/user.constants.js.map +1 -0
  39. package/dist/controllers/action-metadata.controller.d.ts +1 -1
  40. package/dist/controllers/action-metadata.controller.js +1 -1
  41. package/dist/controllers/agent-event.controller.d.ts +1 -1
  42. package/dist/controllers/agent-event.controller.js +1 -1
  43. package/dist/controllers/agent-session.controller.d.ts +1 -1
  44. package/dist/controllers/agent-session.controller.js +1 -1
  45. package/dist/controllers/authentication.controller.d.ts +1 -1
  46. package/dist/controllers/authentication.controller.d.ts.map +1 -1
  47. package/dist/controllers/authentication.controller.js +4 -3
  48. package/dist/controllers/authentication.controller.js.map +1 -1
  49. package/dist/controllers/chatter-message.controller.d.ts +5 -0
  50. package/dist/controllers/chatter-message.controller.d.ts.map +1 -1
  51. package/dist/controllers/chatter-message.controller.js +14 -0
  52. package/dist/controllers/chatter-message.controller.js.map +1 -1
  53. package/dist/controllers/dashboard-user-layout.controller.d.ts +1 -1
  54. package/dist/controllers/dashboard-user-layout.controller.js +1 -1
  55. package/dist/controllers/email-template.controller.d.ts +1 -1
  56. package/dist/controllers/email-template.controller.js +1 -1
  57. package/dist/controllers/export-template.controller.d.ts +1 -1
  58. package/dist/controllers/export-template.controller.d.ts.map +1 -1
  59. package/dist/controllers/export-template.controller.js +6 -4
  60. package/dist/controllers/export-template.controller.js.map +1 -1
  61. package/dist/controllers/export-transaction.controller.d.ts +1 -1
  62. package/dist/controllers/export-transaction.controller.js +1 -1
  63. package/dist/controllers/field-metadata.controller.d.ts +2 -2
  64. package/dist/controllers/import-transaction-error-log.controller.d.ts +1 -1
  65. package/dist/controllers/import-transaction-error-log.controller.js +1 -1
  66. package/dist/controllers/import-transaction.controller.d.ts +1 -1
  67. package/dist/controllers/import-transaction.controller.d.ts.map +1 -1
  68. package/dist/controllers/import-transaction.controller.js +7 -7
  69. package/dist/controllers/import-transaction.controller.js.map +1 -1
  70. package/dist/controllers/list-of-values.controller.d.ts +1 -1
  71. package/dist/controllers/list-of-values.controller.js +1 -1
  72. package/dist/controllers/locale.controller.d.ts +1 -1
  73. package/dist/controllers/locale.controller.js +1 -1
  74. package/dist/controllers/mcp-audit-log.controller.d.ts +1 -1
  75. package/dist/controllers/mcp-audit-log.controller.js +1 -1
  76. package/dist/controllers/media.controller.d.ts +2 -2
  77. package/dist/controllers/media.controller.d.ts.map +1 -1
  78. package/dist/controllers/media.controller.js +13 -8
  79. package/dist/controllers/media.controller.js.map +1 -1
  80. package/dist/controllers/menu-item-metadata.controller.d.ts +1 -1
  81. package/dist/controllers/menu-item-metadata.controller.js +1 -1
  82. package/dist/controllers/model-sequence.controller.d.ts +1 -1
  83. package/dist/controllers/model-sequence.controller.js +1 -1
  84. package/dist/controllers/module-package.controller.d.ts.map +1 -1
  85. package/dist/controllers/module-package.controller.js +2 -3
  86. package/dist/controllers/module-package.controller.js.map +1 -1
  87. package/dist/controllers/mpin-authentication.controller.d.ts +44 -0
  88. package/dist/controllers/mpin-authentication.controller.d.ts.map +1 -0
  89. package/dist/controllers/mpin-authentication.controller.js +102 -0
  90. package/dist/controllers/mpin-authentication.controller.js.map +1 -0
  91. package/dist/controllers/mq-message-queue.controller.d.ts +1 -1
  92. package/dist/controllers/mq-message-queue.controller.js +1 -1
  93. package/dist/controllers/mq-message.controller.d.ts +1 -1
  94. package/dist/controllers/mq-message.controller.js +1 -1
  95. package/dist/controllers/permission-metadata.controller.d.ts +1 -1
  96. package/dist/controllers/permission-metadata.controller.js +1 -1
  97. package/dist/controllers/role-metadata.controller.d.ts +1 -1
  98. package/dist/controllers/role-metadata.controller.js +1 -1
  99. package/dist/controllers/saved-filters.controller.d.ts +1 -1
  100. package/dist/controllers/saved-filters.controller.js +1 -1
  101. package/dist/controllers/scheduled-job.controller.d.ts +1 -1
  102. package/dist/controllers/scheduled-job.controller.js +1 -1
  103. package/dist/controllers/security-rule.controller.d.ts +1 -1
  104. package/dist/controllers/security-rule.controller.js +1 -1
  105. package/dist/controllers/sms-template.controller.d.ts +1 -1
  106. package/dist/controllers/sms-template.controller.js +1 -1
  107. package/dist/controllers/user-activity-history.controller.d.ts +1 -1
  108. package/dist/controllers/user-activity-history.controller.js +1 -1
  109. package/dist/controllers/user-view-metadata.controller.d.ts +1 -1
  110. package/dist/controllers/user-view-metadata.controller.js +1 -1
  111. package/dist/controllers/user.controller.d.ts +2 -2
  112. package/dist/controllers/user.controller.js +2 -2
  113. package/dist/controllers/view-metadata.controller.d.ts +2 -2
  114. package/dist/controllers/view-metadata.controller.js +1 -1
  115. package/dist/controllers/workflow-definition.controller.d.ts +1 -1
  116. package/dist/controllers/workflow-definition.controller.js +1 -1
  117. package/dist/controllers/workflow-execution-artifact.controller.d.ts +1 -1
  118. package/dist/controllers/workflow-execution-artifact.controller.js +1 -1
  119. package/dist/controllers/workflow-execution-log.controller.d.ts +1 -1
  120. package/dist/controllers/workflow-execution-log.controller.js +1 -1
  121. package/dist/controllers/workflow-execution.controller.d.ts +1 -1
  122. package/dist/controllers/workflow-execution.controller.js +1 -1
  123. package/dist/controllers/workflow-secret.controller.d.ts +1 -1
  124. package/dist/controllers/workflow-secret.controller.js +1 -1
  125. package/dist/controllers/workflow-step-execution.controller.d.ts +1 -1
  126. package/dist/controllers/workflow-step-execution.controller.js +1 -1
  127. package/dist/controllers/workflow-trigger-execution.controller.d.ts +1 -1
  128. package/dist/controllers/workflow-trigger-execution.controller.js +1 -1
  129. package/dist/dtos/basic-filters.dto.d.ts +0 -1
  130. package/dist/dtos/basic-filters.dto.d.ts.map +1 -1
  131. package/dist/dtos/basic-filters.dto.js +1 -6
  132. package/dist/dtos/basic-filters.dto.js.map +1 -1
  133. package/dist/dtos/change-mpin.dto.d.ts +6 -0
  134. package/dist/dtos/change-mpin.dto.d.ts.map +1 -0
  135. package/dist/dtos/change-mpin.dto.js +39 -0
  136. package/dist/dtos/change-mpin.dto.js.map +1 -0
  137. package/dist/dtos/create-field-metadata.dto.d.ts +3 -6
  138. package/dist/dtos/create-field-metadata.dto.d.ts.map +1 -1
  139. package/dist/dtos/create-field-metadata.dto.js +15 -10
  140. package/dist/dtos/create-field-metadata.dto.js.map +1 -1
  141. package/dist/dtos/create-model-sequence.dto.d.ts +1 -1
  142. package/dist/dtos/create-model-sequence.dto.d.ts.map +1 -1
  143. package/dist/dtos/create-model-sequence.dto.js +1 -1
  144. package/dist/dtos/create-model-sequence.dto.js.map +1 -1
  145. package/dist/dtos/mpin-login.dto.d.ts +5 -0
  146. package/dist/dtos/mpin-login.dto.d.ts.map +1 -0
  147. package/dist/dtos/mpin-login.dto.js +33 -0
  148. package/dist/dtos/mpin-login.dto.js.map +1 -0
  149. package/dist/dtos/setup-mpin.dto.d.ts +7 -0
  150. package/dist/dtos/setup-mpin.dto.d.ts.map +1 -0
  151. package/dist/dtos/setup-mpin.dto.js +47 -0
  152. package/dist/dtos/setup-mpin.dto.js.map +1 -0
  153. package/dist/entities/common.entity.d.ts +4 -0
  154. package/dist/entities/common.entity.d.ts.map +1 -1
  155. package/dist/entities/common.entity.js +25 -1
  156. package/dist/entities/common.entity.js.map +1 -1
  157. package/dist/entities/dashboard-user-layout.entity.js +2 -2
  158. package/dist/entities/dashboard-user-layout.entity.js.map +1 -1
  159. package/dist/entities/field-metadata.entity.d.ts +1 -0
  160. package/dist/entities/field-metadata.entity.d.ts.map +1 -1
  161. package/dist/entities/field-metadata.entity.js +5 -1
  162. package/dist/entities/field-metadata.entity.js.map +1 -1
  163. package/dist/entities/saved-filters.entity.js +3 -3
  164. package/dist/entities/saved-filters.entity.js.map +1 -1
  165. package/dist/entities/user-device-credential.entity.d.ts +18 -0
  166. package/dist/entities/user-device-credential.entity.d.ts.map +1 -0
  167. package/dist/entities/user-device-credential.entity.js +91 -0
  168. package/dist/entities/user-device-credential.entity.js.map +1 -0
  169. package/dist/entities/user.entity.d.ts.map +1 -1
  170. package/dist/entities/user.entity.js +36 -0
  171. package/dist/entities/user.entity.js.map +1 -1
  172. package/dist/entities/view-metadata.entity.js.map +1 -1
  173. package/dist/entities/workflow-execution-artifact.entity.d.ts.map +1 -1
  174. package/dist/entities/workflow-execution-artifact.entity.js +4 -3
  175. package/dist/entities/workflow-execution-artifact.entity.js.map +1 -1
  176. package/dist/entities/workflow-execution-log.entity.d.ts.map +1 -1
  177. package/dist/entities/workflow-execution-log.entity.js +4 -3
  178. package/dist/entities/workflow-execution-log.entity.js.map +1 -1
  179. package/dist/entities/workflow-execution.entity.d.ts.map +1 -1
  180. package/dist/entities/workflow-execution.entity.js +3 -2
  181. package/dist/entities/workflow-execution.entity.js.map +1 -1
  182. package/dist/entities/workflow-secret.entity.d.ts.map +1 -1
  183. package/dist/entities/workflow-secret.entity.js +3 -2
  184. package/dist/entities/workflow-secret.entity.js.map +1 -1
  185. package/dist/entities/workflow-step-execution.entity.d.ts.map +1 -1
  186. package/dist/entities/workflow-step-execution.entity.js +3 -2
  187. package/dist/entities/workflow-step-execution.entity.js.map +1 -1
  188. package/dist/entities/workflow-trigger-execution.entity.d.ts.map +1 -1
  189. package/dist/entities/workflow-trigger-execution.entity.js +7 -6
  190. package/dist/entities/workflow-trigger-execution.entity.js.map +1 -1
  191. package/dist/factories/mail.factory.d.ts.map +1 -1
  192. package/dist/factories/mail.factory.js +4 -0
  193. package/dist/factories/mail.factory.js.map +1 -1
  194. package/dist/filters/http-exception.filter.d.ts.map +1 -1
  195. package/dist/filters/http-exception.filter.js +5 -1
  196. package/dist/filters/http-exception.filter.js.map +1 -1
  197. package/dist/helpers/bootstrap.helper.d.ts +5 -2
  198. package/dist/helpers/bootstrap.helper.d.ts.map +1 -1
  199. package/dist/helpers/bootstrap.helper.js +7 -3
  200. package/dist/helpers/bootstrap.helper.js.map +1 -1
  201. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts +7 -0
  202. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
  203. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +66 -32
  204. package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
  205. package/dist/helpers/file-download.helper.d.ts +9 -0
  206. package/dist/helpers/file-download.helper.d.ts.map +1 -0
  207. package/dist/helpers/file-download.helper.js +19 -0
  208. package/dist/helpers/file-download.helper.js.map +1 -0
  209. package/dist/helpers/model-metadata-helper.service.d.ts.map +1 -1
  210. package/dist/helpers/model-metadata-helper.service.js +30 -1
  211. package/dist/helpers/model-metadata-helper.service.js.map +1 -1
  212. package/dist/helpers/security.helper.d.ts +5 -1
  213. package/dist/helpers/security.helper.d.ts.map +1 -1
  214. package/dist/helpers/security.helper.js +15 -4
  215. package/dist/helpers/security.helper.js.map +1 -1
  216. package/dist/helpers/solid-core-error-codes-provider.service.d.ts.map +1 -1
  217. package/dist/helpers/solid-core-error-codes-provider.service.js +45 -0
  218. package/dist/helpers/solid-core-error-codes-provider.service.js.map +1 -1
  219. package/dist/helpers/solid-microservice-adapter.service.d.ts.map +1 -1
  220. package/dist/helpers/solid-microservice-adapter.service.js +1 -1
  221. package/dist/helpers/solid-microservice-adapter.service.js.map +1 -1
  222. package/dist/helpers/solid-registry.js +1 -1
  223. package/dist/helpers/solid-registry.js.map +1 -1
  224. package/dist/helpers/typeorm-db-helper.d.ts.map +1 -1
  225. package/dist/helpers/typeorm-db-helper.js +9 -0
  226. package/dist/helpers/typeorm-db-helper.js.map +1 -1
  227. package/dist/index.d.ts +10 -0
  228. package/dist/index.d.ts.map +1 -1
  229. package/dist/index.js +10 -0
  230. package/dist/index.js.map +1 -1
  231. package/dist/interfaces/active-user-data.interface.d.ts +1 -0
  232. package/dist/interfaces/active-user-data.interface.d.ts.map +1 -1
  233. package/dist/interfaces/active-user-data.interface.js.map +1 -1
  234. package/dist/interfaces.d.ts +1 -0
  235. package/dist/interfaces.d.ts.map +1 -1
  236. package/dist/interfaces.js.map +1 -1
  237. package/dist/repository/user-device-credential.repository.d.ts +12 -0
  238. package/dist/repository/user-device-credential.repository.d.ts.map +1 -0
  239. package/dist/repository/user-device-credential.repository.js +34 -0
  240. package/dist/repository/user-device-credential.repository.js.map +1 -0
  241. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  242. package/dist/seeders/module-metadata-seeder.service.js +51 -30
  243. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  244. package/dist/seeders/module-test-data.service.d.ts +3 -1
  245. package/dist/seeders/module-test-data.service.d.ts.map +1 -1
  246. package/dist/seeders/module-test-data.service.js +8 -2
  247. package/dist/seeders/module-test-data.service.js.map +1 -1
  248. package/dist/seeders/seed-data/solid-core-metadata.json +290 -78
  249. package/dist/services/api-key.service.d.ts.map +1 -1
  250. package/dist/services/api-key.service.js +1 -1
  251. package/dist/services/api-key.service.js.map +1 -1
  252. package/dist/services/authentication.service.d.ts +9 -5
  253. package/dist/services/authentication.service.d.ts.map +1 -1
  254. package/dist/services/authentication.service.js +102 -23
  255. package/dist/services/authentication.service.js.map +1 -1
  256. package/dist/services/chatter-message.service.d.ts +16 -1
  257. package/dist/services/chatter-message.service.d.ts.map +1 -1
  258. package/dist/services/chatter-message.service.js +100 -16
  259. package/dist/services/chatter-message.service.js.map +1 -1
  260. package/dist/services/crud-helper.service.d.ts +6 -4
  261. package/dist/services/crud-helper.service.d.ts.map +1 -1
  262. package/dist/services/crud-helper.service.js +32 -48
  263. package/dist/services/crud-helper.service.js.map +1 -1
  264. package/dist/services/crud.service.d.ts +13 -1
  265. package/dist/services/crud.service.d.ts.map +1 -1
  266. package/dist/services/crud.service.js +94 -68
  267. package/dist/services/crud.service.js.map +1 -1
  268. package/dist/services/datasource-introspection.service.d.ts.map +1 -1
  269. package/dist/services/datasource-introspection.service.js.map +1 -1
  270. package/dist/services/draft-publish-helper.service.d.ts +53 -0
  271. package/dist/services/draft-publish-helper.service.d.ts.map +1 -0
  272. package/dist/services/draft-publish-helper.service.js +402 -0
  273. package/dist/services/draft-publish-helper.service.js.map +1 -0
  274. package/dist/services/field-metadata.service.d.ts +2 -2
  275. package/dist/services/field-metadata.service.d.ts.map +1 -1
  276. package/dist/services/field-metadata.service.js +4 -1
  277. package/dist/services/field-metadata.service.js.map +1 -1
  278. package/dist/services/internationalisation-helper.service.d.ts +19 -0
  279. package/dist/services/internationalisation-helper.service.d.ts.map +1 -0
  280. package/dist/services/internationalisation-helper.service.js +107 -0
  281. package/dist/services/internationalisation-helper.service.js.map +1 -0
  282. package/dist/services/locale.service.d.ts +1 -1
  283. package/dist/services/media-storage.utils.d.ts +1 -0
  284. package/dist/services/media-storage.utils.d.ts.map +1 -1
  285. package/dist/services/media-storage.utils.js +12 -1
  286. package/dist/services/media-storage.utils.js.map +1 -1
  287. package/dist/services/media.service.d.ts +3 -2
  288. package/dist/services/media.service.d.ts.map +1 -1
  289. package/dist/services/media.service.js +45 -29
  290. package/dist/services/media.service.js.map +1 -1
  291. package/dist/services/metadata-validation.service.d.ts +7 -0
  292. package/dist/services/metadata-validation.service.d.ts.map +1 -0
  293. package/dist/services/metadata-validation.service.js +42 -0
  294. package/dist/services/metadata-validation.service.js.map +1 -0
  295. package/dist/services/model-metadata.service.d.ts.map +1 -1
  296. package/dist/services/model-metadata.service.js +2 -1
  297. package/dist/services/model-metadata.service.js.map +1 -1
  298. package/dist/services/module-metadata.service.d.ts.map +1 -1
  299. package/dist/services/module-metadata.service.js +9 -1
  300. package/dist/services/module-metadata.service.js.map +1 -1
  301. package/dist/services/mpin.service.d.ts +67 -0
  302. package/dist/services/mpin.service.d.ts.map +1 -0
  303. package/dist/services/mpin.service.js +282 -0
  304. package/dist/services/mpin.service.js.map +1 -0
  305. package/dist/services/object.utils.d.ts +2 -0
  306. package/dist/services/object.utils.d.ts.map +1 -0
  307. package/dist/services/object.utils.js +11 -0
  308. package/dist/services/object.utils.js.map +1 -0
  309. package/dist/services/refresh-token-ids-storage.service.d.ts +25 -6
  310. package/dist/services/refresh-token-ids-storage.service.d.ts.map +1 -1
  311. package/dist/services/refresh-token-ids-storage.service.js +80 -42
  312. package/dist/services/refresh-token-ids-storage.service.js.map +1 -1
  313. package/dist/services/setting.service.d.ts.map +1 -1
  314. package/dist/services/setting.service.js +6 -1
  315. package/dist/services/setting.service.js.map +1 -1
  316. package/dist/services/settings/default-settings-provider.service.d.ts +248 -12
  317. package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
  318. package/dist/services/settings/default-settings-provider.service.js +143 -6
  319. package/dist/services/settings/default-settings-provider.service.js.map +1 -1
  320. package/dist/services/user.service.d.ts +2 -2
  321. package/dist/services/user.service.d.ts.map +1 -1
  322. package/dist/services/user.service.js +9 -3
  323. package/dist/services/user.service.js.map +1 -1
  324. package/dist/services/view-metadata.service.d.ts +1 -2
  325. package/dist/services/view-metadata.service.d.ts.map +1 -1
  326. package/dist/services/view-metadata.service.js +3 -55
  327. package/dist/services/view-metadata.service.js.map +1 -1
  328. package/dist/services/workflow/nodes/execution-fail.node.d.ts +6 -0
  329. package/dist/services/workflow/nodes/execution-fail.node.d.ts.map +1 -0
  330. package/dist/services/workflow/nodes/execution-fail.node.js +81 -0
  331. package/dist/services/workflow/nodes/execution-fail.node.js.map +1 -0
  332. package/dist/services/workflow/nodes/for-each.node.d.ts +2 -0
  333. package/dist/services/workflow/nodes/for-each.node.d.ts.map +1 -1
  334. package/dist/services/workflow/nodes/for-each.node.js +114 -5
  335. package/dist/services/workflow/nodes/for-each.node.js.map +1 -1
  336. package/dist/services/workflow/nodes/if.node.d.ts.map +1 -1
  337. package/dist/services/workflow/nodes/if.node.js +7 -0
  338. package/dist/services/workflow/nodes/if.node.js.map +1 -1
  339. package/dist/services/workflow/nodes/log-write.node.d.ts.map +1 -1
  340. package/dist/services/workflow/nodes/log-write.node.js +2 -0
  341. package/dist/services/workflow/nodes/log-write.node.js.map +1 -1
  342. package/dist/services/workflow/nodes/loop-until.node.d.ts +12 -0
  343. package/dist/services/workflow/nodes/loop-until.node.d.ts.map +1 -0
  344. package/dist/services/workflow/nodes/loop-until.node.js +281 -0
  345. package/dist/services/workflow/nodes/loop-until.node.js.map +1 -0
  346. package/dist/services/workflow/nodes/parallel.node.d.ts.map +1 -1
  347. package/dist/services/workflow/nodes/parallel.node.js +7 -0
  348. package/dist/services/workflow/nodes/parallel.node.js.map +1 -1
  349. package/dist/services/workflow/nodes/sequential.node.d.ts.map +1 -1
  350. package/dist/services/workflow/nodes/sequential.node.js +7 -0
  351. package/dist/services/workflow/nodes/sequential.node.js.map +1 -1
  352. package/dist/services/workflow/nodes/switch.node.d.ts.map +1 -1
  353. package/dist/services/workflow/nodes/switch.node.js +7 -0
  354. package/dist/services/workflow/nodes/switch.node.js.map +1 -1
  355. package/dist/services/workflow/workflow-definition-validator.service.d.ts.map +1 -1
  356. package/dist/services/workflow/workflow-definition-validator.service.js +5 -2
  357. package/dist/services/workflow/workflow-definition-validator.service.js.map +1 -1
  358. package/dist/services/workflow/workflow-expression.service.d.ts.map +1 -1
  359. package/dist/services/workflow/workflow-expression.service.js +5 -0
  360. package/dist/services/workflow/workflow-expression.service.js.map +1 -1
  361. package/dist/services/workflow/workflow-node-registry.service.d.ts +2 -1
  362. package/dist/services/workflow/workflow-node-registry.service.d.ts.map +1 -1
  363. package/dist/services/workflow/workflow-node-registry.service.js +7 -0
  364. package/dist/services/workflow/workflow-node-registry.service.js.map +1 -1
  365. package/dist/services/workflow/workflow-runtime.service.d.ts +8 -0
  366. package/dist/services/workflow/workflow-runtime.service.d.ts.map +1 -1
  367. package/dist/services/workflow/workflow-runtime.service.js +197 -32
  368. package/dist/services/workflow/workflow-runtime.service.js.map +1 -1
  369. package/dist/services/workflow-definition.service.d.ts +1 -1
  370. package/dist/solid-core.module.d.ts +1 -3
  371. package/dist/solid-core.module.d.ts.map +1 -1
  372. package/dist/solid-core.module.js +28 -40
  373. package/dist/solid-core.module.js.map +1 -1
  374. package/dist/testing/adapters/ui/playwright-adapter.d.ts +5 -0
  375. package/dist/testing/adapters/ui/playwright-adapter.d.ts.map +1 -1
  376. package/dist/testing/adapters/ui/playwright-adapter.js +17 -1
  377. package/dist/testing/adapters/ui/playwright-adapter.js.map +1 -1
  378. package/dist/testing/adapters/ui/ui.types.d.ts +2 -0
  379. package/dist/testing/adapters/ui/ui.types.d.ts.map +1 -1
  380. package/dist/testing/adapters/ui/ui.types.js.map +1 -1
  381. package/dist/testing/reporter/console-reporter.d.ts +3 -0
  382. package/dist/testing/reporter/console-reporter.d.ts.map +1 -1
  383. package/dist/testing/reporter/console-reporter.js +2 -0
  384. package/dist/testing/reporter/console-reporter.js.map +1 -1
  385. package/dist/testing/reporter/progress-reporter.d.ts +64 -0
  386. package/dist/testing/reporter/progress-reporter.d.ts.map +1 -0
  387. package/dist/testing/reporter/progress-reporter.js +224 -0
  388. package/dist/testing/reporter/progress-reporter.js.map +1 -0
  389. package/dist/testing/reporter/reporter.types.d.ts +3 -0
  390. package/dist/testing/reporter/reporter.types.d.ts.map +1 -1
  391. package/dist/testing/reporter/reporter.types.js.map +1 -1
  392. package/dist/testing/runner/run-from-metadata.d.ts.map +1 -1
  393. package/dist/testing/runner/run-from-metadata.js +1 -0
  394. package/dist/testing/runner/run-from-metadata.js.map +1 -1
  395. package/dist/testing/steps/ui/actions.step.d.ts.map +1 -1
  396. package/dist/testing/steps/ui/actions.step.js +6 -2
  397. package/dist/testing/steps/ui/actions.step.js.map +1 -1
  398. package/dist/testing/steps/ui/assertions.step.d.ts.map +1 -1
  399. package/dist/testing/steps/ui/assertions.step.js +11 -3
  400. package/dist/testing/steps/ui/assertions.step.js.map +1 -1
  401. package/dist/testing/steps/ui/form.step.d.ts.map +1 -1
  402. package/dist/testing/steps/ui/form.step.js +9 -3
  403. package/dist/testing/steps/ui/form.step.js.map +1 -1
  404. package/dist/testing/steps/ui/manual.step.js +1 -1
  405. package/dist/testing/steps/ui/manual.step.js.map +1 -1
  406. package/dist/testing/steps/ui/navigation.step.d.ts.map +1 -1
  407. package/dist/testing/steps/ui/navigation.step.js +3 -1
  408. package/dist/testing/steps/ui/navigation.step.js.map +1 -1
  409. package/dist/theme/theme-registry.d.ts +14 -0
  410. package/dist/theme/theme-registry.d.ts.map +1 -0
  411. package/dist/theme/theme-registry.js +147 -0
  412. package/dist/theme/theme-registry.js.map +1 -0
  413. package/dist/types/workflow-dsl.types.d.ts +14 -0
  414. package/dist/types/workflow-dsl.types.d.ts.map +1 -1
  415. package/dist/types/workflow-dsl.types.js.map +1 -1
  416. package/package.json +2 -2
  417. package/src/commands/run-tests.command.ts +64 -4
  418. package/src/commands/seed.command.ts +19 -0
  419. package/src/config/cache.options.ts +9 -1
  420. package/src/constants/chatter-message.constants.ts +12 -0
  421. package/src/constants/draft-publish-fields.ts +5 -0
  422. package/src/constants/error-messages.ts +14 -0
  423. package/src/constants/media-file-types.ts +128 -0
  424. package/src/constants/success-messages.ts +5 -0
  425. package/src/constants/user.constants.ts +13 -0
  426. package/src/controllers/authentication.controller.ts +5 -2
  427. package/src/controllers/chatter-message.controller.ts +8 -0
  428. package/src/controllers/export-template.controller.ts +5 -3
  429. package/src/controllers/import-transaction.controller.ts +6 -6
  430. package/src/controllers/media.controller.ts +17 -6
  431. package/src/controllers/module-package.controller.ts +2 -3
  432. package/src/controllers/mpin-authentication.controller.ts +67 -0
  433. package/src/dtos/basic-filters.dto.ts +0 -4
  434. package/src/dtos/change-mpin.dto.ts +18 -0
  435. package/src/dtos/create-field-metadata.dto.ts +17 -9
  436. package/src/dtos/create-model-sequence.dto.ts +1 -1
  437. package/src/dtos/mpin-login.dto.ts +24 -0
  438. package/src/dtos/setup-mpin.dto.ts +51 -0
  439. package/src/entities/common.entity.ts +24 -0
  440. package/src/entities/dashboard-user-layout.entity.ts +2 -2
  441. package/src/entities/field-metadata.entity.ts +3 -0
  442. package/src/entities/saved-filters.entity.ts +3 -3
  443. package/src/entities/user-device-credential.entity.ts +130 -0
  444. package/src/entities/user.entity.ts +53 -36
  445. package/src/entities/view-metadata.entity.ts +2 -2
  446. package/src/entities/workflow-execution-artifact.entity.ts +4 -3
  447. package/src/entities/workflow-execution-log.entity.ts +4 -3
  448. package/src/entities/workflow-execution.entity.ts +3 -2
  449. package/src/entities/workflow-secret.entity.ts +3 -2
  450. package/src/entities/workflow-step-execution.entity.ts +3 -2
  451. package/src/entities/workflow-trigger-execution.entity.ts +7 -6
  452. package/src/factories/mail.factory.ts +7 -1
  453. package/src/filters/http-exception.filter.ts +11 -1
  454. package/src/helpers/bootstrap.helper.ts +43 -6
  455. package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +90 -37
  456. package/src/helpers/file-download.helper.ts +45 -0
  457. package/src/helpers/model-metadata-helper.service.ts +32 -1
  458. package/src/helpers/security.helper.ts +35 -8
  459. package/src/helpers/solid-core-error-codes-provider.service.ts +53 -0
  460. package/src/helpers/solid-microservice-adapter.service.ts +6 -1
  461. package/src/helpers/solid-registry.ts +1 -1
  462. package/src/helpers/typeorm-db-helper.ts +12 -1
  463. package/src/index.ts +10 -0
  464. package/src/interfaces/active-user-data.interface.ts +9 -0
  465. package/src/interfaces.ts +1 -0
  466. package/src/repository/user-device-credential.repository.ts +17 -0
  467. package/src/seeders/module-metadata-seeder.service.ts +61 -39
  468. package/src/seeders/module-test-data.service.ts +6 -0
  469. package/src/seeders/seed-data/solid-core-metadata.json +290 -78
  470. package/src/services/api-key.service.ts +13 -1
  471. package/src/services/authentication.service.ts +203 -41
  472. package/src/services/chatter-message.service.ts +180 -19
  473. package/src/services/crud-helper.service.ts +35 -50
  474. package/src/services/crud.service.ts +132 -105
  475. package/src/services/datasource-introspection.service.ts +5 -0
  476. package/src/services/draft-publish-helper.service.ts +581 -0
  477. package/src/services/field-metadata.service.ts +5 -2
  478. package/src/services/internationalisation-helper.service.ts +148 -0
  479. package/src/services/media-storage.utils.ts +26 -1
  480. package/src/services/media.service.ts +68 -36
  481. package/src/services/metadata-validation.service.ts +39 -0
  482. package/src/services/model-metadata.service.ts +4 -1
  483. package/src/services/module-metadata.service.ts +10 -1
  484. package/src/services/mpin.service.ts +447 -0
  485. package/src/services/object.utils.ts +10 -0
  486. package/src/services/refresh-token-ids-storage.service.ts +229 -90
  487. package/src/services/setting.service.ts +8 -1
  488. package/src/services/settings/default-settings-provider.service.ts +154 -6
  489. package/src/services/user.service.ts +18 -3
  490. package/src/services/view-metadata.service.ts +5 -145
  491. package/src/services/workflow/nodes/execution-fail.node.ts +82 -0
  492. package/src/services/workflow/nodes/for-each.node.ts +140 -6
  493. package/src/services/workflow/nodes/if.node.ts +7 -0
  494. package/src/services/workflow/nodes/log-write.node.ts +2 -0
  495. package/src/services/workflow/nodes/loop-until.node.ts +343 -0
  496. package/src/services/workflow/nodes/parallel.node.ts +7 -0
  497. package/src/services/workflow/nodes/sequential.node.ts +7 -0
  498. package/src/services/workflow/nodes/switch.node.ts +7 -0
  499. package/src/services/workflow/workflow-definition-validator.service.ts +5 -2
  500. package/src/services/workflow/workflow-expression.service.ts +5 -0
  501. package/src/services/workflow/workflow-node-registry.service.ts +11 -0
  502. package/src/services/workflow/workflow-runtime.service.ts +274 -39
  503. package/src/solid-core.module.ts +46 -15
  504. package/src/testing/README.md +75 -3
  505. package/src/testing/adapters/ui/playwright-adapter.ts +25 -0
  506. package/src/testing/adapters/ui/ui.types.ts +2 -0
  507. package/src/testing/reporter/console-reporter.ts +4 -0
  508. package/src/testing/reporter/progress-reporter.ts +251 -0
  509. package/src/testing/reporter/reporter.types.ts +3 -0
  510. package/src/testing/runner/run-from-metadata.ts +1 -0
  511. package/src/testing/steps/ui/actions.step.ts +8 -4
  512. package/src/testing/steps/ui/assertions.step.ts +18 -5
  513. package/src/testing/steps/ui/form.step.ts +16 -6
  514. package/src/testing/steps/ui/manual.step.ts +1 -1
  515. package/src/testing/steps/ui/navigation.step.ts +4 -2
  516. package/src/theme/theme-registry.ts +181 -0
  517. package/src/types/workflow-dsl.types.ts +43 -0
@@ -1,133 +1,272 @@
1
1
  import { CACHE_MANAGER } from '@nestjs/cache-manager';
2
2
  import { Inject, Injectable, forwardRef } from '@nestjs/common';
3
3
  import { Cache } from 'cache-manager';
4
+ import { randomUUID } from 'crypto';
5
+ import type { SolidCoreSetting } from 'src/services/settings/default-settings-provider.service';
4
6
  import { AuthenticationService } from './authentication.service';
7
+ import { SettingService } from './setting.service';
5
8
 
6
9
  // TODO: Ideally this should be in a separate file - putting this here for brevity
7
10
  export class InvalidatedRefreshTokenError extends Error { }
8
11
 
12
+ // How long a rotated-out refresh token stays acceptable. This window is the
13
+ // point of keeping a previous token at all: concurrent requests from one user
14
+ // must not break each other. core-ui's single-flight guard is module-level and
15
+ // therefore per tab, so two tabs crossing the refresh threshold together will
16
+ // both present the same token - the second one is served from here.
17
+ const PREVIOUS_TOKEN_GRACE_MS = 60 * 1000;
18
+
19
+ // The cache entry must outlive the JWT it holds. If it expired first, refresh
20
+ // would fail with ACCESS_DENIED (InvalidatedRefreshTokenError) rather than the
21
+ // SESSION_EXPIRED that the token's own `exp` produces - the same event
22
+ // reported two different ways.
23
+ const STATE_TTL_BUFFER_SECONDS = 60;
24
+
9
25
  type RefreshTokenState = {
10
26
  currentRefreshToken: string;
11
27
  previousRefreshToken: string;
28
+ // Absolute epoch-ms deadline after which previousRefreshToken is refused.
29
+ // Stored rather than scheduled: an in-process timer is lost when the pod
30
+ // restarts, which used to leave the rotated-out token valid until the next
31
+ // rotation instead of for one minute. Optional because entries written
32
+ // before this field existed will not carry it.
33
+ previousValidUntil?: number;
34
+ };
35
+
36
+ /**
37
+ * The bucket-identifying claims carried on a refresh token. Absent on tokens
38
+ * issued before per-device sessions existed, and on tokens issued while
39
+ * preventConcurrentLogins is on - which is exactly how the read path tells the
40
+ * two schemes apart without consulting any setting.
41
+ */
42
+ export type RefreshTokenClaims = {
43
+ deviceKey?: string;
44
+ epoch?: number;
45
+ };
46
+
47
+ export type RotatedRefreshToken = {
48
+ refreshToken: string;
49
+ // The bucket the rotated token now lives in. Differs from the incoming
50
+ // deviceKey only when a pre-migration token was just moved into one.
51
+ deviceKey?: string;
12
52
  };
13
53
 
14
54
  @Injectable()
15
- // export class RefreshTokenIdsStorageService implements OnApplicationBootstrap, OnApplicationShutdown {
16
55
  export class RefreshTokenIdsStorageService {
17
- // private redisClient: Redis;
18
- // onApplicationBootstrap() {
19
- // // TODO: Ideally, we should move this to the dedicated "RedisModule" instead of initiating the connection here.
20
- // this.redisClient = new Redis({
21
- // // TODO: According to best practices, we should use the environment variables here instead.
22
- // host: 'localhost',
23
- // port: 6379,
24
- // });
25
- // }
26
- // onApplicationShutdown(signal?: string) {
27
- // return this.redisClient.quit();
28
- // }
29
-
30
56
  constructor(
31
57
  @Inject(CACHE_MANAGER) private cacheManager: Cache,
32
58
  @Inject(forwardRef(() => AuthenticationService))
33
- private readonly authenticationService: AuthenticationService
59
+ private readonly authenticationService: AuthenticationService,
60
+ private readonly settingService: SettingService,
34
61
  ) { }
35
62
 
36
- async insert(userId: number, refreshToken: string, previousRefreshToken?: string): Promise<void> {
63
+ /**
64
+ * The inverse of the `preventConcurrentLogins` setting, and the only place
65
+ * it is interpreted.
66
+ *
67
+ * That setting already promises, when off, that sessions may coexist;
68
+ * splitting the refresh-token keyspace per device is what finally delivers
69
+ * it. When it is on, its stated purpose is a single live session, so
70
+ * per-device keys would be meaningless and the original single slot is kept.
71
+ *
72
+ * This governs the WRITE path only. Reads always branch on the token's own
73
+ * claim, so toggling the setting never invalidates a live session.
74
+ */
75
+ areConcurrentLoginsAllowed(): boolean {
76
+ return !this.settingService.getConfigValue<SolidCoreSetting>("preventConcurrentLogins");
77
+ }
78
+
79
+ async insert(
80
+ userId: number,
81
+ refreshToken: string,
82
+ previousRefreshToken?: string,
83
+ deviceKey?: string,
84
+ ): Promise<void> {
37
85
  const refreshTokenState: RefreshTokenState = {
38
86
  currentRefreshToken: refreshToken,
39
87
  previousRefreshToken: previousRefreshToken ?? "",
88
+ ...(previousRefreshToken
89
+ ? { previousValidUntil: Date.now() + PREVIOUS_TOKEN_GRACE_MS }
90
+ : {}),
40
91
  };
41
- await this.cacheManager.set(this.getKey(userId), refreshTokenState);
92
+ await this.cacheManager.set(
93
+ this.getKey(userId, deviceKey),
94
+ refreshTokenState,
95
+ this.getStateTtlMs(),
96
+ );
42
97
  }
43
98
 
44
- async validate(userId: number, refreshToken: string): Promise<boolean> {
45
- // TODO: Assume you get this shape out of the cache {"currentRefreshToken": "", "previousRefreshToken": ""}
46
- // Then you will compare against the currentRefreshToken.
47
- const storedId = await this.cacheManager.get(this.getKey(userId));
48
- if (storedId !== refreshToken) {
49
- throw new InvalidatedRefreshTokenError();
50
- }
51
- return storedId === refreshToken;
99
+ async invalidate(userId: number, deviceKey?: string): Promise<void> {
100
+ await this.cacheManager.del(this.getKey(userId, deviceKey));
52
101
  }
53
102
 
54
- async invalidate(userId: number): Promise<void> {
103
+ /**
104
+ * Bulk invalidation - "log out everywhere", and the right hook for password
105
+ * change or forced deactivation.
106
+ *
107
+ * Per-device buckets cannot be enumerated through cache-manager, so this
108
+ * bumps a per-user epoch that every per-device token carries as a claim.
109
+ * One write invalidates every outstanding token without a scan and without
110
+ * the read-modify-write race an index of device keys would have.
111
+ */
112
+ async invalidateAll(userId: number): Promise<void> {
113
+ const currentEpoch = (await this.getEpoch(userId)) ?? 0;
114
+ await this.cacheManager.set(
115
+ this.getEpochKey(userId),
116
+ currentEpoch + 1,
117
+ // Outlives every bucket that could hold a pre-bump token: those were
118
+ // written earlier, so they expire no later than this key does.
119
+ this.getStateTtlMs(),
120
+ );
121
+ // Single-slot mode and pre-migration entries live under the bare key,
122
+ // which carries no epoch claim - delete it directly.
55
123
  await this.cacheManager.del(this.getKey(userId));
56
124
  }
57
125
 
58
- async validateAndRotate(user: any, refreshToken: string): Promise<string> {
59
- let valid = false;
60
-
61
- // TODO: Assume you get this shape out of the cache {"currentRefreshToken": "", "previousRefreshToken": ""}
62
- // Then you will compare against the currentRefreshToken.
63
- const refreshTokenState = await this.cacheManager.get(this.getKey(user.id)) as RefreshTokenState | undefined;
64
-
65
- // Use the authentication service to generate a new refresh token, set it in the currentRefreshToken in scenario 1 and return.
66
-
67
- // if UI.refresh_token is matching with Cache.currentRefreshToken
68
- // then invalidate (updated cache state, no need to delete anything), then generate new token and return.
69
- // also set a setTimeout to run after X minutes, this will simply update the RefreshTokenCacheState to this object {"currentRefreshToken": "R2","justInvalidatedRefreshToken": ""}
70
- // valid=true
71
-
72
- // - if UI.refresh_token is matching Cache.justInvalidatedRefreshToken
73
- // then use the Cache.currentRefreshToken, generate new access token and return.
74
- // We do not modify the cache state at all.
75
- // valid=true
76
-
77
- let newRefreshToken: string | undefined;
78
- if (
79
- refreshTokenState &&
80
- typeof refreshTokenState === 'object' &&
81
- 'currentRefreshToken' in refreshTokenState &&
82
- 'previousRefreshToken' in refreshTokenState
83
- ) {
84
- if (refreshTokenState.currentRefreshToken === refreshToken) {
85
- // Scenario 1: Token matches currentRefreshToken
86
- valid = true;
87
- // Rotate tokens: move current to previous, set new current (simulate generation)
88
- newRefreshToken = await this.authenticationService.generateRefreshToken(user, refreshTokenState.currentRefreshToken); // Replace with real token generation logic
89
-
90
-
91
- // updated cache state
92
- // await this.cacheManager.set(this.getKey(user.id), {
93
- // currentRefreshToken: newRefreshToken,
94
- // previousRefreshToken: refreshTokenState.currentRefreshToken,
95
- // });
96
-
97
- // Optionally, set a timeout to clear previousRefreshToken after X minutes
98
- setTimeout(async () => {
99
- const state = (await this.cacheManager.get(this.getKey(user.id))) as any;
100
- if (state && state.currentRefreshToken === newRefreshToken) {
101
- await this.cacheManager.set(this.getKey(user.id), {
102
- currentRefreshToken: newRefreshToken,
103
- previousRefreshToken: "",
104
- });
105
- }
106
- }, 1 * 60 * 1000); // 5 minutes
107
- } else if (refreshTokenState.previousRefreshToken === refreshToken) {
108
- // Scenario 2: Token matches previousRefreshToken
109
- valid = true;
110
- // Do not modify cache
111
- // Generate new refresh token based on currentRefreshToken
112
- const existingRefreshTokenState = (await this.cacheManager.get(this.getKey(user.id))) as RefreshTokenState | undefined;
113
- newRefreshToken = existingRefreshTokenState?.currentRefreshToken;
126
+ async getEpoch(userId: number): Promise<number | undefined> {
127
+ return (await this.cacheManager.get<number>(this.getEpochKey(userId))) ?? undefined;
128
+ }
129
+
130
+ async validateAndRotate(
131
+ user: any,
132
+ refreshToken: string,
133
+ claims: RefreshTokenClaims = {},
134
+ ): Promise<RotatedRefreshToken> {
135
+ const { deviceKey, epoch } = claims;
136
+
137
+ // The read key comes from the token's own claim, never from a setting.
138
+ // A token issued under either scheme therefore stays valid when
139
+ // preventConcurrentLogins is toggled - in both directions - and a
140
+ // pre-migration token still finds the bare key it was written to.
141
+ const refreshTokenState = await this.cacheManager.get(
142
+ this.getKey(user.id, deviceKey),
143
+ ) as RefreshTokenState | undefined;
144
+
145
+ if (!this.isRefreshTokenState(refreshTokenState)) {
146
+ throw new InvalidatedRefreshTokenError();
147
+ }
148
+
149
+ if (deviceKey && !(await this.isEpochCurrent(user.id, epoch))) {
150
+ throw new InvalidatedRefreshTokenError();
151
+ }
152
+
153
+ // Scenario 1: the live token. Rotate it. generateRefreshToken calls
154
+ // insert(), which writes the new state and stamps the grace deadline
155
+ // onto the token being rotated out.
156
+ if (refreshTokenState.currentRefreshToken === refreshToken) {
157
+ const nextDeviceKey = this.resolveNextDeviceKey(deviceKey);
158
+ const rotated = await this.authenticationService.generateRefreshToken(
159
+ user,
160
+ refreshToken,
161
+ nextDeviceKey,
162
+ );
163
+
164
+ if (nextDeviceKey !== deviceKey) {
165
+ // The session changed buckets - either migrating out of the
166
+ // pre-deploy single slot, or collapsing back into it because
167
+ // preventConcurrentLogins was switched on. The new state is
168
+ // already written; drop the key it came from so nothing is
169
+ // left behind.
170
+ await this.cacheManager.del(this.getKey(user.id, deviceKey));
171
+ }
172
+
173
+ return { refreshToken: rotated, deviceKey: nextDeviceKey };
174
+ }
175
+
176
+ // Scenario 2: the token just rotated out. This is the concurrent-request
177
+ // case the previous slot exists for - a second in-flight request that
178
+ // was issued the old token before the first one rotated it. Hand back
179
+ // the live token so it succeeds, provided the grace window is still open.
180
+ if (refreshTokenState.previousRefreshToken && refreshTokenState.previousRefreshToken === refreshToken) {
181
+ if (!this.isWithinGraceWindow(refreshTokenState)) {
182
+ throw new InvalidatedRefreshTokenError();
114
183
  }
184
+ return { refreshToken: refreshTokenState.currentRefreshToken, deviceKey };
115
185
  }
116
186
 
187
+ throw new InvalidatedRefreshTokenError();
188
+ }
117
189
 
118
- if (!valid) {
119
- throw new InvalidatedRefreshTokenError();
190
+ getCurrentRefreshTokenState(
191
+ userId: number,
192
+ deviceKey?: string,
193
+ ): Promise<RefreshTokenState | undefined> {
194
+ return this.cacheManager.get(this.getKey(userId, deviceKey));
195
+ }
196
+
197
+ private getKey(userId: number, deviceKey?: string): string {
198
+ return deviceKey ? `user-${userId}-${deviceKey}` : `user-${userId}`;
199
+ }
200
+
201
+ // Deliberately not `user-${userId}-epoch`, which a device key of the
202
+ // literal string "epoch" would collide with.
203
+ private getEpochKey(userId: number): string {
204
+ return `user-epoch-${userId}`;
205
+ }
206
+
207
+ /**
208
+ * Which bucket a rotated token should land in. The setting is consulted
209
+ * first so that turning preventConcurrentLogins ON collapses existing
210
+ * per-device sessions back into the single slot on their next refresh -
211
+ * where normal last-login-wins applies, which is what that setting means.
212
+ * Leaving them in their own buckets would keep concurrent sessions alive
213
+ * while the setting claimed to forbid them.
214
+ */
215
+ private resolveNextDeviceKey(deviceKey?: string): string | undefined {
216
+ if (!this.areConcurrentLoginsAllowed()) {
217
+ return undefined;
120
218
  }
219
+ return deviceKey ?? randomUUID();
220
+ }
121
221
 
122
- // TODO: return the refresh token either currentRefreshToken
123
- return newRefreshToken; // Fallback to the provided tokenId if no new token was generated
222
+ private async isEpochCurrent(userId: number, epoch?: number): Promise<boolean> {
223
+ const storedEpoch = await this.getEpoch(userId);
224
+ // Nothing has ever been bulk-invalidated for this user - or the cache
225
+ // was flushed, in which case every bucket is gone and the token is
226
+ // already dead by the read above. Either way, accept.
227
+ if (storedEpoch === undefined) {
228
+ return true;
229
+ }
230
+ return epoch === storedEpoch;
231
+ }
232
+
233
+ // cache-manager v5 expects milliseconds; refreshTokenTtl is configured in
234
+ // seconds. Without an explicit TTL these entries never expire - neither
235
+ // cache path supplies a default - and the keyspace grows without bound.
236
+ private getStateTtlMs(): number {
237
+ const refreshTokenTtlSeconds = Number(
238
+ this.settingService.getConfigValue<SolidCoreSetting>("refreshTokenTtl"),
239
+ );
240
+ return (refreshTokenTtlSeconds + STATE_TTL_BUFFER_SECONDS) * 1000;
124
241
  }
125
242
 
126
- private getKey(userId: number): string {
127
- return `user-${userId}`;
243
+ private isRefreshTokenState(state: unknown): state is RefreshTokenState {
244
+ return (
245
+ !!state &&
246
+ typeof state === 'object' &&
247
+ 'currentRefreshToken' in state &&
248
+ 'previousRefreshToken' in state
249
+ );
128
250
  }
129
251
 
130
- getCurrentRefreshTokenState(userId: number): Promise<RefreshTokenState | undefined> {
131
- return this.cacheManager.get(this.getKey(userId));
252
+ private isWithinGraceWindow(state: RefreshTokenState): boolean {
253
+ // TRANSITIONAL - safe to delete one refreshTokenTtl after this ships.
254
+ //
255
+ // Entries written before previousValidUntil existed carry no deadline
256
+ // to test. They are accepted so that the deploy rejects nobody: a tab
257
+ // that legitimately rotated moments earlier keeps working. The cost is
258
+ // that for such an entry the previous token stays acceptable until its
259
+ // own JWT `exp` rather than for 60s - which is exactly how the old code
260
+ // already behaved whenever its in-process timer was lost to a restart,
261
+ // so this is an unfixed pre-existing case, not a new one.
262
+ //
263
+ // It self-heals: the user's next rotation writes a new-format state
264
+ // with a deadline. Once every pre-deploy entry has rotated or expired
265
+ // (one refreshTokenTtl), this branch is unreachable - drop it, and the
266
+ // undefined case becomes a rejection.
267
+ if (state.previousValidUntil === undefined) {
268
+ return true;
269
+ }
270
+ return Date.now() < state.previousValidUntil;
132
271
  }
133
272
  }
@@ -16,6 +16,8 @@ import { ModuleMetadataRepository } from 'src/repository/module-metadata.reposit
16
16
  import type { SolidCoreSetting } from './settings/default-settings-provider.service';
17
17
  import { Logger } from '@nestjs/common';
18
18
  import { EncryptionService } from './encryption.service';
19
+ import { sanitizeStoredMediaFileName } from './media-storage.utils';
20
+ import { isDangerousMediaFile } from 'src/constants/media-file-types';
19
21
 
20
22
 
21
23
  @Injectable()
@@ -357,8 +359,13 @@ export class SettingService {
357
359
  // Handle uploaded files
358
360
  if (uploadedFiles?.length) {
359
361
  for (const file of uploadedFiles) {
362
+ if (isDangerousMediaFile(file)) {
363
+ throw new BadRequestException('Dangerous file types are not allowed for settings.');
364
+ }
365
+
360
366
  const settingKey = file.fieldname;
361
- const relativeFileName = `${file.filename}-${file.originalname}`;
367
+ // originalname is attacker-controlled and feeds straight into a storage path.
368
+ const relativeFileName = `${file.filename}-${sanitizeStoredMediaFileName(file.originalname)}`;
362
369
 
363
370
  // Read file from local disk (where Multer stores uploads) and write to storage
364
371
  // The path builder constructs the provider-appropriate storage path
@@ -6,10 +6,18 @@ import {
6
6
  SettingDefinition,
7
7
  SettingLevel,
8
8
  } from "src/interfaces";
9
+ import { getDefaultThemeKey, getThemesByMode } from "src/theme/theme-registry";
9
10
 
10
11
  export const DEFAULT_MEDIA_UPLOAD_DIR = "media-uploads";
11
12
  export const DEFAULT_MEDIA_FILE_STORAGE_DIR = "media-files-storage";
12
13
 
14
+ // Seeded setting values are written once and never overwritten, so an unparseable
15
+ // env var would persist. Fall back whenever it is missing or not a positive number.
16
+ const timeoutFromEnv = (value: string | undefined, fallback: number): number => {
17
+ const parsed = Number(value);
18
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
19
+ };
20
+
13
21
  // 1.
14
22
  const getSolidCoreSettings = (isProd: boolean) =>
15
23
  [
@@ -125,10 +133,10 @@ const getSolidCoreSettings = (isProd: boolean) =>
125
133
  },
126
134
  {
127
135
  moduleName: "solid-core",
128
- key: "rowClickAction",
136
+ key: "recordClickAction",
129
137
  value: "edit",
130
138
  level: SettingLevel.SystemAdminEditable,
131
- label: "Row Click Action",
139
+ label: "Record Click Action",
132
140
  group: "app-settings",
133
141
  sortOrder: 45,
134
142
  controlType: "selectionStatic",
@@ -136,7 +144,7 @@ const getSolidCoreSettings = (isProd: boolean) =>
136
144
  { label: "Edit", value: "edit" },
137
145
  { label: "View", value: "view" },
138
146
  ],
139
- helpText: "Decides whether clicking a list row opens the record in edit mode or view mode.",
147
+ helpText: "Decides whether clicking a record in List, Card, Kanban, or Tree View opens it in edit mode or view mode.",
140
148
  },
141
149
  {
142
150
  moduleName: "solid-core",
@@ -217,14 +225,44 @@ const getSolidCoreSettings = (isProd: boolean) =>
217
225
  },
218
226
  {
219
227
  moduleName: "solid-core",
220
- key: "enableDarkMode",
228
+ key: "showThemeToggle",
221
229
  value: true,
222
230
  level: SettingLevel.SystemAdminEditable,
223
- label: "Enable Dark Mode",
231
+ label: "Show Theme Toggle",
224
232
  group: "app-settings",
225
233
  sortOrder: 120,
226
234
  controlType: "boolean",
227
- helpText: "Enables dark theme support for frontend surfaces that honor the global appearance setting.",
235
+ helpText: "Shows the theme toggle in the frontend so users can switch between light and dark themes.",
236
+ },
237
+ {
238
+ moduleName: "solid-core",
239
+ key: "lightTheme",
240
+ value: getDefaultThemeKey("light"),
241
+ level: SettingLevel.SystemAdminEditable,
242
+ label: "Light Theme",
243
+ group: "app-settings",
244
+ sortOrder: 125,
245
+ controlType: "selectionStatic",
246
+ options: getThemesByMode("light").map((theme) => ({
247
+ label: theme.label,
248
+ value: theme.key,
249
+ })),
250
+ helpText: "Selects the theme used whenever the application is in light mode.",
251
+ },
252
+ {
253
+ moduleName: "solid-core",
254
+ key: "darkTheme",
255
+ value: getDefaultThemeKey("dark"),
256
+ level: SettingLevel.SystemAdminEditable,
257
+ label: "Dark Theme",
258
+ group: "app-settings",
259
+ sortOrder: 126,
260
+ controlType: "selectionStatic",
261
+ options: getThemesByMode("dark").map((theme) => ({
262
+ label: theme.label,
263
+ value: theme.key,
264
+ })),
265
+ helpText: "Selects the theme used whenever the application is in dark mode.",
228
266
  },
229
267
  {
230
268
  moduleName: "solid-core",
@@ -494,6 +532,20 @@ const getSolidCoreSettings = (isProd: boolean) =>
494
532
  ],
495
533
  helpText: "Default storage implementation used for file handling, such as local disk or S3.",
496
534
  },
535
+ {
536
+ moduleName: "solid-core",
537
+ key: "maxMediaFileSizeMb",
538
+ value: (() => {
539
+ const parsedMb = Number(process.env.AB_MEDIA_MAX_FILE_SIZE_MB);
540
+ return Number.isFinite(parsedMb) && parsedMb > 0 ? parsedMb : 100;
541
+ })(),
542
+ level: SettingLevel.SystemAdminReadonly,
543
+ label: "Max Media File Size (MB)",
544
+ group: "storage-settings",
545
+ sortOrder: 45,
546
+ controlType: "numeric",
547
+ helpText: "Global upload size limit enforced by Multer under every per-field mediaMaxSizeKb restriction. Read-only here: it's applied once at process start, so changing AB_MEDIA_MAX_FILE_SIZE_MB requires an app restart to take effect - editing this in the admin UI would not do that.",
548
+ },
497
549
 
498
550
  // aws-s3-settings-provider.service.ts
499
551
  {
@@ -1211,6 +1263,77 @@ const getSolidCoreSettings = (isProd: boolean) =>
1211
1263
  sortOrder: 200,
1212
1264
  controlType: "boolean",
1213
1265
  },
1266
+ {
1267
+ moduleName: "solid-core",
1268
+ key: "mpinEnabled",
1269
+ value: false,
1270
+ level: SettingLevel.SystemAdminEditable,
1271
+ label: "Enable MPIN Sign-in",
1272
+ group: "authentication-settings",
1273
+ sortOrder: 210,
1274
+ controlType: "boolean",
1275
+ helpText:
1276
+ "Allows users to set a short device PIN for faster sign-in. Every MPIN route returns 404 while this is off.",
1277
+ },
1278
+ {
1279
+ moduleName: "solid-core",
1280
+ key: "mpinRegex",
1281
+ value: process.env.IAM_MPIN_REGEX ?? "^\\d{4,6}$",
1282
+ level: SettingLevel.SystemAdminEditable,
1283
+ label: "MPIN Format",
1284
+ group: "authentication-settings",
1285
+ sortOrder: 211,
1286
+ controlType: "shortText",
1287
+ helpText:
1288
+ "Regular expression the MPIN must match. Sole authority on MPIN length - the default allows 4 to 6 digits. Predictable PINs (repeated or sequential digits) are rejected separately.",
1289
+ },
1290
+ {
1291
+ moduleName: "solid-core",
1292
+ key: "mpinMaxFailedAttempts",
1293
+ value: parseInt(process.env.IAM_MPIN_MAX_FAILED_ATTEMPTS ?? "5", 10),
1294
+ level: SettingLevel.SystemAdminEditable,
1295
+ label: "MPIN Attempts Before Lockout",
1296
+ group: "authentication-settings",
1297
+ sortOrder: 212,
1298
+ controlType: "numeric",
1299
+ helpText:
1300
+ "Cumulative failed attempts that trigger a temporary lockout. The counter resets only on a successful MPIN sign-in, not when the lockout expires.",
1301
+ },
1302
+ {
1303
+ moduleName: "solid-core",
1304
+ key: "mpinLockoutDuration",
1305
+ value: parseInt(process.env.IAM_MPIN_LOCKOUT_DURATION ?? "900", 10),
1306
+ level: SettingLevel.SystemAdminEditable,
1307
+ label: "MPIN Lockout Duration (seconds)",
1308
+ group: "authentication-settings",
1309
+ sortOrder: 213,
1310
+ controlType: "numeric",
1311
+ helpText: "How long the temporary lockout lasts.",
1312
+ },
1313
+ {
1314
+ moduleName: "solid-core",
1315
+ key: "mpinMaxTotalFailedAttempts",
1316
+ value: parseInt(process.env.IAM_MPIN_MAX_TOTAL_FAILED_ATTEMPTS ?? "10", 10),
1317
+ level: SettingLevel.SystemAdminEditable,
1318
+ label: "MPIN Attempts Before Permanent Deactivation",
1319
+ group: "authentication-settings",
1320
+ sortOrder: 214,
1321
+ controlType: "numeric",
1322
+ helpText:
1323
+ "Cumulative failed attempts that permanently disable MPIN on that device. Must exceed the lockout threshold. The account itself is never locked - the user signs in normally and sets MPIN up again.",
1324
+ },
1325
+ {
1326
+ moduleName: "solid-core",
1327
+ key: "mpinMaxDevicesPerUser",
1328
+ value: parseInt(process.env.IAM_MPIN_MAX_DEVICES_PER_USER ?? "5", 10),
1329
+ level: SettingLevel.SystemAdminEditable,
1330
+ label: "Max MPIN Devices Per User",
1331
+ group: "authentication-settings",
1332
+ sortOrder: 215,
1333
+ controlType: "numeric",
1334
+ helpText:
1335
+ "At the cap the least recently used credential is evicted rather than the setup rejected, so repeated app reinstalls cannot lock a user out of MPIN setup.",
1336
+ },
1214
1337
  // queues-settings-provider.service.ts
1215
1338
  {
1216
1339
  moduleName: "solid-core",
@@ -1474,6 +1597,31 @@ const getSolidCoreSettings = (isProd: boolean) =>
1474
1597
  value: process.env.COMMON_META_WHATSAPP_APP_SECRET,
1475
1598
  level: SettingLevel.SystemEnv,
1476
1599
  },
1600
+ {
1601
+ moduleName: "solid-core",
1602
+ key: "uiTestDefaultTimeoutMs",
1603
+ value: timeoutFromEnv(process.env.COMMON_UI_TEST_DEFAULT_TIMEOUT_MS, 30000),
1604
+ level: SettingLevel.SystemAdminEditable,
1605
+ label: "UI Test Default Timeout (ms)",
1606
+ group: "testing-settings",
1607
+ sortOrder: 10,
1608
+ controlType: "numeric",
1609
+ helpText: "How long UI test steps wait for an element before failing. Raise this if tests time out on slow screens. Overridden by the --ui-timeout-ms flag and by a per-step timeoutMs.",
1610
+ },
1611
+ {
1612
+ moduleName: "solid-core",
1613
+ key: "uiTestNavigationTimeoutMs",
1614
+ value: timeoutFromEnv(
1615
+ process.env.COMMON_UI_TEST_NAVIGATION_TIMEOUT_MS,
1616
+ timeoutFromEnv(process.env.COMMON_UI_TEST_DEFAULT_TIMEOUT_MS, 30000),
1617
+ ),
1618
+ level: SettingLevel.SystemAdminEditable,
1619
+ label: "UI Test Navigation Timeout (ms)",
1620
+ group: "testing-settings",
1621
+ sortOrder: 20,
1622
+ controlType: "numeric",
1623
+ helpText: "How long the ui.goto step waits for a page to load before failing. Falls back to the UI test default timeout.",
1624
+ },
1477
1625
  ] as const satisfies SettingDefinition[];
1478
1626
 
1479
1627
  // 2.
@@ -156,10 +156,25 @@ export class UserService extends CRUDService<User> {
156
156
  if (!user) {
157
157
  throw new Error(ERROR_MESSAGES.USER_NOT_FOUND);
158
158
  }
159
- if (updateDto.roles != null) {
160
- await this.addRolesToUser(user.username, updateDto.roles);
159
+
160
+ const { roles, ...userUpdateDto } = updateDto || {};
161
+ const savedUser = await this.update(
162
+ id,
163
+ userUpdateDto,
164
+ files,
165
+ true,
166
+ solidRequestContext,
167
+ );
168
+
169
+ if (roles != null) {
170
+ await this.addRolesToUser(savedUser.username, roles);
171
+ return this.repo.findOne({
172
+ where: { id: savedUser.id },
173
+ relations: { roles: true },
174
+ });
161
175
  }
162
- await this.update(id, updateDto, files, true);
176
+
177
+ return savedUser;
163
178
  }
164
179
 
165
180
  async addRoleToUser(username: string, roleName: string): Promise<User> {