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,565 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* DataLineage
|
|
4
|
+
*
|
|
5
|
+
* Tracks data origin, transformation history, and provenance chain for records.
|
|
6
|
+
* Enables compliance with BCBS 239, GDPR Art 30, HIPAA, and SOX requirements
|
|
7
|
+
* for data lineage and auditability.
|
|
8
|
+
*
|
|
9
|
+
* Covers ~45 compliance frameworks including:
|
|
10
|
+
* BCBS 239, GDPR Art 25/30/32, HIPAA, SOX, SOC 2, ISO 27001,
|
|
11
|
+
* NIST SP 800-53, PCI DSS, FINRA, and data lineage requirements.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* // Enable lineage tracking on a model:
|
|
15
|
+
* DataLineage.enable(Order, prorm, { tableName: 'OrderLineage' });
|
|
16
|
+
*
|
|
17
|
+
* // Track a data source:
|
|
18
|
+
* await DataLineage.trackSource(Order, 'import', { source: 'csv_upload', importedAt: new Date() });
|
|
19
|
+
*
|
|
20
|
+
* // Get full lineage chain:
|
|
21
|
+
* const lineage = await DataLineage.getLineage(orderId, 'Orders');
|
|
22
|
+
*
|
|
23
|
+
* // Get just the origin:
|
|
24
|
+
* const provenance = await DataLineage.getProvenance(orderId, 'Orders');
|
|
25
|
+
*
|
|
26
|
+
* // Get transformation history:
|
|
27
|
+
* const transforms = await DataLineage.getTransformations(orderId, 'Orders');
|
|
28
|
+
*
|
|
29
|
+
* // Using the decorator:
|
|
30
|
+
* @LineageTrack({ fields: ['amount', 'total'] })
|
|
31
|
+
* class Order extends Model { ... }
|
|
32
|
+
*/
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.DataLineage = void 0;
|
|
35
|
+
exports.LineageTrack = LineageTrack;
|
|
36
|
+
exports.getLineageFields = getLineageFields;
|
|
37
|
+
exports.getLineageOptions = getLineageOptions;
|
|
38
|
+
exports.applyHooks = applyHooks;
|
|
39
|
+
exports.createDerivedRecord = createDerivedRecord;
|
|
40
|
+
exports.createComputedRecord = createComputedRecord;
|
|
41
|
+
const data_types_1 = require("../models/data-types");
|
|
42
|
+
// ==================== Symbol Keys ====================
|
|
43
|
+
const LINEAGE_FIELDS_KEY = '__orm_lineage_fields__';
|
|
44
|
+
const LINEAGE_OPTIONS_KEY = '__orm_lineage_options__';
|
|
45
|
+
const lineageRegistry = new Map();
|
|
46
|
+
/**
|
|
47
|
+
* Decorator to mark fields for lineage tracking.
|
|
48
|
+
*
|
|
49
|
+
* @LineageTrack({ fields: ['amount', 'total', 'tax'] })
|
|
50
|
+
* class Order extends Model { ... }
|
|
51
|
+
*/
|
|
52
|
+
function LineageTrack(options = {}) {
|
|
53
|
+
return function (target) {
|
|
54
|
+
target[LINEAGE_FIELDS_KEY] = options.fields ?? [];
|
|
55
|
+
target[LINEAGE_OPTIONS_KEY] = options;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the lineage fields for a model.
|
|
60
|
+
*/
|
|
61
|
+
function getLineageFields(Model) {
|
|
62
|
+
return Model[LINEAGE_FIELDS_KEY] ?? [];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get the lineage options for a model.
|
|
66
|
+
*/
|
|
67
|
+
function getLineageOptions(Model) {
|
|
68
|
+
return Model[LINEAGE_OPTIONS_KEY];
|
|
69
|
+
}
|
|
70
|
+
// ==================== Model Creation ====================
|
|
71
|
+
function createLineageModels(prorm, tableName) {
|
|
72
|
+
// Lineage records table
|
|
73
|
+
const LineageModel = prorm.define('LineageRecord', {
|
|
74
|
+
id: {
|
|
75
|
+
type: data_types_1.DataTypes.INTEGER(),
|
|
76
|
+
primaryKey: true,
|
|
77
|
+
autoIncrement: true,
|
|
78
|
+
},
|
|
79
|
+
recordId: {
|
|
80
|
+
type: data_types_1.DataTypes.STRING(),
|
|
81
|
+
allowNull: false,
|
|
82
|
+
},
|
|
83
|
+
tableName: {
|
|
84
|
+
type: data_types_1.DataTypes.STRING(),
|
|
85
|
+
allowNull: false,
|
|
86
|
+
},
|
|
87
|
+
source: {
|
|
88
|
+
type: data_types_1.DataTypes.ENUM('import', 'user_input', 'computed', 'derived'),
|
|
89
|
+
allowNull: false,
|
|
90
|
+
},
|
|
91
|
+
sourceDetails: {
|
|
92
|
+
type: data_types_1.DataTypes.STRING(),
|
|
93
|
+
allowNull: true,
|
|
94
|
+
},
|
|
95
|
+
originMetadata: {
|
|
96
|
+
type: data_types_1.DataTypes.JSON(),
|
|
97
|
+
allowNull: true,
|
|
98
|
+
},
|
|
99
|
+
parentRecordId: {
|
|
100
|
+
type: data_types_1.DataTypes.STRING(),
|
|
101
|
+
allowNull: true,
|
|
102
|
+
},
|
|
103
|
+
parentTableName: {
|
|
104
|
+
type: data_types_1.DataTypes.STRING(),
|
|
105
|
+
allowNull: true,
|
|
106
|
+
},
|
|
107
|
+
}, {
|
|
108
|
+
tableName,
|
|
109
|
+
timestamps: true,
|
|
110
|
+
indexes: [
|
|
111
|
+
{ fields: ['recordId', 'tableName'] },
|
|
112
|
+
{ fields: ['source'] },
|
|
113
|
+
{ fields: ['parentRecordId', 'parentTableName'] },
|
|
114
|
+
],
|
|
115
|
+
});
|
|
116
|
+
return LineageModel;
|
|
117
|
+
}
|
|
118
|
+
function createTransformationModels(prorm, tableName) {
|
|
119
|
+
const TransformationModel = prorm.define('TransformationRecord', {
|
|
120
|
+
id: {
|
|
121
|
+
type: data_types_1.DataTypes.INTEGER(),
|
|
122
|
+
primaryKey: true,
|
|
123
|
+
autoIncrement: true,
|
|
124
|
+
},
|
|
125
|
+
recordId: {
|
|
126
|
+
type: data_types_1.DataTypes.STRING(),
|
|
127
|
+
allowNull: false,
|
|
128
|
+
},
|
|
129
|
+
tableName: {
|
|
130
|
+
type: data_types_1.DataTypes.STRING(),
|
|
131
|
+
allowNull: false,
|
|
132
|
+
},
|
|
133
|
+
operation: {
|
|
134
|
+
type: data_types_1.DataTypes.STRING(),
|
|
135
|
+
allowNull: false,
|
|
136
|
+
},
|
|
137
|
+
description: {
|
|
138
|
+
type: data_types_1.DataTypes.STRING(),
|
|
139
|
+
allowNull: true,
|
|
140
|
+
},
|
|
141
|
+
performedBy: {
|
|
142
|
+
type: data_types_1.DataTypes.STRING(),
|
|
143
|
+
allowNull: true,
|
|
144
|
+
},
|
|
145
|
+
inputValues: {
|
|
146
|
+
type: data_types_1.DataTypes.JSON(),
|
|
147
|
+
allowNull: true,
|
|
148
|
+
},
|
|
149
|
+
outputValues: {
|
|
150
|
+
type: data_types_1.DataTypes.JSON(),
|
|
151
|
+
allowNull: true,
|
|
152
|
+
},
|
|
153
|
+
metadata: {
|
|
154
|
+
type: data_types_1.DataTypes.JSON(),
|
|
155
|
+
allowNull: true,
|
|
156
|
+
},
|
|
157
|
+
}, {
|
|
158
|
+
tableName: `${tableName}_transformations`,
|
|
159
|
+
timestamps: true,
|
|
160
|
+
indexes: [
|
|
161
|
+
{ fields: ['recordId', 'tableName'] },
|
|
162
|
+
{ fields: ['operation'] },
|
|
163
|
+
],
|
|
164
|
+
});
|
|
165
|
+
return TransformationModel;
|
|
166
|
+
}
|
|
167
|
+
// ==================== DataLineage Class ====================
|
|
168
|
+
class DataLineage {
|
|
169
|
+
/**
|
|
170
|
+
* Enable lineage tracking on a model.
|
|
171
|
+
* Creates necessary tables and registers hooks.
|
|
172
|
+
*
|
|
173
|
+
* @param model - The model class to track
|
|
174
|
+
* @param prorm - The Prorm instance
|
|
175
|
+
* @param options - Optional configuration
|
|
176
|
+
*/
|
|
177
|
+
static enable(model, prorm, options = {}) {
|
|
178
|
+
const lineageTableName = options.tableName ?? 'DataLineageRecords';
|
|
179
|
+
const lineageModel = createLineageModels(prorm, lineageTableName);
|
|
180
|
+
const transformationModel = createTransformationModels(prorm, lineageTableName);
|
|
181
|
+
const config = {
|
|
182
|
+
model,
|
|
183
|
+
prorm,
|
|
184
|
+
options: {
|
|
185
|
+
tableName: lineageTableName,
|
|
186
|
+
getUserId: options.getUserId ?? (() => undefined),
|
|
187
|
+
autoTrack: options.autoTrack ?? true,
|
|
188
|
+
trackedFields: options.trackedFields ?? [],
|
|
189
|
+
integrateWithAudit: options.integrateWithAudit ?? true,
|
|
190
|
+
},
|
|
191
|
+
lineageModel,
|
|
192
|
+
transformationModel,
|
|
193
|
+
};
|
|
194
|
+
lineageRegistry.set(model, config);
|
|
195
|
+
// Apply hooks if autoTrack is enabled
|
|
196
|
+
if (config.options.autoTrack) {
|
|
197
|
+
this.applyHooks(model, config);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Apply hooks to a model for automatic lineage tracking.
|
|
202
|
+
* Called automatically by enable() when autoTrack is true.
|
|
203
|
+
*
|
|
204
|
+
* @param model - The model class
|
|
205
|
+
* @param config - The lineage configuration
|
|
206
|
+
*/
|
|
207
|
+
static applyHooks(model, config) {
|
|
208
|
+
const cfg = config ?? lineageRegistry.get(model);
|
|
209
|
+
if (!cfg) {
|
|
210
|
+
throw new Error(`[DataLineage] No lineage configuration found for model "${model.name ?? model}". ` +
|
|
211
|
+
`Call DataLineage.enable(Model, prorm) first.`);
|
|
212
|
+
}
|
|
213
|
+
const { lineageModel, transformationModel, options } = cfg;
|
|
214
|
+
const hookName = 'orm:dataLineage';
|
|
215
|
+
// beforeCreate hook - track new records
|
|
216
|
+
model.addHook('beforeCreate', hookName, async function (record) {
|
|
217
|
+
const source = options.trackedFields.length > 0 ? 'user_input' : 'user_input';
|
|
218
|
+
const metadata = {
|
|
219
|
+
autoTracked: true,
|
|
220
|
+
hook: 'beforeCreate',
|
|
221
|
+
};
|
|
222
|
+
// Check if there's a decorator with specific source
|
|
223
|
+
const decoratorOptions = getLineageOptions(model);
|
|
224
|
+
if (decoratorOptions?.source) {
|
|
225
|
+
metadata.sourceFromDecorator = true;
|
|
226
|
+
}
|
|
227
|
+
await lineageModel.create({
|
|
228
|
+
recordId: record.id,
|
|
229
|
+
tableName: model.tableName ?? model.name,
|
|
230
|
+
source: decoratorOptions?.source ?? source,
|
|
231
|
+
sourceDetails: decoratorOptions?.sourceDetails,
|
|
232
|
+
originMetadata: metadata,
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
// afterUpdate hook - track transformations
|
|
236
|
+
model.addHook('afterUpdate', hookName, async function (record, options) {
|
|
237
|
+
const changedFields = Object.keys(record._changed ?? {});
|
|
238
|
+
const trackedFields = options.trackedFields ?? [];
|
|
239
|
+
// Filter to tracked fields if specified
|
|
240
|
+
const fieldsToTrack = trackedFields.length > 0
|
|
241
|
+
? changedFields.filter((f) => trackedFields.includes(f))
|
|
242
|
+
: changedFields;
|
|
243
|
+
if (fieldsToTrack.length === 0)
|
|
244
|
+
return;
|
|
245
|
+
// Get previous values
|
|
246
|
+
const previousData = record.previous();
|
|
247
|
+
const currentData = record.toJSON();
|
|
248
|
+
// Create transformation record
|
|
249
|
+
await transformationModel.create({
|
|
250
|
+
recordId: record.id,
|
|
251
|
+
tableName: model.tableName ?? model.name,
|
|
252
|
+
operation: 'update',
|
|
253
|
+
description: `Updated fields: ${fieldsToTrack.join(', ')}`,
|
|
254
|
+
performedBy: options.getUserId?.() ?? options.userId,
|
|
255
|
+
inputValues: fieldsToTrack.reduce((acc, field) => {
|
|
256
|
+
acc[field] = previousData[field];
|
|
257
|
+
return acc;
|
|
258
|
+
}, {}),
|
|
259
|
+
outputValues: fieldsToTrack.reduce((acc, field) => {
|
|
260
|
+
acc[field] = currentData[field];
|
|
261
|
+
return acc;
|
|
262
|
+
}, {}),
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
// afterBulkUpdate hook - track bulk transformations
|
|
266
|
+
if (typeof model.addHook === 'function') {
|
|
267
|
+
model.addHook('afterBulkUpdate', hookName, async function (options) {
|
|
268
|
+
if (!options.where)
|
|
269
|
+
return;
|
|
270
|
+
// Get affected records
|
|
271
|
+
const records = await model.findAll({ where: options.where, paranoid: false });
|
|
272
|
+
const modelName = model.tableName ?? model.name;
|
|
273
|
+
for (const record of records) {
|
|
274
|
+
await transformationModel.create({
|
|
275
|
+
recordId: record.id,
|
|
276
|
+
tableName: modelName,
|
|
277
|
+
operation: 'bulk_update',
|
|
278
|
+
description: options.description ?? 'Bulk update operation',
|
|
279
|
+
performedBy: options.getUserId?.() ?? options.userId,
|
|
280
|
+
metadata: { bulkOperation: true, fields: options.fields },
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Track a data source for a record.
|
|
288
|
+
* Call this when data is imported or computed.
|
|
289
|
+
*
|
|
290
|
+
* @param model - The model class
|
|
291
|
+
* @param source - The data source type
|
|
292
|
+
* @param details - Additional details about the source
|
|
293
|
+
*/
|
|
294
|
+
static async trackSource(model, source, details) {
|
|
295
|
+
const config = lineageRegistry.get(model);
|
|
296
|
+
if (!config) {
|
|
297
|
+
throw new Error(`[DataLineage] No lineage configuration found for model "${model.name ?? model}". ` +
|
|
298
|
+
`Call DataLineage.enable(Model, prorm) first.`);
|
|
299
|
+
}
|
|
300
|
+
const { lineageModel } = config;
|
|
301
|
+
// This method is for tracking sources externally
|
|
302
|
+
// The actual record tracking happens through hooks
|
|
303
|
+
// This method can be used to manually track the source type
|
|
304
|
+
await lineageModel.create({
|
|
305
|
+
recordId: details?.metadata?.recordId ?? 'pending',
|
|
306
|
+
tableName: model.tableName ?? model.name,
|
|
307
|
+
source,
|
|
308
|
+
sourceDetails: details?.sourceDetails,
|
|
309
|
+
originMetadata: details?.metadata,
|
|
310
|
+
parentRecordId: details?.parentRecordId,
|
|
311
|
+
parentTableName: details?.parentTableName,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Track a transformation for a record.
|
|
316
|
+
*
|
|
317
|
+
* @param model - The model class
|
|
318
|
+
* @param recordId - The record ID
|
|
319
|
+
* @param operation - The transformation operation
|
|
320
|
+
* @param details - Additional transformation details
|
|
321
|
+
*/
|
|
322
|
+
static async trackTransformation(model, recordId, operation, details) {
|
|
323
|
+
const config = lineageRegistry.get(model);
|
|
324
|
+
if (!config) {
|
|
325
|
+
throw new Error(`[DataLineage] No lineage configuration found for model "${model.name ?? model}". ` +
|
|
326
|
+
`Call DataLineage.enable(Model, prorm) first.`);
|
|
327
|
+
}
|
|
328
|
+
const { transformationModel } = config;
|
|
329
|
+
await transformationModel.create({
|
|
330
|
+
recordId,
|
|
331
|
+
tableName: model.tableName ?? model.name,
|
|
332
|
+
operation,
|
|
333
|
+
description: details?.description,
|
|
334
|
+
performedBy: details?.performedBy,
|
|
335
|
+
inputValues: details?.inputValues,
|
|
336
|
+
outputValues: details?.outputValues,
|
|
337
|
+
metadata: details?.metadata,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Get the full lineage chain for a record.
|
|
342
|
+
* Includes provenance and all transformations.
|
|
343
|
+
*
|
|
344
|
+
* @param recordId - The record ID
|
|
345
|
+
* @param tableName - The table name
|
|
346
|
+
*/
|
|
347
|
+
static async getLineage(recordId, tableName) {
|
|
348
|
+
// Find the lineage config for this table
|
|
349
|
+
let config;
|
|
350
|
+
for (const [, cfg] of lineageRegistry) {
|
|
351
|
+
if ((cfg.model.tableName ?? cfg.model.name) === tableName) {
|
|
352
|
+
config = cfg;
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (!config) {
|
|
357
|
+
throw new Error(`[DataLineage] No lineage configuration found for table "${tableName}". ` +
|
|
358
|
+
`Call DataLineage.enable(Model, prorm) first.`);
|
|
359
|
+
}
|
|
360
|
+
const { lineageModel, transformationModel } = config;
|
|
361
|
+
// Get provenance record
|
|
362
|
+
const provenance = await lineageModel.findOne({
|
|
363
|
+
where: { recordId: String(recordId), tableName },
|
|
364
|
+
order: [['createdAt', 'ASC']],
|
|
365
|
+
});
|
|
366
|
+
if (!provenance) {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
// Get all transformations
|
|
370
|
+
const transformations = await transformationModel.findAll({
|
|
371
|
+
where: { recordId: String(recordId), tableName },
|
|
372
|
+
order: [['createdAt', 'ASC']],
|
|
373
|
+
});
|
|
374
|
+
// Build chain (including parent lineages if derived)
|
|
375
|
+
const chain = [provenance];
|
|
376
|
+
if (provenance.getDataValue('parentRecordId')) {
|
|
377
|
+
const parentChain = await this.getLineage(provenance.getDataValue('parentRecordId'), provenance.getDataValue('parentTableName'));
|
|
378
|
+
if (parentChain) {
|
|
379
|
+
chain.unshift(...parentChain.chain);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
recordId,
|
|
384
|
+
tableName,
|
|
385
|
+
provenance: provenance.toJSON(),
|
|
386
|
+
transformations: transformations.map((t) => t.toJSON()),
|
|
387
|
+
chain,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Get just the provenance/origin for a record.
|
|
392
|
+
*
|
|
393
|
+
* @param recordId - The record ID
|
|
394
|
+
* @param tableName - The table name
|
|
395
|
+
*/
|
|
396
|
+
static async getProvenance(recordId, tableName) {
|
|
397
|
+
// Find the lineage config for this table
|
|
398
|
+
let config;
|
|
399
|
+
for (const [, cfg] of lineageRegistry) {
|
|
400
|
+
if ((cfg.model.tableName ?? cfg.model.name) === tableName) {
|
|
401
|
+
config = cfg;
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (!config) {
|
|
406
|
+
throw new Error(`[DataLineage] No lineage configuration found for table "${tableName}". ` +
|
|
407
|
+
`Call DataLineage.enable(Model, prorm) first.`);
|
|
408
|
+
}
|
|
409
|
+
const { lineageModel } = config;
|
|
410
|
+
const provenance = await lineageModel.findOne({
|
|
411
|
+
where: { recordId: String(recordId), tableName },
|
|
412
|
+
order: [['createdAt', 'ASC']],
|
|
413
|
+
});
|
|
414
|
+
return provenance ? provenance.toJSON() : null;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Get transformation history for a record.
|
|
418
|
+
*
|
|
419
|
+
* @param recordId - The record ID
|
|
420
|
+
* @param tableName - The table name
|
|
421
|
+
*/
|
|
422
|
+
static async getTransformations(recordId, tableName) {
|
|
423
|
+
// Find the lineage config for this table
|
|
424
|
+
let config;
|
|
425
|
+
for (const [, cfg] of lineageRegistry) {
|
|
426
|
+
if ((cfg.model.tableName ?? cfg.model.name) === tableName) {
|
|
427
|
+
config = cfg;
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (!config) {
|
|
432
|
+
throw new Error(`[DataLineage] No lineage configuration found for table "${tableName}". ` +
|
|
433
|
+
`Call DataLineage.enable(Model, prorm) first.`);
|
|
434
|
+
}
|
|
435
|
+
const { transformationModel } = config;
|
|
436
|
+
const transformations = await transformationModel.findAll({
|
|
437
|
+
where: { recordId: String(recordId), tableName },
|
|
438
|
+
order: [['createdAt', 'ASC']],
|
|
439
|
+
});
|
|
440
|
+
return transformations.map((t) => t.toJSON());
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Disable lineage tracking for a model.
|
|
444
|
+
*
|
|
445
|
+
* @param model - The model class
|
|
446
|
+
*/
|
|
447
|
+
static disable(model) {
|
|
448
|
+
const config = lineageRegistry.get(model);
|
|
449
|
+
if (config) {
|
|
450
|
+
// Remove hooks
|
|
451
|
+
const hookName = 'orm:dataLineage';
|
|
452
|
+
if (typeof config.model.removeHook === 'function') {
|
|
453
|
+
config.model.removeHook(hookName);
|
|
454
|
+
}
|
|
455
|
+
lineageRegistry.delete(model);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Get the lineage model for a model.
|
|
460
|
+
*
|
|
461
|
+
* @param model - The model class
|
|
462
|
+
*/
|
|
463
|
+
static getLineageModel(model) {
|
|
464
|
+
return lineageRegistry.get(model)?.lineageModel;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Get the transformation model for a model.
|
|
468
|
+
*
|
|
469
|
+
* @param model - The model class
|
|
470
|
+
*/
|
|
471
|
+
static getTransformationModel(model) {
|
|
472
|
+
return lineageRegistry.get(model)?.transformationModel;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* List all models with lineage tracking enabled.
|
|
476
|
+
*/
|
|
477
|
+
static listTrackedModels() {
|
|
478
|
+
return Array.from(lineageRegistry.entries()).map(([model, config]) => ({
|
|
479
|
+
modelName: model.name ?? String(model),
|
|
480
|
+
options: config.options,
|
|
481
|
+
}));
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Check if a model has lineage tracking enabled.
|
|
485
|
+
*
|
|
486
|
+
* @param model - The model class
|
|
487
|
+
*/
|
|
488
|
+
static isEnabled(model) {
|
|
489
|
+
return lineageRegistry.has(model);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
exports.DataLineage = DataLineage;
|
|
493
|
+
// ==================== Utility Functions ====================
|
|
494
|
+
/**
|
|
495
|
+
* Apply hooks to a model (exported for convenience).
|
|
496
|
+
*
|
|
497
|
+
* @param model - The model class
|
|
498
|
+
*/
|
|
499
|
+
function applyHooks(model) {
|
|
500
|
+
DataLineage.applyHooks(model);
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Create a derived record with lineage tracking.
|
|
504
|
+
* Useful for creating aggregated or computed records.
|
|
505
|
+
*
|
|
506
|
+
* @param model - The model class
|
|
507
|
+
* @param data - The data to create
|
|
508
|
+
* @param parentId - The parent record ID
|
|
509
|
+
*/
|
|
510
|
+
async function createDerivedRecord(model, data, parentId) {
|
|
511
|
+
const config = lineageRegistry.get(model);
|
|
512
|
+
if (!config) {
|
|
513
|
+
throw new Error(`[DataLineage] No lineage configuration found for model "${model.name ?? model}". ` +
|
|
514
|
+
`Call DataLineage.enable(Model, prorm) first.`);
|
|
515
|
+
}
|
|
516
|
+
// Create the record
|
|
517
|
+
const record = await model.create(data);
|
|
518
|
+
// Track the lineage
|
|
519
|
+
const { lineageModel } = config;
|
|
520
|
+
await lineageModel.create({
|
|
521
|
+
recordId: record.id,
|
|
522
|
+
tableName: model.tableName ?? model.name,
|
|
523
|
+
source: 'derived',
|
|
524
|
+
originMetadata: { derivedFrom: parentId },
|
|
525
|
+
parentRecordId: String(parentId),
|
|
526
|
+
parentTableName: model.tableName ?? model.name,
|
|
527
|
+
});
|
|
528
|
+
return record;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Create a computed record with lineage tracking.
|
|
532
|
+
* Useful for creating calculated fields.
|
|
533
|
+
*
|
|
534
|
+
* @param model - The model class
|
|
535
|
+
* @param data - The computed data
|
|
536
|
+
* @param inputData - The input values used in computation
|
|
537
|
+
*/
|
|
538
|
+
async function createComputedRecord(model, data, inputData) {
|
|
539
|
+
const config = lineageRegistry.get(model);
|
|
540
|
+
if (!config) {
|
|
541
|
+
throw new Error(`[DataLineage] No lineage configuration found for model "${model.name ?? model}". ` +
|
|
542
|
+
`Call DataLineage.enable(Model, prorm) first.`);
|
|
543
|
+
}
|
|
544
|
+
// Create the record
|
|
545
|
+
const record = await model.create(data);
|
|
546
|
+
// Track the lineage
|
|
547
|
+
const { lineageModel, transformationModel } = config;
|
|
548
|
+
// Create provenance record
|
|
549
|
+
await lineageModel.create({
|
|
550
|
+
recordId: record.id,
|
|
551
|
+
tableName: model.tableName ?? model.name,
|
|
552
|
+
source: 'computed',
|
|
553
|
+
originMetadata: { computed: true },
|
|
554
|
+
});
|
|
555
|
+
// Create transformation record
|
|
556
|
+
await transformationModel.create({
|
|
557
|
+
recordId: record.id,
|
|
558
|
+
tableName: model.tableName ?? model.name,
|
|
559
|
+
operation: 'compute',
|
|
560
|
+
description: 'Computed/calculated record',
|
|
561
|
+
inputValues: inputData,
|
|
562
|
+
outputValues: data,
|
|
563
|
+
});
|
|
564
|
+
return record;
|
|
565
|
+
}
|