@tstdl/base 0.93.86 → 0.93.89

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 (318) 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 +19 -5
  30. package/authentication/models/subject.model.js +25 -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 +14 -8
  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/index.d.ts +1 -1
  115. package/orm/index.js +1 -1
  116. package/orm/query/base.d.ts +17 -17
  117. package/orm/query/base.js +1 -1
  118. package/orm/repository.types.d.ts +46 -2
  119. package/orm/schemas/tsvector.js +1 -1
  120. package/orm/server/drizzle/schema-converter.d.ts +3 -1
  121. package/orm/server/drizzle/schema-converter.js +120 -14
  122. package/orm/server/index.d.ts +1 -0
  123. package/orm/server/index.js +1 -0
  124. package/orm/server/module.d.ts +4 -2
  125. package/orm/server/module.js +6 -5
  126. package/orm/server/query-converter.d.ts +6 -3
  127. package/orm/server/query-converter.js +33 -21
  128. package/orm/server/repository-config.d.ts +8 -0
  129. package/orm/server/repository-config.js +8 -0
  130. package/orm/server/repository.d.ts +117 -43
  131. package/orm/server/repository.js +758 -254
  132. package/orm/server/transaction.d.ts +4 -2
  133. package/orm/server/transaction.js +14 -5
  134. package/orm/server/transactional.d.ts +6 -2
  135. package/orm/server/transactional.js +39 -9
  136. package/orm/server/types.d.ts +2 -0
  137. package/orm/sqls/case-when.d.ts +25 -0
  138. package/orm/sqls/case-when.js +54 -0
  139. package/orm/sqls/index.d.ts +2 -0
  140. package/orm/sqls/index.js +2 -0
  141. package/orm/{sqls.d.ts → sqls/sqls.d.ts} +67 -19
  142. package/orm/{sqls.js → sqls/sqls.js} +116 -22
  143. package/orm/tests/data-types.test.d.ts +1 -0
  144. package/orm/tests/data-types.test.js +39 -0
  145. package/orm/tests/decorators.test.d.ts +1 -0
  146. package/orm/tests/decorators.test.js +77 -0
  147. package/orm/tests/encryption.test.d.ts +1 -0
  148. package/orm/tests/encryption.test.js +34 -0
  149. package/orm/tests/query-complex.test.d.ts +1 -0
  150. package/orm/tests/query-complex.test.js +203 -0
  151. package/orm/tests/query-converter-complex.test.d.ts +1 -0
  152. package/orm/tests/query-converter-complex.test.js +126 -0
  153. package/orm/tests/query-converter.test.d.ts +1 -0
  154. package/orm/tests/query-converter.test.js +123 -0
  155. package/orm/tests/repository-advanced.test.d.ts +1 -0
  156. package/orm/tests/repository-advanced.test.js +232 -0
  157. package/orm/tests/repository-attributes.test.d.ts +1 -0
  158. package/orm/tests/repository-attributes.test.js +99 -0
  159. package/orm/tests/repository-comprehensive.test.d.ts +1 -0
  160. package/orm/tests/repository-comprehensive.test.js +187 -0
  161. package/orm/tests/repository-coverage.test.d.ts +1 -0
  162. package/orm/tests/repository-coverage.test.js +303 -0
  163. package/orm/tests/repository-cti-complex.test.d.ts +1 -0
  164. package/orm/tests/repository-cti-complex.test.js +170 -0
  165. package/orm/tests/repository-cti-embedded.test.d.ts +1 -0
  166. package/orm/tests/repository-cti-embedded.test.js +188 -0
  167. package/orm/tests/repository-cti-extensive.test.d.ts +1 -0
  168. package/orm/tests/repository-cti-extensive.test.js +308 -0
  169. package/orm/tests/repository-cti-mapping.test.d.ts +1 -0
  170. package/orm/tests/repository-cti-mapping.test.js +121 -0
  171. package/orm/tests/repository-cti-search.test.d.ts +1 -0
  172. package/orm/tests/repository-cti-search.test.js +152 -0
  173. package/orm/tests/repository-cti-soft-delete.test.d.ts +1 -0
  174. package/orm/tests/repository-cti-soft-delete.test.js +115 -0
  175. package/orm/tests/repository-cti-transactions.test.d.ts +1 -0
  176. package/orm/tests/repository-cti-transactions.test.js +126 -0
  177. package/orm/tests/repository-cti-upsert-many.test.d.ts +1 -0
  178. package/orm/tests/repository-cti-upsert-many.test.js +127 -0
  179. package/orm/tests/repository-cti.test.d.ts +1 -0
  180. package/orm/tests/repository-cti.test.js +456 -0
  181. package/orm/tests/repository-edge-cases.test.d.ts +1 -0
  182. package/orm/tests/repository-edge-cases.test.js +216 -0
  183. package/orm/tests/repository-expiration.test.d.ts +1 -0
  184. package/orm/tests/repository-expiration.test.js +153 -0
  185. package/orm/tests/repository-extra-coverage.test.d.ts +1 -0
  186. package/orm/tests/repository-extra-coverage.test.js +546 -0
  187. package/orm/tests/repository-mapping.test.d.ts +1 -0
  188. package/orm/tests/repository-mapping.test.js +71 -0
  189. package/orm/tests/repository-regression.test.d.ts +1 -0
  190. package/orm/tests/repository-regression.test.js +330 -0
  191. package/orm/tests/repository-search-coverage.test.d.ts +1 -0
  192. package/orm/tests/repository-search-coverage.test.js +129 -0
  193. package/orm/tests/repository-search.test.d.ts +1 -0
  194. package/orm/tests/repository-search.test.js +116 -0
  195. package/orm/tests/repository-soft-delete.test.d.ts +1 -0
  196. package/orm/tests/repository-soft-delete.test.js +143 -0
  197. package/orm/tests/repository-transactions-nested.test.d.ts +1 -0
  198. package/orm/tests/repository-transactions-nested.test.js +202 -0
  199. package/orm/tests/repository-types.test.d.ts +1 -0
  200. package/orm/tests/repository-types.test.js +218 -0
  201. package/orm/tests/schema-converter.test.d.ts +1 -0
  202. package/orm/tests/schema-converter.test.js +81 -0
  203. package/orm/tests/schema-generation.test.d.ts +1 -0
  204. package/orm/tests/schema-generation.test.js +127 -0
  205. package/orm/tests/sql-helpers.test.d.ts +1 -0
  206. package/orm/tests/sql-helpers.test.js +67 -0
  207. package/orm/tests/transaction-safety.test.d.ts +1 -0
  208. package/orm/tests/transaction-safety.test.js +81 -0
  209. package/orm/tests/transactional.test.d.ts +1 -0
  210. package/orm/tests/transactional.test.js +224 -0
  211. package/orm/tests/utils.test.d.ts +1 -0
  212. package/orm/tests/utils.test.js +70 -0
  213. package/orm/utils.d.ts +7 -0
  214. package/orm/utils.js +26 -6
  215. package/package.json +12 -7
  216. package/pool/pool.js +1 -1
  217. package/rate-limit/index.d.ts +2 -0
  218. package/rate-limit/index.js +2 -0
  219. package/rate-limit/postgres/drizzle/0000_watery_rage.sql +7 -0
  220. package/{queue → rate-limit}/postgres/drizzle/meta/0000_snapshot.json +14 -39
  221. package/rate-limit/postgres/drizzle/meta/_journal.json +13 -0
  222. package/{queue → rate-limit}/postgres/drizzle.config.js +1 -1
  223. package/rate-limit/postgres/index.d.ts +4 -0
  224. package/rate-limit/postgres/index.js +4 -0
  225. package/rate-limit/postgres/module.d.ts +12 -0
  226. package/rate-limit/postgres/module.js +28 -0
  227. package/rate-limit/postgres/postgres-rate-limiter.d.ts +9 -0
  228. package/rate-limit/postgres/postgres-rate-limiter.js +56 -0
  229. package/rate-limit/postgres/rate-limit.model.d.ts +8 -0
  230. package/rate-limit/postgres/rate-limit.model.js +35 -0
  231. package/rate-limit/postgres/rate-limiter.provider.d.ts +6 -0
  232. package/rate-limit/postgres/rate-limiter.provider.js +21 -0
  233. package/rate-limit/postgres/schemas.d.ts +3 -0
  234. package/rate-limit/postgres/schemas.js +4 -0
  235. package/rate-limit/provider.d.ts +9 -0
  236. package/rate-limit/provider.js +2 -0
  237. package/rate-limit/rate-limiter.d.ts +35 -0
  238. package/rate-limit/rate-limiter.js +3 -0
  239. package/rate-limit/tests/postgres-rate-limiter.test.d.ts +1 -0
  240. package/rate-limit/tests/postgres-rate-limiter.test.js +92 -0
  241. package/signals/implementation/configure.d.ts +3 -0
  242. package/signals/implementation/configure.js +3 -0
  243. package/sse/data-stream-source.d.ts +1 -1
  244. package/sse/data-stream-source.js +6 -6
  245. package/task-queue/enqueue-batch.d.ts +17 -0
  246. package/task-queue/enqueue-batch.js +24 -0
  247. package/{queue → task-queue}/index.d.ts +1 -1
  248. package/{queue → task-queue}/index.js +1 -1
  249. package/task-queue/postgres/drizzle/0000_thin_black_panther.sql +74 -0
  250. package/task-queue/postgres/drizzle/meta/0000_snapshot.json +592 -0
  251. package/task-queue/postgres/drizzle/meta/_journal.json +13 -0
  252. package/task-queue/postgres/drizzle.config.d.ts +2 -0
  253. package/task-queue/postgres/drizzle.config.js +11 -0
  254. package/task-queue/postgres/index.d.ts +4 -0
  255. package/task-queue/postgres/index.js +4 -0
  256. package/task-queue/postgres/module.d.ts +12 -0
  257. package/task-queue/postgres/module.js +28 -0
  258. package/task-queue/postgres/schemas.d.ts +16 -0
  259. package/task-queue/postgres/schemas.js +8 -0
  260. package/task-queue/postgres/task-queue.d.ts +83 -0
  261. package/task-queue/postgres/task-queue.js +1054 -0
  262. package/task-queue/postgres/task-queue.provider.d.ts +7 -0
  263. package/{queue/postgres/queue.provider.js → task-queue/postgres/task-queue.provider.js} +8 -8
  264. package/task-queue/postgres/task.model.d.ts +39 -0
  265. package/task-queue/postgres/task.model.js +178 -0
  266. package/{queue → task-queue}/provider.d.ts +3 -3
  267. package/task-queue/provider.js +2 -0
  268. package/{queue → task-queue}/task-context.d.ts +7 -7
  269. package/{queue → task-queue}/task-context.js +8 -8
  270. package/{queue/queue.d.ts → task-queue/task-queue.d.ts} +128 -59
  271. package/task-queue/task-queue.js +200 -0
  272. package/task-queue/tests/complex.test.d.ts +1 -0
  273. package/task-queue/tests/complex.test.js +299 -0
  274. package/task-queue/tests/dependencies.test.d.ts +1 -0
  275. package/task-queue/tests/dependencies.test.js +174 -0
  276. package/task-queue/tests/queue.test.d.ts +1 -0
  277. package/task-queue/tests/queue.test.js +334 -0
  278. package/task-queue/tests/worker.test.d.ts +1 -0
  279. package/task-queue/tests/worker.test.js +163 -0
  280. package/test1.js +1 -1
  281. package/test4.js +2 -2
  282. package/unit-test/index.d.ts +1 -0
  283. package/unit-test/index.js +1 -0
  284. package/unit-test/integration-setup.d.ts +55 -0
  285. package/unit-test/integration-setup.js +182 -0
  286. package/utils/patterns.d.ts +3 -0
  287. package/utils/patterns.js +6 -1
  288. package/audit/drizzle/0001_previous_network.sql +0 -2
  289. package/audit/drizzle/meta/0001_snapshot.json +0 -195
  290. package/queue/enqueue-batch.d.ts +0 -17
  291. package/queue/enqueue-batch.js +0 -18
  292. package/queue/postgres/drizzle/0000_zippy_moondragon.sql +0 -11
  293. package/queue/postgres/drizzle/0001_certain_wild_pack.sql +0 -2
  294. package/queue/postgres/drizzle/0002_dear_meggan.sql +0 -2
  295. package/queue/postgres/drizzle/0003_tricky_venom.sql +0 -30
  296. package/queue/postgres/drizzle/meta/0001_snapshot.json +0 -103
  297. package/queue/postgres/drizzle/meta/0002_snapshot.json +0 -90
  298. package/queue/postgres/drizzle/meta/0003_snapshot.json +0 -288
  299. package/queue/postgres/drizzle/meta/_journal.json +0 -34
  300. package/queue/postgres/index.d.ts +0 -4
  301. package/queue/postgres/index.js +0 -4
  302. package/queue/postgres/module.d.ts +0 -9
  303. package/queue/postgres/module.js +0 -29
  304. package/queue/postgres/queue.d.ts +0 -60
  305. package/queue/postgres/queue.js +0 -681
  306. package/queue/postgres/queue.provider.d.ts +0 -7
  307. package/queue/postgres/schemas.d.ts +0 -14
  308. package/queue/postgres/schemas.js +0 -6
  309. package/queue/postgres/task.model.d.ts +0 -24
  310. package/queue/postgres/task.model.js +0 -115
  311. package/queue/provider.js +0 -2
  312. package/queue/queue.js +0 -131
  313. package/queue/tests/queue.test.js +0 -623
  314. package/test3.d.ts +0 -1
  315. package/test3.js +0 -47
  316. /package/{queue/tests/queue.test.d.ts → api/server/tests/csrf.middleware.test.d.ts} +0 -0
  317. /package/circuit-breaker/postgres/drizzle/{0000_hard_shocker.sql → 0000_cooing_korath.sql} +0 -0
  318. /package/{queue → rate-limit}/postgres/drizzle.config.d.ts +0 -0
