ts-prorm-orm 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1111 -0
- package/LICENSE +21 -0
- package/README.md +573 -0
- package/dist/audit/history-query.js +180 -0
- package/dist/audit/index.js +23 -0
- package/dist/audit/logger.js +236 -0
- package/dist/cache/cache-manager.js +84 -0
- package/dist/cache/index.js +16 -0
- package/dist/cache/redis-cluster-cache.js +557 -0
- package/dist/cli.js +2200 -0
- package/dist/compliance/audit-trail.js +68 -0
- package/dist/compliance/backup-verification.js +685 -0
- package/dist/compliance/breach-detector.js +505 -0
- package/dist/compliance/consent-record.js +227 -0
- package/dist/compliance/consent-versioning.js +331 -0
- package/dist/compliance/cross-border-log.js +530 -0
- package/dist/compliance/data-classifier.js +258 -0
- package/dist/compliance/data-lineage.js +565 -0
- package/dist/compliance/data-masker.js +303 -0
- package/dist/compliance/data-portability.js +265 -0
- package/dist/compliance/data-retention.js +195 -0
- package/dist/compliance/dsar-workflow.js +342 -0
- package/dist/compliance/field-encryption.js +223 -0
- package/dist/compliance/immutable-record.js +148 -0
- package/dist/compliance/index.js +260 -0
- package/dist/compliance/privacy-impact-assessment.js +364 -0
- package/dist/compliance/pseudonymization.js +269 -0
- package/dist/compliance/query-firewall.js +1182 -0
- package/dist/compliance/rate-limiter.js +580 -0
- package/dist/compliance/right-to-erasure.js +117 -0
- package/dist/compliance/row-level-security.js +246 -0
- package/dist/compliance/security-decorator.js +574 -0
- package/dist/compliance/security-monitor.js +525 -0
- package/dist/compliance/sensitive-data-discovery.js +476 -0
- package/dist/compliance/session-isolation.js +472 -0
- package/dist/compliance/tls-enforcer.js +108 -0
- package/dist/compliance/worm-storage.js +712 -0
- package/dist/connection-manager.js +198 -0
- package/dist/connection-pool.js +519 -0
- package/dist/decorators/audit.js +136 -0
- package/dist/decorators/belongs-to-many.js +115 -0
- package/dist/decorators/belongs-to.js +115 -0
- package/dist/decorators/check.js +435 -0
- package/dist/decorators/collate.js +329 -0
- package/dist/decorators/comment.js +205 -0
- package/dist/decorators/database-settings.js +236 -0
- package/dist/decorators/default.js +244 -0
- package/dist/decorators/encryption.js +235 -0
- package/dist/decorators/engine.js +97 -0
- package/dist/decorators/fk-constraints.js +594 -0
- package/dist/decorators/foreign-table.js +136 -0
- package/dist/decorators/generated.js +274 -0
- package/dist/decorators/has-many.js +127 -0
- package/dist/decorators/has-one.js +116 -0
- package/dist/decorators/hstore.js +129 -0
- package/dist/decorators/index.js +355 -0
- package/dist/decorators/json-column.js +83 -0
- package/dist/decorators/jsonb.js +101 -0
- package/dist/decorators/orm-decorators.js +425 -0
- package/dist/decorators/permissions.js +294 -0
- package/dist/decorators/procedure.js +210 -0
- package/dist/decorators/query-options.js +556 -0
- package/dist/decorators/range.js +167 -0
- package/dist/decorators/set-column.js +83 -0
- package/dist/decorators/spatial.js +114 -0
- package/dist/decorators/storage.js +580 -0
- package/dist/decorators/timezone.js +512 -0
- package/dist/decorators/trigger.js +90 -0
- package/dist/decorators/uuid.js +135 -0
- package/dist/decorators/view.js +258 -0
- package/dist/diagrams/chen-diagram.js +354 -0
- package/dist/diagrams/class-diagram.js +384 -0
- package/dist/diagrams/dependency-diagram.js +432 -0
- package/dist/diagrams/er-diagram.js +605 -0
- package/dist/diagrams/flow-diagram.js +394 -0
- package/dist/diagrams/gantt-diagram.js +411 -0
- package/dist/diagrams/index-diagram.js +353 -0
- package/dist/diagrams/index.js +184 -0
- package/dist/diagrams/migration-diagram.js +316 -0
- package/dist/diagrams/model-diagram.js +616 -0
- package/dist/diagrams/package-diagram.js +376 -0
- package/dist/diagrams/palette.js +85 -0
- package/dist/diagrams/relational-diagram.js +455 -0
- package/dist/diagrams/schemadoc-diagram.js +309 -0
- package/dist/diagrams/sequence-diagram.js +307 -0
- package/dist/diagrams/state-diagram.js +344 -0
- package/dist/diagrams/svg-dom.js +111 -0
- package/dist/diagrams/tree-diagram.js +250 -0
- package/dist/dialects/clickhouse/index.js +1742 -0
- package/dist/dialects/cockroachdb/index.js +4677 -0
- package/dist/dialects/cratedb/index.js +441 -0
- package/dist/dialects/databricks/index.js +517 -0
- package/dist/dialects/db2/index.js +2225 -0
- package/dist/dialects/dialect.js +720 -0
- package/dist/dialects/duckdb/index.js +2014 -0
- package/dist/dialects/exasol/index.js +357 -0
- package/dist/dialects/firebird/index.js +495 -0
- package/dist/dialects/greenplum/index.js +457 -0
- package/dist/dialects/hana/index.js +1741 -0
- package/dist/dialects/mariadb/index.js +3590 -0
- package/dist/dialects/mssql/index.js +2296 -0
- package/dist/dialects/mysql/index.js +4058 -0
- package/dist/dialects/oracle/index.js +2999 -0
- package/dist/dialects/postgres/index.js +5319 -0
- package/dist/dialects/query-stream-helper.js +152 -0
- package/dist/dialects/questdb/index.js +433 -0
- package/dist/dialects/redshift/index.js +2277 -0
- package/dist/dialects/singlestore/index.js +354 -0
- package/dist/dialects/snowflake/index.js +2082 -0
- package/dist/dialects/spanner/index.js +1768 -0
- package/dist/dialects/sqlite/index.js +3382 -0
- package/dist/dialects/tidb/index.js +377 -0
- package/dist/dialects/timescaledb/index.js +164 -0
- package/dist/dialects/trino/index.js +420 -0
- package/dist/dialects/turso/index.js +227 -0
- package/dist/dialects/vertica/index.js +317 -0
- package/dist/dialects/yugabytedb/index.js +424 -0
- package/dist/errors/index.js +472 -0
- package/dist/errors/utils.js +340 -0
- package/dist/errors.js +21 -0
- package/dist/extensions/catalog/cloud-warehouse-features.js +207 -0
- package/dist/extensions/catalog/mssql-features.js +147 -0
- package/dist/extensions/catalog/mysql-mariadb-plugins.js +229 -0
- package/dist/extensions/catalog/oracle-db2-features.js +196 -0
- package/dist/extensions/catalog/postgres-extensions.js +516 -0
- package/dist/extensions/index.js +71 -0
- package/dist/extensions/types.js +13 -0
- package/dist/foreign-data.js +173 -0
- package/dist/hooks/hooks-manager.js +350 -0
- package/dist/hooks/index.js +37 -0
- package/dist/index.js +463 -0
- package/dist/logging.js +335 -0
- package/dist/migrations/index.js +40 -0
- package/dist/migrations/migration.js +196 -0
- package/dist/migrations/migrator.js +411 -0
- package/dist/migrations/prormmigration.js +275 -0
- package/dist/migrations/query-interface.js +435 -0
- package/dist/migrations/seeder.js +353 -0
- package/dist/models/associations.js +852 -0
- package/dist/models/constraints.js +288 -0
- package/dist/models/data-types.js +2518 -0
- package/dist/models/decorators.js +445 -0
- package/dist/models/index.js +33 -0
- package/dist/models/indexes.js +531 -0
- package/dist/models/methods.js +382 -0
- package/dist/models/model-manager.js +103 -0
- package/dist/models/model.js +5349 -0
- package/dist/models/operators.js +67 -0
- package/dist/models/scopes.js +189 -0
- package/dist/models/typescript-types.js +26 -0
- package/dist/nosql/aerospike/index.js +205 -0
- package/dist/nosql/allegrograph/index.js +169 -0
- package/dist/nosql/arangodb/index.js +364 -0
- package/dist/nosql/azure-blob/index.js +206 -0
- package/dist/nosql/beanstalkd/index.js +231 -0
- package/dist/nosql/beequeue/index.js +210 -0
- package/dist/nosql/bigchaindb/index.js +195 -0
- package/dist/nosql/bigtable/index.js +224 -0
- package/dist/nosql/blazegraph/index.js +173 -0
- package/dist/nosql/bullmq/index.js +191 -0
- package/dist/nosql/cassandra/index.js +174 -0
- package/dist/nosql/chroma/index.js +190 -0
- package/dist/nosql/cloudflare-kv/index.js +220 -0
- package/dist/nosql/coherence/index.js +200 -0
- package/dist/nosql/cosmosdb/index.js +157 -0
- package/dist/nosql/couchbase/index.js +213 -0
- package/dist/nosql/dax/index.js +212 -0
- package/dist/nosql/deno-kv/index.js +206 -0
- package/dist/nosql/dgraph/index.js +171 -0
- package/dist/nosql/doris/index.js +169 -0
- package/dist/nosql/druid/index.js +162 -0
- package/dist/nosql/dynamodb/index.js +937 -0
- package/dist/nosql/elasticsearch/index.js +377 -0
- package/dist/nosql/etcd/index.js +502 -0
- package/dist/nosql/eventhubs/index.js +213 -0
- package/dist/nosql/eventstore/index.js +254 -0
- package/dist/nosql/faunadb/index.js +188 -0
- package/dist/nosql/firestore/index.js +177 -0
- package/dist/nosql/fluree/index.js +148 -0
- package/dist/nosql/fuseki/index.js +170 -0
- package/dist/nosql/gcs/index.js +172 -0
- package/dist/nosql/gearman/index.js +160 -0
- package/dist/nosql/geode/index.js +196 -0
- package/dist/nosql/graphdb/index.js +169 -0
- package/dist/nosql/graylog/index.js +188 -0
- package/dist/nosql/gridgain/index.js +171 -0
- package/dist/nosql/hazelcast/index.js +162 -0
- package/dist/nosql/hbase/index.js +230 -0
- package/dist/nosql/ignite/index.js +173 -0
- package/dist/nosql/immudb/index.js +184 -0
- package/dist/nosql/index.js +232 -0
- package/dist/nosql/infinispan/index.js +200 -0
- package/dist/nosql/influxdb/index.js +0 -0
- package/dist/nosql/kafka/index.js +234 -0
- package/dist/nosql/keyspaces/index.js +189 -0
- package/dist/nosql/kinesis/index.js +253 -0
- package/dist/nosql/leveldb/index.js +153 -0
- package/dist/nosql/lmdb/index.js +160 -0
- package/dist/nosql/loki/index.js +201 -0
- package/dist/nosql/marklogic/index.js +204 -0
- package/dist/nosql/materialize/index.js +145 -0
- package/dist/nosql/meilisearch/index.js +154 -0
- package/dist/nosql/memcached/index.js +223 -0
- package/dist/nosql/milvus/index.js +410 -0
- package/dist/nosql/minio/index.js +265 -0
- package/dist/nosql/momento/index.js +179 -0
- package/dist/nosql/mongodb/index.js +461 -0
- package/dist/nosql/nats/index.js +247 -0
- package/dist/nosql/nedb/index.js +164 -0
- package/dist/nosql/neo4j/index.js +450 -0
- package/dist/nosql/neptune/index.js +470 -0
- package/dist/nosql/nsq/index.js +200 -0
- package/dist/nosql/opensearch/index.js +186 -0
- package/dist/nosql/orientdb/index.js +175 -0
- package/dist/nosql/papertrail/index.js +200 -0
- package/dist/nosql/pinecone/index.js +0 -0
- package/dist/nosql/pinot/index.js +133 -0
- package/dist/nosql/pouchdb/index.js +172 -0
- package/dist/nosql/prometheus/index.js +174 -0
- package/dist/nosql/provendb/index.js +147 -0
- package/dist/nosql/pubsub/index.js +187 -0
- package/dist/nosql/pulsar/index.js +232 -0
- package/dist/nosql/qdrant/index.js +295 -0
- package/dist/nosql/qldb/index.js +192 -0
- package/dist/nosql/r2/index.js +281 -0
- package/dist/nosql/rabbitmq/index.js +237 -0
- package/dist/nosql/ravendb/index.js +175 -0
- package/dist/nosql/redis/index.js +607 -0
- package/dist/nosql/redpanda/index.js +237 -0
- package/dist/nosql/resque/index.js +203 -0
- package/dist/nosql/rethinkdb/index.js +232 -0
- package/dist/nosql/rocksdb/index.js +152 -0
- package/dist/nosql/rockset/index.js +126 -0
- package/dist/nosql/s3/index.js +298 -0
- package/dist/nosql/scylladb/index.js +178 -0
- package/dist/nosql/signoz/index.js +227 -0
- package/dist/nosql/sns/index.js +201 -0
- package/dist/nosql/solr/index.js +231 -0
- package/dist/nosql/splunk/index.js +227 -0
- package/dist/nosql/sqs/index.js +246 -0
- package/dist/nosql/stardog/index.js +169 -0
- package/dist/nosql/starrocks/index.js +170 -0
- package/dist/nosql/store.js +2 -0
- package/dist/nosql/sumologic/index.js +213 -0
- package/dist/nosql/surrealdb/index.js +179 -0
- package/dist/nosql/terminusdb/index.js +151 -0
- package/dist/nosql/tigergraph/index.js +469 -0
- package/dist/nosql/typesense/index.js +148 -0
- package/dist/nosql/unqlite/index.js +157 -0
- package/dist/nosql/upstash/index.js +167 -0
- package/dist/nosql/vercel-kv/index.js +166 -0
- package/dist/nosql/victoriametrics/index.js +213 -0
- package/dist/nosql/virtuoso/index.js +169 -0
- package/dist/nosql/weaviate/index.js +276 -0
- package/dist/operators/index.js +108 -0
- package/dist/operators.js +2690 -0
- package/dist/prisma-migrate/index.js +38 -0
- package/dist/prisma-migrate/migration-generator.js +125 -0
- package/dist/prisma-migrate/model-generator.js +172 -0
- package/dist/prisma-migrate/relations.js +100 -0
- package/dist/prisma-migrate/schema-parser.js +167 -0
- package/dist/prisma-migrate/type-mapper.js +40 -0
- package/dist/prorm.js +6832 -0
- package/dist/query-builders/cte-builder.js +80 -0
- package/dist/query-builders/functions/aggregate.js +390 -0
- package/dist/query-builders/functions/conditional.js +503 -0
- package/dist/query-builders/functions/datetime.js +695 -0
- package/dist/query-builders/functions/fulltext.js +439 -0
- package/dist/query-builders/functions/index.js +93 -0
- package/dist/query-builders/functions/json.js +427 -0
- package/dist/query-builders/functions/math.js +399 -0
- package/dist/query-builders/functions/string.js +518 -0
- package/dist/query-builders/functions/window.js +328 -0
- package/dist/query-builders/include-builder.js +323 -0
- package/dist/query-builders/index-expression-builder.js +242 -0
- package/dist/query-builders/index.js +161 -0
- package/dist/query-builders/insert-builder.js +164 -0
- package/dist/query-builders/model-helpers.js +38 -0
- package/dist/query-builders/order-limit-builder.js +239 -0
- package/dist/query-builders/sql-compiler.js +2040 -0
- package/dist/query-builders/subquery-builder.js +152 -0
- package/dist/query-builders/update-builder.js +182 -0
- package/dist/query-builders/view-builder.js +218 -0
- package/dist/query-builders/where-builder.js +1532 -0
- package/dist/query-interface.js +622 -0
- package/dist/query-optimizers/batch-optimizer.js +426 -0
- package/dist/query-optimizers/explain-plans.js +415 -0
- package/dist/query-optimizers/index.js +51 -0
- package/dist/query-optimizers/prepared-statement-cache.js +423 -0
- package/dist/query-optimizers/query-hints.js +438 -0
- package/dist/query-optimizers/query-optimizer.js +278 -0
- package/dist/query-optimizers/slow-query-logger.js +271 -0
- package/dist/replica-manager.js +507 -0
- package/dist/schema/index.js +15 -0
- package/dist/schema/migration-generator.js +374 -0
- package/dist/schema/schema-differ.js +549 -0
- package/dist/schema/types.js +6 -0
- package/dist/sql-constants.js +300 -0
- package/dist/sqlite-advanced.js +1047 -0
- package/dist/streams/index.js +12 -0
- package/dist/streams/transforms.js +178 -0
- package/dist/transaction.js +456 -0
- package/dist/types/index.js +180 -0
- package/dist/user-management.js +127 -0
- package/dist/utils/date.js +300 -0
- package/dist/utils/index.js +1079 -0
- package/dist/utils/string.js +161 -0
- package/dist/validators/index.js +19 -0
- package/dist/validators/validator.js +911 -0
- package/package.json +190 -0
|
@@ -0,0 +1,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;
|