@tstdl/base 0.93.87 → 0.93.90

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 (314) hide show
  1. package/ai/genkit/helpers.d.ts +3 -1
  2. package/ai/genkit/helpers.js +3 -3
  3. package/api/server/gateway.d.ts +3 -0
  4. package/api/server/gateway.js +15 -4
  5. package/api/server/middlewares/catch-error.middleware.js +2 -4
  6. package/api/server/middlewares/cors.middleware.js +2 -3
  7. package/api/server/middlewares/csrf.middleware.d.ts +41 -0
  8. package/api/server/middlewares/csrf.middleware.js +108 -0
  9. package/api/server/middlewares/index.d.ts +1 -0
  10. package/api/server/middlewares/index.js +1 -0
  11. package/api/server/module.d.ts +8 -2
  12. package/api/server/module.js +14 -8
  13. package/api/server/tests/csrf.middleware.test.js +91 -0
  14. package/audit/drizzle/{0000_bored_stick.sql → 0000_lumpy_thunderball.sql} +3 -3
  15. package/audit/drizzle/meta/0000_snapshot.json +4 -4
  16. package/audit/drizzle/meta/_journal.json +2 -9
  17. package/audit/module.d.ts +4 -1
  18. package/audit/module.js +3 -2
  19. package/audit/schemas.d.ts +1 -1
  20. package/audit/types.d.ts +1 -1
  21. package/audit/types.js +1 -1
  22. package/authentication/client/authentication.service.d.ts +14 -1
  23. package/authentication/client/authentication.service.js +82 -23
  24. package/authentication/client/http-client.middleware.d.ts +6 -0
  25. package/authentication/client/http-client.middleware.js +36 -0
  26. package/authentication/client/module.js +8 -2
  27. package/authentication/models/service-account.model.d.ts +2 -2
  28. package/authentication/models/service-account.model.js +10 -5
  29. package/authentication/models/subject.model.d.ts +20 -5
  30. package/authentication/models/subject.model.js +34 -29
  31. package/authentication/models/system-account.model.d.ts +3 -2
  32. package/authentication/models/system-account.model.js +11 -5
  33. package/authentication/models/user.model.d.ts +2 -11
  34. package/authentication/models/user.model.js +5 -16
  35. package/authentication/server/authentication-api-request-token.provider.d.ts +0 -2
  36. package/authentication/server/authentication-api-request-token.provider.js +3 -11
  37. package/authentication/server/authentication.api-controller.d.ts +1 -2
  38. package/authentication/server/authentication.api-controller.js +8 -9
  39. package/authentication/server/authentication.audit.d.ts +3 -2
  40. package/authentication/server/authentication.service.d.ts +27 -1
  41. package/authentication/server/authentication.service.js +67 -18
  42. package/authentication/server/drizzle/{0000_normal_paper_doll.sql → 0000_soft_tag.sql} +25 -32
  43. package/authentication/server/drizzle/meta/0000_snapshot.json +180 -205
  44. package/authentication/server/drizzle/meta/_journal.json +2 -2
  45. package/authentication/server/helper.js +9 -2
  46. package/authentication/server/module.d.ts +4 -1
  47. package/authentication/server/module.js +9 -5
  48. package/authentication/server/schemas.d.ts +2 -1
  49. package/authentication/server/schemas.js +2 -2
  50. package/authentication/server/subject.service.d.ts +17 -11
  51. package/authentication/server/subject.service.js +86 -84
  52. package/authentication/tests/authentication-ancillary.service.test.d.ts +1 -0
  53. package/authentication/tests/authentication-ancillary.service.test.js +13 -0
  54. package/authentication/tests/authentication-secret-requirements.validator.test.d.ts +1 -0
  55. package/authentication/tests/authentication-secret-requirements.validator.test.js +29 -0
  56. package/authentication/tests/authentication.api-controller.test.d.ts +1 -0
  57. package/authentication/tests/authentication.api-controller.test.js +88 -0
  58. package/authentication/tests/authentication.api-request-token.provider.test.d.ts +1 -0
  59. package/authentication/tests/authentication.api-request-token.provider.test.js +48 -0
  60. package/authentication/tests/authentication.client-middleware.test.d.ts +1 -0
  61. package/authentication/tests/authentication.client-middleware.test.js +23 -0
  62. package/authentication/tests/authentication.client-service.test.d.ts +1 -0
  63. package/authentication/tests/authentication.client-service.test.js +70 -0
  64. package/authentication/tests/authentication.service.test.d.ts +1 -0
  65. package/authentication/tests/authentication.service.test.js +186 -0
  66. package/authentication/tests/authentication.test-ancillary-service.d.ts +9 -0
  67. package/authentication/tests/authentication.test-ancillary-service.js +27 -0
  68. package/authentication/tests/helper.test.d.ts +1 -0
  69. package/authentication/tests/helper.test.js +107 -0
  70. package/authentication/tests/secret-requirements.error.test.d.ts +1 -0
  71. package/authentication/tests/secret-requirements.error.test.js +14 -0
  72. package/authentication/tests/subject.service.test.d.ts +1 -0
  73. package/authentication/tests/subject.service.test.js +140 -0
  74. package/circuit-breaker/postgres/drizzle/meta/0000_snapshot.json +1 -1
  75. package/circuit-breaker/postgres/drizzle/meta/_journal.json +2 -2
  76. package/circuit-breaker/postgres/module.d.ts +7 -1
  77. package/circuit-breaker/postgres/module.js +8 -6
  78. package/circuit-breaker/tests/circuit-breaker.test.js +2 -22
  79. package/document-management/api/document-management.api.js +2 -6
  80. package/document-management/server/services/document-validation.service.js +6 -5
  81. package/document-management/server/services/document-workflow.service.js +5 -5
  82. package/document-management/service-models/document-folders.view-model.d.ts +5 -2
  83. package/document-management/service-models/document-folders.view-model.js +42 -9
  84. package/document-management/service-models/enriched/enriched-document-management-data.view.js +1 -1
  85. package/examples/document-management/main.js +4 -4
  86. package/http/client/adapters/undici.adapter.d.ts +7 -5
  87. package/http/client/adapters/undici.adapter.js +13 -10
  88. package/http/client/module.d.ts +3 -1
  89. package/http/client/module.js +8 -9
  90. package/http/server/http-server.d.ts +2 -0
  91. package/http/server/node/module.d.ts +6 -2
  92. package/http/server/node/module.js +6 -4
  93. package/http/server/node/node-http-server.d.ts +2 -0
  94. package/http/server/node/node-http-server.js +7 -0
  95. package/http/types.d.ts +1 -1
  96. package/key-value-store/postgres/module.d.ts +7 -1
  97. package/key-value-store/postgres/module.js +7 -3
  98. package/lock/postgres/lock.js +0 -1
  99. package/lock/postgres/module.d.ts +7 -1
  100. package/lock/postgres/module.js +9 -5
  101. package/logger/formatter.d.ts +2 -0
  102. package/logger/formatters/json.js +2 -2
  103. package/logger/formatters/pretty-print.js +8 -10
  104. package/logger/logger.d.ts +1 -1
  105. package/logger/logger.js +15 -12
  106. package/message-bus/local/module.d.ts +5 -2
  107. package/message-bus/local/module.js +5 -4
  108. package/module/module.d.ts +2 -1
  109. package/module/module.js +3 -0
  110. package/module/modules/web-server.module.d.ts +11 -6
  111. package/module/modules/web-server.module.js +15 -10
  112. package/orm/decorators.d.ts +24 -1
  113. package/orm/decorators.js +40 -4
  114. package/orm/query/base.d.ts +17 -17
  115. package/orm/query/base.js +1 -1
  116. package/orm/repository.types.d.ts +45 -1
  117. package/orm/schemas/tsvector.js +1 -1
  118. package/orm/server/drizzle/schema-converter.d.ts +3 -1
  119. package/orm/server/drizzle/schema-converter.js +120 -14
  120. package/orm/server/index.d.ts +1 -0
  121. package/orm/server/index.js +1 -0
  122. package/orm/server/module.d.ts +4 -2
  123. package/orm/server/module.js +6 -5
  124. package/orm/server/query-converter.d.ts +6 -3
  125. package/orm/server/query-converter.js +32 -20
  126. package/orm/server/repository-config.d.ts +8 -0
  127. package/orm/server/repository-config.js +8 -0
  128. package/orm/server/repository.d.ts +117 -43
  129. package/orm/server/repository.js +757 -253
  130. package/orm/server/transaction.d.ts +4 -2
  131. package/orm/server/transaction.js +14 -5
  132. package/orm/server/transactional.d.ts +6 -2
  133. package/orm/server/transactional.js +39 -9
  134. package/orm/server/types.d.ts +2 -0
  135. package/orm/sqls/case-when.d.ts +3 -3
  136. package/orm/sqls/case-when.js +2 -2
  137. package/orm/sqls/sqls.d.ts +31 -5
  138. package/orm/sqls/sqls.js +69 -6
  139. package/orm/tests/data-types.test.d.ts +1 -0
  140. package/orm/tests/data-types.test.js +39 -0
  141. package/orm/tests/decorators.test.d.ts +1 -0
  142. package/orm/tests/decorators.test.js +77 -0
  143. package/orm/tests/encryption.test.d.ts +1 -0
  144. package/orm/tests/encryption.test.js +34 -0
  145. package/orm/tests/query-complex.test.d.ts +1 -0
  146. package/orm/tests/query-complex.test.js +203 -0
  147. package/orm/tests/query-converter-complex.test.d.ts +1 -0
  148. package/orm/tests/query-converter-complex.test.js +126 -0
  149. package/orm/tests/query-converter.test.d.ts +1 -0
  150. package/orm/tests/query-converter.test.js +123 -0
  151. package/orm/tests/repository-advanced.test.d.ts +1 -0
  152. package/orm/tests/repository-advanced.test.js +232 -0
  153. package/orm/tests/repository-attributes.test.d.ts +1 -0
  154. package/orm/tests/repository-attributes.test.js +99 -0
  155. package/orm/tests/repository-comprehensive.test.d.ts +1 -0
  156. package/orm/tests/repository-comprehensive.test.js +187 -0
  157. package/orm/tests/repository-coverage.test.d.ts +1 -0
  158. package/orm/tests/repository-coverage.test.js +303 -0
  159. package/orm/tests/repository-cti-complex.test.d.ts +1 -0
  160. package/orm/tests/repository-cti-complex.test.js +170 -0
  161. package/orm/tests/repository-cti-embedded.test.d.ts +1 -0
  162. package/orm/tests/repository-cti-embedded.test.js +188 -0
  163. package/orm/tests/repository-cti-extensive.test.d.ts +1 -0
  164. package/orm/tests/repository-cti-extensive.test.js +308 -0
  165. package/orm/tests/repository-cti-mapping.test.d.ts +1 -0
  166. package/orm/tests/repository-cti-mapping.test.js +121 -0
  167. package/orm/tests/repository-cti-search.test.d.ts +1 -0
  168. package/orm/tests/repository-cti-search.test.js +152 -0
  169. package/orm/tests/repository-cti-soft-delete.test.d.ts +1 -0
  170. package/orm/tests/repository-cti-soft-delete.test.js +115 -0
  171. package/orm/tests/repository-cti-transactions.test.d.ts +1 -0
  172. package/orm/tests/repository-cti-transactions.test.js +126 -0
  173. package/orm/tests/repository-cti-upsert-many.test.d.ts +1 -0
  174. package/orm/tests/repository-cti-upsert-many.test.js +127 -0
  175. package/orm/tests/repository-cti.test.d.ts +1 -0
  176. package/orm/tests/repository-cti.test.js +456 -0
  177. package/orm/tests/repository-edge-cases.test.d.ts +1 -0
  178. package/orm/tests/repository-edge-cases.test.js +216 -0
  179. package/orm/tests/repository-expiration.test.d.ts +1 -0
  180. package/orm/tests/repository-expiration.test.js +153 -0
  181. package/orm/tests/repository-extra-coverage.test.d.ts +1 -0
  182. package/orm/tests/repository-extra-coverage.test.js +546 -0
  183. package/orm/tests/repository-mapping.test.d.ts +1 -0
  184. package/orm/tests/repository-mapping.test.js +71 -0
  185. package/orm/tests/repository-regression.test.d.ts +1 -0
  186. package/orm/tests/repository-regression.test.js +330 -0
  187. package/orm/tests/repository-search-coverage.test.d.ts +1 -0
  188. package/orm/tests/repository-search-coverage.test.js +129 -0
  189. package/orm/tests/repository-search.test.d.ts +1 -0
  190. package/orm/tests/repository-search.test.js +116 -0
  191. package/orm/tests/repository-soft-delete.test.d.ts +1 -0
  192. package/orm/tests/repository-soft-delete.test.js +143 -0
  193. package/orm/tests/repository-transactions-nested.test.d.ts +1 -0
  194. package/orm/tests/repository-transactions-nested.test.js +202 -0
  195. package/orm/tests/repository-types.test.d.ts +1 -0
  196. package/orm/tests/repository-types.test.js +218 -0
  197. package/orm/tests/schema-converter.test.d.ts +1 -0
  198. package/orm/tests/schema-converter.test.js +81 -0
  199. package/orm/tests/schema-generation.test.d.ts +1 -0
  200. package/orm/tests/schema-generation.test.js +127 -0
  201. package/orm/tests/sql-helpers.test.d.ts +1 -0
  202. package/orm/tests/sql-helpers.test.js +67 -0
  203. package/orm/tests/transaction-safety.test.d.ts +1 -0
  204. package/orm/tests/transaction-safety.test.js +81 -0
  205. package/orm/tests/transactional.test.d.ts +1 -0
  206. package/orm/tests/transactional.test.js +224 -0
  207. package/orm/tests/utils.test.d.ts +1 -0
  208. package/orm/tests/utils.test.js +70 -0
  209. package/orm/utils.d.ts +7 -0
  210. package/orm/utils.js +26 -6
  211. package/package.json +12 -7
  212. package/pool/pool.js +1 -1
  213. package/rate-limit/index.d.ts +2 -0
  214. package/rate-limit/index.js +2 -0
  215. package/rate-limit/postgres/drizzle/0000_watery_rage.sql +7 -0
  216. package/{queue → rate-limit}/postgres/drizzle/meta/0000_snapshot.json +14 -39
  217. package/rate-limit/postgres/drizzle/meta/_journal.json +13 -0
  218. package/{queue → rate-limit}/postgres/drizzle.config.js +1 -1
  219. package/rate-limit/postgres/index.d.ts +4 -0
  220. package/rate-limit/postgres/index.js +4 -0
  221. package/rate-limit/postgres/module.d.ts +12 -0
  222. package/rate-limit/postgres/module.js +28 -0
  223. package/rate-limit/postgres/postgres-rate-limiter.d.ts +9 -0
  224. package/rate-limit/postgres/postgres-rate-limiter.js +56 -0
  225. package/rate-limit/postgres/rate-limit.model.d.ts +8 -0
  226. package/rate-limit/postgres/rate-limit.model.js +35 -0
  227. package/rate-limit/postgres/rate-limiter.provider.d.ts +6 -0
  228. package/rate-limit/postgres/rate-limiter.provider.js +21 -0
  229. package/rate-limit/postgres/schemas.d.ts +3 -0
  230. package/rate-limit/postgres/schemas.js +4 -0
  231. package/rate-limit/provider.d.ts +9 -0
  232. package/rate-limit/provider.js +2 -0
  233. package/rate-limit/rate-limiter.d.ts +35 -0
  234. package/rate-limit/rate-limiter.js +3 -0
  235. package/rate-limit/tests/postgres-rate-limiter.test.d.ts +1 -0
  236. package/rate-limit/tests/postgres-rate-limiter.test.js +92 -0
  237. package/signals/implementation/configure.d.ts +3 -0
  238. package/signals/implementation/configure.js +3 -0
  239. package/sse/data-stream-source.d.ts +1 -1
  240. package/sse/data-stream-source.js +6 -6
  241. package/task-queue/enqueue-batch.d.ts +17 -0
  242. package/task-queue/enqueue-batch.js +24 -0
  243. package/{queue → task-queue}/index.d.ts +1 -1
  244. package/{queue → task-queue}/index.js +1 -1
  245. package/task-queue/postgres/drizzle/0000_thin_black_panther.sql +74 -0
  246. package/task-queue/postgres/drizzle/meta/0000_snapshot.json +592 -0
  247. package/task-queue/postgres/drizzle/meta/_journal.json +13 -0
  248. package/task-queue/postgres/drizzle.config.d.ts +2 -0
  249. package/task-queue/postgres/drizzle.config.js +11 -0
  250. package/task-queue/postgres/index.d.ts +4 -0
  251. package/task-queue/postgres/index.js +4 -0
  252. package/task-queue/postgres/module.d.ts +12 -0
  253. package/task-queue/postgres/module.js +28 -0
  254. package/task-queue/postgres/schemas.d.ts +16 -0
  255. package/task-queue/postgres/schemas.js +8 -0
  256. package/task-queue/postgres/task-queue.d.ts +83 -0
  257. package/task-queue/postgres/task-queue.js +1054 -0
  258. package/task-queue/postgres/task-queue.provider.d.ts +7 -0
  259. package/{queue/postgres/queue.provider.js → task-queue/postgres/task-queue.provider.js} +8 -8
  260. package/task-queue/postgres/task.model.d.ts +39 -0
  261. package/task-queue/postgres/task.model.js +178 -0
  262. package/{queue → task-queue}/provider.d.ts +3 -3
  263. package/task-queue/provider.js +2 -0
  264. package/{queue → task-queue}/task-context.d.ts +7 -7
  265. package/{queue → task-queue}/task-context.js +8 -8
  266. package/{queue/queue.d.ts → task-queue/task-queue.d.ts} +128 -59
  267. package/task-queue/task-queue.js +200 -0
  268. package/task-queue/tests/complex.test.d.ts +1 -0
  269. package/task-queue/tests/complex.test.js +299 -0
  270. package/task-queue/tests/dependencies.test.d.ts +1 -0
  271. package/task-queue/tests/dependencies.test.js +174 -0
  272. package/task-queue/tests/queue.test.d.ts +1 -0
  273. package/task-queue/tests/queue.test.js +334 -0
  274. package/task-queue/tests/worker.test.d.ts +1 -0
  275. package/task-queue/tests/worker.test.js +163 -0
  276. package/test1.js +1 -1
  277. package/test4.js +2 -2
  278. package/unit-test/index.d.ts +1 -0
  279. package/unit-test/index.js +1 -0
  280. package/unit-test/integration-setup.d.ts +55 -0
  281. package/unit-test/integration-setup.js +182 -0
  282. package/utils/patterns.d.ts +3 -0
  283. package/utils/patterns.js +6 -1
  284. package/audit/drizzle/0001_previous_network.sql +0 -2
  285. package/audit/drizzle/meta/0001_snapshot.json +0 -195
  286. package/queue/enqueue-batch.d.ts +0 -17
  287. package/queue/enqueue-batch.js +0 -18
  288. package/queue/postgres/drizzle/0000_zippy_moondragon.sql +0 -11
  289. package/queue/postgres/drizzle/0001_certain_wild_pack.sql +0 -2
  290. package/queue/postgres/drizzle/0002_dear_meggan.sql +0 -2
  291. package/queue/postgres/drizzle/0003_tricky_venom.sql +0 -30
  292. package/queue/postgres/drizzle/meta/0001_snapshot.json +0 -103
  293. package/queue/postgres/drizzle/meta/0002_snapshot.json +0 -90
  294. package/queue/postgres/drizzle/meta/0003_snapshot.json +0 -288
  295. package/queue/postgres/drizzle/meta/_journal.json +0 -34
  296. package/queue/postgres/index.d.ts +0 -4
  297. package/queue/postgres/index.js +0 -4
  298. package/queue/postgres/module.d.ts +0 -9
  299. package/queue/postgres/module.js +0 -29
  300. package/queue/postgres/queue.d.ts +0 -60
  301. package/queue/postgres/queue.js +0 -681
  302. package/queue/postgres/queue.provider.d.ts +0 -7
  303. package/queue/postgres/schemas.d.ts +0 -14
  304. package/queue/postgres/schemas.js +0 -6
  305. package/queue/postgres/task.model.d.ts +0 -24
  306. package/queue/postgres/task.model.js +0 -115
  307. package/queue/provider.js +0 -2
  308. package/queue/queue.js +0 -131
  309. package/queue/tests/queue.test.js +0 -623
  310. package/test3.d.ts +0 -1
  311. package/test3.js +0 -47
  312. /package/{queue/tests/queue.test.d.ts → api/server/tests/csrf.middleware.test.d.ts} +0 -0
  313. /package/circuit-breaker/postgres/drizzle/{0000_hard_shocker.sql → 0000_cooing_korath.sql} +0 -0
  314. /package/{queue → rate-limit}/postgres/drizzle.config.d.ts +0 -0