@@ -7,7 +7,9 @@ export type TransactionConfig = PgTransactionConfig;
7
7
  export declare abstract class Transaction implements AsyncDisposable {
8
8
  #private;
9
9
  readonly afterCommit: import("../../utils/async-hook/index.js").AsyncHook<never, never, unknown>;
10
+ readonly parent?: Transaction;
10
11
  manualCommit: boolean;
12
+ constructor(parent?: Transaction);
11
13
  [Symbol.asyncDispose](): Promise<void>;
12
14
  withManualCommit(): void;
13
15
  /**
@@ -23,8 +25,8 @@ export declare abstract class Transaction implements AsyncDisposable {
23
25
  export declare class DrizzleTransaction extends Transaction {
24
26
  #private;
25
27
  readonly pgTransaction: PgTransaction;
26
- constructor(pgTransaction: PgTransaction);
27
- static create(session: Database | PgTransaction, config?: TransactionConfig): Promise<DrizzleTransaction>;
28
+ constructor(pgTransaction: PgTransaction, parent?: Transaction);
29
+ static create(session: Database | PgTransaction, config?: TransactionConfig, parent?: Transaction): Promise<DrizzleTransaction>;
28
30
  protected _commit(): Promise<void>;
29
31
  protected _rollback(): Promise<void>;
30
32
  private setTransactionResultPromise;
@@ -6,7 +6,11 @@ export class Transaction {
6
6
  #useCounter = 0;
7
7
  #done = false;
8
8
  afterCommit = asyncHook();
9
+ parent;
9
10
  manualCommit = false;
11
+ constructor(parent) {
12
+ this.parent = parent;
13
+ }
10
14
  async [Symbol.asyncDispose]() {
11
15
  if (!this.#done) {
12
16
  await this.rollback();
@@ -49,7 +53,12 @@ export class Transaction {
49
53
  }
50
54
  this.#done = true;
51
55
  await this._commit();
52
- await this.afterCommit.trigger();
56
+ if (this.parent) {
57
+ this.parent.afterCommit.register(async () => await this.afterCommit.trigger());
58
+ }
59
+ else {
60
+ await this.afterCommit.trigger();
61
+ }
53
62
  }
54
63
  async rollback() {
55
64
  if (this.#done) {
@@ -63,14 +72,14 @@ export class DrizzleTransaction extends Transaction {
63
72
  #deferPromise = new DeferredPromise();
64
73
  pgTransaction;
65
74
  #pgTransactionResultPromise;
66
- constructor(pgTransaction) {
67
- super();
75
+ constructor(pgTransaction, parent) {
76
+ super(parent);
68
77
  this.pgTransaction = pgTransaction;
69
78
  }
70
- static async create(session, config) {
79
+ static async create(session, config, parent) {
71
80
  const instancePromise = new DeferredPromise();
72
81
  const pgTransactionResultPromise = session.transaction(async (tx) => {
73
- const transaction = new DrizzleTransaction(tx);
82
+ const transaction = new DrizzleTransaction(tx, parent);
74
83
  instancePromise.resolve(transaction);
75
84
  await transaction.#deferPromise;
76
85
  }, config);
@@ -1,4 +1,5 @@
1
1
  import { type InjectionToken, type ResolveArgument } from '../../injector/index.js';
2
+ import type { Type } from '../../types/index.js';
2
3
  import { Database } from './database.js';
3
4
  import { Transaction, type PgTransaction, type TransactionConfig } from './transaction.js';
4
5
  export type TransactionInitOptions = TransactionConfig & {
@@ -11,7 +12,7 @@ export type TransactionInitOptions = TransactionConfig & {
11
12
  export type TransactionHandler<R> = (transaction: Transaction) => Promise<R>;
12
13
  type TransactionalContext<ContextData = unknown> = {
13
14
  session: Database | PgTransaction;
14
- cache: WeakMap<Database | PgTransaction, any>;
15
+ instances: WeakMap<Type, WeakMap<Database | PgTransaction, any>>;
15
16
  data: ContextData;
16
17
  };
17
18
  declare const getCurrentTransactionalContext: {
@@ -22,7 +23,7 @@ declare const getCurrentTransactionalContext: {
22
23
  export { getCurrentTransactionalContext, isInTransactionalContext, runInTransactionalContext };
23
24
  export declare abstract class Transactional<ContextData = unknown> {
24
25
  #private;
25
- readonly session: PgTransaction | Database;
26
+ readonly session: Database | PgTransaction;
26
27
  readonly isInTransaction: boolean;
27
28
  constructor();
28
29
  /**
@@ -64,6 +65,9 @@ export declare abstract class Transactional<ContextData = unknown> {
64
65
  /**
65
66
  * Starts a new transaction, executes the provided handler function within it,
66
67
  * and automatically commits the transaction if the handler succeeds or rolls it back if it throws an error.
68
+ *
69
+ * If called within an existing transaction, it reuses the existing transaction unless specified otherwise in the config.
70
+ *
67
71
  * @template R The return type of the handler.
68
72
  * @param handler The function to execute within the transaction.
69
73
  * @param config Optional transaction configuration.
@@ -30,7 +30,15 @@ export class Transactional {
30
30
  #injector = inject(Injector);
31
31
  #classConstructor;
32
32
  #context = getCurrentTransactionalContext() ?? {};
33
- #instanceCache = this.#context.cache ?? new WeakMap();
33
+ #instances = this.#context.instances ?? new WeakMap();
34
+ get #instanceCache() {
35
+ let cache = this.#instances.get(this.#classConstructor);
36
+ if (isUndefined(cache)) {
37
+ cache = new WeakMap();
38
+ this.#instances.set(this.#classConstructor, cache);
39
+ }
40
+ return cache;
41
+ }
34
42
  session = this.#context.session ?? inject(Database);
35
43
  isInTransaction = this.session instanceof DrizzlePgTransaction;
36
44
  constructor() {
@@ -42,13 +50,14 @@ export class Transactional {
42
50
  * @returns A promise that resolves to the new Transaction instance.
43
51
  */
44
52
  async startTransaction(config) {
45
- if ((config?.useExisting != false) && this.isInTransaction) {
53
+ if ((config?.useExisting == true) && this.isInTransaction) {
46
54
  const existing = transactionCache.get(this.session);
47
55
  if (isDefined(existing)) {
48
56
  return existing;
49
57
  }
50
58
  }
51
- const transaction = await DrizzleTransaction.create(this.session, config);
59
+ const parentTransaction = tryGetTstdlTransaction(this.session);
60
+ const transaction = await DrizzleTransaction.create(this.session, config, parentTransaction);
52
61
  transactionCache.set(transaction.pgTransaction, transaction);
53
62
  return transaction;
54
63
  }
@@ -68,7 +77,7 @@ export class Transactional {
68
77
  }
69
78
  const context = {
70
79
  session: session,
71
- cache: this.#instanceCache,
80
+ instances: this.#instances,
72
81
  data: this.getTransactionalContextData(),
73
82
  };
74
83
  const repositoryWithSession = runInInjectionContext(this.#injector, () => runInTransactionalContext(context, () => new this.#classConstructor()));
@@ -108,11 +117,19 @@ export class Transactional {
108
117
  if (isUndefined(existingTransaction)) {
109
118
  return await this.transaction(handler);
110
119
  }
111
- return await existingTransaction.use(async () => await handler(existingTransaction));
120
+ const context = {
121
+ session: existingTransaction.pgTransaction,
122
+ instances: this.#instances,
123
+ data: this.getTransactionalContextData(),
124
+ };
125
+ return await existingTransaction.use(async () => await runInTransactionalContext(context, async () => await handler(existingTransaction)));
112
126
  }
113
127
  /**
114
128
  * Starts a new transaction, executes the provided handler function within it,
115
129
  * and automatically commits the transaction if the handler succeeds or rolls it back if it throws an error.
130
+ *
131
+ * If called within an existing transaction, it reuses the existing transaction unless specified otherwise in the config.
132
+ *
116
133
  * @template R The return type of the handler.
117
134
  * @param handler The function to execute within the transaction.
118
135
  * @param config Optional transaction configuration.
@@ -120,17 +137,30 @@ export class Transactional {
120
137
  */
121
138
  async transaction(handler, config) {
122
139
  const transaction = await this.startTransaction(config);
123
- return await transaction.use(async () => await handler(transaction));
140
+ const context = {
141
+ session: transaction.pgTransaction,
142
+ instances: this.#instances,
143
+ data: this.getTransactionalContextData(),
144
+ };
145
+ return await transaction.use(async () => await runInTransactionalContext(context, async () => await handler(transaction)));
124
146
  }
125
147
  getTransactionalContextData() {
126
- return transactionalContextDataGuard; // eslint-disable-line @typescript-eslint/no-unsafe-return
148
+ return transactionalContextDataGuard;
127
149
  }
128
150
  }
129
151
  export function tryGetTransactionalContextData(_instance) {
130
- return getCurrentTransactionalContext()?.data;
152
+ const data = getCurrentTransactionalContext()?.data;
153
+ if (data == transactionalContextDataGuard) {
154
+ return undefined;
155
+ }
156
+ return data;
131
157
  }
132
158
  export function getTransactionalContextData(_instance) {
133
- return getCurrentTransactionalContext(true, getTransactionalContextData).data;
159
+ const data = getCurrentTransactionalContext(true, getTransactionalContextData).data;
160
+ if (data == transactionalContextDataGuard) {
161
+ throw new Error('function getTransactionalContextData must be implemented to use transactional context data.');
162
+ }
163
+ return data;
134
164
  }
135
165
  export function tryGetTstdlTransaction(transactionOrSession) {
136
166
  if (isUndefined(transactionOrSession)) {
@@ -14,6 +14,8 @@ export type ColumnDefinition = {
14
14
  name: string;
15
15
  objectPath: JsonPath;
16
16
  reflectionData: OrmColumnReflectionData | undefined;
17
+ inherited: boolean;
18
+ table: PgTableFromType | ExtraConfigColumnsFromType;
17
19
  buildType: (options: BuildTypeOptions) => PgColumnBuilder<any, any, any, any>;
18
20
  dereferenceObjectPath: (obj: Record) => any;
19
21
  toDatabase: (value: unknown, context: TransformContext) => any;
@@ -0,0 +1,25 @@
1
+ import { type SQL, type SQLWrapper } from 'drizzle-orm';
2
+ export declare class CaseBuilder<TReturn = never> {
3
+ readonly cases: SQL[];
4
+ caseExpression?: SQL | SQLWrapper;
5
+ constructor(expression?: SQL | SQLWrapper);
6
+ /** Adds a WHEN clause. */
7
+ when<TValue>(pattern: SQL | SQLWrapper | string | number | boolean | undefined, result: SQL<TValue> | TValue | SQLWrapper): CaseBuilder<TReturn | TValue>;
8
+ /**
9
+ * Adds an ELSE clause and finishes the statement.
10
+ * If no WHEN clauses were added, it returns the value directly.
11
+ */
12
+ else<TElse>(value: TElse | SQL | SQLWrapper): SQL<TReturn | TElse>;
13
+ /**
14
+ * Finishes the statement without an ELSE (defaults to NULL).
15
+ * If no WHEN clauses were added, it returns NULL directly.
16
+ */
17
+ end(): SQL<TReturn | null>;
18
+ private finalize;
19
+ }
20
+ /**
21
+ * Creates a "Searched Case" builder.
22
+ * Syntax: CASE WHEN condition THEN result ...
23
+ */
24
+ export declare function caseWhen(caseExpression: SQL | SQLWrapper): CaseBuilder;
25
+ export declare function caseWhen<TValue>(condition: SQL | SQLWrapper | string | number | boolean | undefined, value: SQL<TValue> | TValue | SQLWrapper): CaseBuilder<TValue>;
@@ -0,0 +1,54 @@
1
+ import { isDefined } from '../../utils/type-guards.js';
2
+ import { sql } from 'drizzle-orm';
3
+ export class CaseBuilder {
4
+ cases = [];
5
+ caseExpression;
6
+ constructor(expression) {
7
+ this.caseExpression = expression;
8
+ }
9
+ /** Adds a WHEN clause. */
10
+ when(pattern, result) {
11
+ if (isDefined(pattern)) {
12
+ this.cases.push(sql `WHEN ${pattern} THEN ${result}`);
13
+ }
14
+ return this;
15
+ }
16
+ /**
17
+ * Adds an ELSE clause and finishes the statement.
18
+ * If no WHEN clauses were added, it returns the value directly.
19
+ */
20
+ else(value) {
21
+ if (this.cases.length == 0) {
22
+ return sql `${value}`;
23
+ }
24
+ return this.finalize(value);
25
+ }
26
+ /**
27
+ * Finishes the statement without an ELSE (defaults to NULL).
28
+ * If no WHEN clauses were added, it returns NULL directly.
29
+ */
30
+ end() {
31
+ if (this.cases.length == 0) {
32
+ return sql `NULL`;
33
+ }
34
+ return this.finalize();
35
+ }
36
+ finalize(elseValue) {
37
+ const chunks = [sql `CASE`];
38
+ if (isDefined(this.caseExpression)) {
39
+ chunks.push(sql `${this.caseExpression}`);
40
+ }
41
+ chunks.push(sql.join(this.cases, sql ` `));
42
+ const endChunk = isDefined(elseValue)
43
+ ? sql `ELSE ${elseValue} END`
44
+ : sql `END`;
45
+ chunks.push(endChunk);
46
+ return sql.join(chunks, sql ` `);
47
+ }
48
+ }
49
+ export function caseWhen(condition, value) {
50
+ if (isDefined(value)) {
51
+ return new CaseBuilder().when(condition, value);
52
+ }
53
+ return new CaseBuilder(condition);
54
+ }
@@ -0,0 +1,2 @@
1
+ export * from './sqls.js';
2
+ export * from './case-when.js';
@@ -0,0 +1,2 @@
1
+ export * from './sqls.js';
2
+ export * from './case-when.js';
@@ -6,18 +6,14 @@
6
6
  */
7
7
  import { Column, type AnyColumn, type SQL, type SQLChunk } from 'drizzle-orm';
8
8
  import type { GetSelectTableSelection, SelectResultField, TableLike } from 'drizzle-orm/query-builders/select.types';
9
- import type { EnumerationObject, EnumerationValue } from '../types/types.js';
10
- import { type PgEnumFromEnumeration } from './enums.js';
11
- import type { TsVectorWeight } from './query/index.js';
12
- import type { Uuid } from './types.js';
13
- /** Drizzle SQL helper for getting the current transaction's timestamp. Returns a Date object. */
14
- export declare const TRANSACTION_TIMESTAMP: SQL<Date>;
15
- /** Drizzle SQL helper for generating a random UUID (v4). Returns a Uuid string. */
16
- export declare const RANDOM_UUID_V4: SQL<Uuid>;
17
- /** Drizzle SQL helper for generating a random UUID (v7). Returns a Uuid string. */
18
- export declare const RANDOM_UUID_V7: SQL<Uuid>;
9
+ import type { EnumerationObject, EnumerationValue, Record } from '../../types/types.js';
10
+ import { type PgEnumFromEnumeration } from '../enums.js';
11
+ import type { TsVectorWeight } from '../query/index.js';
12
+ import type { Uuid } from '../types.js';
19
13
  /** Represents valid units for PostgreSQL interval values. */
20
14
  export type IntervalUnit = 'millennium' | 'millenniums' | 'millennia' | 'century' | 'centuries' | 'decade' | 'decades' | 'year' | 'years' | 'day' | 'days' | 'hour' | 'hours' | 'minute' | 'minutes' | 'second' | 'seconds' | 'millisecond' | 'milliseconds' | 'microsecond' | 'microseconds';
15
+ export type ExclusiveColumnCondition = Column | boolean | SQL;
16
+ export declare const simpleJsonKeyPattern: RegExp;
21
17
  export type TsHeadlineOptions = {
22
18
  /**
23
19
  * The longest headline to output.
@@ -60,14 +56,41 @@ export type TsHeadlineOptions = {
60
56
  */
61
57
  fragmentDelimiter?: string;
62
58
  };
59
+ /** Drizzle SQL helper for getting the current transaction's timestamp. Returns a Date object. */
60
+ export declare const TRANSACTION_TIMESTAMP: SQL<Date>;
61
+ /** Drizzle SQL helper for generating a random UUID (v4). Returns a Uuid string. */
62
+ export declare const RANDOM_UUID_V4: SQL<Uuid>;
63
+ /** Drizzle SQL helper for generating a random UUID (v7). Returns a Uuid string. */
64
+ export declare const RANDOM_UUID_V7: SQL<Uuid>;
65
+ export declare const SQL_TRUE: SQL<boolean>;
66
+ export declare const SQL_FALSE: SQL<boolean>;
63
67
  export declare function enumValue<T extends EnumerationObject>(enumeration: T, dbEnum: PgEnumFromEnumeration<T> | string | null, value: EnumerationValue<T>): SQL<string>;
64
68
  /**
65
- * Generates a SQL condition that ensures exactly one of the specified columns is non-null and optional custom conditions.
66
- * @param enumeration The enumeration object
67
- * @param discriminator The column used to discriminate between different enumeration values
68
- * @param conditionMapping An object mapping enumeration values to columns and conditions
69
+ * Generates a SQL `CASE` expression to enforce strict, mutually exclusive column usage based on a discriminator value.
70
+ *
71
+ * This function is useful for "Table per Hierarchy" inheritance patterns or polymorphic associations where specific columns should only be present when a discriminator holds a specific value.
72
+ * Particularly in conjunction with check constraints, it ensures data integrity by enforcing that only the relevant columns for a given discriminator value are populated.
73
+ *
74
+ * The logic ensures that for a specific enum value:
75
+ * 1. Columns explicitly mapped to that value are enforced as **IS NOT NULL**.
76
+ * 2. Columns mapped to *other* enum values (but not the current one) are enforced as **IS NULL**.
77
+ * 3. Any custom SQL conditions provided are combined via `AND`.
78
+ *
79
+ * @remarks
80
+ * The function collects all columns defined across the entire `conditionMapping`. If a column appears anywhere in the mapping but is not associated with the current discriminator value being evaluated, it is automatically asserted as `NULL`.
81
+ *
82
+ * @param enumeration - The source enumeration object containing the valid discriminator values.
83
+ * @param discriminator - The database column acting as the type discriminator.
84
+ * @param conditionMapping - A configuration object where keys are enum values and values are:
85
+ * - A `Column` (enforced as NOT NULL).
86
+ * - A `SQL` condition (passed through).
87
+ * - A boolean (converted to SQL TRUE/FALSE).
88
+ * - An array containing a mix of the above.
89
+ * - `null` (implies this enum value is invalid or should result in `FALSE`).
90
+ *
91
+ * @returns A SQL object representing the complete `CASE discriminator WHEN ... THEN ... ELSE FALSE` statement.
69
92
  */
70
- export declare function exclusiveReference<T extends EnumerationObject>(enumeration: T, discriminator: Column, conditionMapping: Record<EnumerationValue<T>, Column | [Column, condition: boolean | SQL] | null>): SQL;
93
+ export declare function exclusiveColumn<T extends EnumerationObject>(enumeration: T, discriminator: Column, conditionMapping: Record<EnumerationValue<T>, ExclusiveColumnCondition | [ExclusiveColumnCondition, ...ExclusiveColumnCondition[]] | null>): SQL;
71
94
  export declare function exclusiveNotNull(...columns: Column[]): SQL;
72
95
  /**
73
96
  * Generates a SQL `CASE ... WHEN ... END` statement for dynamic condition mapping based on an enumeration.
@@ -92,8 +115,7 @@ export declare function exclusiveNotNull(...columns: Column[]): SQL;
92
115
  * By default, it generates an `IS NOT NULL` check.
93
116
  */
94
117
  export declare function enumerationCaseWhen<T extends EnumerationObject>(enumeration: T, discriminator: Column, conditionMapping: Record<EnumerationValue<T>, Column | [Column, ...Column[]] | boolean | SQL>, defaultColumnCondition?: (column: [Column, ...Column[]]) => SQL<unknown> | undefined): SQL;
95
- export declare function array<T>(values: SQL<T>[]): SQL<T[]>;
96
- export declare function array<T = unknown>(values: SQLChunk[]): SQL<T[]>;
118
+ export declare function array<T>(values: readonly (SQL<T> | SQLChunk | T)[]): SQL<T[]>;
97
119
  export declare function autoAlias<T>(column: AnyColumn<{
98
120
  data: T;
99
121
  }>): SQL.Aliased<T>;
@@ -161,7 +183,28 @@ export declare function greatest<T extends (Column | SQL | number)[]>(...values:
161
183
  [P in keyof T]: T[P] extends number ? Exclude<T[P], number> | SQL<number> : T[P];
162
184
  }[number]>>;
163
185
  export declare function greatest<T>(...values: T[]): SQL<SelectResultField<T>>;
164
- export declare function unnest<T>(array: SQL<readonly T[]>): SQL<T>;
186
+ export declare function unnest<T>(array: SQL<readonly T[]> | SQL.Aliased<readonly T[]> | Column): SQL<T>;
187
+ /**
188
+ * Creates a PostgreSQL array contains operator expression (@>).
189
+ * @param left - The array column or expression.
190
+ * @param right - The array value or expression to check for containment.
191
+ * @returns A Drizzle SQL object representing the array contains operation.
192
+ */
193
+ export declare function arrayContains<T>(left: Column | SQL<readonly T[]>, right: readonly T[] | Column | SQL<readonly T[]>): SQL<boolean>;
194
+ /**
195
+ * Creates a PostgreSQL array is contained by operator expression (<@).
196
+ * @param left - The array value or expression to check.
197
+ * @param right - The array column or expression to check against.
198
+ * @returns A Drizzle SQL object representing the array is contained by operation.
199
+ */
200
+ export declare function arrayIsContainedBy<T>(left: readonly T[] | Column | SQL<readonly T[]>, right: Column | SQL<readonly T[]>): SQL<boolean>;
201
+ /**
202
+ * Creates a PostgreSQL array overlaps operator expression (&&).
203
+ * @param left - The first array column or expression.
204
+ * @param right - The second array value or expression.
205
+ * @returns A Drizzle SQL object representing the array overlaps operation.
206
+ */
207
+ export declare function arrayOverlaps<T>(left: Column | SQL<readonly T[]>, right: readonly T[] | Column | SQL<readonly T[]>): SQL<boolean>;
165
208
  export declare function toTsVector(language: string | SQLChunk, text: string | SQLChunk): SQL<string>;
166
209
  export declare function tsvectorToArray(tsvector: SQL): SQL<string[]>;
167
210
  /**
@@ -261,4 +304,9 @@ export declare function isStrictWordSimilar(left: string | SQLChunk, right: stri
261
304
  export declare function distance(left: string | SQLChunk, right: string | SQLChunk): SQL<number>;
262
305
  export declare function wordDistance(left: string | SQLChunk, right: string | SQLChunk): SQL<number>;
263
306
  export declare function strictWordDistance(left: string | SQLChunk, right: string | SQLChunk): SQL<number>;
264
- export declare function jsonbBuildObject(properties: Record<string, any> | [string, any][]): SQL;
307
+ export declare function jsonbBuildObject(properties: Record<string, unknown> | readonly [string, unknown][]): SQL;
308
+ /**
309
+ * A recursive utility to build PostgreSQL JSONB from TS structures.
310
+ * Supports: Nested Objects, Arrays, Drizzle Columns, and Raw SQL.
311
+ */
312
+ export declare function buildJsonb(value: any): SQL;
@@ -4,17 +4,22 @@
4
4
  * simplifying common SQL operations like generating UUIDs, working with intervals,
5
5
  * and aggregating data.
6
6
  */
7
- import { and, Column, eq, sql, isNotNull as sqlIsNotNull, Table } from 'drizzle-orm';
7
+ import { and, Column, eq, isSQLWrapper, sql, isNotNull as sqlIsNotNull, isNull as sqlIsNull, Table } from 'drizzle-orm';
8
8
  import { match, P } from 'ts-pattern';
9
- import { mapObjectValues, objectEntries, objectValues } from '../utils/object/object.js';
10
- import { assertDefined, isArray, isDefined, isInstanceOf, isNotNull, isNull, isNumber, isString } from '../utils/type-guards.js';
11
- import { getEnumName } from './enums.js';
9
+ import { distinct, toArray } from '../../utils/array/array.js';
10
+ import { objectEntries, objectValues } from '../../utils/object/object.js';
11
+ import { assertDefined, isArray, isBoolean, isDefined, isInstanceOf, isNotNull, isNull, isNumber, isObject, isString } from '../../utils/type-guards.js';
12
+ import { getEnumName } from '../enums.js';
13
+ import { caseWhen } from './case-when.js';
14
+ export const simpleJsonKeyPattern = /^[a-zA-Z0-9_-]+$/u;
12
15
  /** Drizzle SQL helper for getting the current transaction's timestamp. Returns a Date object. */
13
16
  export const TRANSACTION_TIMESTAMP = sql `transaction_timestamp()`;
14
17
  /** Drizzle SQL helper for generating a random UUID (v4). Returns a Uuid string. */
15
18
  export const RANDOM_UUID_V4 = sql `gen_random_uuid()`;
16
19
  /** Drizzle SQL helper for generating a random UUID (v7). Returns a Uuid string. */
17
20
  export const RANDOM_UUID_V7 = sql `uuidv7()`;
21
+ export const SQL_TRUE = sql `TRUE`;
22
+ export const SQL_FALSE = sql `FALSE`;
18
23
  export function enumValue(enumeration, dbEnum, value) {
19
24
  if (isNull(dbEnum)) {
20
25
  const enumName = getEnumName(enumeration);
@@ -25,23 +30,50 @@ export function enumValue(enumeration, dbEnum, value) {
25
30
  return sql `'${sql.raw(String(value))}'::${enumType}`;
26
31
  }
27
32
  /**
28
- * Generates a SQL condition that ensures exactly one of the specified columns is non-null and optional custom conditions.
29
- * @param enumeration The enumeration object
30
- * @param discriminator The column used to discriminate between different enumeration values
31
- * @param conditionMapping An object mapping enumeration values to columns and conditions
33
+ * Generates a SQL `CASE` expression to enforce strict, mutually exclusive column usage based on a discriminator value.
34
+ *
35
+ * This function is useful for "Table per Hierarchy" inheritance patterns or polymorphic associations where specific columns should only be present when a discriminator holds a specific value.
36
+ * Particularly in conjunction with check constraints, it ensures data integrity by enforcing that only the relevant columns for a given discriminator value are populated.
37
+ *
38
+ * The logic ensures that for a specific enum value:
39
+ * 1. Columns explicitly mapped to that value are enforced as **IS NOT NULL**.
40
+ * 2. Columns mapped to *other* enum values (but not the current one) are enforced as **IS NULL**.
41
+ * 3. Any custom SQL conditions provided are combined via `AND`.
42
+ *
43
+ * @remarks
44
+ * The function collects all columns defined across the entire `conditionMapping`. If a column appears anywhere in the mapping but is not associated with the current discriminator value being evaluated, it is automatically asserted as `NULL`.
45
+ *
46
+ * @param enumeration - The source enumeration object containing the valid discriminator values.
47
+ * @param discriminator - The database column acting as the type discriminator.
48
+ * @param conditionMapping - A configuration object where keys are enum values and values are:
49
+ * - A `Column` (enforced as NOT NULL).
50
+ * - A `SQL` condition (passed through).
51
+ * - A boolean (converted to SQL TRUE/FALSE).
52
+ * - An array containing a mix of the above.
53
+ * - `null` (implies this enum value is invalid or should result in `FALSE`).
54
+ *
55
+ * @returns A SQL object representing the complete `CASE discriminator WHEN ... THEN ... ELSE FALSE` statement.
32
56
  */
33
- export function exclusiveReference(enumeration, discriminator, conditionMapping) {
34
- const columns = objectValues(conditionMapping).filter(isNotNull).map((value) => isInstanceOf(value, Column) ? value : value[0]);
35
- const mapping = mapObjectValues(conditionMapping, (value) => {
36
- if (isInstanceOf(value, Column)) {
37
- return value;
38
- }
57
+ export function exclusiveColumn(enumeration, discriminator, conditionMapping) {
58
+ const allColumns = objectValues(conditionMapping)
59
+ .filter(isNotNull)
60
+ .flatMap((value) => toArray(value).filter((value) => isInstanceOf(value, Column)));
61
+ const participatingColumns = distinct(allColumns);
62
+ const mapping = objectEntries(conditionMapping).map(([key, value]) => {
39
63
  if (isNull(value)) {
40
- return sql `FALSE`;
64
+ return [key, SQL_FALSE];
41
65
  }
42
- return value[1];
66
+ const requiredColumns = toArray(value).filter((value) => isInstanceOf(value, Column));
67
+ const nullColumns = participatingColumns.filter((column) => !requiredColumns.includes(column));
68
+ const customConditions = toArray(value).filter((val) => !isInstanceOf(val, Column)).map((condition) => isBoolean(condition) ? (condition ? SQL_TRUE : SQL_FALSE) : condition);
69
+ const condition = and(...requiredColumns.map((col) => sqlIsNotNull(col)), ...nullColumns.map((col) => sqlIsNull(col)), ...customConditions);
70
+ return [key, condition];
43
71
  });
44
- return and(exclusiveNotNull(...columns), enumerationCaseWhen(enumeration, discriminator, mapping));
72
+ const kaseWhen = caseWhen(discriminator);
73
+ for (const [key, condition] of mapping) {
74
+ kaseWhen.when(enumValue(enumeration, null, key), condition);
75
+ }
76
+ return kaseWhen.else(SQL_FALSE);
45
77
  }
46
78
  export function exclusiveNotNull(...columns) {
47
79
  return eq(numNonNulls(...columns), sql.raw('1'));
@@ -72,7 +104,7 @@ export function enumerationCaseWhen(enumeration, discriminator, conditionMapping
72
104
  const whens = [];
73
105
  for (const [key, value] of objectEntries(conditionMapping)) {
74
106
  const condition = match(value)
75
- .with(P.boolean, (bool) => bool ? sql `TRUE` : sql `FALSE`)
107
+ .with(P.boolean, (bool) => bool ? SQL_TRUE : SQL_FALSE)
76
108
  .when((value) => isInstanceOf(value, Column), (col) => defaultColumnCondition(col))
77
109
  .otherwise((rawSql) => rawSql);
78
110
  whens.push(sql ` WHEN ${enumValue(enumeration, null, key)} THEN ${condition}`);
@@ -84,7 +116,8 @@ ${sql.join(whens, sql `\n`)}
84
116
  END`;
85
117
  }
86
118
  export function array(values) {
87
- const valueString = sql.join(values, sql.raw(', '));
119
+ const chunks = values.map((value) => isSQLWrapper(value) ? value : sql `${value}`);
120
+ const valueString = sql.join(chunks, sql.raw(', '));
88
121
  return sql `ARRAY[${valueString}]`;
89
122
  }
90
123
  export function autoAlias(column) {
@@ -175,6 +208,36 @@ export function greatest(...values) {
175
208
  export function unnest(array) {
176
209
  return sql `unnest(${array})`;
177
210
  }
211
+ /**
212
+ * Creates a PostgreSQL array contains operator expression (@>).
213
+ * @param left - The array column or expression.
214
+ * @param right - The array value or expression to check for containment.
215
+ * @returns A Drizzle SQL object representing the array contains operation.
216
+ */
217
+ export function arrayContains(left, right) {
218
+ const rightSql = isArray(right) ? array(right) : right;
219
+ return sql `(${left} @> ${rightSql})`;
220
+ }
221
+ /**
222
+ * Creates a PostgreSQL array is contained by operator expression (<@).
223
+ * @param left - The array value or expression to check.
224
+ * @param right - The array column or expression to check against.
225
+ * @returns A Drizzle SQL object representing the array is contained by operation.
226
+ */
227
+ export function arrayIsContainedBy(left, right) {
228
+ const leftSql = isArray(left) ? array(left) : left;
229
+ return sql `(${leftSql} <@ ${right})`;
230
+ }
231
+ /**
232
+ * Creates a PostgreSQL array overlaps operator expression (&&).
233
+ * @param left - The first array column or expression.
234
+ * @param right - The second array value or expression.
235
+ * @returns A Drizzle SQL object representing the array overlaps operation.
236
+ */
237
+ export function arrayOverlaps(left, right) {
238
+ const rightSql = isArray(right) ? array(right) : right;
239
+ return sql `(${left} && ${rightSql})`;
240
+ }
178
241
  export function toTsVector(language, text) {
179
242
  return sql `to_tsvector(${language}, ${text})`;
180
243
  }
@@ -323,8 +386,39 @@ export function strictWordDistance(left, right) {
323
386
  }
324
387
  export function jsonbBuildObject(properties) {
325
388
  const entries = isArray(properties) ? properties : objectEntries(properties);
326
- const chunks = entries
327
- .filter(([_, propValue]) => isDefined(propValue))
328
- .map(([propKey, propValue]) => sql `'${sql.raw(propKey)}', ${propValue}`);
389
+ const chunks = [];
390
+ for (const [key, value] of entries) {
391
+ if (isDefined(value)) {
392
+ const isSimpleKey = simpleJsonKeyPattern.test(key);
393
+ const sqlKey = isSimpleKey ? sql.raw(`'${key}'`) : sql `${key}`;
394
+ chunks.push(sqlKey, buildJsonb(value));
395
+ }
396
+ }
397
+ if (chunks.length == 0) {
398
+ return sql `'{}'::jsonb`;
399
+ }
329
400
  return sql `jsonb_build_object(${sql.join(chunks, sql `, `)})`;
330
401
  }
402
+ /**
403
+ * A recursive utility to build PostgreSQL JSONB from TS structures.
404
+ * Supports: Nested Objects, Arrays, Drizzle Columns, and Raw SQL.
405
+ */
406
+ export function buildJsonb(value) {
407
+ if (isSQLWrapper(value)) {
408
+ return sql `${value}`;
409
+ }
410
+ if (isNull(value)) {
411
+ return sql `null`;
412
+ }
413
+ if (isArray(value)) {
414
+ if (value.length == 0) {
415
+ return sql `'[]'::jsonb`;
416
+ }
417
+ const elements = value.map((inner) => buildJsonb(inner));
418
+ return sql `jsonb_build_array(${sql.join(elements, sql `, `)})`;
419
+ }
420
+ if (isObject(value)) {
421
+ return jsonbBuildObject(value);
422
+ }
423
+ return sql `${value}`;
424
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { dateToNumericDate, numericDateToDateObject } from '../../utils/date-time.js';
3
+ describe('ORM Data Types - numericDate Logic', () => {
4
+ test('dateToNumericDate should return days since epoch', () => {
5
+ const date = new Date(Date.UTC(2024, 0, 1));
6
+ expect(dateToNumericDate(date)).toBe(19723);
7
+ });
8
+ test('numericDateToDateObject should convert days since epoch to date components', () => {
9
+ const { year, month, day } = numericDateToDateObject(19723);
10
+ expect(year).toBe(2024);
11
+ expect(month).toBe(1);
12
+ expect(day).toBe(1);
13
+ });
14
+ test('leap year handling (2024-02-29)', () => {
15
+ const leapDate = new Date(Date.UTC(2024, 1, 29));
16
+ const numeric = dateToNumericDate(leapDate);
17
+ expect(numeric).toBe(19782);
18
+ const { year, month, day } = numericDateToDateObject(19782);
19
+ expect(year).toBe(2024);
20
+ expect(month).toBe(2);
21
+ expect(day).toBe(29);
22
+ });
23
+ test('epoch start (1970-01-01)', () => {
24
+ const epoch = new Date(Date.UTC(1970, 0, 1));
25
+ expect(dateToNumericDate(epoch)).toBe(0);
26
+ const { year, month, day } = numericDateToDateObject(0);
27
+ expect(year).toBe(1970);
28
+ expect(month).toBe(1);
29
+ expect(day).toBe(1);
30
+ });
31
+ test('before epoch (1969-12-31)', () => {
32
+ const beforeEpoch = new Date(Date.UTC(1969, 11, 31));
33
+ expect(dateToNumericDate(beforeEpoch)).toBe(-1);
34
+ const { year, month, day } = numericDateToDateObject(-1);
35
+ expect(year).toBe(1969);
36
+ expect(month).toBe(12);
37
+ expect(day).toBe(31);
38
+ });
39
+ });