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,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Encryption decorators and adapter system for the ORM.
|
|
4
|
+
*
|
|
5
|
+
* Allows fields to be annotated with @Secure so values are automatically
|
|
6
|
+
* hashed before being saved. The encryption algorithm is fully swappable
|
|
7
|
+
* via EncryptionAdapter — default recommendation is bcrypt, but you can
|
|
8
|
+
* plug in argon2, scrypt, or any custom implementation.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
*
|
|
12
|
+
* // 1. Install your preferred package, e.g. npm install bcrypt
|
|
13
|
+
* // 2. Wire up the adapter once at boot time:
|
|
14
|
+
* import bcrypt from 'bcrypt';
|
|
15
|
+
* setEncryptionAdapter({
|
|
16
|
+
* hash: (value) => bcrypt.hash(value, 12),
|
|
17
|
+
* verify: (plain, hashed) => bcrypt.compare(plain, hashed),
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // 3. Annotate fields on your model class:
|
|
21
|
+
* class User extends Model { ... }
|
|
22
|
+
* User.init({
|
|
23
|
+
* password: { type: DataTypes.STRING, allowNull: false }
|
|
24
|
+
* }, { prorm });
|
|
25
|
+
* Secure('password')(User); // field decorator style
|
|
26
|
+
*
|
|
27
|
+
* // Or use the decorator on the class definition (requires experimentalDecorators):
|
|
28
|
+
* @SecureField('password')
|
|
29
|
+
* class User extends Model { ... }
|
|
30
|
+
*/
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.Secure = void 0;
|
|
33
|
+
exports.setEncryptionAdapter = setEncryptionAdapter;
|
|
34
|
+
exports.getEncryptionAdapter = getEncryptionAdapter;
|
|
35
|
+
exports.getSecureFields = getSecureFields;
|
|
36
|
+
exports.SecureField = SecureField;
|
|
37
|
+
exports.hashSecureFields = hashSecureFields;
|
|
38
|
+
exports.verifySecureField = verifySecureField;
|
|
39
|
+
exports.applySecureHooks = applySecureHooks;
|
|
40
|
+
exports.createBcryptAdapter = createBcryptAdapter;
|
|
41
|
+
exports.createArgon2Adapter = createArgon2Adapter;
|
|
42
|
+
// ==================== Global Registry ====================
|
|
43
|
+
let globalAdapter = null;
|
|
44
|
+
/**
|
|
45
|
+
* Set the global encryption adapter used by all @Secure fields that
|
|
46
|
+
* do not specify their own adapter.
|
|
47
|
+
*
|
|
48
|
+
* Call this once during application bootstrap, e.g.:
|
|
49
|
+
*
|
|
50
|
+
* import bcrypt from 'bcrypt';
|
|
51
|
+
* setEncryptionAdapter({
|
|
52
|
+
* hash: (v) => bcrypt.hash(v, 12),
|
|
53
|
+
* verify: (plain, hash) => bcrypt.compare(plain, hash),
|
|
54
|
+
* });
|
|
55
|
+
*/
|
|
56
|
+
function setEncryptionAdapter(adapter) {
|
|
57
|
+
globalAdapter = adapter;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Return the currently registered global adapter, or null if none has
|
|
61
|
+
* been configured.
|
|
62
|
+
*/
|
|
63
|
+
function getEncryptionAdapter() {
|
|
64
|
+
return globalAdapter;
|
|
65
|
+
}
|
|
66
|
+
// ==================== Field Metadata ====================
|
|
67
|
+
const SECURE_FIELDS_KEY = '__orm_secure_fields__';
|
|
68
|
+
/**
|
|
69
|
+
* Read the list of secure-field annotations attached to a model constructor.
|
|
70
|
+
*/
|
|
71
|
+
function getSecureFields(target) {
|
|
72
|
+
return target[SECURE_FIELDS_KEY] || [];
|
|
73
|
+
}
|
|
74
|
+
// ==================== Decorators ====================
|
|
75
|
+
/**
|
|
76
|
+
* Mark a model field so its value is automatically hashed before INSERT/UPDATE.
|
|
77
|
+
*
|
|
78
|
+
* This is a class decorator factory — pass the field name(s):
|
|
79
|
+
*
|
|
80
|
+
* @SecureField('password')
|
|
81
|
+
* @SecureField('pin', { algorithm: 'bcrypt', adapter: myAdapter })
|
|
82
|
+
* class User extends Model { ... }
|
|
83
|
+
*
|
|
84
|
+
* The ORM will call hashSecureFields(instance) in a beforeCreate /
|
|
85
|
+
* beforeUpdate hook to encrypt the field before it reaches the database.
|
|
86
|
+
*/
|
|
87
|
+
function SecureField(field, options = {}) {
|
|
88
|
+
return function (target) {
|
|
89
|
+
if (!target[SECURE_FIELDS_KEY]) {
|
|
90
|
+
target[SECURE_FIELDS_KEY] = [];
|
|
91
|
+
}
|
|
92
|
+
target[SECURE_FIELDS_KEY].push({ field, options });
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Alias for SecureField — preferred shorthand.
|
|
97
|
+
*
|
|
98
|
+
* @Secure('password')
|
|
99
|
+
* class User extends Model { ... }
|
|
100
|
+
*/
|
|
101
|
+
exports.Secure = SecureField;
|
|
102
|
+
// ==================== Runtime Helpers ====================
|
|
103
|
+
/**
|
|
104
|
+
* Hash all @Secure-annotated fields on a model instance.
|
|
105
|
+
*
|
|
106
|
+
* Call this inside a beforeCreate / beforeUpdate hook, or let the ORM
|
|
107
|
+
* do it automatically when you use applySecureHooks().
|
|
108
|
+
*
|
|
109
|
+
* @param instance - The model instance whose fields should be hashed
|
|
110
|
+
*/
|
|
111
|
+
async function hashSecureFields(instance) {
|
|
112
|
+
const ModelClass = instance.constructor;
|
|
113
|
+
const secureFields = getSecureFields(ModelClass);
|
|
114
|
+
for (const { field, options } of secureFields) {
|
|
115
|
+
const value = instance.getDataValue
|
|
116
|
+
? instance.getDataValue(field)
|
|
117
|
+
: instance[field];
|
|
118
|
+
if (value == null || typeof value !== 'string')
|
|
119
|
+
continue;
|
|
120
|
+
// Skip if the value already looks like a hash (starts with $2b$, $argon2, etc.)
|
|
121
|
+
const skipIfHashed = options.skipIfHashed !== false; // default true
|
|
122
|
+
if (skipIfHashed && looksLikeHash(value))
|
|
123
|
+
continue;
|
|
124
|
+
const adapter = options.adapter ?? globalAdapter;
|
|
125
|
+
if (!adapter) {
|
|
126
|
+
throw new Error(`[ORM] No encryption adapter configured for field "${field}". ` +
|
|
127
|
+
`Call setEncryptionAdapter() at boot time, or provide an adapter in @Secure options.`);
|
|
128
|
+
}
|
|
129
|
+
const hashed = await adapter.hash(value);
|
|
130
|
+
if (instance.setDataValue) {
|
|
131
|
+
instance.setDataValue(field, hashed);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
instance[field] = hashed;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Verify a plain-text value against the stored hash of a @Secure field.
|
|
140
|
+
*
|
|
141
|
+
* @param instance - The model instance containing the stored hash
|
|
142
|
+
* @param field - The field name
|
|
143
|
+
* @param plainText - The candidate value to check
|
|
144
|
+
* @returns true if the plain-text matches the stored hash
|
|
145
|
+
*/
|
|
146
|
+
async function verifySecureField(instance, field, plainText) {
|
|
147
|
+
const ModelClass = instance.constructor;
|
|
148
|
+
const secureFields = getSecureFields(ModelClass);
|
|
149
|
+
const meta = secureFields.find((m) => m.field === field);
|
|
150
|
+
const adapter = meta?.options.adapter ?? globalAdapter;
|
|
151
|
+
if (!adapter) {
|
|
152
|
+
throw new Error(`[ORM] No encryption adapter configured for field "${field}". ` +
|
|
153
|
+
`Call setEncryptionAdapter() at boot time.`);
|
|
154
|
+
}
|
|
155
|
+
const stored = instance.getDataValue
|
|
156
|
+
? instance.getDataValue(field)
|
|
157
|
+
: instance[field];
|
|
158
|
+
if (!stored)
|
|
159
|
+
return false;
|
|
160
|
+
return adapter.verify(plainText, stored);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Register beforeCreate and beforeUpdate hooks on a model so that
|
|
164
|
+
* @Secure fields are automatically hashed whenever an instance is saved.
|
|
165
|
+
*
|
|
166
|
+
* Call once per model after Model.init():
|
|
167
|
+
*
|
|
168
|
+
* applySecureHooks(User);
|
|
169
|
+
*
|
|
170
|
+
* @param ModelClass - The model class (result of prorm.define or Model.init)
|
|
171
|
+
*/
|
|
172
|
+
function applySecureHooks(ModelClass) {
|
|
173
|
+
const fields = getSecureFields(ModelClass);
|
|
174
|
+
if (fields.length === 0)
|
|
175
|
+
return;
|
|
176
|
+
// Register hooks via the ORM's addHook API
|
|
177
|
+
if (typeof ModelClass.addHook === 'function') {
|
|
178
|
+
ModelClass.addHook('beforeCreate', 'orm:secureHash', async (instance) => {
|
|
179
|
+
await hashSecureFields(instance);
|
|
180
|
+
});
|
|
181
|
+
ModelClass.addHook('beforeUpdate', 'orm:secureHash', async (instance) => {
|
|
182
|
+
await hashSecureFields(instance);
|
|
183
|
+
});
|
|
184
|
+
ModelClass.addHook('beforeSave', 'orm:secureHash', async (instance) => {
|
|
185
|
+
await hashSecureFields(instance);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// ==================== Convenience Adapters ====================
|
|
190
|
+
/**
|
|
191
|
+
* Create a bcrypt adapter.
|
|
192
|
+
*
|
|
193
|
+
* Requires `bcrypt` or `bcryptjs` to be installed:
|
|
194
|
+
* npm install bcrypt (native, faster)
|
|
195
|
+
* npm install bcryptjs (pure JS, no native build)
|
|
196
|
+
*
|
|
197
|
+
* @param saltRounds - bcrypt cost factor (default: 12)
|
|
198
|
+
*
|
|
199
|
+
* Usage:
|
|
200
|
+
* import bcrypt from 'bcrypt';
|
|
201
|
+
* setEncryptionAdapter(createBcryptAdapter(bcrypt, 12));
|
|
202
|
+
*/
|
|
203
|
+
function createBcryptAdapter(bcryptLib, saltRounds = 12) {
|
|
204
|
+
return {
|
|
205
|
+
hash: (value) => bcryptLib.hash(value, saltRounds),
|
|
206
|
+
verify: (plain, hashed) => bcryptLib.compare(plain, hashed),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Create an argon2 adapter.
|
|
211
|
+
*
|
|
212
|
+
* Requires `argon2` to be installed:
|
|
213
|
+
* npm install argon2
|
|
214
|
+
*
|
|
215
|
+
* Usage:
|
|
216
|
+
* import argon2 from 'argon2';
|
|
217
|
+
* setEncryptionAdapter(createArgon2Adapter(argon2));
|
|
218
|
+
*/
|
|
219
|
+
function createArgon2Adapter(argon2Lib) {
|
|
220
|
+
return {
|
|
221
|
+
hash: (value) => argon2Lib.hash(value),
|
|
222
|
+
verify: (plain, hashed) => argon2Lib.verify(hashed, plain),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
// ==================== Internal Helpers ====================
|
|
226
|
+
/**
|
|
227
|
+
* Heuristic check — does this string look like an already-computed hash?
|
|
228
|
+
* Supports bcrypt ($2a$, $2b$, $2y$), argon2 ($argon2), and scrypt ($scrypt).
|
|
229
|
+
*/
|
|
230
|
+
function looksLikeHash(value) {
|
|
231
|
+
return (/^\$2[aby]\$/.test(value) || // bcrypt
|
|
232
|
+
/^\$argon2/.test(value) || // argon2
|
|
233
|
+
/^\$scrypt\$/.test(value) // scrypt (node-scrypt-format)
|
|
234
|
+
);
|
|
235
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `@Engine` — class decorator for annotating a Model with dialect-specific
|
|
4
|
+
* per-table storage-engine configuration, mirroring the `@View`/
|
|
5
|
+
* `@MaterializedView` pattern (`src/decorators/view.ts`): stores metadata on
|
|
6
|
+
* the class, exposed via a registry, rather than being silently read by
|
|
7
|
+
* `Prorm.sync()` - keeping engine selection an explicit, visible step.
|
|
8
|
+
*
|
|
9
|
+
* Built for ClickHouse's MergeTree-family engine configuration first (the
|
|
10
|
+
* dialect that most needs per-table engine choice - `mergeTreeEngine`/
|
|
11
|
+
* `orderBy`/`ttl`/`partitionBy`/`projections` are all read as loosely-typed
|
|
12
|
+
* extensions off `TableOptions` in `src/dialects/clickhouse/index.ts` since
|
|
13
|
+
* the base `Dialect` interface has no notion of storage engines), but the
|
|
14
|
+
* shape is generic enough for any dialect with similar per-table engine/
|
|
15
|
+
* storage options (e.g. MySQL's `ENGINE=InnoDB`).
|
|
16
|
+
*
|
|
17
|
+
* Example:
|
|
18
|
+
*
|
|
19
|
+
* @Engine({
|
|
20
|
+
* mergeTreeEngine: { type: 'ReplacingMergeTree', version: 'updatedAt' },
|
|
21
|
+
* orderBy: ['id', 'createdAt'],
|
|
22
|
+
* ttl: 'createdAt + INTERVAL 90 DAY',
|
|
23
|
+
* })
|
|
24
|
+
* class Event extends Model {
|
|
25
|
+
* declare id: number;
|
|
26
|
+
* declare createdAt: Date;
|
|
27
|
+
* declare updatedAt: Date;
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* // At table-creation time:
|
|
31
|
+
* const dialect = prorm.getDialectInstance() as ClickHouseDialect;
|
|
32
|
+
* await dialect.createTable('events', columns, {
|
|
33
|
+
* ...EngineRegistry.getTableOptions(Event),
|
|
34
|
+
* });
|
|
35
|
+
*/
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.EngineRegistry = void 0;
|
|
38
|
+
exports.getEngineOptions = getEngineOptions;
|
|
39
|
+
exports.Engine = Engine;
|
|
40
|
+
// ==================== Registry ====================
|
|
41
|
+
const ENGINE_OPTIONS_KEY = '__orm_engine_options__';
|
|
42
|
+
/**
|
|
43
|
+
* Read the @Engine options attached to a class.
|
|
44
|
+
* Returns undefined if the class has no @Engine decorator.
|
|
45
|
+
*/
|
|
46
|
+
function getEngineOptions(target) {
|
|
47
|
+
return target[ENGINE_OPTIONS_KEY];
|
|
48
|
+
}
|
|
49
|
+
// ==================== Decorator ====================
|
|
50
|
+
/**
|
|
51
|
+
* Annotate a Model subclass with dialect-specific storage-engine
|
|
52
|
+
* configuration.
|
|
53
|
+
*
|
|
54
|
+
* @Engine({ mergeTreeEngine: { type: 'AggregatingMergeTree' }, orderBy: ['id'] })
|
|
55
|
+
* class Metrics extends Model { ... }
|
|
56
|
+
*/
|
|
57
|
+
function Engine(options) {
|
|
58
|
+
return function (target) {
|
|
59
|
+
target[ENGINE_OPTIONS_KEY] = { ...options };
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// ==================== Public Registry API ====================
|
|
63
|
+
exports.EngineRegistry = {
|
|
64
|
+
/**
|
|
65
|
+
* Read the raw @Engine options attached to a class.
|
|
66
|
+
*/
|
|
67
|
+
getOptions(target) {
|
|
68
|
+
return getEngineOptions(target);
|
|
69
|
+
},
|
|
70
|
+
/**
|
|
71
|
+
* Returns the @Engine options in the shape `createTable()` expects to be
|
|
72
|
+
* spread into its `options` argument (ClickHouse reads `mergeTreeEngine`/
|
|
73
|
+
* `orderBy`/`ttl`/`partitionBy`/`projections` as loosely-typed extensions;
|
|
74
|
+
* MySQL/MariaDB read `engine` directly off `TableOptions`). Returns an
|
|
75
|
+
* empty object (not undefined) when the class has no @Engine decorator,
|
|
76
|
+
* so callers can always safely spread the result.
|
|
77
|
+
*/
|
|
78
|
+
getTableOptions(target) {
|
|
79
|
+
const opts = getEngineOptions(target);
|
|
80
|
+
if (!opts)
|
|
81
|
+
return {};
|
|
82
|
+
const tableOptions = {};
|
|
83
|
+
if (opts.engine !== undefined)
|
|
84
|
+
tableOptions.engine = opts.engine;
|
|
85
|
+
if (opts.mergeTreeEngine !== undefined)
|
|
86
|
+
tableOptions.mergeTreeEngine = opts.mergeTreeEngine;
|
|
87
|
+
if (opts.orderBy !== undefined)
|
|
88
|
+
tableOptions.orderBy = opts.orderBy;
|
|
89
|
+
if (opts.partitionBy !== undefined)
|
|
90
|
+
tableOptions.partitionBy = opts.partitionBy;
|
|
91
|
+
if (opts.ttl !== undefined)
|
|
92
|
+
tableOptions.ttl = opts.ttl;
|
|
93
|
+
if (opts.projections !== undefined)
|
|
94
|
+
tableOptions.projections = opts.projections;
|
|
95
|
+
return tableOptions;
|
|
96
|
+
},
|
|
97
|
+
};
|