effect-dynamodb 1.24.0 → 1.26.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/dist/Aggregate.d.ts +12 -2
- package/dist/Aggregate.d.ts.map +1 -1
- package/dist/Aggregate.js +284 -55
- package/dist/Aggregate.js.map +1 -1
- package/dist/Batch.d.ts +14 -5
- package/dist/Batch.d.ts.map +1 -1
- package/dist/Batch.js +184 -119
- package/dist/Batch.js.map +1 -1
- package/dist/Collection.d.ts +9 -1
- package/dist/Collection.d.ts.map +1 -1
- package/dist/Collection.js +5 -4
- package/dist/Collection.js.map +1 -1
- package/dist/DynamoClient.d.ts +80 -28
- package/dist/DynamoClient.d.ts.map +1 -1
- package/dist/DynamoClient.js +125 -25
- package/dist/DynamoClient.js.map +1 -1
- package/dist/Entity.d.ts +99 -40
- package/dist/Entity.d.ts.map +1 -1
- package/dist/Entity.js +693 -302
- package/dist/Entity.js.map +1 -1
- package/dist/EventStore.d.ts +598 -75
- package/dist/EventStore.d.ts.map +1 -1
- package/dist/EventStore.js +852 -221
- package/dist/EventStore.js.map +1 -1
- package/dist/Query.d.ts +100 -14
- package/dist/Query.d.ts.map +1 -1
- package/dist/Query.js +275 -71
- package/dist/Query.js.map +1 -1
- package/dist/StorageEncoding.d.ts +160 -0
- package/dist/StorageEncoding.d.ts.map +1 -0
- package/dist/StorageEncoding.js +258 -0
- package/dist/StorageEncoding.js.map +1 -0
- package/dist/Table.d.ts +56 -6
- package/dist/Table.d.ts.map +1 -1
- package/dist/Table.js +142 -60
- package/dist/Table.js.map +1 -1
- package/dist/Transaction.d.ts +8 -0
- package/dist/Transaction.d.ts.map +1 -1
- package/dist/Transaction.js +72 -22
- package/dist/Transaction.js.map +1 -1
- package/dist/VectorSearchEmulation.d.ts +3 -2
- package/dist/VectorSearchEmulation.d.ts.map +1 -1
- package/dist/VectorSearchEmulation.js +14 -2
- package/dist/VectorSearchEmulation.js.map +1 -1
- package/dist/compression/zlib.d.ts +75 -0
- package/dist/compression/zlib.d.ts.map +1 -0
- package/dist/compression/zlib.js +109 -0
- package/dist/compression/zlib.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/BoundCrud.d.ts +39 -35
- package/dist/internal/BoundCrud.d.ts.map +1 -1
- package/dist/internal/BoundCrud.js.map +1 -1
- package/dist/internal/BoundQuery.d.ts +6 -5
- package/dist/internal/BoundQuery.d.ts.map +1 -1
- package/dist/internal/BoundQuery.js.map +1 -1
- package/dist/internal/BoundVectorQuery.d.ts +17 -1
- package/dist/internal/BoundVectorQuery.d.ts.map +1 -1
- package/dist/internal/BoundVectorQuery.js +31 -6
- package/dist/internal/BoundVectorQuery.js.map +1 -1
- package/dist/internal/EntityCombinators.d.ts +8 -5
- package/dist/internal/EntityCombinators.d.ts.map +1 -1
- package/dist/internal/EntityCombinators.js +24 -6
- package/dist/internal/EntityCombinators.js.map +1 -1
- package/dist/internal/EntityOps.d.ts +1 -1
- package/dist/internal/Expr.d.ts +9 -3
- package/dist/internal/Expr.d.ts.map +1 -1
- package/dist/internal/Expr.js +7 -5
- package/dist/internal/Expr.js.map +1 -1
- package/dist/internal/ExpressionLimits.d.ts +45 -0
- package/dist/internal/ExpressionLimits.d.ts.map +1 -0
- package/dist/internal/ExpressionLimits.js +89 -0
- package/dist/internal/ExpressionLimits.js.map +1 -0
- package/dist/internal/Hydrate.d.ts +62 -0
- package/dist/internal/Hydrate.d.ts.map +1 -0
- package/dist/internal/Hydrate.js +191 -0
- package/dist/internal/Hydrate.js.map +1 -0
- package/dist/internal/ItemSize.d.ts +60 -24
- package/dist/internal/ItemSize.d.ts.map +1 -1
- package/dist/internal/ItemSize.js +122 -39
- package/dist/internal/ItemSize.js.map +1 -1
- package/dist/internal/Packed.d.ts +289 -0
- package/dist/internal/Packed.d.ts.map +1 -0
- package/dist/internal/Packed.js +654 -0
- package/dist/internal/Packed.js.map +1 -0
- package/dist/internal/PathBuilder.d.ts +45 -2
- package/dist/internal/PathBuilder.d.ts.map +1 -1
- package/dist/internal/PathBuilder.js +4 -0
- package/dist/internal/PathBuilder.js.map +1 -1
- package/dist/internal/TransactWriteOps.d.ts +22 -46
- package/dist/internal/TransactWriteOps.d.ts.map +1 -1
- package/dist/internal/TransactWriteOps.js +26 -95
- package/dist/internal/TransactWriteOps.js.map +1 -1
- package/dist/internal/TransactableOps.d.ts +1 -1
- package/dist/internal/TransactableOps.d.ts.map +1 -1
- package/dist/internal/TransactableOps.js +2 -0
- package/dist/internal/TransactableOps.js.map +1 -1
- package/dist/internal/WriteDiagnosis.d.ts +353 -0
- package/dist/internal/WriteDiagnosis.d.ts.map +1 -0
- package/dist/internal/WriteDiagnosis.js +1028 -0
- package/dist/internal/WriteDiagnosis.js.map +1 -0
- package/dist/internal/sparse_check.js +43 -79
- package/dist/internal/sparse_check.js.map +1 -1
- package/package.json +6 -2
package/dist/Aggregate.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* Created via {@link make}. Sub-aggregates compose recursively via `.with()`.
|
|
10
10
|
*/
|
|
11
11
|
import { TypeId as SchemaAggregateTypeId } from "@effect-dynamodb/schema/Aggregate.js";
|
|
12
|
+
import * as DynamoModel from "@effect-dynamodb/schema/DynamoModel.js";
|
|
12
13
|
import type * as DynamoSchemaModule from "@effect-dynamodb/schema/DynamoSchema.js";
|
|
13
14
|
import { AggregateAssemblyError, AggregateDecompositionError, AggregateTransactionOverflow, ConditionalCheckFailed, type ItemNotFound, RefNotFound, TransactionCancelled, ValidationError } from "@effect-dynamodb/schema/Errors.js";
|
|
14
15
|
import type { TimestampsConfig } from "@effect-dynamodb/schema/internal/EntityConfig.js";
|
|
@@ -117,6 +118,12 @@ export interface Aggregate<TSchema extends Schema.Top, TKey extends Record<strin
|
|
|
117
118
|
* `db.tables.X.create()` GSI derivation.
|
|
118
119
|
*/
|
|
119
120
|
readonly _tableTag: Context.Service<TableConfig, TableConfig>;
|
|
121
|
+
/**
|
|
122
|
+
* @internal The packed attributes of each edge entity type's items (stored
|
|
123
|
+
* name → config). `DynamoClient.make()` checks the codecs they name against
|
|
124
|
+
* the registry it binds with (`[EDD-9078]`).
|
|
125
|
+
*/
|
|
126
|
+
readonly _packedByType?: ReadonlyMap<string, Readonly<Record<string, DynamoModel.PackedConfig>>>;
|
|
120
127
|
/** Primary key field name (e.g., "pk") */
|
|
121
128
|
readonly pkField: string;
|
|
122
129
|
/**
|
|
@@ -300,8 +307,11 @@ interface AggregateConfig<_TSchema extends Schema.Top, TEdges extends Record<str
|
|
|
300
307
|
* the RCU cost of every assembly read.
|
|
301
308
|
*
|
|
302
309
|
* Only valid where the assembly query can be strongly consistent: the base table (no
|
|
303
|
-
* `collection.index`) or an LSI. DynamoDB rejects consistent reads against a GSI
|
|
304
|
-
*
|
|
310
|
+
* `collection.index`) or an LSI. DynamoDB rejects consistent reads against a GSI; with
|
|
311
|
+
* a GSI collection index, each assembly read fails with that rejection, named for the
|
|
312
|
+
* aggregate and the index (a `ValidationError`, `operation: "<op>.consistentRead"`;
|
|
313
|
+
* `delete`'s read keeps DynamoDB's `DynamoValidationError` tag). It is not refused at
|
|
314
|
+
* `make()` time (#144).
|
|
305
315
|
*/
|
|
306
316
|
readonly consistentRead?: boolean;
|
|
307
317
|
readonly context?: ReadonlyArray<string>;
|
package/dist/Aggregate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Aggregate.d.ts","sourceRoot":"","sources":["../src/Aggregate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,MAAM,IAAI,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;
|
|
1
|
+
{"version":3,"file":"Aggregate.d.ts","sourceRoot":"","sources":["../src/Aggregate.ts"],"names":[],"mappings":"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"}
|
package/dist/Aggregate.js
CHANGED
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
import { TypeId as SchemaAggregateTypeId } from "@effect-dynamodb/schema/Aggregate.js";
|
|
12
12
|
import * as DynamoModel from "@effect-dynamodb/schema/DynamoModel.js";
|
|
13
13
|
import { composeCollectionKey, composeKey } from "@effect-dynamodb/schema/DynamoSchema.js";
|
|
14
|
-
import { AggregateAssemblyError, AggregateDecompositionError, AggregateTransactionOverflow, ConditionalCheckFailed, DynamoError, RefNotFound, TransactionCancelled, ValidationError, } from "@effect-dynamodb/schema/Errors.js";
|
|
14
|
+
import { AggregateAssemblyError, AggregateDecompositionError, AggregateTransactionOverflow, ConditionalCheckFailed, DynamoError, DynamoValidationError, RefNotFound, TransactionCancelled, ValidationError, } from "@effect-dynamodb/schema/Errors.js";
|
|
15
15
|
import { makeCompositeKeyForm, toCompositeKeyRecord, } from "@effect-dynamodb/schema/internal/CompositeCodec.js";
|
|
16
16
|
import { buildDateTransform, containsWireTransform, matchDateRepresentation, optionalField, resolveSystemFields, substituteSchemaDeep, validateNoTransformOverride, } from "@effect-dynamodb/schema/internal/EntitySchemas.js";
|
|
17
|
+
import { isDispatchEligible } from "@effect-dynamodb/schema/internal/PackedCodec.js";
|
|
17
18
|
import { extractArrayElement, getSchemaFields, hasEncodingTransformation, } from "@effect-dynamodb/schema/internal/SchemaAccessors.js";
|
|
18
19
|
import * as KeyComposer from "@effect-dynamodb/schema/KeyComposer.js";
|
|
19
20
|
import { DateTime, Effect, Equal, Option, Schema, SchemaAST, } from "effect";
|
|
@@ -21,8 +22,10 @@ import * as Batch from "./Batch.js";
|
|
|
21
22
|
import { DynamoClient } from "./DynamoClient.js";
|
|
22
23
|
import { fromDefinition as entityFromDefinition } from "./Entity.js";
|
|
23
24
|
import { compileExpr, createConditionOps, emptyPartProblem, isEmptyExpr, parseSimpleShorthand, } from "./internal/Expr.js";
|
|
25
|
+
import { hasStoredForm, packItemSync, unpackItemSync, withRegistry } from "./internal/Packed.js";
|
|
24
26
|
import { createPathBuilder } from "./internal/PathBuilder.js";
|
|
25
27
|
import { generateTimestampPrimitive } from "./internal/TransactableOps.js";
|
|
28
|
+
import { diagnosedValidationError, diagnoseFailure, explainDiagnosis, mergeKeyAttributes, transactItemTarget, withCause, } from "./internal/WriteDiagnosis.js";
|
|
26
29
|
import { fromAttributeMap, toAttributeMap, toAttributeValue } from "./Marshaller.js";
|
|
27
30
|
import { resolvePrimaryKey } from "./Table.js";
|
|
28
31
|
// Internal modules (decomposed from Aggregate.ts)
|
|
@@ -443,6 +446,23 @@ const applyNodeAttrEncoders = (attrs, encoders) => {
|
|
|
443
446
|
// ---------------------------------------------------------------------------
|
|
444
447
|
const makeAggregate = (schema, config) => {
|
|
445
448
|
const aggregateName = config.root.entityType;
|
|
449
|
+
const groupKeys = {
|
|
450
|
+
pkField: config.pk.field,
|
|
451
|
+
// Every key attribute an aggregate item can carry: `pk` / `sk`, the
|
|
452
|
+
// collection index's PK and SK mirror, and the list index's (root items).
|
|
453
|
+
keys: mergeKeyAttributes({
|
|
454
|
+
partition: [
|
|
455
|
+
"pk",
|
|
456
|
+
config.pk.field,
|
|
457
|
+
...(config.list === undefined ? [] : [config.list.pk.field]),
|
|
458
|
+
],
|
|
459
|
+
sort: [
|
|
460
|
+
"sk",
|
|
461
|
+
...(config.collection.sk === undefined ? [] : [config.collection.sk.field]),
|
|
462
|
+
...(config.list === undefined ? [] : [config.list.sk.field]),
|
|
463
|
+
],
|
|
464
|
+
}),
|
|
465
|
+
};
|
|
446
466
|
const contextFields = config.context ?? [];
|
|
447
467
|
// Policy: aggregate fields that already carry a date transform schema
|
|
448
468
|
// (e.g. `Schema.DateTimeUtcFromString`) cannot be combined with a
|
|
@@ -474,6 +494,9 @@ const makeAggregate = (schema, config) => {
|
|
|
474
494
|
parentFields: schemaFields,
|
|
475
495
|
resolveRef,
|
|
476
496
|
});
|
|
497
|
+
// Packed edge attributes, and those read by stored form, by item entity type (#147).
|
|
498
|
+
const packedByType = collectPackedByType(rootNode);
|
|
499
|
+
const dispatchByType = collectDispatchByType(rootNode);
|
|
477
500
|
// Attribute ENCODERS for the root item: decompose works from the
|
|
478
501
|
// schema-decoded domain object, so every root attribute holding a wire
|
|
479
502
|
// transform — a date, a transformed scalar, or a container of either — is
|
|
@@ -508,14 +531,13 @@ const makeAggregate = (schema, config) => {
|
|
|
508
531
|
resolveRef,
|
|
509
532
|
enforceChecks: true,
|
|
510
533
|
});
|
|
511
|
-
// Validate the collection index
|
|
512
|
-
//
|
|
513
|
-
//
|
|
534
|
+
// Validate the collection index against the table's primary key. Best effort: a
|
|
535
|
+
// table registering only aggregates has no primary key to resolve, in which case
|
|
536
|
+
// the check is skipped rather than guessed at. `consistentRead` on a GSI collection
|
|
537
|
+
// index is not refused here: DynamoDB rejects the consistent read, and the
|
|
538
|
+
// assembly read diagnoses it, naming the aggregate and the index (#144).
|
|
514
539
|
const consistentRead = config.consistentRead ?? false;
|
|
515
540
|
const tablePrimary = resolvePrimaryKey(config.table.entities);
|
|
516
|
-
const collectionIsGsi = tablePrimary !== undefined &&
|
|
517
|
-
config.collection.index !== undefined &&
|
|
518
|
-
config.pk.field !== tablePrimary.pk;
|
|
519
541
|
if (tablePrimary !== undefined && config.collection.index === undefined) {
|
|
520
542
|
if (config.pk.field !== tablePrimary.pk) {
|
|
521
543
|
throw new Error(`[EDD-9041] Aggregate "${aggregateName}": collection PK field "${config.pk.field}" is ` +
|
|
@@ -530,14 +552,6 @@ const makeAggregate = (schema, config) => {
|
|
|
530
552
|
`sort by — declare both to assemble through an index, or neither to assemble off the ` +
|
|
531
553
|
`base table.`);
|
|
532
554
|
}
|
|
533
|
-
if (consistentRead && collectionIsGsi) {
|
|
534
|
-
throw new Error(`[EDD-9042] Aggregate "${aggregateName}": \`consistentRead\` is not supported against a ` +
|
|
535
|
-
`GSI. Collection index "${config.collection.index}" is partitioned by ` +
|
|
536
|
-
`"${config.pk.field}" rather than the table's primary partition key ` +
|
|
537
|
-
`("${tablePrimary?.pk}"), which makes it a GSI, and DynamoDB serves GSI reads as ` +
|
|
538
|
-
`eventually consistent only. Drop \`consistentRead\`, or key the aggregate on the ` +
|
|
539
|
-
`table's primary partition key so assembly reads the base table.`);
|
|
540
|
-
}
|
|
541
555
|
// System timestamps (#98). Aggregates compose their DynamoDB items directly
|
|
542
556
|
// rather than routing through Entity write ops, so Entity's timestamp
|
|
543
557
|
// machinery never reached these rows. Resolution goes through the SAME
|
|
@@ -622,20 +636,38 @@ const makeAggregate = (schema, config) => {
|
|
|
622
636
|
}
|
|
623
637
|
return toCompositeKeyRecord(compositeKeyForm, composites);
|
|
624
638
|
};
|
|
625
|
-
/**
|
|
626
|
-
|
|
639
|
+
/**
|
|
640
|
+
* DynamoDB's rejection of an assembly read (#144) — a consistent read of a
|
|
641
|
+
* GSI collection index — explained for the aggregate and its index.
|
|
642
|
+
*/
|
|
643
|
+
const assemblyReadCause = (operation, diagnosis, original) => withCause(`Aggregate "${aggregateName}" ${operation}: ${explainDiagnosis(diagnosis, operation)} ` +
|
|
644
|
+
"Drop `consistentRead` from the aggregate, or key it on the table's primary " +
|
|
645
|
+
"partition key so assembly reads the base table.", original);
|
|
646
|
+
/** Compose the PK and query all items; a rejected read fails as `rejected` names it. */
|
|
647
|
+
const readPartition = (key, rejected) => Effect.flatMap(readPartitionRaw(key, rejected), (read) =>
|
|
648
|
+
// Packed edge attributes, read by their stored form (#147).
|
|
649
|
+
Effect.as(unpackAggregateItems(read.allItems, dispatchByType, aggregateName), read));
|
|
650
|
+
/** {@link readPartition} without unpacking — `delete` needs only the keys. */
|
|
651
|
+
const readPartitionRaw = (key, rejected) => Effect.gen(function* () {
|
|
627
652
|
const client = yield* DynamoClient;
|
|
628
653
|
const tableConfig = yield* config.table.Tag;
|
|
629
654
|
const composites = KeyComposer.extractComposites(config.pk.composite, keyRecord(key));
|
|
630
655
|
const pkValue = composeCollectionKey(config.schema, config.collection.name, composites);
|
|
631
|
-
const allItems = yield* queryAllItems(client, tableConfig.name, config.collection.index, config.pk.field, pkValue, consistentRead);
|
|
656
|
+
const allItems = yield* queryAllItems(client, tableConfig.name, config.collection.index, config.pk.field, pkValue, consistentRead, rejected);
|
|
632
657
|
return { client, tableConfig, pkValue, composites, allItems };
|
|
633
658
|
});
|
|
659
|
+
/** Shared: compose PK and query all items, a rejected read a `ValidationError`. */
|
|
660
|
+
const fetchPartition = (key, operation) => readPartition(key, (diagnosis, original) => new ValidationError({
|
|
661
|
+
entityType: aggregateName,
|
|
662
|
+
operation: `${operation}.consistentRead`,
|
|
663
|
+
cause: assemblyReadCause(operation, diagnosis, original),
|
|
664
|
+
}));
|
|
634
665
|
const derivedSchemas = deriveAggregateSchemas(schema, config.edges, config.pk.composite);
|
|
635
666
|
return {
|
|
636
667
|
[TypeId]: TypeId,
|
|
637
668
|
_tag: "Aggregate",
|
|
638
669
|
_tableTag: config.table.Tag,
|
|
670
|
+
_packedByType: packedByType,
|
|
639
671
|
schema,
|
|
640
672
|
pkField: config.pk.field,
|
|
641
673
|
collection: {
|
|
@@ -654,7 +686,7 @@ const makeAggregate = (schema, config) => {
|
|
|
654
686
|
createSchema: derivedSchemas.inputSchema,
|
|
655
687
|
updateSchema: derivedSchemas.updateSchema,
|
|
656
688
|
get: (key) => Effect.gen(function* () {
|
|
657
|
-
const { allItems } = yield* fetchPartition(key);
|
|
689
|
+
const { allItems } = yield* fetchPartition(key, "get");
|
|
658
690
|
if (allItems.length === 0) {
|
|
659
691
|
return yield* new AggregateAssemblyError({
|
|
660
692
|
aggregate: aggregateName,
|
|
@@ -692,7 +724,7 @@ const makeAggregate = (schema, config) => {
|
|
|
692
724
|
// 6. Build DynamoDB items with composed keys (create only ever PUTs).
|
|
693
725
|
// Every row is new, so `created` has nothing to carry forward.
|
|
694
726
|
const now = yield* DateTime.now;
|
|
695
|
-
const dynamoGroups = buildDynamoItems(groups, config, pkValue, collectionSkComposites, keyRecord, stampFor(now, EMPTY_CREATED));
|
|
727
|
+
const dynamoGroups = buildDynamoItems(yield* packAggregateGroups(groups, packedByType, aggregateName, "create"), config, pkValue, collectionSkComposites, keyRecord, stampFor(now, EMPTY_CREATED));
|
|
696
728
|
// 7. Write via sub-aggregate transactions. The root item goes first in
|
|
697
729
|
// the first transaction, guarded by `attribute_not_exists` (#134):
|
|
698
730
|
// an existing aggregate cancels it before any edge or sub-aggregate
|
|
@@ -707,7 +739,7 @@ const makeAggregate = (schema, config) => {
|
|
|
707
739
|
puts: [...g.items].sort((a, b) => Number(isRoot(b)) - Number(isRoot(a))),
|
|
708
740
|
deletes: [],
|
|
709
741
|
}));
|
|
710
|
-
yield* writeTransactionGroups(client, tableConfig.name, ordered, aggregateName, {
|
|
742
|
+
yield* writeTransactionGroups(client, tableConfig.name, ordered, aggregateName, groupKeys, {
|
|
711
743
|
pkField: config.pk.field,
|
|
712
744
|
entityType: config.root.entityType,
|
|
713
745
|
key: { [config.pk.field]: pkValue, sk: rootSk },
|
|
@@ -715,7 +747,7 @@ const makeAggregate = (schema, config) => {
|
|
|
715
747
|
return decoded;
|
|
716
748
|
}),
|
|
717
749
|
update: (key, mutationFn) => Effect.gen(function* () {
|
|
718
|
-
const { client, tableConfig, pkValue, allItems } = yield* fetchPartition(key);
|
|
750
|
+
const { client, tableConfig, pkValue, allItems } = yield* fetchPartition(key, "update");
|
|
719
751
|
if (allItems.length === 0) {
|
|
720
752
|
return yield* new AggregateAssemblyError({
|
|
721
753
|
aggregate: aggregateName,
|
|
@@ -775,7 +807,9 @@ const makeAggregate = (schema, config) => {
|
|
|
775
807
|
// PUTs and would otherwise clobber it. The old set is only mined for
|
|
776
808
|
// (pk, sk) delete keys, so it is never stamped.
|
|
777
809
|
const now = yield* DateTime.now;
|
|
778
|
-
|
|
810
|
+
// Packed AFTER the group diff above compared the decomposed values, so
|
|
811
|
+
// an unchanged packed value is never rewritten (#147).
|
|
812
|
+
const newDynamo = buildDynamoItems(yield* packAggregateGroups(newGroups, packedByType, aggregateName, "update"), config, pkValue, KeyComposer.extractComposites(config.collection.sk?.composite ?? [], keyRecord(assembledNew)), keyRecord, stampFor(now, readExistingCreated(allItems)));
|
|
779
813
|
const oldDynamo = buildDynamoItems(oldGroups, config, pkValue, KeyComposer.extractComposites(config.collection.sk?.composite ?? [], keyRecord(assembledOld)), keyRecord);
|
|
780
814
|
const skOf = (item) => item.sk?.S ?? "";
|
|
781
815
|
const newItemsByGroup = new Map(newDynamo.map((g) => [g.group, g.items]));
|
|
@@ -811,12 +845,18 @@ const makeAggregate = (schema, config) => {
|
|
|
811
845
|
groupWrites.push({ group: name, puts, deletes });
|
|
812
846
|
}
|
|
813
847
|
if (groupWrites.length > 0) {
|
|
814
|
-
yield* writeTransactionGroups(client, tableConfig.name, groupWrites, aggregateName);
|
|
848
|
+
yield* writeTransactionGroups(client, tableConfig.name, groupWrites, aggregateName, groupKeys);
|
|
815
849
|
}
|
|
816
850
|
return decoded;
|
|
817
851
|
}),
|
|
818
852
|
delete: (key) => Effect.gen(function* () {
|
|
819
|
-
const { client, tableConfig, allItems } = yield*
|
|
853
|
+
const { client, tableConfig, allItems } = yield* readPartitionRaw(key,
|
|
854
|
+
// `delete` declares no `ValidationError`: a rejected read keeps
|
|
855
|
+
// DynamoDB's own tag, its message explained.
|
|
856
|
+
(diagnosis, original) => new DynamoValidationError({
|
|
857
|
+
operation: "Query",
|
|
858
|
+
cause: assemblyReadCause("delete", diagnosis, original),
|
|
859
|
+
}));
|
|
820
860
|
if (allItems.length === 0) {
|
|
821
861
|
return yield* new AggregateAssemblyError({
|
|
822
862
|
aggregate: aggregateName,
|
|
@@ -908,7 +948,7 @@ const makeAggregate = (schema, config) => {
|
|
|
908
948
|
accept: options?.filterBy,
|
|
909
949
|
reverse: options?.reverse === true,
|
|
910
950
|
keyFields: listKeyFields,
|
|
911
|
-
});
|
|
951
|
+
}, aggregateName);
|
|
912
952
|
});
|
|
913
953
|
const shardResults = yield* Effect.all(shardQueries);
|
|
914
954
|
const merged = shardResults.flatMap((r) => r.items);
|
|
@@ -932,7 +972,7 @@ const makeAggregate = (schema, config) => {
|
|
|
932
972
|
accept: options?.filterBy,
|
|
933
973
|
reverse: options?.reverse === true,
|
|
934
974
|
keyFields: listKeyFields,
|
|
935
|
-
});
|
|
975
|
+
}, aggregateName);
|
|
936
976
|
rootItems = result.items;
|
|
937
977
|
nextKey = result.lastKey;
|
|
938
978
|
}
|
|
@@ -944,7 +984,7 @@ const makeAggregate = (schema, config) => {
|
|
|
944
984
|
key[composite] = rootItem[composite];
|
|
945
985
|
}
|
|
946
986
|
const assembled = yield* Effect.gen(function* () {
|
|
947
|
-
const { allItems } = yield* fetchPartition(key);
|
|
987
|
+
const { allItems } = yield* fetchPartition(key, "list");
|
|
948
988
|
if (allItems.length === 0)
|
|
949
989
|
return undefined;
|
|
950
990
|
const result = yield* assembleAggregate(decodeSchema, rootNode, allItems, contextFields, key, aggregateName, systemAttrs);
|
|
@@ -1020,6 +1060,56 @@ const deepEqualGroups = (a, b) => {
|
|
|
1020
1060
|
return deepEqual(item.attributes, other.attributes);
|
|
1021
1061
|
});
|
|
1022
1062
|
};
|
|
1063
|
+
/**
|
|
1064
|
+
* The edge entity's packed fields that are top-level attributes of the node's
|
|
1065
|
+
* items (`itemFields`), refusing one that is a discriminator or a declared
|
|
1066
|
+
* sort-key composite (`[EDD-9072]`): those are compared and composed as scalar
|
|
1067
|
+
* stored values.
|
|
1068
|
+
*/
|
|
1069
|
+
const edgePackedFields = (edgeName, entity, itemFields, discriminator, skComposite) => {
|
|
1070
|
+
if (entity === undefined)
|
|
1071
|
+
return undefined;
|
|
1072
|
+
const configured = DynamoModel.getPackedFields(entity.model);
|
|
1073
|
+
const packed = {};
|
|
1074
|
+
for (const [field, storedAs] of Object.entries(configured)) {
|
|
1075
|
+
if (itemFields !== undefined && !(field in itemFields))
|
|
1076
|
+
continue;
|
|
1077
|
+
if (discriminator !== undefined && field in discriminator) {
|
|
1078
|
+
throw new Error(`[EDD-9072] Aggregate edge "${edgeName}": discriminator "${field}" is a packed field of ` +
|
|
1079
|
+
`entity "${entity.entityType}". A discriminator is matched as a scalar stored value.`);
|
|
1080
|
+
}
|
|
1081
|
+
if ((skComposite ?? []).some((path) => path.split(".")[0] === field)) {
|
|
1082
|
+
throw new Error(`[EDD-9072] Aggregate edge "${edgeName}": sort-key composite "${field}" is a packed field ` +
|
|
1083
|
+
`of entity "${entity.entityType}". Key composites must be scalar model values.`);
|
|
1084
|
+
}
|
|
1085
|
+
packed[field] = storedAs;
|
|
1086
|
+
}
|
|
1087
|
+
return Object.keys(packed).length > 0 ? packed : undefined;
|
|
1088
|
+
};
|
|
1089
|
+
/**
|
|
1090
|
+
* The attributes of an edge entity's items a reader dispatches on by stored
|
|
1091
|
+
* form: its packed ones, plus every non-sparse field of the item whose encoded
|
|
1092
|
+
* side is object- or array-shaped (a native value of it is never `S` or `B`) —
|
|
1093
|
+
* the edge counterpart of an entity's `dispatchAttributes`.
|
|
1094
|
+
*/
|
|
1095
|
+
const edgeDispatchFields = (entity, itemFields, packed) => {
|
|
1096
|
+
if (entity === undefined)
|
|
1097
|
+
return undefined;
|
|
1098
|
+
const sparse = DynamoModel.getSparseFields(entity.model);
|
|
1099
|
+
const dispatch = new Set(Object.keys(packed ?? {}));
|
|
1100
|
+
for (const [field, schema] of Object.entries(itemFields ?? {})) {
|
|
1101
|
+
if (field in sparse)
|
|
1102
|
+
continue;
|
|
1103
|
+
if (isDispatchEligible(schema))
|
|
1104
|
+
dispatch.add(field);
|
|
1105
|
+
}
|
|
1106
|
+
return dispatch.size > 0 ? [...dispatch] : undefined;
|
|
1107
|
+
};
|
|
1108
|
+
/** A node's packed and stored-form-dispatch attributes, as `resolveNode` args. */
|
|
1109
|
+
const edgeStorage = (packed, entity, itemFields) => ({
|
|
1110
|
+
packed,
|
|
1111
|
+
dispatch: edgeDispatchFields(entity, itemFields, packed),
|
|
1112
|
+
});
|
|
1023
1113
|
const resolveNode = (args) => {
|
|
1024
1114
|
const { assemble, cardinality, attrEncoders, decompose, discriminator, edges, entityType, fieldName, keyAttrEncoders, ownDiscriminator, parentFields, refIdentifierField, resolveRef, skComposite, } = args;
|
|
1025
1115
|
const children = [];
|
|
@@ -1046,6 +1136,7 @@ const resolveNode = (args) => {
|
|
|
1046
1136
|
discriminator: mergedDisc,
|
|
1047
1137
|
ownDiscriminator: edge.discriminator,
|
|
1048
1138
|
attrEncoders: buildAttrEncoders(valueFields, resolveRef),
|
|
1139
|
+
...edgeStorage(edgePackedFields(field, edge.entity, valueFields, edge.discriminator, undefined), edge.entity, valueFields),
|
|
1049
1140
|
}));
|
|
1050
1141
|
}
|
|
1051
1142
|
else if (edge._tag === "ManyEdge") {
|
|
@@ -1075,6 +1166,7 @@ const resolveNode = (args) => {
|
|
|
1075
1166
|
refIdentifierField: edge.entity
|
|
1076
1167
|
? DynamoModel.getIdentifierField(edge.entity.model)?.name
|
|
1077
1168
|
: undefined,
|
|
1169
|
+
...edgeStorage(edgePackedFields(field, edge.entity, elementFields, discriminator, edge.sk?.composite), edge.entity, elementFields),
|
|
1078
1170
|
}));
|
|
1079
1171
|
}
|
|
1080
1172
|
else if (edge._tag === "BoundSubAggregate") {
|
|
@@ -1118,21 +1210,105 @@ const resolveNode = (args) => {
|
|
|
1118
1210
|
...(decompose !== undefined && { decompose }),
|
|
1119
1211
|
...(skComposite !== undefined && { skComposite }),
|
|
1120
1212
|
...(refIdentifierField !== undefined && { refIdentifierField }),
|
|
1213
|
+
...(args.packed !== undefined && { packed: args.packed }),
|
|
1214
|
+
...(args.dispatch !== undefined && { dispatch: args.dispatch }),
|
|
1121
1215
|
attrEncoders,
|
|
1122
1216
|
...(keyAttrEncoders !== undefined && { keyAttrEncoders }),
|
|
1123
1217
|
};
|
|
1124
1218
|
};
|
|
1125
1219
|
// ---------------------------------------------------------------------------
|
|
1220
|
+
// Internal: packed edge attributes (#147)
|
|
1221
|
+
// ---------------------------------------------------------------------------
|
|
1222
|
+
/** Every node's packed attributes, by the entity type of its items. */
|
|
1223
|
+
const collectPackedByType = (root) => {
|
|
1224
|
+
const out = new Map();
|
|
1225
|
+
const walk = (node) => {
|
|
1226
|
+
if (node.packed !== undefined) {
|
|
1227
|
+
out.set(node.entityType, { ...(out.get(node.entityType) ?? {}), ...node.packed });
|
|
1228
|
+
}
|
|
1229
|
+
for (const child of node.children)
|
|
1230
|
+
walk(child);
|
|
1231
|
+
};
|
|
1232
|
+
walk(root);
|
|
1233
|
+
return out;
|
|
1234
|
+
};
|
|
1235
|
+
/** Every node's stored-form-dispatch attributes, by the entity type of its items. */
|
|
1236
|
+
const collectDispatchByType = (root) => {
|
|
1237
|
+
const out = new Map();
|
|
1238
|
+
const walk = (node) => {
|
|
1239
|
+
if (node.dispatch !== undefined) {
|
|
1240
|
+
const attrs = out.get(node.entityType) ?? new Set();
|
|
1241
|
+
for (const attr of node.dispatch)
|
|
1242
|
+
attrs.add(attr);
|
|
1243
|
+
out.set(node.entityType, attrs);
|
|
1244
|
+
}
|
|
1245
|
+
for (const child of node.children)
|
|
1246
|
+
walk(child);
|
|
1247
|
+
};
|
|
1248
|
+
walk(root);
|
|
1249
|
+
return new Map([...out].map(([type, attrs]) => [type, [...attrs]]));
|
|
1250
|
+
};
|
|
1251
|
+
/** The decomposed groups with each item's packed attributes in their stored form. */
|
|
1252
|
+
const packAggregateGroups = (groups, packedByType, aggregateName, operation) => packedByType.size === 0
|
|
1253
|
+
? Effect.succeed(groups)
|
|
1254
|
+
: withRegistry((registry) => {
|
|
1255
|
+
const packedGroups = [];
|
|
1256
|
+
for (const group of groups) {
|
|
1257
|
+
const items = [];
|
|
1258
|
+
for (const item of group.items) {
|
|
1259
|
+
const packed = packedByType.get(item.entityType);
|
|
1260
|
+
if (packed === undefined) {
|
|
1261
|
+
items.push(item);
|
|
1262
|
+
continue;
|
|
1263
|
+
}
|
|
1264
|
+
const attributes = { ...item.attributes };
|
|
1265
|
+
const failure = packItemSync(attributes, packed, registry, {
|
|
1266
|
+
entityType: `${aggregateName}.${item.member}`,
|
|
1267
|
+
operation: `aggregate.${operation}.packed`,
|
|
1268
|
+
});
|
|
1269
|
+
if (failure !== undefined)
|
|
1270
|
+
return failure;
|
|
1271
|
+
items.push({ ...item, attributes });
|
|
1272
|
+
}
|
|
1273
|
+
packedGroups.push({ ...group, items });
|
|
1274
|
+
}
|
|
1275
|
+
return packedGroups;
|
|
1276
|
+
});
|
|
1277
|
+
/**
|
|
1278
|
+
* Read, in place, each item's dispatch attributes by their stored form — by
|
|
1279
|
+
* what is stored, whatever the edge entity configures now. A row with no
|
|
1280
|
+
* string or binary value on any of them costs one `typeof` per attribute.
|
|
1281
|
+
* One Effect for the whole partition; the work inside is synchronous.
|
|
1282
|
+
*/
|
|
1283
|
+
const unpackAggregateItems = (items, dispatchByType, aggregateName) => dispatchByType.size === 0
|
|
1284
|
+
? Effect.void
|
|
1285
|
+
: withRegistry((registry) => {
|
|
1286
|
+
for (const item of items) {
|
|
1287
|
+
const dispatch = dispatchByType.get(item.__edd_e__);
|
|
1288
|
+
if (dispatch === undefined || !hasStoredForm(item, dispatch))
|
|
1289
|
+
continue;
|
|
1290
|
+
const failure = unpackItemSync(item, dispatch, registry, {
|
|
1291
|
+
entityType: `${aggregateName}.${String(item.__edd_e__)}`,
|
|
1292
|
+
operation: "aggregate.decode",
|
|
1293
|
+
});
|
|
1294
|
+
if (failure !== undefined)
|
|
1295
|
+
return failure;
|
|
1296
|
+
}
|
|
1297
|
+
return undefined;
|
|
1298
|
+
});
|
|
1299
|
+
// ---------------------------------------------------------------------------
|
|
1126
1300
|
// Internal: Query all items in the aggregate partition
|
|
1127
1301
|
// ---------------------------------------------------------------------------
|
|
1128
|
-
const queryAllItems = (client, tableName, indexName, pkField, pkValue, consistentRead
|
|
1302
|
+
const queryAllItems = (client, tableName, indexName, pkField, pkValue, consistentRead,
|
|
1303
|
+
/** A consistent read of a GSI, which DynamoDB rejects, as the caller names it (#144). */
|
|
1304
|
+
consistentReadRejected) => Effect.gen(function* () {
|
|
1129
1305
|
const allItems = [];
|
|
1130
1306
|
let exclusiveStartKey;
|
|
1131
1307
|
// Paginate through all results. Assembly needs every item in the partition and
|
|
1132
1308
|
// discriminates them by `__edd_e__`, so there is no sort-key condition and no
|
|
1133
1309
|
// ordering requirement — which is why `IndexName` is optional here.
|
|
1134
1310
|
do {
|
|
1135
|
-
const
|
|
1311
|
+
const input = {
|
|
1136
1312
|
TableName: tableName,
|
|
1137
1313
|
IndexName: indexName,
|
|
1138
1314
|
KeyConditionExpression: "#pk = :pk",
|
|
@@ -1140,7 +1316,12 @@ const queryAllItems = (client, tableName, indexName, pkField, pkValue, consisten
|
|
|
1140
1316
|
ExpressionAttributeValues: { ":pk": toAttributeValue(pkValue) },
|
|
1141
1317
|
ExclusiveStartKey: exclusiveStartKey,
|
|
1142
1318
|
ConsistentRead: consistentRead || undefined,
|
|
1143
|
-
}
|
|
1319
|
+
};
|
|
1320
|
+
const result = yield* client.query(input).pipe(diagnoseFailure({
|
|
1321
|
+
sent: () => ({ _tag: "Read", scan: false, input }),
|
|
1322
|
+
only: ["ConsistentReadOnGsi"],
|
|
1323
|
+
rewrite: consistentReadRejected,
|
|
1324
|
+
}));
|
|
1144
1325
|
if (result.Items) {
|
|
1145
1326
|
for (const item of result.Items) {
|
|
1146
1327
|
allItems.push(fromAttributeMap(item));
|
|
@@ -1996,24 +2177,14 @@ keyRecord, stamp) => groups.map((group) => ({
|
|
|
1996
2177
|
return toAttributeMap(attrs);
|
|
1997
2178
|
}),
|
|
1998
2179
|
}));
|
|
1999
|
-
function writeTransactionGroups(client, tableName, groups, aggregateName, rootGuard) {
|
|
2180
|
+
function writeTransactionGroups(client, tableName, groups, aggregateName, keys, rootGuard) {
|
|
2000
2181
|
return Effect.gen(function* () {
|
|
2001
|
-
//
|
|
2002
|
-
//
|
|
2003
|
-
//
|
|
2004
|
-
//
|
|
2005
|
-
//
|
|
2006
|
-
|
|
2007
|
-
const itemCount = group.puts.length + group.deletes.length;
|
|
2008
|
-
if (itemCount > 100) {
|
|
2009
|
-
return yield* new AggregateTransactionOverflow({
|
|
2010
|
-
aggregate: aggregateName,
|
|
2011
|
-
subgraph: group.group,
|
|
2012
|
-
itemCount,
|
|
2013
|
-
limit: 100,
|
|
2014
|
-
});
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2182
|
+
// Each group is its own transaction (DynamoDB: 100 items per
|
|
2183
|
+
// TransactWriteItems, Puts AND Deletes together). DynamoDB's limits are
|
|
2184
|
+
// not pre-checked (#144): a group over one is sent, rejected whole, and
|
|
2185
|
+
// diagnosed — naming the sub-aggregates earlier groups already committed,
|
|
2186
|
+
// since a multi-group write was never atomic.
|
|
2187
|
+
const committed = [];
|
|
2017
2188
|
for (const [groupIndex, group] of groups.entries()) {
|
|
2018
2189
|
// Each group is one transaction — Puts AND Deletes applied atomically, so an
|
|
2019
2190
|
// edge add and a sibling edge removal in the same sub-aggregate commit together.
|
|
@@ -2040,7 +2211,45 @@ function writeTransactionGroups(client, tableName, groups, aggregateName, rootGu
|
|
|
2040
2211
|
})),
|
|
2041
2212
|
...group.deletes.map((key) => ({ Delete: { TableName: tableName, Key: key } })),
|
|
2042
2213
|
];
|
|
2043
|
-
|
|
2214
|
+
// Named once, for both diagnoses below.
|
|
2215
|
+
const sent = () => ({
|
|
2216
|
+
_tag: "Transaction",
|
|
2217
|
+
items: transactItems,
|
|
2218
|
+
targets: () => transactItems.map((item) => {
|
|
2219
|
+
const entityType = item.Put?.Item?.__edd_e__?.S ?? aggregateName;
|
|
2220
|
+
return transactItemTarget(item, tableName, [keys.pkField, "sk"], entityType, `the ${item.Put !== undefined ? entityType : "removed"} item of ` +
|
|
2221
|
+
`sub-aggregate "${group.group}"`);
|
|
2222
|
+
}),
|
|
2223
|
+
});
|
|
2224
|
+
const already = [...committed];
|
|
2225
|
+
const diagnosed = (diagnosis, original) => diagnosis._tag === "TooManyItems"
|
|
2226
|
+
? new AggregateTransactionOverflow({
|
|
2227
|
+
aggregate: aggregateName,
|
|
2228
|
+
subgraph: group.group,
|
|
2229
|
+
itemCount: diagnosis.count,
|
|
2230
|
+
limit: diagnosis.limit,
|
|
2231
|
+
committed: already,
|
|
2232
|
+
operation: rootGuard !== undefined ? "create" : "update",
|
|
2233
|
+
cause: original,
|
|
2234
|
+
})
|
|
2235
|
+
: diagnosedValidationError(diagnosis, original, {
|
|
2236
|
+
operation: `Aggregate.write(${aggregateName})`,
|
|
2237
|
+
entityType: aggregateName,
|
|
2238
|
+
// Each group commits on its own: say exactly what already did.
|
|
2239
|
+
...(already.length > 0 && {
|
|
2240
|
+
committed: `earlier transactions of this write already committed sub-aggregates ` +
|
|
2241
|
+
already.map((name) => `"${name}"`).join(", ") +
|
|
2242
|
+
(rootGuard !== undefined
|
|
2243
|
+
? ", so a retried create fails on the existing root: update or delete " +
|
|
2244
|
+
"the partial aggregate instead"
|
|
2245
|
+
: ""),
|
|
2246
|
+
}),
|
|
2247
|
+
});
|
|
2248
|
+
yield* client.transactWriteItems({ TransactItems: transactItems }).pipe(
|
|
2249
|
+
// One operation per item (#144): diagnosed BEFORE the root guard's
|
|
2250
|
+
// mapping, which would read a repeat's conditional failure as an
|
|
2251
|
+
// existing root.
|
|
2252
|
+
diagnoseFailure({ sent, only: ["RepeatedItem"], rewrite: diagnosed }), Effect.mapError((error) => {
|
|
2044
2253
|
if (error.cause != null &&
|
|
2045
2254
|
typeof error.cause === "object" &&
|
|
2046
2255
|
"name" in error.cause &&
|
|
@@ -2064,7 +2273,11 @@ function writeTransactionGroups(client, tableName, groups, aggregateName, rootGu
|
|
|
2064
2273
|
});
|
|
2065
2274
|
}
|
|
2066
2275
|
return error;
|
|
2067
|
-
})
|
|
2276
|
+
}),
|
|
2277
|
+
// A limit DynamoDB enforced (#144), named — after the root guard's
|
|
2278
|
+
// mapping, which a conditional failure always takes.
|
|
2279
|
+
diagnoseFailure({ sent, keys: () => keys.keys, rewrite: diagnosed }));
|
|
2280
|
+
committed.push(group.group);
|
|
2068
2281
|
}
|
|
2069
2282
|
});
|
|
2070
2283
|
}
|
|
@@ -2089,6 +2302,9 @@ const deleteAllItems = (client, tableName, allItems, pkField) => Effect.gen(func
|
|
|
2089
2302
|
let pending = deleteRequests.slice(i, i + 25);
|
|
2090
2303
|
let retries = 0;
|
|
2091
2304
|
while (pending.length > 0) {
|
|
2305
|
+
// Not diagnosed (#144): these are deletes of keys that were stored, so
|
|
2306
|
+
// they cannot meet a size or sort-key limit, and `delete`'s error
|
|
2307
|
+
// channel has no `ValidationError` to carry one.
|
|
2092
2308
|
const response = yield* client.batchWriteItem({
|
|
2093
2309
|
RequestItems: { [tableName]: pending },
|
|
2094
2310
|
});
|
|
@@ -2121,8 +2337,9 @@ const DELETE_BASE_DELAY_MS = 100;
|
|
|
2121
2337
|
* the same thing here as they do on an entity query, under the same empty-part
|
|
2122
2338
|
* rules (#134): a filter that asserts nothing (`{}`, `and()`) is no filter, an
|
|
2123
2339
|
* empty part directly under `and` is left out, and one anywhere else — or an
|
|
2124
|
-
* `or()` with no parts
|
|
2125
|
-
* `
|
|
2340
|
+
* `or()` with no parts — is refused with a `ValidationError` before anything
|
|
2341
|
+
* is sent. An `isIn` with no values is sent, and DynamoDB's rejection of its
|
|
2342
|
+
* `IN ()` diagnosed (#144).
|
|
2126
2343
|
*/
|
|
2127
2344
|
const compileListFilter = (filter, aggregateName) => {
|
|
2128
2345
|
if (filter === undefined)
|
|
@@ -2173,7 +2390,7 @@ const listKeyFromItem = (item, names) => {
|
|
|
2173
2390
|
}
|
|
2174
2391
|
return key;
|
|
2175
2392
|
};
|
|
2176
|
-
const queryListPartition = (client, tableConfig, listConfig, schema, options) => Effect.gen(function* () {
|
|
2393
|
+
const queryListPartition = (client, tableConfig, listConfig, schema, options, aggregateName) => Effect.gen(function* () {
|
|
2177
2394
|
const { filter, limit, pkValue, skValues } = options;
|
|
2178
2395
|
const exprNames = { "#pk": listConfig.pk.field };
|
|
2179
2396
|
const exprValues = { ":pk": toAttributeValue(pkValue) };
|
|
@@ -2196,7 +2413,7 @@ const queryListPartition = (client, tableConfig, listConfig, schema, options) =>
|
|
|
2196
2413
|
for (;;) {
|
|
2197
2414
|
const remaining = limit === undefined ? undefined : limit - items.length;
|
|
2198
2415
|
const requestLimit = listRequestLimit(options, remaining);
|
|
2199
|
-
const
|
|
2416
|
+
const input = {
|
|
2200
2417
|
TableName: tableConfig.name,
|
|
2201
2418
|
IndexName: listConfig.index,
|
|
2202
2419
|
KeyConditionExpression: keyCondition,
|
|
@@ -2206,7 +2423,19 @@ const queryListPartition = (client, tableConfig, listConfig, schema, options) =>
|
|
|
2206
2423
|
...(filter !== undefined ? { FilterExpression: filter.expression } : {}),
|
|
2207
2424
|
...(requestLimit !== undefined ? { Limit: requestLimit } : {}),
|
|
2208
2425
|
...(options.reverse === true ? { ScanIndexForward: false } : {}),
|
|
2209
|
-
}
|
|
2426
|
+
};
|
|
2427
|
+
const result = yield* client.query(input).pipe(
|
|
2428
|
+
// DynamoDB's rejection of the filter's `IN ()` (an `isIn` with no
|
|
2429
|
+
// values), not pre-checked, is named (#144).
|
|
2430
|
+
diagnoseFailure({
|
|
2431
|
+
sent: () => ({ _tag: "Read", scan: false, input }),
|
|
2432
|
+
only: ["EmptyIn"],
|
|
2433
|
+
rewrite: (diagnosis, original) => new ValidationError({
|
|
2434
|
+
entityType: aggregateName,
|
|
2435
|
+
operation: "list.filter",
|
|
2436
|
+
cause: withCause(explainDiagnosis(diagnosis, `Aggregate "${aggregateName}" list`), original),
|
|
2437
|
+
}),
|
|
2438
|
+
}));
|
|
2210
2439
|
const returned = (result.Items ?? []);
|
|
2211
2440
|
const lastEvaluatedKey = result.LastEvaluatedKey;
|
|
2212
2441
|
// Without a client-side predicate every returned row is kept, so the take
|