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,432 @@
1
+ "use strict";
2
+ /**
3
+ * DependencyDiagram — renders a directed dependency graph of models/tables.
4
+ *
5
+ * Nodes are models/tables; a directed edge `from → to` means "from depends on
6
+ * to" (e.g. table `from` holds a foreign key into table `to`). The generator:
7
+ *
8
+ * - Computes a layered / topological layout: each node's layer is the longest
9
+ * dependency path to a leaf. Nodes with no dependencies sit in layer 0;
10
+ * their dependents fan out into higher layers.
11
+ * - Detects cycles (back edges) with a DFS colouring pass and visually flags
12
+ * the cyclic edges in red. The layering pass carries its own visited guard
13
+ * so a cycle can never send it into an infinite loop.
14
+ * - Draws each node as a labelled rounded box sized to its label, with arrows
15
+ * between dependent boxes.
16
+ *
17
+ * Accent colour (`color`) drives the node fills / arrowheads; the theme
18
+ * (`light` | `dark`) drives the canvas + text colours. Both genuinely change
19
+ * real fill/stroke values in the emitted SVG. The viewBox auto-sizes to the
20
+ * computed layout.
21
+ *
22
+ * Usage:
23
+ * const d = new DependencyDiagram({ nodes, edges, title: 'Schema deps' });
24
+ * const svg = d.generate();
25
+ * d.save('./diagrams/deps.svg');
26
+ */
27
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
28
+ if (k2 === undefined) k2 = k;
29
+ var desc = Object.getOwnPropertyDescriptor(m, k);
30
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
31
+ desc = { enumerable: true, get: function() { return m[k]; } };
32
+ }
33
+ Object.defineProperty(o, k2, desc);
34
+ }) : (function(o, m, k, k2) {
35
+ if (k2 === undefined) k2 = k;
36
+ o[k2] = m[k];
37
+ }));
38
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
39
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
40
+ }) : function(o, v) {
41
+ o["default"] = v;
42
+ });
43
+ var __importStar = (this && this.__importStar) || (function () {
44
+ var ownKeys = function(o) {
45
+ ownKeys = Object.getOwnPropertyNames || function (o) {
46
+ var ar = [];
47
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
48
+ return ar;
49
+ };
50
+ return ownKeys(o);
51
+ };
52
+ return function (mod) {
53
+ if (mod && mod.__esModule) return mod;
54
+ var result = {};
55
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
56
+ __setModuleDefault(result, mod);
57
+ return result;
58
+ };
59
+ })();
60
+ Object.defineProperty(exports, "__esModule", { value: true });
61
+ exports.DependencyDiagram = void 0;
62
+ const fs = __importStar(require("fs"));
63
+ const path = __importStar(require("path"));
64
+ const palette_1 = require("./palette");
65
+ const svg_dom_1 = require("./svg-dom");
66
+ // ─── Main class ──────────────────────────────────────────────────────────────
67
+ class DependencyDiagram {
68
+ constructor(options) {
69
+ this.opts = {
70
+ title: options.title ?? 'Dependency Graph',
71
+ theme: options.theme ?? 'light',
72
+ color: options.color ?? '',
73
+ direction: options.direction ?? 'horizontal',
74
+ nodes: options.nodes ?? [],
75
+ edges: options.edges ?? [],
76
+ };
77
+ }
78
+ // ── Graph analysis ──────────────────────────────────────────────────────
79
+ /** Adjacency: node → the nodes it depends on (its outgoing edge targets). */
80
+ buildAdjacency(names) {
81
+ const adj = new Map();
82
+ for (const n of names)
83
+ adj.set(n, []);
84
+ for (const e of this.opts.edges) {
85
+ if (!names.has(e.from) || !names.has(e.to))
86
+ continue;
87
+ if (e.from === e.to)
88
+ continue; // ignore trivial self-loops for layering
89
+ adj.get(e.from).push(e.to);
90
+ }
91
+ return adj;
92
+ }
93
+ /**
94
+ * Assign each node a layer = longest dependency path length (0 for nodes with
95
+ * no dependencies). Uses memoisation plus an on-stack guard so cyclic edges
96
+ * are simply skipped and never cause infinite recursion.
97
+ */
98
+ computeLayers(names, adj) {
99
+ const layer = new Map();
100
+ const onStack = new Set();
101
+ const visit = (node) => {
102
+ if (layer.has(node))
103
+ return layer.get(node);
104
+ if (onStack.has(node))
105
+ return 0; // cycle guard — break the recursion
106
+ onStack.add(node);
107
+ let best = 0;
108
+ for (const dep of adj.get(node) ?? []) {
109
+ best = Math.max(best, visit(dep) + 1);
110
+ }
111
+ onStack.delete(node);
112
+ layer.set(node, best);
113
+ return best;
114
+ };
115
+ for (const n of names)
116
+ visit(n);
117
+ return layer;
118
+ }
119
+ /**
120
+ * Detect cyclic edges via DFS colouring. Returns a set of "from→to" keys that
121
+ * are back edges (participate in a cycle).
122
+ */
123
+ detectCyclicEdges(names, adj) {
124
+ const cyclic = new Set();
125
+ const WHITE = 0;
126
+ const GRAY = 1;
127
+ const BLACK = 2;
128
+ const color = new Map();
129
+ for (const n of names)
130
+ color.set(n, WHITE);
131
+ const dfs = (node) => {
132
+ color.set(node, GRAY);
133
+ for (const dep of adj.get(node) ?? []) {
134
+ const c = color.get(dep);
135
+ if (c === GRAY) {
136
+ cyclic.add(`${node}${dep}`); // back edge → cycle
137
+ }
138
+ else if (c === WHITE) {
139
+ dfs(dep);
140
+ }
141
+ }
142
+ color.set(node, BLACK);
143
+ };
144
+ for (const n of names)
145
+ if (color.get(n) === WHITE)
146
+ dfs(n);
147
+ return cyclic;
148
+ }
149
+ // ── Geometry helpers ─────────────────────────────────────────────────────
150
+ nodeWidth(name) {
151
+ return Math.max(DependencyDiagram.MIN_NODE_W, Math.round(name.length * DependencyDiagram.CHAR_W + DependencyDiagram.NODE_PAD_X * 2));
152
+ }
153
+ /**
154
+ * Point on the boundary of a rectangle along the line from the rect centre
155
+ * toward an external point — used so arrowheads land on the box edge.
156
+ */
157
+ edgePoint(node, tx, ty) {
158
+ const cx = node.x + node.w / 2;
159
+ const cy = node.y + node.h / 2;
160
+ let dx = tx - cx;
161
+ let dy = ty - cy;
162
+ if (dx === 0 && dy === 0)
163
+ return { x: cx, y: cy };
164
+ const halfW = node.w / 2;
165
+ const halfH = node.h / 2;
166
+ const scaleX = dx !== 0 ? halfW / Math.abs(dx) : Infinity;
167
+ const scaleY = dy !== 0 ? halfH / Math.abs(dy) : Infinity;
168
+ const scale = Math.min(scaleX, scaleY);
169
+ return { x: cx + dx * scale, y: cy + dy * scale };
170
+ }
171
+ // ── Generate full SVG string ─────────────────────────────────────────────
172
+ generate() {
173
+ const { title, theme, color, direction } = this.opts;
174
+ const accent = (0, palette_1.resolveAccent)(color);
175
+ const palettes = (0, palette_1.rotatedPalettes)(color);
176
+ const t = (0, palette_1.resolveTheme)(theme);
177
+ const horizontal = direction !== 'vertical';
178
+ const PAD = DependencyDiagram.PAD;
179
+ const TITLE_H = DependencyDiagram.TITLE_H;
180
+ const NODE_H = DependencyDiagram.NODE_H;
181
+ const LAYER_GAP = DependencyDiagram.LAYER_GAP;
182
+ const NODE_GAP = DependencyDiagram.NODE_GAP;
183
+ const R = DependencyDiagram.R;
184
+ // De-duplicate nodes by name, preserving first occurrence.
185
+ const seen = new Set();
186
+ const nodeDefs = [];
187
+ for (const n of this.opts.nodes) {
188
+ if (!n || seen.has(n.name))
189
+ continue;
190
+ seen.add(n.name);
191
+ nodeDefs.push(n);
192
+ }
193
+ const names = nodeDefs.map((n) => n.name);
194
+ const nameSet = new Set(names);
195
+ const adj = this.buildAdjacency(nameSet);
196
+ const layerMap = this.computeLayers(names, adj);
197
+ const cyclicEdges = this.detectCyclicEdges(names, adj);
198
+ // Group nodes into layers preserving input order for stable slots.
199
+ const layersArr = [];
200
+ for (const n of nodeDefs) {
201
+ const l = layerMap.get(n.name) ?? 0;
202
+ (layersArr[l] ?? (layersArr[l] = [])).push(n);
203
+ }
204
+ for (let i = 0; i < layersArr.length; i++)
205
+ layersArr[i] ?? (layersArr[i] = []);
206
+ // Assign a stable palette per group (for node fill tinting).
207
+ const groupPalette = new Map();
208
+ let gi = 0;
209
+ for (const n of nodeDefs) {
210
+ const g = n.group;
211
+ if (g && !groupPalette.has(g)) {
212
+ groupPalette.set(g, palettes[gi % palettes.length]);
213
+ gi++;
214
+ }
215
+ }
216
+ // ── Compute placements ──────────────────────────────────────────────
217
+ const placed = new Map();
218
+ if (horizontal) {
219
+ // Layers = columns (left→right). Nodes stacked vertically within a column.
220
+ const layerW = layersArr.map((layer) => layer.reduce((m, n) => Math.max(m, this.nodeWidth(n.name)), DependencyDiagram.MIN_NODE_W));
221
+ const colX = [];
222
+ let cursor = PAD;
223
+ for (let l = 0; l < layersArr.length; l++) {
224
+ colX[l] = cursor;
225
+ cursor += layerW[l] + LAYER_GAP;
226
+ }
227
+ const contentTop = PAD + TITLE_H;
228
+ for (let l = 0; l < layersArr.length; l++) {
229
+ layersArr[l].forEach((n, slot) => {
230
+ const w = this.nodeWidth(n.name);
231
+ const x = colX[l] + (layerW[l] - w) / 2;
232
+ const y = contentTop + slot * (NODE_H + NODE_GAP);
233
+ this.place(placed, n, l, slot, x, y, w, NODE_H, accent, groupPalette, t);
234
+ });
235
+ }
236
+ }
237
+ else {
238
+ // Layers = rows (top→bottom). Nodes spread horizontally within a row.
239
+ const colW = names.reduce((m, nm) => Math.max(m, this.nodeWidth(nm)), DependencyDiagram.MIN_NODE_W);
240
+ const contentLeft = PAD;
241
+ for (let l = 0; l < layersArr.length; l++) {
242
+ const y = PAD + TITLE_H + l * (NODE_H + LAYER_GAP);
243
+ layersArr[l].forEach((n, slot) => {
244
+ const w = this.nodeWidth(n.name);
245
+ const x = contentLeft + slot * (colW + NODE_GAP) + (colW - w) / 2;
246
+ this.place(placed, n, l, slot, x, y, w, NODE_H, accent, groupPalette, t);
247
+ });
248
+ }
249
+ }
250
+ // ── Auto-size the canvas ────────────────────────────────────────────
251
+ let maxX = 0;
252
+ let maxY = 0;
253
+ for (const p of placed.values()) {
254
+ maxX = Math.max(maxX, p.x + p.w);
255
+ maxY = Math.max(maxY, p.y + p.h);
256
+ }
257
+ const svgW = Math.round(maxX + PAD);
258
+ const svgH = Math.round(maxY + PAD);
259
+ // ── Build the document ──────────────────────────────────────────────
260
+ const doc = new svg_dom_1.SvgDocument(svgW, svgH);
261
+ const svg = doc.root;
262
+ // Background
263
+ svg.child('rect', { width: svgW, height: svgH, fill: t.canvasBg });
264
+ // Arrowhead markers (accent + cyclic red).
265
+ const CYCLE_COLOR = '#DC2626';
266
+ this.defineArrow(doc, 'depArrow', accent.mid);
267
+ this.defineArrow(doc, 'depArrowCycle', CYCLE_COLOR);
268
+ // Title
269
+ svg
270
+ .child('text', {
271
+ x: PAD,
272
+ y: PAD + 6,
273
+ 'font-family': 'system-ui,-apple-system,sans-serif',
274
+ 'font-size': 22,
275
+ 'font-weight': 800,
276
+ fill: t.textPrimary,
277
+ })
278
+ .text(title);
279
+ svg
280
+ .child('text', {
281
+ x: PAD,
282
+ y: PAD + 26,
283
+ 'font-family': 'system-ui,sans-serif',
284
+ 'font-size': 11,
285
+ fill: t.textSecondary,
286
+ })
287
+ .text(`${placed.size} node${placed.size !== 1 ? 's' : ''} · ${this.opts.edges.length} edge${this.opts.edges.length !== 1 ? 's' : ''}${cyclicEdges.size ? ` · ${cyclicEdges.size} cyclic` : ''}`);
288
+ // ── Edges (drawn before nodes so boxes sit on top) ──────────────────
289
+ const edgeLayer = svg.child('g', { 'stroke-linecap': 'round' });
290
+ for (const e of this.opts.edges) {
291
+ const from = placed.get(e.from);
292
+ const to = placed.get(e.to);
293
+ if (!from || !to)
294
+ continue;
295
+ const isCyclic = cyclicEdges.has(`${e.from}${e.to}`);
296
+ const isSelf = e.from === e.to;
297
+ const stroke = isCyclic ? CYCLE_COLOR : accent.mid;
298
+ const marker = isCyclic ? 'url(#depArrowCycle)' : 'url(#depArrow)';
299
+ if (isSelf) {
300
+ // Self-dependency: small loop above the node.
301
+ const lx = from.x + from.w / 2;
302
+ const ly = from.y;
303
+ edgeLayer.child('path', {
304
+ d: `M ${lx - 10} ${ly} C ${lx - 26} ${ly - 34}, ${lx + 26} ${ly - 34}, ${lx + 10} ${ly}`,
305
+ fill: 'none',
306
+ stroke,
307
+ 'stroke-width': isCyclic ? 2.4 : 1.8,
308
+ 'stroke-dasharray': isCyclic ? '6 4' : undefined,
309
+ 'marker-end': marker,
310
+ });
311
+ continue;
312
+ }
313
+ const toCx = to.x + to.w / 2;
314
+ const toCy = to.y + to.h / 2;
315
+ const fromCx = from.x + from.w / 2;
316
+ const fromCy = from.y + from.h / 2;
317
+ const start = this.edgePoint(from, toCx, toCy);
318
+ const end = this.edgePoint(to, fromCx, fromCy);
319
+ edgeLayer.child('line', {
320
+ x1: start.x,
321
+ y1: start.y,
322
+ x2: end.x,
323
+ y2: end.y,
324
+ stroke,
325
+ 'stroke-width': isCyclic ? 2.4 : 1.7,
326
+ 'stroke-dasharray': isCyclic ? '6 4' : undefined,
327
+ 'marker-end': marker,
328
+ });
329
+ if (e.label) {
330
+ svg
331
+ .child('text', {
332
+ x: (start.x + end.x) / 2,
333
+ y: (start.y + end.y) / 2 - 4,
334
+ 'font-family': "'SF Mono',monospace",
335
+ 'font-size': 9,
336
+ fill: isCyclic ? CYCLE_COLOR : t.textSecondary,
337
+ 'text-anchor': 'middle',
338
+ })
339
+ .text(e.label);
340
+ }
341
+ }
342
+ // ── Nodes ───────────────────────────────────────────────────────────
343
+ for (const n of nodeDefs) {
344
+ const p = placed.get(n.name);
345
+ if (!p)
346
+ continue;
347
+ const g = svg.child('g');
348
+ g.child('rect', {
349
+ x: p.x,
350
+ y: p.y,
351
+ width: p.w,
352
+ height: p.h,
353
+ rx: R,
354
+ fill: p.fill,
355
+ stroke: p.stroke,
356
+ 'stroke-width': 2,
357
+ });
358
+ g.child('text', {
359
+ x: p.x + p.w / 2,
360
+ y: p.y + p.h / 2 + 5,
361
+ 'font-family': "'SF Mono','Fira Code',monospace",
362
+ 'font-size': 14,
363
+ 'font-weight': 700,
364
+ fill: p.text,
365
+ 'text-anchor': 'middle',
366
+ }).text(n.name);
367
+ // Layer badge on the box shoulder.
368
+ g.child('text', {
369
+ x: p.x + 8,
370
+ y: p.y + 13,
371
+ 'font-family': 'system-ui,sans-serif',
372
+ 'font-size': 9,
373
+ 'font-weight': 700,
374
+ fill: p.text,
375
+ opacity: 0.55,
376
+ }).text(`L${p.layer}`);
377
+ }
378
+ return doc.toString('Generated by ts-prorm-orm DependencyDiagram');
379
+ }
380
+ // ── place() records a node's geometry + resolved colours ─────────────────
381
+ place(placed, n, layer, slot, x, y, w, h, accent, groupPalette, t) {
382
+ const pal = (n.group && groupPalette.get(n.group)) || accent;
383
+ placed.set(n.name, {
384
+ name: n.name,
385
+ group: n.group,
386
+ layer,
387
+ slot,
388
+ x,
389
+ y,
390
+ w,
391
+ h,
392
+ fill: pal.light,
393
+ stroke: pal.mid,
394
+ text: pal.text,
395
+ });
396
+ }
397
+ // ── Arrowhead marker in <defs> ───────────────────────────────────────────
398
+ defineArrow(doc, id, color) {
399
+ const marker = doc.defs().child('marker', {
400
+ id,
401
+ viewBox: '0 0 10 10',
402
+ refX: 9,
403
+ refY: 5,
404
+ markerWidth: 7,
405
+ markerHeight: 7,
406
+ orient: 'auto-start-reverse',
407
+ });
408
+ marker.child('path', { d: 'M 0 0 L 10 5 L 0 10 z', fill: color });
409
+ }
410
+ /**
411
+ * Write the SVG to a file, creating parent directories as needed.
412
+ */
413
+ save(outputPath) {
414
+ const resolved = path.resolve(outputPath);
415
+ const dir = path.dirname(resolved);
416
+ if (!fs.existsSync(dir))
417
+ fs.mkdirSync(dir, { recursive: true });
418
+ fs.writeFileSync(resolved, this.generate(), 'utf-8');
419
+ return resolved;
420
+ }
421
+ }
422
+ exports.DependencyDiagram = DependencyDiagram;
423
+ // Layout constants (px)
424
+ DependencyDiagram.PAD = 40;
425
+ DependencyDiagram.TITLE_H = 48;
426
+ DependencyDiagram.NODE_H = 40;
427
+ DependencyDiagram.CHAR_W = 8.4;
428
+ DependencyDiagram.NODE_PAD_X = 22;
429
+ DependencyDiagram.MIN_NODE_W = 72;
430
+ DependencyDiagram.LAYER_GAP = 90; // gap between layer columns/rows
431
+ DependencyDiagram.NODE_GAP = 26; // gap between nodes in a layer
432
+ DependencyDiagram.R = 9;