@@ -5,73 +5,55 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { BadRequestError } from '../../errors/bad-request.error.js';
8
- import { formatPersonName } from '../../formats/formats.js';
9
8
  import { Singleton } from '../../injector/index.js';
10
9
  import { injectRepository } from '../../orm/server/index.js';
11
10
  import { mailPattern } from '../../utils/patterns.js';
12
- import { assertNotNull, isDefined, isUndefined } from '../../utils/type-guards.js';
13
- import { ServiceAccount, Subject, SubjectType, SystemAccount, User, UserStatus } from '../models/index.js';
11
+ import { isDefined, isUndefined } from '../../utils/type-guards.js';
12
+ import { ServiceAccount, Subject, SubjectStatus, SubjectType, SystemAccount, User } from '../models/index.js';
14
13
  let SubjectService = class SubjectService {
15
14
  #subjectRepository = injectRepository(Subject);
16
15
  #systemAccountRepository = injectRepository(SystemAccount);
17
16
  #userRepository = injectRepository(User);
18
17
  #serviceAccountRepository = injectRepository(ServiceAccount);
19
- async getSubject(id) {
20
- return await this.#subjectRepository.load(id);
18
+ async getSubject(id, options) {
19
+ return await this.#subjectRepository.load(id, options);
21
20
  }
22
- async tryGetSubject(id) {
23
- return await this.#subjectRepository.tryLoad(id);
21
+ async tryGetSubject(id, options) {
22
+ return await this.#subjectRepository.tryLoad(id, options);
24
23
  }
25
- async getSystemAccountSubject(tenantId, identifier) {
26
- return await this.#subjectRepository.transaction(async (tx) => {
27
- let systemAccount = await this.#systemAccountRepository.withTransaction(tx).tryLoadByQuery({
24
+ async getSystemAccount(tenantId, identifier) {
25
+ const systemAccount = await this.#systemAccountRepository.tryLoadByQuery({
26
+ tenantId,
27
+ identifier,
28
+ });
29
+ if (isUndefined(systemAccount)) {
30
+ return await this.#systemAccountRepository.insert({
31
+ type: SubjectType.System,
28
32
  tenantId,
29
33
  identifier,
34
+ status: SubjectStatus.Active,
35
+ displayName: `System: ${identifier}`,
36
+ lastActivityTimestamp: null,
30
37
  });
31
- if (isUndefined(systemAccount)) {
32
- systemAccount = await this.#systemAccountRepository.withTransaction(tx).insert({
33
- tenantId,
34
- identifier,
35
- });
36
- return await this.#subjectRepository.withTransaction(tx).insert({
37
- type: SubjectType.System,
38
- tenantId,
39
- systemAccountId: systemAccount.id,
40
- displayName: `System: ${identifier}`,
41
- userId: null,
42
- serviceAccountId: null,
43
- });
44
- }
45
- return await this.#subjectRepository.withTransaction(tx).loadByQuery({
46
- tenantId,
47
- systemAccountId: systemAccount.id,
48
- });
49
- });
38
+ }
39
+ return systemAccount;
50
40
  }
