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,469 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TigerGraph store implementation.
|
|
4
|
+
*
|
|
5
|
+
* TigerGraph is a native graph database (vertices/edges/GSQL), not a SQL
|
|
6
|
+
* engine, so this class does not implement the SQL-shaped `Dialect`
|
|
7
|
+
* interface (see `src/dialects/dialect.ts`). It implements the minimal
|
|
8
|
+
* `NoSqlStore` marker interface (`src/nosql/store.ts`) and exposes
|
|
9
|
+
* TigerGraph's real capabilities directly: installed-query execution,
|
|
10
|
+
* vertex/edge upsert and retrieval, ad-hoc "interpreted" GSQL queries, and
|
|
11
|
+
* schema introspection.
|
|
12
|
+
*
|
|
13
|
+
* ## Why `fetch`, not a driver
|
|
14
|
+
*
|
|
15
|
+
* Unlike every sibling in `src/nosql/` (MongoDB -> `mongodb`, Redis ->
|
|
16
|
+
* `ioredis`, DynamoDB -> `@aws-sdk/*`), there is no official,
|
|
17
|
+
* actively-maintained Node.js/JavaScript SDK for TigerGraph on npm as of
|
|
18
|
+
* this writing. The only packages found (`tigergraph.js`, `tiger-graph`)
|
|
19
|
+
* are small, unmaintained community wrappers last published in 2022, not
|
|
20
|
+
* published or endorsed by TigerGraph Inc, and TigerGraph's own GitHub org
|
|
21
|
+
* ships `pyTigerGraph` (Python) but nothing for Node. TigerGraph *is*
|
|
22
|
+
* fully usable from Node, though: it's a plain HTTP/JSON REST API
|
|
23
|
+
* ("REST++", plus the GSQL server's own HTTP endpoints), so this store
|
|
24
|
+
* talks to it with Node's built-in global `fetch` (Node >=18, this repo's
|
|
25
|
+
* minimum engine) instead of adding a new npm dependency for a thin HTTP
|
|
26
|
+
* wrapper. This is a deliberate choice, not an oversight - see the
|
|
27
|
+
* package's README for the endpoint shapes this was verified against.
|
|
28
|
+
*
|
|
29
|
+
* `library` is reported as `'fetch'` (there is no driver name to report).
|
|
30
|
+
*/
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.TigerGraphStore = void 0;
|
|
33
|
+
const errors_1 = require("../../errors");
|
|
34
|
+
// ---------------------------------------------------------------------
|
|
35
|
+
// Error helpers
|
|
36
|
+
// ---------------------------------------------------------------------
|
|
37
|
+
function wrapConnectionError(err, host) {
|
|
38
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
39
|
+
const message = /fetch failed|econnrefused/i.test(parent.message)
|
|
40
|
+
? 'Connection refused'
|
|
41
|
+
: `Unable to connect to TigerGraph: ${parent.message}`;
|
|
42
|
+
return new errors_1.ConnectionError(message, { parent, database: 'tigergraph', host });
|
|
43
|
+
}
|
|
44
|
+
function wrapDatabaseError(err, message) {
|
|
45
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
46
|
+
const combined = message ? `${message}: ${parent.message}` : parent.message;
|
|
47
|
+
return errors_1.DatabaseError.from(parent, { message: combined });
|
|
48
|
+
}
|
|
49
|
+
/** Convert plain attribute values into TigerGraph's `{"value": x}` wire format, passing already-wrapped values through unchanged. */
|
|
50
|
+
function toWireAttributes(attrs = {}) {
|
|
51
|
+
const wire = {};
|
|
52
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
53
|
+
if (value !== null && typeof value === 'object' && 'value' in value) {
|
|
54
|
+
wire[key] = value;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
wire[key] = { value };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return wire;
|
|
61
|
+
}
|
|
62
|
+
// ---------------------------------------------------------------------
|
|
63
|
+
// Store
|
|
64
|
+
// ---------------------------------------------------------------------
|
|
65
|
+
/**
|
|
66
|
+
* `TigerGraphStore` wraps TigerGraph's REST++ and GSQL-server HTTP APIs
|
|
67
|
+
* behind a typed, promise-based interface: token-based auth, installed
|
|
68
|
+
* (pre-compiled GSQL) query execution, vertex/edge upsert and retrieval,
|
|
69
|
+
* ad-hoc interpreted queries, and vertex/edge-type schema introspection.
|
|
70
|
+
*/
|
|
71
|
+
class TigerGraphStore {
|
|
72
|
+
constructor(options) {
|
|
73
|
+
this.name = 'tigergraph';
|
|
74
|
+
this.library = 'fetch';
|
|
75
|
+
this.token = null;
|
|
76
|
+
this.connected = false;
|
|
77
|
+
if (!options.host) {
|
|
78
|
+
throw new errors_1.ConnectionError('TigerGraphStore requires a "host"', { database: 'tigergraph' });
|
|
79
|
+
}
|
|
80
|
+
if (!options.graph) {
|
|
81
|
+
throw new errors_1.ConnectionError('TigerGraphStore requires a "graph"', { database: 'tigergraph' });
|
|
82
|
+
}
|
|
83
|
+
this.options = options;
|
|
84
|
+
this.restBaseUrl = `${options.host}:${options.restPort ?? 9000}`;
|
|
85
|
+
this.gsqlBaseUrl = `${options.host}:${options.gsqlPort ?? 14240}`;
|
|
86
|
+
this.restPathPrefix = options.restPathPrefix ?? '';
|
|
87
|
+
}
|
|
88
|
+
// ---------------------------------------------------------------------
|
|
89
|
+
// Connection lifecycle
|
|
90
|
+
// ---------------------------------------------------------------------
|
|
91
|
+
async connect() {
|
|
92
|
+
if (this.connected) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
if (this.options.token) {
|
|
97
|
+
this.token = this.options.token;
|
|
98
|
+
}
|
|
99
|
+
else if (this.options.secret) {
|
|
100
|
+
this.token = await this.requestToken(this.options.secret, this.options.tokenLifetime);
|
|
101
|
+
}
|
|
102
|
+
else if (!this.options.username || !this.options.password) {
|
|
103
|
+
throw new Error('no "token", "secret", or "username"/"password" provided - TigerGraph requires one of these to authenticate');
|
|
104
|
+
}
|
|
105
|
+
this.connected = true;
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
this.token = null;
|
|
109
|
+
this.connected = false;
|
|
110
|
+
throw wrapConnectionError(err, this.options.host);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async disconnect() {
|
|
114
|
+
this.token = null;
|
|
115
|
+
this.connected = false;
|
|
116
|
+
}
|
|
117
|
+
isConnected() {
|
|
118
|
+
return this.connected;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Returns a small facade over the underlying HTTP transport for
|
|
122
|
+
* operations not wrapped here - there is no native driver client to
|
|
123
|
+
* expose, since this store talks to TigerGraph over plain `fetch`.
|
|
124
|
+
*/
|
|
125
|
+
getClient() {
|
|
126
|
+
this.requireConnected();
|
|
127
|
+
return {
|
|
128
|
+
token: this.token,
|
|
129
|
+
restBaseUrl: this.restBaseUrl,
|
|
130
|
+
gsqlBaseUrl: this.gsqlBaseUrl,
|
|
131
|
+
restRequest: (path, init) => this.restRequest(path, init),
|
|
132
|
+
gsqlRequest: (path, init) => this.gsqlRequest(path, init),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
requireConnected() {
|
|
136
|
+
if (!this.connected) {
|
|
137
|
+
throw new errors_1.ConnectionError('Not connected to TigerGraph', { database: 'tigergraph' });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
getFetch() {
|
|
141
|
+
// Resolved per-call (not cached at construction time) so tests can
|
|
142
|
+
// swap `global.fetch` per-test (`jest.spyOn(global, 'fetch')` or
|
|
143
|
+
// reassigning `global.fetch`) and have it take effect immediately.
|
|
144
|
+
return this.options.fetch ?? globalThis.fetch;
|
|
145
|
+
}
|
|
146
|
+
// ---------------------------------------------------------------------
|
|
147
|
+
// Auth
|
|
148
|
+
// ---------------------------------------------------------------------
|
|
149
|
+
/**
|
|
150
|
+
* `POST /requesttoken` - exchange a GSQL secret for a REST++ bearer
|
|
151
|
+
* token. Confirmed request/response shape (pre-4.1 REST++; TigerGraph
|
|
152
|
+
* 4.1+ additionally offers `POST /gsql/v1/tokens` on the unified GSQL
|
|
153
|
+
* port, whose exact request/response shape was not independently
|
|
154
|
+
* confirmed while building this store - see README "Known
|
|
155
|
+
* limitations").
|
|
156
|
+
*/
|
|
157
|
+
async requestToken(secret, lifetime) {
|
|
158
|
+
const body = { secret };
|
|
159
|
+
if (lifetime !== undefined) {
|
|
160
|
+
body.lifetime = String(lifetime);
|
|
161
|
+
}
|
|
162
|
+
const fetchImpl = this.getFetch();
|
|
163
|
+
let response;
|
|
164
|
+
try {
|
|
165
|
+
response = await fetchImpl(`${this.restBaseUrl}/requesttoken`, {
|
|
166
|
+
method: 'POST',
|
|
167
|
+
headers: { 'Content-Type': 'application/json' },
|
|
168
|
+
body: JSON.stringify(body),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
throw wrapConnectionError(err, this.options.host);
|
|
173
|
+
}
|
|
174
|
+
const json = await this.parseEnvelope(response, 'requesttoken');
|
|
175
|
+
if (!json.token) {
|
|
176
|
+
throw new errors_1.DatabaseError(json.message || 'TigerGraph did not return a token', {
|
|
177
|
+
original: json.message,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return json.token;
|
|
181
|
+
}
|
|
182
|
+
// ---------------------------------------------------------------------
|
|
183
|
+
// Low-level HTTP helpers
|
|
184
|
+
// ---------------------------------------------------------------------
|
|
185
|
+
async parseEnvelope(response, action) {
|
|
186
|
+
let json;
|
|
187
|
+
try {
|
|
188
|
+
json = (await response.json());
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
// Non-JSON body (e.g. a gateway/proxy error page). Whether TigerGraph
|
|
192
|
+
// itself ever returns a non-2xx status was not confirmed during
|
|
193
|
+
// research (see README) - surface both the HTTP status and body-parse
|
|
194
|
+
// failure so callers can tell the two failure modes apart.
|
|
195
|
+
throw wrapDatabaseError(err, `TigerGraph ${action} failed (HTTP ${response.status} ${response.statusText}, non-JSON response)`);
|
|
196
|
+
}
|
|
197
|
+
// TigerGraph's documented error signal is `error: true` in the JSON
|
|
198
|
+
// envelope; treat a non-2xx HTTP status as a failure too, since it was
|
|
199
|
+
// not confirmed whether every deployment/version always replies 200
|
|
200
|
+
// with `error: true` (see README "Known limitations").
|
|
201
|
+
if (!response.ok || json.error) {
|
|
202
|
+
const code = json.code ? ` (${json.code})` : '';
|
|
203
|
+
throw new errors_1.DatabaseError(`TigerGraph ${action} failed${code}: ${json.message ?? response.statusText}`, {
|
|
204
|
+
original: json.message,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return json;
|
|
208
|
+
}
|
|
209
|
+
authHeaders() {
|
|
210
|
+
if (this.token) {
|
|
211
|
+
return { Authorization: `Bearer ${this.token}` };
|
|
212
|
+
}
|
|
213
|
+
return {};
|
|
214
|
+
}
|
|
215
|
+
basicAuthHeaders() {
|
|
216
|
+
const { username, password } = this.options;
|
|
217
|
+
if (username && password) {
|
|
218
|
+
const encoded = Buffer.from(`${username}:${password}`).toString('base64');
|
|
219
|
+
return { Authorization: `Basic ${encoded}` };
|
|
220
|
+
}
|
|
221
|
+
// Fall back to the REST++ bearer token if no Basic-auth credentials
|
|
222
|
+
// were configured. TigerGraph 4.1+ unifies REST++/GSQL auth behind
|
|
223
|
+
// the same token in some configurations, but this fallback was not
|
|
224
|
+
// independently confirmed - prefer configuring `username`/`password`
|
|
225
|
+
// for GSQL-server calls when possible (see README).
|
|
226
|
+
return this.authHeaders();
|
|
227
|
+
}
|
|
228
|
+
/** Issue a request against the REST++ API (vertex/edge/query endpoints), with bearer-token auth. */
|
|
229
|
+
async restRequest(path, init = {}, action = path) {
|
|
230
|
+
this.requireConnected();
|
|
231
|
+
const fetchImpl = this.getFetch();
|
|
232
|
+
let response;
|
|
233
|
+
try {
|
|
234
|
+
response = await fetchImpl(`${this.restBaseUrl}${this.restPathPrefix}${path}`, {
|
|
235
|
+
...init,
|
|
236
|
+
headers: { ...this.authHeaders(), ...init.headers },
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
throw wrapConnectionError(err, this.options.host);
|
|
241
|
+
}
|
|
242
|
+
return this.parseEnvelope(response, action);
|
|
243
|
+
}
|
|
244
|
+
/** Issue a request against the GSQL server (interpreted queries, schema), with Basic auth. */
|
|
245
|
+
async gsqlRequest(path, init = {}, action = path) {
|
|
246
|
+
this.requireConnected();
|
|
247
|
+
const fetchImpl = this.getFetch();
|
|
248
|
+
let response;
|
|
249
|
+
try {
|
|
250
|
+
response = await fetchImpl(`${this.gsqlBaseUrl}${path}`, {
|
|
251
|
+
...init,
|
|
252
|
+
headers: { ...this.basicAuthHeaders(), ...init.headers },
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
throw wrapConnectionError(err, this.options.host);
|
|
257
|
+
}
|
|
258
|
+
return this.parseEnvelope(response, action);
|
|
259
|
+
}
|
|
260
|
+
// ---------------------------------------------------------------------
|
|
261
|
+
// Installed (pre-compiled GSQL) queries
|
|
262
|
+
// ---------------------------------------------------------------------
|
|
263
|
+
/**
|
|
264
|
+
* Run an installed GSQL query: `GET /query/{graph}/{queryName}?param=value`
|
|
265
|
+
* (simple/scalar params) or `POST /query/{graph}/{queryName}` with a JSON
|
|
266
|
+
* body (complex params, e.g. sets/vertex lists). Confirmed endpoint
|
|
267
|
+
* shape and response envelope (`{version, error, message, results}`).
|
|
268
|
+
*/
|
|
269
|
+
async runInstalledQuery(queryName, params = {}, options = {}) {
|
|
270
|
+
const graph = options.graph ?? this.options.graph;
|
|
271
|
+
const method = options.method ?? 'GET';
|
|
272
|
+
const path = `/query/${encodeURIComponent(graph)}/${encodeURIComponent(queryName)}`;
|
|
273
|
+
if (method === 'GET') {
|
|
274
|
+
const qs = new URLSearchParams(params).toString();
|
|
275
|
+
const envelope = await this.restRequest(qs ? `${path}?${qs}` : path, { method: 'GET' }, `installed query "${queryName}"`);
|
|
276
|
+
return envelope.results;
|
|
277
|
+
}
|
|
278
|
+
const envelope = await this.restRequest(path, {
|
|
279
|
+
method: 'POST',
|
|
280
|
+
headers: { 'Content-Type': 'application/json' },
|
|
281
|
+
body: JSON.stringify(params),
|
|
282
|
+
}, `installed query "${queryName}"`);
|
|
283
|
+
return envelope.results;
|
|
284
|
+
}
|
|
285
|
+
// ---------------------------------------------------------------------
|
|
286
|
+
// Vertex/edge upsert
|
|
287
|
+
// ---------------------------------------------------------------------
|
|
288
|
+
/**
|
|
289
|
+
* `POST /graph/{graph}` - raw upsert matching TigerGraph's documented
|
|
290
|
+
* wire format exactly (attribute values pre-wrapped as `{value: x}`).
|
|
291
|
+
* Prefer `upsertVertex`/`upsertVertices`/`upsertEdge`/`upsertEdges` for
|
|
292
|
+
* the common case of plain attribute values.
|
|
293
|
+
*/
|
|
294
|
+
async upsert(payload, graph = this.options.graph) {
|
|
295
|
+
const envelope = await this.restRequest(`/graph/${encodeURIComponent(graph)}`, {
|
|
296
|
+
method: 'POST',
|
|
297
|
+
headers: { 'Content-Type': 'application/json' },
|
|
298
|
+
body: JSON.stringify(payload),
|
|
299
|
+
}, 'upsert');
|
|
300
|
+
return envelope.results;
|
|
301
|
+
}
|
|
302
|
+
/** Upsert a single vertex with plain (unwrapped) attribute values. */
|
|
303
|
+
async upsertVertex(vertexType, id, attributes = {}, graph) {
|
|
304
|
+
return this.upsert({ vertices: { [vertexType]: { [id]: toWireAttributes(attributes) } } }, graph ?? this.options.graph);
|
|
305
|
+
}
|
|
306
|
+
/** Upsert multiple vertices of the same type in one round trip. */
|
|
307
|
+
async upsertVertices(vertexType, vertices, graph) {
|
|
308
|
+
const wired = {};
|
|
309
|
+
for (const [id, attrs] of Object.entries(vertices)) {
|
|
310
|
+
wired[id] = toWireAttributes(attrs);
|
|
311
|
+
}
|
|
312
|
+
return this.upsert({ vertices: { [vertexType]: wired } }, graph ?? this.options.graph);
|
|
313
|
+
}
|
|
314
|
+
/** Upsert a single edge with plain (unwrapped) attribute values. */
|
|
315
|
+
async upsertEdge(fromType, fromId, edgeType, toType, toId, attributes = {}, graph) {
|
|
316
|
+
return this.upsertEdges([{ fromType, fromId, edgeType, toType, toId, attributes }], graph);
|
|
317
|
+
}
|
|
318
|
+
/** Upsert multiple edges (possibly spanning different types/vertices) in one round trip. */
|
|
319
|
+
async upsertEdges(edges, graph) {
|
|
320
|
+
var _a, _b, _c;
|
|
321
|
+
const edgeMap = {};
|
|
322
|
+
for (const { fromType, fromId, edgeType, toType, toId, attributes } of edges) {
|
|
323
|
+
edgeMap[fromType] ?? (edgeMap[fromType] = {});
|
|
324
|
+
(_a = edgeMap[fromType])[fromId] ?? (_a[fromId] = {});
|
|
325
|
+
(_b = edgeMap[fromType][fromId])[edgeType] ?? (_b[edgeType] = {});
|
|
326
|
+
(_c = edgeMap[fromType][fromId][edgeType])[toType] ?? (_c[toType] = {});
|
|
327
|
+
edgeMap[fromType][fromId][edgeType][toType][toId] = toWireAttributes(attributes);
|
|
328
|
+
}
|
|
329
|
+
return this.upsert({ edges: edgeMap }, graph ?? this.options.graph);
|
|
330
|
+
}
|
|
331
|
+
// ---------------------------------------------------------------------
|
|
332
|
+
// Vertex/edge retrieval
|
|
333
|
+
// ---------------------------------------------------------------------
|
|
334
|
+
/**
|
|
335
|
+
* `GET /graph/{graph}/vertices/{type}/{id}` - fetch a single vertex by
|
|
336
|
+
* ID. Confirmed endpoint/response shape. Returns `null` if the vertex
|
|
337
|
+
* does not exist (TigerGraph reports this as `error: true` with a
|
|
338
|
+
* "does not exist" message rather than an empty result set).
|
|
339
|
+
*/
|
|
340
|
+
async getVertex(vertexType, id, graph) {
|
|
341
|
+
const g = graph ?? this.options.graph;
|
|
342
|
+
const path = `/graph/${encodeURIComponent(g)}/vertices/${encodeURIComponent(vertexType)}/${encodeURIComponent(id)}`;
|
|
343
|
+
try {
|
|
344
|
+
const envelope = await this.restRequest(path, { method: 'GET' }, `get vertex "${vertexType}/${id}"`);
|
|
345
|
+
const results = envelope.results ?? [];
|
|
346
|
+
return results[0] ?? null;
|
|
347
|
+
}
|
|
348
|
+
catch (err) {
|
|
349
|
+
if (err instanceof errors_1.DatabaseError && /does not exist/i.test(err.message)) {
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
throw err;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* `GET /graph/{graph}/vertices/{type}` - list vertices of a type
|
|
357
|
+
* (omitting the ID segment of the single-vertex endpoint above).
|
|
358
|
+
* TigerGraph's built-in-endpoints documentation confirms the
|
|
359
|
+
* single-vertex form; the list form (same resource without an ID, plus
|
|
360
|
+
* an optional `limit` query parameter) follows the standard REST++
|
|
361
|
+
* collection-resource pattern but its full parameter set (`filter`,
|
|
362
|
+
* `select`, `sort`, etc.) was not independently re-verified while
|
|
363
|
+
* building this store - only `limit` is wired up here (see README).
|
|
364
|
+
*/
|
|
365
|
+
async listVerticesByType(vertexType, options = {}) {
|
|
366
|
+
const graph = options.graph ?? this.options.graph;
|
|
367
|
+
let path = `/graph/${encodeURIComponent(graph)}/vertices/${encodeURIComponent(vertexType)}`;
|
|
368
|
+
if (options.limit !== undefined) {
|
|
369
|
+
path += `?limit=${encodeURIComponent(options.limit)}`;
|
|
370
|
+
}
|
|
371
|
+
const envelope = await this.restRequest(path, { method: 'GET' }, `list vertices "${vertexType}"`);
|
|
372
|
+
return envelope.results ?? [];
|
|
373
|
+
}
|
|
374
|
+
/** `DELETE /graph/{graph}/vertices/{type}/{id}` - delete a single vertex (and its incident edges). Returns the number of vertices deleted (`0` or `1`). */
|
|
375
|
+
async deleteVertex(vertexType, id, graph) {
|
|
376
|
+
const g = graph ?? this.options.graph;
|
|
377
|
+
const path = `/graph/${encodeURIComponent(g)}/vertices/${encodeURIComponent(vertexType)}/${encodeURIComponent(id)}`;
|
|
378
|
+
const envelope = await this.restRequest(path, { method: 'DELETE' }, `delete vertex "${vertexType}/${id}"`);
|
|
379
|
+
return envelope.results?.deleted_vertices ?? 0;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* `GET /graph/{graph}/edges/{fromType}/{fromId}/{edgeType}/{toType}/{toId}`
|
|
383
|
+
* - fetch the edge(s) between two specific vertices. Confirmed
|
|
384
|
+
* endpoint/response shape. Returns an empty array if no such edge
|
|
385
|
+
* exists.
|
|
386
|
+
*/
|
|
387
|
+
async getEdge(fromType, fromId, edgeType, toType, toId, graph) {
|
|
388
|
+
const g = graph ?? this.options.graph;
|
|
389
|
+
const path = `/graph/${encodeURIComponent(g)}/edges/${encodeURIComponent(fromType)}/${encodeURIComponent(fromId)}/${encodeURIComponent(edgeType)}/${encodeURIComponent(toType)}/${encodeURIComponent(toId)}`;
|
|
390
|
+
try {
|
|
391
|
+
const envelope = await this.restRequest(path, { method: 'GET' }, `get edge "${fromType}/${fromId}->${toType}/${toId}"`);
|
|
392
|
+
return envelope.results ?? [];
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
if (err instanceof errors_1.DatabaseError && /does not exist/i.test(err.message)) {
|
|
396
|
+
return [];
|
|
397
|
+
}
|
|
398
|
+
throw err;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* `GET /graph/{graph}/edges/{fromType}/{fromId}[/{edgeType}]` - list all
|
|
403
|
+
* edges (of the given type, if provided) originating from a vertex.
|
|
404
|
+
* Same confidence note as `getEdge()`'s partial-path collection form.
|
|
405
|
+
*/
|
|
406
|
+
async listEdgesFrom(fromType, fromId, edgeType, graph) {
|
|
407
|
+
const g = graph ?? this.options.graph;
|
|
408
|
+
let path = `/graph/${encodeURIComponent(g)}/edges/${encodeURIComponent(fromType)}/${encodeURIComponent(fromId)}`;
|
|
409
|
+
if (edgeType) {
|
|
410
|
+
path += `/${encodeURIComponent(edgeType)}`;
|
|
411
|
+
}
|
|
412
|
+
const envelope = await this.restRequest(path, { method: 'GET' }, `list edges from "${fromType}/${fromId}"`);
|
|
413
|
+
return envelope.results ?? [];
|
|
414
|
+
}
|
|
415
|
+
/** `DELETE /graph/{graph}/edges/{fromType}/{fromId}/{edgeType}/{toType}/{toId}` - delete a single edge. Returns the number of edges deleted. */
|
|
416
|
+
async deleteEdge(fromType, fromId, edgeType, toType, toId, graph) {
|
|
417
|
+
const g = graph ?? this.options.graph;
|
|
418
|
+
const path = `/graph/${encodeURIComponent(g)}/edges/${encodeURIComponent(fromType)}/${encodeURIComponent(fromId)}/${encodeURIComponent(edgeType)}/${encodeURIComponent(toType)}/${encodeURIComponent(toId)}`;
|
|
419
|
+
const envelope = await this.restRequest(path, { method: 'DELETE' }, `delete edge "${fromType}/${fromId}->${toType}/${toId}"`);
|
|
420
|
+
return envelope.results?.deleted_edges ?? 0;
|
|
421
|
+
}
|
|
422
|
+
// ---------------------------------------------------------------------
|
|
423
|
+
// Ad-hoc "interpreted" GSQL queries
|
|
424
|
+
// ---------------------------------------------------------------------
|
|
425
|
+
/**
|
|
426
|
+
* `POST /gsql/v1/queries/interpret` - compile and run a GSQL query
|
|
427
|
+
* string on the fly, without installing it first. Confirmed endpoint,
|
|
428
|
+
* auth (HTTP Basic, not the REST++ bearer token), and request shape:
|
|
429
|
+
* the request body is the raw GSQL text (`Content-Type: text/plain`),
|
|
430
|
+
* and GSQL query parameters are passed as URL query-string parameters.
|
|
431
|
+
*
|
|
432
|
+
* The older path `/gsqlserver/interpreted_query` (used by some
|
|
433
|
+
* pre-4.x deployments) is documented only in community sources, not in
|
|
434
|
+
* TigerGraph's current official docs, and its exact request shape was
|
|
435
|
+
* not independently confirmed - this store intentionally does not
|
|
436
|
+
* implement that alias rather than guess at its shape. If you're on a
|
|
437
|
+
* version that needs it, issue the request yourself via
|
|
438
|
+
* `getClient().restRequest()`/`gsqlRequest()` (or plain `fetch`) once
|
|
439
|
+
* you've confirmed the shape against your deployment.
|
|
440
|
+
*/
|
|
441
|
+
async runInterpretedQuery(gsql, options = {}) {
|
|
442
|
+
const params = options.params ?? {};
|
|
443
|
+
const qs = new URLSearchParams(params).toString();
|
|
444
|
+
const path = qs ? `/gsql/v1/queries/interpret?${qs}` : '/gsql/v1/queries/interpret';
|
|
445
|
+
const envelope = await this.gsqlRequest(path, {
|
|
446
|
+
method: 'POST',
|
|
447
|
+
headers: { 'Content-Type': 'text/plain' },
|
|
448
|
+
body: gsql,
|
|
449
|
+
}, 'interpreted query');
|
|
450
|
+
return envelope.results;
|
|
451
|
+
}
|
|
452
|
+
// ---------------------------------------------------------------------
|
|
453
|
+
// Schema introspection
|
|
454
|
+
// ---------------------------------------------------------------------
|
|
455
|
+
/** `GET /gsql/v1/schema/vertices?graph={graph}` - list the graph's vertex types. Confirmed endpoint/response shape. */
|
|
456
|
+
async listVertexTypes(graph) {
|
|
457
|
+
const g = graph ?? this.options.graph;
|
|
458
|
+
const envelope = await this.gsqlRequest(`/gsql/v1/schema/vertices?graph=${encodeURIComponent(g)}`, { method: 'GET' }, 'list vertex types');
|
|
459
|
+
return envelope.results ?? [];
|
|
460
|
+
}
|
|
461
|
+
/** `GET /gsql/v1/schema/edges?graph={graph}` - list the graph's edge types. Confirmed endpoint/response shape. */
|
|
462
|
+
async listEdgeTypes(graph) {
|
|
463
|
+
const g = graph ?? this.options.graph;
|
|
464
|
+
const envelope = await this.gsqlRequest(`/gsql/v1/schema/edges?graph=${encodeURIComponent(g)}`, { method: 'GET' }, 'list edge types');
|
|
465
|
+
return envelope.results ?? [];
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
exports.TigerGraphStore = TigerGraphStore;
|
|
469
|
+
exports.default = TigerGraphStore;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Typesense store implementation.
|
|
4
|
+
*
|
|
5
|
+
* Typesense is a typo-tolerant search engine built around an inverted index
|
|
6
|
+
* with faceting, filtering and ranking rather than SQL: no `JOIN`, no
|
|
7
|
+
* transaction log, documents grouped into strongly-typed collections (each
|
|
8
|
+
* described by a schema of fields). It therefore does not fit the SQL-shaped
|
|
9
|
+
* `Dialect` interface used by the SQL dialects in this repo. Instead it
|
|
10
|
+
* implements the minimal `NoSqlStore` marker interface (`src/nosql/store.ts`)
|
|
11
|
+
* and exposes Typesense's real capabilities grouped by concern: connection
|
|
12
|
+
* lifecycle, collection management and document CRUD/search.
|
|
13
|
+
*
|
|
14
|
+
* Uses the official `typesense` JS driver. The driver is loaded lazily via
|
|
15
|
+
* `require` so importing this module does not force the dependency to be
|
|
16
|
+
* resolved unless a Typesense store is actually constructed. For unit-testing
|
|
17
|
+
* without a live server, the constructor also accepts an already-built
|
|
18
|
+
* `client`, which `connect()` will use as-is instead of instantiating a new
|
|
19
|
+
* one.
|
|
20
|
+
*
|
|
21
|
+
* Operations are reached through the driver's fluent, resource-scoped API:
|
|
22
|
+
* `client.collections()` / `client.collections(name)` for collections and
|
|
23
|
+
* `client.collections(name).documents()` / `.documents(id)` for documents.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.TypesenseStore = void 0;
|
|
27
|
+
const errors_1 = require("../../errors");
|
|
28
|
+
function wrapConnectionError(err, nodes) {
|
|
29
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
30
|
+
const message = /econnrefused/i.test(parent.message)
|
|
31
|
+
? 'Connection refused'
|
|
32
|
+
: `Unable to connect to Typesense: ${parent.message}`;
|
|
33
|
+
const first = Array.isArray(nodes) ? nodes[0] : undefined;
|
|
34
|
+
return new errors_1.ConnectionError(message, {
|
|
35
|
+
parent,
|
|
36
|
+
database: 'typesense',
|
|
37
|
+
host: first?.host,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function wrapDatabaseError(err, message) {
|
|
41
|
+
const parent = err instanceof Error ? err : new Error(String(err));
|
|
42
|
+
const statusCode = err?.httpStatus;
|
|
43
|
+
const detail = statusCode ? `${parent.message} (HTTP ${statusCode})` : parent.message;
|
|
44
|
+
const combined = message ? `${message}: ${detail}` : detail;
|
|
45
|
+
return errors_1.DatabaseError.from(parent, { message: combined });
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* `TypesenseStore` wraps the official `typesense` client and exposes
|
|
49
|
+
* collection management, document CRUD, and search as a typed, promise-based
|
|
50
|
+
* API.
|
|
51
|
+
*/
|
|
52
|
+
class TypesenseStore {
|
|
53
|
+
constructor(options) {
|
|
54
|
+
this.name = 'typesense';
|
|
55
|
+
this.library = 'typesense';
|
|
56
|
+
this.client = null;
|
|
57
|
+
this.connected = false;
|
|
58
|
+
this.options = options;
|
|
59
|
+
}
|
|
60
|
+
// ---------------------------------------------------------------------
|
|
61
|
+
// Connection lifecycle
|
|
62
|
+
// ---------------------------------------------------------------------
|
|
63
|
+
async connect() {
|
|
64
|
+
if (this.connected && this.client) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
let client = this.options.client;
|
|
68
|
+
if (!client) {
|
|
69
|
+
// Lazy-load the driver so importing this module doesn't require the
|
|
70
|
+
// dependency to be present unless a store is actually constructed.
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
72
|
+
const Typesense = require('typesense');
|
|
73
|
+
client = new Typesense.Client({ nodes: this.options.nodes, apiKey: this.options.apiKey });
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
// `health.retrieve()` surfaces connection failures (bad host, refused
|
|
77
|
+
// connection) immediately instead of on the caller's first real
|
|
78
|
+
// operation.
|
|
79
|
+
await client.health.retrieve();
|
|
80
|
+
this.client = client;
|
|
81
|
+
this.connected = true;
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
this.client = null;
|
|
85
|
+
this.connected = false;
|
|
86
|
+
throw wrapConnectionError(err, this.options.nodes);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async disconnect() {
|
|
90
|
+
// The Typesense client is stateless HTTP; there's no socket to close.
|
|
91
|
+
this.client = null;
|
|
92
|
+
this.connected = false;
|
|
93
|
+
}
|
|
94
|
+
isConnected() {
|
|
95
|
+
return this.connected && this.client !== null;
|
|
96
|
+
}
|
|
97
|
+
/** Returns the underlying `typesense` client for anything not wrapped here. */
|
|
98
|
+
getClient() {
|
|
99
|
+
return this.requireClient();
|
|
100
|
+
}
|
|
101
|
+
requireClient() {
|
|
102
|
+
if (!this.client) {
|
|
103
|
+
throw new errors_1.ConnectionError('Not connected to Typesense', { database: 'typesense' });
|
|
104
|
+
}
|
|
105
|
+
return this.client;
|
|
106
|
+
}
|
|
107
|
+
async exec(fn, action) {
|
|
108
|
+
const client = this.requireClient();
|
|
109
|
+
try {
|
|
110
|
+
return await fn(client);
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
throw wrapDatabaseError(err, `Typesense ${action} failed`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// ---------------------------------------------------------------------
|
|
117
|
+
// Collection management
|
|
118
|
+
// ---------------------------------------------------------------------
|
|
119
|
+
/** Create a collection from a schema (`{ name, fields, default_sorting_field? }`). */
|
|
120
|
+
async createCollection(schema) {
|
|
121
|
+
return this.exec((c) => c.collections().create(schema), 'CREATE COLLECTION');
|
|
122
|
+
}
|
|
123
|
+
/** Delete a collection by name. */
|
|
124
|
+
async deleteCollection(name) {
|
|
125
|
+
return this.exec((c) => c.collections(name).delete(), 'DELETE COLLECTION');
|
|
126
|
+
}
|
|
127
|
+
// ---------------------------------------------------------------------
|
|
128
|
+
// Document CRUD + search
|
|
129
|
+
// ---------------------------------------------------------------------
|
|
130
|
+
/** Index (create) a single document in a collection. */
|
|
131
|
+
async indexDocument(collection, document) {
|
|
132
|
+
return this.exec((c) => c.collections(collection).documents().create(document), 'INDEX DOCUMENT');
|
|
133
|
+
}
|
|
134
|
+
/** Run a search against a collection's documents. */
|
|
135
|
+
async search(collection, params) {
|
|
136
|
+
return this.exec((c) => c.collections(collection).documents().search(params), 'SEARCH');
|
|
137
|
+
}
|
|
138
|
+
/** Retrieve a single document by its `id`. */
|
|
139
|
+
async getDocument(collection, id) {
|
|
140
|
+
return this.exec((c) => c.collections(collection).documents(id).retrieve(), 'GET DOCUMENT');
|
|
141
|
+
}
|
|
142
|
+
/** Delete a single document by its `id`. */
|
|
143
|
+
async deleteDocument(collection, id) {
|
|
144
|
+
return this.exec((c) => c.collections(collection).documents(id).delete(), 'DELETE DOCUMENT');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.TypesenseStore = TypesenseStore;
|
|
148
|
+
exports.default = TypesenseStore;
|