ts-prorm-orm 1.1.0
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/CHANGELOG.md +1111 -0
- package/LICENSE +21 -0
- package/README.md +573 -0
- package/dist/audit/history-query.js +180 -0
- package/dist/audit/index.js +23 -0
- package/dist/audit/logger.js +236 -0
- package/dist/cache/cache-manager.js +84 -0
- package/dist/cache/index.js +16 -0
- package/dist/cache/redis-cluster-cache.js +557 -0
- package/dist/cli.js +2200 -0
- package/dist/compliance/audit-trail.js +68 -0
- package/dist/compliance/backup-verification.js +685 -0
- package/dist/compliance/breach-detector.js +505 -0
- package/dist/compliance/consent-record.js +227 -0
- package/dist/compliance/consent-versioning.js +331 -0
- package/dist/compliance/cross-border-log.js +530 -0
- package/dist/compliance/data-classifier.js +258 -0
- package/dist/compliance/data-lineage.js +565 -0
- package/dist/compliance/data-masker.js +303 -0
- package/dist/compliance/data-portability.js +265 -0
- package/dist/compliance/data-retention.js +195 -0
- package/dist/compliance/dsar-workflow.js +342 -0
- package/dist/compliance/field-encryption.js +223 -0
- package/dist/compliance/immutable-record.js +148 -0
- package/dist/compliance/index.js +260 -0
- package/dist/compliance/privacy-impact-assessment.js +364 -0
- package/dist/compliance/pseudonymization.js +269 -0
- package/dist/compliance/query-firewall.js +1182 -0
- package/dist/compliance/rate-limiter.js +580 -0
- package/dist/compliance/right-to-erasure.js +117 -0
- package/dist/compliance/row-level-security.js +246 -0
- package/dist/compliance/security-decorator.js +574 -0
- package/dist/compliance/security-monitor.js +525 -0
- package/dist/compliance/sensitive-data-discovery.js +476 -0
- package/dist/compliance/session-isolation.js +472 -0
- package/dist/compliance/tls-enforcer.js +108 -0
- package/dist/compliance/worm-storage.js +712 -0
- package/dist/connection-manager.js +198 -0
- package/dist/connection-pool.js +519 -0
- package/dist/decorators/audit.js +136 -0
- package/dist/decorators/belongs-to-many.js +115 -0
- package/dist/decorators/belongs-to.js +115 -0
- package/dist/decorators/check.js +435 -0
- package/dist/decorators/collate.js +329 -0
- package/dist/decorators/comment.js +205 -0
- package/dist/decorators/database-settings.js +236 -0
- package/dist/decorators/default.js +244 -0
- package/dist/decorators/encryption.js +235 -0
- package/dist/decorators/engine.js +97 -0
- package/dist/decorators/fk-constraints.js +594 -0
- package/dist/decorators/foreign-table.js +136 -0
- package/dist/decorators/generated.js +274 -0
- package/dist/decorators/has-many.js +127 -0
- package/dist/decorators/has-one.js +116 -0
- package/dist/decorators/hstore.js +129 -0
- package/dist/decorators/index.js +355 -0
- package/dist/decorators/json-column.js +83 -0
- package/dist/decorators/jsonb.js +101 -0
- package/dist/decorators/orm-decorators.js +425 -0
- package/dist/decorators/permissions.js +294 -0
- package/dist/decorators/procedure.js +210 -0
- package/dist/decorators/query-options.js +556 -0
- package/dist/decorators/range.js +167 -0
- package/dist/decorators/set-column.js +83 -0
- package/dist/decorators/spatial.js +114 -0
- package/dist/decorators/storage.js +580 -0
- package/dist/decorators/timezone.js +512 -0
- package/dist/decorators/trigger.js +90 -0
- package/dist/decorators/uuid.js +135 -0
- package/dist/decorators/view.js +258 -0
- package/dist/diagrams/chen-diagram.js +354 -0
- package/dist/diagrams/class-diagram.js +384 -0
- package/dist/diagrams/dependency-diagram.js +432 -0
- package/dist/diagrams/er-diagram.js +605 -0
- package/dist/diagrams/flow-diagram.js +394 -0
- package/dist/diagrams/gantt-diagram.js +411 -0
- package/dist/diagrams/index-diagram.js +353 -0
- package/dist/diagrams/index.js +184 -0
- package/dist/diagrams/migration-diagram.js +316 -0
- package/dist/diagrams/model-diagram.js +616 -0
- package/dist/diagrams/package-diagram.js +376 -0
- package/dist/diagrams/palette.js +85 -0
- package/dist/diagrams/relational-diagram.js +455 -0
- package/dist/diagrams/schemadoc-diagram.js +309 -0
- package/dist/diagrams/sequence-diagram.js +307 -0
- package/dist/diagrams/state-diagram.js +344 -0
- package/dist/diagrams/svg-dom.js +111 -0
- package/dist/diagrams/tree-diagram.js +250 -0
- package/dist/dialects/clickhouse/index.js +1742 -0
- package/dist/dialects/cockroachdb/index.js +4677 -0
- package/dist/dialects/cratedb/index.js +441 -0
- package/dist/dialects/databricks/index.js +517 -0
- package/dist/dialects/db2/index.js +2225 -0
- package/dist/dialects/dialect.js +720 -0
- package/dist/dialects/duckdb/index.js +2014 -0
- package/dist/dialects/exasol/index.js +357 -0
- package/dist/dialects/firebird/index.js +495 -0
- package/dist/dialects/greenplum/index.js +457 -0
- package/dist/dialects/hana/index.js +1741 -0
- package/dist/dialects/mariadb/index.js +3590 -0
- package/dist/dialects/mssql/index.js +2296 -0
- package/dist/dialects/mysql/index.js +4058 -0
- package/dist/dialects/oracle/index.js +2999 -0
- package/dist/dialects/postgres/index.js +5319 -0
- package/dist/dialects/query-stream-helper.js +152 -0
- package/dist/dialects/questdb/index.js +433 -0
- package/dist/dialects/redshift/index.js +2277 -0
- package/dist/dialects/singlestore/index.js +354 -0
- package/dist/dialects/snowflake/index.js +2082 -0
- package/dist/dialects/spanner/index.js +1768 -0
- package/dist/dialects/sqlite/index.js +3382 -0
- package/dist/dialects/tidb/index.js +377 -0
- package/dist/dialects/timescaledb/index.js +164 -0
- package/dist/dialects/trino/index.js +420 -0
- package/dist/dialects/turso/index.js +227 -0
- package/dist/dialects/vertica/index.js +317 -0
- package/dist/dialects/yugabytedb/index.js +424 -0
- package/dist/errors/index.js +472 -0
- package/dist/errors/utils.js +340 -0
- package/dist/errors.js +21 -0
- package/dist/extensions/catalog/cloud-warehouse-features.js +207 -0
- package/dist/extensions/catalog/mssql-features.js +147 -0
- package/dist/extensions/catalog/mysql-mariadb-plugins.js +229 -0
- package/dist/extensions/catalog/oracle-db2-features.js +196 -0
- package/dist/extensions/catalog/postgres-extensions.js +516 -0
- package/dist/extensions/index.js +71 -0
- package/dist/extensions/types.js +13 -0
- package/dist/foreign-data.js +173 -0
- package/dist/hooks/hooks-manager.js +350 -0
- package/dist/hooks/index.js +37 -0
- package/dist/index.js +463 -0
- package/dist/logging.js +335 -0
- package/dist/migrations/index.js +40 -0
- package/dist/migrations/migration.js +196 -0
- package/dist/migrations/migrator.js +411 -0
- package/dist/migrations/prormmigration.js +275 -0
- package/dist/migrations/query-interface.js +435 -0
- package/dist/migrations/seeder.js +353 -0
- package/dist/models/associations.js +852 -0
- package/dist/models/constraints.js +288 -0
- package/dist/models/data-types.js +2518 -0
- package/dist/models/decorators.js +445 -0
- package/dist/models/index.js +33 -0
- package/dist/models/indexes.js +531 -0
- package/dist/models/methods.js +382 -0
- package/dist/models/model-manager.js +103 -0
- package/dist/models/model.js +5349 -0
- package/dist/models/operators.js +67 -0
- package/dist/models/scopes.js +189 -0
- package/dist/models/typescript-types.js +26 -0
- package/dist/nosql/aerospike/index.js +205 -0
- package/dist/nosql/allegrograph/index.js +169 -0
- package/dist/nosql/arangodb/index.js +364 -0
- package/dist/nosql/azure-blob/index.js +206 -0
- package/dist/nosql/beanstalkd/index.js +231 -0
- package/dist/nosql/beequeue/index.js +210 -0
- package/dist/nosql/bigchaindb/index.js +195 -0
- package/dist/nosql/bigtable/index.js +224 -0
- package/dist/nosql/blazegraph/index.js +173 -0
- package/dist/nosql/bullmq/index.js +191 -0
- package/dist/nosql/cassandra/index.js +174 -0
- package/dist/nosql/chroma/index.js +190 -0
- package/dist/nosql/cloudflare-kv/index.js +220 -0
- package/dist/nosql/coherence/index.js +200 -0
- package/dist/nosql/cosmosdb/index.js +157 -0
- package/dist/nosql/couchbase/index.js +213 -0
- package/dist/nosql/dax/index.js +212 -0
- package/dist/nosql/deno-kv/index.js +206 -0
- package/dist/nosql/dgraph/index.js +171 -0
- package/dist/nosql/doris/index.js +169 -0
- package/dist/nosql/druid/index.js +162 -0
- package/dist/nosql/dynamodb/index.js +937 -0
- package/dist/nosql/elasticsearch/index.js +377 -0
- package/dist/nosql/etcd/index.js +502 -0
- package/dist/nosql/eventhubs/index.js +213 -0
- package/dist/nosql/eventstore/index.js +254 -0
- package/dist/nosql/faunadb/index.js +188 -0
- package/dist/nosql/firestore/index.js +177 -0
- package/dist/nosql/fluree/index.js +148 -0
- package/dist/nosql/fuseki/index.js +170 -0
- package/dist/nosql/gcs/index.js +172 -0
- package/dist/nosql/gearman/index.js +160 -0
- package/dist/nosql/geode/index.js +196 -0
- package/dist/nosql/graphdb/index.js +169 -0
- package/dist/nosql/graylog/index.js +188 -0
- package/dist/nosql/gridgain/index.js +171 -0
- package/dist/nosql/hazelcast/index.js +162 -0
- package/dist/nosql/hbase/index.js +230 -0
- package/dist/nosql/ignite/index.js +173 -0
- package/dist/nosql/immudb/index.js +184 -0
- package/dist/nosql/index.js +232 -0
- package/dist/nosql/infinispan/index.js +200 -0
- package/dist/nosql/influxdb/index.js +0 -0
- package/dist/nosql/kafka/index.js +234 -0
- package/dist/nosql/keyspaces/index.js +189 -0
- package/dist/nosql/kinesis/index.js +253 -0
- package/dist/nosql/leveldb/index.js +153 -0
- package/dist/nosql/lmdb/index.js +160 -0
- package/dist/nosql/loki/index.js +201 -0
- package/dist/nosql/marklogic/index.js +204 -0
- package/dist/nosql/materialize/index.js +145 -0
- package/dist/nosql/meilisearch/index.js +154 -0
- package/dist/nosql/memcached/index.js +223 -0
- package/dist/nosql/milvus/index.js +410 -0
- package/dist/nosql/minio/index.js +265 -0
- package/dist/nosql/momento/index.js +179 -0
- package/dist/nosql/mongodb/index.js +461 -0
- package/dist/nosql/nats/index.js +247 -0
- package/dist/nosql/nedb/index.js +164 -0
- package/dist/nosql/neo4j/index.js +450 -0
- package/dist/nosql/neptune/index.js +470 -0
- package/dist/nosql/nsq/index.js +200 -0
- package/dist/nosql/opensearch/index.js +186 -0
- package/dist/nosql/orientdb/index.js +175 -0
- package/dist/nosql/papertrail/index.js +200 -0
- package/dist/nosql/pinecone/index.js +0 -0
- package/dist/nosql/pinot/index.js +133 -0
- package/dist/nosql/pouchdb/index.js +172 -0
- package/dist/nosql/prometheus/index.js +174 -0
- package/dist/nosql/provendb/index.js +147 -0
- package/dist/nosql/pubsub/index.js +187 -0
- package/dist/nosql/pulsar/index.js +232 -0
- package/dist/nosql/qdrant/index.js +295 -0
- package/dist/nosql/qldb/index.js +192 -0
- package/dist/nosql/r2/index.js +281 -0
- package/dist/nosql/rabbitmq/index.js +237 -0
- package/dist/nosql/ravendb/index.js +175 -0
- package/dist/nosql/redis/index.js +607 -0
- package/dist/nosql/redpanda/index.js +237 -0
- package/dist/nosql/resque/index.js +203 -0
- package/dist/nosql/rethinkdb/index.js +232 -0
- package/dist/nosql/rocksdb/index.js +152 -0
- package/dist/nosql/rockset/index.js +126 -0
- package/dist/nosql/s3/index.js +298 -0
- package/dist/nosql/scylladb/index.js +178 -0
- package/dist/nosql/signoz/index.js +227 -0
- package/dist/nosql/sns/index.js +201 -0
- package/dist/nosql/solr/index.js +231 -0
- package/dist/nosql/splunk/index.js +227 -0
- package/dist/nosql/sqs/index.js +246 -0
- package/dist/nosql/stardog/index.js +169 -0
- package/dist/nosql/starrocks/index.js +170 -0
- package/dist/nosql/store.js +2 -0
- package/dist/nosql/sumologic/index.js +213 -0
- package/dist/nosql/surrealdb/index.js +179 -0
- package/dist/nosql/terminusdb/index.js +151 -0
- package/dist/nosql/tigergraph/index.js +469 -0
- package/dist/nosql/typesense/index.js +148 -0
- package/dist/nosql/unqlite/index.js +157 -0
- package/dist/nosql/upstash/index.js +167 -0
- package/dist/nosql/vercel-kv/index.js +166 -0
- package/dist/nosql/victoriametrics/index.js +213 -0
- package/dist/nosql/virtuoso/index.js +169 -0
- package/dist/nosql/weaviate/index.js +276 -0
- package/dist/operators/index.js +108 -0
- package/dist/operators.js +2690 -0
- package/dist/prisma-migrate/index.js +38 -0
- package/dist/prisma-migrate/migration-generator.js +125 -0
- package/dist/prisma-migrate/model-generator.js +172 -0
- package/dist/prisma-migrate/relations.js +100 -0
- package/dist/prisma-migrate/schema-parser.js +167 -0
- package/dist/prisma-migrate/type-mapper.js +40 -0
- package/dist/prorm.js +6832 -0
- package/dist/query-builders/cte-builder.js +80 -0
- package/dist/query-builders/functions/aggregate.js +390 -0
- package/dist/query-builders/functions/conditional.js +503 -0
- package/dist/query-builders/functions/datetime.js +695 -0
- package/dist/query-builders/functions/fulltext.js +439 -0
- package/dist/query-builders/functions/index.js +93 -0
- package/dist/query-builders/functions/json.js +427 -0
- package/dist/query-builders/functions/math.js +399 -0
- package/dist/query-builders/functions/string.js +518 -0
- package/dist/query-builders/functions/window.js +328 -0
- package/dist/query-builders/include-builder.js +323 -0
- package/dist/query-builders/index-expression-builder.js +242 -0
- package/dist/query-builders/index.js +161 -0
- package/dist/query-builders/insert-builder.js +164 -0
- package/dist/query-builders/model-helpers.js +38 -0
- package/dist/query-builders/order-limit-builder.js +239 -0
- package/dist/query-builders/sql-compiler.js +2040 -0
- package/dist/query-builders/subquery-builder.js +152 -0
- package/dist/query-builders/update-builder.js +182 -0
- package/dist/query-builders/view-builder.js +218 -0
- package/dist/query-builders/where-builder.js +1532 -0
- package/dist/query-interface.js +622 -0
- package/dist/query-optimizers/batch-optimizer.js +426 -0
- package/dist/query-optimizers/explain-plans.js +415 -0
- package/dist/query-optimizers/index.js +51 -0
- package/dist/query-optimizers/prepared-statement-cache.js +423 -0
- package/dist/query-optimizers/query-hints.js +438 -0
- package/dist/query-optimizers/query-optimizer.js +278 -0
- package/dist/query-optimizers/slow-query-logger.js +271 -0
- package/dist/replica-manager.js +507 -0
- package/dist/schema/index.js +15 -0
- package/dist/schema/migration-generator.js +374 -0
- package/dist/schema/schema-differ.js +549 -0
- package/dist/schema/types.js +6 -0
- package/dist/sql-constants.js +300 -0
- package/dist/sqlite-advanced.js +1047 -0
- package/dist/streams/index.js +12 -0
- package/dist/streams/transforms.js +178 -0
- package/dist/transaction.js +456 -0
- package/dist/types/index.js +180 -0
- package/dist/user-management.js +127 -0
- package/dist/utils/date.js +300 -0
- package/dist/utils/index.js +1079 -0
- package/dist/utils/string.js +161 -0
- package/dist/validators/index.js +19 -0
- package/dist/validators/validator.js +911 -0
- package/package.json +190 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Timezone Decorator for the ORM
|
|
4
|
+
*
|
|
5
|
+
* Provides automatic timezone conversion for date/time columns.
|
|
6
|
+
* Supports column-level, table-level, and database-level timezone configuration.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
*
|
|
10
|
+
* // Column-level timezone
|
|
11
|
+
* @Timezone('UTC') // Store as UTC, convert on read/write
|
|
12
|
+
* createdAt: Date;
|
|
13
|
+
*
|
|
14
|
+
* @Timezone('America/New_York') // Eastern time
|
|
15
|
+
* orderPlacedAt: Date;
|
|
16
|
+
*
|
|
17
|
+
* @Timezone('Europe/London') // British time
|
|
18
|
+
* updatedAt: Date;
|
|
19
|
+
*
|
|
20
|
+
* // Auto-detect from user context
|
|
21
|
+
* @Timezone({ from: 'user' })
|
|
22
|
+
* lastLoginAt: Date;
|
|
23
|
+
*
|
|
24
|
+
* // With format options
|
|
25
|
+
* @Timezone({
|
|
26
|
+
* zone: 'UTC',
|
|
27
|
+
* format: 'ISO', // 'ISO', 'DATE', 'DATETIME', 'TIMESTAMP'
|
|
28
|
+
* storeAs: 'utc' // 'utc', 'local', 'string'
|
|
29
|
+
* })
|
|
30
|
+
* timestamp: Date;
|
|
31
|
+
*
|
|
32
|
+
* // Table-level default timezone
|
|
33
|
+
* @Table({ timezone: 'America/Los_Angeles' })
|
|
34
|
+
* class WestCoastOrder extends Model {}
|
|
35
|
+
*
|
|
36
|
+
* // Database-level timezone setting
|
|
37
|
+
* @DatabaseSetting('timezone', 'UTC')
|
|
38
|
+
*/
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.TimezoneEnum = void 0;
|
|
41
|
+
exports.setTimezoneContext = setTimezoneContext;
|
|
42
|
+
exports.getTimezoneContext = getTimezoneContext;
|
|
43
|
+
exports.clearTimezoneContext = clearTimezoneContext;
|
|
44
|
+
exports.resolveTimezoneFromContext = resolveTimezoneFromContext;
|
|
45
|
+
exports.getTimezoneFields = getTimezoneFields;
|
|
46
|
+
exports.getTimezoneForField = getTimezoneForField;
|
|
47
|
+
exports.getTableTimezone = getTableTimezone;
|
|
48
|
+
exports.getDefaultTimezone = getDefaultTimezone;
|
|
49
|
+
exports.convertToTimezone = convertToTimezone;
|
|
50
|
+
exports.convertFromTimezone = convertFromTimezone;
|
|
51
|
+
exports.formatDateTimezone = formatDateTimezone;
|
|
52
|
+
exports.parseDate = parseDate;
|
|
53
|
+
exports.convertOnRead = convertOnRead;
|
|
54
|
+
exports.convertOnWrite = convertOnWrite;
|
|
55
|
+
exports.Timezone = Timezone;
|
|
56
|
+
exports.applyTimezoneToQuery = applyTimezoneToQuery;
|
|
57
|
+
exports.convertWhereToTimezone = convertWhereToTimezone;
|
|
58
|
+
exports.getEffectiveTimezone = getEffectiveTimezone;
|
|
59
|
+
exports.getAllTimezoneFields = getAllTimezoneFields;
|
|
60
|
+
exports.convertInstanceOnRead = convertInstanceOnRead;
|
|
61
|
+
exports.convertInstanceOnWrite = convertInstanceOnWrite;
|
|
62
|
+
exports.applyTimezoneHooks = applyTimezoneHooks;
|
|
63
|
+
require("reflect-metadata");
|
|
64
|
+
// ==================== Constants ====================
|
|
65
|
+
/**
|
|
66
|
+
* Common timezone constants for easy reference
|
|
67
|
+
*/
|
|
68
|
+
exports.TimezoneEnum = {
|
|
69
|
+
UTC: 'UTC',
|
|
70
|
+
US_EASTERN: 'America/New_York',
|
|
71
|
+
US_CENTRAL: 'America/Chicago',
|
|
72
|
+
US_MOUNTAIN: 'America/Denver',
|
|
73
|
+
US_PACIFIC: 'America/Los_Angeles',
|
|
74
|
+
EUROPE_LONDON: 'Europe/London',
|
|
75
|
+
EUROPE_PARIS: 'Europe/Paris',
|
|
76
|
+
EUROPE_BERLIN: 'Europe/Berlin',
|
|
77
|
+
ASIA_TOKYO: 'Asia/Tokyo',
|
|
78
|
+
ASIA_SHANGHAI: 'Asia/Shanghai',
|
|
79
|
+
ASIA_SINGAPORE: 'Asia/Singapore',
|
|
80
|
+
AUSTRALIA_SYDNEY: 'Australia/Sydney',
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Metadata keys for storing timezone information
|
|
84
|
+
*/
|
|
85
|
+
const TIMEZONE_FIELDS_KEY = '__orm_timezone_fields__';
|
|
86
|
+
const TABLE_TIMEZONE_KEY = '__orm_table_timezone__';
|
|
87
|
+
const DEFAULT_TIMEZONE_KEY = '__orm_default_timezone__';
|
|
88
|
+
// ==================== Context Management ====================
|
|
89
|
+
/**
|
|
90
|
+
* Current timezone context (thread-local storage for request scope)
|
|
91
|
+
*/
|
|
92
|
+
let currentTimezoneContext = null;
|
|
93
|
+
/**
|
|
94
|
+
* Set timezone context for current request/operation
|
|
95
|
+
*
|
|
96
|
+
* @param context - The timezone context
|
|
97
|
+
*/
|
|
98
|
+
function setTimezoneContext(context) {
|
|
99
|
+
currentTimezoneContext = context;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get current timezone context
|
|
103
|
+
*
|
|
104
|
+
* @returns The current timezone context
|
|
105
|
+
*/
|
|
106
|
+
function getTimezoneContext() {
|
|
107
|
+
return currentTimezoneContext;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Clear timezone context
|
|
111
|
+
*/
|
|
112
|
+
function clearTimezoneContext() {
|
|
113
|
+
currentTimezoneContext = null;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get timezone from context based on source configuration
|
|
117
|
+
*
|
|
118
|
+
* @param source - The timezone source configuration
|
|
119
|
+
* @returns The resolved timezone string
|
|
120
|
+
*/
|
|
121
|
+
function resolveTimezoneFromContext(source) {
|
|
122
|
+
if (typeof source === 'string') {
|
|
123
|
+
return source;
|
|
124
|
+
}
|
|
125
|
+
if (typeof source === 'object' && 'from' in source) {
|
|
126
|
+
const ctx = currentTimezoneContext;
|
|
127
|
+
if (!ctx)
|
|
128
|
+
return undefined;
|
|
129
|
+
switch (source.from) {
|
|
130
|
+
case 'user':
|
|
131
|
+
return ctx.timezone;
|
|
132
|
+
case 'request':
|
|
133
|
+
case 'context':
|
|
134
|
+
return ctx.timezone || ctx.request?.timezone || ctx.timezone;
|
|
135
|
+
default:
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
// ==================== Metadata Storage ====================
|
|
142
|
+
/**
|
|
143
|
+
* Get all timezone field metadata from a model class
|
|
144
|
+
*/
|
|
145
|
+
function getTimezoneFields(target) {
|
|
146
|
+
return target[TIMEZONE_FIELDS_KEY] || [];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get timezone metadata for a specific field
|
|
150
|
+
*/
|
|
151
|
+
function getTimezoneForField(target, field) {
|
|
152
|
+
const fields = getTimezoneFields(target);
|
|
153
|
+
return fields.find((f) => f.field === field);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get table-level timezone configuration
|
|
157
|
+
*/
|
|
158
|
+
function getTableTimezone(target) {
|
|
159
|
+
return target[TABLE_TIMEZONE_KEY];
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get default timezone from class
|
|
163
|
+
*/
|
|
164
|
+
function getDefaultTimezone(target) {
|
|
165
|
+
return target[DEFAULT_TIMEZONE_KEY];
|
|
166
|
+
}
|
|
167
|
+
// ==================== Timezone Conversion ====================
|
|
168
|
+
/**
|
|
169
|
+
* Convert date to specified timezone
|
|
170
|
+
*
|
|
171
|
+
* @param date - The date to convert
|
|
172
|
+
* @param targetZone - Target timezone (e.g., 'America/New_York')
|
|
173
|
+
* @returns Date adjusted to target timezone
|
|
174
|
+
*/
|
|
175
|
+
function convertToTimezone(date, targetZone) {
|
|
176
|
+
// Get offset for target timezone
|
|
177
|
+
const targetDate = new Date(date.toLocaleString('en-US', { timeZone: targetZone }));
|
|
178
|
+
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
|
|
179
|
+
const offsetMs = targetDate.getTime() - utcDate.getTime();
|
|
180
|
+
const result = new Date(date.getTime() + offsetMs);
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Convert date from specified timezone to UTC
|
|
185
|
+
*
|
|
186
|
+
* @param date - The date to convert
|
|
187
|
+
* @param sourceZone - Source timezone (e.g., 'America/New_York')
|
|
188
|
+
* @returns Date in UTC
|
|
189
|
+
*/
|
|
190
|
+
function convertFromTimezone(date, sourceZone) {
|
|
191
|
+
// Create date as if it's in the source timezone
|
|
192
|
+
const str = date.toISOString();
|
|
193
|
+
const sourceDateStr = date.toLocaleString('en-US', { timeZone: sourceZone });
|
|
194
|
+
const sourceDate = new Date(sourceDateStr);
|
|
195
|
+
// Calculate the offset
|
|
196
|
+
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
|
|
197
|
+
const offsetMs = sourceDate.getTime() - utcDate.getTime();
|
|
198
|
+
return new Date(date.getTime() - offsetMs);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Format date according to specified format
|
|
202
|
+
*
|
|
203
|
+
* @param date - The date to format
|
|
204
|
+
* @param format - The format type
|
|
205
|
+
* @returns Formatted date string or timestamp
|
|
206
|
+
*/
|
|
207
|
+
function formatDateTimezone(date, format) {
|
|
208
|
+
switch (format) {
|
|
209
|
+
case 'ISO':
|
|
210
|
+
return date.toISOString();
|
|
211
|
+
case 'DATE':
|
|
212
|
+
return date.toISOString().split('T')[0];
|
|
213
|
+
case 'DATETIME':
|
|
214
|
+
return date.toISOString().replace('T', ' ').split('.')[0];
|
|
215
|
+
case 'TIMESTAMP':
|
|
216
|
+
return date.getTime();
|
|
217
|
+
default:
|
|
218
|
+
return date.toISOString();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Parse date from database storage format
|
|
223
|
+
*
|
|
224
|
+
* @param value - The value from database
|
|
225
|
+
* @param format - The expected format
|
|
226
|
+
* @returns Parsed Date object
|
|
227
|
+
*/
|
|
228
|
+
function parseDate(value, format) {
|
|
229
|
+
if (value === null || value === undefined) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
if (value instanceof Date) {
|
|
233
|
+
return value;
|
|
234
|
+
}
|
|
235
|
+
if (typeof value === 'number') {
|
|
236
|
+
// Timestamp
|
|
237
|
+
return new Date(value);
|
|
238
|
+
}
|
|
239
|
+
if (typeof value === 'string') {
|
|
240
|
+
return new Date(value);
|
|
241
|
+
}
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
// ==================== Field-Level Operations ====================
|
|
245
|
+
/**
|
|
246
|
+
* Convert value when reading from database
|
|
247
|
+
*
|
|
248
|
+
* @param value - Raw value from database
|
|
249
|
+
* @param meta - Timezone metadata
|
|
250
|
+
* @returns Converted value
|
|
251
|
+
*/
|
|
252
|
+
function convertOnRead(value, meta) {
|
|
253
|
+
if (!meta.convertOnRead) {
|
|
254
|
+
return value;
|
|
255
|
+
}
|
|
256
|
+
const date = parseDate(value, meta.format);
|
|
257
|
+
if (!date) {
|
|
258
|
+
return value;
|
|
259
|
+
}
|
|
260
|
+
// Resolve actual timezone
|
|
261
|
+
let targetZone;
|
|
262
|
+
if (typeof meta.zone === 'string') {
|
|
263
|
+
targetZone = meta.zone;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
targetZone = resolveTimezoneFromContext(meta.zone);
|
|
267
|
+
}
|
|
268
|
+
if (!targetZone) {
|
|
269
|
+
return value;
|
|
270
|
+
}
|
|
271
|
+
// Convert to target timezone
|
|
272
|
+
const converted = convertToTimezone(date, targetZone);
|
|
273
|
+
// Return in requested format
|
|
274
|
+
if (meta.storeAs === 'string') {
|
|
275
|
+
return formatDateTimezone(converted, meta.format);
|
|
276
|
+
}
|
|
277
|
+
return converted;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Convert value when writing to database
|
|
281
|
+
*
|
|
282
|
+
* @param value - Value from application
|
|
283
|
+
* @param meta - Timezone metadata
|
|
284
|
+
* @returns Value ready for database
|
|
285
|
+
*/
|
|
286
|
+
function convertOnWrite(value, meta) {
|
|
287
|
+
if (!meta.convertOnWrite) {
|
|
288
|
+
return value;
|
|
289
|
+
}
|
|
290
|
+
if (value === null || value === undefined) {
|
|
291
|
+
return value;
|
|
292
|
+
}
|
|
293
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
294
|
+
if (isNaN(date.getTime())) {
|
|
295
|
+
return value;
|
|
296
|
+
}
|
|
297
|
+
// Resolve source timezone
|
|
298
|
+
let sourceZone;
|
|
299
|
+
if (typeof meta.zone === 'string') {
|
|
300
|
+
sourceZone = meta.zone;
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
sourceZone = resolveTimezoneFromContext(meta.zone);
|
|
304
|
+
}
|
|
305
|
+
// If storing as UTC, convert to UTC
|
|
306
|
+
if (meta.storeAs === 'utc') {
|
|
307
|
+
return date.toISOString();
|
|
308
|
+
}
|
|
309
|
+
// If storing as string with timezone info
|
|
310
|
+
if (meta.storeAs === 'string') {
|
|
311
|
+
return formatDateTimezone(date, meta.format);
|
|
312
|
+
}
|
|
313
|
+
// Store as local time
|
|
314
|
+
return date;
|
|
315
|
+
}
|
|
316
|
+
// ==================== Decorators ====================
|
|
317
|
+
/**
|
|
318
|
+
* Column-level timezone decorator
|
|
319
|
+
*
|
|
320
|
+
* @param options - Timezone options or timezone string
|
|
321
|
+
*/
|
|
322
|
+
function Timezone(options = {}) {
|
|
323
|
+
return function (target, propertyKey) {
|
|
324
|
+
const field = propertyKey;
|
|
325
|
+
const column = typeof options === 'string' ? options : options.column || propertyKey;
|
|
326
|
+
const zone = typeof options === 'string' ? options : options.zone || 'UTC';
|
|
327
|
+
const format = typeof options === 'string' ? 'ISO' : options.format || 'ISO';
|
|
328
|
+
const storeAs = typeof options === 'string' ? 'utc' : options.storeAs || 'utc';
|
|
329
|
+
const convertOnRead = typeof options === 'string' ? true : (options.convertOnRead ?? true);
|
|
330
|
+
const convertOnWrite = typeof options === 'string' ? true : (options.convertOnWrite ?? true);
|
|
331
|
+
// Store on the prototype (class), not the instance
|
|
332
|
+
const Constructor = target.constructor;
|
|
333
|
+
if (!Constructor[TIMEZONE_FIELDS_KEY]) {
|
|
334
|
+
Constructor[TIMEZONE_FIELDS_KEY] = [];
|
|
335
|
+
}
|
|
336
|
+
Constructor[TIMEZONE_FIELDS_KEY].push({
|
|
337
|
+
field,
|
|
338
|
+
column,
|
|
339
|
+
zone,
|
|
340
|
+
format,
|
|
341
|
+
storeAs,
|
|
342
|
+
convertOnRead,
|
|
343
|
+
convertOnWrite,
|
|
344
|
+
});
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
// ==================== Query Helper ====================
|
|
348
|
+
/**
|
|
349
|
+
* Apply timezone conversion to query options
|
|
350
|
+
*
|
|
351
|
+
* @param queryOptions - Original query options
|
|
352
|
+
* @param targetTimezone - Target timezone for conversion
|
|
353
|
+
* @returns Modified query options with timezone
|
|
354
|
+
*/
|
|
355
|
+
function applyTimezoneToQuery(queryOptions, targetTimezone) {
|
|
356
|
+
if (!queryOptions) {
|
|
357
|
+
return { timezone: targetTimezone };
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
...queryOptions,
|
|
361
|
+
timezone: targetTimezone,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Convert where clause dates to specified timezone
|
|
366
|
+
*
|
|
367
|
+
* @param where - Original where clause
|
|
368
|
+
* @param timezone - Target timezone
|
|
369
|
+
* @returns Modified where clause with timezone context
|
|
370
|
+
*/
|
|
371
|
+
function convertWhereToTimezone(where, timezone) {
|
|
372
|
+
if (!where) {
|
|
373
|
+
return where;
|
|
374
|
+
}
|
|
375
|
+
// Process each key in the where clause
|
|
376
|
+
const result = {};
|
|
377
|
+
for (const [key, value] of Object.entries(where)) {
|
|
378
|
+
if (value instanceof Date) {
|
|
379
|
+
// Convert date to target timezone
|
|
380
|
+
result[key] = convertToTimezone(value, timezone);
|
|
381
|
+
}
|
|
382
|
+
else if (typeof value === 'object' && value !== null) {
|
|
383
|
+
// Recursively process nested objects
|
|
384
|
+
result[key] = convertWhereToTimezone(value, timezone);
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
result[key] = value;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return result;
|
|
391
|
+
}
|
|
392
|
+
// ==================== Model-Level Helpers ====================
|
|
393
|
+
/**
|
|
394
|
+
* Get effective timezone for a field (column-level or table-level default)
|
|
395
|
+
*
|
|
396
|
+
* @param target - Model class
|
|
397
|
+
* @param field - Field name
|
|
398
|
+
* @returns Effective timezone or undefined
|
|
399
|
+
*/
|
|
400
|
+
function getEffectiveTimezone(target, field) {
|
|
401
|
+
// Check column-level first
|
|
402
|
+
const fieldMeta = getTimezoneForField(target, field);
|
|
403
|
+
if (fieldMeta) {
|
|
404
|
+
if (typeof fieldMeta.zone === 'string') {
|
|
405
|
+
return fieldMeta.zone;
|
|
406
|
+
}
|
|
407
|
+
return resolveTimezoneFromContext(fieldMeta.zone);
|
|
408
|
+
}
|
|
409
|
+
// Fall back to table-level default
|
|
410
|
+
const tableOptions = getTableTimezone(target);
|
|
411
|
+
if (tableOptions?.timezone) {
|
|
412
|
+
return tableOptions.timezone;
|
|
413
|
+
}
|
|
414
|
+
// Fall back to global default
|
|
415
|
+
return getDefaultTimezone(target);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Get all timezone metadata for a model including table defaults
|
|
419
|
+
*
|
|
420
|
+
* @param target - Model class
|
|
421
|
+
* @returns Array of timezone field metadata
|
|
422
|
+
*/
|
|
423
|
+
function getAllTimezoneFields(target) {
|
|
424
|
+
return getTimezoneFields(target);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Convert model instance field values based on timezone metadata
|
|
428
|
+
*
|
|
429
|
+
* @param instance - Model instance
|
|
430
|
+
* @param targetTimezone - Target timezone for conversion
|
|
431
|
+
* @returns Converted instance values
|
|
432
|
+
*/
|
|
433
|
+
function convertInstanceOnRead(instance, targetTimezone) {
|
|
434
|
+
const ModelClass = instance.constructor;
|
|
435
|
+
const timezoneFields = getTimezoneFields(ModelClass);
|
|
436
|
+
if (timezoneFields.length === 0) {
|
|
437
|
+
return instance;
|
|
438
|
+
}
|
|
439
|
+
const result = {};
|
|
440
|
+
for (const [key, value] of Object.entries(instance.toJSON ? instance.toJSON() : instance)) {
|
|
441
|
+
const meta = timezoneFields.find((f) => f.field === key || f.column === key);
|
|
442
|
+
if (meta) {
|
|
443
|
+
result[key] = convertOnRead(value, {
|
|
444
|
+
...meta,
|
|
445
|
+
zone: targetTimezone,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
result[key] = value;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return result;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Convert model instance field values before writing to database
|
|
456
|
+
*
|
|
457
|
+
* @param instance - Model instance
|
|
458
|
+
* @returns Converted values ready for database
|
|
459
|
+
*/
|
|
460
|
+
function convertInstanceOnWrite(instance) {
|
|
461
|
+
const ModelClass = instance.constructor;
|
|
462
|
+
const timezoneFields = getTimezoneFields(ModelClass);
|
|
463
|
+
if (timezoneFields.length === 0) {
|
|
464
|
+
return instance;
|
|
465
|
+
}
|
|
466
|
+
const result = {};
|
|
467
|
+
const data = instance.getDataValue
|
|
468
|
+
? (() => {
|
|
469
|
+
const data = {};
|
|
470
|
+
for (const f of timezoneFields) {
|
|
471
|
+
data[f.column] = instance.getDataValue(f.field);
|
|
472
|
+
}
|
|
473
|
+
return data;
|
|
474
|
+
})()
|
|
475
|
+
: instance;
|
|
476
|
+
for (const [key, value] of Object.entries(data)) {
|
|
477
|
+
const meta = timezoneFields.find((f) => f.field === key || f.column === key);
|
|
478
|
+
if (meta) {
|
|
479
|
+
const converted = convertOnWrite(value, meta);
|
|
480
|
+
result[meta.column] = converted;
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
result[key] = value;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return result;
|
|
487
|
+
}
|
|
488
|
+
// ==================== Hook Registration ====================
|
|
489
|
+
/**
|
|
490
|
+
* Register hooks for automatic timezone conversion
|
|
491
|
+
* Call this after Model.init()
|
|
492
|
+
*
|
|
493
|
+
* @param ModelClass - The model class
|
|
494
|
+
*/
|
|
495
|
+
function applyTimezoneHooks(ModelClass) {
|
|
496
|
+
const timezoneFields = getTimezoneFields(ModelClass);
|
|
497
|
+
if (timezoneFields.length === 0)
|
|
498
|
+
return;
|
|
499
|
+
// Before find hooks to set timezone context
|
|
500
|
+
if (typeof ModelClass.addHook === 'function') {
|
|
501
|
+
ModelClass.addHook('beforeFind', 'orm:timezone', async (instance) => {
|
|
502
|
+
// Set context based on query options if timezone specified
|
|
503
|
+
const timezone = instance._timezone || instance.timezone;
|
|
504
|
+
if (timezone) {
|
|
505
|
+
setTimezoneContext({ timezone });
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
ModelClass.addHook('afterFind', 'orm:timezone', async () => {
|
|
509
|
+
clearTimezoneContext();
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @Trigger class decorator — declare a database trigger on a model class.
|
|
4
|
+
*
|
|
5
|
+
* Stores trigger metadata using plain class properties (no reflect-metadata
|
|
6
|
+
* dependency) consistent with the rest of this decorator suite.
|
|
7
|
+
*
|
|
8
|
+
* Example:
|
|
9
|
+
*
|
|
10
|
+
* @Trigger({
|
|
11
|
+
* name: 'trg_users_audit',
|
|
12
|
+
* timing: 'AFTER',
|
|
13
|
+
* event: 'INSERT',
|
|
14
|
+
* body: `INSERT INTO audit_log(action, ts) VALUES ('INSERT', NOW())`,
|
|
15
|
+
* forEach: 'ROW',
|
|
16
|
+
* })
|
|
17
|
+
* class User extends Model { ... }
|
|
18
|
+
*
|
|
19
|
+
* // Retrieve trigger definitions:
|
|
20
|
+
* const triggers = getTriggers(User);
|
|
21
|
+
*
|
|
22
|
+
* // Generate DDL:
|
|
23
|
+
* const sql = buildCreateTriggerSQL('users', triggers[0], 'mysql');
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.Trigger = Trigger;
|
|
27
|
+
exports.getTriggers = getTriggers;
|
|
28
|
+
exports.buildCreateTriggerSQL = buildCreateTriggerSQL;
|
|
29
|
+
// ==================== Metadata key ====================
|
|
30
|
+
const TRIGGERS_KEY = '__orm_triggers__';
|
|
31
|
+
// ==================== Decorator ====================
|
|
32
|
+
/**
|
|
33
|
+
* Attach a trigger definition to a model class.
|
|
34
|
+
* Multiple @Trigger decorators may be stacked on the same class.
|
|
35
|
+
*/
|
|
36
|
+
function Trigger(options) {
|
|
37
|
+
return (target) => {
|
|
38
|
+
if (!target[TRIGGERS_KEY]) {
|
|
39
|
+
target[TRIGGERS_KEY] = [];
|
|
40
|
+
}
|
|
41
|
+
target[TRIGGERS_KEY] = [...target[TRIGGERS_KEY], options];
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// ==================== Helpers ====================
|
|
45
|
+
/**
|
|
46
|
+
* Return all trigger definitions registered on a class.
|
|
47
|
+
*/
|
|
48
|
+
function getTriggers(target) {
|
|
49
|
+
return target[TRIGGERS_KEY] || [];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Generate a CREATE TRIGGER DDL statement for the given dialect.
|
|
53
|
+
*
|
|
54
|
+
* Supported dialects: 'mysql' | 'mariadb' | 'postgres' | 'sqlite'
|
|
55
|
+
*/
|
|
56
|
+
function buildCreateTriggerSQL(tableName, trigger, dialect) {
|
|
57
|
+
const forEach = trigger.forEach ?? 'ROW';
|
|
58
|
+
const name = trigger.name;
|
|
59
|
+
switch (dialect) {
|
|
60
|
+
case 'postgres': {
|
|
61
|
+
// PostgreSQL requires a trigger function; we embed the body in a
|
|
62
|
+
// DO-style anonymous block as a convention for metadata purposes.
|
|
63
|
+
const fnName = `${name}_fn`;
|
|
64
|
+
const createFn = `CREATE OR REPLACE FUNCTION ${fnName}()\n` +
|
|
65
|
+
`RETURNS TRIGGER LANGUAGE plpgsql AS $$\n` +
|
|
66
|
+
`BEGIN\n ${trigger.body}\n RETURN NEW;\nEND;\n$$`;
|
|
67
|
+
const createTrg = `CREATE TRIGGER ${name}\n` +
|
|
68
|
+
` ${trigger.timing} ${trigger.event}\n` +
|
|
69
|
+
` ON "${tableName}"\n` +
|
|
70
|
+
` FOR EACH ${forEach}\n` +
|
|
71
|
+
` EXECUTE FUNCTION ${fnName}()`;
|
|
72
|
+
return `${createFn};\n\n${createTrg}`;
|
|
73
|
+
}
|
|
74
|
+
case 'sqlite': {
|
|
75
|
+
// SQLite does not support FOR EACH STATEMENT; always ROW-level implicitly.
|
|
76
|
+
return (`CREATE TRIGGER IF NOT EXISTS ${name}\n` +
|
|
77
|
+
` ${trigger.timing} ${trigger.event}\n` +
|
|
78
|
+
` ON \`${tableName}\`\n` +
|
|
79
|
+
`BEGIN\n ${trigger.body};\nEND`);
|
|
80
|
+
}
|
|
81
|
+
// mysql / mariadb (default)
|
|
82
|
+
default: {
|
|
83
|
+
return (`CREATE TRIGGER \`${name}\`\n` +
|
|
84
|
+
` ${trigger.timing} ${trigger.event}\n` +
|
|
85
|
+
` ON \`${tableName}\`\n` +
|
|
86
|
+
` FOR EACH ${forEach}\n` +
|
|
87
|
+
`BEGIN\n ${trigger.body};\nEND`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|