51
41
  async createUser(data) {
52
42
  const { tenantId, email, firstName, lastName, status } = data;
53
- return await this.#userRepository.transaction(async (tx) => {
54
- const user = await this.#userRepository.withTransaction(tx).insert({
55
- tenantId,
56
- email,
57
- firstName,
58
- lastName,
59
- status: status ?? UserStatus.Active,
60
- });
61
- await this.#subjectRepository.withTransaction(tx).insert({
62
- tenantId,
63
- type: SubjectType.User,
64
- displayName: `${firstName} ${lastName}`,
65
- systemAccountId: null,
66
- userId: user.id,
67
- serviceAccountId: null,
68
- });
69
- return user;
43
+ return await this.#userRepository.insert({
44
+ type: SubjectType.User,
45
+ tenantId,
46
+ email,
47
+ firstName,
48
+ lastName,
49
+ status: status ?? SubjectStatus.Active,
50
+ lastActivityTimestamp: null,
70
51
  });
71
52
  }
72
53
  async updateUser(tenantId, userId, data) {
73
54
  const { firstName, lastName, status } = data;
74
55
  await this.#userRepository.transaction(async (tx) => {
56
+ const userRepository = this.#userRepository.withTransaction(tx);
75
57
  const updateData = {};
76
58
  if (isDefined(firstName)) {
77
59
  updateData.firstName = firstName;
@@ -83,10 +65,7 @@ let SubjectService = class SubjectService {
83
65
  updateData.status = status;
84
66
  }
85
67
  if (Object.keys(updateData).length > 0) {
86
- const updatedUser = await this.#userRepository.withTransaction(tx).updateByQuery({ tenantId, userId }, updateData);
87
- if (isDefined(firstName) || isDefined(lastName)) {
88
- await this.#subjectRepository.withTransaction(tx).updateByQuery({ tenantId, userId }, { displayName: formatPersonName(updatedUser) });
89
- }
68
+ await userRepository.updateByQuery({ tenantId, id: userId }, updateData);
90
69
  }
91
70
  });
