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.
Files changed (310) hide show
  1. package/CHANGELOG.md +1111 -0
  2. package/LICENSE +21 -0
  3. package/README.md +573 -0
  4. package/dist/audit/history-query.js +180 -0
  5. package/dist/audit/index.js +23 -0
  6. package/dist/audit/logger.js +236 -0
  7. package/dist/cache/cache-manager.js +84 -0
  8. package/dist/cache/index.js +16 -0
  9. package/dist/cache/redis-cluster-cache.js +557 -0
  10. package/dist/cli.js +2200 -0
  11. package/dist/compliance/audit-trail.js +68 -0
  12. package/dist/compliance/backup-verification.js +685 -0
  13. package/dist/compliance/breach-detector.js +505 -0
  14. package/dist/compliance/consent-record.js +227 -0
  15. package/dist/compliance/consent-versioning.js +331 -0
  16. package/dist/compliance/cross-border-log.js +530 -0
  17. package/dist/compliance/data-classifier.js +258 -0
  18. package/dist/compliance/data-lineage.js +565 -0
  19. package/dist/compliance/data-masker.js +303 -0
  20. package/dist/compliance/data-portability.js +265 -0
  21. package/dist/compliance/data-retention.js +195 -0
  22. package/dist/compliance/dsar-workflow.js +342 -0
  23. package/dist/compliance/field-encryption.js +223 -0
  24. package/dist/compliance/immutable-record.js +148 -0
  25. package/dist/compliance/index.js +260 -0
  26. package/dist/compliance/privacy-impact-assessment.js +364 -0
  27. package/dist/compliance/pseudonymization.js +269 -0
  28. package/dist/compliance/query-firewall.js +1182 -0
  29. package/dist/compliance/rate-limiter.js +580 -0
  30. package/dist/compliance/right-to-erasure.js +117 -0
  31. package/dist/compliance/row-level-security.js +246 -0
  32. package/dist/compliance/security-decorator.js +574 -0
  33. package/dist/compliance/security-monitor.js +525 -0
  34. package/dist/compliance/sensitive-data-discovery.js +476 -0
  35. package/dist/compliance/session-isolation.js +472 -0
  36. package/dist/compliance/tls-enforcer.js +108 -0
  37. package/dist/compliance/worm-storage.js +712 -0
  38. package/dist/connection-manager.js +198 -0
  39. package/dist/connection-pool.js +519 -0
  40. package/dist/decorators/audit.js +136 -0
  41. package/dist/decorators/belongs-to-many.js +115 -0
  42. package/dist/decorators/belongs-to.js +115 -0
  43. package/dist/decorators/check.js +435 -0
  44. package/dist/decorators/collate.js +329 -0
  45. package/dist/decorators/comment.js +205 -0
  46. package/dist/decorators/database-settings.js +236 -0
  47. package/dist/decorators/default.js +244 -0
  48. package/dist/decorators/encryption.js +235 -0
  49. package/dist/decorators/engine.js +97 -0
  50. package/dist/decorators/fk-constraints.js +594 -0
  51. package/dist/decorators/foreign-table.js +136 -0
  52. package/dist/decorators/generated.js +274 -0
  53. package/dist/decorators/has-many.js +127 -0
  54. package/dist/decorators/has-one.js +116 -0
  55. package/dist/decorators/hstore.js +129 -0
  56. package/dist/decorators/index.js +355 -0
  57. package/dist/decorators/json-column.js +83 -0
  58. package/dist/decorators/jsonb.js +101 -0
  59. package/dist/decorators/orm-decorators.js +425 -0
  60. package/dist/decorators/permissions.js +294 -0
  61. package/dist/decorators/procedure.js +210 -0
  62. package/dist/decorators/query-options.js +556 -0
  63. package/dist/decorators/range.js +167 -0
  64. package/dist/decorators/set-column.js +83 -0
  65. package/dist/decorators/spatial.js +114 -0
  66. package/dist/decorators/storage.js +580 -0
  67. package/dist/decorators/timezone.js +512 -0
  68. package/dist/decorators/trigger.js +90 -0
  69. package/dist/decorators/uuid.js +135 -0
  70. package/dist/decorators/view.js +258 -0
  71. package/dist/diagrams/chen-diagram.js +354 -0
  72. package/dist/diagrams/class-diagram.js +384 -0
  73. package/dist/diagrams/dependency-diagram.js +432 -0
  74. package/dist/diagrams/er-diagram.js +605 -0
  75. package/dist/diagrams/flow-diagram.js +394 -0
  76. package/dist/diagrams/gantt-diagram.js +411 -0
  77. package/dist/diagrams/index-diagram.js +353 -0
  78. package/dist/diagrams/index.js +184 -0
  79. package/dist/diagrams/migration-diagram.js +316 -0
  80. package/dist/diagrams/model-diagram.js +616 -0
  81. package/dist/diagrams/package-diagram.js +376 -0
  82. package/dist/diagrams/palette.js +85 -0
  83. package/dist/diagrams/relational-diagram.js +455 -0
  84. package/dist/diagrams/schemadoc-diagram.js +309 -0
  85. package/dist/diagrams/sequence-diagram.js +307 -0
  86. package/dist/diagrams/state-diagram.js +344 -0
  87. package/dist/diagrams/svg-dom.js +111 -0
  88. package/dist/diagrams/tree-diagram.js +250 -0
  89. package/dist/dialects/clickhouse/index.js +1742 -0
  90. package/dist/dialects/cockroachdb/index.js +4677 -0
  91. package/dist/dialects/cratedb/index.js +441 -0
  92. package/dist/dialects/databricks/index.js +517 -0
  93. package/dist/dialects/db2/index.js +2225 -0
  94. package/dist/dialects/dialect.js +720 -0
  95. package/dist/dialects/duckdb/index.js +2014 -0
  96. package/dist/dialects/exasol/index.js +357 -0
  97. package/dist/dialects/firebird/index.js +495 -0
  98. package/dist/dialects/greenplum/index.js +457 -0
  99. package/dist/dialects/hana/index.js +1741 -0
  100. package/dist/dialects/mariadb/index.js +3590 -0
  101. package/dist/dialects/mssql/index.js +2296 -0
  102. package/dist/dialects/mysql/index.js +4058 -0
  103. package/dist/dialects/oracle/index.js +2999 -0
  104. package/dist/dialects/postgres/index.js +5319 -0
  105. package/dist/dialects/query-stream-helper.js +152 -0
  106. package/dist/dialects/questdb/index.js +433 -0
  107. package/dist/dialects/redshift/index.js +2277 -0
  108. package/dist/dialects/singlestore/index.js +354 -0
  109. package/dist/dialects/snowflake/index.js +2082 -0
  110. package/dist/dialects/spanner/index.js +1768 -0
  111. package/dist/dialects/sqlite/index.js +3382 -0
  112. package/dist/dialects/tidb/index.js +377 -0
  113. package/dist/dialects/timescaledb/index.js +164 -0
  114. package/dist/dialects/trino/index.js +420 -0
  115. package/dist/dialects/turso/index.js +227 -0
  116. package/dist/dialects/vertica/index.js +317 -0
  117. package/dist/dialects/yugabytedb/index.js +424 -0
  118. package/dist/errors/index.js +472 -0
  119. package/dist/errors/utils.js +340 -0
  120. package/dist/errors.js +21 -0
  121. package/dist/extensions/catalog/cloud-warehouse-features.js +207 -0
  122. package/dist/extensions/catalog/mssql-features.js +147 -0
  123. package/dist/extensions/catalog/mysql-mariadb-plugins.js +229 -0
  124. package/dist/extensions/catalog/oracle-db2-features.js +196 -0
  125. package/dist/extensions/catalog/postgres-extensions.js +516 -0
  126. package/dist/extensions/index.js +71 -0
  127. package/dist/extensions/types.js +13 -0
  128. package/dist/foreign-data.js +173 -0
  129. package/dist/hooks/hooks-manager.js +350 -0
  130. package/dist/hooks/index.js +37 -0
  131. package/dist/index.js +463 -0
  132. package/dist/logging.js +335 -0
  133. package/dist/migrations/index.js +40 -0
  134. package/dist/migrations/migration.js +196 -0
  135. package/dist/migrations/migrator.js +411 -0
  136. package/dist/migrations/prormmigration.js +275 -0
  137. package/dist/migrations/query-interface.js +435 -0
  138. package/dist/migrations/seeder.js +353 -0
  139. package/dist/models/associations.js +852 -0
  140. package/dist/models/constraints.js +288 -0
  141. package/dist/models/data-types.js +2518 -0
  142. package/dist/models/decorators.js +445 -0
  143. package/dist/models/index.js +33 -0
  144. package/dist/models/indexes.js +531 -0
  145. package/dist/models/methods.js +382 -0
  146. package/dist/models/model-manager.js +103 -0
  147. package/dist/models/model.js +5349 -0
  148. package/dist/models/operators.js +67 -0
  149. package/dist/models/scopes.js +189 -0
  150. package/dist/models/typescript-types.js +26 -0
  151. package/dist/nosql/aerospike/index.js +205 -0
  152. package/dist/nosql/allegrograph/index.js +169 -0
  153. package/dist/nosql/arangodb/index.js +364 -0
  154. package/dist/nosql/azure-blob/index.js +206 -0
  155. package/dist/nosql/beanstalkd/index.js +231 -0
  156. package/dist/nosql/beequeue/index.js +210 -0
  157. package/dist/nosql/bigchaindb/index.js +195 -0
  158. package/dist/nosql/bigtable/index.js +224 -0
  159. package/dist/nosql/blazegraph/index.js +173 -0
  160. package/dist/nosql/bullmq/index.js +191 -0
  161. package/dist/nosql/cassandra/index.js +174 -0
  162. package/dist/nosql/chroma/index.js +190 -0
  163. package/dist/nosql/cloudflare-kv/index.js +220 -0
  164. package/dist/nosql/coherence/index.js +200 -0
  165. package/dist/nosql/cosmosdb/index.js +157 -0
  166. package/dist/nosql/couchbase/index.js +213 -0
  167. package/dist/nosql/dax/index.js +212 -0
  168. package/dist/nosql/deno-kv/index.js +206 -0
  169. package/dist/nosql/dgraph/index.js +171 -0
  170. package/dist/nosql/doris/index.js +169 -0
  171. package/dist/nosql/druid/index.js +162 -0
  172. package/dist/nosql/dynamodb/index.js +937 -0
  173. package/dist/nosql/elasticsearch/index.js +377 -0
  174. package/dist/nosql/etcd/index.js +502 -0
  175. package/dist/nosql/eventhubs/index.js +213 -0
  176. package/dist/nosql/eventstore/index.js +254 -0
  177. package/dist/nosql/faunadb/index.js +188 -0
  178. package/dist/nosql/firestore/index.js +177 -0
  179. package/dist/nosql/fluree/index.js +148 -0
  180. package/dist/nosql/fuseki/index.js +170 -0
  181. package/dist/nosql/gcs/index.js +172 -0
  182. package/dist/nosql/gearman/index.js +160 -0
  183. package/dist/nosql/geode/index.js +196 -0
  184. package/dist/nosql/graphdb/index.js +169 -0
  185. package/dist/nosql/graylog/index.js +188 -0
  186. package/dist/nosql/gridgain/index.js +171 -0
  187. package/dist/nosql/hazelcast/index.js +162 -0
  188. package/dist/nosql/hbase/index.js +230 -0
  189. package/dist/nosql/ignite/index.js +173 -0
  190. package/dist/nosql/immudb/index.js +184 -0
  191. package/dist/nosql/index.js +232 -0
  192. package/dist/nosql/infinispan/index.js +200 -0
  193. package/dist/nosql/influxdb/index.js +0 -0
  194. package/dist/nosql/kafka/index.js +234 -0
  195. package/dist/nosql/keyspaces/index.js +189 -0
  196. package/dist/nosql/kinesis/index.js +253 -0
  197. package/dist/nosql/leveldb/index.js +153 -0
  198. package/dist/nosql/lmdb/index.js +160 -0
  199. package/dist/nosql/loki/index.js +201 -0
  200. package/dist/nosql/marklogic/index.js +204 -0
  201. package/dist/nosql/materialize/index.js +145 -0
  202. package/dist/nosql/meilisearch/index.js +154 -0
  203. package/dist/nosql/memcached/index.js +223 -0
  204. package/dist/nosql/milvus/index.js +410 -0
  205. package/dist/nosql/minio/index.js +265 -0
  206. package/dist/nosql/momento/index.js +179 -0
  207. package/dist/nosql/mongodb/index.js +461 -0
  208. package/dist/nosql/nats/index.js +247 -0
  209. package/dist/nosql/nedb/index.js +164 -0
  210. package/dist/nosql/neo4j/index.js +450 -0
  211. package/dist/nosql/neptune/index.js +470 -0
  212. package/dist/nosql/nsq/index.js +200 -0
  213. package/dist/nosql/opensearch/index.js +186 -0
  214. package/dist/nosql/orientdb/index.js +175 -0
  215. package/dist/nosql/papertrail/index.js +200 -0
  216. package/dist/nosql/pinecone/index.js +0 -0
  217. package/dist/nosql/pinot/index.js +133 -0
  218. package/dist/nosql/pouchdb/index.js +172 -0
  219. package/dist/nosql/prometheus/index.js +174 -0
  220. package/dist/nosql/provendb/index.js +147 -0
  221. package/dist/nosql/pubsub/index.js +187 -0
  222. package/dist/nosql/pulsar/index.js +232 -0
  223. package/dist/nosql/qdrant/index.js +295 -0
  224. package/dist/nosql/qldb/index.js +192 -0
  225. package/dist/nosql/r2/index.js +281 -0
  226. package/dist/nosql/rabbitmq/index.js +237 -0
  227. package/dist/nosql/ravendb/index.js +175 -0
  228. package/dist/nosql/redis/index.js +607 -0
  229. package/dist/nosql/redpanda/index.js +237 -0
  230. package/dist/nosql/resque/index.js +203 -0
  231. package/dist/nosql/rethinkdb/index.js +232 -0
  232. package/dist/nosql/rocksdb/index.js +152 -0
  233. package/dist/nosql/rockset/index.js +126 -0
  234. package/dist/nosql/s3/index.js +298 -0
  235. package/dist/nosql/scylladb/index.js +178 -0
  236. package/dist/nosql/signoz/index.js +227 -0
  237. package/dist/nosql/sns/index.js +201 -0
  238. package/dist/nosql/solr/index.js +231 -0
  239. package/dist/nosql/splunk/index.js +227 -0
  240. package/dist/nosql/sqs/index.js +246 -0
  241. package/dist/nosql/stardog/index.js +169 -0
  242. package/dist/nosql/starrocks/index.js +170 -0
  243. package/dist/nosql/store.js +2 -0
  244. package/dist/nosql/sumologic/index.js +213 -0
  245. package/dist/nosql/surrealdb/index.js +179 -0
  246. package/dist/nosql/terminusdb/index.js +151 -0
  247. package/dist/nosql/tigergraph/index.js +469 -0
  248. package/dist/nosql/typesense/index.js +148 -0
  249. package/dist/nosql/unqlite/index.js +157 -0
  250. package/dist/nosql/upstash/index.js +167 -0
  251. package/dist/nosql/vercel-kv/index.js +166 -0
  252. package/dist/nosql/victoriametrics/index.js +213 -0
  253. package/dist/nosql/virtuoso/index.js +169 -0
  254. package/dist/nosql/weaviate/index.js +276 -0
  255. package/dist/operators/index.js +108 -0
  256. package/dist/operators.js +2690 -0
  257. package/dist/prisma-migrate/index.js +38 -0
  258. package/dist/prisma-migrate/migration-generator.js +125 -0
  259. package/dist/prisma-migrate/model-generator.js +172 -0
  260. package/dist/prisma-migrate/relations.js +100 -0
  261. package/dist/prisma-migrate/schema-parser.js +167 -0
  262. package/dist/prisma-migrate/type-mapper.js +40 -0
  263. package/dist/prorm.js +6832 -0
  264. package/dist/query-builders/cte-builder.js +80 -0
  265. package/dist/query-builders/functions/aggregate.js +390 -0
  266. package/dist/query-builders/functions/conditional.js +503 -0
  267. package/dist/query-builders/functions/datetime.js +695 -0
  268. package/dist/query-builders/functions/fulltext.js +439 -0
  269. package/dist/query-builders/functions/index.js +93 -0
  270. package/dist/query-builders/functions/json.js +427 -0
  271. package/dist/query-builders/functions/math.js +399 -0
  272. package/dist/query-builders/functions/string.js +518 -0
  273. package/dist/query-builders/functions/window.js +328 -0
  274. package/dist/query-builders/include-builder.js +323 -0
  275. package/dist/query-builders/index-expression-builder.js +242 -0
  276. package/dist/query-builders/index.js +161 -0
  277. package/dist/query-builders/insert-builder.js +164 -0
  278. package/dist/query-builders/model-helpers.js +38 -0
  279. package/dist/query-builders/order-limit-builder.js +239 -0
  280. package/dist/query-builders/sql-compiler.js +2040 -0
  281. package/dist/query-builders/subquery-builder.js +152 -0
  282. package/dist/query-builders/update-builder.js +182 -0
  283. package/dist/query-builders/view-builder.js +218 -0
  284. package/dist/query-builders/where-builder.js +1532 -0
  285. package/dist/query-interface.js +622 -0
  286. package/dist/query-optimizers/batch-optimizer.js +426 -0
  287. package/dist/query-optimizers/explain-plans.js +415 -0
  288. package/dist/query-optimizers/index.js +51 -0
  289. package/dist/query-optimizers/prepared-statement-cache.js +423 -0
  290. package/dist/query-optimizers/query-hints.js +438 -0
  291. package/dist/query-optimizers/query-optimizer.js +278 -0
  292. package/dist/query-optimizers/slow-query-logger.js +271 -0
  293. package/dist/replica-manager.js +507 -0
  294. package/dist/schema/index.js +15 -0
  295. package/dist/schema/migration-generator.js +374 -0
  296. package/dist/schema/schema-differ.js +549 -0
  297. package/dist/schema/types.js +6 -0
  298. package/dist/sql-constants.js +300 -0
  299. package/dist/sqlite-advanced.js +1047 -0
  300. package/dist/streams/index.js +12 -0
  301. package/dist/streams/transforms.js +178 -0
  302. package/dist/transaction.js +456 -0
  303. package/dist/types/index.js +180 -0
  304. package/dist/user-management.js +127 -0
  305. package/dist/utils/date.js +300 -0
  306. package/dist/utils/index.js +1079 -0
  307. package/dist/utils/string.js +161 -0
  308. package/dist/validators/index.js +19 -0
  309. package/dist/validators/validator.js +911 -0
  310. package/package.json +190 -0
