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,937 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DynamoDbStore = exports.DynamoDbError = exports.DynamoStreamViewType = exports.DynamoReturnValues = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Amazon DynamoDB store.
|
|
6
|
+
*
|
|
7
|
+
* DynamoDB is a partition-key-based key-value/document store, not a SQL
|
|
8
|
+
* database — it has no arbitrary WHERE-clause queries, no joins, and no
|
|
9
|
+
* ad hoc filtering without first reading data (`FilterExpression` runs
|
|
10
|
+
* *after* items are read/consumed against RCU, it does not reduce the
|
|
11
|
+
* amount of data scanned). Access patterns must be designed up front
|
|
12
|
+
* around the table's partition key (and optional sort key), plus any
|
|
13
|
+
* Global/Local Secondary Indexes. Because none of that fits the SQL-shaped
|
|
14
|
+
* `Dialect` interface (`src/dialects/dialect.ts`), this store instead
|
|
15
|
+
* implements the minimal `NoSqlStore` marker interface
|
|
16
|
+
* (`src/nosql/store.ts`) and exposes DynamoDB's own item-level operations
|
|
17
|
+
* directly.
|
|
18
|
+
*
|
|
19
|
+
* Built on AWS SDK v3: `DynamoDBClient` (`@aws-sdk/client-dynamodb`) is the
|
|
20
|
+
* low-level client that speaks the DynamoDB wire protocol in terms of
|
|
21
|
+
* `AttributeValue` maps (`{ S: 'foo' }`, `{ N: '1' }`, ...). This store
|
|
22
|
+
* wraps it in a `DynamoDBDocumentClient` (`@aws-sdk/lib-dynamodb`), which
|
|
23
|
+
* marshals/unmarshals plain JavaScript objects to/from that wire format
|
|
24
|
+
* automatically, so callers of this class work with ordinary objects.
|
|
25
|
+
*/
|
|
26
|
+
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
27
|
+
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
28
|
+
const errors_1 = require("../../errors");
|
|
29
|
+
/**
|
|
30
|
+
* Companion enum for DynamoDB's `ReturnValues` parameter (PutItem, UpdateItem,
|
|
31
|
+
* DeleteItem). Provided for callers who prefer enum members over raw string
|
|
32
|
+
* literals; the underlying option fields still accept the original
|
|
33
|
+
* `'NONE' | 'ALL_OLD' | 'ALL_NEW' | 'UPDATED_OLD' | 'UPDATED_NEW'` string
|
|
34
|
+
* literals too; this is purely additive and does not narrow what's accepted.
|
|
35
|
+
*/
|
|
36
|
+
var DynamoReturnValues;
|
|
37
|
+
(function (DynamoReturnValues) {
|
|
38
|
+
DynamoReturnValues["None"] = "NONE";
|
|
39
|
+
DynamoReturnValues["AllOld"] = "ALL_OLD";
|
|
40
|
+
DynamoReturnValues["AllNew"] = "ALL_NEW";
|
|
41
|
+
DynamoReturnValues["UpdatedOld"] = "UPDATED_OLD";
|
|
42
|
+
DynamoReturnValues["UpdatedNew"] = "UPDATED_NEW";
|
|
43
|
+
})(DynamoReturnValues || (exports.DynamoReturnValues = DynamoReturnValues = {}));
|
|
44
|
+
/**
|
|
45
|
+
* Companion enum for DynamoDB's `StreamViewType` (used by `enableStreams`).
|
|
46
|
+
* Provided for callers who prefer enum members over raw string literals; the
|
|
47
|
+
* `enableStreams` parameter still accepts the original
|
|
48
|
+
* `'NEW_IMAGE' | 'OLD_IMAGE' | 'NEW_AND_OLD_IMAGES' | 'KEYS_ONLY'` string
|
|
49
|
+
* literals too; this is purely additive and does not narrow what's accepted.
|
|
50
|
+
*/
|
|
51
|
+
var DynamoStreamViewType;
|
|
52
|
+
(function (DynamoStreamViewType) {
|
|
53
|
+
DynamoStreamViewType["NewImage"] = "NEW_IMAGE";
|
|
54
|
+
DynamoStreamViewType["OldImage"] = "OLD_IMAGE";
|
|
55
|
+
DynamoStreamViewType["NewAndOldImages"] = "NEW_AND_OLD_IMAGES";
|
|
56
|
+
DynamoStreamViewType["KeysOnly"] = "KEYS_ONLY";
|
|
57
|
+
})(DynamoStreamViewType || (exports.DynamoStreamViewType = DynamoStreamViewType = {}));
|
|
58
|
+
const MAX_BATCH_GET_ITEMS = 100;
|
|
59
|
+
const MAX_BATCH_WRITE_ITEMS = 25;
|
|
60
|
+
const DEFAULT_MAX_ATTEMPTS = 5;
|
|
61
|
+
const DEFAULT_BASE_DELAY_MS = 50;
|
|
62
|
+
const DEFAULT_MAX_DELAY_MS = 2000;
|
|
63
|
+
/**
|
|
64
|
+
* `DatabaseError` subclass used for all DynamoDB failures wrapped by this
|
|
65
|
+
* store. Adds `cancellationReasons`, populated whenever the underlying AWS
|
|
66
|
+
* error is a `TransactionCanceledException` (thrown by
|
|
67
|
+
* `transactWriteItems()`/`transactGetItems()`), so callers can inspect which
|
|
68
|
+
* item(s) caused the transaction to be cancelled and why without digging
|
|
69
|
+
* into `.parent`/`.original`.
|
|
70
|
+
*/
|
|
71
|
+
class DynamoDbError extends errors_1.DatabaseError {
|
|
72
|
+
constructor(message, options = {}) {
|
|
73
|
+
const { cancellationReasons, ...rest } = options;
|
|
74
|
+
super(message, rest);
|
|
75
|
+
this.cancellationReasons = cancellationReasons;
|
|
76
|
+
this.name = 'DynamoDbError';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
exports.DynamoDbError = DynamoDbError;
|
|
80
|
+
/**
|
|
81
|
+
* Amazon DynamoDB store, implementing the minimal `NoSqlStore` marker
|
|
82
|
+
* interface. Unlike the SQL dialects, there is no `query(sql)` — DynamoDB's
|
|
83
|
+
* operations are item/key-shaped (Get/Put/Update/Delete/Query/Scan/Batch/Transact),
|
|
84
|
+
* and that shape is preserved here rather than forced into a SQL string.
|
|
85
|
+
*/
|
|
86
|
+
class DynamoDbStore {
|
|
87
|
+
constructor(options = {}) {
|
|
88
|
+
this.name = 'dynamodb';
|
|
89
|
+
this.library = '@aws-sdk/client-dynamodb';
|
|
90
|
+
this.client = null;
|
|
91
|
+
this.docClient = null;
|
|
92
|
+
this.connected = false;
|
|
93
|
+
this.options = options;
|
|
94
|
+
}
|
|
95
|
+
// ---------------------------------------------------------------------
|
|
96
|
+
// NoSqlStore lifecycle
|
|
97
|
+
// ---------------------------------------------------------------------
|
|
98
|
+
async connect() {
|
|
99
|
+
try {
|
|
100
|
+
const config = {
|
|
101
|
+
...this.options.clientConfig,
|
|
102
|
+
region: this.options.region ?? this.options.clientConfig?.region,
|
|
103
|
+
endpoint: this.options.endpoint ?? this.options.clientConfig?.endpoint,
|
|
104
|
+
credentials: this.options.credentials ?? this.options.clientConfig?.credentials,
|
|
105
|
+
};
|
|
106
|
+
this.client = new client_dynamodb_1.DynamoDBClient(config);
|
|
107
|
+
this.docClient = lib_dynamodb_1.DynamoDBDocumentClient.from(this.client, this.options.translateConfig);
|
|
108
|
+
this.connected = true;
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
this.connected = false;
|
|
112
|
+
throw new errors_1.ConnectionError(`Failed to connect to DynamoDB: ${err instanceof Error ? err.message : String(err)}`, { parent: err instanceof Error ? err : undefined });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async disconnect() {
|
|
116
|
+
try {
|
|
117
|
+
this.docClient?.destroy();
|
|
118
|
+
this.client?.destroy();
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
this.client = null;
|
|
122
|
+
this.docClient = null;
|
|
123
|
+
this.connected = false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
isConnected() {
|
|
127
|
+
return this.connected;
|
|
128
|
+
}
|
|
129
|
+
/** Returns the `DynamoDBDocumentClient` (plain-object marshalling), not the raw `DynamoDBClient`. */
|
|
130
|
+
getClient() {
|
|
131
|
+
return this.requireClient();
|
|
132
|
+
}
|
|
133
|
+
/** Access to the low-level `DynamoDBClient` (raw `AttributeValue` shapes), for anything the document client doesn't cover. */
|
|
134
|
+
getRawClient() {
|
|
135
|
+
if (!this.client) {
|
|
136
|
+
throw new errors_1.ConnectionError('DynamoDB client is not connected. Call connect() first.');
|
|
137
|
+
}
|
|
138
|
+
return this.client;
|
|
139
|
+
}
|
|
140
|
+
requireClient() {
|
|
141
|
+
if (!this.docClient || !this.connected) {
|
|
142
|
+
throw new errors_1.ConnectionError('DynamoDB client is not connected. Call connect() first.');
|
|
143
|
+
}
|
|
144
|
+
return this.docClient;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Wraps an AWS SDK failure in a `DynamoDbError` (a `DatabaseError`
|
|
148
|
+
* subclass) while preserving the original error (incl. `.name` like
|
|
149
|
+
* `ConditionalCheckFailedException`) on `.parent`/`.original`. When the
|
|
150
|
+
* underlying error is a `TransactionCanceledException`, its
|
|
151
|
+
* `CancellationReasons` (per-item cancellation detail from
|
|
152
|
+
* `transactWriteItems()`/`transactGetItems()`) are also surfaced directly
|
|
153
|
+
* on `.cancellationReasons`, so callers don't have to dig into `.parent`
|
|
154
|
+
* to find out which item caused a transaction to be cancelled.
|
|
155
|
+
*/
|
|
156
|
+
wrapError(action, err) {
|
|
157
|
+
const parent = err instanceof Error ? err : undefined;
|
|
158
|
+
const name = parent?.name ? ` (${parent.name})` : '';
|
|
159
|
+
const rawReasons = err?.CancellationReasons;
|
|
160
|
+
const cancellationReasons = Array.isArray(rawReasons)
|
|
161
|
+
? rawReasons.map((r) => ({ code: r.Code, message: r.Message, item: r.Item }))
|
|
162
|
+
: undefined;
|
|
163
|
+
return new DynamoDbError(`DynamoDB ${action} failed${name}: ${parent?.message ?? String(err)}`, {
|
|
164
|
+
parent,
|
|
165
|
+
original: parent?.message,
|
|
166
|
+
cancellationReasons,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
// ---------------------------------------------------------------------
|
|
170
|
+
// Single-item CRUD
|
|
171
|
+
// ---------------------------------------------------------------------
|
|
172
|
+
async getItem(tableName, key, options = {}) {
|
|
173
|
+
const client = this.requireClient();
|
|
174
|
+
const input = {
|
|
175
|
+
TableName: tableName,
|
|
176
|
+
Key: key,
|
|
177
|
+
ConsistentRead: options.consistentRead,
|
|
178
|
+
ProjectionExpression: options.projectionExpression,
|
|
179
|
+
};
|
|
180
|
+
try {
|
|
181
|
+
const result = await client.send(new lib_dynamodb_1.GetCommand(input));
|
|
182
|
+
return result.Item;
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
throw this.wrapError(`GetItem on '${tableName}'`, err);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* `options.returnValues` defaults to `'NONE'` (matching real PutItem
|
|
190
|
+
* default behavior). Pass `'ALL_OLD'` to get the item's previous value
|
|
191
|
+
* back (`undefined` if there wasn't one).
|
|
192
|
+
*/
|
|
193
|
+
async putItem(tableName, item, options = {}) {
|
|
194
|
+
const client = this.requireClient();
|
|
195
|
+
const input = {
|
|
196
|
+
TableName: tableName,
|
|
197
|
+
Item: item,
|
|
198
|
+
ConditionExpression: options.conditionExpression,
|
|
199
|
+
ExpressionAttributeNames: options.expressionAttributeNames,
|
|
200
|
+
ExpressionAttributeValues: options.expressionAttributeValues,
|
|
201
|
+
ReturnValues: options.returnValues,
|
|
202
|
+
};
|
|
203
|
+
try {
|
|
204
|
+
const result = await client.send(new lib_dynamodb_1.PutCommand(input));
|
|
205
|
+
return result.Attributes;
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
throw this.wrapError(`PutItem on '${tableName}'`, err);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Convenience update: SETs each key in `updates` to its value. Builds a
|
|
213
|
+
* simple `UpdateExpression`/`ExpressionAttributeValues` internally, using
|
|
214
|
+
* `#k0`, `#k1`, ... as attribute name placeholders (so reserved words like
|
|
215
|
+
* `status` are always safe) merged with any `options.expressionAttributeNames`.
|
|
216
|
+
* For REMOVE/ADD/DELETE or nested-path updates, use `updateItemRaw()`.
|
|
217
|
+
*/
|
|
218
|
+
async updateItem(tableName, key, updates, options = {}) {
|
|
219
|
+
const entries = Object.entries(updates);
|
|
220
|
+
if (entries.length === 0) {
|
|
221
|
+
throw new errors_1.DatabaseError('updateItem() requires at least one attribute to update', {
|
|
222
|
+
original: 'empty updates object',
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
const names = { ...options.expressionAttributeNames };
|
|
226
|
+
const values = {};
|
|
227
|
+
const sets = [];
|
|
228
|
+
entries.forEach(([attr, value], i) => {
|
|
229
|
+
const nameKey = `#k${i}`;
|
|
230
|
+
const valueKey = `:v${i}`;
|
|
231
|
+
names[nameKey] = attr;
|
|
232
|
+
values[valueKey] = value;
|
|
233
|
+
sets.push(`${nameKey} = ${valueKey}`);
|
|
234
|
+
});
|
|
235
|
+
return this.updateItemRaw(tableName, {
|
|
236
|
+
TableName: tableName,
|
|
237
|
+
Key: key,
|
|
238
|
+
UpdateExpression: `SET ${sets.join(', ')}`,
|
|
239
|
+
ExpressionAttributeNames: names,
|
|
240
|
+
ExpressionAttributeValues: values,
|
|
241
|
+
ConditionExpression: options.conditionExpression,
|
|
242
|
+
ReturnValues: (options.returnValues ?? 'ALL_NEW'),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
/** Escape hatch for update expressions beyond the flat-SET convenience wrapper above (REMOVE/ADD/DELETE, nested paths, etc). */
|
|
246
|
+
async updateItemRaw(tableName, input) {
|
|
247
|
+
const client = this.requireClient();
|
|
248
|
+
try {
|
|
249
|
+
const result = await client.send(new lib_dynamodb_1.UpdateCommand({ ...input, TableName: tableName }));
|
|
250
|
+
return result.Attributes;
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
throw this.wrapError(`UpdateItem on '${tableName}'`, err);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/** `options.returnValues` defaults to `'ALL_OLD'` (returns the deleted item); pass `'NONE'` to skip returning it. */
|
|
257
|
+
async deleteItem(tableName, key, options = {}) {
|
|
258
|
+
const client = this.requireClient();
|
|
259
|
+
const input = {
|
|
260
|
+
TableName: tableName,
|
|
261
|
+
Key: key,
|
|
262
|
+
ConditionExpression: options.conditionExpression,
|
|
263
|
+
ExpressionAttributeNames: options.expressionAttributeNames,
|
|
264
|
+
ExpressionAttributeValues: options.expressionAttributeValues,
|
|
265
|
+
ReturnValues: (options.returnValues ?? 'ALL_OLD'),
|
|
266
|
+
};
|
|
267
|
+
try {
|
|
268
|
+
const result = await client.send(new lib_dynamodb_1.DeleteCommand(input));
|
|
269
|
+
return result.Attributes;
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
throw this.wrapError(`DeleteItem on '${tableName}'`, err);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// ---------------------------------------------------------------------
|
|
276
|
+
// Query / Scan
|
|
277
|
+
// ---------------------------------------------------------------------
|
|
278
|
+
/**
|
|
279
|
+
* Query by partition key equality (plus an optional sort-key condition)
|
|
280
|
+
* against the base table or a named GSI/LSI. This is the primary way to
|
|
281
|
+
* read more than one item efficiently from DynamoDB — unlike Scan, it
|
|
282
|
+
* only reads the partition(s) that match, not the whole table.
|
|
283
|
+
*/
|
|
284
|
+
async query(tableName, params) {
|
|
285
|
+
const client = this.requireClient();
|
|
286
|
+
try {
|
|
287
|
+
const result = await client.send(new lib_dynamodb_1.QueryCommand({
|
|
288
|
+
TableName: tableName,
|
|
289
|
+
IndexName: params.indexName,
|
|
290
|
+
KeyConditionExpression: params.keyConditionExpression,
|
|
291
|
+
ExpressionAttributeNames: params.expressionAttributeNames,
|
|
292
|
+
ExpressionAttributeValues: params.expressionAttributeValues,
|
|
293
|
+
FilterExpression: params.filterExpression,
|
|
294
|
+
ProjectionExpression: params.projectionExpression,
|
|
295
|
+
Limit: params.limit,
|
|
296
|
+
ScanIndexForward: params.scanIndexForward,
|
|
297
|
+
ExclusiveStartKey: params.exclusiveStartKey,
|
|
298
|
+
ConsistentRead: params.consistentRead,
|
|
299
|
+
}));
|
|
300
|
+
return {
|
|
301
|
+
items: (result.Items ?? []),
|
|
302
|
+
lastEvaluatedKey: result.LastEvaluatedKey,
|
|
303
|
+
count: result.Count ?? 0,
|
|
304
|
+
scannedCount: result.ScannedCount ?? 0,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
throw this.wrapError(`Query on '${tableName}'`, err);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Full table (or index) scan. This reads every item in the table — cost
|
|
313
|
+
* and latency scale with table size, not with how many items you actually
|
|
314
|
+
* want, and `filterExpression` is applied *after* the read (it does not
|
|
315
|
+
* reduce RCU consumption or the amount of data scanned). Prefer `query()`
|
|
316
|
+
* with a well-designed partition/sort key or GSI. Only reach for `scan()`
|
|
317
|
+
* for admin/maintenance/export tasks, small tables, or as a last resort
|
|
318
|
+
* when no query-friendly access pattern exists.
|
|
319
|
+
*/
|
|
320
|
+
async scan(tableName, params = {}) {
|
|
321
|
+
const client = this.requireClient();
|
|
322
|
+
try {
|
|
323
|
+
const result = await client.send(new lib_dynamodb_1.ScanCommand({
|
|
324
|
+
TableName: tableName,
|
|
325
|
+
IndexName: params.indexName,
|
|
326
|
+
FilterExpression: params.filterExpression,
|
|
327
|
+
ProjectionExpression: params.projectionExpression,
|
|
328
|
+
ExpressionAttributeNames: params.expressionAttributeNames,
|
|
329
|
+
ExpressionAttributeValues: params.expressionAttributeValues,
|
|
330
|
+
Limit: params.limit,
|
|
331
|
+
ExclusiveStartKey: params.exclusiveStartKey,
|
|
332
|
+
ConsistentRead: params.consistentRead,
|
|
333
|
+
Segment: params.segment,
|
|
334
|
+
TotalSegments: params.totalSegments,
|
|
335
|
+
}));
|
|
336
|
+
return {
|
|
337
|
+
items: (result.Items ?? []),
|
|
338
|
+
lastEvaluatedKey: result.LastEvaluatedKey,
|
|
339
|
+
count: result.Count ?? 0,
|
|
340
|
+
scannedCount: result.ScannedCount ?? 0,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
throw this.wrapError(`Scan on '${tableName}'`, err);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Auto-paginating `query()`: DynamoDB caps each `Query` response at 1MB of
|
|
349
|
+
* data, so a query over more items than that fits in a single page and
|
|
350
|
+
* returns a `lastEvaluatedKey` that the caller must pass back in as
|
|
351
|
+
* `exclusiveStartKey` to fetch the next page. Forgetting to loop on that
|
|
352
|
+
* silently truncates results. `queryAll()` follows `lastEvaluatedKey`
|
|
353
|
+
* automatically and returns every matching item across all pages.
|
|
354
|
+
*
|
|
355
|
+
* `params.limit`, if set, still applies per-page (as DynamoDB defines
|
|
356
|
+
* it) — it does not cap the total number of items returned here. To
|
|
357
|
+
* consume pages one at a time instead of buffering everything in memory,
|
|
358
|
+
* use `queryPages()`.
|
|
359
|
+
*/
|
|
360
|
+
async queryAll(tableName, params) {
|
|
361
|
+
const items = [];
|
|
362
|
+
for await (const page of this.queryPages(tableName, params)) {
|
|
363
|
+
items.push(...page);
|
|
364
|
+
}
|
|
365
|
+
return items;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Async-generator form of `queryAll()`: yields each page of items as it
|
|
369
|
+
* arrives instead of buffering the entire result set in memory, while
|
|
370
|
+
* still following `lastEvaluatedKey` automatically.
|
|
371
|
+
*/
|
|
372
|
+
async *queryPages(tableName, params) {
|
|
373
|
+
let exclusiveStartKey = params.exclusiveStartKey;
|
|
374
|
+
do {
|
|
375
|
+
const result = await this.query(tableName, { ...params, exclusiveStartKey });
|
|
376
|
+
yield result.items;
|
|
377
|
+
exclusiveStartKey = result.lastEvaluatedKey;
|
|
378
|
+
} while (exclusiveStartKey !== undefined);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Auto-paginating `scan()` — see `queryAll()` above for why this matters
|
|
382
|
+
* (the same 1MB-per-page cap applies to Scan). Reminder: `scan()` is
|
|
383
|
+
* already O(table size) per the warning on `scan()` itself; `scanAll()`
|
|
384
|
+
* makes that even more explicit by reading literally the whole table (or
|
|
385
|
+
* index) into memory across as many pages as it takes.
|
|
386
|
+
*/
|
|
387
|
+
async scanAll(tableName, params = {}) {
|
|
388
|
+
const items = [];
|
|
389
|
+
for await (const page of this.scanPages(tableName, params)) {
|
|
390
|
+
items.push(...page);
|
|
391
|
+
}
|
|
392
|
+
return items;
|
|
393
|
+
}
|
|
394
|
+
/** Async-generator form of `scanAll()`; yields each page as it arrives. */
|
|
395
|
+
async *scanPages(tableName, params = {}) {
|
|
396
|
+
let exclusiveStartKey = params.exclusiveStartKey;
|
|
397
|
+
do {
|
|
398
|
+
const result = await this.scan(tableName, { ...params, exclusiveStartKey });
|
|
399
|
+
yield result.items;
|
|
400
|
+
exclusiveStartKey = result.lastEvaluatedKey;
|
|
401
|
+
} while (exclusiveStartKey !== undefined);
|
|
402
|
+
}
|
|
403
|
+
// ---------------------------------------------------------------------
|
|
404
|
+
// Batch operations
|
|
405
|
+
// ---------------------------------------------------------------------
|
|
406
|
+
/** Sleeps for `ms` milliseconds. */
|
|
407
|
+
delay(ms) {
|
|
408
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Exponential backoff with full jitter (AWS's recommended strategy):
|
|
412
|
+
* a random delay between 0 and `min(maxDelayMs, baseDelayMs * 2^attempt)`.
|
|
413
|
+
* `attempt` is 0-based (the delay before the *first* retry, i.e. after
|
|
414
|
+
* the initial attempt has already failed/returned unprocessed items).
|
|
415
|
+
*/
|
|
416
|
+
backoffDelayMs(attempt, baseDelayMs, maxDelayMs) {
|
|
417
|
+
const cap = Math.min(maxDelayMs, baseDelayMs * 2 ** attempt);
|
|
418
|
+
return Math.random() * cap;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* BatchGetItem across one or more tables. DynamoDB caps a single
|
|
422
|
+
* BatchGetItem call at 100 items total; this method chunks `keys`
|
|
423
|
+
* internally (25-at-a-time chunks well under the limit, sent
|
|
424
|
+
* sequentially) so callers can pass an arbitrarily large key list without
|
|
425
|
+
* worrying about the limit themselves. Also retries any
|
|
426
|
+
* `UnprocessedKeys` DynamoDB returns for a chunk (throttling) with
|
|
427
|
+
* exponential backoff and jitter between attempts, up to
|
|
428
|
+
* `options.maxAttempts` (default 5) attempts per chunk, before giving up
|
|
429
|
+
* and returning whatever's still unprocessed to the caller.
|
|
430
|
+
*/
|
|
431
|
+
async batchGetItem(tableName, keys, options = {}) {
|
|
432
|
+
const client = this.requireClient();
|
|
433
|
+
const chunkSize = Math.min(options.chunkSize ?? MAX_BATCH_GET_ITEMS, MAX_BATCH_GET_ITEMS);
|
|
434
|
+
const maxAttempts = Math.max(1, options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS);
|
|
435
|
+
const baseDelayMs = options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
|
|
436
|
+
const maxDelayMs = options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
|
|
437
|
+
const items = [];
|
|
438
|
+
const unprocessedKeys = [];
|
|
439
|
+
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
440
|
+
const chunk = keys.slice(i, i + chunkSize);
|
|
441
|
+
try {
|
|
442
|
+
let requestKeys = chunk;
|
|
443
|
+
let attempts = 0;
|
|
444
|
+
while (requestKeys.length > 0 && attempts < maxAttempts) {
|
|
445
|
+
if (attempts > 0) {
|
|
446
|
+
await this.delay(this.backoffDelayMs(attempts - 1, baseDelayMs, maxDelayMs));
|
|
447
|
+
}
|
|
448
|
+
const result = await client.send(new lib_dynamodb_1.BatchGetCommand({
|
|
449
|
+
RequestItems: {
|
|
450
|
+
[tableName]: { Keys: requestKeys, ConsistentRead: options.consistentRead },
|
|
451
|
+
},
|
|
452
|
+
}));
|
|
453
|
+
const fetched = (result.Responses?.[tableName] ?? []);
|
|
454
|
+
items.push(...fetched);
|
|
455
|
+
const unprocessed = result.UnprocessedKeys?.[tableName]?.Keys;
|
|
456
|
+
requestKeys = unprocessed ?? [];
|
|
457
|
+
attempts += 1;
|
|
458
|
+
}
|
|
459
|
+
if (requestKeys.length > 0) {
|
|
460
|
+
unprocessedKeys.push(...requestKeys);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
catch (err) {
|
|
464
|
+
throw this.wrapError(`BatchGetItem on '${tableName}'`, err);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return { items, unprocessedKeys };
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* BatchWriteItem (put and/or delete) against a single table. DynamoDB
|
|
471
|
+
* caps a single BatchWriteItem call at 25 write requests; this method
|
|
472
|
+
* chunks `requests` internally so callers can pass an arbitrarily large
|
|
473
|
+
* batch. Also retries `UnprocessedItems` with exponential backoff and
|
|
474
|
+
* jitter, up to `options.maxAttempts` (default 5) attempts per chunk,
|
|
475
|
+
* before returning whatever's left to the caller.
|
|
476
|
+
*/
|
|
477
|
+
async batchWriteItem(tableName, requests, options = {}) {
|
|
478
|
+
const client = this.requireClient();
|
|
479
|
+
const chunkSize = Math.min(options.chunkSize ?? MAX_BATCH_WRITE_ITEMS, MAX_BATCH_WRITE_ITEMS);
|
|
480
|
+
const maxAttempts = Math.max(1, options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS);
|
|
481
|
+
const baseDelayMs = options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
|
|
482
|
+
const maxDelayMs = options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
|
|
483
|
+
let unprocessedCount = 0;
|
|
484
|
+
const toWriteRequest = (r) => 'put' in r && r.put !== undefined
|
|
485
|
+
? { PutRequest: { Item: r.put } }
|
|
486
|
+
: { DeleteRequest: { Key: r.delete } };
|
|
487
|
+
for (let i = 0; i < requests.length; i += chunkSize) {
|
|
488
|
+
const chunk = requests.slice(i, i + chunkSize).map(toWriteRequest);
|
|
489
|
+
try {
|
|
490
|
+
let pending = chunk;
|
|
491
|
+
let attempts = 0;
|
|
492
|
+
while (pending.length > 0 && attempts < maxAttempts) {
|
|
493
|
+
if (attempts > 0) {
|
|
494
|
+
await this.delay(this.backoffDelayMs(attempts - 1, baseDelayMs, maxDelayMs));
|
|
495
|
+
}
|
|
496
|
+
const result = await client.send(new lib_dynamodb_1.BatchWriteCommand({ RequestItems: { [tableName]: pending } }));
|
|
497
|
+
const unprocessed = result.UnprocessedItems?.[tableName];
|
|
498
|
+
pending = unprocessed ?? [];
|
|
499
|
+
attempts += 1;
|
|
500
|
+
}
|
|
501
|
+
unprocessedCount += pending.length;
|
|
502
|
+
}
|
|
503
|
+
catch (err) {
|
|
504
|
+
throw this.wrapError(`BatchWriteItem on '${tableName}'`, err);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return { unprocessedCount };
|
|
508
|
+
}
|
|
509
|
+
// ---------------------------------------------------------------------
|
|
510
|
+
// Transactions (multi-item ACID, up to 100 items across tables)
|
|
511
|
+
// ---------------------------------------------------------------------
|
|
512
|
+
/**
|
|
513
|
+
* Passthrough to DynamoDB's TransactWriteItems — up to 100 Put/Update/
|
|
514
|
+
* Delete/ConditionCheck actions, atomically, potentially across multiple
|
|
515
|
+
* tables. Not chunked: DynamoDB rejects the whole transaction if it
|
|
516
|
+
* exceeds the limit, so it's on the caller to stay under it (unlike the
|
|
517
|
+
* batch methods above, there's no safe way to silently split a
|
|
518
|
+
* transaction into several without breaking its atomicity guarantee).
|
|
519
|
+
*/
|
|
520
|
+
async transactWriteItems(input) {
|
|
521
|
+
const client = this.requireClient();
|
|
522
|
+
try {
|
|
523
|
+
const { transactItems, ...rest } = input;
|
|
524
|
+
await client.send(new lib_dynamodb_1.TransactWriteCommand({ ...rest, TransactItems: transactItems }));
|
|
525
|
+
}
|
|
526
|
+
catch (err) {
|
|
527
|
+
throw this.wrapError('TransactWriteItems', err);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
/** Passthrough to DynamoDB's TransactGetItems — up to 100 Get actions, read as a single consistent snapshot. */
|
|
531
|
+
async transactGetItems(input) {
|
|
532
|
+
const client = this.requireClient();
|
|
533
|
+
try {
|
|
534
|
+
const { transactItems, ...rest } = input;
|
|
535
|
+
const result = await client.send(new lib_dynamodb_1.TransactGetCommand({ ...rest, TransactItems: transactItems }));
|
|
536
|
+
return (result.Responses ?? []).map((r) => r.Item);
|
|
537
|
+
}
|
|
538
|
+
catch (err) {
|
|
539
|
+
throw this.wrapError('TransactGetItems', err);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Enables DynamoDB Streams on a table via `UpdateTableCommand`'s
|
|
544
|
+
* `StreamSpecification`, choosing what a stream record captures:
|
|
545
|
+
* `KEYS_ONLY` (just the key attributes of the modified item), `NEW_IMAGE`
|
|
546
|
+
* (the entire item as it appears after modification), `OLD_IMAGE` (as it
|
|
547
|
+
* appeared before), or `NEW_AND_OLD_IMAGES` (both). This only flips the
|
|
548
|
+
* table-level setting and returns the ARN DynamoDB assigns the new
|
|
549
|
+
* stream — it does not read from the stream itself; see "Known
|
|
550
|
+
* limitations" in the README for why a stream *consumer* is out of scope
|
|
551
|
+
* for this store.
|
|
552
|
+
*/
|
|
553
|
+
async enableStreams(tableName, streamViewType) {
|
|
554
|
+
const client = this.getRawClient();
|
|
555
|
+
try {
|
|
556
|
+
const result = await client.send(new client_dynamodb_1.UpdateTableCommand({
|
|
557
|
+
TableName: tableName,
|
|
558
|
+
StreamSpecification: {
|
|
559
|
+
StreamEnabled: true,
|
|
560
|
+
StreamViewType: streamViewType,
|
|
561
|
+
},
|
|
562
|
+
}));
|
|
563
|
+
return result.TableDescription?.LatestStreamArn;
|
|
564
|
+
}
|
|
565
|
+
catch (err) {
|
|
566
|
+
throw this.wrapError(`UpdateTable (enableStreams) on '${tableName}'`, err);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
/** Disables DynamoDB Streams on a table via `UpdateTableCommand`. */
|
|
570
|
+
async disableStreams(tableName) {
|
|
571
|
+
const client = this.getRawClient();
|
|
572
|
+
try {
|
|
573
|
+
await client.send(new client_dynamodb_1.UpdateTableCommand({
|
|
574
|
+
TableName: tableName,
|
|
575
|
+
StreamSpecification: { StreamEnabled: false },
|
|
576
|
+
}));
|
|
577
|
+
}
|
|
578
|
+
catch (err) {
|
|
579
|
+
throw this.wrapError(`UpdateTable (disableStreams) on '${tableName}'`, err);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Reads back the table's current stream ARN (`undefined` if streams
|
|
584
|
+
* aren't enabled) via `DescribeTableCommand`. Consuming the stream itself
|
|
585
|
+
* (shard iterators / record polling) requires the separate
|
|
586
|
+
* `@aws-sdk/client-dynamodb-streams` package or a Lambda event source
|
|
587
|
+
* mapping — out of scope here, see the README.
|
|
588
|
+
*/
|
|
589
|
+
async getStreamArn(tableName) {
|
|
590
|
+
const client = this.getRawClient();
|
|
591
|
+
try {
|
|
592
|
+
const result = await client.send(new client_dynamodb_1.DescribeTableCommand({ TableName: tableName }));
|
|
593
|
+
return result.Table?.LatestStreamArn;
|
|
594
|
+
}
|
|
595
|
+
catch (err) {
|
|
596
|
+
throw this.wrapError(`DescribeTable (getStreamArn) on '${tableName}'`, err);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Enables TTL on a table via `UpdateTimeToLiveCommand`: DynamoDB
|
|
601
|
+
* periodically (usually within 48 hours, not immediately) deletes items
|
|
602
|
+
* whose `attributeName` holds a Unix epoch-seconds number in the past.
|
|
603
|
+
* A table may only have one TTL attribute at a time.
|
|
604
|
+
*/
|
|
605
|
+
async enableTTL(tableName, attributeName) {
|
|
606
|
+
const client = this.getRawClient();
|
|
607
|
+
try {
|
|
608
|
+
await client.send(new client_dynamodb_1.UpdateTimeToLiveCommand({
|
|
609
|
+
TableName: tableName,
|
|
610
|
+
TimeToLiveSpecification: { Enabled: true, AttributeName: attributeName },
|
|
611
|
+
}));
|
|
612
|
+
}
|
|
613
|
+
catch (err) {
|
|
614
|
+
throw this.wrapError(`UpdateTimeToLive (enableTTL) on '${tableName}'`, err);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Disables TTL on a table. DynamoDB requires the currently-enabled
|
|
619
|
+
* `AttributeName` to be repeated on the disabling call, so this first
|
|
620
|
+
* reads it back via `describeTTL()` and is a no-op if TTL isn't
|
|
621
|
+
* currently enabled on the table.
|
|
622
|
+
*/
|
|
623
|
+
async disableTTL(tableName) {
|
|
624
|
+
const client = this.getRawClient();
|
|
625
|
+
const current = await this.describeTTL(tableName);
|
|
626
|
+
const attributeName = current.attributeName;
|
|
627
|
+
if (!attributeName)
|
|
628
|
+
return;
|
|
629
|
+
try {
|
|
630
|
+
await client.send(new client_dynamodb_1.UpdateTimeToLiveCommand({
|
|
631
|
+
TableName: tableName,
|
|
632
|
+
TimeToLiveSpecification: { Enabled: false, AttributeName: attributeName },
|
|
633
|
+
}));
|
|
634
|
+
}
|
|
635
|
+
catch (err) {
|
|
636
|
+
throw this.wrapError(`UpdateTimeToLive (disableTTL) on '${tableName}'`, err);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
/** Reads back a table's TTL configuration via `DescribeTimeToLiveCommand`. */
|
|
640
|
+
async describeTTL(tableName) {
|
|
641
|
+
const client = this.getRawClient();
|
|
642
|
+
try {
|
|
643
|
+
const result = await client.send(new client_dynamodb_1.DescribeTimeToLiveCommand({ TableName: tableName }));
|
|
644
|
+
return {
|
|
645
|
+
status: result.TimeToLiveDescription?.TimeToLiveStatus,
|
|
646
|
+
attributeName: result.TimeToLiveDescription?.AttributeName,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
catch (err) {
|
|
650
|
+
throw this.wrapError(`DescribeTimeToLive on '${tableName}'`, err);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
// ---------------------------------------------------------------------
|
|
654
|
+
// Table lifecycle
|
|
655
|
+
// ---------------------------------------------------------------------
|
|
656
|
+
/**
|
|
657
|
+
* Creates a table via `CreateTableCommand`. Thin passthrough to the raw
|
|
658
|
+
* SDK input shape: `params.TableName`, `params.KeySchema`, and
|
|
659
|
+
* `params.AttributeDefinitions` are required (as real CreateTable
|
|
660
|
+
* requires), plus either `params.BillingMode: 'PAY_PER_REQUEST'` or
|
|
661
|
+
* `params.ProvisionedThroughput` (real CreateTable requires one or the
|
|
662
|
+
* other), and optionally `params.GlobalSecondaryIndexes`/
|
|
663
|
+
* `params.LocalSecondaryIndexes`. Table creation is asynchronous on AWS's
|
|
664
|
+
* side (the table starts in `CREATING` status) — this call returns as
|
|
665
|
+
* soon as DynamoDB accepts the request, it does not wait for the table to
|
|
666
|
+
* become `ACTIVE`; poll `describeTable()` if you need to wait.
|
|
667
|
+
*/
|
|
668
|
+
async createTable(params) {
|
|
669
|
+
const client = this.getRawClient();
|
|
670
|
+
try {
|
|
671
|
+
const result = await client.send(new client_dynamodb_1.CreateTableCommand(params));
|
|
672
|
+
return result.TableDescription;
|
|
673
|
+
}
|
|
674
|
+
catch (err) {
|
|
675
|
+
throw this.wrapError(`CreateTable on '${params.TableName}'`, err);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Deletes a table via `DeleteTableCommand`. Like `createTable()`, this is
|
|
680
|
+
* asynchronous on AWS's side (the table moves to `DELETING` status); this
|
|
681
|
+
* call returns as soon as DynamoDB accepts the request.
|
|
682
|
+
*/
|
|
683
|
+
async deleteTable(tableName) {
|
|
684
|
+
const client = this.getRawClient();
|
|
685
|
+
try {
|
|
686
|
+
const result = await client.send(new client_dynamodb_1.DeleteTableCommand({ TableName: tableName }));
|
|
687
|
+
return result.TableDescription;
|
|
688
|
+
}
|
|
689
|
+
catch (err) {
|
|
690
|
+
throw this.wrapError(`DeleteTable on '${tableName}'`, err);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Lists table names in the current account/region via `ListTablesCommand`.
|
|
695
|
+
* A single call returns at most 100 table names (or `params.limit` if
|
|
696
|
+
* lower); pass `params.exclusiveStartTableName` (from a previous call's
|
|
697
|
+
* `lastEvaluatedTableName`) to page through the rest.
|
|
698
|
+
*/
|
|
699
|
+
async listTables(params = {}) {
|
|
700
|
+
const client = this.getRawClient();
|
|
701
|
+
try {
|
|
702
|
+
const result = await client.send(new client_dynamodb_1.ListTablesCommand({
|
|
703
|
+
ExclusiveStartTableName: params.exclusiveStartTableName,
|
|
704
|
+
Limit: params.limit,
|
|
705
|
+
}));
|
|
706
|
+
return {
|
|
707
|
+
tableNames: result.TableNames ?? [],
|
|
708
|
+
lastEvaluatedTableName: result.LastEvaluatedTableName,
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
catch (err) {
|
|
712
|
+
throw this.wrapError('ListTables', err);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Reads back a table's full description (status, key schema, billing
|
|
717
|
+
* mode, indexes, stream ARN, item count, etc.) via `DescribeTableCommand`.
|
|
718
|
+
* `getStreamArn()` above is a narrow convenience wrapper around this same
|
|
719
|
+
* command for just the stream ARN.
|
|
720
|
+
*/
|
|
721
|
+
async describeTable(tableName) {
|
|
722
|
+
const client = this.getRawClient();
|
|
723
|
+
try {
|
|
724
|
+
const result = await client.send(new client_dynamodb_1.DescribeTableCommand({ TableName: tableName }));
|
|
725
|
+
return result.Table;
|
|
726
|
+
}
|
|
727
|
+
catch (err) {
|
|
728
|
+
throw this.wrapError(`DescribeTable on '${tableName}'`, err);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Adds or removes a Global Secondary Index on an existing table via
|
|
733
|
+
* `UpdateTableCommand`'s `GlobalSecondaryIndexUpdates`. DynamoDB only
|
|
734
|
+
* allows one GSI create/delete/update action per `UpdateTable` call, so
|
|
735
|
+
* `gsiUpdates` must contain exactly one `GlobalSecondaryIndexUpdate`
|
|
736
|
+
* entry (`{ Create: {...} }`, `{ Update: {...} }`, or `{ Delete: {...} }`).
|
|
737
|
+
* When creating a new GSI, `attributeDefinitions` must include the key
|
|
738
|
+
* attribute(s) of the new index (DynamoDB validates this).
|
|
739
|
+
*/
|
|
740
|
+
async updateTableIndexes(tableName, gsiUpdates, attributeDefinitions) {
|
|
741
|
+
const client = this.getRawClient();
|
|
742
|
+
try {
|
|
743
|
+
const result = await client.send(new client_dynamodb_1.UpdateTableCommand({
|
|
744
|
+
TableName: tableName,
|
|
745
|
+
GlobalSecondaryIndexUpdates: gsiUpdates,
|
|
746
|
+
AttributeDefinitions: attributeDefinitions,
|
|
747
|
+
}));
|
|
748
|
+
return result.TableDescription;
|
|
749
|
+
}
|
|
750
|
+
catch (err) {
|
|
751
|
+
throw this.wrapError(`UpdateTable (updateTableIndexes) on '${tableName}'`, err);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
// ---------------------------------------------------------------------
|
|
755
|
+
// Billing/capacity mode
|
|
756
|
+
// ---------------------------------------------------------------------
|
|
757
|
+
/**
|
|
758
|
+
* Switches a table between on-demand (`PAY_PER_REQUEST`) and provisioned
|
|
759
|
+
* (`PROVISIONED`) capacity via `UpdateTableCommand`. When switching to
|
|
760
|
+
* `PROVISIONED`, `throughput` (`ReadCapacityUnits`/`WriteCapacityUnits`)
|
|
761
|
+
* is required (real DynamoDB rejects the call without it); it's ignored
|
|
762
|
+
* when switching to `PAY_PER_REQUEST`.
|
|
763
|
+
*/
|
|
764
|
+
async updateBillingMode(tableName, mode, throughput) {
|
|
765
|
+
const client = this.getRawClient();
|
|
766
|
+
try {
|
|
767
|
+
const result = await client.send(new client_dynamodb_1.UpdateTableCommand({
|
|
768
|
+
TableName: tableName,
|
|
769
|
+
BillingMode: mode,
|
|
770
|
+
ProvisionedThroughput: mode === 'PROVISIONED' ? throughput : undefined,
|
|
771
|
+
}));
|
|
772
|
+
return result.TableDescription;
|
|
773
|
+
}
|
|
774
|
+
catch (err) {
|
|
775
|
+
throw this.wrapError(`UpdateTable (updateBillingMode) on '${tableName}'`, err);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
// ---------------------------------------------------------------------
|
|
779
|
+
// Point-in-Time Recovery (PITR)
|
|
780
|
+
// ---------------------------------------------------------------------
|
|
781
|
+
/**
|
|
782
|
+
* Enables Point-in-Time Recovery on a table via
|
|
783
|
+
* `UpdateContinuousBackupsCommand`, allowing restore to any second within
|
|
784
|
+
* the retention window (35 days by default; DynamoDB currently ignores
|
|
785
|
+
* a custom `recoveryPeriodInDays` on enable and defaults to 35).
|
|
786
|
+
*/
|
|
787
|
+
async enablePITR(tableName) {
|
|
788
|
+
const client = this.getRawClient();
|
|
789
|
+
try {
|
|
790
|
+
await client.send(new client_dynamodb_1.UpdateContinuousBackupsCommand({
|
|
791
|
+
TableName: tableName,
|
|
792
|
+
PointInTimeRecoverySpecification: { PointInTimeRecoveryEnabled: true },
|
|
793
|
+
}));
|
|
794
|
+
}
|
|
795
|
+
catch (err) {
|
|
796
|
+
throw this.wrapError(`UpdateContinuousBackups (enablePITR) on '${tableName}'`, err);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
/** Disables Point-in-Time Recovery on a table via `UpdateContinuousBackupsCommand`. */
|
|
800
|
+
async disablePITR(tableName) {
|
|
801
|
+
const client = this.getRawClient();
|
|
802
|
+
try {
|
|
803
|
+
await client.send(new client_dynamodb_1.UpdateContinuousBackupsCommand({
|
|
804
|
+
TableName: tableName,
|
|
805
|
+
PointInTimeRecoverySpecification: { PointInTimeRecoveryEnabled: false },
|
|
806
|
+
}));
|
|
807
|
+
}
|
|
808
|
+
catch (err) {
|
|
809
|
+
throw this.wrapError(`UpdateContinuousBackups (disablePITR) on '${tableName}'`, err);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
/** Reads back a table's PITR configuration via `DescribeContinuousBackupsCommand`. */
|
|
813
|
+
async describePITR(tableName) {
|
|
814
|
+
const client = this.getRawClient();
|
|
815
|
+
try {
|
|
816
|
+
const result = await client.send(new client_dynamodb_1.DescribeContinuousBackupsCommand({ TableName: tableName }));
|
|
817
|
+
const pitr = result.ContinuousBackupsDescription?.PointInTimeRecoveryDescription;
|
|
818
|
+
return {
|
|
819
|
+
status: pitr?.PointInTimeRecoveryStatus,
|
|
820
|
+
recoveryPeriodInDays: pitr?.RecoveryPeriodInDays,
|
|
821
|
+
earliestRestorableDateTime: pitr?.EarliestRestorableDateTime,
|
|
822
|
+
latestRestorableDateTime: pitr?.LatestRestorableDateTime,
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
catch (err) {
|
|
826
|
+
throw this.wrapError(`DescribeContinuousBackups on '${tableName}'`, err);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
// ---------------------------------------------------------------------
|
|
830
|
+
// On-demand backup/restore
|
|
831
|
+
// ---------------------------------------------------------------------
|
|
832
|
+
//
|
|
833
|
+
// Distinct from PITR above: these are named, on-demand snapshots (created
|
|
834
|
+
// explicitly, kept until deleted) rather than the continuous, rolling
|
|
835
|
+
// 35-day recovery window PITR provides.
|
|
836
|
+
/** Creates an on-demand backup of a table via `CreateBackupCommand`. */
|
|
837
|
+
async createBackup(tableName, backupName) {
|
|
838
|
+
const client = this.getRawClient();
|
|
839
|
+
try {
|
|
840
|
+
const result = await client.send(new client_dynamodb_1.CreateBackupCommand({ TableName: tableName, BackupName: backupName }));
|
|
841
|
+
return result.BackupDetails;
|
|
842
|
+
}
|
|
843
|
+
catch (err) {
|
|
844
|
+
throw this.wrapError(`CreateBackup on '${tableName}'`, err);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
/** Reads back a backup's full description via `DescribeBackupCommand`. */
|
|
848
|
+
async describeBackup(backupArn) {
|
|
849
|
+
const client = this.getRawClient();
|
|
850
|
+
try {
|
|
851
|
+
const result = await client.send(new client_dynamodb_1.DescribeBackupCommand({ BackupArn: backupArn }));
|
|
852
|
+
return result.BackupDescription;
|
|
853
|
+
}
|
|
854
|
+
catch (err) {
|
|
855
|
+
throw this.wrapError(`DescribeBackup on '${backupArn}'`, err);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Lists backups via `ListBackupsCommand`, optionally scoped to a single
|
|
860
|
+
* table. Does not auto-paginate — pass the last item's `BackupArn` back
|
|
861
|
+
* in as `exclusiveStartBackupArn` to fetch the next page, same pattern as
|
|
862
|
+
* `listTables()`.
|
|
863
|
+
*/
|
|
864
|
+
async listBackups(tableName, options = {}) {
|
|
865
|
+
const client = this.getRawClient();
|
|
866
|
+
try {
|
|
867
|
+
const result = await client.send(new client_dynamodb_1.ListBackupsCommand({
|
|
868
|
+
TableName: tableName,
|
|
869
|
+
Limit: options.limit,
|
|
870
|
+
ExclusiveStartBackupArn: options.exclusiveStartBackupArn,
|
|
871
|
+
}));
|
|
872
|
+
return result.BackupSummaries ?? [];
|
|
873
|
+
}
|
|
874
|
+
catch (err) {
|
|
875
|
+
throw this.wrapError('ListBackups', err);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Restores a backup into a new table via `RestoreTableFromBackupCommand`.
|
|
880
|
+
* DynamoDB always restores into a *new* table (`newTableName` must not
|
|
881
|
+
* already exist) — it does not overwrite the original table the backup
|
|
882
|
+
* was taken from. Like `createTable()`, this is asynchronous: the new
|
|
883
|
+
* table starts in `CREATING` status.
|
|
884
|
+
*/
|
|
885
|
+
async restoreTableFromBackup(backupArn, newTableName) {
|
|
886
|
+
const client = this.getRawClient();
|
|
887
|
+
try {
|
|
888
|
+
const result = await client.send(new client_dynamodb_1.RestoreTableFromBackupCommand({ BackupArn: backupArn, TargetTableName: newTableName }));
|
|
889
|
+
return result.TableDescription;
|
|
890
|
+
}
|
|
891
|
+
catch (err) {
|
|
892
|
+
throw this.wrapError(`RestoreTableFromBackup to '${newTableName}'`, err);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
// ---------------------------------------------------------------------
|
|
896
|
+
// Tagging
|
|
897
|
+
// ---------------------------------------------------------------------
|
|
898
|
+
/** Adds (or updates, if the key already exists) tags on a table/backup resource via `TagResourceCommand`. */
|
|
899
|
+
async tagResource(resourceArn, tags) {
|
|
900
|
+
const client = this.getRawClient();
|
|
901
|
+
const Tags = Array.isArray(tags)
|
|
902
|
+
? tags
|
|
903
|
+
: Object.entries(tags).map(([Key, Value]) => ({ Key, Value }));
|
|
904
|
+
try {
|
|
905
|
+
await client.send(new client_dynamodb_1.TagResourceCommand({ ResourceArn: resourceArn, Tags }));
|
|
906
|
+
}
|
|
907
|
+
catch (err) {
|
|
908
|
+
throw this.wrapError(`TagResource on '${resourceArn}'`, err);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
/** Removes tags (by key) from a table/backup resource via `UntagResourceCommand`. */
|
|
912
|
+
async untagResource(resourceArn, tagKeys) {
|
|
913
|
+
const client = this.getRawClient();
|
|
914
|
+
try {
|
|
915
|
+
await client.send(new client_dynamodb_1.UntagResourceCommand({ ResourceArn: resourceArn, TagKeys: tagKeys }));
|
|
916
|
+
}
|
|
917
|
+
catch (err) {
|
|
918
|
+
throw this.wrapError(`UntagResource on '${resourceArn}'`, err);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Lists tags on a table/backup resource via `ListTagsOfResourceCommand`.
|
|
923
|
+
* Does not auto-paginate — pass a returned `nextToken` back in as
|
|
924
|
+
* `options.nextToken` to fetch the next page.
|
|
925
|
+
*/
|
|
926
|
+
async listTags(resourceArn, options = {}) {
|
|
927
|
+
const client = this.getRawClient();
|
|
928
|
+
try {
|
|
929
|
+
const result = await client.send(new client_dynamodb_1.ListTagsOfResourceCommand({ ResourceArn: resourceArn, NextToken: options.nextToken }));
|
|
930
|
+
return { tags: result.Tags ?? [], nextToken: result.NextToken };
|
|
931
|
+
}
|
|
932
|
+
catch (err) {
|
|
933
|
+
throw this.wrapError(`ListTagsOfResource on '${resourceArn}'`, err);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
exports.DynamoDbStore = DynamoDbStore;
|