92
71
  }
@@ -95,10 +74,10 @@ let SubjectService = class SubjectService {
95
74
  if (!mailPattern.test(email)) {
96
75
  throw new BadRequestError(`Invalid email format.`);
97
76
  }
98
- await this.#userRepository.updateByQuery({ tenantId, userId }, { email });
77
+ await this.#userRepository.updateByQuery({ tenantId, id: userId }, { email });
99
78
  }
100
- async getUserSubject(tenantId, userId) {
101
- return await this.#subjectRepository.loadByQuery({ tenantId, userId });
79
+ async getUser(tenantId, userId) {
80
+ return await this.#userRepository.loadByQuery({ tenantId, id: userId });
102
81
  }
103
82
  async getUserByEmail(tenantId, email) {
104
83
  return await this.#userRepository.loadByQuery({ tenantId, email });
@@ -111,48 +90,71 @@ let SubjectService = class SubjectService {
111
90
  return isDefined(user);
112
91
  }
113
92
  async getUserBySubject(subject) {
114
- assertNotNull(subject.userId, 'Subject is not a user subject');
115
- return await this.#userRepository.load(subject.userId);
93
+ if (subject instanceof User) {
94
+ return subject;
95
+ }
96
+ if (subject.type != SubjectType.User) {
97
+ throw new BadRequestError('Subject is not a user subject');
98
+ }
99
+ return await this.#userRepository.loadByQuery({ tenantId: subject.tenantId, id: subject.id });
116
100
  }
117
101
  async loadManyUsersByEmails(tenantId, emails) {
118
102
  return await this.#userRepository.loadManyByQuery({ tenantId, email: { $in: emails } });
119
103
  }
120
104
  async createServiceAccount(data) {
121
- const { tenantId, description, parent } = data;
122
- return await this.#serviceAccountRepository.transaction(async (tx) => {
123
- const serviceAccount = await this.#serviceAccountRepository.withTransaction(tx).insert({
124
- tenantId,
125
- description,
126
- parent,
127
- });
128
- await this.#subjectRepository.withTransaction(tx).insert({
129
- tenantId,
130
- type: SubjectType.ServiceAccount,
131
- displayName: description,
132
- userId: null,
133
- systemAccountId: null,
134
- serviceAccountId: serviceAccount.id,
135
- });
136
- return serviceAccount;
105
+ const { tenantId, description, displayName, parent, status } = data;
106
+ return await this.#serviceAccountRepository.insert({
107
+ type: SubjectType.ServiceAccount,
108
+ tenantId,
109
+ description,
110
+ displayName,
111
+ parent,
112
+ status: status ?? SubjectStatus.Active,
113
+ lastActivityTimestamp: null,
137
114
  });
