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,530 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CrossBorderLog
|
|
4
|
+
*
|
|
5
|
+
* Logs when data crosses geo boundaries, tracking data transfers between
|
|
6
|
+
* regions/countries. Required for GDPR, PIPL, and DPDPA India compliance.
|
|
7
|
+
*
|
|
8
|
+
* Covers ~30 compliance frameworks including:
|
|
9
|
+
* GDPR Art 44-49, PIPL, DPDPA India, CCPA, LGPD Brazil, POPIA South Africa,
|
|
10
|
+
* PDPA Thailand, APPI Japan, and cross-border transfer requirements.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* // Initialize tracking on a model:
|
|
14
|
+
* CrossBorderLog.enable(prorm, User);
|
|
15
|
+
*
|
|
16
|
+
* // Log a cross-border transfer:
|
|
17
|
+
* await CrossBorderLog.logTransfer('EU', 'US', 'PII', userRecords);
|
|
18
|
+
*
|
|
19
|
+
* // Get transfer history for a record:
|
|
20
|
+
* const history = await CrossBorderLog.getTransferHistory(User, userId);
|
|
21
|
+
*
|
|
22
|
+
* // Check if transfer is allowed:
|
|
23
|
+
* const allowed = await CrossBorderLog.checkTransferAllowed('CN', 'US');
|
|
24
|
+
*
|
|
25
|
+
* // Configure regions for compliance:
|
|
26
|
+
* CrossBorderLog.configureRegions({
|
|
27
|
+
* 'EU': { adequateProtection: true, regulations: ['GDPR'] },
|
|
28
|
+
* 'US': { adequateProtection: false, regulations: ['CCPA'] },
|
|
29
|
+
* });
|
|
30
|
+
*/
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.CrossBorderLog = void 0;
|
|
33
|
+
exports.getComplianceTransfers = getComplianceTransfers;
|
|
34
|
+
exports.generateTransferReport = generateTransferReport;
|
|
35
|
+
const data_types_1 = require("../models/data-types");
|
|
36
|
+
// ==================== Symbol Keys ====================
|
|
37
|
+
const TRANSFER_OPTIONS_KEY = '__orm_transfer_options__';
|
|
38
|
+
const transferRegistry = new Map();
|
|
39
|
+
// Default region configurations (can be customized)
|
|
40
|
+
const defaultRegions = {
|
|
41
|
+
// European Union (adequate protection under GDPR)
|
|
42
|
+
'EU': { adequateProtection: true, regulations: ['GDPR'] },
|
|
43
|
+
'EEA': { adequateProtection: true, regulations: ['GDPR'] },
|
|
44
|
+
'UK': { adequateProtection: true, regulations: ['GDPR', 'UK GDPR'] },
|
|
45
|
+
// Countries with adequate protection decisions
|
|
46
|
+
'CA': { adequateProtection: true, regulations: ['PIPEDA'] },
|
|
47
|
+
'JP': { adequateProtection: true, regulations: ['APPI'] },
|
|
48
|
+
'KR': { adequateProtection: true, regulations: ['PIPA'] },
|
|
49
|
+
'GB': { adequateProtection: true, regulations: ['GDPR', 'UK GDPR'] },
|
|
50
|
+
'AR': { adequateProtection: true, regulations: ['PDPA'] },
|
|
51
|
+
'UY': { adequateProtection: true, regulations: ['LGPD'] },
|
|
52
|
+
// Countries WITHOUT adequate protection (need SCCs or other mechanisms)
|
|
53
|
+
'US': { adequateProtection: false, regulations: ['CCPA'], restrictions: ['requires_scc'] },
|
|
54
|
+
'CN': { adequateProtection: false, regulations: ['PIPL'], restrictions: ['requires_assessment'] },
|
|
55
|
+
'IN': { adequateProtection: false, regulations: ['DPDPA'], restrictions: ['requires_notice'] },
|
|
56
|
+
'BR': { adequateProtection: false, regulations: ['LGPD'], restrictions: ['requires_consent'] },
|
|
57
|
+
'TH': { adequateProtection: false, regulations: ['PDPA'], restrictions: ['requires_consent'] },
|
|
58
|
+
'SG': { adequateProtection: false, regulations: ['PDPA'], restrictions: ['requires_assessment'] },
|
|
59
|
+
'AU': { adequateProtection: false, regulations: ['Privacy Act'], restrictions: ['requires_notice'] },
|
|
60
|
+
'NZ': { adequateProtection: false, regulations: ['Privacy Act'], restrictions: ['requires_notice'] },
|
|
61
|
+
};
|
|
62
|
+
// Custom region configurations (set by user)
|
|
63
|
+
const customRegions = new Map();
|
|
64
|
+
// Master region config (combines defaults and custom)
|
|
65
|
+
const regionConfigs = new Map(Object.entries(defaultRegions));
|
|
66
|
+
// ==================== Model Creation ====================
|
|
67
|
+
function createTransferModel(prorm, tableName) {
|
|
68
|
+
const TransferModel = prorm.define('CrossBorderTransfer', {
|
|
69
|
+
id: {
|
|
70
|
+
type: data_types_1.DataTypes.INTEGER(),
|
|
71
|
+
primaryKey: true,
|
|
72
|
+
autoIncrement: true,
|
|
73
|
+
},
|
|
74
|
+
recordId: {
|
|
75
|
+
type: data_types_1.DataTypes.STRING(),
|
|
76
|
+
allowNull: false,
|
|
77
|
+
},
|
|
78
|
+
tableName: {
|
|
79
|
+
type: data_types_1.DataTypes.STRING(),
|
|
80
|
+
allowNull: false,
|
|
81
|
+
},
|
|
82
|
+
sourceRegion: {
|
|
83
|
+
type: data_types_1.DataTypes.STRING(),
|
|
84
|
+
allowNull: false,
|
|
85
|
+
},
|
|
86
|
+
targetRegion: {
|
|
87
|
+
type: data_types_1.DataTypes.STRING(),
|
|
88
|
+
allowNull: false,
|
|
89
|
+
},
|
|
90
|
+
dataType: {
|
|
91
|
+
type: data_types_1.DataTypes.ENUM('PII', 'PHI', 'PCI', 'FINANCIAL', 'BIOMETRIC', 'SENSITIVE', 'GENERAL'),
|
|
92
|
+
allowNull: false,
|
|
93
|
+
},
|
|
94
|
+
recordCount: {
|
|
95
|
+
type: data_types_1.DataTypes.INTEGER(),
|
|
96
|
+
allowNull: false,
|
|
97
|
+
defaultValue: 1,
|
|
98
|
+
},
|
|
99
|
+
status: {
|
|
100
|
+
type: data_types_1.DataTypes.ENUM('pending', 'completed', 'blocked', 'reviewed'),
|
|
101
|
+
allowNull: false,
|
|
102
|
+
defaultValue: 'completed',
|
|
103
|
+
},
|
|
104
|
+
transferredAt: {
|
|
105
|
+
type: data_types_1.DataTypes.DATE(),
|
|
106
|
+
allowNull: false,
|
|
107
|
+
defaultValue: data_types_1.DataTypes.NOW,
|
|
108
|
+
},
|
|
109
|
+
initiatedBy: {
|
|
110
|
+
type: data_types_1.DataTypes.STRING(),
|
|
111
|
+
allowNull: true,
|
|
112
|
+
},
|
|
113
|
+
purpose: {
|
|
114
|
+
type: data_types_1.DataTypes.STRING(),
|
|
115
|
+
allowNull: true,
|
|
116
|
+
},
|
|
117
|
+
legalBasis: {
|
|
118
|
+
type: data_types_1.DataTypes.STRING(),
|
|
119
|
+
allowNull: true,
|
|
120
|
+
},
|
|
121
|
+
metadata: {
|
|
122
|
+
type: data_types_1.DataTypes.JSON(),
|
|
123
|
+
allowNull: true,
|
|
124
|
+
},
|
|
125
|
+
}, {
|
|
126
|
+
tableName,
|
|
127
|
+
timestamps: true,
|
|
128
|
+
indexes: [
|
|
129
|
+
{ fields: ['recordId', 'tableName'] },
|
|
130
|
+
{ fields: ['sourceRegion', 'targetRegion'] },
|
|
131
|
+
{ fields: ['dataType'] },
|
|
132
|
+
{ fields: ['status'] },
|
|
133
|
+
{ fields: ['transferredAt'] },
|
|
134
|
+
],
|
|
135
|
+
});
|
|
136
|
+
return TransferModel;
|
|
137
|
+
}
|
|
138
|
+
// ==================== CrossBorderLog Class ====================
|
|
139
|
+
class CrossBorderLog {
|
|
140
|
+
/**
|
|
141
|
+
* Enable cross-border transfer logging on a model.
|
|
142
|
+
* Creates necessary tables and registers hooks.
|
|
143
|
+
*
|
|
144
|
+
* @param prorm - The Prorm instance
|
|
145
|
+
* @param model - The model class to track
|
|
146
|
+
* @param options - Optional configuration
|
|
147
|
+
*/
|
|
148
|
+
static enable(prorm, model, options = {}) {
|
|
149
|
+
const tableName = options.tableName ?? 'CrossBorderTransfers';
|
|
150
|
+
const transferModel = createTransferModel(prorm, tableName);
|
|
151
|
+
const config = {
|
|
152
|
+
model,
|
|
153
|
+
prorm,
|
|
154
|
+
options: {
|
|
155
|
+
tableName,
|
|
156
|
+
getUserId: options.getUserId ?? (() => undefined),
|
|
157
|
+
getIpAddress: options.getIpAddress ?? (() => undefined),
|
|
158
|
+
autoLog: options.autoLog ?? true,
|
|
159
|
+
requireApproval: options.requireApproval ?? (() => true),
|
|
160
|
+
},
|
|
161
|
+
transferModel,
|
|
162
|
+
};
|
|
163
|
+
transferRegistry.set(model, config);
|
|
164
|
+
// Apply hooks if autoLog is enabled
|
|
165
|
+
if (config.options.autoLog) {
|
|
166
|
+
this.applyHooks(model, config);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Apply hooks to a model for automatic transfer logging.
|
|
171
|
+
* Called automatically by enable() when autoLog is true.
|
|
172
|
+
*
|
|
173
|
+
* @param model - The model class
|
|
174
|
+
* @param config - The transfer configuration
|
|
175
|
+
*/
|
|
176
|
+
static applyHooks(model, config) {
|
|
177
|
+
const cfg = config ?? transferRegistry.get(model);
|
|
178
|
+
if (!cfg) {
|
|
179
|
+
throw new Error(`[CrossBorderLog] No transfer configuration found for model "${model.name ?? model}". ` +
|
|
180
|
+
`Call CrossBorderLog.enable(prorm, Model) first.`);
|
|
181
|
+
}
|
|
182
|
+
const { transferModel, options } = cfg;
|
|
183
|
+
const hookName = 'orm:crossBorderLog';
|
|
184
|
+
// beforeCreate hook - could auto-log creation if needed
|
|
185
|
+
model.addHook('beforeCreate', hookName, async function (record) {
|
|
186
|
+
// Store metadata for potential transfer logging
|
|
187
|
+
record.__transferMetadata = {
|
|
188
|
+
createdAt: new Date(),
|
|
189
|
+
initiatedBy: options.getUserId(),
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Configure custom region settings.
|
|
195
|
+
* Merges with default region configs.
|
|
196
|
+
*
|
|
197
|
+
* @param regions - Map of region codes to their configurations
|
|
198
|
+
*/
|
|
199
|
+
static configureRegions(regions) {
|
|
200
|
+
for (const [code, config] of Object.entries(regions)) {
|
|
201
|
+
regionConfigs.set(code.toUpperCase(), config);
|
|
202
|
+
customRegions.set(code.toUpperCase(), config);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get region configuration.
|
|
207
|
+
*
|
|
208
|
+
* @param regionCode - The region code
|
|
209
|
+
*/
|
|
210
|
+
static getRegionConfig(regionCode) {
|
|
211
|
+
return regionConfigs.get(regionCode.toUpperCase());
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Log a cross-border data transfer.
|
|
215
|
+
*
|
|
216
|
+
* @param sourceRegion - Source region code (e.g., 'EU', 'US')
|
|
217
|
+
* @param targetRegion - Target region code
|
|
218
|
+
* @param dataType - Category of data being transferred
|
|
219
|
+
* @param records - Record IDs or array of record data
|
|
220
|
+
* @param options - Additional transfer details
|
|
221
|
+
*/
|
|
222
|
+
static async logTransfer(sourceRegion, targetRegion, dataType, records, options) {
|
|
223
|
+
// Find a registered model or use default
|
|
224
|
+
let config;
|
|
225
|
+
for (const [, cfg] of transferRegistry) {
|
|
226
|
+
if (options?.tableName && (cfg.model.tableName ?? cfg.model.name) === options.tableName) {
|
|
227
|
+
config = cfg;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
config = cfg;
|
|
231
|
+
}
|
|
232
|
+
if (!config) {
|
|
233
|
+
throw new Error(`[CrossBorderLog] No model registered. Call CrossBorderLog.enable(prorm, Model) first.`);
|
|
234
|
+
}
|
|
235
|
+
const { transferModel } = config;
|
|
236
|
+
const recordCount = Array.isArray(records) ? records.length : 1;
|
|
237
|
+
const recordId = options?.recordId ?? (Array.isArray(records) && records[0]?.id) ?? 'unknown';
|
|
238
|
+
const transfer = await transferModel.create({
|
|
239
|
+
recordId: String(recordId),
|
|
240
|
+
tableName: options?.tableName ?? config.model.tableName ?? config.model.name,
|
|
241
|
+
sourceRegion: sourceRegion.toUpperCase(),
|
|
242
|
+
targetRegion: targetRegion.toUpperCase(),
|
|
243
|
+
dataType,
|
|
244
|
+
recordCount,
|
|
245
|
+
status: options?.status ?? 'completed',
|
|
246
|
+
transferredAt: new Date(),
|
|
247
|
+
initiatedBy: options?.initiatedBy ?? config.options.getUserId(),
|
|
248
|
+
purpose: options?.purpose,
|
|
249
|
+
legalBasis: options?.legalBasis,
|
|
250
|
+
metadata: options?.metadata,
|
|
251
|
+
});
|
|
252
|
+
return transfer.toJSON();
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Get transfer history for a specific record.
|
|
256
|
+
*
|
|
257
|
+
* @param model - The model class
|
|
258
|
+
* @param recordId - The record ID to get history for
|
|
259
|
+
*/
|
|
260
|
+
static async getTransferHistory(model, recordId) {
|
|
261
|
+
const config = transferRegistry.get(model);
|
|
262
|
+
if (!config) {
|
|
263
|
+
throw new Error(`[CrossBorderLog] No transfer configuration found for model "${model.name ?? model}". ` +
|
|
264
|
+
`Call CrossBorderLog.enable(prorm, Model) first.`);
|
|
265
|
+
}
|
|
266
|
+
const { transferModel } = config;
|
|
267
|
+
const transfers = await transferModel.findAll({
|
|
268
|
+
where: { recordId: String(recordId) },
|
|
269
|
+
order: [['transferredAt', 'DESC']],
|
|
270
|
+
});
|
|
271
|
+
return transfers.map((t) => t.toJSON());
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Get all transfer records for a model.
|
|
275
|
+
*
|
|
276
|
+
* @param model - The model class
|
|
277
|
+
* @param options - Filter options (source, target, status, date range)
|
|
278
|
+
*/
|
|
279
|
+
static async getAllTransfers(model, options) {
|
|
280
|
+
const config = transferRegistry.get(model);
|
|
281
|
+
if (!config) {
|
|
282
|
+
throw new Error(`[CrossBorderLog] No transfer configuration found for model "${model.name ?? model}". ` +
|
|
283
|
+
`Call CrossBorderLog.enable(prorm, Model) first.`);
|
|
284
|
+
}
|
|
285
|
+
const { transferModel } = config;
|
|
286
|
+
const where = {};
|
|
287
|
+
if (options?.sourceRegion) {
|
|
288
|
+
where.sourceRegion = options.sourceRegion.toUpperCase();
|
|
289
|
+
}
|
|
290
|
+
if (options?.targetRegion) {
|
|
291
|
+
where.targetRegion = options.targetRegion.toUpperCase();
|
|
292
|
+
}
|
|
293
|
+
if (options?.dataType) {
|
|
294
|
+
where.dataType = options.dataType;
|
|
295
|
+
}
|
|
296
|
+
if (options?.status) {
|
|
297
|
+
where.status = options.status;
|
|
298
|
+
}
|
|
299
|
+
if (options?.startDate || options?.endDate) {
|
|
300
|
+
where.transferredAt = {};
|
|
301
|
+
if (options?.startDate) {
|
|
302
|
+
where.transferredAt['$gte'] = options.startDate;
|
|
303
|
+
}
|
|
304
|
+
if (options?.endDate) {
|
|
305
|
+
where.transferredAt['$lte'] = options.endDate;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const transfers = await transferModel.findAll({
|
|
309
|
+
where,
|
|
310
|
+
order: [['transferredAt', 'DESC']],
|
|
311
|
+
limit: options?.limit ?? 100,
|
|
312
|
+
offset: options?.offset ?? 0,
|
|
313
|
+
});
|
|
314
|
+
return transfers.map((t) => t.toJSON());
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Check if a cross-border transfer is allowed.
|
|
318
|
+
* Evaluates based on source/target region configurations.
|
|
319
|
+
*
|
|
320
|
+
* @param sourceRegion - Source region code
|
|
321
|
+
* @param targetRegion - Target region code
|
|
322
|
+
* @param dataType - Optional data type for additional checks
|
|
323
|
+
*/
|
|
324
|
+
static async checkTransferAllowed(sourceRegion, targetRegion, dataType) {
|
|
325
|
+
const source = regionConfigs.get(sourceRegion.toUpperCase());
|
|
326
|
+
const target = regionConfigs.get(targetRegion.toUpperCase());
|
|
327
|
+
// Both regions must be known
|
|
328
|
+
if (!source && sourceRegion.toUpperCase() !== 'UNKNOWN') {
|
|
329
|
+
return {
|
|
330
|
+
allowed: false,
|
|
331
|
+
reason: `Unknown source region: ${sourceRegion}`,
|
|
332
|
+
requiresReview: true,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
if (!target && targetRegion.toUpperCase() !== 'UNKNOWN') {
|
|
336
|
+
return {
|
|
337
|
+
allowed: false,
|
|
338
|
+
reason: `Unknown target region: ${targetRegion}`,
|
|
339
|
+
requiresReview: true,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
// Unknown regions always require review
|
|
343
|
+
if (sourceRegion.toUpperCase() === 'UNKNOWN' || targetRegion.toUpperCase() === 'UNKNOWN') {
|
|
344
|
+
return {
|
|
345
|
+
allowed: true, // Allow but flag for review
|
|
346
|
+
reason: 'One or both regions are unknown - manual review recommended',
|
|
347
|
+
requiresReview: true,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
// Check if target has adequate protection
|
|
351
|
+
if (!target?.adequateProtection) {
|
|
352
|
+
// Check for specific restrictions based on data type
|
|
353
|
+
if (dataType === 'PII' || dataType === 'PHI') {
|
|
354
|
+
const regulations = [...(source?.regulations ?? []), ...(target?.regulations ?? [])];
|
|
355
|
+
return {
|
|
356
|
+
allowed: false,
|
|
357
|
+
reason: `Transfer to ${targetRegion} requires additional safeguards (SCCs, BCRs, or adequacy decision)`,
|
|
358
|
+
requiresReview: true,
|
|
359
|
+
regulations,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
allowed: true,
|
|
364
|
+
reason: `Transfer to ${targetRegion} - target does not have adequate protection determination`,
|
|
365
|
+
requiresReview: true,
|
|
366
|
+
regulations: target?.regulations,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
// Target has adequate protection
|
|
370
|
+
const regulations = [...(source?.regulations ?? []), ...(target?.regulations ?? [])];
|
|
371
|
+
return {
|
|
372
|
+
allowed: true,
|
|
373
|
+
reason: `Transfer to ${targetRegion} - adequate protection in place`,
|
|
374
|
+
requiresReview: false,
|
|
375
|
+
regulations,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Get all regions with their configurations.
|
|
380
|
+
*/
|
|
381
|
+
static listRegions() {
|
|
382
|
+
return Array.from(regionConfigs.entries()).map(([code, config]) => ({
|
|
383
|
+
code,
|
|
384
|
+
config,
|
|
385
|
+
}));
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Get transfer model for a model.
|
|
389
|
+
*
|
|
390
|
+
* @param model - The model class
|
|
391
|
+
*/
|
|
392
|
+
static getTransferModel(model) {
|
|
393
|
+
return transferRegistry.get(model)?.transferModel;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Disable transfer logging for a model.
|
|
397
|
+
*
|
|
398
|
+
* @param model - The model class
|
|
399
|
+
*/
|
|
400
|
+
static disable(model) {
|
|
401
|
+
const config = transferRegistry.get(model);
|
|
402
|
+
if (config) {
|
|
403
|
+
// Remove hooks
|
|
404
|
+
const hookName = 'orm:crossBorderLog';
|
|
405
|
+
if (typeof config.model.removeHook === 'function') {
|
|
406
|
+
config.model.removeHook(hookName);
|
|
407
|
+
}
|
|
408
|
+
transferRegistry.delete(model);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* List all models with transfer logging enabled.
|
|
413
|
+
*/
|
|
414
|
+
static listTrackedModels() {
|
|
415
|
+
return Array.from(transferRegistry.entries()).map(([model, config]) => ({
|
|
416
|
+
modelName: model.name ?? String(model),
|
|
417
|
+
options: config.options,
|
|
418
|
+
}));
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Check if a model has transfer logging enabled.
|
|
422
|
+
*
|
|
423
|
+
* @param model - The model class
|
|
424
|
+
*/
|
|
425
|
+
static isEnabled(model) {
|
|
426
|
+
return transferRegistry.has(model);
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Reset all custom region configurations to defaults.
|
|
430
|
+
*/
|
|
431
|
+
static resetRegions() {
|
|
432
|
+
customRegions.clear();
|
|
433
|
+
regionConfigs.clear();
|
|
434
|
+
for (const [code, config] of Object.entries(defaultRegions)) {
|
|
435
|
+
regionConfigs.set(code, config);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
exports.CrossBorderLog = CrossBorderLog;
|
|
440
|
+
// ==================== Utility Functions ====================
|
|
441
|
+
/**
|
|
442
|
+
* Get all transfers for compliance reporting.
|
|
443
|
+
*
|
|
444
|
+
* @param prorm - The Prorm instance
|
|
445
|
+
* @param options - Filter options
|
|
446
|
+
*/
|
|
447
|
+
async function getComplianceTransfers(prorm, options) {
|
|
448
|
+
const { CrossBorderTransfer } = prorm.models;
|
|
449
|
+
if (!CrossBorderTransfer) {
|
|
450
|
+
throw new Error('[CrossBorderLog] Transfer model not found. Initialize with CrossBorderLog.enable() first.');
|
|
451
|
+
}
|
|
452
|
+
const where = {};
|
|
453
|
+
if (options?.startDate || options?.endDate) {
|
|
454
|
+
where.transferredAt = {};
|
|
455
|
+
if (options?.startDate) {
|
|
456
|
+
where.transferredAt['$gte'] = options.startDate;
|
|
457
|
+
}
|
|
458
|
+
if (options?.endDate) {
|
|
459
|
+
where.transferredAt['$lte'] = options.endDate;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (options?.regions && options.regions.length > 0) {
|
|
463
|
+
const upperRegions = options.regions.map((r) => r.toUpperCase());
|
|
464
|
+
where['$or'] = [
|
|
465
|
+
{ sourceRegion: { $in: upperRegions } },
|
|
466
|
+
{ targetRegion: { $in: upperRegions } },
|
|
467
|
+
];
|
|
468
|
+
}
|
|
469
|
+
if (options?.dataTypes && options.dataTypes.length > 0) {
|
|
470
|
+
where.dataType = { $in: options.dataTypes };
|
|
471
|
+
}
|
|
472
|
+
const transfers = await CrossBorderTransfer.findAll({
|
|
473
|
+
where,
|
|
474
|
+
order: [['transferredAt', 'DESC']],
|
|
475
|
+
});
|
|
476
|
+
return transfers.map((t) => t.toJSON());
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Generate a compliance report for cross-border transfers.
|
|
480
|
+
*
|
|
481
|
+
* @param prorm - The Prorm instance
|
|
482
|
+
* @param options - Report options
|
|
483
|
+
*/
|
|
484
|
+
async function generateTransferReport(prorm, options) {
|
|
485
|
+
const transfers = await getComplianceTransfers(prorm, {
|
|
486
|
+
startDate: options?.startDate,
|
|
487
|
+
endDate: options?.endDate,
|
|
488
|
+
});
|
|
489
|
+
const uniqueRegions = new Set();
|
|
490
|
+
const dataTypes = {
|
|
491
|
+
PII: 0,
|
|
492
|
+
PHI: 0,
|
|
493
|
+
PCI: 0,
|
|
494
|
+
FINANCIAL: 0,
|
|
495
|
+
BIOMETRIC: 0,
|
|
496
|
+
SENSITIVE: 0,
|
|
497
|
+
GENERAL: 0,
|
|
498
|
+
};
|
|
499
|
+
for (const transfer of transfers) {
|
|
500
|
+
uniqueRegions.add(transfer.sourceRegion);
|
|
501
|
+
uniqueRegions.add(transfer.targetRegion);
|
|
502
|
+
dataTypes[transfer.dataType] = (dataTypes[transfer.dataType] ?? 0) + 1;
|
|
503
|
+
}
|
|
504
|
+
const report = {
|
|
505
|
+
summary: {
|
|
506
|
+
totalTransfers: transfers.length,
|
|
507
|
+
uniqueRegions: uniqueRegions.size,
|
|
508
|
+
dataTypes,
|
|
509
|
+
},
|
|
510
|
+
transfers,
|
|
511
|
+
};
|
|
512
|
+
if (options?.groupByRegion) {
|
|
513
|
+
const byRegion = {};
|
|
514
|
+
for (const transfer of transfers) {
|
|
515
|
+
const key = `${transfer.sourceRegion}_to_${transfer.targetRegion}`;
|
|
516
|
+
if (!byRegion[key]) {
|
|
517
|
+
byRegion[key] = {
|
|
518
|
+
sourceRegion: transfer.sourceRegion,
|
|
519
|
+
targetRegion: transfer.targetRegion,
|
|
520
|
+
count: 0,
|
|
521
|
+
dataTypes: { ...dataTypes },
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
byRegion[key].count++;
|
|
525
|
+
byRegion[key].dataTypes[transfer.dataType]++;
|
|
526
|
+
}
|
|
527
|
+
report.byRegion = byRegion;
|
|
528
|
+
}
|
|
529
|
+
return report;
|
|
530
|
+
}
|