ts-prorm-orm 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1111 -0
- package/LICENSE +21 -0
- package/README.md +573 -0
- package/dist/audit/history-query.js +180 -0
- package/dist/audit/index.js +23 -0
- package/dist/audit/logger.js +236 -0
- package/dist/cache/cache-manager.js +84 -0
- package/dist/cache/index.js +16 -0
- package/dist/cache/redis-cluster-cache.js +557 -0
- package/dist/cli.js +2200 -0
- package/dist/compliance/audit-trail.js +68 -0
- package/dist/compliance/backup-verification.js +685 -0
- package/dist/compliance/breach-detector.js +505 -0
- package/dist/compliance/consent-record.js +227 -0
- package/dist/compliance/consent-versioning.js +331 -0
- package/dist/compliance/cross-border-log.js +530 -0
- package/dist/compliance/data-classifier.js +258 -0
- package/dist/compliance/data-lineage.js +565 -0
- package/dist/compliance/data-masker.js +303 -0
- package/dist/compliance/data-portability.js +265 -0
- package/dist/compliance/data-retention.js +195 -0
- package/dist/compliance/dsar-workflow.js +342 -0
- package/dist/compliance/field-encryption.js +223 -0
- package/dist/compliance/immutable-record.js +148 -0
- package/dist/compliance/index.js +260 -0
- package/dist/compliance/privacy-impact-assessment.js +364 -0
- package/dist/compliance/pseudonymization.js +269 -0
- package/dist/compliance/query-firewall.js +1182 -0
- package/dist/compliance/rate-limiter.js +580 -0
- package/dist/compliance/right-to-erasure.js +117 -0
- package/dist/compliance/row-level-security.js +246 -0
- package/dist/compliance/security-decorator.js +574 -0
- package/dist/compliance/security-monitor.js +525 -0
- package/dist/compliance/sensitive-data-discovery.js +476 -0
- package/dist/compliance/session-isolation.js +472 -0
- package/dist/compliance/tls-enforcer.js +108 -0
- package/dist/compliance/worm-storage.js +712 -0
- package/dist/connection-manager.js +198 -0
- package/dist/connection-pool.js +519 -0
- package/dist/decorators/audit.js +136 -0
- package/dist/decorators/belongs-to-many.js +115 -0
- package/dist/decorators/belongs-to.js +115 -0
- package/dist/decorators/check.js +435 -0
- package/dist/decorators/collate.js +329 -0
- package/dist/decorators/comment.js +205 -0
- package/dist/decorators/database-settings.js +236 -0
- package/dist/decorators/default.js +244 -0
- package/dist/decorators/encryption.js +235 -0
- package/dist/decorators/engine.js +97 -0
- package/dist/decorators/fk-constraints.js +594 -0
- package/dist/decorators/foreign-table.js +136 -0
- package/dist/decorators/generated.js +274 -0
- package/dist/decorators/has-many.js +127 -0
- package/dist/decorators/has-one.js +116 -0
- package/dist/decorators/hstore.js +129 -0
- package/dist/decorators/index.js +355 -0
- package/dist/decorators/json-column.js +83 -0
- package/dist/decorators/jsonb.js +101 -0
- package/dist/decorators/orm-decorators.js +425 -0
- package/dist/decorators/permissions.js +294 -0
- package/dist/decorators/procedure.js +210 -0
- package/dist/decorators/query-options.js +556 -0
- package/dist/decorators/range.js +167 -0
- package/dist/decorators/set-column.js +83 -0
- package/dist/decorators/spatial.js +114 -0
- package/dist/decorators/storage.js +580 -0
- package/dist/decorators/timezone.js +512 -0
- package/dist/decorators/trigger.js +90 -0
- package/dist/decorators/uuid.js +135 -0
- package/dist/decorators/view.js +258 -0
- package/dist/diagrams/chen-diagram.js +354 -0
- package/dist/diagrams/class-diagram.js +384 -0
- package/dist/diagrams/dependency-diagram.js +432 -0
- package/dist/diagrams/er-diagram.js +605 -0
- package/dist/diagrams/flow-diagram.js +394 -0
- package/dist/diagrams/gantt-diagram.js +411 -0
- package/dist/diagrams/index-diagram.js +353 -0
- package/dist/diagrams/index.js +184 -0
- package/dist/diagrams/migration-diagram.js +316 -0
- package/dist/diagrams/model-diagram.js +616 -0
- package/dist/diagrams/package-diagram.js +376 -0
- package/dist/diagrams/palette.js +85 -0
- package/dist/diagrams/relational-diagram.js +455 -0
- package/dist/diagrams/schemadoc-diagram.js +309 -0
- package/dist/diagrams/sequence-diagram.js +307 -0
- package/dist/diagrams/state-diagram.js +344 -0
- package/dist/diagrams/svg-dom.js +111 -0
- package/dist/diagrams/tree-diagram.js +250 -0
- package/dist/dialects/clickhouse/index.js +1742 -0
- package/dist/dialects/cockroachdb/index.js +4677 -0
- package/dist/dialects/cratedb/index.js +441 -0
- package/dist/dialects/databricks/index.js +517 -0
- package/dist/dialects/db2/index.js +2225 -0
- package/dist/dialects/dialect.js +720 -0
- package/dist/dialects/duckdb/index.js +2014 -0
- package/dist/dialects/exasol/index.js +357 -0
- package/dist/dialects/firebird/index.js +495 -0
- package/dist/dialects/greenplum/index.js +457 -0
- package/dist/dialects/hana/index.js +1741 -0
- package/dist/dialects/mariadb/index.js +3590 -0
- package/dist/dialects/mssql/index.js +2296 -0
- package/dist/dialects/mysql/index.js +4058 -0
- package/dist/dialects/oracle/index.js +2999 -0
- package/dist/dialects/postgres/index.js +5319 -0
- package/dist/dialects/query-stream-helper.js +152 -0
- package/dist/dialects/questdb/index.js +433 -0
- package/dist/dialects/redshift/index.js +2277 -0
- package/dist/dialects/singlestore/index.js +354 -0
- package/dist/dialects/snowflake/index.js +2082 -0
- package/dist/dialects/spanner/index.js +1768 -0
- package/dist/dialects/sqlite/index.js +3382 -0
- package/dist/dialects/tidb/index.js +377 -0
- package/dist/dialects/timescaledb/index.js +164 -0
- package/dist/dialects/trino/index.js +420 -0
- package/dist/dialects/turso/index.js +227 -0
- package/dist/dialects/vertica/index.js +317 -0
- package/dist/dialects/yugabytedb/index.js +424 -0
- package/dist/errors/index.js +472 -0
- package/dist/errors/utils.js +340 -0
- package/dist/errors.js +21 -0
- package/dist/extensions/catalog/cloud-warehouse-features.js +207 -0
- package/dist/extensions/catalog/mssql-features.js +147 -0
- package/dist/extensions/catalog/mysql-mariadb-plugins.js +229 -0
- package/dist/extensions/catalog/oracle-db2-features.js +196 -0
- package/dist/extensions/catalog/postgres-extensions.js +516 -0
- package/dist/extensions/index.js +71 -0
- package/dist/extensions/types.js +13 -0
- package/dist/foreign-data.js +173 -0
- package/dist/hooks/hooks-manager.js +350 -0
- package/dist/hooks/index.js +37 -0
- package/dist/index.js +463 -0
- package/dist/logging.js +335 -0
- package/dist/migrations/index.js +40 -0
- package/dist/migrations/migration.js +196 -0
- package/dist/migrations/migrator.js +411 -0
- package/dist/migrations/prormmigration.js +275 -0
- package/dist/migrations/query-interface.js +435 -0
- package/dist/migrations/seeder.js +353 -0
- package/dist/models/associations.js +852 -0
- package/dist/models/constraints.js +288 -0
- package/dist/models/data-types.js +2518 -0
- package/dist/models/decorators.js +445 -0
- package/dist/models/index.js +33 -0
- package/dist/models/indexes.js +531 -0
- package/dist/models/methods.js +382 -0
- package/dist/models/model-manager.js +103 -0
- package/dist/models/model.js +5349 -0
- package/dist/models/operators.js +67 -0
- package/dist/models/scopes.js +189 -0
- package/dist/models/typescript-types.js +26 -0
- package/dist/nosql/aerospike/index.js +205 -0
- package/dist/nosql/allegrograph/index.js +169 -0
- package/dist/nosql/arangodb/index.js +364 -0
- package/dist/nosql/azure-blob/index.js +206 -0
- package/dist/nosql/beanstalkd/index.js +231 -0
- package/dist/nosql/beequeue/index.js +210 -0
- package/dist/nosql/bigchaindb/index.js +195 -0
- package/dist/nosql/bigtable/index.js +224 -0
- package/dist/nosql/blazegraph/index.js +173 -0
- package/dist/nosql/bullmq/index.js +191 -0
- package/dist/nosql/cassandra/index.js +174 -0
- package/dist/nosql/chroma/index.js +190 -0
- package/dist/nosql/cloudflare-kv/index.js +220 -0
- package/dist/nosql/coherence/index.js +200 -0
- package/dist/nosql/cosmosdb/index.js +157 -0
- package/dist/nosql/couchbase/index.js +213 -0
- package/dist/nosql/dax/index.js +212 -0
- package/dist/nosql/deno-kv/index.js +206 -0
- package/dist/nosql/dgraph/index.js +171 -0
- package/dist/nosql/doris/index.js +169 -0
- package/dist/nosql/druid/index.js +162 -0
- package/dist/nosql/dynamodb/index.js +937 -0
- package/dist/nosql/elasticsearch/index.js +377 -0
- package/dist/nosql/etcd/index.js +502 -0
- package/dist/nosql/eventhubs/index.js +213 -0
- package/dist/nosql/eventstore/index.js +254 -0
- package/dist/nosql/faunadb/index.js +188 -0
- package/dist/nosql/firestore/index.js +177 -0
- package/dist/nosql/fluree/index.js +148 -0
- package/dist/nosql/fuseki/index.js +170 -0
- package/dist/nosql/gcs/index.js +172 -0
- package/dist/nosql/gearman/index.js +160 -0
- package/dist/nosql/geode/index.js +196 -0
- package/dist/nosql/graphdb/index.js +169 -0
- package/dist/nosql/graylog/index.js +188 -0
- package/dist/nosql/gridgain/index.js +171 -0
- package/dist/nosql/hazelcast/index.js +162 -0
- package/dist/nosql/hbase/index.js +230 -0
- package/dist/nosql/ignite/index.js +173 -0
- package/dist/nosql/immudb/index.js +184 -0
- package/dist/nosql/index.js +232 -0
- package/dist/nosql/infinispan/index.js +200 -0
- package/dist/nosql/influxdb/index.js +0 -0
- package/dist/nosql/kafka/index.js +234 -0
- package/dist/nosql/keyspaces/index.js +189 -0
- package/dist/nosql/kinesis/index.js +253 -0
- package/dist/nosql/leveldb/index.js +153 -0
- package/dist/nosql/lmdb/index.js +160 -0
- package/dist/nosql/loki/index.js +201 -0
- package/dist/nosql/marklogic/index.js +204 -0
- package/dist/nosql/materialize/index.js +145 -0
- package/dist/nosql/meilisearch/index.js +154 -0
- package/dist/nosql/memcached/index.js +223 -0
- package/dist/nosql/milvus/index.js +410 -0
- package/dist/nosql/minio/index.js +265 -0
- package/dist/nosql/momento/index.js +179 -0
- package/dist/nosql/mongodb/index.js +461 -0
- package/dist/nosql/nats/index.js +247 -0
- package/dist/nosql/nedb/index.js +164 -0
- package/dist/nosql/neo4j/index.js +450 -0
- package/dist/nosql/neptune/index.js +470 -0
- package/dist/nosql/nsq/index.js +200 -0
- package/dist/nosql/opensearch/index.js +186 -0
- package/dist/nosql/orientdb/index.js +175 -0
- package/dist/nosql/papertrail/index.js +200 -0
- package/dist/nosql/pinecone/index.js +0 -0
- package/dist/nosql/pinot/index.js +133 -0
- package/dist/nosql/pouchdb/index.js +172 -0
- package/dist/nosql/prometheus/index.js +174 -0
- package/dist/nosql/provendb/index.js +147 -0
- package/dist/nosql/pubsub/index.js +187 -0
- package/dist/nosql/pulsar/index.js +232 -0
- package/dist/nosql/qdrant/index.js +295 -0
- package/dist/nosql/qldb/index.js +192 -0
- package/dist/nosql/r2/index.js +281 -0
- package/dist/nosql/rabbitmq/index.js +237 -0
- package/dist/nosql/ravendb/index.js +175 -0
- package/dist/nosql/redis/index.js +607 -0
- package/dist/nosql/redpanda/index.js +237 -0
- package/dist/nosql/resque/index.js +203 -0
- package/dist/nosql/rethinkdb/index.js +232 -0
- package/dist/nosql/rocksdb/index.js +152 -0
- package/dist/nosql/rockset/index.js +126 -0
- package/dist/nosql/s3/index.js +298 -0
- package/dist/nosql/scylladb/index.js +178 -0
- package/dist/nosql/signoz/index.js +227 -0
- package/dist/nosql/sns/index.js +201 -0
- package/dist/nosql/solr/index.js +231 -0
- package/dist/nosql/splunk/index.js +227 -0
- package/dist/nosql/sqs/index.js +246 -0
- package/dist/nosql/stardog/index.js +169 -0
- package/dist/nosql/starrocks/index.js +170 -0
- package/dist/nosql/store.js +2 -0
- package/dist/nosql/sumologic/index.js +213 -0
- package/dist/nosql/surrealdb/index.js +179 -0
- package/dist/nosql/terminusdb/index.js +151 -0
- package/dist/nosql/tigergraph/index.js +469 -0
- package/dist/nosql/typesense/index.js +148 -0
- package/dist/nosql/unqlite/index.js +157 -0
- package/dist/nosql/upstash/index.js +167 -0
- package/dist/nosql/vercel-kv/index.js +166 -0
- package/dist/nosql/victoriametrics/index.js +213 -0
- package/dist/nosql/virtuoso/index.js +169 -0
- package/dist/nosql/weaviate/index.js +276 -0
- package/dist/operators/index.js +108 -0
- package/dist/operators.js +2690 -0
- package/dist/prisma-migrate/index.js +38 -0
- package/dist/prisma-migrate/migration-generator.js +125 -0
- package/dist/prisma-migrate/model-generator.js +172 -0
- package/dist/prisma-migrate/relations.js +100 -0
- package/dist/prisma-migrate/schema-parser.js +167 -0
- package/dist/prisma-migrate/type-mapper.js +40 -0
- package/dist/prorm.js +6832 -0
- package/dist/query-builders/cte-builder.js +80 -0
- package/dist/query-builders/functions/aggregate.js +390 -0
- package/dist/query-builders/functions/conditional.js +503 -0
- package/dist/query-builders/functions/datetime.js +695 -0
- package/dist/query-builders/functions/fulltext.js +439 -0
- package/dist/query-builders/functions/index.js +93 -0
- package/dist/query-builders/functions/json.js +427 -0
- package/dist/query-builders/functions/math.js +399 -0
- package/dist/query-builders/functions/string.js +518 -0
- package/dist/query-builders/functions/window.js +328 -0
- package/dist/query-builders/include-builder.js +323 -0
- package/dist/query-builders/index-expression-builder.js +242 -0
- package/dist/query-builders/index.js +161 -0
- package/dist/query-builders/insert-builder.js +164 -0
- package/dist/query-builders/model-helpers.js +38 -0
- package/dist/query-builders/order-limit-builder.js +239 -0
- package/dist/query-builders/sql-compiler.js +2040 -0
- package/dist/query-builders/subquery-builder.js +152 -0
- package/dist/query-builders/update-builder.js +182 -0
- package/dist/query-builders/view-builder.js +218 -0
- package/dist/query-builders/where-builder.js +1532 -0
- package/dist/query-interface.js +622 -0
- package/dist/query-optimizers/batch-optimizer.js +426 -0
- package/dist/query-optimizers/explain-plans.js +415 -0
- package/dist/query-optimizers/index.js +51 -0
- package/dist/query-optimizers/prepared-statement-cache.js +423 -0
- package/dist/query-optimizers/query-hints.js +438 -0
- package/dist/query-optimizers/query-optimizer.js +278 -0
- package/dist/query-optimizers/slow-query-logger.js +271 -0
- package/dist/replica-manager.js +507 -0
- package/dist/schema/index.js +15 -0
- package/dist/schema/migration-generator.js +374 -0
- package/dist/schema/schema-differ.js +549 -0
- package/dist/schema/types.js +6 -0
- package/dist/sql-constants.js +300 -0
- package/dist/sqlite-advanced.js +1047 -0
- package/dist/streams/index.js +12 -0
- package/dist/streams/transforms.js +178 -0
- package/dist/transaction.js +456 -0
- package/dist/types/index.js +180 -0
- package/dist/user-management.js +127 -0
- package/dist/utils/date.js +300 -0
- package/dist/utils/index.js +1079 -0
- package/dist/utils/string.js +161 -0
- package/dist/validators/index.js +19 -0
- package/dist/validators/validator.js +911 -0
- package/package.json +190 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SchemaDocDiagram — generates a print-friendly "data dictionary" / schema
|
|
4
|
+
* documentation sheet as SVG.
|
|
5
|
+
*
|
|
6
|
+
* Where ERDiagram / ModelDiagram render the *visual* shape of a schema, this
|
|
7
|
+
* generator renders the *reference* counterpart: for every table a titled
|
|
8
|
+
* section followed by a tabular grid with one row per column, listing
|
|
9
|
+
* Column · Type · Nullable · Key · Default · Description. Rows are
|
|
10
|
+
* zebra-striped and each grid's header row is painted in the accent color.
|
|
11
|
+
* A document title banner and a summary line ("N tables, M columns") top the
|
|
12
|
+
* sheet.
|
|
13
|
+
*
|
|
14
|
+
* All markup is built against a real DOM document (see ./svg-dom) — never by
|
|
15
|
+
* concatenating strings — and both `--theme` and `--color` genuinely change
|
|
16
|
+
* fill/stroke values in the output.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* const doc = new SchemaDocDiagram({ tables: [...], title: 'My Schema' });
|
|
20
|
+
* doc.save('./diagrams/schema.svg');
|
|
21
|
+
* // or:
|
|
22
|
+
* const svgString = doc.generate();
|
|
23
|
+
*/
|
|
24
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
27
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
28
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
29
|
+
}
|
|
30
|
+
Object.defineProperty(o, k2, desc);
|
|
31
|
+
}) : (function(o, m, k, k2) {
|
|
32
|
+
if (k2 === undefined) k2 = k;
|
|
33
|
+
o[k2] = m[k];
|
|
34
|
+
}));
|
|
35
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
36
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
37
|
+
}) : function(o, v) {
|
|
38
|
+
o["default"] = v;
|
|
39
|
+
});
|
|
40
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
41
|
+
var ownKeys = function(o) {
|
|
42
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
43
|
+
var ar = [];
|
|
44
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
45
|
+
return ar;
|
|
46
|
+
};
|
|
47
|
+
return ownKeys(o);
|
|
48
|
+
};
|
|
49
|
+
return function (mod) {
|
|
50
|
+
if (mod && mod.__esModule) return mod;
|
|
51
|
+
var result = {};
|
|
52
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
53
|
+
__setModuleDefault(result, mod);
|
|
54
|
+
return result;
|
|
55
|
+
};
|
|
56
|
+
})();
|
|
57
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
|
+
exports.SchemaDocDiagram = void 0;
|
|
59
|
+
const fs = __importStar(require("fs"));
|
|
60
|
+
const path = __importStar(require("path"));
|
|
61
|
+
const palette_1 = require("./palette");
|
|
62
|
+
const svg_dom_1 = require("./svg-dom");
|
|
63
|
+
// ─── Layout constants (px) ──────────────────────────────────────────────────
|
|
64
|
+
const W = 820; // content width
|
|
65
|
+
const PAD = 28; // outer canvas padding
|
|
66
|
+
const BANNER_H = 78; // title banner height
|
|
67
|
+
const SUMMARY_H = 34; // summary line strip
|
|
68
|
+
const SECTION_TITLE_H = 40; // per-table section title bar
|
|
69
|
+
const COL_HDR_H = 30; // grid header row
|
|
70
|
+
const ROW_H = 28; // grid data row
|
|
71
|
+
const SECTION_GAP = 22; // space between tables
|
|
72
|
+
const R = 12; // border radius
|
|
73
|
+
// Column layout: [label, x offset, width]
|
|
74
|
+
const COLS = [
|
|
75
|
+
{ label: 'Column', x: 0, w: 170 },
|
|
76
|
+
{ label: 'Type', x: 170, w: 140 },
|
|
77
|
+
{ label: 'Nullable', x: 310, w: 80 },
|
|
78
|
+
{ label: 'Key', x: 390, w: 60 },
|
|
79
|
+
{ label: 'Default', x: 450, w: 130 },
|
|
80
|
+
{ label: 'Description', x: 580, w: 240 },
|
|
81
|
+
];
|
|
82
|
+
const FONT_SANS = 'system-ui,-apple-system,sans-serif';
|
|
83
|
+
const FONT_MONO = "'SF Mono','Fira Code',monospace";
|
|
84
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
85
|
+
/** Truncate text to roughly fit `width` px at the given font size. */
|
|
86
|
+
function truncate(text, width, fontSize) {
|
|
87
|
+
const maxChars = Math.max(1, Math.floor(width / (fontSize * 0.6)));
|
|
88
|
+
if (text.length <= maxChars)
|
|
89
|
+
return text;
|
|
90
|
+
if (maxChars <= 1)
|
|
91
|
+
return '…';
|
|
92
|
+
return text.slice(0, maxChars - 1) + '…';
|
|
93
|
+
}
|
|
94
|
+
// ─── Main class ────────────────────────────────────────────────────────────
|
|
95
|
+
class SchemaDocDiagram {
|
|
96
|
+
constructor(options) {
|
|
97
|
+
this.opts = {
|
|
98
|
+
tables: options.tables ?? [],
|
|
99
|
+
title: options.title ?? 'Schema Documentation',
|
|
100
|
+
theme: options.theme ?? 'light',
|
|
101
|
+
color: options.color ?? '',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/** Total column count across all tables. */
|
|
105
|
+
totalColumns() {
|
|
106
|
+
return this.opts.tables.reduce((n, t) => n + (t.columns?.length ?? 0), 0);
|
|
107
|
+
}
|
|
108
|
+
/** Height of one table's rendered section. */
|
|
109
|
+
sectionHeight(table) {
|
|
110
|
+
return SECTION_TITLE_H + COL_HDR_H + (table.columns?.length ?? 0) * ROW_H;
|
|
111
|
+
}
|
|
112
|
+
generate() {
|
|
113
|
+
const { tables, title, theme, color } = this.opts;
|
|
114
|
+
const accent = (0, palette_1.resolveAccent)(color);
|
|
115
|
+
const t = (0, palette_1.resolveTheme)(theme);
|
|
116
|
+
// ── Compute total height (auto-size viewBox) ────────────────────────
|
|
117
|
+
let contentH = BANNER_H + SUMMARY_H + 16;
|
|
118
|
+
tables.forEach((tbl, i) => {
|
|
119
|
+
contentH += this.sectionHeight(tbl);
|
|
120
|
+
if (i < tables.length - 1)
|
|
121
|
+
contentH += SECTION_GAP;
|
|
122
|
+
});
|
|
123
|
+
const svgW = W + 2 * PAD;
|
|
124
|
+
const svgH = contentH + 2 * PAD;
|
|
125
|
+
const cx = PAD;
|
|
126
|
+
const doc = new svg_dom_1.SvgDocument(svgW, svgH);
|
|
127
|
+
const svg = doc.root;
|
|
128
|
+
// ── Canvas background (theme) ───────────────────────────────────────
|
|
129
|
+
svg.child('rect', { width: svgW, height: svgH, fill: t.canvasBg });
|
|
130
|
+
// ── Title banner (accent) ───────────────────────────────────────────
|
|
131
|
+
const uid = 'sd';
|
|
132
|
+
doc.linearGradient(`bg${uid}`, [
|
|
133
|
+
[0, accent.from],
|
|
134
|
+
[100, accent.mid],
|
|
135
|
+
]);
|
|
136
|
+
svg.child('rect', {
|
|
137
|
+
x: cx,
|
|
138
|
+
y: PAD,
|
|
139
|
+
width: W,
|
|
140
|
+
height: BANNER_H,
|
|
141
|
+
rx: R,
|
|
142
|
+
fill: `url(#bg${uid})`,
|
|
143
|
+
});
|
|
144
|
+
svg
|
|
145
|
+
.child('text', {
|
|
146
|
+
x: cx + 22,
|
|
147
|
+
y: PAD + 34,
|
|
148
|
+
'font-family': FONT_SANS,
|
|
149
|
+
'font-size': 24,
|
|
150
|
+
'font-weight': 800,
|
|
151
|
+
fill: '#FFFFFF',
|
|
152
|
+
})
|
|
153
|
+
.text(truncate(title, W - 44, 24));
|
|
154
|
+
svg
|
|
155
|
+
.child('text', {
|
|
156
|
+
x: cx + 22,
|
|
157
|
+
y: PAD + 58,
|
|
158
|
+
'font-family': FONT_SANS,
|
|
159
|
+
'font-size': 12,
|
|
160
|
+
'font-weight': 600,
|
|
161
|
+
fill: 'rgba(255,255,255,0.85)',
|
|
162
|
+
'letter-spacing': 1,
|
|
163
|
+
})
|
|
164
|
+
.text('DATA DICTIONARY');
|
|
165
|
+
// ── Summary line ────────────────────────────────────────────────────
|
|
166
|
+
const summaryY = PAD + BANNER_H + 8;
|
|
167
|
+
const tableCount = tables.length;
|
|
168
|
+
const colCount = this.totalColumns();
|
|
169
|
+
svg
|
|
170
|
+
.child('text', {
|
|
171
|
+
x: cx + 4,
|
|
172
|
+
y: summaryY + 22,
|
|
173
|
+
'font-family': FONT_SANS,
|
|
174
|
+
'font-size': 13,
|
|
175
|
+
'font-weight': 600,
|
|
176
|
+
fill: t.textSecondary,
|
|
177
|
+
})
|
|
178
|
+
.text(`${tableCount} table${tableCount !== 1 ? 's' : ''}, ${colCount} column${colCount !== 1 ? 's' : ''}`);
|
|
179
|
+
// ── Table sections ──────────────────────────────────────────────────
|
|
180
|
+
let y = summaryY + SUMMARY_H + 8;
|
|
181
|
+
tables.forEach((table) => {
|
|
182
|
+
y = this.drawTable(doc, svg, table, cx, y, accent, t);
|
|
183
|
+
y += SECTION_GAP;
|
|
184
|
+
});
|
|
185
|
+
return doc.toString('Generated by ts-prorm-orm SchemaDocDiagram');
|
|
186
|
+
}
|
|
187
|
+
/** Draw one table section starting at `y`; returns the y after the section. */
|
|
188
|
+
drawTable(doc, svg, table, cx, y, accent, t) {
|
|
189
|
+
const columns = table.columns ?? [];
|
|
190
|
+
// ── Section title bar ─────────────────────────────────────────────
|
|
191
|
+
svg.child('rect', {
|
|
192
|
+
x: cx,
|
|
193
|
+
y,
|
|
194
|
+
width: W,
|
|
195
|
+
height: SECTION_TITLE_H,
|
|
196
|
+
rx: 8,
|
|
197
|
+
fill: t.colHeaderBg,
|
|
198
|
+
});
|
|
199
|
+
svg.child('rect', { x: cx, y, width: 5, height: SECTION_TITLE_H, fill: accent.mid });
|
|
200
|
+
svg
|
|
201
|
+
.child('text', {
|
|
202
|
+
x: cx + 18,
|
|
203
|
+
y: y + 26,
|
|
204
|
+
'font-family': FONT_MONO,
|
|
205
|
+
'font-size': 16,
|
|
206
|
+
'font-weight': 700,
|
|
207
|
+
fill: t.textPrimary,
|
|
208
|
+
})
|
|
209
|
+
.text(truncate(table.name, 420, 16));
|
|
210
|
+
if (table.comment) {
|
|
211
|
+
svg
|
|
212
|
+
.child('text', {
|
|
213
|
+
x: cx + W - 14,
|
|
214
|
+
y: y + 26,
|
|
215
|
+
'font-family': FONT_SANS,
|
|
216
|
+
'font-size': 11,
|
|
217
|
+
fill: t.textSecondary,
|
|
218
|
+
'text-anchor': 'end',
|
|
219
|
+
})
|
|
220
|
+
.text(truncate(table.comment, 340, 11));
|
|
221
|
+
}
|
|
222
|
+
// ── Grid header row (accent) ──────────────────────────────────────
|
|
223
|
+
const gridX = cx;
|
|
224
|
+
let rowY = y + SECTION_TITLE_H;
|
|
225
|
+
svg.child('rect', {
|
|
226
|
+
x: gridX,
|
|
227
|
+
y: rowY,
|
|
228
|
+
width: W,
|
|
229
|
+
height: COL_HDR_H,
|
|
230
|
+
fill: accent.from,
|
|
231
|
+
});
|
|
232
|
+
COLS.forEach((c) => {
|
|
233
|
+
svg
|
|
234
|
+
.child('text', {
|
|
235
|
+
x: gridX + c.x + 10,
|
|
236
|
+
y: rowY + 20,
|
|
237
|
+
'font-family': FONT_SANS,
|
|
238
|
+
'font-size': 11,
|
|
239
|
+
'font-weight': 700,
|
|
240
|
+
fill: '#FFFFFF',
|
|
241
|
+
'letter-spacing': 0.4,
|
|
242
|
+
})
|
|
243
|
+
.text(c.label);
|
|
244
|
+
});
|
|
245
|
+
// ── Data rows (zebra) ─────────────────────────────────────────────
|
|
246
|
+
rowY += COL_HDR_H;
|
|
247
|
+
columns.forEach((col, i) => {
|
|
248
|
+
const rowBg = i % 2 === 0 ? t.cardBg : t.rowAlt;
|
|
249
|
+
svg.child('rect', { x: gridX, y: rowY, width: W, height: ROW_H, fill: rowBg });
|
|
250
|
+
const baseY = rowY + 18;
|
|
251
|
+
const cell = (idx, value, opts) => {
|
|
252
|
+
const c = COLS[idx];
|
|
253
|
+
const fs = 12;
|
|
254
|
+
svg
|
|
255
|
+
.child('text', {
|
|
256
|
+
x: gridX + c.x + 10,
|
|
257
|
+
y: baseY,
|
|
258
|
+
'font-family': opts?.mono ? FONT_MONO : FONT_SANS,
|
|
259
|
+
'font-size': fs,
|
|
260
|
+
fill: opts?.fill ?? t.textPrimary,
|
|
261
|
+
})
|
|
262
|
+
.text(truncate(value, c.w - 16, fs));
|
|
263
|
+
};
|
|
264
|
+
cell(0, col.name, { mono: true, fill: t.textPrimary });
|
|
265
|
+
cell(1, col.type, { mono: true, fill: t.textSecondary });
|
|
266
|
+
cell(2, col.nullable === false ? 'NO' : 'YES', {
|
|
267
|
+
fill: col.nullable === false ? accent.mid : t.textSecondary,
|
|
268
|
+
});
|
|
269
|
+
cell(3, col.key ?? '', { mono: true, fill: accent.text });
|
|
270
|
+
cell(4, col.default != null ? String(col.default) : '', {
|
|
271
|
+
mono: true,
|
|
272
|
+
fill: t.textSecondary,
|
|
273
|
+
});
|
|
274
|
+
cell(5, col.description ?? '', { fill: t.textSecondary });
|
|
275
|
+
svg.child('line', {
|
|
276
|
+
x1: gridX,
|
|
277
|
+
y1: rowY + ROW_H,
|
|
278
|
+
x2: gridX + W,
|
|
279
|
+
y2: rowY + ROW_H,
|
|
280
|
+
stroke: t.border,
|
|
281
|
+
'stroke-width': 1,
|
|
282
|
+
});
|
|
283
|
+
rowY += ROW_H;
|
|
284
|
+
});
|
|
285
|
+
// ── Grid outline ──────────────────────────────────────────────────
|
|
286
|
+
svg.child('rect', {
|
|
287
|
+
x: gridX,
|
|
288
|
+
y: y + SECTION_TITLE_H,
|
|
289
|
+
width: W,
|
|
290
|
+
height: COL_HDR_H + columns.length * ROW_H,
|
|
291
|
+
fill: 'none',
|
|
292
|
+
stroke: t.border,
|
|
293
|
+
'stroke-width': 1,
|
|
294
|
+
});
|
|
295
|
+
return y + this.sectionHeight(table);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Write the SVG to a file. Creates parent directories if needed.
|
|
299
|
+
*/
|
|
300
|
+
save(outputPath) {
|
|
301
|
+
const resolved = path.resolve(outputPath);
|
|
302
|
+
const dir = path.dirname(resolved);
|
|
303
|
+
if (!fs.existsSync(dir))
|
|
304
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
305
|
+
fs.writeFileSync(resolved, this.generate(), 'utf-8');
|
|
306
|
+
return resolved;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
exports.SchemaDocDiagram = SchemaDocDiagram;
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SequenceDiagram — generates a UML sequence diagram as an SVG.
|
|
4
|
+
*
|
|
5
|
+
* Renders participants (actors / objects / databases) as header boxes across
|
|
6
|
+
* the top, each dropping a vertical dashed lifeline. Messages are drawn as
|
|
7
|
+
* numbered horizontal arrows between lifelines in order: a solid arrow is a
|
|
8
|
+
* synchronous call, a dashed arrow is a return / async message. Activation
|
|
9
|
+
* bars (thin rectangles) are drawn on a lifeline while it is active.
|
|
10
|
+
*
|
|
11
|
+
* The accent color (via resolveAccent) drives participant header boxes and
|
|
12
|
+
* arrowheads; the theme (via resolveTheme) drives the canvas, lifelines and
|
|
13
|
+
* text. Both `theme` and `color` genuinely change fill/stroke values in the
|
|
14
|
+
* generated SVG. The viewBox is auto-sized to fit all content.
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* const diag = new SequenceDiagram({ participants: […], messages: […] });
|
|
18
|
+
* diag.save('./diagrams/flow.svg');
|
|
19
|
+
* // or:
|
|
20
|
+
* const svgString = diag.generate();
|
|
21
|
+
*/
|
|
22
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
25
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
26
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
27
|
+
}
|
|
28
|
+
Object.defineProperty(o, k2, desc);
|
|
29
|
+
}) : (function(o, m, k, k2) {
|
|
30
|
+
if (k2 === undefined) k2 = k;
|
|
31
|
+
o[k2] = m[k];
|
|
32
|
+
}));
|
|
33
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
34
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
35
|
+
}) : function(o, v) {
|
|
36
|
+
o["default"] = v;
|
|
37
|
+
});
|
|
38
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
39
|
+
var ownKeys = function(o) {
|
|
40
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
41
|
+
var ar = [];
|
|
42
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
43
|
+
return ar;
|
|
44
|
+
};
|
|
45
|
+
return ownKeys(o);
|
|
46
|
+
};
|
|
47
|
+
return function (mod) {
|
|
48
|
+
if (mod && mod.__esModule) return mod;
|
|
49
|
+
var result = {};
|
|
50
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
51
|
+
__setModuleDefault(result, mod);
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
})();
|
|
55
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.SequenceDiagram = void 0;
|
|
57
|
+
const fs = __importStar(require("fs"));
|
|
58
|
+
const path = __importStar(require("path"));
|
|
59
|
+
const palette_1 = require("./palette");
|
|
60
|
+
const svg_dom_1 = require("./svg-dom");
|
|
61
|
+
// ─── Main class ────────────────────────────────────────────────────────────
|
|
62
|
+
class SequenceDiagram {
|
|
63
|
+
constructor(options) {
|
|
64
|
+
this.opts = {
|
|
65
|
+
participants: options.participants ?? [],
|
|
66
|
+
messages: options.messages ?? [],
|
|
67
|
+
title: options.title ?? '',
|
|
68
|
+
theme: options.theme ?? 'light',
|
|
69
|
+
color: options.color ?? '',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** Kind glyph drawn inside a participant header. */
|
|
73
|
+
kindGlyph(kind) {
|
|
74
|
+
if (kind === 'actor')
|
|
75
|
+
return '☺';
|
|
76
|
+
if (kind === 'database')
|
|
77
|
+
return '⛁';
|
|
78
|
+
return '▭';
|
|
79
|
+
}
|
|
80
|
+
generate() {
|
|
81
|
+
const { participants, messages, title, theme, color } = this.opts;
|
|
82
|
+
const accent = (0, palette_1.resolveAccent)(color);
|
|
83
|
+
const t = (0, palette_1.resolveTheme)(theme);
|
|
84
|
+
const PAD = SequenceDiagram.PAD;
|
|
85
|
+
const LANE = SequenceDiagram.LANE;
|
|
86
|
+
const HDR_W = SequenceDiagram.HDR_W;
|
|
87
|
+
const HDR_H = SequenceDiagram.HDR_H;
|
|
88
|
+
const TITLE_H = title ? SequenceDiagram.TITLE_H : 0;
|
|
89
|
+
const MSG_GAP = SequenceDiagram.MSG_GAP;
|
|
90
|
+
const TOP_GAP = SequenceDiagram.TOP_GAP;
|
|
91
|
+
const BOT_GAP = SequenceDiagram.BOT_GAP;
|
|
92
|
+
const ACT_W = SequenceDiagram.ACT_W;
|
|
93
|
+
const n = Math.max(participants.length, 1);
|
|
94
|
+
const m = messages.length;
|
|
95
|
+
// Lifeline x-centers.
|
|
96
|
+
const laneX = (i) => PAD + HDR_W / 2 + i * LANE;
|
|
97
|
+
const indexOf = (name) => {
|
|
98
|
+
const idx = participants.findIndex((p) => p.name === name);
|
|
99
|
+
return idx < 0 ? 0 : idx;
|
|
100
|
+
};
|
|
101
|
+
// Vertical geometry.
|
|
102
|
+
const headerTop = PAD + TITLE_H;
|
|
103
|
+
const headerBottom = headerTop + HDR_H;
|
|
104
|
+
const firstMsgY = headerBottom + TOP_GAP;
|
|
105
|
+
const lifelineBottom = firstMsgY + m * MSG_GAP + BOT_GAP;
|
|
106
|
+
const svgW = PAD * 2 + HDR_W + (n - 1) * LANE;
|
|
107
|
+
const svgH = lifelineBottom + PAD;
|
|
108
|
+
const doc = new svg_dom_1.SvgDocument(svgW, svgH);
|
|
109
|
+
const svg = doc.root;
|
|
110
|
+
// ── Canvas background (theme-driven) ────────────────────────────────
|
|
111
|
+
svg.child('rect', { width: svgW, height: svgH, fill: t.canvasBg });
|
|
112
|
+
// ── Arrowhead marker (accent-colored) ───────────────────────────────
|
|
113
|
+
const headId = `seqhead${accent.name.replace(/\W/g, '')}`;
|
|
114
|
+
const marker = doc.defs().child('marker', {
|
|
115
|
+
id: headId,
|
|
116
|
+
markerWidth: 10,
|
|
117
|
+
markerHeight: 10,
|
|
118
|
+
refX: 8,
|
|
119
|
+
refY: 4,
|
|
120
|
+
orient: 'auto',
|
|
121
|
+
markerUnits: 'userSpaceOnUse',
|
|
122
|
+
});
|
|
123
|
+
marker.child('path', { d: 'M0,0 L8,4 L0,8 Z', fill: accent.mid });
|
|
124
|
+
// ── Title ───────────────────────────────────────────────────────────
|
|
125
|
+
if (title) {
|
|
126
|
+
svg
|
|
127
|
+
.child('text', {
|
|
128
|
+
x: svgW / 2,
|
|
129
|
+
y: PAD + 24,
|
|
130
|
+
'font-family': 'system-ui,-apple-system,sans-serif',
|
|
131
|
+
'font-size': 20,
|
|
132
|
+
'font-weight': 800,
|
|
133
|
+
fill: t.textPrimary,
|
|
134
|
+
'text-anchor': 'middle',
|
|
135
|
+
})
|
|
136
|
+
.text(title);
|
|
137
|
+
}
|
|
138
|
+
// ── Lifelines (dashed, theme-colored) ───────────────────────────────
|
|
139
|
+
participants.forEach((_p, i) => {
|
|
140
|
+
const x = laneX(i);
|
|
141
|
+
svg.child('line', {
|
|
142
|
+
x1: x,
|
|
143
|
+
y1: headerBottom,
|
|
144
|
+
x2: x,
|
|
145
|
+
y2: lifelineBottom,
|
|
146
|
+
stroke: t.border,
|
|
147
|
+
'stroke-width': 1.5,
|
|
148
|
+
'stroke-dasharray': '6 5',
|
|
149
|
+
class: 'lifeline',
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
// ── Activation bars (drawn under arrows so arrows sit on top edge) ──
|
|
153
|
+
messages.forEach((msg, i) => {
|
|
154
|
+
if (!msg.activate)
|
|
155
|
+
return;
|
|
156
|
+
const y = firstMsgY + i * MSG_GAP;
|
|
157
|
+
const x = laneX(indexOf(msg.to));
|
|
158
|
+
// Extend the bar to the next message (or to the lifeline end).
|
|
159
|
+
const endY = i + 1 < m ? firstMsgY + (i + 1) * MSG_GAP : y + MSG_GAP;
|
|
160
|
+
svg.child('rect', {
|
|
161
|
+
x: x - ACT_W / 2,
|
|
162
|
+
y,
|
|
163
|
+
width: ACT_W,
|
|
164
|
+
height: Math.max(endY - y, MSG_GAP / 2),
|
|
165
|
+
fill: accent.light,
|
|
166
|
+
stroke: accent.mid,
|
|
167
|
+
'stroke-width': 1,
|
|
168
|
+
class: 'activation',
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
// ── Participant header boxes (accent-colored) ───────────────────────
|
|
172
|
+
participants.forEach((p, i) => {
|
|
173
|
+
const cxLine = laneX(i);
|
|
174
|
+
const boxX = cxLine - HDR_W / 2;
|
|
175
|
+
svg.child('rect', {
|
|
176
|
+
x: boxX,
|
|
177
|
+
y: headerTop,
|
|
178
|
+
width: HDR_W,
|
|
179
|
+
height: HDR_H,
|
|
180
|
+
rx: 8,
|
|
181
|
+
fill: accent.from,
|
|
182
|
+
stroke: accent.mid,
|
|
183
|
+
'stroke-width': 1.5,
|
|
184
|
+
class: 'participant',
|
|
185
|
+
});
|
|
186
|
+
svg
|
|
187
|
+
.child('text', {
|
|
188
|
+
x: cxLine,
|
|
189
|
+
y: headerTop + 20,
|
|
190
|
+
'font-family': 'system-ui,-apple-system,sans-serif',
|
|
191
|
+
'font-size': 13,
|
|
192
|
+
fill: '#FFFFFF',
|
|
193
|
+
'text-anchor': 'middle',
|
|
194
|
+
})
|
|
195
|
+
.text(this.kindGlyph(p.kind));
|
|
196
|
+
svg
|
|
197
|
+
.child('text', {
|
|
198
|
+
x: cxLine,
|
|
199
|
+
y: headerTop + 38,
|
|
200
|
+
'font-family': "'SF Mono','Fira Code',monospace",
|
|
201
|
+
'font-size': 13,
|
|
202
|
+
'font-weight': 700,
|
|
203
|
+
fill: '#FFFFFF',
|
|
204
|
+
'text-anchor': 'middle',
|
|
205
|
+
})
|
|
206
|
+
.text(p.name.length > 16 ? p.name.slice(0, 15) + '…' : p.name);
|
|
207
|
+
});
|
|
208
|
+
// ── Messages (numbered arrows) ──────────────────────────────────────
|
|
209
|
+
messages.forEach((msg, i) => {
|
|
210
|
+
const y = firstMsgY + i * MSG_GAP;
|
|
211
|
+
const fromI = indexOf(msg.from);
|
|
212
|
+
const toI = indexOf(msg.to);
|
|
213
|
+
let x1 = laneX(fromI);
|
|
214
|
+
let x2 = laneX(toI);
|
|
215
|
+
const isReturn = !!msg.return;
|
|
216
|
+
const rightward = x2 >= x1;
|
|
217
|
+
// Nudge endpoints off activation bars so arrowheads stay visible.
|
|
218
|
+
if (x1 !== x2) {
|
|
219
|
+
x1 += rightward ? ACT_W / 2 : -ACT_W / 2;
|
|
220
|
+
x2 += rightward ? -ACT_W / 2 : ACT_W / 2;
|
|
221
|
+
}
|
|
222
|
+
if (fromI === toI) {
|
|
223
|
+
// Self-message: a small loop back to the same lifeline.
|
|
224
|
+
const w = 60;
|
|
225
|
+
const yb = y + 22;
|
|
226
|
+
svg.child('path', {
|
|
227
|
+
d: `M${x1},${y} h${w} v${yb - y} h-${w}`,
|
|
228
|
+
fill: 'none',
|
|
229
|
+
stroke: accent.mid,
|
|
230
|
+
'stroke-width': 1.6,
|
|
231
|
+
'stroke-dasharray': isReturn ? '5 4' : undefined,
|
|
232
|
+
'marker-end': `url(#${headId})`,
|
|
233
|
+
class: isReturn ? 'message message-return' : 'message message-call',
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
svg.child('line', {
|
|
238
|
+
x1,
|
|
239
|
+
y1: y,
|
|
240
|
+
x2,
|
|
241
|
+
y2: y,
|
|
242
|
+
stroke: accent.mid,
|
|
243
|
+
'stroke-width': 1.6,
|
|
244
|
+
'stroke-dasharray': isReturn ? '5 4' : undefined,
|
|
245
|
+
'marker-end': `url(#${headId})`,
|
|
246
|
+
class: isReturn ? 'message message-return' : 'message message-call',
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
// Sequence number badge on the source side.
|
|
250
|
+
const badgeX = rightward ? Math.min(x1, x2) + 11 : Math.max(x1, x2) - 11;
|
|
251
|
+
svg.child('circle', {
|
|
252
|
+
cx: badgeX,
|
|
253
|
+
cy: y - 12,
|
|
254
|
+
r: 9,
|
|
255
|
+
fill: accent.mid,
|
|
256
|
+
class: 'seq-num',
|
|
257
|
+
});
|
|
258
|
+
svg
|
|
259
|
+
.child('text', {
|
|
260
|
+
x: badgeX,
|
|
261
|
+
y: y - 8,
|
|
262
|
+
'font-family': "'SF Mono',monospace",
|
|
263
|
+
'font-size': 10,
|
|
264
|
+
'font-weight': 700,
|
|
265
|
+
fill: '#FFFFFF',
|
|
266
|
+
'text-anchor': 'middle',
|
|
267
|
+
})
|
|
268
|
+
.text(i + 1);
|
|
269
|
+
// Message label above the arrow.
|
|
270
|
+
const midX = (x1 + x2) / 2;
|
|
271
|
+
svg
|
|
272
|
+
.child('text', {
|
|
273
|
+
x: fromI === toI ? x1 + 66 : midX,
|
|
274
|
+
y: y - 8,
|
|
275
|
+
'font-family': 'system-ui,-apple-system,sans-serif',
|
|
276
|
+
'font-size': 12,
|
|
277
|
+
'font-weight': 600,
|
|
278
|
+
fill: t.textPrimary,
|
|
279
|
+
'text-anchor': fromI === toI ? 'start' : 'middle',
|
|
280
|
+
})
|
|
281
|
+
.text(msg.label);
|
|
282
|
+
});
|
|
283
|
+
return doc.toString('Generated by ts-prorm-orm SequenceDiagram');
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Write the SVG to a file. Creates parent directories if needed.
|
|
287
|
+
*/
|
|
288
|
+
save(outputPath) {
|
|
289
|
+
const resolved = path.resolve(outputPath);
|
|
290
|
+
const dir = path.dirname(resolved);
|
|
291
|
+
if (!fs.existsSync(dir))
|
|
292
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
293
|
+
fs.writeFileSync(resolved, this.generate(), 'utf-8');
|
|
294
|
+
return resolved;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
exports.SequenceDiagram = SequenceDiagram;
|
|
298
|
+
// Layout constants (px)
|
|
299
|
+
SequenceDiagram.PAD = 30;
|
|
300
|
+
SequenceDiagram.LANE = 170; // horizontal spacing between lifelines
|
|
301
|
+
SequenceDiagram.HDR_W = 140; // participant header box width
|
|
302
|
+
SequenceDiagram.HDR_H = 46; // participant header box height
|
|
303
|
+
SequenceDiagram.TITLE_H = 40;
|
|
304
|
+
SequenceDiagram.MSG_GAP = 54; // vertical spacing between messages
|
|
305
|
+
SequenceDiagram.TOP_GAP = 40; // gap below headers before first message
|
|
306
|
+
SequenceDiagram.BOT_GAP = 40; // gap below last message before lifelines end
|
|
307
|
+
SequenceDiagram.ACT_W = 12; // activation bar width
|