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,294 @@
1
+ "use strict";
2
+ /**
3
+ * Permission / access-control decorators for the ORM.
4
+ *
5
+ * These decorators let you annotate model classes and individual fields with
6
+ * access rules — which roles can read, write, or are completely blocked from
7
+ * a field or an entire model. At query time you call filterByPermissions()
8
+ * to strip out any fields the current user is not allowed to see.
9
+ *
10
+ * ──────────────────────────────────────────────────────────────────────────
11
+ * Quick Reference
12
+ * ──────────────────────────────────────────────────────────────────────────
13
+ *
14
+ * @CanView('admin', 'manager') — only these roles can read the field/model
15
+ * @CannotView('guest') — these roles are DENIED read access
16
+ * @CanWrite('admin') — only these roles can write the field
17
+ * @CannotWrite('guest') — these roles are DENIED write access
18
+ * @Permission({ ... }) — full control with explicit allow/deny lists
19
+ *
20
+ * ──────────────────────────────────────────────────────────────────────────
21
+ * Example
22
+ * ──────────────────────────────────────────────────────────────────────────
23
+ *
24
+ * // Field-level permissions (applied via static registration):
25
+ * PermissionRegistry.addFieldRule(User, 'ssn', { canView: ['admin'] });
26
+ * PermissionRegistry.addFieldRule(User, 'salary', { canView: ['admin', 'hr'] });
27
+ * PermissionRegistry.addFieldRule(User, 'password', { canView: [] }); // nobody
28
+ *
29
+ * // Class-level permission (applied via decorator):
30
+ * @CanView('admin', 'manager')
31
+ * class AuditLog extends Model { ... }
32
+ *
33
+ * // At request time:
34
+ * const currentRole = 'guest';
35
+ * const user = await User.findOne({ where: { id: 1 } });
36
+ * const safeUser = filterByPermissions(user, currentRole);
37
+ * // safeUser.ssn === undefined, safeUser.salary === undefined
38
+ *
39
+ * // Or get the allowed attribute list for a findAll() call:
40
+ * const attrs = getAllowedAttributes(User, currentRole);
41
+ * const users = await User.findAll({ attributes: attrs });
42
+ */
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.PermissionRegistry = void 0;
45
+ exports.canViewField = canViewField;
46
+ exports.canWriteField = canWriteField;
47
+ exports.getAllowedAttributes = getAllowedAttributes;
48
+ exports.filterByPermissions = filterByPermissions;
49
+ exports.filterWriteByPermissions = filterWriteByPermissions;
50
+ exports.CanView = CanView;
51
+ exports.CannotView = CannotView;
52
+ exports.CanWrite = CanWrite;
53
+ exports.CannotWrite = CannotWrite;
54
+ exports.Permission = Permission;
55
+ exports.withPermissions = withPermissions;
56
+ // ==================== Registry ====================
57
+ // model constructor → field name → rule
58
+ const fieldRules = new Map();
59
+ // model constructor → model-level rule
60
+ const modelRules = new Map();
61
+ function getFieldMap(target) {
62
+ if (!fieldRules.has(target))
63
+ fieldRules.set(target, new Map());
64
+ return fieldRules.get(target);
65
+ }
66
+ exports.PermissionRegistry = {
67
+ // ── Field-level rules ──────────────────────────────────────────────────
68
+ /**
69
+ * Register or update a permission rule for a specific field on a model.
70
+ *
71
+ * @param target - The model class constructor
72
+ * @param fieldName - The attribute/field name
73
+ * @param rule - The access rule
74
+ */
75
+ addFieldRule(target, fieldName, rule) {
76
+ const map = getFieldMap(target);
77
+ const existing = map.get(fieldName) ?? {};
78
+ map.set(fieldName, { ...existing, ...rule });
79
+ },
80
+ /**
81
+ * Get the rule for a specific field (or undefined if none).
82
+ */
83
+ getFieldRule(target, fieldName) {
84
+ return getFieldMap(target).get(fieldName);
85
+ },
86
+ /**
87
+ * Get all field rules registered on a model.
88
+ */
89
+ getAllFieldRules(target) {
90
+ return getFieldMap(target);
91
+ },
92
+ // ── Model-level rules ──────────────────────────────────────────────────
93
+ /**
94
+ * Register or update a model-level permission rule (applies to ALL fields).
95
+ */
96
+ addModelRule(target, rule) {
97
+ const existing = modelRules.get(target) ?? {};
98
+ modelRules.set(target, { ...existing, ...rule });
99
+ },
100
+ /**
101
+ * Get the model-level rule (or undefined if none).
102
+ */
103
+ getModelRule(target) {
104
+ return modelRules.get(target);
105
+ },
106
+ };
107
+ // ==================== Access-Check Helpers ====================
108
+ /**
109
+ * Check whether a given role can VIEW a field on a model.
110
+ *
111
+ * Resolution order:
112
+ * 1. Field-level cannotView → deny if role is listed
113
+ * 2. Field-level canView → allow only if role is listed (or '*' present)
114
+ * 3. Model-level cannotView → deny if role is listed
115
+ * 4. Model-level canView → allow only if role is listed (or '*' present)
116
+ * 5. Default: allow
117
+ *
118
+ * @param ModelClass - The model constructor
119
+ * @param fieldName - The attribute name to check
120
+ * @param role - The current user's role
121
+ */
122
+ function canViewField(ModelClass, fieldName, role) {
123
+ const fieldRule = exports.PermissionRegistry.getFieldRule(ModelClass, fieldName);
124
+ const modelRule = exports.PermissionRegistry.getModelRule(ModelClass);
125
+ // Field-level cannotView
126
+ if (fieldRule?.cannotView?.includes(role))
127
+ return false;
128
+ // Field-level canView
129
+ if (fieldRule?.canView !== undefined) {
130
+ return fieldRule.canView.includes('*') || fieldRule.canView.includes(role);
131
+ }
132
+ // Model-level cannotView
133
+ if (modelRule?.cannotView?.includes(role))
134
+ return false;
135
+ // Model-level canView
136
+ if (modelRule?.canView !== undefined) {
137
+ return modelRule.canView.includes('*') || modelRule.canView.includes(role);
138
+ }
139
+ return true; // default: allow
140
+ }
141
+ /**
142
+ * Check whether a given role can WRITE a field on a model.
143
+ */
144
+ function canWriteField(ModelClass, fieldName, role) {
145
+ const fieldRule = exports.PermissionRegistry.getFieldRule(ModelClass, fieldName);
146
+ const modelRule = exports.PermissionRegistry.getModelRule(ModelClass);
147
+ if (fieldRule?.cannotWrite?.includes(role))
148
+ return false;
149
+ if (fieldRule?.canWrite !== undefined) {
150
+ return fieldRule.canWrite.includes('*') || fieldRule.canWrite.includes(role);
151
+ }
152
+ if (modelRule?.cannotWrite?.includes(role))
153
+ return false;
154
+ if (modelRule?.canWrite !== undefined) {
155
+ return modelRule.canWrite.includes('*') || modelRule.canWrite.includes(role);
156
+ }
157
+ return true;
158
+ }
159
+ // ==================== Attribute Filtering ====================
160
+ /**
161
+ * Return the list of field names on a model that `role` is allowed to VIEW.
162
+ *
163
+ * Useful for passing to `findAll({ attributes: ... })` to prevent loading
164
+ * restricted data at the database level.
165
+ *
166
+ * @param ModelClass - The model constructor
167
+ * @param role - The current user's role
168
+ * @param allAttributes - The full list of attribute names. If omitted,
169
+ * the registry's field rules are used as the universe
170
+ * (only fields with explicit rules are considered).
171
+ */
172
+ function getAllowedAttributes(ModelClass, role, allAttributes) {
173
+ const fields = allAttributes ?? Array.from(exports.PermissionRegistry.getAllFieldRules(ModelClass).keys());
174
+ return fields.filter((f) => canViewField(ModelClass, f, role));
175
+ }
176
+ /**
177
+ * Strip restricted fields from a plain object or model instance.
178
+ *
179
+ * Returns a new plain object with only the fields `role` is allowed to see.
180
+ * Does not mutate the original.
181
+ *
182
+ * @param instance - A model instance or plain record object
183
+ * @param role - The current user's role
184
+ * @param ModelClass - The model class (defaults to instance.constructor)
185
+ */
186
+ function filterByPermissions(instance, role, ModelClass) {
187
+ const Model = ModelClass ?? instance.constructor;
188
+ // Get the plain data object
189
+ const data = typeof instance.toJSON === 'function' ? instance.toJSON() : { ...instance };
190
+ const result = {};
191
+ for (const [key, value] of Object.entries(data)) {
192
+ if (canViewField(Model, key, role)) {
193
+ result[key] = value;
194
+ }
195
+ }
196
+ return result;
197
+ }
198
+ /**
199
+ * Strip restricted fields from a plain object for WRITE operations.
200
+ * Removes any fields the role is not allowed to write.
201
+ */
202
+ function filterWriteByPermissions(data, role, ModelClass) {
203
+ const result = {};
204
+ for (const [key, value] of Object.entries(data)) {
205
+ if (canWriteField(ModelClass, key, role)) {
206
+ result[key] = value;
207
+ }
208
+ }
209
+ return result;
210
+ }
211
+ // ==================== Decorators ====================
212
+ /**
213
+ * Allow only the listed roles to VIEW the decorated class (all fields) or
214
+ * use PermissionRegistry.addFieldRule() for per-field control.
215
+ *
216
+ * @CanView('admin', 'manager')
217
+ * class AuditLog extends Model { ... }
218
+ */
219
+ function CanView(...roles) {
220
+ return function (target) {
221
+ exports.PermissionRegistry.addModelRule(target, { canView: roles });
222
+ };
223
+ }
224
+ /**
225
+ * Deny the listed roles from viewing the decorated class.
226
+ *
227
+ * @CannotView('guest', 'public')
228
+ * class SensitiveReport extends Model { ... }
229
+ */
230
+ function CannotView(...roles) {
231
+ return function (target) {
232
+ exports.PermissionRegistry.addModelRule(target, { cannotView: roles });
233
+ };
234
+ }
235
+ /**
236
+ * Allow only the listed roles to WRITE to the decorated class.
237
+ *
238
+ * @CanWrite('admin')
239
+ * class Settings extends Model { ... }
240
+ */
241
+ function CanWrite(...roles) {
242
+ return function (target) {
243
+ exports.PermissionRegistry.addModelRule(target, { canWrite: roles });
244
+ };
245
+ }
246
+ /**
247
+ * Deny the listed roles from writing to the decorated class.
248
+ */
249
+ function CannotWrite(...roles) {
250
+ return function (target) {
251
+ exports.PermissionRegistry.addModelRule(target, { cannotWrite: roles });
252
+ };
253
+ }
254
+ /**
255
+ * Full permission decorator — applies read and write rules at the model level.
256
+ *
257
+ * @Permission({ canView: ['admin', 'manager'], canWrite: ['admin'] })
258
+ * class Invoice extends Model { ... }
259
+ */
260
+ function Permission(options) {
261
+ return function (target) {
262
+ exports.PermissionRegistry.addModelRule(target, options);
263
+ };
264
+ }
265
+ // ==================== Controller-Style Helpers ====================
266
+ /**
267
+ * A higher-order helper that wraps a controller function and automatically
268
+ * applies permission filtering on the result.
269
+ *
270
+ * Example:
271
+ *
272
+ * const getUser = withPermissions(User, async (id: number) => {
273
+ * return User.findByPk(id);
274
+ * });
275
+ *
276
+ * const safeUser = await getUser(1, 'guest');
277
+ * // safeUser has only the fields 'guest' is allowed to see
278
+ *
279
+ * @param ModelClass - The model class whose permissions to apply
280
+ * @param fn - The async controller function (last arg must be role string)
281
+ */
282
+ function withPermissions(ModelClass, fn) {
283
+ return async (...args) => {
284
+ const role = args[args.length - 1];
285
+ const fnArgs = args.slice(0, -1);
286
+ const result = await fn(...fnArgs);
287
+ if (!result)
288
+ return null;
289
+ if (Array.isArray(result)) {
290
+ return result.map((item) => filterByPermissions(item, role, ModelClass));
291
+ }
292
+ return filterByPermissions(result, role, ModelClass);
293
+ };
294
+ }
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ /**
3
+ * TypeScript decorators for SQL Stored Procedures and SQL Functions.
4
+ *
5
+ * @Procedure — annotate a class method (or standalone function wrapper) so
6
+ * it is understood by the ORM as a stored procedure.
7
+ * The ORM can generate the CREATE PROCEDURE SQL and register
8
+ * a callable wrapper on your Prorm instance.
9
+ *
10
+ * @SqlFunction — like @Procedure but for database functions that RETURN a
11
+ * value and can be called inside SELECT / WHERE clauses.
12
+ *
13
+ * Both decorators store metadata in a registry. Call
14
+ * ProcedureRegistry.getSQL(target, method) to retrieve the generated DDL,
15
+ * and ProcedureRegistry.call(prorm, target, method, args) to execute.
16
+ *
17
+ * Example:
18
+ *
19
+ * class UserProcedures {
20
+ * @Procedure({
21
+ * name: 'get_user_by_email',
22
+ * params: [{ name: 'p_email', type: 'VARCHAR(255)', mode: 'IN' }],
23
+ * dialect: 'mysql',
24
+ * body: `BEGIN SELECT * FROM users WHERE email = p_email; END`,
25
+ * })
26
+ * getUserByEmail(email: string) {}
27
+ *
28
+ * @SqlFunction({
29
+ * name: 'full_name',
30
+ * params: [
31
+ * { name: 'first', type: 'TEXT' },
32
+ * { name: 'last', type: 'TEXT' },
33
+ * ],
34
+ * returns: 'TEXT',
35
+ * dialect: 'postgres',
36
+ * body: `SELECT first || ' ' || last`,
37
+ * language: 'SQL',
38
+ * })
39
+ * fullName(first: string, last: string): string { return ''; }
40
+ * }
41
+ *
42
+ * // Generate and run the DDL
43
+ * const sql = ProcedureRegistry.getSQL(UserProcedures, 'getUserByEmail');
44
+ * await prorm.query(sql);
45
+ *
46
+ * // Or use the auto-call helper
47
+ * const rows = await ProcedureRegistry.call(prorm, UserProcedures, 'getUserByEmail', ['john@example.com']);
48
+ */
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.ProcedureRegistry = void 0;
51
+ exports.Procedure = Procedure;
52
+ exports.SqlFunction = SqlFunction;
53
+ const registry = new Map();
54
+ function getOrCreateMap(target) {
55
+ if (!registry.has(target))
56
+ registry.set(target, new Map());
57
+ return registry.get(target);
58
+ }
59
+ // ==================== Decorators ====================
60
+ /**
61
+ * Mark a method as a SQL stored procedure.
62
+ *
63
+ * The decorated method is a no-op at runtime — use ProcedureRegistry.call()
64
+ * to execute the actual database procedure.
65
+ */
66
+ function Procedure(options = {}) {
67
+ return function (target, propertyKey, _descriptor) {
68
+ const map = getOrCreateMap(target.constructor ?? target);
69
+ map.set(propertyKey, {
70
+ kind: 'procedure',
71
+ methodKey: propertyKey,
72
+ options: { name: propertyKey, ...options },
73
+ });
74
+ };
75
+ }
76
+ /**
77
+ * Mark a method as a SQL function (returns a value, usable in SELECT/WHERE).
78
+ *
79
+ * The decorated method is a no-op at runtime — use ProcedureRegistry.call()
80
+ * to execute the actual database function.
81
+ */
82
+ function SqlFunction(options) {
83
+ return function (target, propertyKey, _descriptor) {
84
+ const map = getOrCreateMap(target.constructor ?? target);
85
+ map.set(propertyKey, {
86
+ kind: 'function',
87
+ methodKey: propertyKey,
88
+ options: { name: propertyKey, ...options },
89
+ });
90
+ };
91
+ }
92
+ // ==================== SQL Generation ====================
93
+ function buildMysqlProcedure(meta) {
94
+ const opts = meta.options;
95
+ const name = opts.schema ? `\`${opts.schema}\`.\`${opts.name}\`` : `\`${opts.name}\``;
96
+ const params = (opts.params ?? []).map((p) => `${p.mode ?? 'IN'} ${p.name} ${p.type}`).join(', ');
97
+ const drop = opts.replace ? `DROP PROCEDURE IF EXISTS ${name};\n` : '';
98
+ const body = opts.body ?? '-- TODO: add procedure body here';
99
+ return `${drop}CREATE PROCEDURE ${name}(${params})\nBEGIN\n ${body}\nEND`;
100
+ }
101
+ function buildMysqlFunction(meta) {
102
+ const opts = meta.options;
103
+ const name = opts.schema ? `\`${opts.schema}\`.\`${opts.name}\`` : `\`${opts.name}\``;
104
+ const params = (opts.params ?? []).map((p) => `${p.name} ${p.type}`).join(', ');
105
+ const determinism = opts.volatility ?? 'DETERMINISTIC';
106
+ const drop = opts.replace ? `DROP FUNCTION IF EXISTS ${name};\n` : '';
107
+ const body = opts.body ?? '-- TODO: add function body here';
108
+ return `${drop}CREATE FUNCTION ${name}(${params})\nRETURNS ${opts.returns}\n${determinism}\nBEGIN\n ${body}\nEND`;
109
+ }
110
+ function buildPostgresProcedure(meta) {
111
+ const opts = meta.options;
112
+ const name = opts.schema ? `"${opts.schema}"."${opts.name}"` : `"${opts.name}"`;
113
+ const params = (opts.params ?? []).map((p) => `${p.mode ?? 'IN'} ${p.name} ${p.type}`).join(', ');
114
+ const orReplace = opts.replace ? 'OR REPLACE ' : '';
115
+ const body = opts.body ?? '-- TODO: add procedure body here';
116
+ return `CREATE ${orReplace}PROCEDURE ${name}(${params})\nLANGUAGE plpgsql\nAS $$\nBEGIN\n ${body}\nEND;\n$$`;
117
+ }
118
+ function buildPostgresFunction(meta) {
119
+ const opts = meta.options;
120
+ const name = opts.schema ? `"${opts.schema}"."${opts.name}"` : `"${opts.name}"`;
121
+ const params = (opts.params ?? []).map((p) => `${p.name} ${p.type}`).join(', ');
122
+ const language = opts.language ?? 'plpgsql';
123
+ const volatility = opts.volatility ? `\n${opts.volatility}` : '';
124
+ const orReplace = opts.replace ? 'OR REPLACE ' : '';
125
+ const body = opts.body ?? '-- TODO: add function body here';
126
+ const bodyWrapper = language === 'SQL' ? `$$ ${body} $$` : `$$\nBEGIN\n ${body}\nEND;\n$$`;
127
+ return `CREATE ${orReplace}FUNCTION ${name}(${params})\nRETURNS ${opts.returns}${volatility}\nLANGUAGE ${language}\nAS ${bodyWrapper}`;
128
+ }
129
+ // ==================== Public Registry API ====================
130
+ exports.ProcedureRegistry = {
131
+ /**
132
+ * Get all registered procedures/functions on a class.
133
+ */
134
+ getAll(target) {
135
+ return Array.from(getOrCreateMap(target).values());
136
+ },
137
+ /**
138
+ * Get a single procedure/function meta by method name.
139
+ */
140
+ get(target, methodKey) {
141
+ return getOrCreateMap(target).get(methodKey);
142
+ },
143
+ /**
144
+ * Generate the CREATE PROCEDURE / CREATE FUNCTION SQL for a registered method.
145
+ *
146
+ * @param target - The class constructor
147
+ * @param methodKey - The method name
148
+ * @param dialect - Override the dialect stored in the decorator options
149
+ */
150
+ getSQL(target, methodKey, dialect) {
151
+ const meta = getOrCreateMap(target).get(methodKey);
152
+ if (!meta)
153
+ throw new Error(`[ORM] No @Procedure/@SqlFunction found for method "${methodKey}"`);
154
+ const effectiveDialect = dialect ?? meta.options.dialect ?? 'mysql';
155
+ if (meta.kind === 'procedure') {
156
+ if (effectiveDialect === 'postgres')
157
+ return buildPostgresProcedure(meta);
158
+ return buildMysqlProcedure(meta); // mysql / mariadb
159
+ }
160
+ else {
161
+ if (effectiveDialect === 'postgres')
162
+ return buildPostgresFunction(meta);
163
+ return buildMysqlFunction(meta);
164
+ }
165
+ },
166
+ /**
167
+ * Generate DDL for every procedure/function on a class and execute them.
168
+ *
169
+ * @param prorm - Prorm instance
170
+ * @param target - The class constructor
171
+ * @param dialect - Override dialect
172
+ */
173
+ async createAll(prorm, target, dialect) {
174
+ for (const [methodKey] of getOrCreateMap(target)) {
175
+ const sql = exports.ProcedureRegistry.getSQL(target, methodKey, dialect);
176
+ await prorm.query(sql);
177
+ }
178
+ },
179
+ /**
180
+ * Call a stored procedure.
181
+ *
182
+ * @param prorm - Prorm instance
183
+ * @param target - The class constructor that has the @Procedure decorator
184
+ * @param methodKey - The method name
185
+ * @param args - Arguments to pass (positional, matched to params order)
186
+ */
187
+ async call(prorm, target, methodKey, args = []) {
188
+ const meta = getOrCreateMap(target).get(methodKey);
189
+ if (!meta)
190
+ throw new Error(`[ORM] No @Procedure found for method "${methodKey}"`);
191
+ const opts = meta.options;
192
+ const name = opts.name ?? methodKey;
193
+ const dialect = opts.dialect ?? 'mysql';
194
+ const placeholders = args.map(() => (dialect === 'postgres' ? '$?' : '?')).join(', ');
195
+ let sql;
196
+ if (meta.kind === 'procedure') {
197
+ sql =
198
+ dialect === 'mysql' || dialect === 'mariadb'
199
+ ? `CALL \`${name}\`(${placeholders})`
200
+ : `CALL "${name}"(${placeholders})`;
201
+ }
202
+ else {
203
+ sql =
204
+ dialect === 'postgres'
205
+ ? `SELECT * FROM "${name}"(${placeholders})`
206
+ : `SELECT ${name}(${placeholders})`;
207
+ }
208
+ return prorm.query(sql, { replacements: args });
209
+ },
210
+ };