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,195 @@
1
+ "use strict";
2
+ /**
3
+ * DataRetentionPolicy
4
+ *
5
+ * Attach a mandatory data retention period to any model.
6
+ * Records older than the policy window are purged (deleted or anonymized)
7
+ * when DataRetentionPolicy.purge() is called.
8
+ *
9
+ * Covers ~80 compliance frameworks including:
10
+ * GDPR (storage limitation), SOX (7yr), MiFID II (5yr), SEC 17a-4,
11
+ * FINRA, HIPAA, PIPEDA, LGPD, APPI, PDPA variants, and more.
12
+ *
13
+ * Usage:
14
+ * // Set a 7-year retention on financial records:
15
+ * DataRetentionPolicy.set(FinancialRecord, {
16
+ * maxAge: { years: 7 },
17
+ * field: 'createdAt',
18
+ * onExpiry: 'delete',
19
+ * });
20
+ *
21
+ * // Purge expired records (call from a scheduled job):
22
+ * const { deleted } = await DataRetentionPolicy.purge(FinancialRecord, prorm);
23
+ *
24
+ * // Or purge all registered models:
25
+ * const report = await DataRetentionPolicy.purgeAll(prorm);
26
+ */
27
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
28
+ if (k2 === undefined) k2 = k;
29
+ var desc = Object.getOwnPropertyDescriptor(m, k);
30
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
31
+ desc = { enumerable: true, get: function() { return m[k]; } };
32
+ }
33
+ Object.defineProperty(o, k2, desc);
34
+ }) : (function(o, m, k, k2) {
35
+ if (k2 === undefined) k2 = k;
36
+ o[k2] = m[k];
37
+ }));
38
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
39
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
40
+ }) : function(o, v) {
41
+ o["default"] = v;
42
+ });
43
+ var __importStar = (this && this.__importStar) || (function () {
44
+ var ownKeys = function(o) {
45
+ ownKeys = Object.getOwnPropertyNames || function (o) {
46
+ var ar = [];
47
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
48
+ return ar;
49
+ };
50
+ return ownKeys(o);
51
+ };
52
+ return function (mod) {
53
+ if (mod && mod.__esModule) return mod;
54
+ var result = {};
55
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
56
+ __setModuleDefault(result, mod);
57
+ return result;
58
+ };
59
+ })();
60
+ Object.defineProperty(exports, "__esModule", { value: true });
61
+ exports.DataRetentionPolicy = void 0;
62
+ // ==================== Duration Math ====================
63
+ function durationToMs(d) {
64
+ if (d.ms != null)
65
+ return d.ms;
66
+ let ms = 0;
67
+ if (d.hours)
68
+ ms += d.hours * 60 * 60 * 1000;
69
+ if (d.days)
70
+ ms += d.days * 24 * 60 * 60 * 1000;
71
+ if (d.months)
72
+ ms += d.months * 30 * 24 * 60 * 60 * 1000;
73
+ if (d.years)
74
+ ms += d.years * 365 * 24 * 60 * 60 * 1000;
75
+ return ms;
76
+ }
77
+ const registry = new Map();
78
+ // ==================== DataRetentionPolicy ====================
79
+ class DataRetentionPolicy {
80
+ /**
81
+ * Register a retention policy on a model.
82
+ * This does not start any background process — call purge() on a schedule.
83
+ *
84
+ * @param model - The model class
85
+ * @param options - Retention configuration
86
+ */
87
+ static set(model, options) {
88
+ registry.set(model, {
89
+ model,
90
+ options: {
91
+ field: options.field ?? 'createdAt',
92
+ onExpiry: options.onExpiry ?? 'delete',
93
+ preserveFields: options.preserveFields ?? [],
94
+ maxAge: options.maxAge,
95
+ },
96
+ });
97
+ }
98
+ /**
99
+ * Remove the retention policy from a model.
100
+ */
101
+ static unset(model) {
102
+ registry.delete(model);
103
+ }
104
+ /**
105
+ * Get the registered policy for a model, or undefined.
106
+ */
107
+ static get(model) {
108
+ return registry.get(model)?.options;
109
+ }
110
+ /**
111
+ * Purge expired records for a specific model.
112
+ *
113
+ * @param model - The model class
114
+ * @param prorm - The Prorm instance (for Op support)
115
+ * @returns Number of records deleted or anonymized
116
+ */
117
+ static async purge(model, prorm) {
118
+ const entry = registry.get(model);
119
+ if (!entry) {
120
+ throw new Error(`[DataRetentionPolicy] No retention policy registered for model "${model.name ?? model}". ` +
121
+ `Call DataRetentionPolicy.set(Model, options) first.`);
122
+ }
123
+ const { options } = entry;
124
+ const cutoff = new Date(Date.now() - durationToMs(options.maxAge));
125
+ const field = options.field;
126
+ // Build a where clause: field < cutoff
127
+ // Use prorm Op if available, otherwise fall back to raw object key
128
+ let where;
129
+ try {
130
+ const Op = prorm?.Op ?? (await Promise.resolve().then(() => __importStar(require('../models/operators'))).then((m) => m.Op));
131
+ where = { [field]: { [Op.lt]: cutoff } };
132
+ }
133
+ catch {
134
+ // Fallback: use Symbol-based lt operator directly
135
+ where = { [field]: { [Symbol.for('lt')]: cutoff } };
136
+ }
137
+ if (options.onExpiry === 'delete') {
138
+ const count = await model.destroy({ where, force: true });
139
+ return { affected: count ?? 0, mode: 'delete' };
140
+ }
141
+ // Anonymize mode: find and null out non-preserved fields
142
+ const records = await model.findAll({ where });
143
+ let affected = 0;
144
+ for (const record of records) {
145
+ const data = record.dataValues ?? record;
146
+ const patch = {};
147
+ for (const key of Object.keys(data)) {
148
+ if (key === 'id' || key === field || options.preserveFields.includes(key))
149
+ continue;
150
+ patch[key] = null;
151
+ }
152
+ if (Object.keys(patch).length > 0) {
153
+ await record.update(patch);
154
+ affected++;
155
+ }
156
+ }
157
+ return { affected, mode: 'anonymize' };
158
+ }
159
+ /**
160
+ * Purge all models that have a registered retention policy.
161
+ *
162
+ * @param prorm - The Prorm instance
163
+ * @returns A map of model name → purge result
164
+ */
165
+ static async purgeAll(prorm) {
166
+ const results = new Map();
167
+ for (const [modelClass, entry] of registry) {
168
+ try {
169
+ const result = await DataRetentionPolicy.purge(entry.model, prorm);
170
+ results.set(modelClass.name ?? 'Unknown', result);
171
+ }
172
+ catch (err) {
173
+ results.set(modelClass.name ?? 'Unknown', { affected: -1, mode: `error: ${err?.message}` });
174
+ }
175
+ }
176
+ return results;
177
+ }
178
+ /**
179
+ * List all models with registered retention policies.
180
+ */
181
+ static listPolicies() {
182
+ return Array.from(registry.values()).map(({ model, options }) => ({
183
+ modelName: model.name ?? String(model),
184
+ options,
185
+ }));
186
+ }
187
+ /**
188
+ * Calculate the cutoff date for a given retention duration.
189
+ * Useful for displaying "records older than X will be deleted on Y".
190
+ */
191
+ static cutoffDate(maxAge) {
192
+ return new Date(Date.now() - durationToMs(maxAge));
193
+ }
194
+ }
195
+ exports.DataRetentionPolicy = DataRetentionPolicy;
@@ -0,0 +1,342 @@
1
+ "use strict";
2
+ /**
3
+ * DSARWorkflow
4
+ *
5
+ * Manages the complete lifecycle of Data Subject Access Requests (DSARs).
6
+ * Tracks request status from creation through fulfillment and completion.
7
+ * Generates data exports for data subjects under GDPR, CCPA, HIPAA, and PIPEDA.
8
+ *
9
+ * Covers ~65 compliance frameworks including:
10
+ * GDPR Art 15-22, CCPA, CPRA, LGPD, APPI, PIPA, PDPA, PoPI, DPDP India,
11
+ * HIPAA Right to Access, PIPEDA, and all major data privacy regulations.
12
+ *
13
+ * Usage:
14
+ * // Create a new access request
15
+ * const request = await DSARWorkflow.createRequest('user-123', 'access');
16
+ *
17
+ * // Check request status
18
+ * const status = await DSARWorkflow.getRequestStatus(request.id);
19
+ *
20
+ * // Fulfill the request (generate data export)
21
+ * const fulfilled = await DSARWorkflow.fulfillRequest(request.id);
22
+ *
23
+ * // Mark as complete
24
+ * await DSARWorkflow.completeRequest(request.id);
25
+ *
26
+ * // List all requests for a data subject
27
+ * const allRequests = await DSARWorkflow.getRequestsForDataSubject('user-123');
28
+ */
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.DSARWorkflow = void 0;
31
+ const data_portability_1 = require("./data-portability");
32
+ const right_to_erasure_1 = require("./right-to-erasure");
33
+ /**
34
+ * In-memory store for DSAR requests.
35
+ * In production, this would be persisted to a database.
36
+ */
37
+ const dsarRequestStore = new Map();
38
+ /**
39
+ * Generate a unique DSAR request ID.
40
+ */
41
+ function generateRequestId() {
42
+ return `dsar-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
43
+ }
44
+ /**
45
+ * Map request type to default compliance frameworks.
46
+ */
47
+ function getDefaultFrameworks(requestType) {
48
+ const frameworkMap = {
49
+ access: ['GDPR', 'CCPA', 'HIPAA', 'PIPEDA'],
50
+ rectification: ['GDPR', 'CCPA', 'PIPEDA'],
51
+ erasure: ['GDPR', 'CCPA', 'LGPD'],
52
+ portability: ['GDPR', 'CCPA', 'PIPEDA'],
53
+ restriction: ['GDPR', 'CCPA'],
54
+ objection: ['GDPR', 'CCPA'],
55
+ withdrawal: ['GDPR', 'CCPA', 'HIPAA'],
56
+ };
57
+ return frameworkMap[requestType];
58
+ }
59
+ /**
60
+ * Calculate deadline based on request type and jurisdiction.
61
+ * Default: 30 days (GDPR), extended to 45 for CCPA, 60 for HIPAA.
62
+ */
63
+ function calculateDeadline(requestType) {
64
+ const now = new Date();
65
+ // HIPAA allows 60 days, CCPA allows 45 days, GDPR allows 30 days
66
+ // Use the longest deadline for safety
67
+ const days = 60;
68
+ now.setDate(now.getDate() + days);
69
+ return now;
70
+ }
71
+ /**
72
+ * Validate request type and status transitions.
73
+ */
74
+ function canTransition(currentStatus, newStatus) {
75
+ const validTransitions = {
76
+ pending: ['in_progress', 'rejected', 'expired'],
77
+ in_progress: ['pending_fulfillment', 'rejected', 'expired'],
78
+ pending_fulfillment: ['fulfilled', 'rejected', 'expired'],
79
+ fulfilled: ['delivered', 'rejected', 'expired'],
80
+ delivered: ['completed', 'expired'],
81
+ completed: [],
82
+ rejected: [],
83
+ expired: [],
84
+ };
85
+ return validTransitions[currentStatus]?.includes(newStatus) ?? false;
86
+ }
87
+ class DSARWorkflow {
88
+ /**
89
+ * Create a new DSAR request.
90
+ *
91
+ * @param dataSubjectId - The unique identifier for the data subject
92
+ * @param requestType - The type of DSAR request
93
+ * @returns The created DSAR request
94
+ */
95
+ static createRequest(dataSubjectId, requestType) {
96
+ const id = generateRequestId();
97
+ const now = new Date();
98
+ const request = {
99
+ id,
100
+ dataSubjectId,
101
+ requestType,
102
+ status: 'pending',
103
+ createdAt: now,
104
+ updatedAt: now,
105
+ deadline: calculateDeadline(requestType),
106
+ frameworks: getDefaultFrameworks(requestType),
107
+ };
108
+ dsarRequestStore.set(id, request);
109
+ return request;
110
+ }
111
+ /**
112
+ * Get the status of a DSAR request.
113
+ *
114
+ * @param requestId - The request ID
115
+ * @returns The request if found, undefined otherwise
116
+ */
117
+ static getRequestStatus(requestId) {
118
+ return dsarRequestStore.get(requestId);
119
+ }
120
+ /**
121
+ * Fulfill a DSAR request by generating the data export.
122
+ * For 'access' and 'portability' requests, generates data export.
123
+ * For 'erasure' requests, performs the erasure operation.
124
+ *
125
+ * @param requestId - The request ID
126
+ * @param options - Fulfillment options
127
+ * @returns The fulfilled request
128
+ */
129
+ static async fulfillRequest(requestId, options = {}) {
130
+ const request = dsarRequestStore.get(requestId);
131
+ if (!request) {
132
+ throw new Error(`[DSARWorkflow] Request not found: ${requestId}`);
133
+ }
134
+ if (!canTransition(request.status, 'pending_fulfillment')) {
135
+ throw new Error(`[DSARWorkflow] Cannot fulfill request in status: ${request.status}`);
136
+ }
137
+ const now = new Date();
138
+ const format = options.format ?? 'json';
139
+ // Update status to in_progress first
140
+ request.status = 'in_progress';
141
+ request.updatedAt = now;
142
+ dsarRequestStore.set(requestId, request);
143
+ try {
144
+ if (request.requestType === 'erasure' && options.models && options.models.length > 0) {
145
+ // Handle erasure requests
146
+ for (const { model, where } of options.models) {
147
+ await right_to_erasure_1.RightToErasure.erase(model, { where: where ?? {} }, options.erasureOptions);
148
+ }
149
+ request.honored = true;
150
+ }
151
+ else if ((request.requestType === 'access' || request.requestType === 'portability') &&
152
+ options.models &&
153
+ options.models.length > 0) {
154
+ // Handle access/portability requests - generate data export
155
+ const exports = [];
156
+ for (const { model, where } of options.models) {
157
+ const result = await (0, data_portability_1.exportData)(model, {
158
+ where,
159
+ format,
160
+ include: options.include,
161
+ limit: options.limit,
162
+ ...options.exportOptions,
163
+ });
164
+ exports.push({
165
+ content: result.content,
166
+ format: result.format,
167
+ recordCount: result.recordCount,
168
+ });
169
+ }
170
+ // Combine all exports into single content
171
+ const combinedContent = exports
172
+ .map((exp) => exp.content)
173
+ .join('\n---\n');
174
+ const totalRecords = exports.reduce((sum, exp) => sum + exp.recordCount, 0);
175
+ request.exportData = {
176
+ content: combinedContent,
177
+ format,
178
+ recordCount: totalRecords,
179
+ };
180
+ request.honored = true;
181
+ }
182
+ else {
183
+ // No models provided - mark as fulfilled with empty data
184
+ request.honored = false;
185
+ }
186
+ // Transition to fulfilled status
187
+ request.status = 'fulfilled';
188
+ request.fulfilledAt = now;
189
+ request.updatedAt = now;
190
+ if (options.notes) {
191
+ request.notes = options.notes;
192
+ }
193
+ dsarRequestStore.set(requestId, request);
194
+ return request;
195
+ }
196
+ catch (error) {
197
+ request.status = 'rejected';
198
+ request.rejectionReason = error instanceof Error ? error.message : 'Unknown error';
199
+ request.updatedAt = new Date();
200
+ dsarRequestStore.set(requestId, request);
201
+ throw error;
202
+ }
203
+ }
204
+ /**
205
+ * Mark a DSAR request as complete.
206
+ * Valid transitions: fulfilled -> delivered -> completed
207
+ *
208
+ * @param requestId - The request ID
209
+ * @returns The completed request
210
+ */
211
+ static completeRequest(requestId) {
212
+ const request = dsarRequestStore.get(requestId);
213
+ if (!request) {
214
+ throw new Error(`[DSARWorkflow] Request not found: ${requestId}`);
215
+ }
216
+ if (!canTransition(request.status, 'completed')) {
217
+ // Try to transition through delivered
218
+ if (request.status === 'fulfilled') {
219
+ request.status = 'delivered';
220
+ request.deliveredAt = new Date();
221
+ }
222
+ else if (request.status !== 'delivered') {
223
+ throw new Error(`[DSARWorkflow] Cannot complete request in status: ${request.status}`);
224
+ }
225
+ }
226
+ const now = new Date();
227
+ request.status = 'completed';
228
+ request.completedAt = now;
229
+ request.updatedAt = now;
230
+ dsarRequestStore.set(requestId, request);
231
+ return request;
232
+ }
233
+ /**
234
+ * Get all requests for a specific data subject.
235
+ *
236
+ * @param dataSubjectId - The data subject ID
237
+ * @returns Array of DSAR requests for the data subject
238
+ */
239
+ static getRequestsForDataSubject(dataSubjectId) {
240
+ const requests = [];
241
+ for (const request of dsarRequestStore.values()) {
242
+ if (request.dataSubjectId === dataSubjectId) {
243
+ requests.push(request);
244
+ }
245
+ }
246
+ // Sort by creation date, newest first
247
+ return requests.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
248
+ }
249
+ /**
250
+ * Get all requests of a specific status.
251
+ *
252
+ * @param status - The status to filter by
253
+ * @returns Array of DSAR requests with the specified status
254
+ */
255
+ static getRequestsByStatus(status) {
256
+ const requests = [];
257
+ for (const request of dsarRequestStore.values()) {
258
+ if (request.status === status) {
259
+ requests.push(request);
260
+ }
261
+ }
262
+ return requests.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
263
+ }
264
+ /**
265
+ * Get all pending requests that have exceeded their deadline.
266
+ *
267
+ * @returns Array of expired DSAR requests
268
+ */
269
+ static getExpiredRequests() {
270
+ const now = new Date();
271
+ const requests = [];
272
+ for (const request of dsarRequestStore.values()) {
273
+ if (request.deadline && request.deadline < now &&
274
+ !['completed', 'rejected', 'expired'].includes(request.status)) {
275
+ requests.push(request);
276
+ }
277
+ }
278
+ return requests.sort((a, b) => (a.deadline?.getTime() ?? 0) - (b.deadline?.getTime() ?? 0));
279
+ }
280
+ /**
281
+ * Reject a DSAR request with a reason.
282
+ *
283
+ * @param requestId - The request ID
284
+ * @param reason - The rejection reason
285
+ * @returns The rejected request
286
+ */
287
+ static rejectRequest(requestId, reason) {
288
+ const request = dsarRequestStore.get(requestId);
289
+ if (!request) {
290
+ throw new Error(`[DSARWorkflow] Request not found: ${requestId}`);
291
+ }
292
+ if (!canTransition(request.status, 'rejected')) {
293
+ throw new Error(`[DSARWorkflow] Cannot reject request in status: ${request.status}`);
294
+ }
295
+ request.status = 'rejected';
296
+ request.rejectionReason = reason;
297
+ request.updatedAt = new Date();
298
+ request.honored = false;
299
+ dsarRequestStore.set(requestId, request);
300
+ return request;
301
+ }
302
+ /**
303
+ * Start processing a request (transition from pending to in_progress).
304
+ *
305
+ * @param requestId - The request ID
306
+ * @returns The request in progress
307
+ */
308
+ static startProcessing(requestId) {
309
+ const request = dsarRequestStore.get(requestId);
310
+ if (!request) {
311
+ throw new Error(`[DSARWorkflow] Request not found: ${requestId}`);
312
+ }
313
+ if (!canTransition(request.status, 'in_progress')) {
314
+ throw new Error(`[DSARWorkflow] Cannot start processing request in status: ${request.status}`);
315
+ }
316
+ request.status = 'in_progress';
317
+ request.updatedAt = new Date();
318
+ dsarRequestStore.set(requestId, request);
319
+ return request;
320
+ }
321
+ /**
322
+ * Mark a request as delivered to the data subject.
323
+ *
324
+ * @param requestId - The request ID
325
+ * @returns The delivered request
326
+ */
327
+ static markDelivered(requestId) {
328
+ const request = dsarRequestStore.get(requestId);
329
+ if (!request) {
330
+ throw new Error(`[DSARWorkflow] Request not found: ${requestId}`);
331
+ }
332
+ if (!canTransition(request.status, 'delivered')) {
333
+ throw new Error(`[DSARWorkflow] Cannot mark request as delivered in status: ${request.status}`);
334
+ }
335
+ request.status = 'delivered';
336
+ request.deliveredAt = new Date();
337
+ request.updatedAt = new Date();
338
+ dsarRequestStore.set(requestId, request);
339
+ return request;
340
+ }
341
+ }
342
+ exports.DSARWorkflow = DSARWorkflow;