@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
@@ -0,0 +1,232 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Injector, runInInjectionContext } from '../../injector/index.js';
11
+ import { Integer, StringProperty } from '../../schema/index.js';
12
+ import { toArrayAsync } from '../../utils/async-iterable-helpers/to-array.js';
13
+ import { sql } from 'drizzle-orm';
14
+ import { beforeAll, describe, expect, test } from 'vitest';
15
+ import { Table } from '../decorators.js';
16
+ import { Entity } from '../entity.js';
17
+ import { configureOrm, Database } from '../server/index.js';
18
+ import { injectRepository } from '../server/repository.js';
19
+ describe('ORM Repository Advanced (Integration)', () => {
20
+ let injector;
21
+ let db;
22
+ const schema = 'test_orm_advanced';
23
+ let AdvancedEntity = class AdvancedEntity extends Entity {
24
+ name;
25
+ value;
26
+ };
27
+ __decorate([
28
+ StringProperty(),
29
+ __metadata("design:type", String)
30
+ ], AdvancedEntity.prototype, "name", void 0);
31
+ __decorate([
32
+ Integer(),
33
+ __metadata("design:type", Number)
34
+ ], AdvancedEntity.prototype, "value", void 0);
35
+ AdvancedEntity = __decorate([
36
+ Table('advanced_entities', { schema })
37
+ ], AdvancedEntity);
38
+ beforeAll(async () => {
39
+ injector = new Injector('Test');
40
+ configureOrm({
41
+ repositoryConfig: { schema },
42
+ connection: {
43
+ host: '127.0.0.1', port: 5432, user: 'tstdl', password: 'wf7rq6glrk5jykne', database: 'tstdl',
44
+ },
45
+ });
46
+ db = injector.resolve(Database);
47
+ await db.execute(sql `CREATE SCHEMA IF NOT EXISTS ${sql.identifier(schema)}`);
48
+ await db.execute(sql `DROP TABLE IF EXISTS ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
49
+ await db.execute(sql `
50
+ CREATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} (
51
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
52
+ name TEXT NOT NULL,
53
+ value INTEGER NOT NULL,
54
+ revision INTEGER NOT NULL,
55
+ revision_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
56
+ create_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
57
+ delete_timestamp TIMESTAMP WITH TIME ZONE,
58
+ attributes JSONB NOT NULL DEFAULT '{}'
59
+ )
60
+ `);
61
+ });
62
+ test('should support loadManyCursor', async () => {
63
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
64
+ await runInInjectionContext(injector, async () => {
65
+ const repository = injectRepository(AdvancedEntity);
66
+ const e1 = await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }));
67
+ const e2 = await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E2', value: 2 }));
68
+ const cursor = repository.loadManyCursor([e1.id, e2.id]);
69
+ const results = await toArrayAsync(cursor);
70
+ expect(results).toHaveLength(2);
71
+ expect(results.map((r) => r.name).sort()).toEqual(['E1', 'E2']);
72
+ });
73
+ });
74
+ test('should support loadAllCursor', async () => {
75
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
76
+ await runInInjectionContext(injector, async () => {
77
+ const repository = injectRepository(AdvancedEntity);
78
+ await repository.insertMany([
79
+ Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }),
80
+ Object.assign(new AdvancedEntity(), { name: 'E2', value: 2 }),
81
+ ]);
82
+ const cursor = repository.loadAllCursor();
83
+ const results = await toArrayAsync(cursor);
84
+ expect(results).toHaveLength(2);
85
+ });
86
+ });
87
+ test('should support countByQuery with various filters', async () => {
88
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
89
+ await runInInjectionContext(injector, async () => {
90
+ const repository = injectRepository(AdvancedEntity);
91
+ await repository.insertMany([
92
+ Object.assign(new AdvancedEntity(), { name: 'A', value: 10 }),
93
+ Object.assign(new AdvancedEntity(), { name: 'B', value: 20 }),
94
+ Object.assign(new AdvancedEntity(), { name: 'C', value: 30 }),
95
+ ]);
96
+ expect(await repository.countByQuery({ value: { $gt: 15 } })).toBe(2);
97
+ expect(await repository.countByQuery({ value: { $lt: 25 } })).toBe(2);
98
+ expect(await repository.countByQuery({ name: 'A' })).toBe(1);
99
+ });
100
+ });
101
+ test('should support has and hasByQuery', async () => {
102
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
103
+ await runInInjectionContext(injector, async () => {
104
+ const repository = injectRepository(AdvancedEntity);
105
+ const e1 = await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }));
106
+ expect(await repository.has(e1.id)).toBe(true);
107
+ expect(await repository.has('00000000-0000-0000-0000-000000000000')).toBe(false);
108
+ expect(await repository.hasByQuery({ name: 'E1' })).toBe(true);
109
+ expect(await repository.hasByQuery({ name: 'E2' })).toBe(false);
110
+ });
111
+ });
112
+ test('should support tryUpdate', async () => {
113
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
114
+ await runInInjectionContext(injector, async () => {
115
+ const repository = injectRepository(AdvancedEntity);
116
+ const e1 = await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }));
117
+ const updated = await repository.tryUpdate(e1.id, { name: 'E1-Updated' });
118
+ expect(updated.name).toBe('E1-Updated');
119
+ const missing = await repository.tryUpdate('00000000-0000-0000-0000-000000000000', { name: 'X' });
120
+ expect(missing).toBeUndefined();
121
+ });
122
+ });
123
+ test('should support tryUpdateByQuery', async () => {
124
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
125
+ await runInInjectionContext(injector, async () => {
126
+ const repository = injectRepository(AdvancedEntity);
127
+ await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }));
128
+ const updated = await repository.tryUpdateByQuery({ name: 'E1' }, { value: 100 });
129
+ expect(updated.value).toBe(100);
130
+ const missing = await repository.tryUpdateByQuery({ name: 'Missing' }, { value: 0 });
131
+ expect(missing).toBeUndefined();
132
+ });
133
+ });
134
+ test('should support insertIfNotExists', async () => {
135
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
136
+ await runInInjectionContext(injector, async () => {
137
+ const repository = injectRepository(AdvancedEntity);
138
+ const e1 = await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }));
139
+ // Conflict on ID
140
+ const conflict = await repository.insertIfNotExists('id', Object.assign(new AdvancedEntity(), { id: e1.id, name: 'Conflict', value: 2 }));
141
+ expect(conflict).toBeUndefined();
142
+ // No conflict
143
+ const success = await repository.insertIfNotExists('id', Object.assign(new AdvancedEntity(), { name: 'E2', value: 2 }));
144
+ expect(success).toBeDefined();
145
+ expect(success.name).toBe('E2');
146
+ });
147
+ });
148
+ test('should support pagination (limit/offset)', async () => {
149
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
150
+ await runInInjectionContext(injector, async () => {
151
+ const repository = injectRepository(AdvancedEntity);
152
+ await repository.insertMany([
153
+ Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }),
154
+ Object.assign(new AdvancedEntity(), { name: 'E2', value: 2 }),
155
+ Object.assign(new AdvancedEntity(), { name: 'E3', value: 3 }),
156
+ ]);
157
+ const p1 = await repository.loadAll({ limit: 2, order: { value: 'asc' } });
158
+ expect(p1).toHaveLength(2);
159
+ expect(p1[0].name).toBe('E1');
160
+ expect(p1[1].name).toBe('E2');
161
+ const p2 = await repository.loadAll({ limit: 2, offset: 2, order: { value: 'asc' } });
162
+ expect(p2).toHaveLength(1);
163
+ expect(p2[0].name).toBe('E3');
164
+ });
165
+ });
166
+ test('should support complex ordering', async () => {
167
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
168
+ await runInInjectionContext(injector, async () => {
169
+ const repository = injectRepository(AdvancedEntity);
170
+ await repository.insertMany([
171
+ Object.assign(new AdvancedEntity(), { name: 'B', value: 10 }),
172
+ Object.assign(new AdvancedEntity(), { name: 'A', value: 20 }),
173
+ Object.assign(new AdvancedEntity(), { name: 'A', value: 10 }),
174
+ ]);
175
+ const results = await repository.loadAll({
176
+ order: [
177
+ ['name', 'asc'],
178
+ ['value', 'desc'],
179
+ ],
180
+ });
181
+ expect(results[0].name).toBe('A');
182
+ expect(results[0].value).toBe(20);
183
+ expect(results[1].name).toBe('A');
184
+ expect(results[1].value).toBe(10);
185
+ expect(results[2].name).toBe('B');
186
+ });
187
+ });
188
+ test('should return empty array for empty loadMany', async () => {
189
+ await runInInjectionContext(injector, async () => {
190
+ const repository = injectRepository(AdvancedEntity);
191
+ const results = await repository.loadMany([]);
192
+ expect(results).toEqual([]);
193
+ });
194
+ });
195
+ test('should fail on duplicate ID insert', async () => {
196
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
197
+ await runInInjectionContext(injector, async () => {
198
+ const repository = injectRepository(AdvancedEntity);
199
+ const id = '00000000-0000-0000-0000-000000000001';
200
+ await repository.insert(Object.assign(new AdvancedEntity(), { id, name: 'E1', value: 1 }));
201
+ await expect(repository.insert(Object.assign(new AdvancedEntity(), { id, name: 'E2', value: 2 }))).rejects.toThrow();
202
+ });
203
+ });
204
+ test('should support explicit transaction usage', async () => {
205
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
206
+ await runInInjectionContext(injector, async () => {
207
+ const repository = injectRepository(AdvancedEntity);
208
+ await repository.transaction(async (tx) => {
209
+ const txRepo = repository.withTransaction(tx);
210
+ await txRepo.insert(Object.assign(new AdvancedEntity(), { name: 'TxEntity', value: 100 }));
211
+ // Should be visible in tx
212
+ expect(await txRepo.count()).toBe(1);
213
+ });
214
+ // Should be committed
215
+ expect(await repository.count()).toBe(1);
216
+ });
217
+ });
218
+ test('should exclude soft-deleted entities by default', async () => {
219
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
220
+ await runInInjectionContext(injector, async () => {
221
+ const repository = injectRepository(AdvancedEntity);
222
+ const e1 = await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }));
223
+ await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E2', value: 2 }));
224
+ await repository.delete(e1.id);
225
+ const all = await repository.loadAll();
226
+ expect(all).toHaveLength(1);
227
+ expect(all[0].name).toBe('E2');
228
+ const withDeleted = await repository.loadManyByQuery({}, { withDeleted: true });
229
+ expect(withDeleted).toHaveLength(2);
230
+ });
231
+ });
232
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,99 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Injector, runInInjectionContext } from '../../injector/index.js';
11
+ import { StringProperty } from '../../schema/index.js';
12
+ import { sql } from 'drizzle-orm';
13
+ import { beforeAll, describe, expect, test } from 'vitest';
14
+ import { Table } from '../decorators.js';
15
+ import { Entity } from '../entity.js';
16
+ import { configureOrm, Database } from '../server/index.js';
17
+ import { injectRepository } from '../server/repository.js';
18
+ describe('ORM Repository Attributes (Integration)', () => {
19
+ let injector;
20
+ let db;
21
+ const schema = 'test_orm_attributes';
22
+ let AttributeEntity = class AttributeEntity extends Entity {
23
+ name;
24
+ };
25
+ __decorate([
26
+ StringProperty(),
27
+ __metadata("design:type", String)
28
+ ], AttributeEntity.prototype, "name", void 0);
29
+ AttributeEntity = __decorate([
30
+ Table('attribute_entities', { schema })
31
+ ], AttributeEntity);
32
+ beforeAll(async () => {
33
+ injector = new Injector('Test');
34
+ configureOrm({
35
+ repositoryConfig: { schema },
36
+ connection: {
37
+ host: '127.0.0.1', port: 5432, user: 'tstdl', password: 'wf7rq6glrk5jykne', database: 'tstdl',
38
+ },
39
+ });
40
+ db = injector.resolve(Database);
41
+ await db.execute(sql `CREATE SCHEMA IF NOT EXISTS ${sql.identifier(schema)}`);
42
+ await db.execute(sql `DROP TABLE IF EXISTS ${sql.identifier(schema)}.${sql.identifier('attribute_entities')} CASCADE`);
43
+ await db.execute(sql `
44
+ CREATE TABLE ${sql.identifier(schema)}.${sql.identifier('attribute_entities')} (
45
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
46
+ name TEXT NOT NULL,
47
+ revision INTEGER NOT NULL,
48
+ revision_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
49
+ create_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
50
+ delete_timestamp TIMESTAMP WITH TIME ZONE,
51
+ attributes JSONB NOT NULL DEFAULT '{}'
52
+ )
53
+ `);
54
+ });
55
+ test('should support partial attribute updates', async () => {
56
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('attribute_entities')} CASCADE`);
57
+ await runInInjectionContext(injector, async () => {
58
+ const repository = injectRepository(AttributeEntity);
59
+ const e1 = await repository.insert(Object.assign(new AttributeEntity(), {
60
+ name: 'E1',
61
+ metadata: { attributes: { a: 1, b: 2 } },
62
+ }));
63
+ expect(e1.metadata.attributes).toEqual({ a: 1, b: 2 });
64
+ const updated = await repository.update(e1.id, {
65
+ metadata: { attributes: { b: 3, c: 4 } },
66
+ });
67
+ expect(updated.metadata.attributes).toEqual({ a: 1, b: 3, c: 4 });
68
+ });
69
+ });
70
+ test('should update attributes during soft delete', async () => {
71
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('attribute_entities')} CASCADE`);
72
+ await runInInjectionContext(injector, async () => {
73
+ const repository = injectRepository(AttributeEntity);
74
+ const e1 = await repository.insert(Object.assign(new AttributeEntity(), {
75
+ name: 'E1',
76
+ metadata: { attributes: { initial: true } },
77
+ }));
78
+ const deleted = await repository.delete(e1.id, undefined, { attributes: { deleted: true } });
79
+ expect(deleted.metadata.attributes).toEqual({ initial: true, deleted: true });
80
+ });
81
+ });
82
+ test('should support querying by raw SQL on attributes', async () => {
83
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('attribute_entities')} CASCADE`);
84
+ await runInInjectionContext(injector, async () => {
85
+ const repository = injectRepository(AttributeEntity);
86
+ await repository.insert(Object.assign(new AttributeEntity(), {
87
+ name: 'E1',
88
+ metadata: { attributes: { key: 'value1' } },
89
+ }));
90
+ await repository.insert(Object.assign(new AttributeEntity(), {
91
+ name: 'E2',
92
+ metadata: { attributes: { key: 'value2' } },
93
+ }));
94
+ const results = await repository.loadManyByQuery(sql `attributes->>'key' = 'value1'`);
95
+ expect(results).toHaveLength(1);
96
+ expect(results[0].name).toBe('E1');
97
+ });
98
+ });
99
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,187 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { sql } from 'drizzle-orm';
11
+ import { beforeAll, describe, expect, test } from 'vitest';
12
+ import { Injector, runInInjectionContext } from '../../injector/index.js';
13
+ import { Integer, StringProperty } from '../../schema/index.js';
14
+ import { GeneratedTsVector, Table } from '../decorators.js';
15
+ import { Entity } from '../entity.js';
16
+ import { configureOrm, Database } from '../server/index.js';
17
+ import { injectRepository } from '../server/repository.js';
18
+ describe('ORM Repository Comprehensive Options & Edge Cases', () => {
19
+ let injector;
20
+ let db;
21
+ const schema = 'test_orm_comprehensive';
22
+ let ComprehensiveEntity = class ComprehensiveEntity extends Entity {
23
+ name;
24
+ category;
25
+ value;
26
+ searchVector;
27
+ };
28
+ __decorate([
29
+ StringProperty(),
30
+ __metadata("design:type", String)
31
+ ], ComprehensiveEntity.prototype, "name", void 0);
32
+ __decorate([
33
+ StringProperty(),
34
+ __metadata("design:type", String)
35
+ ], ComprehensiveEntity.prototype, "category", void 0);
36
+ __decorate([
37
+ Integer(),
38
+ __metadata("design:type", Number)
39
+ ], ComprehensiveEntity.prototype, "value", void 0);
40
+ __decorate([
41
+ StringProperty(),
42
+ GeneratedTsVector({ sources: ['name', 'category'] }),
43
+ __metadata("design:type", Object)
44
+ ], ComprehensiveEntity.prototype, "searchVector", void 0);
45
+ ComprehensiveEntity = __decorate([
46
+ Table('comprehensive_entities', { schema })
47
+ ], ComprehensiveEntity);
48
+ beforeAll(async () => {
49
+ injector = new Injector('Test');
50
+ configureOrm({
51
+ repositoryConfig: { schema },
52
+ connection: {
53
+ host: '127.0.0.1', port: 5432, user: 'tstdl', password: 'wf7rq6glrk5jykne', database: 'tstdl',
54
+ },
55
+ });
56
+ db = injector.resolve(Database);
57
+ await db.execute(sql `CREATE SCHEMA IF NOT EXISTS ${sql.identifier(schema)}`);
58
+ await db.execute(sql `DROP TABLE IF EXISTS ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} CASCADE`);
59
+ await db.execute(sql `
60
+ CREATE TABLE ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} (
61
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
62
+ name TEXT NOT NULL,
63
+ category TEXT NOT NULL,
64
+ value INTEGER NOT NULL,
65
+ search_vector tsvector GENERATED ALWAYS AS (to_tsvector('simple', name || ' ' || category)) STORED,
66
+ revision INTEGER NOT NULL,
67
+ revision_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
68
+ create_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
69
+ delete_timestamp TIMESTAMP WITH TIME ZONE,
70
+ attributes JSONB NOT NULL DEFAULT '{}'
71
+ )
72
+ `);
73
+ await db.execute(sql `CREATE INDEX idx_comprehensive_search ON ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} USING GIN (search_vector)`);
74
+ });
75
+ test('should support DISTINCT ON loading', async () => {
76
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} CASCADE`);
77
+ await runInInjectionContext(injector, async () => {
78
+ const repository = injectRepository(ComprehensiveEntity);
79
+ await repository.insertMany([
80
+ Object.assign(new ComprehensiveEntity(), { name: 'A1', category: 'Cat1', value: 10 }),
81
+ Object.assign(new ComprehensiveEntity(), { name: 'A2', category: 'Cat1', value: 20 }),
82
+ Object.assign(new ComprehensiveEntity(), { name: 'B1', category: 'Cat2', value: 30 }),
83
+ ]);
84
+ // Load distinct by category, order by category and value desc to get the one with highest value per category
85
+ const results = await repository.loadManyByQuery({}, {
86
+ distinct: ['category'],
87
+ order: [['category', 'asc'], ['value', 'desc']],
88
+ });
89
+ expect(results).toHaveLength(2);
90
+ expect(results.find((r) => r.category === 'Cat1').name).toBe('A2'); // Value 20 > 10
91
+ expect(results.find((r) => r.category === 'Cat2').name).toBe('B1');
92
+ });
93
+ });
94
+ test('should support search with filter and distinct', async () => {
95
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} CASCADE`);
96
+ await runInInjectionContext(injector, async () => {
97
+ const repository = injectRepository(ComprehensiveEntity);
98
+ await repository.insertMany([
99
+ Object.assign(new ComprehensiveEntity(), { name: 'Apple One', category: 'Fruit', value: 5 }),
100
+ Object.assign(new ComprehensiveEntity(), { name: 'Apple Two', category: 'Fruit', value: 15 }),
101
+ Object.assign(new ComprehensiveEntity(), { name: 'Apple Three', category: 'Tech', value: 25 }),
102
+ ]);
103
+ // Search "Apple", filter value > 10, distinct category
104
+ const results = await repository.search({
105
+ query: { $tsvector: { fields: ['searchVector'], query: 'Apple' } },
106
+ filter: { value: { $gt: 10 } },
107
+ distinct: ['category'],
108
+ order: [['category', 'asc'], ['value', 'desc']], // Distinct on requires matching order prefix
109
+ });
110
+ expect(results).toHaveLength(2);
111
+ const categories = results.map((r) => r.entity.category).sort();
112
+ expect(categories).toEqual(['Fruit', 'Tech']);
113
+ const fruit = results.find((r) => r.entity.category === 'Fruit');
114
+ expect(fruit.entity.name).toBe('Apple Two'); // Value 15 > 10, Apple One filtered out or lower
115
+ });
116
+ });
117
+ test('should handle soft deletes correctly in updates', async () => {
118
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} CASCADE`);
119
+ await runInInjectionContext(injector, async () => {
120
+ const repository = injectRepository(ComprehensiveEntity);
121
+ const entity = await repository.insert(Object.assign(new ComprehensiveEntity(), { name: 'To Delete', category: 'Test', value: 1 }));
122
+ await repository.delete(entity.id);
123
+ // Try update without withDeleted (should fail/return undefined)
124
+ const tryUpdateResult = await repository.tryUpdate(entity.id, { name: 'Resurrected?' });
125
+ expect(tryUpdateResult).toBeUndefined();
126
+ // Ensure DB is unchanged
127
+ const row = await repository.tryLoad(entity.id, { withDeleted: true });
128
+ expect(row.name).toBe('To Delete');
129
+ // Update WITH withDeleted (should work)
130
+ const updateResult = await repository.update(entity.id, { name: 'Zombie' }, { withDeleted: true });
131
+ expect(updateResult.name).toBe('Zombie');
132
+ expect(updateResult.metadata.deleteTimestamp).not.toBeNull(); // Still deleted
133
+ });
134
+ });
135
+ test('should handle count with and without deleted', async () => {
136
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} CASCADE`);
137
+ await runInInjectionContext(injector, async () => {
138
+ const repository = injectRepository(ComprehensiveEntity);
139
+ await repository.insert(Object.assign(new ComprehensiveEntity(), { name: 'Alive', category: 'A', value: 1 }));
140
+ const dead = await repository.insert(Object.assign(new ComprehensiveEntity(), { name: 'Dead', category: 'A', value: 2 }));
141
+ await repository.delete(dead.id);
142
+ expect(await repository.count()).toBe(1);
143
+ expect(await repository.count({ withDeleted: true })).toBe(2);
144
+ expect(await repository.countByQuery({ category: 'A' })).toBe(1);
145
+ expect(await repository.countByQuery({ category: 'A' }, { withDeleted: true })).toBe(2);
146
+ });
147
+ });
148
+ test('should fail gracefully (return undefined) for missing entities in tryLoad operations', async () => {
149
+ await runInInjectionContext(injector, async () => {
150
+ const repository = injectRepository(ComprehensiveEntity);
151
+ expect(await repository.tryLoad('00000000-0000-0000-0000-000000000000')).toBeUndefined();
152
+ expect(await repository.tryLoadByQuery({ name: 'NonExistent' })).toBeUndefined();
153
+ });
154
+ });
155
+ test('should return empty array for empty search results', async () => {
156
+ await runInInjectionContext(injector, async () => {
157
+ const repository = injectRepository(ComprehensiveEntity);
158
+ const results = await repository.search({ query: { $tsvector: { fields: ['name'], query: 'NonExistentTerm' } } });
159
+ expect(results).toEqual([]);
160
+ });
161
+ });
162
+ test('should support complex ordering with multiple fields', async () => {
163
+ await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} CASCADE`);
164
+ await runInInjectionContext(injector, async () => {
165
+ const repository = injectRepository(ComprehensiveEntity);
166
+ await repository.insertMany([
167
+ Object.assign(new ComprehensiveEntity(), { name: 'A', category: 'X', value: 1 }),
168
+ Object.assign(new ComprehensiveEntity(), { name: 'B', category: 'X', value: 2 }),
169
+ Object.assign(new ComprehensiveEntity(), { name: 'A', category: 'Y', value: 3 }),
170
+ ]);
171
+ // Order by Name ASC, then Value DESC
172
+ const results = await repository.loadAll({
173
+ order: [['name', 'asc'], ['value', 'desc']],
174
+ });
175
+ expect(results).toHaveLength(3);
176
+ expect(results[0].name).toBe('A');
177
+ expect(results[0].value).toBe(3); // Y (3) comes before X (1) because both are A? No, wait.
178
+ // Name A (Y, 3) and Name A (X, 1).
179
+ // Order: Name ASC. A, A, B.
180
+ // Tie-break Name: Value DESC. 3, 1.
181
+ // So A(3) then A(1).
182
+ expect(results[1].name).toBe('A');
183
+ expect(results[1].value).toBe(1);
184
+ expect(results[2].name).toBe('B');
185
+ });
186
+ });
187
+ });
@@ -0,0 +1 @@
1
+ export {};