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,237 @@
1
+ "use strict";
2
+ /**
3
+ * Redpanda store implementation.
4
+ *
5
+ * Redpanda is a streaming data platform that is wire-compatible with the
6
+ * Apache Kafka protocol - it speaks the exact same protocol, so it reuses the
7
+ * `kafkajs` driver unchanged. Like Kafka, it has no query language and does
8
+ * not fit the SQL-shaped `Dialect` interface, so this class implements the
9
+ * minimal `NoSqlStore` marker interface and exposes Redpanda's real
10
+ * operations - producing to topics, creating/listing topics via the admin
11
+ * client, and subscribing consumers - instead of a `query(sql)` shape.
12
+ *
13
+ * Because Redpanda speaks the Kafka protocol, this store is a mirror of
14
+ * `KafkaStore`: same `kafkajs` client, same producer/consumer/admin surface.
15
+ * `kafkajs` is loaded lazily inside `connect()` (or supplied pre-built via
16
+ * `options.client`) so importing this module does not require the driver to
17
+ * be installed.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.RedpandaStore = void 0;
21
+ const errors_1 = require("../../errors");
22
+ function wrapConnectionError(err, brokers) {
23
+ const parent = err instanceof Error ? err : new Error(String(err));
24
+ const message = /econnrefused/i.test(parent.message)
25
+ ? 'Connection refused'
26
+ : `Unable to connect to Redpanda: ${parent.message}`;
27
+ return new errors_1.ConnectionError(message, {
28
+ parent,
29
+ database: 'redpanda',
30
+ host: brokers?.join(','),
31
+ });
32
+ }
33
+ function wrapDatabaseError(err, message) {
34
+ const parent = err instanceof Error ? err : new Error(String(err));
35
+ const combined = message ? `${message}: ${parent.message}` : parent.message;
36
+ return errors_1.DatabaseError.from(parent, { message: combined });
37
+ }
38
+ /**
39
+ * `RedpandaStore` wraps a `kafkajs` client (Redpanda is Kafka-protocol
40
+ * compatible) and exposes the core streaming operations: producing messages,
41
+ * topic administration, and consumer subscriptions.
42
+ */
43
+ class RedpandaStore {
44
+ constructor(options = {}) {
45
+ this.name = 'redpanda';
46
+ this.library = 'kafkajs';
47
+ this.client = null;
48
+ this.producer = null;
49
+ /** Consumers created via `subscribe()`, tracked so `disconnect()` can stop them all. */
50
+ this.consumers = [];
51
+ this.connected = false;
52
+ this.options = options;
53
+ }
54
+ // ---------------------------------------------------------------------
55
+ // Connection lifecycle
56
+ // ---------------------------------------------------------------------
57
+ async connect() {
58
+ if (this.connected && this.client) {
59
+ return;
60
+ }
61
+ try {
62
+ if (this.options.client) {
63
+ this.client = this.options.client;
64
+ }
65
+ else {
66
+ // Lazy-load the driver so importing this module doesn't require
67
+ // `kafkajs` to be installed unless a real connection is made.
68
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
69
+ const { Kafka } = require('kafkajs');
70
+ this.client = new Kafka({
71
+ clientId: this.options.clientId,
72
+ brokers: this.options.brokers ?? [],
73
+ ssl: this.options.ssl,
74
+ sasl: this.options.sasl,
75
+ });
76
+ }
77
+ this.producer = this.client.producer();
78
+ await this.producer.connect();
79
+ this.connected = true;
80
+ }
81
+ catch (err) {
82
+ this.client = null;
83
+ this.producer = null;
84
+ this.connected = false;
85
+ throw wrapConnectionError(err, this.options.brokers);
86
+ }
87
+ }
88
+ async disconnect() {
89
+ // Stop consumers first so they stop pulling before the producer/client go.
90
+ for (const consumer of this.consumers.splice(0)) {
91
+ try {
92
+ await consumer.disconnect();
93
+ }
94
+ catch {
95
+ // Best-effort teardown - keep disconnecting the rest.
96
+ }
97
+ }
98
+ if (this.producer) {
99
+ try {
100
+ await this.producer.disconnect();
101
+ }
102
+ catch {
103
+ // Best-effort teardown.
104
+ }
105
+ this.producer = null;
106
+ }
107
+ this.client = null;
108
+ this.connected = false;
109
+ }
110
+ isConnected() {
111
+ return this.connected && this.client !== null;
112
+ }
113
+ /** Returns the underlying `kafkajs` `Kafka` client for anything not wrapped here. */
114
+ getClient() {
115
+ return this.requireClient();
116
+ }
117
+ requireClient() {
118
+ if (!this.client) {
119
+ throw new errors_1.ConnectionError('Not connected to Redpanda', { database: 'redpanda' });
120
+ }
121
+ return this.client;
122
+ }
123
+ requireProducer() {
124
+ if (!this.producer) {
125
+ throw new errors_1.ConnectionError('Not connected to Redpanda', { database: 'redpanda' });
126
+ }
127
+ return this.producer;
128
+ }
129
+ // ---------------------------------------------------------------------
130
+ // Producing
131
+ // ---------------------------------------------------------------------
132
+ /**
133
+ * Produce one or more messages to `topic`. Returns kafkajs's record
134
+ * metadata (per-partition base offsets).
135
+ */
136
+ async produce(topic, messages) {
137
+ const producer = this.requireProducer();
138
+ try {
139
+ return await producer.send({ topic, messages });
140
+ }
141
+ catch (err) {
142
+ throw wrapDatabaseError(err, `Redpanda produce to "${topic}" failed`);
143
+ }
144
+ }
145
+ // ---------------------------------------------------------------------
146
+ // Topic administration
147
+ // ---------------------------------------------------------------------
148
+ /**
149
+ * Create `topic` via a short-lived admin connection. Returns kafkajs's
150
+ * boolean indicating whether the topic was newly created (`false` if it
151
+ * already existed).
152
+ */
153
+ async createTopic(topic, opts = {}) {
154
+ const client = this.requireClient();
155
+ const admin = client.admin();
156
+ try {
157
+ await admin.connect();
158
+ return await admin.createTopics({
159
+ topics: [
160
+ {
161
+ topic,
162
+ numPartitions: opts.numPartitions,
163
+ replicationFactor: opts.replicationFactor,
164
+ },
165
+ ],
166
+ });
167
+ }
168
+ catch (err) {
169
+ throw wrapDatabaseError(err, `Redpanda createTopic "${topic}" failed`);
170
+ }
171
+ finally {
172
+ try {
173
+ await admin.disconnect();
174
+ }
175
+ catch {
176
+ // Best-effort teardown of the admin connection.
177
+ }
178
+ }
179
+ }
180
+ /** List all topic names known to the cluster via a short-lived admin connection. */
181
+ async listTopics() {
182
+ const client = this.requireClient();
183
+ const admin = client.admin();
184
+ try {
185
+ await admin.connect();
186
+ return await admin.listTopics();
187
+ }
188
+ catch (err) {
189
+ throw wrapDatabaseError(err, 'Redpanda listTopics failed');
190
+ }
191
+ finally {
192
+ try {
193
+ await admin.disconnect();
194
+ }
195
+ catch {
196
+ // Best-effort teardown of the admin connection.
197
+ }
198
+ }
199
+ }
200
+ // ---------------------------------------------------------------------
201
+ // Consuming
202
+ // ---------------------------------------------------------------------
203
+ /**
204
+ * Subscribe a new consumer to `topics` and invoke `handler` for each
205
+ * message (kafkajs's `eachMessage` payload: `{ topic, partition, message }`).
206
+ * The consumer joins the group `options.groupId` (a generated id is used if
207
+ * omitted) and is tracked so `disconnect()` stops it. Returns the created
208
+ * kafkajs consumer.
209
+ */
210
+ async subscribe(topics, handler, options = {}) {
211
+ const client = this.requireClient();
212
+ const topicList = Array.isArray(topics) ? topics : [topics];
213
+ const groupId = options.groupId ?? `${this.options.clientId ?? 'redpanda'}-group-${Date.now()}`;
214
+ const consumer = client.consumer({ groupId });
215
+ try {
216
+ await consumer.connect();
217
+ await consumer.subscribe({
218
+ topics: topicList,
219
+ fromBeginning: options.fromBeginning ?? false,
220
+ });
221
+ await consumer.run({ eachMessage: handler });
222
+ this.consumers.push(consumer);
223
+ return consumer;
224
+ }
225
+ catch (err) {
226
+ try {
227
+ await consumer.disconnect();
228
+ }
229
+ catch {
230
+ // Don't leak the half-set-up consumer's connection.
231
+ }
232
+ throw wrapDatabaseError(err, `Redpanda subscribe to "${topicList.join(',')}" failed`);
233
+ }
234
+ }
235
+ }
236
+ exports.RedpandaStore = RedpandaStore;
237
+ exports.default = RedpandaStore;
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ /**
3
+ * Resque (node-resque) job/task-queue store.
4
+ *
5
+ * node-resque is a Redis-backed background-job system compatible with Ruby's
6
+ * Resque. A `Queue` enqueues jobs (a job class name plus args) into named
7
+ * queues, and a `Worker`/`MultiWorker` pulls jobs and runs the matching
8
+ * handler from a *jobs* map. It is not a database and does not fit the
9
+ * SQL-shaped `Dialect` interface, so this class implements the minimal
10
+ * `NoSqlStore` marker interface and exposes node-resque's real
11
+ * enqueue/worker operations instead.
12
+ *
13
+ * Uses the `node-resque` driver. It is an optional peer loaded lazily (via
14
+ * `require`) inside `connect()`, so importing this module does not require it
15
+ * to be installed unless you actually connect. A pre-built driver module
16
+ * (providing `Queue` and `Worker` constructors) can be injected via
17
+ * `ResqueStoreOptions.client`, in which case `connect()` adopts it and skips
18
+ * the `require` entirely (used by the unit tests to inject mocks).
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.ResqueStore = void 0;
22
+ const errors_1 = require("../../errors");
23
+ function wrapConnectionError(err) {
24
+ const parent = err instanceof Error ? err : new Error(String(err));
25
+ const message = /econnrefused/i.test(parent.message)
26
+ ? 'Connection refused'
27
+ : `Unable to connect to Resque: ${parent.message}`;
28
+ return new errors_1.ConnectionError(message, { parent, database: 'resque' });
29
+ }
30
+ function wrapDatabaseError(err, action) {
31
+ const parent = err instanceof Error ? err : new Error(String(err));
32
+ return errors_1.DatabaseError.from(parent, { message: `Resque ${action} failed: ${parent.message}` });
33
+ }
34
+ /** Normalize a job spec into `{ name, args }`. */
35
+ function toSpec(jobData) {
36
+ if (typeof jobData === 'string')
37
+ return { name: jobData, args: [] };
38
+ return { name: jobData.name, args: jobData.args ?? [] };
39
+ }
40
+ /**
41
+ * `ResqueStore` wraps a node-resque `Queue` for producing and a pool of
42
+ * `Worker`s (one per queue name) for consuming, exposing the common queue
43
+ * surface.
44
+ */
45
+ class ResqueStore {
46
+ constructor(options = {}) {
47
+ this.name = 'resque';
48
+ this.library = 'node-resque';
49
+ this.driver = null;
50
+ this.queue = null;
51
+ this.connected = false;
52
+ this.workers = new Map();
53
+ /** Accumulated `{ jobName: handler }` map shared across workers. */
54
+ this.jobs = {};
55
+ this.options = options;
56
+ }
57
+ // ---------------------------------------------------------------------
58
+ // Connection lifecycle
59
+ // ---------------------------------------------------------------------
60
+ async connect() {
61
+ if (this.connected && this.queue) {
62
+ return;
63
+ }
64
+ try {
65
+ this.driver = this.options.client != null ? this.options.client : require('node-resque');
66
+ this.queue = new this.driver.Queue({ connection: this.connectionOpts }, this.jobs);
67
+ if (typeof this.queue.connect === 'function') {
68
+ await this.queue.connect();
69
+ }
70
+ this.connected = true;
71
+ }
72
+ catch (err) {
73
+ this.queue = null;
74
+ this.driver = null;
75
+ this.connected = false;
76
+ throw wrapConnectionError(err);
77
+ }
78
+ }
79
+ async disconnect() {
80
+ for (const worker of this.workers.values()) {
81
+ try {
82
+ if (worker && typeof worker.end === 'function')
83
+ await worker.end();
84
+ }
85
+ catch {
86
+ // Ignore teardown errors.
87
+ }
88
+ }
89
+ this.workers.clear();
90
+ try {
91
+ if (this.queue && typeof this.queue.end === 'function')
92
+ await this.queue.end();
93
+ }
94
+ catch {
95
+ // Ignore teardown errors.
96
+ }
97
+ this.queue = null;
98
+ this.driver = null;
99
+ this.connected = false;
100
+ }
101
+ isConnected() {
102
+ return this.connected && this.queue !== null;
103
+ }
104
+ /** Returns the underlying node-resque `Queue` for operations not wrapped here. */
105
+ getClient() {
106
+ return this.requireQueue();
107
+ }
108
+ requireQueue() {
109
+ if (!this.queue) {
110
+ throw new errors_1.ConnectionError('Not connected to Resque', { database: 'resque' });
111
+ }
112
+ return this.queue;
113
+ }
114
+ get connectionOpts() {
115
+ return this.options.connection ?? { host: '127.0.0.1', port: 6379 };
116
+ }
117
+ // ---------------------------------------------------------------------
118
+ // Queue operations
119
+ // ---------------------------------------------------------------------
120
+ /**
121
+ * Enqueue a job onto a named queue via `queue.enqueue(queue, name, args)`.
122
+ * node-resque assigns no client-visible job id, so this resolves to `null`.
123
+ */
124
+ async enqueue(queue, jobData) {
125
+ try {
126
+ const { name, args } = toSpec(jobData);
127
+ await this.requireQueue().enqueue(queue, name, args);
128
+ return null;
129
+ }
130
+ catch (err) {
131
+ throw wrapDatabaseError(err, 'enqueue');
132
+ }
133
+ }
134
+ /**
135
+ * Register a `Worker` on a queue that runs `handler` for jobs on it. The
136
+ * handler is added to the shared jobs map under the queue name, and one
137
+ * worker is cached per queue name (a repeat `process()` returns it).
138
+ */
139
+ async process(queue, handler, opts = {}) {
140
+ try {
141
+ const existing = this.workers.get(queue);
142
+ if (existing)
143
+ return existing;
144
+ // node-resque looks up handlers by job name in the jobs map; register
145
+ // this handler (as a `perform` function) under the queue name.
146
+ this.jobs[queue] = { perform: handler };
147
+ const driver = this.driver;
148
+ if (!driver) {
149
+ throw new errors_1.ConnectionError('Not connected to Resque', { database: 'resque' });
150
+ }
151
+ const worker = new driver.Worker({ connection: this.connectionOpts, queues: [queue], ...opts }, this.jobs);
152
+ if (typeof worker.connect === 'function')
153
+ await worker.connect();
154
+ if (typeof worker.start === 'function')
155
+ worker.start();
156
+ this.workers.set(queue, worker);
157
+ return worker;
158
+ }
159
+ catch (err) {
160
+ if (err instanceof errors_1.ConnectionError)
161
+ throw err;
162
+ throw wrapDatabaseError(err, 'process');
163
+ }
164
+ }
165
+ /**
166
+ * List queued jobs on a named queue (node-resque `queue.queued(queue,
167
+ * start, stop)`). node-resque has no addressable per-id lookup, so `id` is
168
+ * interpreted as the 0-based index into the returned list.
169
+ */
170
+ async getJob(queue, id) {
171
+ try {
172
+ const list = await this.requireQueue().queued(queue, 0, -1);
173
+ const index = typeof id === 'number' ? id : Number(id);
174
+ return Array.isArray(list) ? (list[index] ?? null) : null;
175
+ }
176
+ catch (err) {
177
+ throw wrapDatabaseError(err, 'getJob');
178
+ }
179
+ }
180
+ /**
181
+ * Remove matching jobs from a queue via `queue.del(queue, name, args)`.
182
+ * `id` is the registered job name to delete.
183
+ */
184
+ async remove(queue, id) {
185
+ try {
186
+ return await this.requireQueue().del(queue, id);
187
+ }
188
+ catch (err) {
189
+ throw wrapDatabaseError(err, 'remove');
190
+ }
191
+ }
192
+ /** Return the number of jobs currently queued on a named queue. */
193
+ async stats(queue) {
194
+ try {
195
+ return await this.requireQueue().length(queue);
196
+ }
197
+ catch (err) {
198
+ throw wrapDatabaseError(err, 'stats');
199
+ }
200
+ }
201
+ }
202
+ exports.ResqueStore = ResqueStore;
203
+ exports.default = ResqueStore;
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ /**
3
+ * RethinkDB store implementation.
4
+ *
5
+ * RethinkDB is a document store whose queries are built by chaining methods
6
+ * on a term-builder (`r`) and executed with a trailing `.run()` - e.g.
7
+ * `r.table('users').filter({ active: true }).run(conn)`. That fluent,
8
+ * function-composition query model is nothing like the SQL-shaped `Dialect`
9
+ * interface (no `query(sql)`, no identifier escaping, no relational DDL), so
10
+ * this class implements the minimal `NoSqlStore` marker interface for
11
+ * connection lifecycle and otherwise exposes RethinkDB's real surface
12
+ * directly: document CRUD, ReQL `filter` predicates, table/database
13
+ * creation, and a `run()` escape hatch for arbitrary ReQL terms.
14
+ *
15
+ * Uses the `rethinkdb-ts` driver. The driver is lazily `require`d inside
16
+ * `connect()` so importing this module never forces `rethinkdb-ts` to be
17
+ * installed; the term-builder (`r`) and/or a `connection` can also be
18
+ * injected via the constructor options (used by the tests, and handy when a
19
+ * pool is managed elsewhere), in which case `connect()` skips the require.
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.RethinkDBStore = void 0;
23
+ const errors_1 = require("../../errors");
24
+ const DEFAULT_HOST = 'localhost';
25
+ const DEFAULT_PORT = 28015;
26
+ const DEFAULT_DB = 'test';
27
+ function wrapConnectionError(err, host) {
28
+ const parent = err instanceof Error ? err : new Error(String(err));
29
+ const message = /econnrefused/i.test(parent.message)
30
+ ? 'Connection refused'
31
+ : `Unable to connect to RethinkDB: ${parent.message}`;
32
+ return new errors_1.ConnectionError(message, { parent, database: 'rethinkdb', host });
33
+ }
34
+ function wrapDatabaseError(err, message) {
35
+ const parent = err instanceof Error ? err : new Error(String(err));
36
+ // Keep the action context (e.g. "RethinkDB insert failed") but don't
37
+ // discard the real underlying driver error text - append it so callers
38
+ // inspecting `err.message` still see what actually went wrong.
39
+ const combined = message ? `${message}: ${parent.message}` : parent.message;
40
+ return errors_1.DatabaseError.from(parent, { message: combined });
41
+ }
42
+ /**
43
+ * `RethinkDBStore` wraps the `rethinkdb-ts` term-builder (`r`) in a small,
44
+ * promise-based convenience layer over document CRUD, ReQL `filter`
45
+ * queries, and table/database creation, plus a `run()` escape hatch for
46
+ * arbitrary ReQL terms.
47
+ */
48
+ class RethinkDBStore {
49
+ constructor(options = {}) {
50
+ this.name = 'rethinkdb';
51
+ this.library = 'rethinkdb-ts';
52
+ /** The `rethinkdb-ts` term-builder (`r`) once connected. */
53
+ this.r = null;
54
+ /** A single connection, when in single-connection (injected) mode. */
55
+ this.connection = null;
56
+ this.connected = false;
57
+ this.options = options;
58
+ }
59
+ // ---------------------------------------------------------------------
60
+ // Connection lifecycle
61
+ // ---------------------------------------------------------------------
62
+ async connect() {
63
+ if (this.connected && this.r) {
64
+ return;
65
+ }
66
+ const host = this.options.host ?? DEFAULT_HOST;
67
+ const port = this.options.port ?? DEFAULT_PORT;
68
+ const db = this.options.db ?? DEFAULT_DB;
69
+ try {
70
+ // Prefer an injected term-builder; otherwise lazily require the driver
71
+ // so importing this module never forces `rethinkdb-ts` to be installed.
72
+ let r = this.options.r;
73
+ if (!r) {
74
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
75
+ const mod = require('rethinkdb-ts');
76
+ r = mod.r;
77
+ }
78
+ if (this.options.connection) {
79
+ // Single-connection mode: run queries against the injected connection.
80
+ this.connection = this.options.connection;
81
+ }
82
+ else if (!this.options.r) {
83
+ // Real-driver path: stand up a driver-managed connection pool. (When
84
+ // `r` is injected without a connection - e.g. in tests - queries run
85
+ // via the injected `r`'s own pool and no pool is created here.)
86
+ await r.connectPool({ servers: [{ host, port }], db });
87
+ }
88
+ this.r = r;
89
+ this.connected = true;
90
+ }
91
+ catch (err) {
92
+ this.r = null;
93
+ this.connection = null;
94
+ this.connected = false;
95
+ throw wrapConnectionError(err, host);
96
+ }
97
+ }
98
+ async disconnect() {
99
+ try {
100
+ if (this.connection && typeof this.connection.close === 'function') {
101
+ await this.connection.close();
102
+ }
103
+ else if (this.r && typeof this.r.getPoolMaster === 'function') {
104
+ const poolMaster = this.r.getPoolMaster();
105
+ if (poolMaster && typeof poolMaster.drain === 'function') {
106
+ await poolMaster.drain();
107
+ }
108
+ }
109
+ }
110
+ catch (err) {
111
+ throw wrapDatabaseError(err, 'RethinkDB disconnect failed');
112
+ }
113
+ finally {
114
+ this.r = null;
115
+ this.connection = null;
116
+ this.connected = false;
117
+ }
118
+ }
119
+ isConnected() {
120
+ return this.connected && this.r !== null;
121
+ }
122
+ /** Returns the underlying `rethinkdb-ts` term-builder (`r`). */
123
+ getClient() {
124
+ return this.requireR();
125
+ }
126
+ requireR() {
127
+ if (!this.r || !this.connected) {
128
+ throw new errors_1.ConnectionError('Not connected to RethinkDB. Call connect() first.', {
129
+ database: 'rethinkdb',
130
+ host: this.options.host,
131
+ });
132
+ }
133
+ return this.r;
134
+ }
135
+ /**
136
+ * Execute a built ReQL query. In single-connection mode the connection is
137
+ * passed to `.run(connection)`; with a driver-managed pool `.run()` needs
138
+ * no argument.
139
+ */
140
+ runQuery(query) {
141
+ return this.connection ? query.run(this.connection) : query.run();
142
+ }
143
+ async exec(build, action) {
144
+ const r = this.requireR();
145
+ try {
146
+ return await this.runQuery(build(r));
147
+ }
148
+ catch (err) {
149
+ throw wrapDatabaseError(err, `RethinkDB ${action} failed`);
150
+ }
151
+ }
152
+ // ---------------------------------------------------------------------
153
+ // Document CRUD
154
+ // ---------------------------------------------------------------------
155
+ /** Insert a document (or array of documents) into `table`. */
156
+ async insert(table, doc) {
157
+ return this.exec((r) => r.table(table).insert(doc), 'insert');
158
+ }
159
+ /** Fetch a single document from `table` by its primary key. Resolves to `null` if not found. */
160
+ async get(table, id) {
161
+ return this.exec((r) => r.table(table).get(id), 'get');
162
+ }
163
+ /** Partially update the document with primary key `id` in `table`, merging `patch`. */
164
+ async update(table, id, patch) {
165
+ return this.exec((r) => r.table(table).get(id).update(patch), 'update');
166
+ }
167
+ /** Delete the document with primary key `id` from `table`. */
168
+ async delete(table, id) {
169
+ return this.exec((r) => r.table(table).get(id).delete(), 'delete');
170
+ }
171
+ /**
172
+ * Return all documents in `table` matching `predicate` as an array.
173
+ * `predicate` is any ReQL `filter` argument - a match object
174
+ * (`{ active: true }`) or a predicate function (`row => row('age').gt(18)`).
175
+ * The result is materialized (cursor drained to an array) before returning.
176
+ */
177
+ async filter(table, predicate) {
178
+ const result = await this.exec((r) => r.table(table).filter(predicate), 'filter');
179
+ return this.toArray(result);
180
+ }
181
+ /** Drain a ReQL cursor to an array (arrays and plain values are passed through). */
182
+ async toArray(result) {
183
+ if (result && typeof result.toArray === 'function') {
184
+ return await result.toArray();
185
+ }
186
+ if (Array.isArray(result)) {
187
+ return result;
188
+ }
189
+ return result == null ? [] : [result];
190
+ }
191
+ // ---------------------------------------------------------------------
192
+ // Schema / administration
193
+ // ---------------------------------------------------------------------
194
+ /**
195
+ * Create a table. Pass a single `name` to create it in the connection's
196
+ * default database, or `(db, name)` to create it in a specific database.
197
+ */
198
+ async createTable(dbOrName, name) {
199
+ if (name !== undefined) {
200
+ return this.exec((r) => r.db(dbOrName).tableCreate(name), 'createTable');
201
+ }
202
+ return this.exec((r) => r.tableCreate(dbOrName), 'createTable');
203
+ }
204
+ /** Create a database. */
205
+ async createDatabase(name) {
206
+ return this.exec((r) => r.dbCreate(name), 'createDatabase');
207
+ }
208
+ // ---------------------------------------------------------------------
209
+ // Escape hatch
210
+ // ---------------------------------------------------------------------
211
+ /**
212
+ * Run an arbitrary ReQL `query` term (built from `getClient()`), for
213
+ * operations not wrapped here. In single-connection mode the store's
214
+ * connection is supplied to `.run()` automatically.
215
+ */
216
+ async run(query) {
217
+ if (!this.connected) {
218
+ throw new errors_1.ConnectionError('Not connected to RethinkDB. Call connect() first.', {
219
+ database: 'rethinkdb',
220
+ host: this.options.host,
221
+ });
222
+ }
223
+ try {
224
+ return await this.runQuery(query);
225
+ }
226
+ catch (err) {
227
+ throw wrapDatabaseError(err, 'RethinkDB run failed');
228
+ }
229
+ }
230
+ }
231
+ exports.RethinkDBStore = RethinkDBStore;
232
+ exports.default = RethinkDBStore;