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,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UnqliteStore = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* UnQLite store.
|
|
6
|
+
*
|
|
7
|
+
* UnQLite is an embedded, in-process, serverless database engine with a simple
|
|
8
|
+
* key-value store layer (plus a document store, not modelled here). A database
|
|
9
|
+
* is opened against a filesystem path and exposes KV primitives — `store`,
|
|
10
|
+
* `fetch`, `delete`, and record iteration. It has no SQL-shaped surface, so it
|
|
11
|
+
* does not fit the `Dialect` interface used by the SQL dialects in this repo.
|
|
12
|
+
* Instead it implements the minimal `NoSqlStore` marker interface
|
|
13
|
+
* (`src/nosql/store.ts`) and maps a common KV surface onto UnQLite's calls.
|
|
14
|
+
*
|
|
15
|
+
* Built on the `unqlite` npm driver (callback-based), loaded lazily inside
|
|
16
|
+
* `connect()` (via `require`) so importing this module never forces the driver
|
|
17
|
+
* to be installed; tests inject a pre-built mock `client` instead and never
|
|
18
|
+
* reach the `require`.
|
|
19
|
+
*/
|
|
20
|
+
const errors_1 = require("../../errors");
|
|
21
|
+
/**
|
|
22
|
+
* `UnqliteStore` wraps an `unqlite` client and exposes its embedded KV
|
|
23
|
+
* surface: `put`/`get`/`del` (mapped to `store`/`fetch`/`delete`), `batch` for
|
|
24
|
+
* multi-key writes, and `list` for key iteration.
|
|
25
|
+
*/
|
|
26
|
+
class UnqliteStore {
|
|
27
|
+
constructor(options = {}) {
|
|
28
|
+
this.name = 'unqlite';
|
|
29
|
+
this.library = 'unqlite';
|
|
30
|
+
this.client = null;
|
|
31
|
+
this.connected = false;
|
|
32
|
+
this.options = options;
|
|
33
|
+
this.path = options.path ?? './data/unqlite.db';
|
|
34
|
+
}
|
|
35
|
+
async connect() {
|
|
36
|
+
if (this.connected && this.client) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
if (this.options.client) {
|
|
41
|
+
this.client = this.options.client;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
45
|
+
const UnQLite = require('unqlite');
|
|
46
|
+
this.client = new UnQLite(this.path);
|
|
47
|
+
}
|
|
48
|
+
this.connected = true;
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
this.client = null;
|
|
52
|
+
this.connected = false;
|
|
53
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
54
|
+
throw new errors_1.ConnectionError(`Unable to open UnQLite: ${parent.message}`, {
|
|
55
|
+
parent,
|
|
56
|
+
database: 'unqlite',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async disconnect() {
|
|
61
|
+
if (this.client) {
|
|
62
|
+
const client = this.client;
|
|
63
|
+
if (typeof client.close === 'function') {
|
|
64
|
+
try {
|
|
65
|
+
await this.promisify((cb) => client.close(cb));
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// best-effort close
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
this.client = null;
|
|
72
|
+
}
|
|
73
|
+
this.connected = false;
|
|
74
|
+
}
|
|
75
|
+
isConnected() {
|
|
76
|
+
return this.connected && this.client !== null;
|
|
77
|
+
}
|
|
78
|
+
/** Returns the underlying `unqlite` client for anything not wrapped here. */
|
|
79
|
+
getClient() {
|
|
80
|
+
return this.requireClient();
|
|
81
|
+
}
|
|
82
|
+
requireClient() {
|
|
83
|
+
if (!this.client || !this.connected) {
|
|
84
|
+
throw new errors_1.ConnectionError('UnQLite client is not connected. Call connect() first.', {
|
|
85
|
+
database: 'unqlite',
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return this.client;
|
|
89
|
+
}
|
|
90
|
+
wrapError(action, err) {
|
|
91
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
92
|
+
return errors_1.DatabaseError.from(parent, { message: `UnQLite ${action} failed: ${parent.message}` });
|
|
93
|
+
}
|
|
94
|
+
/** Wrap a callback-style UnQLite call into a Promise. */
|
|
95
|
+
promisify(fn) {
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
fn((err, result) => (err ? reject(err) : resolve(result)));
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/** Writes (or overwrites) the value at `key` (UnQLite `store`). */
|
|
101
|
+
async put(key, value) {
|
|
102
|
+
const client = this.requireClient();
|
|
103
|
+
try {
|
|
104
|
+
await this.promisify((cb) => client.store(key, value, cb));
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
throw this.wrapError('put', err);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** Reads the value at `key` (UnQLite `fetch`). */
|
|
111
|
+
async get(key) {
|
|
112
|
+
const client = this.requireClient();
|
|
113
|
+
try {
|
|
114
|
+
return await this.promisify((cb) => client.fetch(key, cb));
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
throw this.wrapError('get', err);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Deletes the value at `key` (UnQLite `delete`). */
|
|
121
|
+
async del(key) {
|
|
122
|
+
const client = this.requireClient();
|
|
123
|
+
try {
|
|
124
|
+
await this.promisify((cb) => client.delete(key, cb));
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
throw this.wrapError('del', err);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Applies a list of `put`/`del` operations sequentially. */
|
|
131
|
+
async batch(ops) {
|
|
132
|
+
for (const op of ops) {
|
|
133
|
+
if (op.type === 'put') {
|
|
134
|
+
await this.put(op.key, op.value);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
await this.del(op.key);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** Lists all keys by iterating records with UnQLite's `each` cursor. */
|
|
142
|
+
async list() {
|
|
143
|
+
const client = this.requireClient();
|
|
144
|
+
try {
|
|
145
|
+
const keys = [];
|
|
146
|
+
await this.promisify((cb) => client.each((key) => {
|
|
147
|
+
keys.push(String(key));
|
|
148
|
+
}, cb));
|
|
149
|
+
return keys;
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
throw this.wrapError('list', err);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
exports.UnqliteStore = UnqliteStore;
|
|
157
|
+
exports.default = UnqliteStore;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Upstash Redis store implementation.
|
|
4
|
+
*
|
|
5
|
+
* Upstash Redis is a serverless, globally-replicated Redis served over an
|
|
6
|
+
* HTTP/REST connection via the `@upstash/redis` client. It has no SQL query
|
|
7
|
+
* surface, so this class implements the minimal `NoSqlStore` marker
|
|
8
|
+
* interface and exposes an edge-KV `get`/`set`/`del`/`list`/`incr` surface
|
|
9
|
+
* mapped onto the `@upstash/redis` client.
|
|
10
|
+
*
|
|
11
|
+
* ## Driver
|
|
12
|
+
*
|
|
13
|
+
* Uses `@upstash/redis` (`new Redis({ url, token })`). The driver is loaded
|
|
14
|
+
* lazily inside `connect()` (or supplied pre-built via the `client` option)
|
|
15
|
+
* so importing this module never requires `@upstash/redis` to be installed
|
|
16
|
+
* unless a store is actually connected. Tests inject a mock `client`.
|
|
17
|
+
*
|
|
18
|
+
* ## Serialization
|
|
19
|
+
*
|
|
20
|
+
* String values are passed through verbatim; every other value is
|
|
21
|
+
* `JSON.stringify`'d before being written.
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.UpstashStore = void 0;
|
|
25
|
+
const errors_1 = require("../../errors");
|
|
26
|
+
function serialize(value) {
|
|
27
|
+
return typeof value === 'string' ? value : JSON.stringify(value);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* `UpstashStore` wraps an `@upstash/redis` client behind the `NoSqlStore`
|
|
31
|
+
* lifecycle and an edge-KV `get`/`set`/`del`/`list`/`incr` surface.
|
|
32
|
+
*/
|
|
33
|
+
class UpstashStore {
|
|
34
|
+
constructor(options = {}) {
|
|
35
|
+
this.name = 'upstash';
|
|
36
|
+
this.library = '@upstash/redis';
|
|
37
|
+
this.client = null;
|
|
38
|
+
this.connected = false;
|
|
39
|
+
this.options = options;
|
|
40
|
+
}
|
|
41
|
+
// ---------------------------------------------------------------------
|
|
42
|
+
// Connection lifecycle
|
|
43
|
+
// ---------------------------------------------------------------------
|
|
44
|
+
async connect() {
|
|
45
|
+
if (this.connected && this.client) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
if (this.options.client) {
|
|
50
|
+
this.client = this.options.client;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
54
|
+
const mod = require('@upstash/redis');
|
|
55
|
+
const Redis = mod.Redis ?? mod.default ?? mod;
|
|
56
|
+
this.client = new Redis({
|
|
57
|
+
url: this.options.url,
|
|
58
|
+
token: this.options.token,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
this.connected = true;
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
this.client = null;
|
|
65
|
+
this.connected = false;
|
|
66
|
+
throw wrapConnectionError(err, this.options.url);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async disconnect() {
|
|
70
|
+
this.client = null;
|
|
71
|
+
this.connected = false;
|
|
72
|
+
}
|
|
73
|
+
isConnected() {
|
|
74
|
+
return this.connected && this.client !== null;
|
|
75
|
+
}
|
|
76
|
+
/** Returns the underlying `@upstash/redis` client for anything not wrapped here. */
|
|
77
|
+
getClient() {
|
|
78
|
+
return this.requireClient();
|
|
79
|
+
}
|
|
80
|
+
requireClient() {
|
|
81
|
+
if (!this.client) {
|
|
82
|
+
throw new errors_1.ConnectionError('Not connected to Upstash Redis', {
|
|
83
|
+
database: 'upstash',
|
|
84
|
+
host: this.options.url,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return this.client;
|
|
88
|
+
}
|
|
89
|
+
// ---------------------------------------------------------------------
|
|
90
|
+
// Key-value operations
|
|
91
|
+
// ---------------------------------------------------------------------
|
|
92
|
+
/** Read a key. Returns whatever the client returns (`null` if missing). */
|
|
93
|
+
async get(key) {
|
|
94
|
+
const client = this.requireClient();
|
|
95
|
+
try {
|
|
96
|
+
return await client.get(key);
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
throw wrapDatabaseError(err, 'GET');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Write `value` under `key`. Strings are stored verbatim; other values are
|
|
104
|
+
* JSON-serialized. `options.ttl` maps to Redis `EX` (seconds),
|
|
105
|
+
* `options.expiration` to `EXAT` (absolute Unix seconds).
|
|
106
|
+
*/
|
|
107
|
+
async set(key, value, options = {}) {
|
|
108
|
+
const client = this.requireClient();
|
|
109
|
+
const setOptions = {};
|
|
110
|
+
if (options.ttl !== undefined)
|
|
111
|
+
setOptions.ex = options.ttl;
|
|
112
|
+
if (options.expiration !== undefined)
|
|
113
|
+
setOptions.exat = options.expiration;
|
|
114
|
+
try {
|
|
115
|
+
return await client.set(key, serialize(value), Object.keys(setOptions).length > 0 ? setOptions : undefined);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
throw wrapDatabaseError(err, 'SET');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** Delete a key. Returns the number of keys removed. */
|
|
122
|
+
async del(key) {
|
|
123
|
+
const client = this.requireClient();
|
|
124
|
+
try {
|
|
125
|
+
return await client.del(key);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
throw wrapDatabaseError(err, 'DEL');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/** List key names, optionally filtered by `prefix` (matched as `<prefix>*`). */
|
|
132
|
+
async list(prefix) {
|
|
133
|
+
const client = this.requireClient();
|
|
134
|
+
try {
|
|
135
|
+
return await client.keys(prefix ? `${prefix}*` : '*');
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
throw wrapDatabaseError(err, 'LIST');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** Atomically increment the numeric value at `key` by `by` (default `1`). */
|
|
142
|
+
async incr(key, by = 1) {
|
|
143
|
+
const client = this.requireClient();
|
|
144
|
+
try {
|
|
145
|
+
return by === 1 ? await client.incr(key) : await client.incrby(key, by);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
throw wrapDatabaseError(err, 'INCR');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.UpstashStore = UpstashStore;
|
|
153
|
+
function wrapConnectionError(err, host) {
|
|
154
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
155
|
+
return new errors_1.ConnectionError(`Unable to connect to Upstash Redis: ${parent.message}`, {
|
|
156
|
+
parent,
|
|
157
|
+
database: 'upstash',
|
|
158
|
+
host,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
function wrapDatabaseError(err, action) {
|
|
162
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
163
|
+
return errors_1.DatabaseError.from(parent, {
|
|
164
|
+
message: `Upstash Redis ${action} failed: ${parent.message}`,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
exports.default = UpstashStore;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Vercel KV store implementation.
|
|
4
|
+
*
|
|
5
|
+
* Vercel KV is a serverless, globally-replicated Redis-compatible key-value
|
|
6
|
+
* store fronted by the `@vercel/kv` client (a durable-HTTP Redis client). It
|
|
7
|
+
* has no SQL query surface, so this class implements the minimal
|
|
8
|
+
* `NoSqlStore` marker interface and exposes an edge-KV
|
|
9
|
+
* `get`/`set`/`del`/`list`/`incr` surface mapped onto the `@vercel/kv`
|
|
10
|
+
* client.
|
|
11
|
+
*
|
|
12
|
+
* ## Driver
|
|
13
|
+
*
|
|
14
|
+
* Uses `@vercel/kv` (`createClient({ url, token })`). The driver is loaded
|
|
15
|
+
* lazily inside `connect()` (or supplied pre-built via the `client` option)
|
|
16
|
+
* so importing this module never requires `@vercel/kv` to be installed
|
|
17
|
+
* unless a store is actually connected. Tests inject a mock `client`.
|
|
18
|
+
*
|
|
19
|
+
* ## Serialization
|
|
20
|
+
*
|
|
21
|
+
* String values are passed through verbatim; every other value is
|
|
22
|
+
* `JSON.stringify`'d before being written, so reads via `get()` return the
|
|
23
|
+
* serialized string.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.VercelKvStore = void 0;
|
|
27
|
+
const errors_1 = require("../../errors");
|
|
28
|
+
function serialize(value) {
|
|
29
|
+
return typeof value === 'string' ? value : JSON.stringify(value);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* `VercelKvStore` wraps a `@vercel/kv` client behind the `NoSqlStore`
|
|
33
|
+
* lifecycle and an edge-KV `get`/`set`/`del`/`list`/`incr` surface.
|
|
34
|
+
*/
|
|
35
|
+
class VercelKvStore {
|
|
36
|
+
constructor(options = {}) {
|
|
37
|
+
this.name = 'vercel-kv';
|
|
38
|
+
this.library = '@vercel/kv';
|
|
39
|
+
this.client = null;
|
|
40
|
+
this.connected = false;
|
|
41
|
+
this.options = options;
|
|
42
|
+
}
|
|
43
|
+
// ---------------------------------------------------------------------
|
|
44
|
+
// Connection lifecycle
|
|
45
|
+
// ---------------------------------------------------------------------
|
|
46
|
+
async connect() {
|
|
47
|
+
if (this.connected && this.client) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
if (this.options.client) {
|
|
52
|
+
this.client = this.options.client;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
56
|
+
const kv = require('@vercel/kv');
|
|
57
|
+
this.client = kv.createClient({
|
|
58
|
+
url: this.options.url,
|
|
59
|
+
token: this.options.token,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
this.connected = true;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
this.client = null;
|
|
66
|
+
this.connected = false;
|
|
67
|
+
throw wrapConnectionError(err, this.options.url);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async disconnect() {
|
|
71
|
+
this.client = null;
|
|
72
|
+
this.connected = false;
|
|
73
|
+
}
|
|
74
|
+
isConnected() {
|
|
75
|
+
return this.connected && this.client !== null;
|
|
76
|
+
}
|
|
77
|
+
/** Returns the underlying `@vercel/kv` client for anything not wrapped here. */
|
|
78
|
+
getClient() {
|
|
79
|
+
return this.requireClient();
|
|
80
|
+
}
|
|
81
|
+
requireClient() {
|
|
82
|
+
if (!this.client) {
|
|
83
|
+
throw new errors_1.ConnectionError('Not connected to Vercel KV', {
|
|
84
|
+
database: 'vercel-kv',
|
|
85
|
+
host: this.options.url,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return this.client;
|
|
89
|
+
}
|
|
90
|
+
// ---------------------------------------------------------------------
|
|
91
|
+
// Key-value operations
|
|
92
|
+
// ---------------------------------------------------------------------
|
|
93
|
+
/** Read a key. Returns whatever the client returns (`null` if missing). */
|
|
94
|
+
async get(key) {
|
|
95
|
+
const client = this.requireClient();
|
|
96
|
+
try {
|
|
97
|
+
return await client.get(key);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
throw wrapDatabaseError(err, 'GET');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Write `value` under `key`. Strings are stored verbatim; other values are
|
|
105
|
+
* JSON-serialized. `options.ttl` maps to Redis `EX` (seconds),
|
|
106
|
+
* `options.expiration` to `EXAT` (absolute Unix seconds).
|
|
107
|
+
*/
|
|
108
|
+
async set(key, value, options = {}) {
|
|
109
|
+
const client = this.requireClient();
|
|
110
|
+
const setOptions = {};
|
|
111
|
+
if (options.ttl !== undefined)
|
|
112
|
+
setOptions.ex = options.ttl;
|
|
113
|
+
if (options.expiration !== undefined)
|
|
114
|
+
setOptions.exat = options.expiration;
|
|
115
|
+
try {
|
|
116
|
+
return await client.set(key, serialize(value), Object.keys(setOptions).length > 0 ? setOptions : undefined);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
throw wrapDatabaseError(err, 'SET');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** Delete a key. Returns the number of keys removed. */
|
|
123
|
+
async del(key) {
|
|
124
|
+
const client = this.requireClient();
|
|
125
|
+
try {
|
|
126
|
+
return await client.del(key);
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
throw wrapDatabaseError(err, 'DEL');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/** List key names, optionally filtered by `prefix` (matched as `<prefix>*`). */
|
|
133
|
+
async list(prefix) {
|
|
134
|
+
const client = this.requireClient();
|
|
135
|
+
try {
|
|
136
|
+
return await client.keys(prefix ? `${prefix}*` : '*');
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
throw wrapDatabaseError(err, 'LIST');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** Atomically increment the numeric value at `key` by `by` (default `1`). */
|
|
143
|
+
async incr(key, by = 1) {
|
|
144
|
+
const client = this.requireClient();
|
|
145
|
+
try {
|
|
146
|
+
return by === 1 ? await client.incr(key) : await client.incrby(key, by);
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
throw wrapDatabaseError(err, 'INCR');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
exports.VercelKvStore = VercelKvStore;
|
|
154
|
+
function wrapConnectionError(err, host) {
|
|
155
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
156
|
+
return new errors_1.ConnectionError(`Unable to connect to Vercel KV: ${parent.message}`, {
|
|
157
|
+
parent,
|
|
158
|
+
database: 'vercel-kv',
|
|
159
|
+
host,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function wrapDatabaseError(err, action) {
|
|
163
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
164
|
+
return errors_1.DatabaseError.from(parent, { message: `Vercel KV ${action} failed: ${parent.message}` });
|
|
165
|
+
}
|
|
166
|
+
exports.default = VercelKvStore;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* VictoriaMetrics store implementation.
|
|
4
|
+
*
|
|
5
|
+
* VictoriaMetrics is a fast, cost-effective time-series database. Crucially
|
|
6
|
+
* it is **API-compatible with Prometheus**: it exposes the same
|
|
7
|
+
* `/api/v1/query` and `/api/v1/query_range` PromQL endpoints (plus MetricsQL
|
|
8
|
+
* extensions), so PromQL queries written against Prometheus work unchanged
|
|
9
|
+
* here. Unlike Prometheus, VictoriaMetrics also accepts client-side writes -
|
|
10
|
+
* it can ingest data pushed to it in several line formats, including the
|
|
11
|
+
* Prometheus text exposition format via `/api/v1/import/prometheus`.
|
|
12
|
+
*
|
|
13
|
+
* Because there is no SQL-shaped query surface, no row-level UPDATE/DELETE,
|
|
14
|
+
* and no identifier escaping, this engine does not fit the SQL `Dialect`
|
|
15
|
+
* interface (`src/dialects/dialect.ts`). This class implements the minimal
|
|
16
|
+
* `NoSqlStore` marker interface (`src/nosql/store.ts`) and exposes the
|
|
17
|
+
* Prometheus-compatible read API plus VictoriaMetrics's write API.
|
|
18
|
+
*
|
|
19
|
+
* ## No canonical driver - HTTP over `fetch`
|
|
20
|
+
*
|
|
21
|
+
* VictoriaMetrics has no single canonical npm client; it is an HTTP/JSON
|
|
22
|
+
* (and text-ingest) API. This store talks to it with the global `fetch`.
|
|
23
|
+
* For tests (and custom transports) an alternative HTTP client can be
|
|
24
|
+
* injected via `VictoriaMetricsStoreOptions.client`; when omitted, a tiny
|
|
25
|
+
* internal client over `fetch` + `baseURL` is built at connect time.
|
|
26
|
+
*
|
|
27
|
+
* ## Endpoints wrapped
|
|
28
|
+
*
|
|
29
|
+
* Read (Prometheus-compatible, under `/api/v1`):
|
|
30
|
+
* - `instantQuery(promql, time?)` -> `GET /api/v1/query`
|
|
31
|
+
* - `rangeQuery(promql, {start,end,step})` -> `GET /api/v1/query_range`
|
|
32
|
+
*
|
|
33
|
+
* Write (VictoriaMetrics-specific):
|
|
34
|
+
* - `importPrometheus(lines)` -> `POST /api/v1/import/prometheus`
|
|
35
|
+
* - `write(metric, value, timestamp?, labels?)` builds one Prometheus
|
|
36
|
+
* exposition line and POSTs it via `importPrometheus`.
|
|
37
|
+
*
|
|
38
|
+
* Read methods return the parsed `data` field of the response envelope.
|
|
39
|
+
*/
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.VictoriaMetricsStore = exports.DEFAULT_VICTORIAMETRICS_BASE_URL = void 0;
|
|
42
|
+
exports.createFetchClient = createFetchClient;
|
|
43
|
+
exports.buildPrometheusLine = buildPrometheusLine;
|
|
44
|
+
const errors_1 = require("../../errors");
|
|
45
|
+
/** Default VictoriaMetrics HTTP API base URL (single-node). */
|
|
46
|
+
exports.DEFAULT_VICTORIAMETRICS_BASE_URL = 'http://localhost:8428';
|
|
47
|
+
/** Build a `fetch`-backed client for the given base URL. */
|
|
48
|
+
function createFetchClient(baseURL, headers) {
|
|
49
|
+
const buildUrl = (path, params) => {
|
|
50
|
+
const url = new URL(path, baseURL.endsWith('/') ? baseURL : `${baseURL}/`);
|
|
51
|
+
for (const [key, value] of Object.entries(params ?? {})) {
|
|
52
|
+
if (value === undefined)
|
|
53
|
+
continue;
|
|
54
|
+
if (Array.isArray(value)) {
|
|
55
|
+
for (const item of value)
|
|
56
|
+
url.searchParams.append(key, item);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
url.searchParams.set(key, value);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return url.toString();
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
async get(path, params) {
|
|
66
|
+
const res = await fetch(buildUrl(path, params), { method: 'GET', headers });
|
|
67
|
+
if (!res.ok) {
|
|
68
|
+
throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
69
|
+
}
|
|
70
|
+
return res.json();
|
|
71
|
+
},
|
|
72
|
+
async post(path, body, contentType) {
|
|
73
|
+
const res = await fetch(buildUrl(path), {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
body,
|
|
76
|
+
headers: { ...(headers ?? {}), ...(contentType ? { 'Content-Type': contentType } : {}) },
|
|
77
|
+
});
|
|
78
|
+
if (!res.ok) {
|
|
79
|
+
throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
80
|
+
}
|
|
81
|
+
const text = await res.text();
|
|
82
|
+
return text || undefined;
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Build a single Prometheus text-exposition line:
|
|
88
|
+
*
|
|
89
|
+
* metric{label="value",...} value [timestamp_ms]
|
|
90
|
+
*
|
|
91
|
+
* Label values are escaped per the exposition format (`\`, `"`, newline).
|
|
92
|
+
*/
|
|
93
|
+
function buildPrometheusLine(metric, value, timestamp, labels) {
|
|
94
|
+
const entries = Object.entries(labels ?? {});
|
|
95
|
+
const labelPart = entries.length
|
|
96
|
+
? `{${entries
|
|
97
|
+
.map(([k, v]) => `${k}="${String(v).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`)
|
|
98
|
+
.join(',')}}`
|
|
99
|
+
: '';
|
|
100
|
+
const tsPart = timestamp === undefined ? '' : ` ${timestamp}`;
|
|
101
|
+
return `${metric}${labelPart} ${value}${tsPart}`;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* `VictoriaMetricsStore` wraps VictoriaMetrics's Prometheus-compatible
|
|
105
|
+
* PromQL read API (instant/range queries) and its Prometheus-exposition
|
|
106
|
+
* write/import API behind the `NoSqlStore` lifecycle.
|
|
107
|
+
*/
|
|
108
|
+
class VictoriaMetricsStore {
|
|
109
|
+
constructor(options = {}) {
|
|
110
|
+
this.name = 'victoriametrics';
|
|
111
|
+
this.library = 'fetch';
|
|
112
|
+
this.client = null;
|
|
113
|
+
this.connected = false;
|
|
114
|
+
this.baseURL = options.baseURL ?? exports.DEFAULT_VICTORIAMETRICS_BASE_URL;
|
|
115
|
+
this.injectedClient = options.client;
|
|
116
|
+
this.headers = options.headers;
|
|
117
|
+
}
|
|
118
|
+
// ---------------------------------------------------------------------
|
|
119
|
+
// Connection lifecycle
|
|
120
|
+
// ---------------------------------------------------------------------
|
|
121
|
+
async connect() {
|
|
122
|
+
if (this.connected && this.client) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
this.client = this.injectedClient ?? createFetchClient(this.baseURL, this.headers);
|
|
126
|
+
this.connected = true;
|
|
127
|
+
}
|
|
128
|
+
async disconnect() {
|
|
129
|
+
this.client = null;
|
|
130
|
+
this.connected = false;
|
|
131
|
+
}
|
|
132
|
+
isConnected() {
|
|
133
|
+
return this.connected && this.client !== null;
|
|
134
|
+
}
|
|
135
|
+
/** Returns the underlying (internal or injected) HTTP client. */
|
|
136
|
+
getClient() {
|
|
137
|
+
return this.requireClient();
|
|
138
|
+
}
|
|
139
|
+
requireClient() {
|
|
140
|
+
if (!this.client) {
|
|
141
|
+
throw new errors_1.ConnectionError('Not connected to VictoriaMetrics', {
|
|
142
|
+
database: 'victoriametrics',
|
|
143
|
+
host: this.baseURL,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return this.client;
|
|
147
|
+
}
|
|
148
|
+
async doGet(path, params, action) {
|
|
149
|
+
const client = this.requireClient();
|
|
150
|
+
try {
|
|
151
|
+
const body = await client.get(path, params);
|
|
152
|
+
return body?.data;
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
156
|
+
throw errors_1.DatabaseError.from(parent, {
|
|
157
|
+
message: `VictoriaMetrics ${action ?? 'request'} failed: ${parent.message}`,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// ---------------------------------------------------------------------
|
|
162
|
+
// PromQL query API (Prometheus-compatible, read-only)
|
|
163
|
+
// ---------------------------------------------------------------------
|
|
164
|
+
/**
|
|
165
|
+
* Runs a PromQL/MetricsQL instant query via `GET /api/v1/query`
|
|
166
|
+
* (identical to the Prometheus endpoint). Returns the `data` payload.
|
|
167
|
+
*/
|
|
168
|
+
async instantQuery(promql, time) {
|
|
169
|
+
return this.doGet('/api/v1/query', { query: promql, time: time === undefined ? undefined : String(time) }, 'instantQuery');
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Runs a PromQL/MetricsQL range query via `GET /api/v1/query_range`
|
|
173
|
+
* (identical to the Prometheus endpoint). Returns the `data` payload.
|
|
174
|
+
*/
|
|
175
|
+
async rangeQuery(promql, options) {
|
|
176
|
+
return this.doGet('/api/v1/query_range', {
|
|
177
|
+
query: promql,
|
|
178
|
+
start: String(options.start),
|
|
179
|
+
end: String(options.end),
|
|
180
|
+
step: String(options.step),
|
|
181
|
+
}, 'rangeQuery');
|
|
182
|
+
}
|
|
183
|
+
// ---------------------------------------------------------------------
|
|
184
|
+
// Write API (VictoriaMetrics-specific)
|
|
185
|
+
// ---------------------------------------------------------------------
|
|
186
|
+
/**
|
|
187
|
+
* Imports raw data in Prometheus text-exposition format via
|
|
188
|
+
* `POST /api/v1/import/prometheus`. Accepts a single newline-delimited
|
|
189
|
+
* string or an array of lines (joined with newlines).
|
|
190
|
+
*/
|
|
191
|
+
async importPrometheus(lines) {
|
|
192
|
+
const client = this.requireClient();
|
|
193
|
+
const body = Array.isArray(lines) ? lines.join('\n') : lines;
|
|
194
|
+
try {
|
|
195
|
+
return await client.post('/api/v1/import/prometheus', body, 'text/plain');
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
199
|
+
throw errors_1.DatabaseError.from(parent, {
|
|
200
|
+
message: `VictoriaMetrics importPrometheus failed: ${parent.message}`,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Writes a single sample by building one Prometheus-exposition line
|
|
206
|
+
* (`metric{labels} value [timestamp_ms]`) and importing it via
|
|
207
|
+
* `importPrometheus`.
|
|
208
|
+
*/
|
|
209
|
+
async write(metric, value, timestamp, labels) {
|
|
210
|
+
return this.importPrometheus(buildPrometheusLine(metric, value, timestamp, labels));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
exports.VictoriaMetricsStore = VictoriaMetricsStore;
|