@@ -0,0 +1,472 @@
1
+ "use strict";
2
+ /**
3
+ * SessionIsolation
4
+ *
5
+ * Provides multi-tenant data isolation using AsyncLocalStorage for automatic
6
+ * tenant context propagation. Implements tenant-specific encryption keys,
7
+ * session management, and tenant-aware audit logging.
8
+ *
9
+ * Covers compliance frameworks including:
10
+ * ISO 27017, FedRAMP, CPRA, GDPR, HIPAA, SOC 2, NIST SP 800-53, and more.
11
+ *
12
+ * Usage:
13
+ * // At app startup, initialize session isolation:
14
+ * SessionIsolation.init({ sessionTimeout: 30 * 60 * 1000 });
15
+ *
16
+ * // Register tenant encryption keys:
17
+ * SessionIsolation.registerTenantKey('tenant-123', 'encryption-key-32-bytes');
18
+ *
19
+ * // In your authentication middleware:
20
+ * SessionIsolation.setTenant({
21
+ * tenantId: 'tenant-123',
22
+ * sessionId: 'session-abc',
23
+ * userId: 'user-456',
24
+ * roles: ['admin']
25
+ * });
26
+ *
27
+ * // Run operations in a specific tenant context:
28
+ * const result = SessionIsolation.runInTenant('tenant-123', () => {
29
+ * return db.User.findAll();
30
+ * });
31
+ *
32
+ * // Use the decorator on models:
33
+ * @TenantIsolation({ tenantId: 'tenantId', auditEnabled: true })
34
+ * class Document extends Model { ... }
35
+ */
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.TenantIsolationManager = exports.SessionIsolation = void 0;
38
+ exports.TenantIsolation = TenantIsolation;
39
+ exports.getTenantIsolationOptions = getTenantIsolationOptions;
40
+ exports.sessionIsolationMiddleware = sessionIsolationMiddleware;
41
+ exports.getTenantAuditMetadata = getTenantAuditMetadata;
42
+ const async_hooks_1 = require("async_hooks");
43
+ const operators_1 = require("../models/operators");
44
+ // ==================== Symbol Keys for Metadata ====================
45
+ const TENANT_CONTEXT_KEY = Symbol('tenantContext');
46
+ const SESSION_ISOLATION_DATA = Symbol('sessionIsolationData');
47
+ // ==================== In-Memory Storage ====================
48
+ class InMemorySessionStore {
49
+ constructor() {
50
+ this.sessions = new Map();
51
+ }
52
+ async get(sessionId) {
53
+ return this.sessions.get(sessionId) ?? null;
54
+ }
55
+ async set(sessionId, info) {
56
+ this.sessions.set(sessionId, info);
57
+ }
58
+ async delete(sessionId) {
59
+ this.sessions.delete(sessionId);
60
+ }
61
+ async getByTenant(tenantId) {
62
+ return [...this.sessions.values()].filter((s) => s.tenantId === tenantId);
63
+ }
64
+ async getByUser(userId) {
65
+ return [...this.sessions.values()].filter((s) => s.userId === userId);
66
+ }
67
+ async cleanup(expiredBefore) {
68
+ let count = 0;
69
+ for (const [id, session] of this.sessions) {
70
+ if (session.expiresAt < expiredBefore) {
71
+ this.sessions.delete(id);
72
+ count++;
73
+ }
74
+ }
75
+ return count;
76
+ }
77
+ }
78
+ class InMemoryKeyStore {
79
+ constructor() {
80
+ this.keys = new Map();
81
+ }
82
+ async get(tenantId) {
83
+ return this.keys.get(tenantId) ?? null;
84
+ }
85
+ async set(tenantId, key) {
86
+ this.keys.set(tenantId, key);
87
+ }
88
+ async delete(tenantId) {
89
+ this.keys.delete(tenantId);
90
+ }
91
+ async getAll() {
92
+ return [...this.keys.values()];
93
+ }
94
+ }
95
+ // ==================== AsyncLocalStorage ====================
96
+ const tenantContextStore = new async_hooks_1.AsyncLocalStorage();
97
+ // Global state
98
+ let isInitialized = false;
99
+ let options = {};
100
+ let sessionStore;
101
+ let keyStore;
102
+ let cleanupTimer = null;
103
+ // ==================== SessionIsolation Class ====================
104
+ class SessionIsolation {
105
+ /**
106
+ * Initialize session isolation. Call once at application startup.
107
+ *
108
+ * @param opts - Configuration options
109
+ */
110
+ static init(opts = {}) {
111
+ if (isInitialized) {
112
+ console.warn('[SessionIsolation] Already initialized. Call reset() first to reinitialize.');
113
+ return;
114
+ }
115
+ options = {
116
+ sessionTimeout: 30 * 60 * 1000, // 30 minutes
117
+ maxSessionsPerTenant: undefined,
118
+ maxSessionsPerUser: 5,
119
+ autoCleanup: true,
120
+ cleanupInterval: 5 * 60 * 1000, // 5 minutes
121
+ auditEnabled: true,
122
+ ...opts,
123
+ };
124
+ // Initialize stores
125
+ sessionStore = options.sessionStore ?? new InMemorySessionStore();
126
+ keyStore = options.keyStore ?? new InMemoryKeyStore();
127
+ // Start cleanup timer
128
+ if (options.autoCleanup) {
129
+ cleanupTimer = setInterval(() => {
130
+ SessionIsolation.cleanupExpiredSessions().catch((err) => {
131
+ console.error('[SessionIsolation] Cleanup error:', err);
132
+ });
133
+ }, options.cleanupInterval);
134
+ }
135
+ isInitialized = true;
136
+ }
137
+ /**
138
+ * Reset the session isolation system. Useful for testing.
139
+ */
140
+ static reset() {
141
+ if (cleanupTimer) {
142
+ clearInterval(cleanupTimer);
143
+ cleanupTimer = null;
144
+ }
145
+ isInitialized = false;
146
+ options = {};
147
+ }
148
+ /**
149
+ * Set the current tenant context for this request/operation.
150
+ *
151
+ * @param ctx - The tenant context
152
+ */
153
+ static setTenant(ctx) {
154
+ if (!isInitialized) {
155
+ throw new Error('[SessionIsolation] Not initialized. Call init() first.');
156
+ }
157
+ // enterWith() propagates the context for the remainder of the current
158
+ // synchronous execution and any asynchronous operations chained from
159
+ // it, without requiring the caller to wrap downstream work in a
160
+ // callback. For strict per-request isolation under concurrent load,
161
+ // prefer runInTenant() or sessionIsolationMiddleware(), which wrap the
162
+ // actual downstream work in tenantContextStore.run() directly.
163
+ tenantContextStore.enterWith(ctx);
164
+ }
165
+ /**
166
+ * Get the current tenant context.
167
+ *
168
+ * @returns The current tenant context, or undefined if not set
169
+ */
170
+ static getTenant() {
171
+ return tenantContextStore.getStore();
172
+ }
173
+ /**
174
+ * Clear the current tenant context.
175
+ */
176
+ static clearTenant() {
177
+ tenantContextStore.enterWith(undefined);
178
+ }
179
+ /**
180
+ * Run a function within a specific tenant context.
181
+ *
182
+ * @param tenantId - The tenant ID to run in
183
+ * @param fn - The function to execute
184
+ * @returns The result of the function
185
+ */
186
+ static runInTenant(tenantId, fn) {
187
+ const ctx = {
188
+ tenantId,
189
+ sessionId: `isolated-${Date.now()}`,
190
+ };
191
+ return tenantContextStore.run(ctx, fn);
192
+ }
193
+ /**
194
+ * Register an encryption key for a tenant.
195
+ *
196
+ * @param tenantId - The tenant ID
197
+ * @param key - The encryption key (32 bytes recommended for AES-256)
198
+ * @param keyVersion - Optional key version for rotation
199
+ */
200
+ static async registerTenantKey(tenantId, key, keyVersion) {
201
+ if (!isInitialized) {
202
+ throw new Error('[SessionIsolation] Not initialized. Call init() first.');
203
+ }
204
+ const tenantKey = {
205
+ tenantId,
206
+ key,
207
+ keyVersion,
208
+ createdAt: new Date(),
209
+ };
210
+ await keyStore.set(tenantId, tenantKey);
211
+ }
212
+ /**
213
+ * Get the encryption key for a tenant.
214
+ *
215
+ * @param tenantId - The tenant ID
216
+ * @returns The tenant key, or undefined if not found
217
+ */
218
+ static async getTenantKey(tenantId) {
219
+ if (!isInitialized) {
220
+ throw new Error('[SessionIsolation] Not initialized. Call init() first.');
221
+ }
222
+ return (await keyStore.get(tenantId)) ?? undefined;
223
+ }
224
+ /**
225
+ * Validate a session.
226
+ *
227
+ * @param sessionId - The session ID to validate
228
+ * @returns SessionInfo if valid, null if invalid/expired
229
+ */
230
+ static async validateSession(sessionId) {
231
+ if (!isInitialized) {
232
+ throw new Error('[SessionIsolation] Not initialized. Call init() first.');
233
+ }
234
+ const session = await sessionStore.get(sessionId);
235
+ if (!session) {
236
+ return null;
237
+ }
238
+ // Check if expired
239
+ if (new Date() > session.expiresAt) {
240
+ await sessionStore.delete(sessionId);
241
+ return null;
242
+ }
243
+ // Check if inactive
244
+ if (!session.isActive) {
245
+ return null;
246
+ }
247
+ // Update last activity
248
+ session.lastActivityAt = new Date();
249
+ await sessionStore.set(sessionId, session);
250
+ return session;
251
+ }
252
+ /**
253
+ * Manually expire a session.
254
+ *
255
+ * @param sessionId - The session ID to expire
256
+ */
257
+ static async expireSession(sessionId) {
258
+ if (!isInitialized) {
259
+ throw new Error('[SessionIsolation] Not initialized. Call init() first.');
260
+ }
261
+ await sessionStore.delete(sessionId);
262
+ }
263
+ /**
264
+ * Create a new session for a tenant.
265
+ *
266
+ * @param tenantId - The tenant ID
267
+ * @param userId - The user ID
268
+ * @param options - Additional session options
269
+ * @returns The created session info
270
+ */
271
+ static async createSession(tenantId, userId, options) {
272
+ if (!isInitialized) {
273
+ throw new Error('[SessionIsolation] Not initialized. Call init() first.');
274
+ }
275
+ const sessionId = `session-${tenantId}-${userId}-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
276
+ const now = new Date();
277
+ const expiresAt = options?.customExpiresAt ?? new Date(now.getTime() + SessionIsolation.getSessionTimeout());
278
+ const sessionInfo = {
279
+ sessionId,
280
+ tenantId,
281
+ userId,
282
+ createdAt: now,
283
+ expiresAt,
284
+ lastActivityAt: now,
285
+ isActive: true,
286
+ ipAddress: options?.ipAddress,
287
+ userAgent: options?.userAgent,
288
+ };
289
+ await sessionStore.set(sessionId, sessionInfo);
290
+ return sessionInfo;
291
+ }
292
+ /**
293
+ * Get all active sessions for a tenant.
294
+ *
295
+ * @param tenantId - The tenant ID
296
+ * @returns Array of session info
297
+ */
298
+ static async getTenantSessions(tenantId) {
299
+ if (!isInitialized) {
300
+ throw new Error('[SessionIsolation] Not initialized. Call init() first.');
301
+ }
302
+ return sessionStore.getByTenant(tenantId);
303
+ }
304
+ /**
305
+ * Get all active sessions for a user.
306
+ *
307
+ * @param userId - The user ID
308
+ * @returns Array of session info
309
+ */
310
+ static async getUserSessions(userId) {
311
+ if (!isInitialized) {
312
+ throw new Error('[SessionIsolation] Not initialized. Call init() first.');
313
+ }
314
+ return sessionStore.getByUser(userId);
315
+ }
316
+ /**
317
+ * Clean up all expired sessions.
318
+ *
319
+ * @returns Number of sessions cleaned up
320
+ */
321
+ static async cleanupExpiredSessions() {
322
+ if (!isInitialized) {
323
+ throw new Error('[SessionIsolation] Not initialized. Call init() first.');
324
+ }
325
+ const expiredBefore = new Date();
326
+ return sessionStore.cleanup(expiredBefore);
327
+ }
328
+ /**
329
+ * Get the current session timeout value.
330
+ *
331
+ * @returns Timeout in milliseconds
332
+ */
333
+ static getSessionTimeout() {
334
+ return options.sessionTimeout ?? 30 * 60 * 1000;
335
+ }
336
+ /**
337
+ * Check if session isolation is initialized.
338
+ *
339
+ * @returns True if initialized
340
+ */
341
+ static isReady() {
342
+ return isInitialized;
343
+ }
344
+ }
345
+ exports.SessionIsolation = SessionIsolation;
346
+ // ==================== Decorator ====================
347
+ /**
348
+ * Decorator to apply tenant isolation to a model.
349
+ *
350
+ * @TenantIsolation({ tenantId: 'tenantId', auditEnabled: true })
351
+ * class Document extends Model {
352
+ * tenantId: string;
353
+ * title: string;
354
+ * }
355
+ *
356
+ * @param options - Tenant isolation options
357
+ */
358
+ function TenantIsolation(options) {
359
+ return function (target) {
360
+ // Store decorator metadata on the model
361
+ Reflect.defineMetadata(TENANT_CONTEXT_KEY, options, target);
362
+ // Add tenant context to query hooks if model has beforeFind
363
+ if (target.prototype?.beforeFind) {
364
+ target.addHook('beforeFind', 'orm:tenantFilter', (opts) => {
365
+ const ctx = SessionIsolation.getTenant();
366
+ if (!ctx)
367
+ return;
368
+ // Add tenant filter to where clause
369
+ const tenantField = options.tenantId;
370
+ if (tenantField && ctx.tenantId) {
371
+ opts.where = opts.where
372
+ ? { [operators_1.Op.and]: [opts.where, { [tenantField]: ctx.tenantId }] }
373
+ : { [tenantField]: ctx.tenantId };
374
+ }
375
+ // Add role check if required
376
+ if (options.requiredRoles?.length) {
377
+ const userRoles = ctx.roles ?? [];
378
+ const hasRequiredRole = options.requiredRoles.some((r) => userRoles.includes(r));
379
+ if (!hasRequiredRole) {
380
+ throw new Error(`Access denied. Required roles: ${options.requiredRoles.join(', ')}`);
381
+ }
382
+ }
383
+ });
384
+ }
385
+ };
386
+ }
387
+ /**
388
+ * Get tenant isolation options from a decorated model.
389
+ *
390
+ * @param model - The model class
391
+ * @returns The decorator options, or undefined if not decorated
392
+ */
393
+ function getTenantIsolationOptions(model) {
394
+ return Reflect.getMetadata(TENANT_CONTEXT_KEY, model);
395
+ }
396
+ // ==================== Middleware Integration ====================
397
+ /**
398
+ * Express/Koa middleware to automatically set tenant context from request.
399
+ *
400
+ * app.use(authMiddleware); // sets req.user
401
+ * app.use(sessionIsolationMiddleware);
402
+ *
403
+ * @param options - Middleware options
404
+ */
405
+ function sessionIsolationMiddleware(req, res, next) {
406
+ if (!isInitialized) {
407
+ console.warn('[SessionIsolation] Not initialized. Call init() first.');
408
+ next();
409
+ return;
410
+ }
411
+ // Try to get tenant info from request (e.g., from auth middleware)
412
+ const tenantId = req.tenantId ?? req.user?.tenantId ?? req.headers['x-tenant-id'];
413
+ const sessionId = req.sessionId ?? req.headers['x-session-id'];
414
+ const userId = req.user?.id ?? req.userId;
415
+ if (tenantId) {
416
+ const ctx = {
417
+ tenantId,
418
+ sessionId,
419
+ userId,
420
+ roles: req.user?.roles ?? [],
421
+ metadata: {
422
+ ipAddress: req.ip ?? req.connection?.remoteAddress,
423
+ userAgent: req.headers['user-agent'],
424
+ },
425
+ };
426
+ // Wrap the downstream middleware/handler chain in the ALS context so
427
+ // the tenant context actually propagates to whatever `next()`
428
+ // triggers (including further async work), rather than being
429
+ // set-and-discarded before the real request handling runs.
430
+ tenantContextStore.run(ctx, next);
431
+ return;
432
+ }
433
+ next();
434
+ }
435
+ /**
436
+ * Get tenant-aware audit metadata for the current context.
437
+ * Use this to include tenant information in audit logs.
438
+ *
439
+ * @returns Audit metadata object
440
+ */
441
+ function getTenantAuditMetadata() {
442
+ const ctx = SessionIsolation.getTenant();
443
+ if (!ctx) {
444
+ return {};
445
+ }
446
+ return {
447
+ tenantId: ctx.tenantId,
448
+ sessionId: ctx.sessionId,
449
+ userId: ctx.userId,
450
+ roles: ctx.roles,
451
+ timestamp: new Date().toISOString(),
452
+ };
453
+ }
454
+ // ==================== Class Export ====================
455
+ exports.TenantIsolationManager = {
456
+ init: SessionIsolation.init,
457
+ reset: SessionIsolation.reset,
458
+ setTenant: SessionIsolation.setTenant,
459
+ getTenant: SessionIsolation.getTenant,
460
+ clearTenant: SessionIsolation.clearTenant,
461
+ runInTenant: SessionIsolation.runInTenant,
462
+ registerTenantKey: SessionIsolation.registerTenantKey,
463
+ getTenantKey: SessionIsolation.getTenantKey,
464
+ validateSession: SessionIsolation.validateSession,
465
+ expireSession: SessionIsolation.expireSession,
466
+ createSession: SessionIsolation.createSession,
467
+ getTenantSessions: SessionIsolation.getTenantSessions,
468
+ getUserSessions: SessionIsolation.getUserSessions,
469
+ cleanupExpiredSessions: SessionIsolation.cleanupExpiredSessions,
470
+ getSessionTimeout: SessionIsolation.getSessionTimeout,
471
+ isReady: SessionIsolation.isReady,
472
+ };
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ /**
3
+ * TLSEnforcer
4
+ *
5
+ * Validates and enforces TLS/SSL on database connections at startup.
6
+ * Throws a descriptive error if the connection is not encrypted,
7
+ * preventing insecure deployments from reaching production.
8
+ *
9
+ * Covers ~54 compliance frameworks including:
10
+ * GDPR Art 32, HIPAA, PCI DSS, NIST SP 800-53, FedRAMP, SOC 2,
11
+ * ISO 27001, NYDFS 23 NYCRR 500, HITRUST, NIS2, DORA, and more.
12
+ *
13
+ * Usage:
14
+ * const prorm = new Prorm({ ... });
15
+ * TLSEnforcer.enforce(prorm); // strict
16
+ * TLSEnforcer.enforce(prorm, { mode: 'warn' }); // log only
17
+ * TLSEnforcer.enforce(prorm, { allowLocal: true }); // skip for localhost
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.TLSEnforcer = void 0;
21
+ const LOCAL_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '0.0.0.0']);
22
+ function isLocalHost(host) {
23
+ return LOCAL_HOSTS.has((host ?? '').toLowerCase());
24
+ }
25
+ function extractConnectionOptions(prorm) {
26
+ const opts = prorm?.options ?? prorm?.config ?? {};
27
+ return {
28
+ dialect: opts.dialect ?? 'unknown',
29
+ host: opts.host,
30
+ dialectOptions: opts.dialectOptions ?? {},
31
+ };
32
+ }
33
+ /**
34
+ * Determine whether the connection options specify TLS/SSL.
35
+ */
36
+ function hasTLSConfigured(dialect, dialectOptions) {
37
+ // PostgreSQL / MariaDB / MySQL: ssl option in dialectOptions
38
+ if (dialectOptions?.ssl)
39
+ return true;
40
+ // MSSQL: encrypt in dialectOptions.options
41
+ if (dialectOptions?.options?.encrypt === true)
42
+ return true;
43
+ return false;
44
+ }
45
+ class TLSEnforcer {
46
+ /**
47
+ * Enforce TLS on the given Prorm instance.
48
+ * Call immediately after constructing the Prorm instance.
49
+ *
50
+ * @param prorm - The Prorm instance to inspect
51
+ * @param options - Enforcement mode and options
52
+ */
53
+ static enforce(prorm, options = {}) {
54
+ const { mode = 'strict', allowLocal = false, logger = console.warn } = options;
55
+ if (mode === 'off')
56
+ return;
57
+ const { dialect, host, dialectOptions } = extractConnectionOptions(prorm);
58
+ if (allowLocal && isLocalHost(host))
59
+ return;
60
+ const tlsOk = hasTLSConfigured(dialect, dialectOptions);
61
+ if (!tlsOk) {
62
+ const message = `[TLSEnforcer] Database connection to "${host ?? 'unknown'}" (dialect: ${dialect}) ` +
63
+ `does not have SSL/TLS configured. ` +
64
+ `Set dialectOptions.ssl (PostgreSQL/MySQL/MariaDB) or dialectOptions.options.encrypt=true (MSSQL). ` +
65
+ `This is required by GDPR Art 32, HIPAA §164.312(e), PCI DSS Req 4, and many other frameworks.`;
66
+ if (mode === 'strict') {
67
+ throw new Error(message);
68
+ }
69
+ else {
70
+ logger(message);
71
+ }
72
+ }
73
+ }
74
+ /**
75
+ * Return recommended SSL dialectOptions for a given dialect.
76
+ * Use this as a starting point when configuring your connection.
77
+ */
78
+ static recommendedOptions(dialect, options = {}) {
79
+ const { rejectUnauthorized = true, ca } = options;
80
+ switch (dialect) {
81
+ case 'postgres':
82
+ return {
83
+ ssl: {
84
+ require: true,
85
+ rejectUnauthorized,
86
+ ...(ca ? { ca } : {}),
87
+ },
88
+ };
89
+ case 'mysql':
90
+ case 'mariadb':
91
+ return {
92
+ ssl: {
93
+ require: true,
94
+ rejectUnauthorized,
95
+ ...(ca ? { ca } : {}),
96
+ },
97
+ };
98
+ case 'mssql':
99
+ return {
100
+ options: {
101
+ encrypt: true,
102
+ trustServerCertificate: !rejectUnauthorized,
103
+ },
104
+ };
105
+ }
106
+ }
107
+ }
108
+ exports.TLSEnforcer = TLSEnforcer;