effect-dynamodb 1.16.0 → 1.18.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 +48 -4
- package/dist/Aggregate.d.ts.map +1 -1
- package/dist/Aggregate.js +153 -21
- package/dist/Aggregate.js.map +1 -1
- package/dist/Batch.d.ts +12 -7
- package/dist/Batch.d.ts.map +1 -1
- package/dist/Batch.js +20 -7
- package/dist/Batch.js.map +1 -1
- package/dist/Entity.d.ts +25 -7
- package/dist/Entity.d.ts.map +1 -1
- package/dist/Entity.js +9 -7
- package/dist/Entity.js.map +1 -1
- package/dist/Query.d.ts.map +1 -1
- package/dist/Query.js +12 -4
- package/dist/Query.js.map +1 -1
- package/dist/Transaction.d.ts +25 -15
- package/dist/Transaction.d.ts.map +1 -1
- package/dist/Transaction.js +32 -14
- package/dist/Transaction.js.map +1 -1
- package/dist/internal/BoundCrud.d.ts +70 -5
- package/dist/internal/BoundCrud.d.ts.map +1 -1
- package/dist/internal/BoundCrud.js +63 -5
- package/dist/internal/BoundCrud.js.map +1 -1
- package/dist/internal/TransactableOps.d.ts +10 -0
- package/dist/internal/TransactableOps.d.ts.map +1 -1
- package/dist/internal/TransactableOps.js +12 -0
- package/dist/internal/TransactableOps.js.map +1 -1
- package/package.json +2 -2
package/dist/Aggregate.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ import { AggregateAssemblyError, AggregateDecompositionError, AggregateTransacti
|
|
|
14
14
|
import type { TimestampsConfig } from "@effect-dynamodb/schema/internal/EntityConfig.js";
|
|
15
15
|
import { type Context, Effect, Schema } from "effect";
|
|
16
16
|
import { DynamoClient, type DynamoClientError } from "./DynamoClient.js";
|
|
17
|
+
import { type ConditionOps, type ConditionShorthand, type Expr } from "./internal/Expr.js";
|
|
18
|
+
import { type PathBuilder } from "./internal/PathBuilder.js";
|
|
17
19
|
import { type Table, type TableConfig } from "./Table.js";
|
|
18
20
|
export type { Cursor, DiscriminatorConfig, UpdateContext, } from "@effect-dynamodb/schema/internal/AggregateCursor.js";
|
|
19
21
|
export { type AggregateEdge, isManyEdge, isOneEdge, isRefEdge, type ManyEdge, type ManyEdgeConfig, many, type OneEdge, one, type RefEdge, type RefEntity, ref, } from "@effect-dynamodb/schema/internal/AggregateEdges.js";
|
|
@@ -24,12 +26,45 @@ import type { AggregateEdge } from "@effect-dynamodb/schema/internal/AggregateEd
|
|
|
24
26
|
import type { AggregateInputType, BoundSubAggregate, SubAggregate } from "@effect-dynamodb/schema/internal/AggregateTypes.js";
|
|
25
27
|
export declare const TypeId: typeof SchemaAggregateTypeId;
|
|
26
28
|
export type TypeId = typeof SchemaAggregateTypeId;
|
|
29
|
+
/**
|
|
30
|
+
* Server-side predicate for {@link ListOptions.filter} — the same two forms
|
|
31
|
+
* `BoundQuery.filter()` takes, so there is one filter vocabulary in the library:
|
|
32
|
+
*
|
|
33
|
+
* - callback: `(t, { gt }) => gt(t.total, 100)`
|
|
34
|
+
* - shorthand: `{ status: "shipped" }` (attribute equality, ANDed)
|
|
35
|
+
*/
|
|
36
|
+
export type ListFilter<Model> = ((t: PathBuilder<Model, Model, never>, ops: ConditionOps<Model>) => Expr) | ConditionShorthand;
|
|
27
37
|
/** Options for paginated list queries */
|
|
28
|
-
export interface ListOptions {
|
|
29
|
-
/**
|
|
38
|
+
export interface ListOptions<Model = Record<string, unknown>> {
|
|
39
|
+
/**
|
|
40
|
+
* Return at most this many aggregates — a contract on results, not on rows
|
|
41
|
+
* examined. Under {@link ListOptions.filter} the root query accumulates
|
|
42
|
+
* across as many requests as it takes to fill the page. Use
|
|
43
|
+
* {@link ListOptions.pageSize} to size the requests themselves.
|
|
44
|
+
*/
|
|
30
45
|
readonly limit?: number;
|
|
46
|
+
/**
|
|
47
|
+
* Rows examined per DynamoDB request (`Limit`) — a contract on round trips,
|
|
48
|
+
* not on what comes back. Mostly useful with a selective `filter`, where a
|
|
49
|
+
* request can return nothing and the loop simply asks again.
|
|
50
|
+
*/
|
|
51
|
+
readonly pageSize?: number;
|
|
31
52
|
/** Opaque cursor from a previous list call to resume pagination */
|
|
32
53
|
readonly cursor?: string;
|
|
54
|
+
/**
|
|
55
|
+
* `FilterExpression` applied to the **root-item** query, server-side.
|
|
56
|
+
*
|
|
57
|
+
* Worth reaching for beyond ergonomics: `list` assembles each surviving root
|
|
58
|
+
* item with its own partition read, so a predicate applied after the fact
|
|
59
|
+
* pays a full assembly for every aggregate it then discards. Pushing it into
|
|
60
|
+
* the query means the discarded rows are never assembled.
|
|
61
|
+
*
|
|
62
|
+
* Filters the root item's stored attributes only — edge items are not
|
|
63
|
+
* examined by this query.
|
|
64
|
+
*/
|
|
65
|
+
readonly filter?: ListFilter<Model>;
|
|
66
|
+
/** Walk the list index in descending order (`ScanIndexForward: false`). */
|
|
67
|
+
readonly reverse?: boolean;
|
|
33
68
|
}
|
|
34
69
|
/** Result of a paginated list query */
|
|
35
70
|
export interface ListResult<T> {
|
|
@@ -113,8 +148,17 @@ export interface Aggregate<TSchema extends Schema.Top, TKey extends Record<strin
|
|
|
113
148
|
* Supports cursor-based pagination via `options.limit` and `options.cursor`.
|
|
114
149
|
* When `limit` is specified, returns at most that many aggregates plus a cursor
|
|
115
150
|
* for the next page. Without `limit`, returns all matching aggregates.
|
|
151
|
+
*
|
|
152
|
+
* `options.filter` adds a server-side `FilterExpression` to the root-item
|
|
153
|
+
* query; `limit` still means "this many aggregates", the loop accumulating
|
|
154
|
+
* across requests until the page fills or the key range ends. A `null` cursor
|
|
155
|
+
* means genuinely exhausted.
|
|
156
|
+
*
|
|
157
|
+
* On a **sharded** list (`list.cardinality`) `limit` bounds the merged result
|
|
158
|
+
* but there is no resumable position across shards, so `cursor` is always
|
|
159
|
+
* `null` and passing one fails with `EDD-9051`.
|
|
116
160
|
*/
|
|
117
|
-
readonly list: (filter?: Record<string, unknown>, options?: ListOptions) => Effect.Effect<ListResult<Schema.Schema.Type<TSchema>>, AggregateAssemblyError | DynamoClientError | ValidationError, DynamoClient | TableConfig>;
|
|
161
|
+
readonly list: (filter?: Record<string, unknown>, options?: ListOptions<Schema.Schema.Type<TSchema>>) => Effect.Effect<ListResult<Schema.Schema.Type<TSchema>>, AggregateAssemblyError | DynamoClientError | ValidationError, DynamoClient | TableConfig>;
|
|
118
162
|
/**
|
|
119
163
|
* Derived input schema for `create()`.
|
|
120
164
|
*
|
|
@@ -149,7 +193,7 @@ export interface BoundAggregate<TSchema extends Schema.Top, TKey extends Record<
|
|
|
149
193
|
readonly create: (input: Record<string, unknown>) => Effect.Effect<Schema.Schema.Type<TSchema>, AggregateWriteError, never>;
|
|
150
194
|
readonly update: (key: TKey, mutationFn: (context: UpdateContext<TSchema["Iso"], Schema.Schema.Type<TSchema>>) => Schema.Schema.Type<TSchema> | TSchema["Iso"]) => Effect.Effect<Schema.Schema.Type<TSchema>, AggregateWriteError, never>;
|
|
151
195
|
readonly delete: (key: TKey) => Effect.Effect<void, AggregateAssemblyError | DynamoClientError, never>;
|
|
152
|
-
readonly list: (filter?: Record<string, unknown>, options?: ListOptions) => Effect.Effect<ListResult<Schema.Schema.Type<TSchema>>, AggregateAssemblyError | DynamoClientError | ValidationError, never>;
|
|
196
|
+
readonly list: (filter?: Record<string, unknown>, options?: ListOptions<Schema.Schema.Type<TSchema>>) => Effect.Effect<ListResult<Schema.Schema.Type<TSchema>>, AggregateAssemblyError | DynamoClientError | ValidationError, never>;
|
|
153
197
|
readonly inputSchema: Schema.Codec<TInput>;
|
|
154
198
|
/** Alias for `inputSchema` — consistent with `Entity.createSchema`. */
|
|
155
199
|
readonly createSchema: Schema.Codec<TInput>;
|
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;AAGtF,OAAO,KAAK,KAAK,kBAAkB,MAAM,yCAAyC,CAAA;AAGlF,OAAO,EACL,sBAAsB,EACtB,2BAA2B,EAC3B,4BAA4B,EAC5B,KAAK,YAAY,EACjB,WAAW,EACX,oBAAoB,EACpB,eAAe,EAChB,MAAM,mCAAmC,CAAA;AAK1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kDAAkD,CAAA;AAUxF,OAAO,EAAE,KAAK,OAAO,EAAY,MAAM,EAAsB,MAAM,EAAa,MAAM,QAAQ,CAAA;AAE9F,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAA4B,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"Aggregate.d.ts","sourceRoot":"","sources":["../src/Aggregate.ts"],"names":[],"mappings":"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"}
|
package/dist/Aggregate.js
CHANGED
|
@@ -20,6 +20,8 @@ import { DateTime, Effect, Option, Schema, SchemaAST } from "effect";
|
|
|
20
20
|
import * as Batch from "./Batch.js";
|
|
21
21
|
import { DynamoClient } from "./DynamoClient.js";
|
|
22
22
|
import { fromDefinition as entityFromDefinition } from "./Entity.js";
|
|
23
|
+
import { compileExpr, createConditionOps, parseSimpleShorthand, } from "./internal/Expr.js";
|
|
24
|
+
import { createPathBuilder } from "./internal/PathBuilder.js";
|
|
23
25
|
import { generateTimestampPrimitive } from "./internal/TransactableOps.js";
|
|
24
26
|
import { fromAttributeMap, toAttributeMap, toAttributeValue } from "./Marshaller.js";
|
|
25
27
|
import { resolvePrimaryKey } from "./Table.js";
|
|
@@ -570,9 +572,21 @@ const makeAggregate = (schema, config) => {
|
|
|
570
572
|
}
|
|
571
573
|
const listConfig = config.list;
|
|
572
574
|
const limit = options?.limit;
|
|
573
|
-
const
|
|
574
|
-
|
|
575
|
-
|
|
575
|
+
const pageSize = options?.pageSize;
|
|
576
|
+
// Root-item FilterExpression. Compiled once — the same two forms
|
|
577
|
+
// `BoundQuery.filter()` takes, through the same `Expr` compiler, so
|
|
578
|
+
// there is one filter vocabulary rather than a second dialect here.
|
|
579
|
+
const rootFilter = compileListFilter(options?.filter);
|
|
580
|
+
// Attribute names that make up a resume key on the list index: the index
|
|
581
|
+
// key plus the table key. A `LastEvaluatedKey` supplies them when there
|
|
582
|
+
// is one; this is the fallback for the case a filtered request over-reads
|
|
583
|
+
// on the LAST page, where the surplus still has to be re-read next time.
|
|
584
|
+
const listKeyFields = Array.from(new Set([
|
|
585
|
+
tablePrimary?.pk ?? config.pk.field,
|
|
586
|
+
tablePrimary?.sk ?? "sk",
|
|
587
|
+
listConfig.pk.field,
|
|
588
|
+
listConfig.sk.field,
|
|
589
|
+
]));
|
|
576
590
|
// Compose PK from filter values matching PK composites. The filter is a
|
|
577
591
|
// DOMAIN record, so it goes through the same key form the write side
|
|
578
592
|
// used — otherwise `list` composes a key `create` never wrote and
|
|
@@ -592,22 +606,64 @@ const makeAggregate = (schema, config) => {
|
|
|
592
606
|
let rootItems;
|
|
593
607
|
let nextKey;
|
|
594
608
|
if (listConfig.cardinality) {
|
|
595
|
-
//
|
|
596
|
-
//
|
|
609
|
+
// A sharded list is a fan-out over N independent partitions, and a
|
|
610
|
+
// DynamoDB cursor addresses a position in ONE of them. Resuming would
|
|
611
|
+
// need a per-shard cursor set plus a defined global merge order, which
|
|
612
|
+
// this fan-out does not have (results are concatenated shard by shard).
|
|
613
|
+
// Rejecting is the honest answer — the previous behaviour accepted the
|
|
614
|
+
// cursor and restarted from the beginning, silently.
|
|
615
|
+
if (options?.cursor !== undefined) {
|
|
616
|
+
return yield* new ValidationError({
|
|
617
|
+
entityType: aggregateName,
|
|
618
|
+
operation: "list",
|
|
619
|
+
cause: `[EDD-9051] Aggregate "${aggregateName}" has a sharded list ` +
|
|
620
|
+
`(list.cardinality = ${listConfig.cardinality}), and a sharded fan-out has no ` +
|
|
621
|
+
"resumable cursor: a DynamoDB cursor names a position in one partition, while " +
|
|
622
|
+
"this query spans all of them with no defined order across shards. `list` on a " +
|
|
623
|
+
"sharded aggregate always returns `cursor: null`; use `limit` to bound the " +
|
|
624
|
+
"result, or drop `list.cardinality` if the partition needs paging more than it " +
|
|
625
|
+
"needs spreading.",
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
// Fan out across shards and merge. `limit` bounds each shard too — no
|
|
629
|
+
// single shard can contribute more than the whole page.
|
|
597
630
|
const shardQueries = Array.from({ length: listConfig.cardinality }, (_, shard) => {
|
|
598
631
|
const shardPkValue = composeCollectionKey(config.schema, listConfig.name, [
|
|
599
632
|
...listPkComposites,
|
|
600
633
|
String(shard),
|
|
601
634
|
]);
|
|
602
|
-
return queryListPartition(client, tableConfig, listConfig, config.schema,
|
|
635
|
+
return queryListPartition(client, tableConfig, listConfig, config.schema, {
|
|
636
|
+
pkValue: shardPkValue,
|
|
637
|
+
skValues,
|
|
638
|
+
limit,
|
|
639
|
+
pageSize,
|
|
640
|
+
filter: rootFilter,
|
|
641
|
+
reverse: options?.reverse === true,
|
|
642
|
+
keyFields: listKeyFields,
|
|
643
|
+
});
|
|
603
644
|
});
|
|
604
645
|
const shardResults = yield* Effect.all(shardQueries);
|
|
605
|
-
|
|
646
|
+
const merged = shardResults.flatMap((r) => r.items);
|
|
647
|
+
// Truncate the merged fan-out to `limit`. Previously the option was
|
|
648
|
+
// accepted and discarded, so a large aggregate set returned everything.
|
|
649
|
+
rootItems = limit === undefined ? merged : merged.slice(0, limit);
|
|
606
650
|
nextKey = undefined;
|
|
607
651
|
}
|
|
608
652
|
else {
|
|
653
|
+
const startKey = options?.cursor
|
|
654
|
+
? JSON.parse(atob(options.cursor))
|
|
655
|
+
: undefined;
|
|
609
656
|
const listPkValue = composeCollectionKey(config.schema, listConfig.name, listPkComposites);
|
|
610
|
-
const result = yield* queryListPartition(client, tableConfig, listConfig, config.schema,
|
|
657
|
+
const result = yield* queryListPartition(client, tableConfig, listConfig, config.schema, {
|
|
658
|
+
pkValue: listPkValue,
|
|
659
|
+
skValues,
|
|
660
|
+
limit,
|
|
661
|
+
pageSize,
|
|
662
|
+
startKey,
|
|
663
|
+
filter: rootFilter,
|
|
664
|
+
reverse: options?.reverse === true,
|
|
665
|
+
keyFields: listKeyFields,
|
|
666
|
+
});
|
|
611
667
|
rootItems = result.items;
|
|
612
668
|
nextKey = result.lastKey;
|
|
613
669
|
}
|
|
@@ -1636,7 +1692,60 @@ const deleteAllItems = (client, tableName, allItems, pkField) => Effect.gen(func
|
|
|
1636
1692
|
});
|
|
1637
1693
|
}
|
|
1638
1694
|
});
|
|
1639
|
-
|
|
1695
|
+
// ---------------------------------------------------------------------------
|
|
1696
|
+
// Internal: list filter compilation
|
|
1697
|
+
// ---------------------------------------------------------------------------
|
|
1698
|
+
/**
|
|
1699
|
+
* @internal Compile a {@link ListFilter} to a `FilterExpression` fragment.
|
|
1700
|
+
*
|
|
1701
|
+
* Both forms route through the same `Expr` compiler `BoundQuery.filter()` uses,
|
|
1702
|
+
* so `{ status: "shipped" }` and `(t, { eq }) => eq(t.status, "shipped")` mean
|
|
1703
|
+
* the same thing here as they do on an entity query. A shorthand with no
|
|
1704
|
+
* entries compiles to the empty string — that is a no-op, not `FilterExpression: ""`,
|
|
1705
|
+
* which DynamoDB rejects.
|
|
1706
|
+
*/
|
|
1707
|
+
const compileListFilter = (filter) => {
|
|
1708
|
+
if (filter === undefined)
|
|
1709
|
+
return undefined;
|
|
1710
|
+
const expr = typeof filter === "function"
|
|
1711
|
+
? filter(createPathBuilder(), createConditionOps())
|
|
1712
|
+
: parseSimpleShorthand(filter);
|
|
1713
|
+
const compiled = compileExpr(expr);
|
|
1714
|
+
return compiled.expression === "" ? undefined : compiled;
|
|
1715
|
+
};
|
|
1716
|
+
/**
|
|
1717
|
+
* @internal DynamoDB `Limit` for the next request — the same rule `Query.ts`
|
|
1718
|
+
* applies, and for the same reason: `Limit` bounds rows EXAMINED, and a
|
|
1719
|
+
* `FilterExpression` runs after, so under a filter `limit` cannot be expressed
|
|
1720
|
+
* as `Limit` at all. `pageSize` (or, unset, a natural 1 MB page) is the budget
|
|
1721
|
+
* then, and the loop keeps asking until the page fills.
|
|
1722
|
+
*/
|
|
1723
|
+
const listRequestLimit = (options, remaining) => {
|
|
1724
|
+
if (remaining === undefined)
|
|
1725
|
+
return options.pageSize;
|
|
1726
|
+
if (options.filter !== undefined)
|
|
1727
|
+
return options.pageSize;
|
|
1728
|
+
return options.pageSize === undefined ? remaining : Math.min(options.pageSize, remaining);
|
|
1729
|
+
};
|
|
1730
|
+
/**
|
|
1731
|
+
* @internal Rebuild a resume key from an item the query returned. Every item
|
|
1732
|
+
* carries its table key and index key as ordinary attributes, so any item can
|
|
1733
|
+
* address itself. Returns `undefined` if a name is missing.
|
|
1734
|
+
*/
|
|
1735
|
+
const listKeyFromItem = (item, names) => {
|
|
1736
|
+
if (names.length === 0)
|
|
1737
|
+
return undefined;
|
|
1738
|
+
const key = {};
|
|
1739
|
+
for (const name of names) {
|
|
1740
|
+
const value = item[name];
|
|
1741
|
+
if (value === undefined)
|
|
1742
|
+
return undefined;
|
|
1743
|
+
key[name] = value;
|
|
1744
|
+
}
|
|
1745
|
+
return key;
|
|
1746
|
+
};
|
|
1747
|
+
const queryListPartition = (client, tableConfig, listConfig, schema, options) => Effect.gen(function* () {
|
|
1748
|
+
const { filter, limit, pkValue, skValues } = options;
|
|
1640
1749
|
const exprNames = { "#pk": listConfig.pk.field };
|
|
1641
1750
|
const exprValues = { ":pk": toAttributeValue(pkValue) };
|
|
1642
1751
|
let keyCondition = "#pk = :pk";
|
|
@@ -1646,28 +1755,51 @@ const queryListPartition = (client, tableConfig, listConfig, schema, pkValue, sk
|
|
|
1646
1755
|
exprValues[":skPrefix"] = toAttributeValue(skPrefix);
|
|
1647
1756
|
keyCondition += " AND begins_with(#sk, :skPrefix)";
|
|
1648
1757
|
}
|
|
1758
|
+
if (filter !== undefined) {
|
|
1759
|
+
Object.assign(exprNames, filter.names);
|
|
1760
|
+
Object.assign(exprValues, filter.values);
|
|
1761
|
+
}
|
|
1762
|
+
if (limit !== undefined && limit <= 0)
|
|
1763
|
+
return { items: [], lastKey: undefined };
|
|
1649
1764
|
const items = [];
|
|
1650
|
-
let
|
|
1651
|
-
|
|
1765
|
+
let startKey = options.startKey;
|
|
1766
|
+
let lastKey;
|
|
1767
|
+
for (;;) {
|
|
1768
|
+
const remaining = limit === undefined ? undefined : limit - items.length;
|
|
1769
|
+
const requestLimit = listRequestLimit(options, remaining);
|
|
1652
1770
|
const result = yield* client.query({
|
|
1653
1771
|
TableName: tableConfig.name,
|
|
1654
1772
|
IndexName: listConfig.index,
|
|
1655
1773
|
KeyConditionExpression: keyCondition,
|
|
1656
1774
|
ExpressionAttributeNames: exprNames,
|
|
1657
1775
|
ExpressionAttributeValues: exprValues,
|
|
1658
|
-
ExclusiveStartKey:
|
|
1659
|
-
...(
|
|
1776
|
+
ExclusiveStartKey: startKey,
|
|
1777
|
+
...(filter !== undefined ? { FilterExpression: filter.expression } : {}),
|
|
1778
|
+
...(requestLimit !== undefined ? { Limit: requestLimit } : {}),
|
|
1779
|
+
...(options.reverse === true ? { ScanIndexForward: false } : {}),
|
|
1660
1780
|
});
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1781
|
+
const returned = (result.Items ?? []);
|
|
1782
|
+
const lastEvaluatedKey = result.LastEvaluatedKey;
|
|
1783
|
+
const take = remaining === undefined ? returned.length : Math.min(remaining, returned.length);
|
|
1784
|
+
for (let i = 0; i < take; i++)
|
|
1785
|
+
items.push(fromAttributeMap(returned[i]));
|
|
1786
|
+
// Over-read — a filtered request returns whatever survives the filter, not
|
|
1787
|
+
// `Limit` rows. The surplus is dropped, so `LastEvaluatedKey` (the last row
|
|
1788
|
+
// EXAMINED) would skip past items the caller never saw. Resume from the
|
|
1789
|
+
// last item actually handed back instead.
|
|
1790
|
+
if (take < returned.length) {
|
|
1791
|
+
lastKey =
|
|
1792
|
+
listKeyFromItem(returned[take - 1], lastEvaluatedKey != null ? Object.keys(lastEvaluatedKey) : options.keyFields) ?? lastEvaluatedKey;
|
|
1793
|
+
break;
|
|
1665
1794
|
}
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
if (
|
|
1795
|
+
const exhausted = lastEvaluatedKey == null;
|
|
1796
|
+
const limitReached = limit !== undefined && items.length >= limit;
|
|
1797
|
+
if (exhausted || limitReached) {
|
|
1798
|
+
lastKey = lastEvaluatedKey;
|
|
1669
1799
|
break;
|
|
1670
|
-
|
|
1800
|
+
}
|
|
1801
|
+
startKey = lastEvaluatedKey;
|
|
1802
|
+
}
|
|
1671
1803
|
return { items, lastKey };
|
|
1672
1804
|
});
|
|
1673
1805
|
//# sourceMappingURL=Aggregate.js.map
|