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,616 @@
1
+ "use strict";
2
+ /**
3
+ * ModelDiagram — generates a fully-rendered SVG card for a single ORM model.
4
+ *
5
+ * Features:
6
+ * - Color-coded data types (strings, numbers, dates, booleans, JSON, UUID, …)
7
+ * - Constraint badges: PK · FK · UQ · NN · AI · TS
8
+ * - Left-edge accent stripe per row (gold = PK, blue = FK)
9
+ * - Associations section (hasOne / hasMany / belongsTo / belongsToMany)
10
+ * - Dark footer with table name + metadata
11
+ * - Auto-sized SVG viewBox
12
+ *
13
+ * Usage:
14
+ * const diag = new ModelDiagram({ name: 'User', fields: { … }, associations: […] });
15
+ * diag.save('./diagrams/User.svg');
16
+ * // or:
17
+ * const svgString = diag.generate();
18
+ */
19
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ var desc = Object.getOwnPropertyDescriptor(m, k);
22
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
23
+ desc = { enumerable: true, get: function() { return m[k]; } };
24
+ }
25
+ Object.defineProperty(o, k2, desc);
26
+ }) : (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ o[k2] = m[k];
29
+ }));
30
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
31
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
32
+ }) : function(o, v) {
33
+ o["default"] = v;
34
+ });
35
+ var __importStar = (this && this.__importStar) || (function () {
36
+ var ownKeys = function(o) {
37
+ ownKeys = Object.getOwnPropertyNames || function (o) {
38
+ var ar = [];
39
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
40
+ return ar;
41
+ };
42
+ return ownKeys(o);
43
+ };
44
+ return function (mod) {
45
+ if (mod && mod.__esModule) return mod;
46
+ var result = {};
47
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
48
+ __setModuleDefault(result, mod);
49
+ return result;
50
+ };
51
+ })();
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.ModelDiagram = void 0;
54
+ const fs = __importStar(require("fs"));
55
+ const path = __importStar(require("path"));
56
+ const palette_1 = require("./palette");
57
+ const svg_dom_1 = require("./svg-dom");
58
+ const TYPE_COLORS = [
59
+ { pattern: /STRING|VARCHAR|CHAR|TEXT/i, text: '#059669', bg: '#D1FAE5' },
60
+ {
61
+ pattern: /INT|BIGINT|SMALLINT|TINYINT|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER/i,
62
+ text: '#7C3AED',
63
+ bg: '#EDE9FE',
64
+ },
65
+ { pattern: /DATE|TIME|TIMESTAMP/i, text: '#EA580C', bg: '#FFEDD5' },
66
+ { pattern: /BOOL/i, text: '#DB2777', bg: '#FCE7F3' },
67
+ { pattern: /UUID/i, text: '#0891B2', bg: '#CFFAFE' },
68
+ { pattern: /JSONB/i, text: '#4338CA', bg: '#E0E7FF' },
69
+ { pattern: /JSON/i, text: '#4F46E5', bg: '#E0E7FF' },
70
+ { pattern: /BLOB|BINARY|VARBINARY/i, text: '#92400E', bg: '#FEF3C7' },
71
+ { pattern: /ENUM/i, text: '#B45309', bg: '#FEF3C7' },
72
+ { pattern: /ARRAY/i, text: '#0369A1', bg: '#E0F2FE' },
73
+ { pattern: /GEOMETRY|GEOGRAPHY/i, text: '#166534', bg: '#DCFCE7' },
74
+ ];
75
+ function typeColor(t) {
76
+ for (const c of TYPE_COLORS) {
77
+ if (c.pattern.test(t))
78
+ return { text: c.text, bg: c.bg };
79
+ }
80
+ return { text: '#475569', bg: '#F1F5F9' };
81
+ }
82
+ function resolveType(field) {
83
+ const t = field.type;
84
+ if (typeof t === 'string')
85
+ return t;
86
+ if (t && typeof t === 'object') {
87
+ if (typeof t.toSqlString === 'function')
88
+ return t.toSqlString();
89
+ if (t.key)
90
+ return String(t.key);
91
+ }
92
+ return 'UNKNOWN';
93
+ }
94
+ const ASSOC_META = {
95
+ hasOne: { icon: '1:1', color: '#0891B2', bg: '#CFFAFE', label: 'hasOne' },
96
+ hasMany: { icon: '1:N', color: '#7C3AED', bg: '#EDE9FE', label: 'hasMany' },
97
+ belongsTo: { icon: 'N:1', color: '#059669', bg: '#D1FAE5', label: 'belongsTo' },
98
+ belongsToMany: { icon: 'N:N', color: '#DC2626', bg: '#FEE2E2', label: 'belongsToMany' },
99
+ };
100
+ // ─── Main class ────────────────────────────────────────────────────────────
101
+ class ModelDiagram {
102
+ constructor(options) {
103
+ // NOTE: spread `...options` FIRST so the defaults below win. Spreading last
104
+ // would re-apply raw null/undefined values (e.g. `associations: null`) over
105
+ // the guarded defaults and crash later when they are dereferenced.
106
+ this.opts = {
107
+ ...options,
108
+ name: options.name ?? 'Model',
109
+ tableName: options.tableName ?? (options.name ?? 'model').toLowerCase() + 's',
110
+ fields: options.fields ?? {},
111
+ associations: options.associations ?? [],
112
+ timestamps: options.timestamps ?? true,
113
+ paranoid: options.paranoid ?? false,
114
+ underscored: options.underscored ?? false,
115
+ comment: options.comment ?? '',
116
+ theme: options.theme ?? 'light',
117
+ color: options.color ?? '',
118
+ };
119
+ }
120
+ // ── Row badge helper ────────────────────────────────────────────────────
121
+ badges(field, isTS) {
122
+ const b = [];
123
+ if (field.primaryKey)
124
+ b.push({ text: 'PK', bg: '#FEF3C7', fg: '#92400E' });
125
+ if (field.foreignKey || field.references)
126
+ b.push({ text: 'FK', bg: '#DBEAFE', fg: '#1E3A8A' });
127
+ if (field.autoIncrement)
128
+ b.push({ text: 'AI', bg: '#D1FAE5', fg: '#064E3B' });
129
+ if (field.unique && !field.primaryKey)
130
+ b.push({ text: 'UQ', bg: '#EDE9FE', fg: '#2E1065' });
131
+ if (field.allowNull === false)
132
+ b.push({ text: 'NN', bg: '#FEE2E2', fg: '#7F1D1D' });
133
+ if (isTS)
134
+ b.push({ text: 'TS', bg: '#F1F5F9', fg: '#475569' });
135
+ return b;
136
+ }
137
+ // ── SVG badge (drawn into a parent group) ────────────────────────────────
138
+ drawBadge(parent, b, x, y) {
139
+ const w = b.text.length * 7 + 12;
140
+ parent.child('rect', { x, y, width: w, height: 20, rx: 5, fill: b.bg });
141
+ parent
142
+ .child('text', {
143
+ x: x + w / 2,
144
+ y: y + 14,
145
+ 'font-family': "'SF Mono','Fira Code',monospace",
146
+ 'font-size': 10,
147
+ 'font-weight': 700,
148
+ fill: b.fg,
149
+ 'text-anchor': 'middle',
150
+ })
151
+ .text(b.text);
152
+ return w;
153
+ }
154
+ // ── Generate full SVG string (built via a real DOM document) ──────────────
155
+ generate() {
156
+ const { name, tableName, fields, associations, timestamps, paranoid, comment, theme, color } = this.opts;
157
+ const accent = (0, palette_1.resolveAccent)(color);
158
+ const t = (0, palette_1.resolveTheme)(theme);
159
+ const W = ModelDiagram.W;
160
+ const HDR = ModelDiagram.HDR;
161
+ const COL_HDR = ModelDiagram.COL_HDR;
162
+ const ROW_H = ModelDiagram.ROW_H;
163
+ const ASSOC_HDR = ModelDiagram.ASSOC_HDR;
164
+ const ASSOC_ROW = ModelDiagram.ASSOC_ROW;
165
+ const FOOTER = ModelDiagram.FOOTER;
166
+ const PAD = ModelDiagram.PAD;
167
+ const R = ModelDiagram.R;
168
+ // ── Build field list (add timestamps if requested) ──────────────────
169
+ const fieldEntries = Object.entries(fields);
170
+ const tsFields = ['createdAt', 'updatedAt'];
171
+ if (paranoid)
172
+ tsFields.push('deletedAt');
173
+ if (timestamps) {
174
+ tsFields.forEach((tf) => {
175
+ if (!fieldEntries.find(([k]) => k === tf)) {
176
+ fieldEntries.push([tf, { type: 'DATE', allowNull: tf === 'deletedAt' }]);
177
+ }
178
+ });
179
+ }
180
+ const hasAssoc = associations.length > 0;
181
+ const assocBlockH = hasAssoc ? ASSOC_HDR + associations.length * ASSOC_ROW + 8 : 0;
182
+ const cardH = HDR + COL_HDR + fieldEntries.length * ROW_H + assocBlockH + FOOTER;
183
+ const svgW = W + 2 * PAD;
184
+ const svgH = cardH + 2 * PAD;
185
+ const cx = PAD;
186
+ const cy = PAD;
187
+ const uid = name.replace(/\W/g, '');
188
+ const doc = new svg_dom_1.SvgDocument(svgW, svgH);
189
+ const svg = doc.root;
190
+ // ── Background ──────────────────────────────────────────────────────
191
+ svg.child('rect', { width: svgW, height: svgH, fill: t.canvasBg });
192
+ // ── Defs: header gradient, drop shadow, card clip ───────────────────
193
+ doc.linearGradient(`hg${uid}`, [
194
+ [0, accent.from],
195
+ [55, accent.mid],
196
+ [100, accent.to],
197
+ ]);
198
+ const filter = doc
199
+ .defs()
200
+ .child('filter', { id: `sh${uid}`, x: '-6%', y: '-6%', width: '112%', height: '125%' });
201
+ filter.child('feDropShadow', { dx: 0, dy: 6, stdDeviation: 10, 'flood-color': '#00000025' });
202
+ doc
203
+ .defs()
204
+ .child('clipPath', { id: `cp${uid}` })
205
+ .child('rect', { x: cx, y: cy, width: W, height: cardH, rx: R });
206
+ // ── Card shell + surface ────────────────────────────────────────────
207
+ svg.child('rect', {
208
+ x: cx - 1,
209
+ y: cy - 1,
210
+ width: W + 2,
211
+ height: cardH + 2,
212
+ rx: R + 1,
213
+ fill: t.border,
214
+ filter: `url(#sh${uid})`,
215
+ });
216
+ svg.child('rect', {
217
+ x: cx,
218
+ y: cy,
219
+ width: W,
220
+ height: cardH,
221
+ rx: R,
222
+ fill: t.cardBg,
223
+ 'clip-path': `url(#cp${uid})`,
224
+ });
225
+ // ── Header ──────────────────────────────────────────────────────────
226
+ svg.child('rect', { x: cx, y: cy, width: W, height: HDR, fill: `url(#hg${uid})` });
227
+ const icon = svg.child('g', { transform: `translate(${cx + 16}, ${cy + 14})` });
228
+ icon.child('rect', { x: 0, y: 0, width: 40, height: 40, rx: 10, fill: 'rgba(255,255,255,0.18)' });
229
+ icon.child('rect', { x: 12, y: 8, width: 16, height: 20, rx: 2, fill: 'white', opacity: 0.9 });
230
+ icon.child('rect', { x: 15, y: 4, width: 10, height: 6, rx: 1, fill: 'white', opacity: 0.9 });
231
+ icon.child('rect', { x: 17, y: 13, width: 6, height: 2, rx: 1, fill: accent.mid });
232
+ icon.child('rect', { x: 17, y: 17, width: 6, height: 2, rx: 1, fill: accent.mid });
233
+ icon.child('rect', { x: 17, y: 21, width: 4, height: 2, rx: 1, fill: accent.mid });
234
+ svg
235
+ .child('text', {
236
+ x: cx + 68,
237
+ y: cy + 34,
238
+ 'font-family': 'system-ui,-apple-system,sans-serif',
239
+ 'font-size': 22,
240
+ 'font-weight': 800,
241
+ fill: '#FFFFFF',
242
+ })
243
+ .text(name);
244
+ if (comment) {
245
+ svg
246
+ .child('text', {
247
+ x: cx + 68,
248
+ y: cy + 54,
249
+ 'font-family': 'system-ui,sans-serif',
250
+ 'font-size': 11,
251
+ fill: 'rgba(255,255,255,0.65)',
252
+ })
253
+ .text(comment);
254
+ }
255
+ svg.child('rect', {
256
+ x: cx + W - 96,
257
+ y: cy + 20,
258
+ width: 80,
259
+ height: 26,
260
+ rx: 13,
261
+ fill: 'rgba(255,255,255,0.18)',
262
+ });
263
+ svg
264
+ .child('text', {
265
+ x: cx + W - 56,
266
+ y: cy + 38,
267
+ 'font-family': "'SF Mono',monospace",
268
+ 'font-size': 11,
269
+ 'font-weight': 700,
270
+ fill: 'rgba(255,255,255,0.92)',
271
+ 'text-anchor': 'middle',
272
+ })
273
+ .text('ORM MODEL');
274
+ // ── Column header row ───────────────────────────────────────────────
275
+ const colHdrY = cy + HDR;
276
+ svg.child('rect', { x: cx, y: colHdrY, width: W, height: COL_HDR, fill: t.colHeaderBg });
277
+ const colHdr = (x, label) => svg
278
+ .child('text', {
279
+ x,
280
+ y: colHdrY + 22,
281
+ 'font-family': 'system-ui,sans-serif',
282
+ 'font-size': 11,
283
+ 'font-weight': 700,
284
+ fill: t.textSecondary,
285
+ 'letter-spacing': 0.5,
286
+ })
287
+ .text(label);
288
+ colHdr(cx + 36, 'COLUMN');
289
+ colHdr(cx + 225, 'TYPE');
290
+ colHdr(cx + 380, 'CONSTRAINTS');
291
+ svg.child('line', {
292
+ x1: cx,
293
+ y1: colHdrY + COL_HDR,
294
+ x2: cx + W,
295
+ y2: colHdrY + COL_HDR,
296
+ stroke: t.border,
297
+ 'stroke-width': 1,
298
+ });
299
+ // ── Field rows ──────────────────────────────────────────────────────
300
+ const fieldSectionY = colHdrY + COL_HDR;
301
+ fieldEntries.forEach(([colName, field], i) => {
302
+ const typeStr = resolveType(field);
303
+ const tc = typeColor(typeStr);
304
+ const rowY = fieldSectionY + i * ROW_H;
305
+ const midY = rowY + ROW_H / 2;
306
+ const textBase = midY + 5;
307
+ const rowBg = i % 2 === 0 ? t.cardBg : t.rowAlt;
308
+ const isTS = tsFields.includes(colName);
309
+ const isPK = !!field.primaryKey;
310
+ const isFK = !!(field.foreignKey || field.references);
311
+ svg.child('rect', { x: cx, y: rowY, width: W, height: ROW_H, fill: rowBg });
312
+ if (isPK)
313
+ svg.child('rect', { x: cx, y: rowY, width: 5, height: ROW_H, fill: '#F59E0B' });
314
+ else if (isFK)
315
+ svg.child('rect', { x: cx, y: rowY, width: 5, height: ROW_H, fill: '#3B82F6' });
316
+ // Row icon
317
+ const iconX = cx + 19;
318
+ if (isPK) {
319
+ svg
320
+ .child('g', { transform: `translate(${iconX - 7}, ${textBase - 10})` })
321
+ .child('path', {
322
+ d: 'M12.65 10A6 6 0 0 1 6 4a6 6 0 0 1 6.65 6H12.65zM2.93 4.93l6.42 6.42a8 8 0 0 1 9.3 0l1.42-1.42a1 1 0 0 0 0-1.41l-1.42-1.42a1 1 0 0 0-1.41 0l-6.42 6.42a1 1 0 0 0 0 1.41l1.42 1.42a1 1 0 0 0 1.41 0l6.42-6.42a8 8 0 0 0 0-11.3L13.08 2.22a1 1 0 0 0-1.08-.28H12a1 1 0 0 0-.77.36L8.7 4.91 2.93 4.93z',
323
+ fill: '#F59E0B',
324
+ });
325
+ }
326
+ else if (isFK) {
327
+ const g = svg.child('g', { transform: `translate(${iconX - 7}, ${textBase - 10})` });
328
+ g.child('path', {
329
+ d: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71',
330
+ stroke: '#3B82F6',
331
+ 'stroke-width': 2,
332
+ fill: 'none',
333
+ 'stroke-linecap': 'round',
334
+ });
335
+ g.child('path', {
336
+ d: 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71',
337
+ stroke: '#3B82F6',
338
+ 'stroke-width': 2,
339
+ fill: 'none',
340
+ 'stroke-linecap': 'round',
341
+ });
342
+ }
343
+ else if (isTS) {
344
+ const g = svg.child('g', { transform: `translate(${iconX - 7}, ${textBase - 10})` });
345
+ g.child('circle', { cx: 8, cy: 8, r: 7, stroke: '#64748B', 'stroke-width': 1.5, fill: 'none' });
346
+ g.child('path', {
347
+ d: 'M8 4v4l3 2',
348
+ stroke: '#64748B',
349
+ 'stroke-width': 1.5,
350
+ fill: 'none',
351
+ 'stroke-linecap': 'round',
352
+ });
353
+ }
354
+ else {
355
+ svg.child('circle', { cx: iconX, cy: midY, r: 3.5, fill: '#CBD5E1' });
356
+ }
357
+ // Field name
358
+ const nameW = isPK ? 700 : isFK ? 600 : 500;
359
+ const nameC = isPK ? '#F59E0B' : isFK ? '#3B82F6' : t.textPrimary;
360
+ const dispName = colName.length > 22 ? colName.slice(0, 21) + '…' : colName;
361
+ svg
362
+ .child('text', {
363
+ x: cx + 36,
364
+ y: textBase,
365
+ 'font-family': "'SF Mono','Fira Code',monospace",
366
+ 'font-size': 13,
367
+ 'font-weight': nameW,
368
+ fill: nameC,
369
+ })
370
+ .text(dispName);
371
+ // Type badge
372
+ const typX = cx + 225;
373
+ const typTxt = typeStr.length > 18 ? typeStr.slice(0, 17) + '…' : typeStr;
374
+ svg.child('rect', { x: typX, y: rowY + 11, width: 140, height: 20, rx: 6, fill: tc.bg });
375
+ svg
376
+ .child('text', {
377
+ x: typX + 70,
378
+ y: rowY + 25,
379
+ 'font-family': "'SF Mono',monospace",
380
+ 'font-size': 11,
381
+ 'font-weight': 600,
382
+ fill: tc.text,
383
+ 'text-anchor': 'middle',
384
+ })
385
+ .text(typTxt);
386
+ // Constraint badges
387
+ let bx = cx + 380;
388
+ for (const b of this.badges(field, isTS)) {
389
+ bx += this.drawBadge(svg, b, bx, rowY + 11) + 4;
390
+ }
391
+ // Default value
392
+ if (field.defaultValue !== undefined && field.defaultValue !== null && !isTS) {
393
+ svg
394
+ .child('text', {
395
+ x: cx + W - 10,
396
+ y: textBase,
397
+ 'font-family': 'monospace',
398
+ 'font-size': 10,
399
+ fill: '#94A3B8',
400
+ 'text-anchor': 'end',
401
+ })
402
+ .text(`default: ${String(field.defaultValue).slice(0, 10)}`);
403
+ }
404
+ // FK reference label
405
+ if (isFK && field.references) {
406
+ svg
407
+ .child('text', {
408
+ x: cx + 36,
409
+ y: rowY + ROW_H - 6,
410
+ 'font-family': 'monospace',
411
+ 'font-size': 9,
412
+ fill: '#94A3B8',
413
+ })
414
+ .text(`→ ${field.references.model}.${field.references.key}`);
415
+ }
416
+ svg.child('line', {
417
+ x1: cx,
418
+ y1: rowY + ROW_H,
419
+ x2: cx + W,
420
+ y2: rowY + ROW_H,
421
+ stroke: t.border,
422
+ 'stroke-width': 1,
423
+ });
424
+ });
425
+ // ── Associations ────────────────────────────────────────────────────
426
+ const assocBaseY = fieldSectionY + fieldEntries.length * ROW_H;
427
+ if (hasAssoc) {
428
+ svg.child('rect', { x: cx, y: assocBaseY, width: W, height: ASSOC_HDR, fill: '#F0F9FF' });
429
+ svg
430
+ .child('text', {
431
+ x: cx + 16,
432
+ y: assocBaseY + 24,
433
+ 'font-family': 'system-ui,sans-serif',
434
+ 'font-size': 12,
435
+ 'font-weight': 700,
436
+ fill: '#0369A1',
437
+ 'letter-spacing': 1,
438
+ })
439
+ .text('ASSOCIATIONS');
440
+ svg.child('line', {
441
+ x1: cx,
442
+ y1: assocBaseY + ASSOC_HDR,
443
+ x2: cx + W,
444
+ y2: assocBaseY + ASSOC_HDR,
445
+ stroke: '#BAE6FD',
446
+ 'stroke-width': 1,
447
+ });
448
+ associations.forEach((assoc, i) => {
449
+ const aY = assocBaseY + ASSOC_HDR + i * ASSOC_ROW;
450
+ const meta = ASSOC_META[assoc.type] ?? {
451
+ icon: '?',
452
+ color: '#475569',
453
+ bg: '#F1F5F9',
454
+ label: assoc.type,
455
+ };
456
+ const rowBg = i % 2 === 0 ? '#F0F9FF' : '#E0F2FE';
457
+ const aTextY = aY + 20;
458
+ svg.child('rect', { x: cx, y: aY, width: W, height: ASSOC_ROW, fill: rowBg });
459
+ const cbw = 34;
460
+ svg.child('rect', { x: cx + 10, y: aY + 6, width: cbw, height: 18, rx: 5, fill: meta.bg });
461
+ svg
462
+ .child('text', {
463
+ x: cx + 10 + cbw / 2,
464
+ y: aY + 19,
465
+ 'font-family': 'monospace',
466
+ 'font-size': 10,
467
+ 'font-weight': 700,
468
+ fill: meta.color,
469
+ 'text-anchor': 'middle',
470
+ })
471
+ .text(meta.icon);
472
+ svg
473
+ .child('text', {
474
+ x: cx + 52,
475
+ y: aTextY,
476
+ 'font-family': 'system-ui,sans-serif',
477
+ 'font-size': 12,
478
+ 'font-weight': 600,
479
+ fill: '#374151',
480
+ })
481
+ .text(meta.label);
482
+ svg
483
+ .child('text', { x: cx + 148, y: aTextY, 'font-size': 14, fill: '#94A3B8' })
484
+ .text('→');
485
+ svg
486
+ .child('text', {
487
+ x: cx + 168,
488
+ y: aTextY,
489
+ 'font-family': "'SF Mono',monospace",
490
+ 'font-size': 12,
491
+ 'font-weight': 700,
492
+ fill: meta.color,
493
+ })
494
+ .text(assoc.target);
495
+ if (assoc.foreignKey) {
496
+ svg
497
+ .child('text', {
498
+ x: cx + 290,
499
+ y: aTextY,
500
+ 'font-family': 'monospace',
501
+ 'font-size': 10,
502
+ fill: '#64748B',
503
+ })
504
+ .text(`(${assoc.foreignKey})`);
505
+ }
506
+ if (assoc.through) {
507
+ svg
508
+ .child('text', {
509
+ x: cx + 370,
510
+ y: aTextY,
511
+ 'font-family': 'monospace',
512
+ 'font-size': 10,
513
+ fill: '#7C3AED',
514
+ })
515
+ .text(`via: ${assoc.through}`);
516
+ }
517
+ if (assoc.as) {
518
+ svg
519
+ .child('text', {
520
+ x: cx + W - 10,
521
+ y: aTextY,
522
+ 'font-family': 'monospace',
523
+ 'font-size': 10,
524
+ fill: '#6366F1',
525
+ 'text-anchor': 'end',
526
+ })
527
+ .text(`as: ${assoc.as}`);
528
+ }
529
+ svg.child('line', {
530
+ x1: cx,
531
+ y1: aY + ASSOC_ROW,
532
+ x2: cx + W,
533
+ y2: aY + ASSOC_ROW,
534
+ stroke: '#BAE6FD',
535
+ 'stroke-width': 0.5,
536
+ });
537
+ });
538
+ }
539
+ // ── Footer ──────────────────────────────────────────────────────────
540
+ const footerY = assocBaseY + assocBlockH;
541
+ svg.child('rect', { x: cx, y: footerY, width: W, height: FOOTER, fill: t.footerBg });
542
+ svg.child('rect', {
543
+ x: cx,
544
+ y: footerY + FOOTER - R,
545
+ width: W,
546
+ height: R,
547
+ rx: R,
548
+ fill: t.footerBg,
549
+ });
550
+ const footTable = svg.child('text', {
551
+ x: cx + 16,
552
+ y: footerY + 28,
553
+ 'font-family': 'system-ui,sans-serif',
554
+ 'font-size': 11,
555
+ fill: '#94A3B8',
556
+ });
557
+ footTable.text('TABLE: ');
558
+ footTable.child('tspan', { 'font-family': "'SF Mono',monospace", fill: accent.to }).text(tableName);
559
+ if (timestamps) {
560
+ const ts = svg.child('text', {
561
+ x: cx + 230,
562
+ y: footerY + 28,
563
+ 'font-family': 'system-ui,sans-serif',
564
+ 'font-size': 11,
565
+ fill: '#94A3B8',
566
+ });
567
+ ts.child('tspan', { fill: accent.to }).text('◷');
568
+ ts.text(' timestamps');
569
+ }
570
+ if (paranoid) {
571
+ const pn = svg.child('text', {
572
+ x: cx + 340,
573
+ y: footerY + 28,
574
+ 'font-family': 'system-ui,sans-serif',
575
+ 'font-size': 11,
576
+ fill: '#94A3B8',
577
+ });
578
+ pn.child('tspan', { fill: '#F87171' }).text('⌫');
579
+ pn.text(' paranoid');
580
+ }
581
+ svg
582
+ .child('text', {
583
+ x: cx + W - 10,
584
+ y: footerY + 28,
585
+ 'font-family': 'system-ui,sans-serif',
586
+ 'font-size': 11,
587
+ fill: '#475569',
588
+ 'text-anchor': 'end',
589
+ })
590
+ .text(`${fieldEntries.length} column${fieldEntries.length !== 1 ? 's' : ''}`);
591
+ return doc.toString('Generated by ts-prorm-orm ModelDiagram');
592
+ }
593
+ /**
594
+ * Write the SVG to a file. Creates parent directories if needed.
595
+ */
596
+ save(outputPath) {
597
+ const resolved = path.resolve(outputPath);
598
+ const dir = path.dirname(resolved);
599
+ if (!fs.existsSync(dir))
600
+ fs.mkdirSync(dir, { recursive: true });
601
+ const svg = this.generate();
602
+ fs.writeFileSync(resolved, svg, 'utf-8');
603
+ return resolved;
604
+ }
605
+ }
606
+ exports.ModelDiagram = ModelDiagram;
607
+ // Layout constants (px)
608
+ ModelDiagram.W = 600;
609
+ ModelDiagram.HDR = 68;
610
+ ModelDiagram.COL_HDR = 32;
611
+ ModelDiagram.ROW_H = 40;
612
+ ModelDiagram.ASSOC_HDR = 36;
613
+ ModelDiagram.ASSOC_ROW = 30;
614
+ ModelDiagram.FOOTER = 42;
615
+ ModelDiagram.PAD = 24;
616
+ ModelDiagram.R = 14; // border-radius