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,316 @@
1
+ "use strict";
2
+ /**
3
+ * MigrationDiagram — generates an SVG showing database schema changes.
4
+ *
5
+ * Features:
6
+ * - Shows tables with their columns (before/after or just new state)
7
+ * - Color-coded change indicators: green (added), red (removed), yellow (modified)
8
+ * - Migration metadata header with timestamp and description
9
+ * - Support for multiple migration steps
10
+ * - Compact field rows with type badges
11
+ *
12
+ * Usage:
13
+ * const diag = new MigrationDiagram({
14
+ * title: 'User Migration',
15
+ * description: 'Add email verification fields',
16
+ * changes: [
17
+ * { table: 'users', type: 'modify', changes: [...] },
18
+ * { table: 'sessions', type: 'add', changes: [...] },
19
+ * ]
20
+ * });
21
+ * diag.save('./diagrams/migration.svg');
22
+ */
23
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ var desc = Object.getOwnPropertyDescriptor(m, k);
26
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
27
+ desc = { enumerable: true, get: function() { return m[k]; } };
28
+ }
29
+ Object.defineProperty(o, k2, desc);
30
+ }) : (function(o, m, k, k2) {
31
+ if (k2 === undefined) k2 = k;
32
+ o[k2] = m[k];
33
+ }));
34
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
35
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
36
+ }) : function(o, v) {
37
+ o["default"] = v;
38
+ });
39
+ var __importStar = (this && this.__importStar) || (function () {
40
+ var ownKeys = function(o) {
41
+ ownKeys = Object.getOwnPropertyNames || function (o) {
42
+ var ar = [];
43
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
44
+ return ar;
45
+ };
46
+ return ownKeys(o);
47
+ };
48
+ return function (mod) {
49
+ if (mod && mod.__esModule) return mod;
50
+ var result = {};
51
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
52
+ __setModuleDefault(result, mod);
53
+ return result;
54
+ };
55
+ })();
56
+ Object.defineProperty(exports, "__esModule", { value: true });
57
+ exports.MigrationDiagram = void 0;
58
+ const fs = __importStar(require("fs"));
59
+ const path = __importStar(require("path"));
60
+ // ─── Constants ─────────────────────────────────────────────────────────────
61
+ const CHANGE_COLORS = {
62
+ add: { header: '#065F46', mid: '#059669', light: '#D1FAE5', text: '#065F46', icon: '#059669' },
63
+ remove: { header: '#7F1D1D', mid: '#DC2626', light: '#FEE2E2', text: '#7F1D1D', icon: '#DC2626' },
64
+ modify: { header: '#92400E', mid: '#D97706', light: '#FEF3C7', text: '#92400E', icon: '#D97706' },
65
+ };
66
+ const TABLE_COLORS = [
67
+ { header: '#1E3A8A', mid: '#2563EB', light: '#DBEAFE', text: '#1E3A8A' },
68
+ { header: '#5B21B6', mid: '#7C3AED', light: '#EDE9FE', text: '#5B21B6' },
69
+ { header: '#065F46', mid: '#059669', light: '#D1FAE5', text: '#065F46' },
70
+ { header: '#92400E', mid: '#D97706', light: '#FEF3C7', text: '#92400E' },
71
+ { header: '#831843', mid: '#DB2777', light: '#FCE7F3', text: '#831843' },
72
+ { header: '#0C4A6E', mid: '#0284C7', light: '#E0F2FE', text: '#0C4A6E' },
73
+ ];
74
+ // ─── Helpers ─────────────────────────────────────────────────────────────
75
+ function xmlEsc(s) {
76
+ return String(s ?? '')
77
+ .replace(/&/g, '&amp;')
78
+ .replace(/</g, '&lt;')
79
+ .replace(/>/g, '&gt;')
80
+ .replace(/"/g, '&quot;');
81
+ }
82
+ function typeBadgeColor(type) {
83
+ const t = type.toUpperCase();
84
+ if (/STRING|VARCHAR|CHAR|TEXT/i.test(t))
85
+ return { text: '#059669', bg: '#D1FAE5' };
86
+ if (/INT|BIGINT|SMALLINT|FLOAT|DOUBLE|DECIMAL/i.test(t))
87
+ return { text: '#7C3AED', bg: '#EDE9FE' };
88
+ if (/DATE|TIME|TIMESTAMP/i.test(t))
89
+ return { text: '#EA580C', bg: '#FFEDD5' };
90
+ if (/BOOL/i.test(t))
91
+ return { text: '#DB2777', bg: '#FCE7F3' };
92
+ if (/UUID/i.test(t))
93
+ return { text: '#0891B2', bg: '#CFFAFE' };
94
+ if (/JSON/i.test(t))
95
+ return { text: '#4F46E5', bg: '#E0E7FF' };
96
+ return { text: '#475569', bg: '#F1F5F9' };
97
+ }
98
+ // ─── Main class ────────────────────────────────────────────────────────────
99
+ class MigrationDiagram {
100
+ constructor(options) {
101
+ this.opts = {
102
+ title: options.title ?? 'Database Migration',
103
+ description: options.description ?? '',
104
+ timestamp: options.timestamp ?? new Date().toISOString(),
105
+ changes: options.changes ?? [],
106
+ theme: options.theme ?? 'light',
107
+ color: options.color ?? 'blue',
108
+ };
109
+ }
110
+ // ── Compute layout positions ─────────────────────────────────────────
111
+ computeLayout() {
112
+ const layout = [];
113
+ let y = this.opts.changes.length > 0 ? MigrationDiagram.BANNER_H + MigrationDiagram.MARGIN : 0;
114
+ let maxW = 0;
115
+ this.opts.changes.forEach((change, idx) => {
116
+ const rowCount = Math.max((change.changes ?? []).length, 1);
117
+ const h = MigrationDiagram.BOX_HDR + rowCount * MigrationDiagram.BOX_ROW + MigrationDiagram.FOOT_H;
118
+ const x = MigrationDiagram.MARGIN;
119
+ layout.push({
120
+ change,
121
+ x,
122
+ y,
123
+ w: MigrationDiagram.BOX_W,
124
+ h,
125
+ colorIdx: idx % TABLE_COLORS.length,
126
+ });
127
+ maxW = Math.max(maxW, MigrationDiagram.BOX_W);
128
+ y += h + MigrationDiagram.V_GAP;
129
+ });
130
+ return layout;
131
+ }
132
+ // ── Render banner ────────────────────────────────────────────────────
133
+ renderBanner(totalW) {
134
+ const H = MigrationDiagram.BANNER_H;
135
+ const { title, description, timestamp } = this.opts;
136
+ return `
137
+ <!-- ═══ BANNER ═══ -->
138
+ <defs>
139
+ <linearGradient id="migBanner" x1="0" y1="0" x2="1" y2="0"
140
+ gradientUnits="objectBoundingBox">
141
+ <stop offset="0%" stop-color="#7C3AED"/>
142
+ <stop offset="100%" stop-color="#A855F7"/>
143
+ </linearGradient>
144
+ <filter id="migBannerSh" x="-5%" y="-5%" width="110%" height="120%">
145
+ <feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#7C3AED30"/>
146
+ </filter>
147
+ </defs>
148
+ <rect width="${totalW}" height="${H}" fill="url(#migBanner)" filter="url(#migBannerSh)"/>
149
+
150
+ <!-- Database icon (SVG) -->
151
+ <g transform="translate(30, 24)">
152
+ <ellipse cx="14" cy="8" rx="12" ry="4" fill="white" opacity="0.3"/>
153
+ <path d="M2 12v12c0 2.2 5.4 4 12 4s12-1.8 12-4V12" fill="white" opacity="0.9"/>
154
+ <path d="M2 16c0 2.2 5.4 4 12 4s12-1.8 12-4" fill="none" stroke="white" stroke-width="1.5" opacity="0.5"/>
155
+ <path d="M2 20c0 2.2 5.4 4 12 4s12-1.8 12-4" fill="none" stroke="white" stroke-width="1.5" opacity="0.3"/>
156
+ </g>
157
+
158
+ <text x="58" y="36" font-family="system-ui" font-size="22" font-weight="800" fill="white">${xmlEsc(title)}</text>
159
+ <text x="58" y="54" font-family="system-ui" font-size="12" fill="rgba(255,255,255,0.75)">${xmlEsc(description)}</text>
160
+ <text x="${totalW - 30}" y="54" font-family="'SF Mono',monospace" font-size="11" fill="rgba(255,255,255,0.6)" text-anchor="end">${timestamp}</text>
161
+
162
+ <!-- Migration tag -->
163
+ <rect x="${totalW - 130}" y="20" width="100" height="26" rx="13" fill="rgba(255,255,255,0.15)"/>
164
+ <text x="${totalW - 80}" y="38" font-family="'SF Mono',monospace" font-size="11" font-weight="700" fill="white" text-anchor="middle">MIGRATION</text>`;
165
+ }
166
+ // ── Render a table change box ─────────────────────────────────────────
167
+ renderChangeBox(item) {
168
+ const { change, x, y, w, h, colorIdx } = item;
169
+ // Fallback color for unknown/undefined change types so `colors.header` etc.
170
+ // never throw.
171
+ const colors = CHANGE_COLORS[change.type] ?? {
172
+ header: '#334155',
173
+ mid: '#475569',
174
+ light: '#E2E8F0',
175
+ text: '#334155',
176
+ icon: '#475569',
177
+ };
178
+ const R = 10;
179
+ // Sanitize the table name for use in an SVG element id (must be a valid,
180
+ // unique NCName) and escape it wherever it lands in markup/comments.
181
+ const safeId = String(change.table ?? 'table').replace(/\W/g, '') + colorIdx;
182
+ const changes = change.changes ?? [];
183
+ let out = `\n <!-- Change: ${xmlEsc(change.table)} (${xmlEsc(change.type)}) -->`;
184
+ // Shadow
185
+ out += `\n <defs>
186
+ <filter id="migSh${safeId}" x="-8%" y="-8%" width="116%" height="130%">
187
+ <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#00000015"/>
188
+ </filter>
189
+ </defs>`;
190
+ // Box background
191
+ out += `\n <rect x="${x}" y="${y}" width="${w}" height="${h}" rx="${R}" fill="white" filter="url(#migSh${safeId})"/>`;
192
+ // Header with change type indicator
193
+ out += `\n <rect x="${x}" y="${y}" width="${w}" height="${MigrationDiagram.BOX_HDR}" rx="${R}" fill="${colors.header}"/>`;
194
+ // Change type badge
195
+ out += `\n <rect x="${x + 12}" y="${y + 10}" width="56" height="22" rx="6" fill="${colors.light}"/>`;
196
+ out += `\n <text x="${x + 40}" y="${y + 25}" font-family="'SF Mono',monospace" font-size="10" font-weight="700" fill="${colors.text}" text-anchor="middle">${xmlEsc(String(change.type ?? '').toUpperCase())}</text>`;
197
+ // Table name
198
+ out += `\n <text x="${x + 80}" y="${y + 27}" font-family="system-ui" font-size="15" font-weight="700" fill="white">${xmlEsc(change.table)}</text>`;
199
+ // Column changes
200
+ changes.forEach((col, i) => {
201
+ const fy = y + MigrationDiagram.BOX_HDR + i * MigrationDiagram.BOX_ROW;
202
+ const rowBg = i % 2 === 0 ? '#FFFFFF' : '#F9FAFB';
203
+ const tc = typeBadgeColor(col.type);
204
+ out += `\n <rect x="${x}" y="${fy}" width="${w}" height="${MigrationDiagram.BOX_ROW}" fill="${rowBg}"/>`;
205
+ // Change indicator stripe
206
+ const stripeColor = col.action === 'add' ? '#059669' : col.action === 'remove' ? '#DC2626' : '#D97706';
207
+ out += `\n <rect x="${x}" y="${fy}" width="4" height="${MigrationDiagram.BOX_ROW}" fill="${stripeColor}"/>`;
208
+ // Action icon (SVG)
209
+ const ix = x + 16;
210
+ const iy = fy + MigrationDiagram.BOX_ROW / 2;
211
+ if (col.action === 'add') {
212
+ out += `\n <circle cx="${ix}" cy="${iy}" r="6" fill="${stripeColor}"/>
213
+ <path d="M${ix - 3} ${iy}v6M${ix} ${iy - 3}h6" stroke="white" stroke-width="2" fill="none"/>`;
214
+ }
215
+ else if (col.action === 'remove') {
216
+ out += `\n <circle cx="${ix}" cy="${iy}" r="6" fill="${stripeColor}"/>
217
+ <path d="M${ix - 3} ${iy}h6" stroke="white" stroke-width="2" fill="none"/>`;
218
+ }
219
+ else {
220
+ out += `\n <circle cx="${ix}" cy="${iy}" r="6" fill="${stripeColor}"/>
221
+ <path d="M${ix - 3} ${iy}h6" stroke="white" stroke-width="2" fill="none"/>
222
+ <path d="M${ix} ${iy - 3}v6" stroke="white" stroke-width="2" fill="none"/>`;
223
+ }
224
+ // Column name
225
+ const nameX = x + 36;
226
+ const nameW = col.action === 'modify' && col.oldType ? '600' : '500';
227
+ out += `\n <text x="${nameX}" y="${iy + 4}" font-family="'SF Mono',monospace" font-size="12" font-weight="${nameW}" fill="#1E293B">${xmlEsc(col.name)}</text>`;
228
+ // Type badge
229
+ if (col.action === 'modify' && col.oldType) {
230
+ // Show old → new type
231
+ out += `\n <text x="${x + w - 10}" y="${iy + 4}" font-family="'SF Mono',monospace" font-size="9" fill="#94A3B8" text-anchor="end">${xmlEsc(col.oldType)} → ${xmlEsc(col.type)}</text>`;
232
+ }
233
+ else {
234
+ const tbW = col.type.length * 6 + 12;
235
+ out += `\n <rect x="${x + w - tbW - 8}" y="${fy + 6}" width="${tbW}" height="16" rx="4" fill="${tc.bg}"/>`;
236
+ out += `\n <text x="${x + w - tbW / 2 - 8}" y="${fy + 18}" font-family="'SF Mono',monospace" font-size="9" font-weight="600" fill="${tc.text}" text-anchor="middle">${xmlEsc(col.type.slice(0, 12))}</text>`;
237
+ }
238
+ // Row separator
239
+ out += `\n <line x1="${x}" y1="${fy + MigrationDiagram.BOX_ROW}" x2="${x + w}" y2="${fy + MigrationDiagram.BOX_ROW}" stroke="#E2E8F0"/>`;
240
+ });
241
+ // Footer
242
+ const footY = y + MigrationDiagram.BOX_HDR + changes.length * MigrationDiagram.BOX_ROW;
243
+ out += `\n <rect x="${x}" y="${footY}" width="${w}" height="${MigrationDiagram.FOOT_H}" fill="${colors.light}" rx="0 0 ${R} ${R}"/>`;
244
+ out += `\n <text x="${x + 12}" y="${footY + 18}" font-family="system-ui" font-size="10" fill="${colors.text}" font-weight="600">${changes.length} change${changes.length !== 1 ? 's' : ''}</text>`;
245
+ return out;
246
+ }
247
+ // ── Render legend ────────────────────────────────────────────────────
248
+ renderLegend(x, y) {
249
+ const items = [
250
+ { label: 'Added', color: '#059669', bg: '#D1FAE5' },
251
+ { label: 'Removed', color: '#DC2626', bg: '#FEE2E2' },
252
+ { label: 'Modified', color: '#D97706', bg: '#FEF3C7' },
253
+ ];
254
+ let out = `\n <!-- LEGEND -->`;
255
+ items.forEach((item, i) => {
256
+ const ix = x + i * 110;
257
+ out += `\n <rect x="${ix}" y="${y}" width="90" height="28" rx="6" fill="${item.bg}"/>`;
258
+ out += `\n <circle cx="${ix + 16}" cy="${y + 14}" r="6" fill="${item.color}"/>`;
259
+ out += `\n <text x="${ix + 30}" y="${y + 18}" font-family="system-ui" font-size="11" font-weight="600" fill="${item.color}">${item.label}</text>`;
260
+ });
261
+ return out;
262
+ }
263
+ // ── Generate full SVG ────────────────────────────────────────────────
264
+ generate() {
265
+ const { changes } = this.opts;
266
+ const MARGIN = MigrationDiagram.MARGIN;
267
+ const BANNER = MigrationDiagram.BANNER_H;
268
+ const layout = this.computeLayout();
269
+ // Calculate dimensions
270
+ let totalW = MigrationDiagram.BOX_W + 2 * MARGIN + 100;
271
+ let totalH = BANNER + MARGIN;
272
+ if (layout.length > 0) {
273
+ const lastItem = layout[layout.length - 1];
274
+ totalH = lastItem.y + lastItem.h + MARGIN + 50;
275
+ }
276
+ const svg = [
277
+ `<?xml version="1.0" encoding="UTF-8"?>`,
278
+ `<!-- Generated by ts-prorm-orm MigrationDiagram -->`,
279
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${totalW}" height="${totalH}" viewBox="0 0 ${totalW} ${totalH}">`,
280
+ ` <rect width="${totalW}" height="${totalH}" fill="#F8FAFC"/>`,
281
+ ];
282
+ // Banner
283
+ svg.push(this.renderBanner(totalW));
284
+ // Change boxes
285
+ layout.forEach((item) => {
286
+ svg.push(this.renderChangeBox(item));
287
+ });
288
+ // Legend
289
+ if (layout.length > 0) {
290
+ svg.push(this.renderLegend(MARGIN, totalH - 50));
291
+ }
292
+ svg.push(`</svg>`);
293
+ return svg.join('\n');
294
+ }
295
+ /**
296
+ * Write the SVG to a file.
297
+ */
298
+ save(outputPath) {
299
+ const resolved = path.resolve(outputPath);
300
+ const dir = path.dirname(resolved);
301
+ if (!fs.existsSync(dir))
302
+ fs.mkdirSync(dir, { recursive: true });
303
+ const svg = this.generate();
304
+ fs.writeFileSync(resolved, svg, 'utf-8');
305
+ return resolved;
306
+ }
307
+ }
308
+ exports.MigrationDiagram = MigrationDiagram;
309
+ // Layout constants
310
+ MigrationDiagram.BOX_W = 320;
311
+ MigrationDiagram.BOX_HDR = 42;
312
+ MigrationDiagram.BOX_ROW = 28;
313
+ MigrationDiagram.FOOT_H = 28;
314
+ MigrationDiagram.MARGIN = 40;
315
+ MigrationDiagram.BANNER_H = 80;
316
+ MigrationDiagram.V_GAP = 50;