@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.
- package/ai/genkit/helpers.d.ts +3 -1
- package/ai/genkit/helpers.js +3 -3
- package/api/server/gateway.d.ts +3 -0
- package/api/server/gateway.js +15 -4
- package/api/server/middlewares/catch-error.middleware.js +2 -4
- package/api/server/middlewares/cors.middleware.js +2 -3
- package/api/server/middlewares/csrf.middleware.d.ts +41 -0
- package/api/server/middlewares/csrf.middleware.js +108 -0
- package/api/server/middlewares/index.d.ts +1 -0
- package/api/server/middlewares/index.js +1 -0
- package/api/server/module.d.ts +8 -2
- package/api/server/module.js +14 -8
- package/api/server/tests/csrf.middleware.test.js +91 -0
- package/audit/drizzle/{0000_bored_stick.sql → 0000_lumpy_thunderball.sql} +3 -3
- package/audit/drizzle/meta/0000_snapshot.json +4 -4
- package/audit/drizzle/meta/_journal.json +2 -9
- package/audit/module.d.ts +4 -1
- package/audit/module.js +3 -2
- package/audit/schemas.d.ts +1 -1
- package/audit/types.d.ts +1 -1
- package/audit/types.js +1 -1
- package/authentication/client/authentication.service.d.ts +14 -1
- package/authentication/client/authentication.service.js +82 -23
- package/authentication/client/http-client.middleware.d.ts +6 -0
- package/authentication/client/http-client.middleware.js +36 -0
- package/authentication/client/module.js +8 -2
- package/authentication/models/service-account.model.d.ts +2 -2
- package/authentication/models/service-account.model.js +10 -5
- package/authentication/models/subject.model.d.ts +19 -5
- package/authentication/models/subject.model.js +25 -29
- package/authentication/models/system-account.model.d.ts +3 -2
- package/authentication/models/system-account.model.js +11 -5
- package/authentication/models/user.model.d.ts +2 -11
- package/authentication/models/user.model.js +5 -16
- package/authentication/server/authentication-api-request-token.provider.d.ts +0 -2
- package/authentication/server/authentication-api-request-token.provider.js +3 -11
- package/authentication/server/authentication.api-controller.d.ts +1 -2
- package/authentication/server/authentication.api-controller.js +8 -9
- package/authentication/server/authentication.audit.d.ts +3 -2
- package/authentication/server/authentication.service.d.ts +27 -1
- package/authentication/server/authentication.service.js +67 -18
- package/authentication/server/drizzle/{0000_normal_paper_doll.sql → 0000_soft_tag.sql} +25 -32
- package/authentication/server/drizzle/meta/0000_snapshot.json +180 -205
- package/authentication/server/drizzle/meta/_journal.json +2 -2
- package/authentication/server/helper.js +9 -2
- package/authentication/server/module.d.ts +4 -1
- package/authentication/server/module.js +9 -5
- package/authentication/server/schemas.d.ts +2 -1
- package/authentication/server/schemas.js +2 -2
- package/authentication/server/subject.service.d.ts +14 -8
- package/authentication/server/subject.service.js +86 -84
- package/authentication/tests/authentication-ancillary.service.test.d.ts +1 -0
- package/authentication/tests/authentication-ancillary.service.test.js +13 -0
- package/authentication/tests/authentication-secret-requirements.validator.test.d.ts +1 -0
- package/authentication/tests/authentication-secret-requirements.validator.test.js +29 -0
- package/authentication/tests/authentication.api-controller.test.d.ts +1 -0
- package/authentication/tests/authentication.api-controller.test.js +88 -0
- package/authentication/tests/authentication.api-request-token.provider.test.d.ts +1 -0
- package/authentication/tests/authentication.api-request-token.provider.test.js +48 -0
- package/authentication/tests/authentication.client-middleware.test.d.ts +1 -0
- package/authentication/tests/authentication.client-middleware.test.js +23 -0
- package/authentication/tests/authentication.client-service.test.d.ts +1 -0
- package/authentication/tests/authentication.client-service.test.js +70 -0
- package/authentication/tests/authentication.service.test.d.ts +1 -0
- package/authentication/tests/authentication.service.test.js +186 -0
- package/authentication/tests/authentication.test-ancillary-service.d.ts +9 -0
- package/authentication/tests/authentication.test-ancillary-service.js +27 -0
- package/authentication/tests/helper.test.d.ts +1 -0
- package/authentication/tests/helper.test.js +107 -0
- package/authentication/tests/secret-requirements.error.test.d.ts +1 -0
- package/authentication/tests/secret-requirements.error.test.js +14 -0
- package/authentication/tests/subject.service.test.d.ts +1 -0
- package/authentication/tests/subject.service.test.js +140 -0
- package/circuit-breaker/postgres/drizzle/meta/0000_snapshot.json +1 -1
- package/circuit-breaker/postgres/drizzle/meta/_journal.json +2 -2
- package/circuit-breaker/postgres/module.d.ts +7 -1
- package/circuit-breaker/postgres/module.js +8 -6
- package/circuit-breaker/tests/circuit-breaker.test.js +2 -22
- package/document-management/api/document-management.api.js +2 -6
- package/document-management/server/services/document-validation.service.js +6 -5
- package/document-management/server/services/document-workflow.service.js +5 -5
- package/document-management/service-models/document-folders.view-model.d.ts +5 -2
- package/document-management/service-models/document-folders.view-model.js +42 -9
- package/document-management/service-models/enriched/enriched-document-management-data.view.js +1 -1
- package/examples/document-management/main.js +4 -4
- package/http/client/adapters/undici.adapter.d.ts +7 -5
- package/http/client/adapters/undici.adapter.js +13 -10
- package/http/client/module.d.ts +3 -1
- package/http/client/module.js +8 -9
- package/http/server/http-server.d.ts +2 -0
- package/http/server/node/module.d.ts +6 -2
- package/http/server/node/module.js +6 -4
- package/http/server/node/node-http-server.d.ts +2 -0
- package/http/server/node/node-http-server.js +7 -0
- package/http/types.d.ts +1 -1
- package/key-value-store/postgres/module.d.ts +7 -1
- package/key-value-store/postgres/module.js +7 -3
- package/lock/postgres/lock.js +0 -1
- package/lock/postgres/module.d.ts +7 -1
- package/lock/postgres/module.js +9 -5
- package/logger/formatter.d.ts +2 -0
- package/logger/formatters/json.js +2 -2
- package/logger/formatters/pretty-print.js +8 -10
- package/logger/logger.d.ts +1 -1
- package/logger/logger.js +15 -12
- package/message-bus/local/module.d.ts +5 -2
- package/message-bus/local/module.js +5 -4
- package/module/module.d.ts +2 -1
- package/module/module.js +3 -0
- package/module/modules/web-server.module.d.ts +11 -6
- package/module/modules/web-server.module.js +15 -10
- package/orm/decorators.d.ts +24 -1
- package/orm/decorators.js +40 -4
- package/orm/index.d.ts +1 -1
- package/orm/index.js +1 -1
- package/orm/query/base.d.ts +17 -17
- package/orm/query/base.js +1 -1
- package/orm/repository.types.d.ts +46 -2
- package/orm/schemas/tsvector.js +1 -1
- package/orm/server/drizzle/schema-converter.d.ts +3 -1
- package/orm/server/drizzle/schema-converter.js +120 -14
- package/orm/server/index.d.ts +1 -0
- package/orm/server/index.js +1 -0
- package/orm/server/module.d.ts +4 -2
- package/orm/server/module.js +6 -5
- package/orm/server/query-converter.d.ts +6 -3
- package/orm/server/query-converter.js +33 -21
- package/orm/server/repository-config.d.ts +8 -0
- package/orm/server/repository-config.js +8 -0
- package/orm/server/repository.d.ts +117 -43
- package/orm/server/repository.js +758 -254
- package/orm/server/transaction.d.ts +4 -2
- package/orm/server/transaction.js +14 -5
- package/orm/server/transactional.d.ts +6 -2
- package/orm/server/transactional.js +39 -9
- package/orm/server/types.d.ts +2 -0
- package/orm/sqls/case-when.d.ts +25 -0
- package/orm/sqls/case-when.js +54 -0
- package/orm/sqls/index.d.ts +2 -0
- package/orm/sqls/index.js +2 -0
- package/orm/{sqls.d.ts → sqls/sqls.d.ts} +67 -19
- package/orm/{sqls.js → sqls/sqls.js} +116 -22
- package/orm/tests/data-types.test.d.ts +1 -0
- package/orm/tests/data-types.test.js +39 -0
- package/orm/tests/decorators.test.d.ts +1 -0
- package/orm/tests/decorators.test.js +77 -0
- package/orm/tests/encryption.test.d.ts +1 -0
- package/orm/tests/encryption.test.js +34 -0
- package/orm/tests/query-complex.test.d.ts +1 -0
- package/orm/tests/query-complex.test.js +203 -0
- package/orm/tests/query-converter-complex.test.d.ts +1 -0
- package/orm/tests/query-converter-complex.test.js +126 -0
- package/orm/tests/query-converter.test.d.ts +1 -0
- package/orm/tests/query-converter.test.js +123 -0
- package/orm/tests/repository-advanced.test.d.ts +1 -0
- package/orm/tests/repository-advanced.test.js +232 -0
- package/orm/tests/repository-attributes.test.d.ts +1 -0
- package/orm/tests/repository-attributes.test.js +99 -0
- package/orm/tests/repository-comprehensive.test.d.ts +1 -0
- package/orm/tests/repository-comprehensive.test.js +187 -0
- package/orm/tests/repository-coverage.test.d.ts +1 -0
- package/orm/tests/repository-coverage.test.js +303 -0
- package/orm/tests/repository-cti-complex.test.d.ts +1 -0
- package/orm/tests/repository-cti-complex.test.js +170 -0
- package/orm/tests/repository-cti-embedded.test.d.ts +1 -0
- package/orm/tests/repository-cti-embedded.test.js +188 -0
- package/orm/tests/repository-cti-extensive.test.d.ts +1 -0
- package/orm/tests/repository-cti-extensive.test.js +308 -0
- package/orm/tests/repository-cti-mapping.test.d.ts +1 -0
- package/orm/tests/repository-cti-mapping.test.js +121 -0
- package/orm/tests/repository-cti-search.test.d.ts +1 -0
- package/orm/tests/repository-cti-search.test.js +152 -0
- package/orm/tests/repository-cti-soft-delete.test.d.ts +1 -0
- package/orm/tests/repository-cti-soft-delete.test.js +115 -0
- package/orm/tests/repository-cti-transactions.test.d.ts +1 -0
- package/orm/tests/repository-cti-transactions.test.js +126 -0
- package/orm/tests/repository-cti-upsert-many.test.d.ts +1 -0
- package/orm/tests/repository-cti-upsert-many.test.js +127 -0
- package/orm/tests/repository-cti.test.d.ts +1 -0
- package/orm/tests/repository-cti.test.js +456 -0
- package/orm/tests/repository-edge-cases.test.d.ts +1 -0
- package/orm/tests/repository-edge-cases.test.js +216 -0
- package/orm/tests/repository-expiration.test.d.ts +1 -0
- package/orm/tests/repository-expiration.test.js +153 -0
- package/orm/tests/repository-extra-coverage.test.d.ts +1 -0
- package/orm/tests/repository-extra-coverage.test.js +546 -0
- package/orm/tests/repository-mapping.test.d.ts +1 -0
- package/orm/tests/repository-mapping.test.js +71 -0
- package/orm/tests/repository-regression.test.d.ts +1 -0
- package/orm/tests/repository-regression.test.js +330 -0
- package/orm/tests/repository-search-coverage.test.d.ts +1 -0
- package/orm/tests/repository-search-coverage.test.js +129 -0
- package/orm/tests/repository-search.test.d.ts +1 -0
- package/orm/tests/repository-search.test.js +116 -0
- package/orm/tests/repository-soft-delete.test.d.ts +1 -0
- package/orm/tests/repository-soft-delete.test.js +143 -0
- package/orm/tests/repository-transactions-nested.test.d.ts +1 -0
- package/orm/tests/repository-transactions-nested.test.js +202 -0
- package/orm/tests/repository-types.test.d.ts +1 -0
- package/orm/tests/repository-types.test.js +218 -0
- package/orm/tests/schema-converter.test.d.ts +1 -0
- package/orm/tests/schema-converter.test.js +81 -0
- package/orm/tests/schema-generation.test.d.ts +1 -0
- package/orm/tests/schema-generation.test.js +127 -0
- package/orm/tests/sql-helpers.test.d.ts +1 -0
- package/orm/tests/sql-helpers.test.js +67 -0
- package/orm/tests/transaction-safety.test.d.ts +1 -0
- package/orm/tests/transaction-safety.test.js +81 -0
- package/orm/tests/transactional.test.d.ts +1 -0
- package/orm/tests/transactional.test.js +224 -0
- package/orm/tests/utils.test.d.ts +1 -0
- package/orm/tests/utils.test.js +70 -0
- package/orm/utils.d.ts +7 -0
- package/orm/utils.js +26 -6
- package/package.json +12 -7
- package/pool/pool.js +1 -1
- package/rate-limit/index.d.ts +2 -0
- package/rate-limit/index.js +2 -0
- package/rate-limit/postgres/drizzle/0000_watery_rage.sql +7 -0
- package/{queue → rate-limit}/postgres/drizzle/meta/0000_snapshot.json +14 -39
- package/rate-limit/postgres/drizzle/meta/_journal.json +13 -0
- package/{queue → rate-limit}/postgres/drizzle.config.js +1 -1
- package/rate-limit/postgres/index.d.ts +4 -0
- package/rate-limit/postgres/index.js +4 -0
- package/rate-limit/postgres/module.d.ts +12 -0
- package/rate-limit/postgres/module.js +28 -0
- package/rate-limit/postgres/postgres-rate-limiter.d.ts +9 -0
- package/rate-limit/postgres/postgres-rate-limiter.js +56 -0
- package/rate-limit/postgres/rate-limit.model.d.ts +8 -0
- package/rate-limit/postgres/rate-limit.model.js +35 -0
- package/rate-limit/postgres/rate-limiter.provider.d.ts +6 -0
- package/rate-limit/postgres/rate-limiter.provider.js +21 -0
- package/rate-limit/postgres/schemas.d.ts +3 -0
- package/rate-limit/postgres/schemas.js +4 -0
- package/rate-limit/provider.d.ts +9 -0
- package/rate-limit/provider.js +2 -0
- package/rate-limit/rate-limiter.d.ts +35 -0
- package/rate-limit/rate-limiter.js +3 -0
- package/rate-limit/tests/postgres-rate-limiter.test.d.ts +1 -0
- package/rate-limit/tests/postgres-rate-limiter.test.js +92 -0
- package/signals/implementation/configure.d.ts +3 -0
- package/signals/implementation/configure.js +3 -0
- package/sse/data-stream-source.d.ts +1 -1
- package/sse/data-stream-source.js +6 -6
- package/task-queue/enqueue-batch.d.ts +17 -0
- package/task-queue/enqueue-batch.js +24 -0
- package/{queue → task-queue}/index.d.ts +1 -1
- package/{queue → task-queue}/index.js +1 -1
- package/task-queue/postgres/drizzle/0000_thin_black_panther.sql +74 -0
- package/task-queue/postgres/drizzle/meta/0000_snapshot.json +592 -0
- package/task-queue/postgres/drizzle/meta/_journal.json +13 -0
- package/task-queue/postgres/drizzle.config.d.ts +2 -0
- package/task-queue/postgres/drizzle.config.js +11 -0
- package/task-queue/postgres/index.d.ts +4 -0
- package/task-queue/postgres/index.js +4 -0
- package/task-queue/postgres/module.d.ts +12 -0
- package/task-queue/postgres/module.js +28 -0
- package/task-queue/postgres/schemas.d.ts +16 -0
- package/task-queue/postgres/schemas.js +8 -0
- package/task-queue/postgres/task-queue.d.ts +83 -0
- package/task-queue/postgres/task-queue.js +1054 -0
- package/task-queue/postgres/task-queue.provider.d.ts +7 -0
- package/{queue/postgres/queue.provider.js → task-queue/postgres/task-queue.provider.js} +8 -8
- package/task-queue/postgres/task.model.d.ts +39 -0
- package/task-queue/postgres/task.model.js +178 -0
- package/{queue → task-queue}/provider.d.ts +3 -3
- package/task-queue/provider.js +2 -0
- package/{queue → task-queue}/task-context.d.ts +7 -7
- package/{queue → task-queue}/task-context.js +8 -8
- package/{queue/queue.d.ts → task-queue/task-queue.d.ts} +128 -59
- package/task-queue/task-queue.js +200 -0
- package/task-queue/tests/complex.test.d.ts +1 -0
- package/task-queue/tests/complex.test.js +299 -0
- package/task-queue/tests/dependencies.test.d.ts +1 -0
- package/task-queue/tests/dependencies.test.js +174 -0
- package/task-queue/tests/queue.test.d.ts +1 -0
- package/task-queue/tests/queue.test.js +334 -0
- package/task-queue/tests/worker.test.d.ts +1 -0
- package/task-queue/tests/worker.test.js +163 -0
- package/test1.js +1 -1
- package/test4.js +2 -2
- package/unit-test/index.d.ts +1 -0
- package/unit-test/index.js +1 -0
- package/unit-test/integration-setup.d.ts +55 -0
- package/unit-test/integration-setup.js +182 -0
- package/utils/patterns.d.ts +3 -0
- package/utils/patterns.js +6 -1
- package/audit/drizzle/0001_previous_network.sql +0 -2
- package/audit/drizzle/meta/0001_snapshot.json +0 -195
- package/queue/enqueue-batch.d.ts +0 -17
- package/queue/enqueue-batch.js +0 -18
- package/queue/postgres/drizzle/0000_zippy_moondragon.sql +0 -11
- package/queue/postgres/drizzle/0001_certain_wild_pack.sql +0 -2
- package/queue/postgres/drizzle/0002_dear_meggan.sql +0 -2
- package/queue/postgres/drizzle/0003_tricky_venom.sql +0 -30
- package/queue/postgres/drizzle/meta/0001_snapshot.json +0 -103
- package/queue/postgres/drizzle/meta/0002_snapshot.json +0 -90
- package/queue/postgres/drizzle/meta/0003_snapshot.json +0 -288
- package/queue/postgres/drizzle/meta/_journal.json +0 -34
- package/queue/postgres/index.d.ts +0 -4
- package/queue/postgres/index.js +0 -4
- package/queue/postgres/module.d.ts +0 -9
- package/queue/postgres/module.js +0 -29
- package/queue/postgres/queue.d.ts +0 -60
- package/queue/postgres/queue.js +0 -681
- package/queue/postgres/queue.provider.d.ts +0 -7
- package/queue/postgres/schemas.d.ts +0 -14
- package/queue/postgres/schemas.js +0 -6
- package/queue/postgres/task.model.d.ts +0 -24
- package/queue/postgres/task.model.js +0 -115
- package/queue/provider.js +0 -2
- package/queue/queue.js +0 -131
- package/queue/tests/queue.test.js +0 -623
- package/test3.d.ts +0 -1
- package/test3.js +0 -47
- /package/{queue/tests/queue.test.d.ts → api/server/tests/csrf.middleware.test.d.ts} +0 -0
- /package/circuit-breaker/postgres/drizzle/{0000_hard_shocker.sql → 0000_cooing_korath.sql} +0 -0
- /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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
#
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
148
|
+
return transactionalContextDataGuard;
|
|
127
149
|
}
|
|
128
150
|
}
|
|
129
151
|
export function tryGetTransactionalContextData(_instance) {
|
|
130
|
-
|
|
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
|
-
|
|
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)) {
|
package/orm/server/types.d.ts
CHANGED
|
@@ -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
|
+
}
|
|
@@ -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 '
|
|
10
|
-
import { type PgEnumFromEnumeration } from '
|
|
11
|
-
import type { TsVectorWeight } from '
|
|
12
|
-
import type { Uuid } from '
|
|
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
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
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
|
|
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,
|
|
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 {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
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
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
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
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
64
|
+
return [key, SQL_FALSE];
|
|
41
65
|
}
|
|
42
|
-
|
|
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
|
-
|
|
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 ?
|
|
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
|
|
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 =
|
|
327
|
-
|
|
328
|
-
|
|
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
|
+
});
|