138
115
  }
139
116
  async updateServiceAccount(tenantId, serviceAccountId, data) {
140
- const { displayName, description } = data;
141
- await this.#subjectRepository.transaction(async (tx) => {
142
- if (isDefined(displayName)) {
143
- await this.#subjectRepository.withTransaction(tx).updateByQuery({ tenantId, serviceAccountId }, { displayName });
144
- }
145
- if (isDefined(description)) {
146
- await this.#serviceAccountRepository.withTransaction(tx).updateByQuery({ tenantId, serviceAccountId }, { description });
147
- }
148
- });
117
+ const { displayName, description, status } = data;
118
+ const update = {};
119
+ if (isDefined(displayName)) {
120
+ update.displayName = displayName;
121
+ }
122
+ if (isDefined(description)) {
123
+ update.description = description;
124
+ }
125
+ if (isDefined(status)) {
126
+ update.status = status;
127
+ }
128
+ if (Object.keys(update).length > 0) {
129
+ await this.#serviceAccountRepository.updateByQuery({ tenantId, id: serviceAccountId }, update);
130
+ }
149
131
  }
150
- async getServiceAccountSubject(tenantId, serviceAccountId) {
151
- return await this.#subjectRepository.loadByQuery({ tenantId, serviceAccountId });
132
+ async getServiceAccount(tenantId, serviceAccountId) {
133
+ return await this.#serviceAccountRepository.loadByQuery({ tenantId, id: serviceAccountId });
152
134
  }
153
135
  async getServiceAccountBySubject(subject) {
154
- assertNotNull(subject.serviceAccountId, 'Subject is not a service account subject');
155
- return await this.#serviceAccountRepository.load(subject.serviceAccountId);
136
+ if (subject instanceof ServiceAccount) {
137
+ return subject;
138
+ }
139
+ if (subject.type != SubjectType.ServiceAccount) {
140
+ throw new BadRequestError('Subject is not a service account subject');
141
+ }
142
+ return await this.#serviceAccountRepository.loadByQuery({ tenantId: subject.tenantId, id: subject.id });
143
+ }
144
+ async exists(tenantId, id) {
145
+ return await this.#subjectRepository.hasByQuery({ tenantId, id });
146
+ }
147
+ async deleteUser(tenantId, userId) {
148
+ await this.#userRepository.deleteByQuery({ tenantId, id: userId });
149
+ }
150
+ async deleteServiceAccount(tenantId, serviceAccountId) {
151
+ await this.#serviceAccountRepository.deleteByQuery({ tenantId, id: serviceAccountId });
152
+ }
153
+ async listUsers(tenantId) {
154
+ return await this.#userRepository.loadManyByQuery({ tenantId });
155
+ }
156
+ async listServiceAccounts(tenantId) {
157
+ return await this.#serviceAccountRepository.loadManyByQuery({ tenantId });
156
158
  }
157
159
  };
