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,607 @@
1
+ "use strict";
2
+ /**
3
+ * Redis store implementation.
4
+ *
5
+ * Redis is a key-value store with several typed data structures (strings,
6
+ * hashes, lists, sets, sorted sets, streams) plus pub/sub and pipelining. It has no
7
+ * query language and does not fit the SQL-shaped `Dialect` interface, so
8
+ * this class implements the minimal `NoSqlStore` marker interface and
9
+ * exposes Redis's real operations grouped by data structure instead of
10
+ * forcing everything into a `query(sql)` shape.
11
+ *
12
+ * Uses the `ioredis` driver.
13
+ */
14
+ var __importDefault = (this && this.__importDefault) || function (mod) {
15
+ return (mod && mod.__esModule) ? mod : { "default": mod };
16
+ };
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.RedisStore = void 0;
19
+ const ioredis_1 = __importDefault(require("ioredis"));
20
+ const errors_1 = require("../../errors");
21
+ function wrapConnectionError(err, host) {
22
+ const parent = err instanceof Error ? err : new Error(String(err));
23
+ const message = /econnrefused/i.test(parent.message)
24
+ ? 'Connection refused'
25
+ : `Unable to connect to Redis: ${parent.message}`;
26
+ return new errors_1.ConnectionError(message, { parent, database: 'redis', host });
27
+ }
28
+ /** Convert Redis's flat `[field, value, field, value, ...]` array into a record. */
29
+ function fieldsToRecord(flat) {
30
+ const record = {};
31
+ for (let i = 0; i < flat.length; i += 2) {
32
+ record[flat[i]] = flat[i + 1];
33
+ }
34
+ return record;
35
+ }
36
+ /** Convert a raw `XRANGE`/`XREVRANGE`-shaped reply into `StreamEntry[]`. */
37
+ function toStreamEntries(raw) {
38
+ if (!raw)
39
+ return [];
40
+ return raw.map(([id, fields]) => ({ id, fields: fieldsToRecord(fields) }));
41
+ }
42
+ /** Convert a raw `XREAD`/`XREADGROUP`-shaped reply into `StreamReadResult[]`. */
43
+ function toStreamReadResults(raw) {
44
+ if (!raw)
45
+ return null;
46
+ return raw.map(([key, entries]) => ({ key, entries: toStreamEntries(entries) }));
47
+ }
48
+ function wrapDatabaseError(err, message) {
49
+ const parent = err instanceof Error ? err : new Error(String(err));
50
+ // Keep the `action` context (e.g. "Redis GET failed") but don't discard the
51
+ // real underlying Redis error text (e.g. "WRONGTYPE Operation against a key
52
+ // holding the wrong kind of value") - append it so callers inspecting
53
+ // `err.message` still see what actually went wrong.
54
+ const combined = message ? `${message}: ${parent.message}` : parent.message;
55
+ return errors_1.DatabaseError.from(parent, { message: combined });
56
+ }
57
+ /**
58
+ * `RedisStore` wraps an `ioredis` client and exposes Redis's data-structure
59
+ * commands (string/hash/list/set/sorted-set/stream), pub/sub, and
60
+ * pipelining as a typed, promise-based API.
61
+ */
62
+ class RedisStore {
63
+ constructor(options = {}) {
64
+ this.name = 'redis';
65
+ this.library = 'ioredis';
66
+ this.client = null;
67
+ this.subscriber = null;
68
+ this.connected = false;
69
+ /**
70
+ * Per-channel handlers for the shared subscriber connection. A single
71
+ * `message` listener is attached once and dispatches to the handler
72
+ * registered for the incoming message's channel, so each `subscribe()`
73
+ * call's handler only ever receives messages for its own channel.
74
+ */
75
+ this.channelHandlers = new Map();
76
+ /**
77
+ * Per-pattern handlers for the shared subscriber connection, mirroring
78
+ * `channelHandlers` but for `PSUBSCRIBE`/`PUNSUBSCRIBE`.
79
+ */
80
+ this.patternHandlers = new Map();
81
+ this.options = options;
82
+ }
83
+ // ---------------------------------------------------------------------
84
+ // Connection lifecycle
85
+ // ---------------------------------------------------------------------
86
+ createClient() {
87
+ const { url, ...rest } = this.options;
88
+ // ioredis connects lazily by default when constructed this way, so we
89
+ // explicitly opt into eager connection and await `connect()` below to
90
+ // surface connection failures immediately.
91
+ const opts = { lazyConnect: true, ...rest };
92
+ return url ? new ioredis_1.default(url, opts) : new ioredis_1.default(opts);
93
+ }
94
+ async connect() {
95
+ if (this.connected && this.client) {
96
+ return;
97
+ }
98
+ try {
99
+ this.client = this.createClient();
100
+ await this.client.connect();
101
+ this.connected = true;
102
+ }
103
+ catch (err) {
104
+ // Don't leak the failed client's socket - ioredis will otherwise keep
105
+ // retrying the connection in the background even though we're about
106
+ // to drop our only reference to it.
107
+ this.client?.disconnect();
108
+ this.client = null;
109
+ this.connected = false;
110
+ throw wrapConnectionError(err, this.options.host);
111
+ }
112
+ }
113
+ async disconnect() {
114
+ if (this.subscriber) {
115
+ this.subscriber.disconnect();
116
+ this.subscriber = null;
117
+ }
118
+ if (this.client) {
119
+ try {
120
+ await this.client.quit();
121
+ }
122
+ catch {
123
+ this.client.disconnect();
124
+ }
125
+ this.client = null;
126
+ }
127
+ this.connected = false;
128
+ }
129
+ isConnected() {
130
+ return this.connected && this.client !== null && this.client.status === 'ready';
131
+ }
132
+ /** Returns the underlying `ioredis` client for anything not wrapped here. */
133
+ getClient() {
134
+ return this.requireClient();
135
+ }
136
+ requireClient() {
137
+ if (!this.client) {
138
+ throw new errors_1.ConnectionError('Not connected to Redis', { database: 'redis' });
139
+ }
140
+ return this.client;
141
+ }
142
+ async exec(fn, action) {
143
+ const client = this.requireClient();
144
+ try {
145
+ return await fn(client);
146
+ }
147
+ catch (err) {
148
+ throw wrapDatabaseError(err, `Redis ${action} failed`);
149
+ }
150
+ }
151
+ // ---------------------------------------------------------------------
152
+ // JSON helpers (opt-in convenience on top of the raw string API)
153
+ // ---------------------------------------------------------------------
154
+ /** Serialize a value with `JSON.stringify` and `SET` it. */
155
+ async setJSON(key, value, options) {
156
+ return this.set(key, JSON.stringify(value), options);
157
+ }
158
+ /** `GET` a key and `JSON.parse` it. Returns `null` if the key is missing. */
159
+ async getJSON(key) {
160
+ const raw = await this.get(key);
161
+ if (raw === null) {
162
+ return null;
163
+ }
164
+ try {
165
+ return JSON.parse(raw);
166
+ }
167
+ catch (err) {
168
+ throw wrapDatabaseError(err, `Redis GETJSON failed (invalid JSON for key "${key}")`);
169
+ }
170
+ }
171
+ // ---------------------------------------------------------------------
172
+ // String commands
173
+ // ---------------------------------------------------------------------
174
+ async get(key) {
175
+ return this.exec((c) => c.get(key), 'GET');
176
+ }
177
+ async set(key, value, options = {}) {
178
+ return this.exec((c) => {
179
+ const args = [];
180
+ if (options.ex !== undefined)
181
+ args.push('EX', options.ex);
182
+ if (options.px !== undefined)
183
+ args.push('PX', options.px);
184
+ if (options.keepttl)
185
+ args.push('KEEPTTL');
186
+ if (options.nx)
187
+ args.push('NX');
188
+ if (options.xx)
189
+ args.push('XX');
190
+ return c.set(key, value, ...args);
191
+ }, 'SET');
192
+ }
193
+ async del(...keys) {
194
+ return this.exec((c) => c.del(...keys), 'DEL');
195
+ }
196
+ async exists(...keys) {
197
+ return this.exec((c) => c.exists(...keys), 'EXISTS');
198
+ }
199
+ async incr(key) {
200
+ return this.exec((c) => c.incr(key), 'INCR');
201
+ }
202
+ async incrby(key, amount) {
203
+ return this.exec((c) => c.incrby(key, amount), 'INCRBY');
204
+ }
205
+ async expire(key, seconds) {
206
+ return this.exec((c) => c.expire(key, seconds), 'EXPIRE');
207
+ }
208
+ async ttl(key) {
209
+ return this.exec((c) => c.ttl(key), 'TTL');
210
+ }
211
+ // ---------------------------------------------------------------------
212
+ // Hash commands
213
+ // ---------------------------------------------------------------------
214
+ async hget(key, field) {
215
+ return this.exec((c) => c.hget(key, field), 'HGET');
216
+ }
217
+ async hset(key, fields) {
218
+ return this.exec((c) => c.hset(key, fields), 'HSET');
219
+ }
220
+ async hgetall(key) {
221
+ return this.exec((c) => c.hgetall(key), 'HGETALL');
222
+ }
223
+ async hdel(key, ...fields) {
224
+ return this.exec((c) => c.hdel(key, ...fields), 'HDEL');
225
+ }
226
+ async hincrby(key, field, amount) {
227
+ return this.exec((c) => c.hincrby(key, field, amount), 'HINCRBY');
228
+ }
229
+ // ---------------------------------------------------------------------
230
+ // List commands
231
+ // ---------------------------------------------------------------------
232
+ async lpush(key, ...values) {
233
+ return this.exec((c) => c.lpush(key, ...values), 'LPUSH');
234
+ }
235
+ async rpush(key, ...values) {
236
+ return this.exec((c) => c.rpush(key, ...values), 'RPUSH');
237
+ }
238
+ async lrange(key, start, stop) {
239
+ return this.exec((c) => c.lrange(key, start, stop), 'LRANGE');
240
+ }
241
+ async lpop(key, count) {
242
+ return this.exec((c) => (count === undefined ? c.lpop(key) : c.lpop(key, count)), 'LPOP');
243
+ }
244
+ async rpop(key, count) {
245
+ return this.exec((c) => (count === undefined ? c.rpop(key) : c.rpop(key, count)), 'RPOP');
246
+ }
247
+ // ---------------------------------------------------------------------
248
+ // Set commands
249
+ // ---------------------------------------------------------------------
250
+ async sadd(key, ...members) {
251
+ return this.exec((c) => c.sadd(key, ...members), 'SADD');
252
+ }
253
+ async srem(key, ...members) {
254
+ return this.exec((c) => c.srem(key, ...members), 'SREM');
255
+ }
256
+ async smembers(key) {
257
+ return this.exec((c) => c.smembers(key), 'SMEMBERS');
258
+ }
259
+ async sismember(key, member) {
260
+ return this.exec(async (c) => (await c.sismember(key, member)) === 1, 'SISMEMBER');
261
+ }
262
+ // ---------------------------------------------------------------------
263
+ // Sorted set commands
264
+ // ---------------------------------------------------------------------
265
+ async zadd(key, ...members) {
266
+ return this.exec((c) => {
267
+ const args = [];
268
+ for (const { score, member } of members) {
269
+ args.push(score, member);
270
+ }
271
+ return c.zadd(key, ...args);
272
+ }, 'ZADD');
273
+ }
274
+ async zrange(key, start, stop, options = {}) {
275
+ return this.exec((c) => {
276
+ const args = [];
277
+ if (options.rev)
278
+ args.push('REV');
279
+ if (options.withScores)
280
+ args.push('WITHSCORES');
281
+ if (options.limit)
282
+ args.push('LIMIT', options.limit.offset, options.limit.count);
283
+ return c.zrange(key, start, stop, ...args);
284
+ }, 'ZRANGE');
285
+ }
286
+ async zrangebyscore(key, min, max, options = {}) {
287
+ return this.exec((c) => {
288
+ const args = [];
289
+ if (options.withScores)
290
+ args.push('WITHSCORES');
291
+ if (options.limit)
292
+ args.push('LIMIT', options.limit.offset, options.limit.count);
293
+ return c.zrangebyscore(key, min, max, ...args);
294
+ }, 'ZRANGEBYSCORE');
295
+ }
296
+ async zrem(key, ...members) {
297
+ return this.exec((c) => c.zrem(key, ...members), 'ZREM');
298
+ }
299
+ async zscore(key, member) {
300
+ return this.exec((c) => c.zscore(key, member), 'ZSCORE');
301
+ }
302
+ // ---------------------------------------------------------------------
303
+ // Stream commands
304
+ // ---------------------------------------------------------------------
305
+ /**
306
+ * `XADD` - append an entry to a stream, creating it if it doesn't exist.
307
+ * Pass `'*'` as `id` to let Redis auto-generate a monotonic
308
+ * `<ms>-<seq>` ID (the common case); returns the ID that was assigned.
309
+ */
310
+ async xadd(key, id, fields) {
311
+ return this.exec((c) => {
312
+ const args = [];
313
+ for (const [field, value] of Object.entries(fields)) {
314
+ args.push(field, value);
315
+ }
316
+ return c.xadd(key, id, ...args);
317
+ }, 'XADD');
318
+ }
319
+ /** `XRANGE` - read entries with IDs between `start` and `end` (inclusive), in ID order. Use `'-'`/`'+'` for the full range. */
320
+ async xrange(key, start, end, count) {
321
+ return this.exec(async (c) => {
322
+ const raw = count === undefined ? await c.xrange(key, start, end) : await c.xrange(key, start, end, 'COUNT', count);
323
+ return toStreamEntries(raw);
324
+ }, 'XRANGE');
325
+ }
326
+ /** `XREVRANGE` - like `xrange()` but in reverse ID order; `start` is the higher/first ID, `end` the lower/last. */
327
+ async xrevrange(key, start, end, count) {
328
+ return this.exec(async (c) => {
329
+ const raw = count === undefined
330
+ ? await c.xrevrange(key, start, end)
331
+ : await c.xrevrange(key, start, end, 'COUNT', count);
332
+ return toStreamEntries(raw);
333
+ }, 'XREVRANGE');
334
+ }
335
+ /**
336
+ * `XREAD` - read entries newer than `ids` from one or more streams.
337
+ * `keys[i]` is paired with `ids[i]`; pass `'$'` for an ID to only read
338
+ * entries added after the call starts, or `'0'` to read from the
339
+ * beginning. With `options.block`, blocks (up to that many ms, or
340
+ * forever if `0`) waiting for new entries instead of returning
341
+ * immediately. Returns `null` if nothing matched (e.g. a non-blocking
342
+ * read found nothing new).
343
+ */
344
+ async xread(keys, ids, options = {}) {
345
+ return this.exec(async (c) => {
346
+ const args = [];
347
+ if (options.count !== undefined)
348
+ args.push('COUNT', options.count);
349
+ if (options.block !== undefined)
350
+ args.push('BLOCK', options.block);
351
+ args.push('STREAMS', ...keys, ...ids);
352
+ const raw = (await c.xread(...args));
353
+ return toStreamReadResults(raw);
354
+ }, 'XREAD');
355
+ }
356
+ /** `XLEN` - number of entries in a stream. */
357
+ async xlen(key) {
358
+ return this.exec((c) => c.xlen(key), 'XLEN');
359
+ }
360
+ /** `XDEL` - remove specific entries from a stream by ID. Returns the number actually removed. */
361
+ async xdel(key, ids) {
362
+ return this.exec((c) => c.xdel(key, ...ids), 'XDEL');
363
+ }
364
+ /**
365
+ * `XTRIM` - trim a stream to (approximately) `threshold` entries
366
+ * (`'MAXLEN'`) or drop entries older than a given ID (`'MINID'`).
367
+ * Returns the number of entries removed.
368
+ */
369
+ async xtrim(key, strategy, threshold) {
370
+ return this.exec((c) => c.xtrim(key, strategy, threshold), 'XTRIM');
371
+ }
372
+ /**
373
+ * `XGROUP CREATE` - create a consumer group on a stream, starting
374
+ * delivery from `id` (default `'$'`, i.e. only new entries). Creates the
375
+ * stream first (via `MKSTREAM`) if it doesn't exist yet.
376
+ */
377
+ async xgroupCreate(key, groupName, id = '$') {
378
+ return this.exec((c) => c.xgroup('CREATE', key, groupName, id, 'MKSTREAM'), 'XGROUP CREATE');
379
+ }
380
+ /**
381
+ * `XREADGROUP` - read entries from one or more streams as a named
382
+ * consumer within a consumer group, so entries are tracked in the
383
+ * group's pending-entries list until `xack()`'d. Pass `'>'` for an ID to
384
+ * receive only entries never delivered to this group; any other ID
385
+ * re-reads this consumer's already-delivered-but-unacked entries.
386
+ */
387
+ async xreadgroup(groupName, consumerName, keys, ids, options = {}) {
388
+ return this.exec(async (c) => {
389
+ const args = ['GROUP', groupName, consumerName];
390
+ if (options.count !== undefined)
391
+ args.push('COUNT', options.count);
392
+ if (options.block !== undefined)
393
+ args.push('BLOCK', options.block);
394
+ args.push('STREAMS', ...keys, ...ids);
395
+ const raw = (await c.xreadgroup(...args));
396
+ return toStreamReadResults(raw);
397
+ }, 'XREADGROUP');
398
+ }
399
+ /** `XACK` - acknowledge that a consumer group has finished processing the given entry IDs. */
400
+ async xack(key, groupName, ids) {
401
+ return this.exec((c) => c.xack(key, groupName, ...ids), 'XACK');
402
+ }
403
+ // ---------------------------------------------------------------------
404
+ // Scripting (Lua)
405
+ // ---------------------------------------------------------------------
406
+ /**
407
+ * `EVAL` - run a Lua `script` server-side, atomically. `keys` are passed
408
+ * as Redis's `KEYS[1..N]` (and count toward the `numkeys` argument),
409
+ * `args` as `ARGV[1..N]`. Prefer `evalsha()` once a script has been
410
+ * cached (via `scriptLoad()` or a prior `eval()`) to avoid resending the
411
+ * source on every call.
412
+ */
413
+ async eval(script, keys = [], args = []) {
414
+ return this.exec((c) => c.eval(script, keys.length, ...keys, ...args), 'EVAL');
415
+ }
416
+ /**
417
+ * `EVALSHA` - run a previously cached Lua script by its SHA1 digest
418
+ * (see `scriptLoad()`), avoiding the cost of resending the script body.
419
+ * Redis replies with a `NOSCRIPT` error (surfaced here as a
420
+ * `DatabaseError`) if the script isn't cached on the server.
421
+ */
422
+ async evalsha(sha1, keys = [], args = []) {
423
+ return this.exec((c) => c.evalsha(sha1, keys.length, ...keys, ...args), 'EVALSHA');
424
+ }
425
+ /**
426
+ * `SCRIPT LOAD` - cache a Lua script on the server without executing it,
427
+ * returning its SHA1 digest for later `evalsha()` calls.
428
+ */
429
+ async scriptLoad(script) {
430
+ return this.exec((c) => c.script('LOAD', script), 'SCRIPT LOAD');
431
+ }
432
+ // ---------------------------------------------------------------------
433
+ // HyperLogLog (approximate cardinality)
434
+ // ---------------------------------------------------------------------
435
+ /** `PFADD` - add elements to a HyperLogLog. Returns `1` if the estimated cardinality changed, `0` otherwise. */
436
+ async pfadd(key, ...elements) {
437
+ return this.exec((c) => c.pfadd(key, ...elements), 'PFADD');
438
+ }
439
+ /** `PFCOUNT` - the approximate cardinality of one HyperLogLog, or the union of several. */
440
+ async pfcount(...keys) {
441
+ return this.exec((c) => c.pfcount(...keys), 'PFCOUNT');
442
+ }
443
+ /** `PFMERGE` - merge one or more source HyperLogLogs into `destKey` (created/overwritten as the union). */
444
+ async pfmerge(destKey, ...sourceKeys) {
445
+ return this.exec((c) => c.pfmerge(destKey, ...sourceKeys), 'PFMERGE');
446
+ }
447
+ // ---------------------------------------------------------------------
448
+ // Pub/sub
449
+ // ---------------------------------------------------------------------
450
+ async publish(channel, message) {
451
+ return this.exec((c) => c.publish(channel, message), 'PUBLISH');
452
+ }
453
+ /**
454
+ * Subscribe to a channel and invoke `handler` for each message received.
455
+ *
456
+ * Pub/sub requires a dedicated connection in Redis (a subscribed
457
+ * connection can't issue other commands), so this lazily creates a
458
+ * second `ioredis` client reusing the same connection options and keeps
459
+ * it alive for the lifetime of the store (or until `unsubscribe`/
460
+ * `disconnect` is called).
461
+ */
462
+ /**
463
+ * Lazily create the shared subscriber connection and attach the
464
+ * `message`/`pmessage` dispatch listeners exactly once. Both `subscribe()`
465
+ * and `psubscribe()` share this single connection (a subscribed Redis
466
+ * connection can't issue other commands, so both plain and pattern
467
+ * subscriptions ride the same dedicated socket).
468
+ */
469
+ async ensureSubscriber() {
470
+ if (this.subscriber) {
471
+ return this.subscriber;
472
+ }
473
+ const subscriber = this.createClient();
474
+ try {
475
+ await subscriber.connect();
476
+ }
477
+ catch (err) {
478
+ // Don't leak the failed subscriber's socket - same reasoning as
479
+ // `connect()` above.
480
+ subscriber.disconnect();
481
+ throw wrapConnectionError(err, this.options.host);
482
+ }
483
+ this.subscriber = subscriber;
484
+ // A single shared listener dispatches by channel so that each
485
+ // `subscribe(channel, handler)` call's handler only ever receives
486
+ // messages for its own channel, rather than every channel getting
487
+ // routed to whichever handler happened to be registered first.
488
+ this.subscriber.on('message', (ch, msg) => {
489
+ const channelHandler = this.channelHandlers.get(ch);
490
+ channelHandler?.(msg, ch);
491
+ });
492
+ // Same idea for pattern subscriptions, dispatched by the pattern that
493
+ // matched rather than the concrete channel.
494
+ this.subscriber.on('pmessage', (pattern, ch, msg) => {
495
+ const patternHandler = this.patternHandlers.get(pattern);
496
+ patternHandler?.(msg, ch, pattern);
497
+ });
498
+ return this.subscriber;
499
+ }
500
+ async subscribe(channel, handler) {
501
+ const subscriber = await this.ensureSubscriber();
502
+ this.channelHandlers.set(channel, handler);
503
+ try {
504
+ await subscriber.subscribe(channel);
505
+ }
506
+ catch (err) {
507
+ this.channelHandlers.delete(channel);
508
+ throw wrapDatabaseError(err, 'Redis SUBSCRIBE failed');
509
+ }
510
+ }
511
+ async unsubscribe(channel) {
512
+ if (!this.subscriber) {
513
+ return;
514
+ }
515
+ try {
516
+ if (channel) {
517
+ await this.subscriber.unsubscribe(channel);
518
+ this.channelHandlers.delete(channel);
519
+ }
520
+ else {
521
+ await this.subscriber.unsubscribe();
522
+ this.channelHandlers.clear();
523
+ this.closeSubscriberIfIdle();
524
+ }
525
+ }
526
+ catch (err) {
527
+ throw wrapDatabaseError(err, 'Redis UNSUBSCRIBE failed');
528
+ }
529
+ }
530
+ /**
531
+ * `PSUBSCRIBE` - subscribe to all channels matching a glob-style `pattern`
532
+ * (e.g. `news.*`, `__keyevent@0__:expired`) and invoke `handler` for each
533
+ * matching message. This is the standard mechanism for consuming Redis
534
+ * keyspace notifications (e.g. subscribing to
535
+ * `__keyevent@0__:expired` to react to key expirations), where the
536
+ * concrete channel name varies and only a pattern subscription can catch
537
+ * all of them. Shares the same lazily-created subscriber connection as
538
+ * `subscribe()`.
539
+ */
540
+ async psubscribe(pattern, handler) {
541
+ const subscriber = await this.ensureSubscriber();
542
+ this.patternHandlers.set(pattern, handler);
543
+ try {
544
+ await subscriber.psubscribe(pattern);
545
+ }
546
+ catch (err) {
547
+ this.patternHandlers.delete(pattern);
548
+ throw wrapDatabaseError(err, 'Redis PSUBSCRIBE failed');
549
+ }
550
+ }
551
+ /**
552
+ * `PUNSUBSCRIBE` - stop receiving messages for `pattern` (or every pattern
553
+ * subscription, if omitted). Closes the shared subscriber connection once
554
+ * both the channel and pattern handler sets are empty.
555
+ */
556
+ async punsubscribe(pattern) {
557
+ if (!this.subscriber) {
558
+ return;
559
+ }
560
+ try {
561
+ if (pattern) {
562
+ await this.subscriber.punsubscribe(pattern);
563
+ this.patternHandlers.delete(pattern);
564
+ }
565
+ else {
566
+ await this.subscriber.punsubscribe();
567
+ this.patternHandlers.clear();
568
+ }
569
+ this.closeSubscriberIfIdle();
570
+ }
571
+ catch (err) {
572
+ throw wrapDatabaseError(err, 'Redis PUNSUBSCRIBE failed');
573
+ }
574
+ }
575
+ /** Tear down the shared subscriber connection once nothing is subscribed through it. */
576
+ closeSubscriberIfIdle() {
577
+ if (this.subscriber && this.channelHandlers.size === 0 && this.patternHandlers.size === 0) {
578
+ this.subscriber.disconnect();
579
+ this.subscriber = null;
580
+ }
581
+ }
582
+ // ---------------------------------------------------------------------
583
+ // Pipelining / transactions
584
+ // ---------------------------------------------------------------------
585
+ /**
586
+ * Queue multiple commands and send them in a single round trip. Unlike
587
+ * `multi()`, commands are not guaranteed atomic — a failure in one
588
+ * command does not abort the others. Returns the ioredis chainable
589
+ * commander; call `.exec()` to run it.
590
+ */
591
+ pipeline() {
592
+ return this.requireClient().pipeline();
593
+ }
594
+ /**
595
+ * Queue multiple commands to run atomically (Redis `MULTI`/`EXEC`). This
596
+ * is Redis's substitute for cross-key transactions: all queued commands
597
+ * either all run or (if `.discard()` is called) none do, but there is no
598
+ * rollback of partial effects and no cross-command isolation the way a
599
+ * SQL transaction provides. Returns the ioredis chainable commander;
600
+ * call `.exec()` to run it.
601
+ */
602
+ multi() {
603
+ return this.requireClient().multi();
604
+ }
605
+ }
606
+ exports.RedisStore = RedisStore;
607
+ exports.default = RedisStore;