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,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Apache Solr store implementation.
|
|
4
|
+
*
|
|
5
|
+
* Solr is a search engine built on Lucene: documents are schema-flexible
|
|
6
|
+
* JSON records indexed into a "core" (or "collection" in SolrCloud), and
|
|
7
|
+
* the primary operation is a Lucene/DisMax query (`q`, `fq`, `fl`, `sort`,
|
|
8
|
+
* `start`/`rows`, faceting) against that inverted index - not SQL, not
|
|
9
|
+
* joins, not a transaction log. Writes are buffered until an explicit
|
|
10
|
+
* `commit` makes them searchable. None of that fits the SQL-shaped
|
|
11
|
+
* `Dialect` interface used by the SQL dialects in this repo, so this class
|
|
12
|
+
* implements the minimal `NoSqlStore` marker interface (`src/nosql/store.ts`)
|
|
13
|
+
* and exposes Solr's real operations (add, search, delete, commit, optimize)
|
|
14
|
+
* directly.
|
|
15
|
+
*
|
|
16
|
+
* Uses the `solr-client` driver, lazy-loaded inside `connect()` via
|
|
17
|
+
* `require()` so the dependency is only needed when a Solr store is actually
|
|
18
|
+
* constructed (tests inject a pre-built mock `client` instead).
|
|
19
|
+
*
|
|
20
|
+
* ## Driver call shape (callback vs promise)
|
|
21
|
+
*
|
|
22
|
+
* The classic `solr-client` API is Node-style callbacks:
|
|
23
|
+
* `client.add(docs, (err, obj) => ...)`, `client.commit(cb)`,
|
|
24
|
+
* `client.search(query, cb)`, `client.deleteByID(id, cb)`,
|
|
25
|
+
* `client.deleteByQuery(q, cb)`, `client.optimize(cb)`. Some builds /
|
|
26
|
+
* forks instead return a Promise when no callback is passed. To work with
|
|
27
|
+
* either, every call goes through {@link invoke}, which passes a callback
|
|
28
|
+
* *and* inspects the return value: if the driver returned a thenable it is
|
|
29
|
+
* awaited directly (the callback is then ignored), otherwise the callback
|
|
30
|
+
* settles the Promise. `client.query()` (when present) builds a query
|
|
31
|
+
* object via a fluent builder; if the driver doesn't expose it, `search()`
|
|
32
|
+
* accepts a raw query object/string unchanged.
|
|
33
|
+
*/
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.SolrStore = void 0;
|
|
36
|
+
const errors_1 = require("../../errors");
|
|
37
|
+
/**
|
|
38
|
+
* Bridge the driver's callback-or-promise call convention into a Promise.
|
|
39
|
+
* Passes a Node-style callback; if the method instead returns a thenable,
|
|
40
|
+
* that thenable wins and the callback is ignored.
|
|
41
|
+
*/
|
|
42
|
+
function invoke(fn) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
let settledByReturn = false;
|
|
45
|
+
const maybe = fn((err, result) => {
|
|
46
|
+
if (settledByReturn) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (err) {
|
|
50
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
resolve(result);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
if (maybe && typeof maybe.then === 'function') {
|
|
57
|
+
settledByReturn = true;
|
|
58
|
+
maybe.then(resolve, reject);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function wrapConnectionError(err, host) {
|
|
63
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
64
|
+
const message = /econnrefused|connect failed/i.test(parent.message)
|
|
65
|
+
? 'Connection refused'
|
|
66
|
+
: `Unable to connect to Solr: ${parent.message}`;
|
|
67
|
+
return new errors_1.ConnectionError(message, { parent, database: 'solr', host });
|
|
68
|
+
}
|
|
69
|
+
function wrapDatabaseError(err, message) {
|
|
70
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
71
|
+
const combined = message ? `${message}: ${parent.message}` : parent.message;
|
|
72
|
+
return errors_1.DatabaseError.from(parent, { message: combined });
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* `SolrStore` wraps a `solr-client` client and exposes Solr's document
|
|
76
|
+
* indexing, querying, deletion, and commit/optimize operations as a typed,
|
|
77
|
+
* promise-based API.
|
|
78
|
+
*/
|
|
79
|
+
class SolrStore {
|
|
80
|
+
constructor(options = {}) {
|
|
81
|
+
this.name = 'solr';
|
|
82
|
+
this.library = 'solr-client';
|
|
83
|
+
this.client = null;
|
|
84
|
+
this.connected = false;
|
|
85
|
+
this.options = options;
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------
|
|
88
|
+
// Connection lifecycle
|
|
89
|
+
// ---------------------------------------------------------------------
|
|
90
|
+
async connect() {
|
|
91
|
+
if (this.connected && this.client) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
if (this.options.client) {
|
|
96
|
+
// A client was injected (e.g. a test mock) - use it as-is and skip
|
|
97
|
+
// the driver require()/createClient() step.
|
|
98
|
+
this.client = this.options.client;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
102
|
+
const solr = require('solr-client');
|
|
103
|
+
this.client = solr.createClient({
|
|
104
|
+
host: this.options.host,
|
|
105
|
+
port: this.options.port,
|
|
106
|
+
core: this.options.core,
|
|
107
|
+
path: this.options.path,
|
|
108
|
+
protocol: this.options.protocol,
|
|
109
|
+
secure: this.options.secure,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
this.connected = true;
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
this.client = null;
|
|
116
|
+
this.connected = false;
|
|
117
|
+
throw wrapConnectionError(err, this.options.host);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async disconnect() {
|
|
121
|
+
// The solr-client is a stateless HTTP client - there is no socket to
|
|
122
|
+
// close, so "disconnecting" just drops the client reference.
|
|
123
|
+
this.client = null;
|
|
124
|
+
this.connected = false;
|
|
125
|
+
}
|
|
126
|
+
isConnected() {
|
|
127
|
+
return this.connected && this.client !== null;
|
|
128
|
+
}
|
|
129
|
+
/** Returns the underlying `solr-client` client for anything not wrapped here. */
|
|
130
|
+
getClient() {
|
|
131
|
+
return this.requireClient();
|
|
132
|
+
}
|
|
133
|
+
requireClient() {
|
|
134
|
+
if (!this.client) {
|
|
135
|
+
throw new errors_1.ConnectionError('Not connected to Solr', { database: 'solr' });
|
|
136
|
+
}
|
|
137
|
+
return this.client;
|
|
138
|
+
}
|
|
139
|
+
async exec(fn, action) {
|
|
140
|
+
const client = this.requireClient();
|
|
141
|
+
try {
|
|
142
|
+
return await fn(client);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
throw wrapDatabaseError(err, `Solr ${action} failed`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// ---------------------------------------------------------------------
|
|
149
|
+
// Indexing
|
|
150
|
+
// ---------------------------------------------------------------------
|
|
151
|
+
/**
|
|
152
|
+
* Add (index) one or more documents, then `commit` so they become
|
|
153
|
+
* searchable. Solr buffers adds until a commit, so the two are done
|
|
154
|
+
* together here for the common "index and see it" case; call `add` via
|
|
155
|
+
* `getClient()` directly if you want to batch many adds before a single
|
|
156
|
+
* commit.
|
|
157
|
+
*/
|
|
158
|
+
async add(docs) {
|
|
159
|
+
return this.exec(async (c) => {
|
|
160
|
+
const result = await invoke((cb) => c.add(docs, cb));
|
|
161
|
+
await invoke((cb) => c.commit(cb));
|
|
162
|
+
return result;
|
|
163
|
+
}, 'ADD');
|
|
164
|
+
}
|
|
165
|
+
// ---------------------------------------------------------------------
|
|
166
|
+
// Search
|
|
167
|
+
// ---------------------------------------------------------------------
|
|
168
|
+
/**
|
|
169
|
+
* Run a query. Pass either a raw query object/string (Solr query params,
|
|
170
|
+
* e.g. `'q=title:solr&rows=10'`) or a query built by
|
|
171
|
+
* {@link SolrStore.query}. Returns the driver's raw response object
|
|
172
|
+
* (`{ responseHeader, response: { numFound, docs, ... } }`).
|
|
173
|
+
*/
|
|
174
|
+
async search(query) {
|
|
175
|
+
return this.exec((c) => invoke((cb) => c.search(query, cb)), 'SEARCH');
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Build a query with the driver's fluent builder (`.q(...)`, `.start(...)`,
|
|
179
|
+
* `.rows(...)`, `.sort(...)`, ...), when the driver exposes `client.query()`.
|
|
180
|
+
* Throws if this driver build doesn't provide a builder - construct a raw
|
|
181
|
+
* query object/string and pass it straight to {@link SolrStore.search} in
|
|
182
|
+
* that case.
|
|
183
|
+
*/
|
|
184
|
+
query() {
|
|
185
|
+
const client = this.requireClient();
|
|
186
|
+
if (typeof client.query !== 'function') {
|
|
187
|
+
throw new errors_1.DatabaseError('This solr-client build does not expose query(); pass a raw query to search()');
|
|
188
|
+
}
|
|
189
|
+
return client.query();
|
|
190
|
+
}
|
|
191
|
+
// ---------------------------------------------------------------------
|
|
192
|
+
// Delete
|
|
193
|
+
// ---------------------------------------------------------------------
|
|
194
|
+
/** Delete a single document by its unique-key value, then commit. */
|
|
195
|
+
async deleteByID(id) {
|
|
196
|
+
return this.exec(async (c) => {
|
|
197
|
+
const result = await invoke((cb) => c.deleteByID(id, cb));
|
|
198
|
+
await invoke((cb) => c.commit(cb));
|
|
199
|
+
return result;
|
|
200
|
+
}, 'DELETE BY ID');
|
|
201
|
+
}
|
|
202
|
+
/** Delete every document matching a Solr query (e.g. `'category:obsolete'`), then commit. */
|
|
203
|
+
async deleteByQuery(query) {
|
|
204
|
+
return this.exec(async (c) => {
|
|
205
|
+
const result = await invoke((cb) => c.deleteByQuery(query, cb));
|
|
206
|
+
await invoke((cb) => c.commit(cb));
|
|
207
|
+
return result;
|
|
208
|
+
}, 'DELETE BY QUERY');
|
|
209
|
+
}
|
|
210
|
+
// ---------------------------------------------------------------------
|
|
211
|
+
// Commit / optimize
|
|
212
|
+
// ---------------------------------------------------------------------
|
|
213
|
+
/** Commit buffered writes so they become visible to search. */
|
|
214
|
+
async commit() {
|
|
215
|
+
return this.exec((c) => invoke((cb) => c.commit(cb)), 'COMMIT');
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Optimize the index (merge segments). Optional - not every `solr-client`
|
|
219
|
+
* build exposes `optimize`; throws a `DatabaseError` if this one doesn't.
|
|
220
|
+
*/
|
|
221
|
+
async optimize() {
|
|
222
|
+
return this.exec((c) => {
|
|
223
|
+
if (typeof c.optimize !== 'function') {
|
|
224
|
+
throw new Error('This solr-client build does not expose optimize()');
|
|
225
|
+
}
|
|
226
|
+
return invoke((cb) => c.optimize({}, cb));
|
|
227
|
+
}, 'OPTIMIZE');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
exports.SolrStore = SolrStore;
|
|
231
|
+
exports.default = SolrStore;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Splunk store implementation.
|
|
4
|
+
*
|
|
5
|
+
* Splunk is a log-ingestion and search platform. Data is ingested through
|
|
6
|
+
* the HTTP Event Collector (HEC) and searched with SPL (Search Processing
|
|
7
|
+
* Language) via the REST search API. There is no SQL-shaped query surface,
|
|
8
|
+
* no row-level UPDATE/DELETE, and no identifier escaping, so Splunk does not
|
|
9
|
+
* fit the SQL `Dialect` interface (`src/dialects/dialect.ts`). This class
|
|
10
|
+
* implements the minimal `NoSqlStore` marker interface
|
|
11
|
+
* (`src/nosql/store.ts`) and exposes Splunk's ingest/search API.
|
|
12
|
+
*
|
|
13
|
+
* ## Driver: `splunk-sdk` if present, else HTTP over `fetch`
|
|
14
|
+
*
|
|
15
|
+
* If the optional `splunk-sdk` package is installed it is lazily required at
|
|
16
|
+
* connect time and exposed via `getSdk()`; the ingest/search methods here
|
|
17
|
+
* always speak the REST/HEC HTTP API over the global `fetch` (so `library`
|
|
18
|
+
* is `'fetch'`). For tests (and custom transports) an alternative HTTP
|
|
19
|
+
* client can be injected via `SplunkStoreOptions.client`.
|
|
20
|
+
*
|
|
21
|
+
* ## Endpoints wrapped
|
|
22
|
+
*
|
|
23
|
+
* - `push(meta, events)` -> `POST /services/collector/event` (HEC ingest)
|
|
24
|
+
* - `query(spl, opts?)` -> `POST /services/search/jobs` (SPL search)
|
|
25
|
+
* - `labels()` -> `GET /services/data/sourcetypes` (sourcetypes)
|
|
26
|
+
* - `streams()` -> `GET /services/data/indexes` (indexes)
|
|
27
|
+
*/
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.SplunkStore = exports.DEFAULT_SPLUNK_BASE_URL = void 0;
|
|
30
|
+
exports.createFetchClient = createFetchClient;
|
|
31
|
+
const errors_1 = require("../../errors");
|
|
32
|
+
/** Default Splunk management/REST base URL. */
|
|
33
|
+
exports.DEFAULT_SPLUNK_BASE_URL = 'https://localhost:8088';
|
|
34
|
+
/** Build a `fetch`-backed client for the given base URL. */
|
|
35
|
+
function createFetchClient(baseURL, headers) {
|
|
36
|
+
const buildUrl = (path, params) => {
|
|
37
|
+
const url = new URL(path, baseURL.endsWith('/') ? baseURL : `${baseURL}/`);
|
|
38
|
+
for (const [key, value] of Object.entries(params ?? {})) {
|
|
39
|
+
if (value === undefined)
|
|
40
|
+
continue;
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
for (const item of value)
|
|
43
|
+
url.searchParams.append(key, item);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
url.searchParams.set(key, value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return url.toString();
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
async get(path, params) {
|
|
53
|
+
const res = await fetch(buildUrl(path, params), { method: 'GET', headers });
|
|
54
|
+
if (!res.ok)
|
|
55
|
+
throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
56
|
+
return res.json();
|
|
57
|
+
},
|
|
58
|
+
async post(path, body, contentType) {
|
|
59
|
+
const res = await fetch(buildUrl(path), {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
body,
|
|
62
|
+
headers: {
|
|
63
|
+
...(headers ?? {}),
|
|
64
|
+
...(contentType ? { 'Content-Type': contentType } : {}),
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
if (!res.ok)
|
|
68
|
+
throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
69
|
+
const text = await res.text();
|
|
70
|
+
if (!text)
|
|
71
|
+
return undefined;
|
|
72
|
+
try {
|
|
73
|
+
return JSON.parse(text);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return text;
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* `SplunkStore` wraps Splunk's HTTP Event Collector (ingest) and REST search
|
|
83
|
+
* API (SPL search jobs + index/sourcetype metadata) behind the `NoSqlStore`
|
|
84
|
+
* lifecycle.
|
|
85
|
+
*/
|
|
86
|
+
class SplunkStore {
|
|
87
|
+
constructor(options = {}) {
|
|
88
|
+
this.name = 'splunk';
|
|
89
|
+
this.library = 'fetch';
|
|
90
|
+
this.client = null;
|
|
91
|
+
this.sdk = null;
|
|
92
|
+
this.connected = false;
|
|
93
|
+
this.baseURL = options.baseURL ?? exports.DEFAULT_SPLUNK_BASE_URL;
|
|
94
|
+
this.token = options.token;
|
|
95
|
+
this.injectedClient = options.client;
|
|
96
|
+
this.headers = {
|
|
97
|
+
...(options.headers ?? {}),
|
|
98
|
+
...(options.token ? { Authorization: `Splunk ${options.token}` } : {}),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// ---------------------------------------------------------------------
|
|
102
|
+
// Connection lifecycle
|
|
103
|
+
// ---------------------------------------------------------------------
|
|
104
|
+
async connect() {
|
|
105
|
+
if (this.connected && this.client)
|
|
106
|
+
return;
|
|
107
|
+
this.client = this.injectedClient ?? createFetchClient(this.baseURL, this.headers);
|
|
108
|
+
// Lazily surface the optional official SDK if it happens to be installed.
|
|
109
|
+
if (!this.injectedClient) {
|
|
110
|
+
try {
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
112
|
+
this.sdk = require('splunk-sdk');
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
this.sdk = null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
this.connected = true;
|
|
119
|
+
}
|
|
120
|
+
async disconnect() {
|
|
121
|
+
this.client = null;
|
|
122
|
+
this.sdk = null;
|
|
123
|
+
this.connected = false;
|
|
124
|
+
}
|
|
125
|
+
isConnected() {
|
|
126
|
+
return this.connected && this.client !== null;
|
|
127
|
+
}
|
|
128
|
+
/** Returns the underlying (internal or injected) HTTP client. */
|
|
129
|
+
getClient() {
|
|
130
|
+
return this.requireClient();
|
|
131
|
+
}
|
|
132
|
+
/** Returns the lazily-required `splunk-sdk` module, or `null` if not installed. */
|
|
133
|
+
getSdk() {
|
|
134
|
+
return this.sdk;
|
|
135
|
+
}
|
|
136
|
+
requireClient() {
|
|
137
|
+
if (!this.client) {
|
|
138
|
+
throw new errors_1.ConnectionError('Not connected to Splunk', {
|
|
139
|
+
database: 'splunk',
|
|
140
|
+
host: this.baseURL,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return this.client;
|
|
144
|
+
}
|
|
145
|
+
async doGet(path, params, action) {
|
|
146
|
+
const client = this.requireClient();
|
|
147
|
+
try {
|
|
148
|
+
return await client.get(path, params);
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
152
|
+
throw errors_1.DatabaseError.from(parent, {
|
|
153
|
+
message: `Splunk ${action ?? 'request'} failed: ${parent.message}`,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// ---------------------------------------------------------------------
|
|
158
|
+
// Ingest (HEC)
|
|
159
|
+
// ---------------------------------------------------------------------
|
|
160
|
+
/**
|
|
161
|
+
* Sends a batch of events to the HTTP Event Collector via
|
|
162
|
+
* `POST /services/collector/event`. Each event is wrapped in a HEC
|
|
163
|
+
* envelope carrying the shared `meta` (index/sourcetype/source/host); the
|
|
164
|
+
* envelopes are newline-concatenated as HEC expects.
|
|
165
|
+
*/
|
|
166
|
+
async push(meta, events) {
|
|
167
|
+
const client = this.requireClient();
|
|
168
|
+
const body = events
|
|
169
|
+
.map((event) => JSON.stringify({
|
|
170
|
+
event,
|
|
171
|
+
index: meta.index,
|
|
172
|
+
sourcetype: meta.sourcetype,
|
|
173
|
+
source: meta.source,
|
|
174
|
+
host: meta.host,
|
|
175
|
+
}))
|
|
176
|
+
.join('\n');
|
|
177
|
+
try {
|
|
178
|
+
return await client.post('/services/collector/event', body, 'application/json');
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
182
|
+
throw errors_1.DatabaseError.from(parent, { message: `Splunk push failed: ${parent.message}` });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// ---------------------------------------------------------------------
|
|
186
|
+
// Search (SPL)
|
|
187
|
+
// ---------------------------------------------------------------------
|
|
188
|
+
/**
|
|
189
|
+
* Dispatches an SPL search via `POST /services/search/jobs`. The `search`
|
|
190
|
+
* string is normalized to start with `search ` when it isn't already a
|
|
191
|
+
* generating command. Returns the parsed response (job SID or, for a
|
|
192
|
+
* oneshot search, the results).
|
|
193
|
+
*/
|
|
194
|
+
async query(spl, options = {}) {
|
|
195
|
+
const client = this.requireClient();
|
|
196
|
+
const search = /^\s*(search|\|)/.test(spl) ? spl : `search ${spl}`;
|
|
197
|
+
const params = new URLSearchParams();
|
|
198
|
+
params.set('search', search);
|
|
199
|
+
params.set('output_mode', options.outputMode ?? 'json');
|
|
200
|
+
if (options.earliest !== undefined)
|
|
201
|
+
params.set('earliest_time', options.earliest);
|
|
202
|
+
if (options.latest !== undefined)
|
|
203
|
+
params.set('latest_time', options.latest);
|
|
204
|
+
if (options.oneshot)
|
|
205
|
+
params.set('exec_mode', 'oneshot');
|
|
206
|
+
try {
|
|
207
|
+
return await client.post('/services/search/jobs', params.toString(), 'application/x-www-form-urlencoded');
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
211
|
+
throw errors_1.DatabaseError.from(parent, { message: `Splunk query failed: ${parent.message}` });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// ---------------------------------------------------------------------
|
|
215
|
+
// Metadata
|
|
216
|
+
// ---------------------------------------------------------------------
|
|
217
|
+
/** Lists the configured source types via `GET /services/data/sourcetypes`. */
|
|
218
|
+
async labels() {
|
|
219
|
+
return this.doGet('/services/data/sourcetypes', { output_mode: 'json' }, 'labels');
|
|
220
|
+
}
|
|
221
|
+
/** Lists the configured indexes via `GET /services/data/indexes`. */
|
|
222
|
+
async streams() {
|
|
223
|
+
return this.doGet('/services/data/indexes', { output_mode: 'json' }, 'streams');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
exports.SplunkStore = SplunkStore;
|
|
227
|
+
exports.default = SplunkStore;
|