158
160
  SubjectService = __decorate([
@@ -0,0 +1,13 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { runInInjectionContext } from '../../injector/index.js';
3
+ import { setupIntegrationTest } from '../../unit-test/index.js';
4
+ import { DefaultAuthenticationAncillaryService } from './authentication.test-ancillary-service.js';
5
+ describe('AuthenticationAncillaryService', () => {
6
+ test('default implementation should be abstract or have defaults', async () => {
7
+ const { injector } = await setupIntegrationTest({ modules: { authentication: true } });
8
+ await runInInjectionContext(injector, async () => {
9
+ const service = await injector.resolveAsync(DefaultAuthenticationAncillaryService);
10
+ expect(await service.canImpersonate({}, {}, {})).toBe(true);
11
+ });
12
+ });
13
+ });
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { SecretRequirementsError } from '../errors/secret-requirements.error.js';
3
+ import { DefaultAuthenticationSecretRequirementsValidator } from '../server/authentication-secret-requirements.validator.js';
4
+ describe('DefaultAuthenticationSecretRequirementsValidator', () => {
5
+ const validator = new DefaultAuthenticationSecretRequirementsValidator();
6
+ it('should return success when password is strong and not pwned', async () => {
7
+ // A very long random string is unlikely to be pwned and will be strong
8
+ const result = await validator.testSecretRequirements('Very-Strong-And-Long-Password-2026!@#$%^&*()');
9
+ expect(result.success).toBe(true);
10
+ });
11
+ it('should return failure when password is pwned', async () => {
12
+ // "password" is definitely pwned
13
+ const result = await validator.testSecretRequirements('password');
14
+ expect(result.success).toBe(false);
15
+ expect(result.reason).toContain('exposed in data breach');
16
+ });
17
+ it('should return failure when password is too weak', async () => {
18
+ // "abc" is too weak (and likely pwned)
19
+ const result = await validator.testSecretRequirements('abc');
20
+ expect(result.success).toBe(false);
21
+ expect(result.reason).toBeDefined();
22
+ });
23
+ it('should throw SecretRequirementsError on validation failure', async () => {
24
+ await expect(validator.validateSecretRequirements('abc')).rejects.toThrow(SecretRequirementsError);
25
+ });
26
+ it('should not throw on validation success', async () => {
27
+ await expect(validator.validateSecretRequirements('Very-Strong-And-Long-Password-2026!@#$%^&*()')).resolves.not.toThrow();
28
+ });
29
+ });
@@ -0,0 +1,88 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'vitest';
2
+ import { AuthenticationClientService } from '../../authentication/client/index.js';
3
+ import { AuthenticationAncillaryService, AuthenticationService as AuthenticationServerService } from '../../authentication/server/index.js';
4
+ import { HttpClientOptions } from '../../http/client/index.js';
5
+ import { HttpServer } from '../../http/server/index.js';
6
+ import { runInInjectionContext } from '../../injector/index.js';
7
+ import { clearTenantData, setupIntegrationTest } from '../../unit-test/index.js';
8
+ import { SubjectService } from '../server/subject.service.js';
9
+ import { DefaultAuthenticationAncillaryService } from './authentication.test-ancillary-service.js';
10
+ describe('AuthenticationApiController Integration', () => {
11
+ let injector;
12
+ let database;
13
+ let service;
14
+ let serverService;
15
+ let subjectService;
16
+ let server;
17
+ const schema = 'authentication';
18
+ const tenantId = crypto.randomUUID();
19
+ beforeAll(async () => {
20
+ ({ injector, database } = await setupIntegrationTest({
21
+ modules: { authentication: true, audit: true, keyValueStore: true, test: true, api: true, webServer: true },
22
+ authenticationAncillaryService: DefaultAuthenticationAncillaryService,
23
+ }));
24
+ await runInInjectionContext(injector, async () => {
25
+ server = injector.resolve(HttpServer);
26
+ const httpClientOptions = injector.resolve(HttpClientOptions);
27
+ httpClientOptions.baseUrl = `http://localhost:${server.port}`;
28
+ serverService = await injector.resolveAsync(AuthenticationServerService);
29
+ subjectService = await injector.resolveAsync(SubjectService);
30
+ service = await injector.resolveAsync(AuthenticationClientService);
31
+ service.initialize();
32
+ });
33
+ });
34
+ afterAll(async () => {
35
+ await server?.close(1000);
36
+ await injector?.dispose();
37
+ });
38
+ beforeEach(async () => {
39
+ await clearTenantData(database, schema, ['credentials', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
40
+ });
41
+ test('login should work via API client', async () => {
42
+ await runInInjectionContext(injector, async () => {
43
+ const user = await subjectService.createUser({ tenantId, email: 'api-login@example.com', firstName: 'A', lastName: 'L' });
44
+ await serverService.setCredentials(user, 'Strong-Password-2026!');
45
+ await service.login({ tenantId, subject: user.id }, 'Strong-Password-2026!');
46
+ expect(service.isLoggedIn()).toBe(true);
47
+ expect(service.subjectId()).toBe(user.id);
48
+ });
49
+ });
50
+ test('checkSecret should work via API client', async () => {
51
+ const result = await service.checkSecret('abc');
52
+ expect(result.strength).toBeLessThan(2);
53
+ });
54
+ test('refresh should work via API client', async () => {
55
+ await runInInjectionContext(injector, async () => {
56
+ const user = await subjectService.createUser({ tenantId, email: 'api-refresh@example.com', firstName: 'A', lastName: 'L' });
57
+ await serverService.setCredentials(user, 'Strong-Password-2026!');
58
+ await service.login({ tenantId, subject: user.id }, 'Strong-Password-2026!');
59
+ const initialToken = service.token()?.jti;
60
+ await service.refresh();
61
+ expect(service.token()?.jti).not.toBe(initialToken);
62
+ });
63
+ });
64
+ test('impersonation should work via API client', async () => {
65
+ await runInInjectionContext(injector, async () => {
66
+ const admin = await subjectService.createUser({ tenantId, email: 'api-admin@example.com', firstName: 'A', lastName: 'D' });
67
+ const user = await subjectService.createUser({ tenantId, email: 'api-user@example.com', firstName: 'U', lastName: 'S' });
68
+ await serverService.setCredentials(admin, 'Admin-Pass-123!');
69
+ await service.login({ tenantId, subject: admin.id }, 'Admin-Pass-123!');
70
+ await service.impersonate(user.id);
71
+ expect(service.subjectId()).toBe(user.id);
72
+ expect(service.token()?.impersonator).toBe(admin.id);
73
+ await service.unimpersonate();
74
+ expect(service.subjectId()).toBe(admin.id);
75
+ });
76
+ });
77
+ test('secret reset should work via API client', async () => {
78
+ await runInInjectionContext(injector, async () => {
79
+ const user = await subjectService.createUser({ tenantId, email: 'api-reset@example.com', firstName: 'A', lastName: 'L' });
80
+ const ancillaryService = injector.resolve(AuthenticationAncillaryService);
81
+ await service.initResetSecret({ tenantId, subject: user.id }, undefined);
82
+ expect(ancillaryService.lastResetData).toBeDefined();
83
+ await service.resetSecret(ancillaryService.lastResetData.token, 'New-API-Pass-123!');
84
+ await service.login({ tenantId, subject: user.id }, 'New-API-Pass-123!');
85
+ expect(service.isLoggedIn()).toBe(true);
86
+ });
87
+ });
88
+ });
@@ -0,0 +1,48 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from 'vitest';
2
+ import { AuthenticationApiRequestTokenProvider } from '../../authentication/server/authentication-api-request-token.provider.js';
3
+ import { AuthenticationService } from '../../authentication/server/index.js';
4
+ import { runInInjectionContext } from '../../injector/index.js';
5
+ import { setupIntegrationTest } from '../../unit-test/index.js';
6
+ import { SubjectService } from '../server/subject.service.js';
7
+ describe('AuthenticationApiRequestTokenProvider', () => {
8
+ let injector;
9
+ let authenticationService;
10
+ let subjectService;
11
+ let tokenProvider;
12
+ const tenantId = crypto.randomUUID();
13
+ beforeAll(async () => {
14
+ ({ injector } = await setupIntegrationTest({ modules: { authentication: true, test: true } }));
15
+ authenticationService = await injector.resolveAsync(AuthenticationService);
16
+ subjectService = await injector.resolveAsync(SubjectService);
17
+ tokenProvider = injector.resolve(AuthenticationApiRequestTokenProvider);
18
+ });
19
+ afterAll(async () => {
20
+ await injector?.dispose();
21
+ });
22
+ test('should extract and validate token', async () => {
23
+ await runInInjectionContext(injector, async () => {
24
+ const user = await subjectService.createUser({ tenantId, email: 'provider-test@example.com', firstName: 'P', lastName: 'T' });
25
+ const tokenResult = await authenticationService.getToken(user, undefined);
26
+ const request = {
27
+ headers: {
28
+ tryGet: (name) => name == 'Authorization' ? `Bearer ${tokenResult.token}` : undefined,
29
+ },
30
+ cookies: {
31
+ tryGet: () => undefined,
32
+ },
33
+ };
34
+ const data = { request };
35
+ const token = await tokenProvider.tryGetToken(data);
36
+ expect(token.payload.subject).toBe(user.id);
37
+ });
38
+ });
39
+ test('should return null if no token', async () => {
40
+ const request = {
41
+ headers: { tryGet: () => undefined },
42
+ cookies: { tryGet: () => undefined },
43
+ };
44
+ const data = { request };
45
+ const token = await tokenProvider.tryGetToken(data);
46
+ expect(token).toBeNull();
47
+ });
48
+ });
@@ -0,0 +1,23 @@
1
+ import { of } from 'rxjs';
2
+ import { describe, expect, test, vi } from 'vitest';
3
+ import { HttpClientRequest } from '../../http/client/index.js';
4
+ import { waitForAuthenticationCredentialsMiddleware } from '../client/http-client.middleware.js';
5
+ describe('waitForAuthenticationCredentialsMiddleware', () => {
6
+ test('should wait for token and call next', async () => {
7
+ const authenticationServiceMock = {
8
+ token: vi.fn().mockReturnValue({ jti: 'test-token' }),
9
+ validToken$: of({ jti: 'test-token' }),
10
+ hasValidToken: true,
11
+ };
12
+ const middleware = waitForAuthenticationCredentialsMiddleware(authenticationServiceMock);
13
+ const request = new HttpClientRequest('http://localhost');
14
+ request.context = {
15
+ endpoint: {
16
+ credentials: true,
17
+ },
18
+ };
19
+ const next = vi.fn().mockResolvedValue(undefined);
20
+ await middleware({ request }, next);
21
+ expect(next).toHaveBeenCalled();
22
+ });
23
+ });
@@ -0,0 +1,70 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'vitest';
2
+ import { AuthenticationClientService } from '../../authentication/client/index.js';
3
+ import { AuthenticationService as AuthenticationServerService } from '../../authentication/server/index.js';
4
+ import { HttpClientOptions } from '../../http/client/index.js';
5
+ import { HttpServer } from '../../http/server/index.js';
6
+ import { runInInjectionContext } from '../../injector/index.js';
7
+ import { clearTenantData, setupIntegrationTest } from '../../unit-test/index.js';
8
+ import { SubjectService } from '../server/subject.service.js';
9
+ import { DefaultAuthenticationAncillaryService } from './authentication.test-ancillary-service.js';
10
+ describe('AuthenticationClientService Integration', () => {
11
+ let injector;
12
+ let database;
13
+ let service;
14
+ let serverService;
15
+ let subjectService;
16
+ let server;
17
+ const schema = 'authentication';
18
+ const tenantId = crypto.randomUUID();
19
+ beforeAll(async () => {
20
+ ({ injector, database } = await setupIntegrationTest({
21
+ modules: { authentication: true, audit: true, keyValueStore: true, test: true, api: true, webServer: true },
22
+ authenticationAncillaryService: DefaultAuthenticationAncillaryService,
23
+ }));
24
+ await runInInjectionContext(injector, async () => {
25
+ server = injector.resolve(HttpServer);
26
+ const httpClientOptions = injector.resolve(HttpClientOptions);
27
+ httpClientOptions.baseUrl = `http://localhost:${server.port}`;
28
+ serverService = await injector.resolveAsync(AuthenticationServerService);
29
+ subjectService = await injector.resolveAsync(SubjectService);
30
+ service = await injector.resolveAsync(AuthenticationClientService);
31
+ service.initialize();
32
+ });
33
+ });
34
+ afterAll(async () => {
35
+ await server?.close(1000);
36
+ await injector?.dispose();
37
+ });
38
+ beforeEach(async () => {
39
+ await clearTenantData(database, schema, ['credentials', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
40
+ });
41
+ test('login and logout should work', async () => {
42
+ await runInInjectionContext(injector, async () => {
43
+ const user = await subjectService.createUser({ tenantId, email: 'client-test@example.com', firstName: 'C', lastName: 'T' });
44
+ await serverService.setCredentials(user, 'Strong-Pass-2026!');
45
+ expect(service.isLoggedIn()).toBe(false);
46
+ await service.login({ tenantId, subject: user.id }, 'Strong-Pass-2026!');
47
+ expect(service.isLoggedIn()).toBe(true);
48
+ expect(service.subjectId()).toBe(user.id);
49
+ await service.logout();
50
+ expect(service.isLoggedIn()).toBe(false);
51
+ });
52
+ });
53
+ test('refresh should work', async () => {
54
+ await runInInjectionContext(injector, async () => {
55
+ const user = await subjectService.createUser({ tenantId, email: 'refresh-test@example.com', firstName: 'R', lastName: 'T' });
56
+ await serverService.setCredentials(user, 'Strong-Pass-2026!');
57
+ await service.login({ tenantId, subject: user.id }, 'Strong-Pass-2026!');
58
+ const initialToken = service.token()?.jti;
59
+ await service.refresh();
60
+ expect(service.token()?.jti).not.toBe(initialToken);
61
+ expect(service.isLoggedIn()).toBe(true);
62
+ });
63
+ });
64
+ test('checkSecret should work', async () => {
65
+ const result = await service.checkSecret('123');
66
+ expect(result.strength).toBeLessThan(2);
67
+ const strongResult = await service.checkSecret('Very-Strong-Password-2026-!@#$');
68
+ expect(strongResult.strength).toBeGreaterThanOrEqual(2);
69
+ });
70
+ });