effect-dynamodb 1.25.0 → 1.27.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 +7 -0
- package/dist/Aggregate.d.ts.map +1 -1
- package/dist/Aggregate.js +150 -4
- package/dist/Aggregate.js.map +1 -1
- package/dist/Collection.d.ts +9 -1
- package/dist/Collection.d.ts.map +1 -1
- package/dist/Collection.js +3 -0
- 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 +83 -11
- package/dist/DynamoClient.js.map +1 -1
- package/dist/Entity.d.ts +80 -27
- package/dist/Entity.d.ts.map +1 -1
- package/dist/Entity.js +406 -76
- package/dist/Entity.js.map +1 -1
- package/dist/EventStore.d.ts +454 -55
- package/dist/EventStore.d.ts.map +1 -1
- package/dist/EventStore.js +681 -129
- package/dist/EventStore.js.map +1 -1
- package/dist/Query.d.ts +53 -0
- package/dist/Query.d.ts.map +1 -1
- package/dist/Query.js +88 -6
- 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.map +1 -1
- package/dist/Table.js +8 -6
- package/dist/Table.js.map +1 -1
- package/dist/Transaction.d.ts.map +1 -1
- package/dist/Transaction.js +5 -0
- package/dist/Transaction.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 +3 -3
- package/dist/internal/BoundQuery.d.ts.map +1 -1
- package/dist/internal/BoundQuery.js.map +1 -1
- package/dist/internal/BoundVectorQuery.d.ts +12 -0
- package/dist/internal/BoundVectorQuery.d.ts.map +1 -1
- package/dist/internal/BoundVectorQuery.js +14 -1
- package/dist/internal/BoundVectorQuery.js.map +1 -1
- package/dist/internal/EntityCombinators.d.ts.map +1 -1
- package/dist/internal/EntityCombinators.js +16 -1
- package/dist/internal/EntityCombinators.js.map +1 -1
- package/dist/internal/Expr.d.ts +5 -3
- package/dist/internal/Expr.d.ts.map +1 -1
- package/dist/internal/Expr.js +1 -1
- package/dist/internal/Expr.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/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/package.json +6 -2
package/dist/EventStore.d.ts
CHANGED
|
@@ -15,17 +15,26 @@
|
|
|
15
15
|
* - Stream indexes (`makeStream({ indexes })`): sub-streams ordered by a key
|
|
16
16
|
* derived from each event, on an LSI or GSI, each projecting all of the
|
|
17
17
|
* event, all but its metadata, or only its keys — `readIndex`,
|
|
18
|
-
* `query.index`, and `indexDefinitions` for the `CreateTable` fragments
|
|
18
|
+
* `query.index`, and `indexDefinitions` for the `CreateTable` fragments.
|
|
19
|
+
* A GSI may derive its partition too, so several streams share one
|
|
20
|
+
* partition of it, snapshots included (#149)
|
|
19
21
|
* - `fold` / `foldFrom` helpers for state reconstruction
|
|
22
|
+
* - Packed storage (`makeStream({ storedAs: { data } })`, `snapshot.storedAs`):
|
|
23
|
+
* event data and snapshot state as one JSON string, optionally compressed —
|
|
24
|
+
* read by the stored form, so a stream may mix native, JSON and compressed
|
|
25
|
+
* items; `measureAppend` / `measureSnapshot` for caller-side size planning,
|
|
26
|
+
* and `decodeStreamImage` for DynamoDB Streams records
|
|
20
27
|
*
|
|
21
28
|
* Built on the existing library primitives (DynamoSchema, KeyComposer, Query,
|
|
22
29
|
* DynamoClient, Marshaller).
|
|
23
30
|
*/
|
|
24
31
|
import type { AttributeDefinition, GlobalSecondaryIndex, LocalSecondaryIndex } from "@aws-sdk/client-dynamodb";
|
|
32
|
+
import { type PackedConfig } from "@effect-dynamodb/schema/DynamoModel.js";
|
|
25
33
|
import * as DynamoSchema from "@effect-dynamodb/schema/DynamoSchema.js";
|
|
26
34
|
import { AdditionalItemConditionFailed, AppendTooLarge, type ConcurrentModification, DuplicateCommand, type OptimisticLockError, TransactionCancelled, type UniqueConstraintViolation, ValidationError, VersionConflict } from "@effect-dynamodb/schema/Errors.js";
|
|
27
35
|
import { DateTime, type Duration, Effect, Option, Pipeable, Schedule, Schema } from "effect";
|
|
28
36
|
import { DynamoClient, type DynamoClientError } from "./DynamoClient.js";
|
|
37
|
+
import { type StreamImage } from "./internal/Packed.js";
|
|
29
38
|
import { type TransactWriteOp } from "./internal/TransactWriteOps.js";
|
|
30
39
|
import * as Query from "./Query.js";
|
|
31
40
|
import { type Table, type TableConfig } from "./Table.js";
|
|
@@ -66,6 +75,28 @@ export interface StreamEvent<A, M = Record<string, unknown> | undefined> {
|
|
|
66
75
|
*/
|
|
67
76
|
readonly timestamp: string;
|
|
68
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* A {@link StreamEvent} together with the event's stored `data` in its wire
|
|
80
|
+
* form — what `decodeStreamImage(image, { raw: true })` returns.
|
|
81
|
+
*/
|
|
82
|
+
export interface RawStreamEvent<A, M = Record<string, unknown> | undefined> extends StreamEvent<A, M> {
|
|
83
|
+
/**
|
|
84
|
+
* The event's `data` as stored, before the event schema decoded it: the
|
|
85
|
+
* parsed JSON of a packed `data` (`{ _tag, ...fields }`), or the unmarshalled
|
|
86
|
+
* map of a native one — fields the event schema does not model included. The
|
|
87
|
+
* same object the decode read, so nothing is parsed twice.
|
|
88
|
+
*/
|
|
89
|
+
readonly rawData: Readonly<Record<string, unknown>>;
|
|
90
|
+
}
|
|
91
|
+
/** Options of `decodeStreamImage`. */
|
|
92
|
+
export interface DecodeStreamImageOptions {
|
|
93
|
+
/**
|
|
94
|
+
* Also return the event's stored `data` in wire form (`rawData`, see
|
|
95
|
+
* {@link RawStreamEvent}) — for a consumer that needs fields the event
|
|
96
|
+
* schema does not model, without parsing the image a second time.
|
|
97
|
+
*/
|
|
98
|
+
readonly raw?: boolean | undefined;
|
|
99
|
+
}
|
|
69
100
|
/**
|
|
70
101
|
* Metadata type carried on {@link StreamEvent} for a stream: the decoded
|
|
71
102
|
* metadata schema type when the stream declares one, an untyped record
|
|
@@ -128,7 +159,69 @@ export interface SnapshotConfig<TSchema extends Schema.Top = Schema.Top> {
|
|
|
128
159
|
readonly schema: TSchema;
|
|
129
160
|
readonly mode?: SnapshotMode | undefined;
|
|
130
161
|
readonly every?: number | undefined;
|
|
162
|
+
/**
|
|
163
|
+
* How the snapshot's `state` is stored: a native map (omitted, the default)
|
|
164
|
+
* or packed — `DynamoModel.Packed({ format: "json", compression? })`, one
|
|
165
|
+
* JSON string, compressed to binary at or over the threshold when that is
|
|
166
|
+
* smaller. Packing shrinks a large state the most: an inline snapshot rides
|
|
167
|
+
* in every append's transaction, and DynamoDB's item and transaction size
|
|
168
|
+
* limits count its **stored** bytes.
|
|
169
|
+
*
|
|
170
|
+
* The state schema's encoded side must be object- or array-shaped
|
|
171
|
+
* (`[EDD-9073]`). Reads go by the stored form, so changing this setting
|
|
172
|
+
* needs no rewrite: a snapshot written before still decodes.
|
|
173
|
+
*/
|
|
174
|
+
readonly storedAs?: PackedConfig | undefined;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* How a stream stores event `data` (`makeStream({ storedAs })`). `data` is a
|
|
178
|
+
* native map by default; `DynamoModel.Packed({ format: "json", compression? })`
|
|
179
|
+
* stores it as one JSON string (`S`), compressed to binary (`B`) at or over the
|
|
180
|
+
* threshold when that is smaller. Metadata always stays a map.
|
|
181
|
+
*
|
|
182
|
+
* Reads go by each item's stored form, never by this setting, so a stream may
|
|
183
|
+
* hold a mix — events appended before packing was turned on still decode, and
|
|
184
|
+
* so do packed events after it is turned off.
|
|
185
|
+
*/
|
|
186
|
+
export interface StreamStorageConfig {
|
|
187
|
+
readonly data?: PackedConfig | undefined;
|
|
188
|
+
}
|
|
189
|
+
/** The resolved storage settings exposed on a stream (`stream.storedAs`). */
|
|
190
|
+
export interface StreamStorageSettings {
|
|
191
|
+
/** Event `data` (`makeStream({ storedAs: { data } })`); `undefined` = native map. */
|
|
192
|
+
readonly data: PackedConfig | undefined;
|
|
193
|
+
/** Snapshot `state` (`snapshot.storedAs`); `undefined` = native map. */
|
|
194
|
+
readonly state: PackedConfig | undefined;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* The bytes an {@link EventStream.append} would write, by DynamoDB's own size
|
|
198
|
+
* accounting — {@link EventStream.measureAppend}. Every number counts stored
|
|
199
|
+
* bytes: packed (and compressed) `data` and `state` as they would be sent.
|
|
200
|
+
*/
|
|
201
|
+
export interface AppendMeasurement {
|
|
202
|
+
/**
|
|
203
|
+
* The item size of each event `Put`, in order — what DynamoDB's 400 KB
|
|
204
|
+
* (409,600-byte) item limit measures.
|
|
205
|
+
*/
|
|
206
|
+
readonly events: ReadonlyArray<number>;
|
|
207
|
+
/** The item size of the idempotency sentinel `Put`, when `idempotency` is given. */
|
|
208
|
+
readonly sentinel: number | undefined;
|
|
209
|
+
/** The item size of the inline snapshot `Put`, when `snapshot` is given. */
|
|
210
|
+
readonly snapshot: number | undefined;
|
|
211
|
+
/**
|
|
212
|
+
* What the stream's own actions (the contiguity check, the event puts, the
|
|
213
|
+
* sentinel and the inline snapshot) add to the `TransactWriteItems` request
|
|
214
|
+
* — its 4 MB (4,194,304-byte) limit counts items, keys and expressions.
|
|
215
|
+
* Caller `additionalItems` are not included: add their size yourself.
|
|
216
|
+
*/
|
|
217
|
+
readonly transactionBytes: number;
|
|
131
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* The options {@link EventStream.measureAppend} accepts: those of
|
|
221
|
+
* {@link EventStream.append} except `additionalItems`.
|
|
222
|
+
*/
|
|
223
|
+
export type MeasureAppendOptions<TMetadata, TState = never> = Omit<AppendOptionsBase<TState>, "additionalItems"> & AppendMetadataOptions<TMetadata>;
|
|
224
|
+
export type { LambdaAttributeValue, StreamImage } from "./internal/Packed.js";
|
|
132
225
|
/**
|
|
133
226
|
* How {@link commandHandler} writes snapshots.
|
|
134
227
|
*
|
|
@@ -353,20 +446,27 @@ export interface LatestState<TState, TEvent, M = Record<string, unknown> | undef
|
|
|
353
446
|
readonly events: ReadonlyArray<StreamEvent<TEvent, M>>;
|
|
354
447
|
readonly version: number;
|
|
355
448
|
}
|
|
449
|
+
/**
|
|
450
|
+
* The stream id as a stream's index functions receive it: the value of each
|
|
451
|
+
* `streamId.composite` field, by name.
|
|
452
|
+
*/
|
|
453
|
+
export type StreamIndexStreamId = {
|
|
454
|
+
readonly [field: string]: string;
|
|
455
|
+
};
|
|
356
456
|
/**
|
|
357
457
|
* Derives an event's key in a stream index from the domain event (before it is
|
|
358
|
-
* encoded)
|
|
458
|
+
* encoded), the version it is appended at — its version in the stream — and
|
|
459
|
+
* the stream id it is appended to.
|
|
359
460
|
*
|
|
360
461
|
* - A string is the event's sort key in the index. It is stored **raw**: no
|
|
361
462
|
* casing and no prefixing is applied, so compose it to sort the way you
|
|
362
463
|
* query (zero-pad numbers, for example).
|
|
363
464
|
* - `undefined` leaves the event out of the index (a sparse index).
|
|
364
|
-
* -
|
|
365
|
-
* `ValidationError` before anything is written
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
* is written.
|
|
465
|
+
* - Any other value, or a `key` that throws, fails the append with
|
|
466
|
+
* `ValidationError` before anything is written. An empty string or a key
|
|
467
|
+
* over DynamoDB's 1024-byte sort-key limit is sent and rejected by
|
|
468
|
+
* DynamoDB; the append then fails with `ValidationError` naming the index
|
|
469
|
+
* and the event (#144), and nothing is written.
|
|
370
470
|
*
|
|
371
471
|
* The key is derived only when an event is appended. Events are never
|
|
372
472
|
* rewritten, so changing `key` later leaves events already stored under their
|
|
@@ -376,7 +476,60 @@ export interface LatestState<TState, TEvent, M = Record<string, unknown> | undef
|
|
|
376
476
|
* existing events is the application's decision (see `indexes` on
|
|
377
477
|
* {@link makeStream}).
|
|
378
478
|
*/
|
|
379
|
-
export type StreamIndexKey<TEvent> = (event: TEvent, version: number) => string | undefined;
|
|
479
|
+
export type StreamIndexKey<TEvent, TStreamId = StreamIndexStreamId> = (event: TEvent, version: number, streamId: TStreamId) => string | undefined;
|
|
480
|
+
/**
|
|
481
|
+
* Derives an event's partition in a GSI stream index (`partition` on a
|
|
482
|
+
* {@link GlobalStreamIndexConfig}, #149) from the domain event, the version it
|
|
483
|
+
* is appended at and the stream id — the arguments of the index's `key`, in
|
|
484
|
+
* the same order. Called for every appended event, with `key`.
|
|
485
|
+
*
|
|
486
|
+
* - A string is the event's partition key in the index, stored **raw** (no
|
|
487
|
+
* casing, no prefix) in the index's `pk` attribute. Several streams may
|
|
488
|
+
* derive the same value, so one query of the index reads a group of
|
|
489
|
+
* streams — `ORDER#<orderId>` for an order's line, payment and shipment
|
|
490
|
+
* streams, say.
|
|
491
|
+
* - `undefined` leaves the event out of the index, as an `undefined` `key`
|
|
492
|
+
* does. An event is indexed only when both return a string.
|
|
493
|
+
* - Within one append, every event whose partition is a string must have the
|
|
494
|
+
* same one, and so must the inline snapshot's entry (see
|
|
495
|
+
* {@link StreamIndexSnapshotEntry}); otherwise the append fails with
|
|
496
|
+
* `ValidationError` and nothing is written. Another value, or a function
|
|
497
|
+
* that throws, fails it the same way. An empty string, or one over
|
|
498
|
+
* DynamoDB's 2048-byte partition-key limit, is DynamoDB's to reject: the
|
|
499
|
+
* rejection is reported as a `ValidationError` naming the index and the
|
|
500
|
+
* event.
|
|
501
|
+
*/
|
|
502
|
+
export type StreamIndexPartition<TEvent, TStreamId = StreamIndexStreamId> = (event: TEvent, version: number, streamId: TStreamId) => string | undefined;
|
|
503
|
+
/**
|
|
504
|
+
* Puts a stream's snapshot in a GSI stream index with a derived partition
|
|
505
|
+
* (`snapshot` on a {@link GlobalStreamIndexConfig}, #149): from the state
|
|
506
|
+
* being written (before it is encoded), the snapshot's `asOfVersion` and the
|
|
507
|
+
* stream id — in the order of the events' `key` and `partition` — the
|
|
508
|
+
* snapshot item's partition and sort key in the index —
|
|
509
|
+
* both stored **raw** — or `undefined` to leave it out.
|
|
510
|
+
*
|
|
511
|
+
* Every snapshot write derives it — `writeSnapshot`, the inline snapshot of
|
|
512
|
+
* `append` and `commandHandler` (`mode: "inline"` or `"after-append"`) — and
|
|
513
|
+
* the attributes are part of the snapshot item itself, so an inline snapshot
|
|
514
|
+
* enters the index in the append's own transaction. The snapshot `Put`
|
|
515
|
+
* replaces the whole item, so returning `undefined` takes a snapshot already
|
|
516
|
+
* in the index out of it.
|
|
517
|
+
*
|
|
518
|
+
* The inline snapshot of an append must have the partition of the append's
|
|
519
|
+
* events in the index (when they have one); a standalone `writeSnapshot` has
|
|
520
|
+
* no events to compare with. Another value than an object of two strings, or
|
|
521
|
+
* a function that throws, fails the write with `ValidationError` before
|
|
522
|
+
* anything is written.
|
|
523
|
+
*
|
|
524
|
+
* A snapshot is the state **as of** `asOfVersion`: it is current after every
|
|
525
|
+
* append only with `snapshot: { mode: "inline" }` and no `every`. A reader of
|
|
526
|
+
* the index that needs the head folds the stream's indexed events after
|
|
527
|
+
* `asOfVersion` onto it.
|
|
528
|
+
*/
|
|
529
|
+
export type StreamIndexSnapshotEntry<TState, TStreamId = StreamIndexStreamId> = (state: TState, asOfVersion: number, streamId: TStreamId) => {
|
|
530
|
+
readonly partition: string;
|
|
531
|
+
readonly key: string;
|
|
532
|
+
} | undefined;
|
|
380
533
|
/**
|
|
381
534
|
* A stream index on a **local** secondary index (the default `type`). An LSI
|
|
382
535
|
* shares the table's partition key (`pk`), so its items are exactly the
|
|
@@ -393,13 +546,17 @@ export type StreamIndexKey<TEvent> = (event: TEvent, version: number) => string
|
|
|
393
546
|
* the index copies from each event is its `projection` — see
|
|
394
547
|
* {@link StreamIndexProjection}.
|
|
395
548
|
*/
|
|
396
|
-
export interface LocalStreamIndexConfig<TEvent> {
|
|
549
|
+
export interface LocalStreamIndexConfig<TEvent, TStreamId = StreamIndexStreamId> {
|
|
397
550
|
readonly type?: "lsi" | undefined;
|
|
398
551
|
readonly index: string;
|
|
399
552
|
readonly sk: string;
|
|
400
553
|
/** An LSI uses the table partition key; supplying `pk` is refused (`[EDD-9063]`). */
|
|
401
554
|
readonly pk?: undefined;
|
|
402
|
-
readonly key: StreamIndexKey<TEvent>;
|
|
555
|
+
readonly key: StreamIndexKey<TEvent, TStreamId>;
|
|
556
|
+
/** An LSI's partition is the stream's; supplying `partition` is refused (`[EDD-9063]`). */
|
|
557
|
+
readonly partition?: undefined;
|
|
558
|
+
/** Only a GSI with a derived `partition` indexes snapshots (`[EDD-9063]`). */
|
|
559
|
+
readonly snapshot?: undefined;
|
|
403
560
|
/**
|
|
404
561
|
* What the index copies from each event — see {@link StreamIndexProjection}.
|
|
405
562
|
* Default `"all"`. Must match the deployed index.
|
|
@@ -407,30 +564,56 @@ export interface LocalStreamIndexConfig<TEvent> {
|
|
|
407
564
|
readonly projection?: StreamIndexProjection | undefined;
|
|
408
565
|
}
|
|
409
566
|
/**
|
|
410
|
-
* A stream index on a **global** secondary index. `append` writes
|
|
411
|
-
* partition key value into the `pk` attribute of each indexed
|
|
412
|
-
* index is scoped to the same stream: the eventually consistent
|
|
413
|
-
* an LSI, without the LSI's 10 GB item-collection cap. DynamoDB
|
|
414
|
-
* strongly consistent read of it, diagnosed as a `ValidationError`
|
|
415
|
-
* index.
|
|
567
|
+
* A stream index on a **global** secondary index. By default `append` writes
|
|
568
|
+
* the stream's partition key value into the `pk` attribute of each indexed
|
|
569
|
+
* event, so the index is scoped to the same stream: the eventually consistent
|
|
570
|
+
* equivalent of an LSI, without the LSI's 10 GB item-collection cap. DynamoDB
|
|
571
|
+
* rejects a strongly consistent read of it, diagnosed as a `ValidationError`
|
|
572
|
+
* naming the index.
|
|
573
|
+
*
|
|
574
|
+
* With `partition` (#149), the partition is derived instead — see
|
|
575
|
+
* {@link StreamIndexPartition} — and stored raw, so several streams can write
|
|
576
|
+
* the same partition of one GSI they all declare, and one query of it reads
|
|
577
|
+
* the whole group (an order's line, payment and shipment streams under
|
|
578
|
+
* `ORDER#<orderId>`, say). {@link indexDefinitions} emits a physical index
|
|
579
|
+
* the streams declare alike once. With `snapshot` as well, each stream's
|
|
580
|
+
* snapshot item is in the index too — see {@link StreamIndexSnapshotEntry}.
|
|
581
|
+
* {@link EventStream.readIndex} then takes the partition to read, and
|
|
582
|
+
* returns the given stream's events in it.
|
|
416
583
|
*
|
|
417
584
|
* Unlike an LSI, the GSI itself can be added to an existing table
|
|
418
|
-
* (`UpdateTable`), but that does not index the stream's existing
|
|
419
|
-
* index attributes are written only by `append
|
|
420
|
-
* the index is declared are in it — see
|
|
585
|
+
* (`UpdateTable`), but that does not index the stream's existing items: the
|
|
586
|
+
* index attributes are written only by `append` (and snapshot writes), so only
|
|
587
|
+
* events appended after the index is declared are in it — see
|
|
588
|
+
* {@link StreamIndexKey}.
|
|
421
589
|
*
|
|
422
590
|
* - `index` — the physical GSI name.
|
|
423
|
-
* - `pk` — the attribute carrying the
|
|
591
|
+
* - `pk` — the attribute carrying the partition (the GSI's HASH key): the
|
|
592
|
+
* stream's partition key, or the derived `partition`.
|
|
424
593
|
* - `sk` — the attribute carrying the derived key (the GSI's RANGE key).
|
|
425
594
|
* - `key` — see {@link StreamIndexKey}.
|
|
595
|
+
* - `partition` — optional, see {@link StreamIndexPartition}.
|
|
596
|
+
* - `snapshot` — optional, requires `partition` and a stream `snapshot`
|
|
597
|
+
* config; see {@link StreamIndexSnapshotEntry}.
|
|
426
598
|
* - `projection` — see {@link StreamIndexProjection}.
|
|
599
|
+
*
|
|
600
|
+
* Write `type: "gsi"` in the entry itself. TypeScript infers the parameter
|
|
601
|
+
* types of `partition` and `snapshot` from the GSI member of the entry union,
|
|
602
|
+
* which it selects by the literal `type`: an entry whose `type` comes from an
|
|
603
|
+
* object spread (`{ ...shared, partition: (event) => … }`) gets implicit-`any`
|
|
604
|
+
* parameters there, although `key` still infers. Annotate them, or give the
|
|
605
|
+
* shared fragment a {@link GlobalStreamIndexConfig} type.
|
|
427
606
|
*/
|
|
428
|
-
export interface GlobalStreamIndexConfig<TEvent> {
|
|
607
|
+
export interface GlobalStreamIndexConfig<TEvent, TStreamId = StreamIndexStreamId, TState = unknown> {
|
|
429
608
|
readonly type: "gsi";
|
|
430
609
|
readonly index: string;
|
|
431
610
|
readonly pk: string;
|
|
432
611
|
readonly sk: string;
|
|
433
|
-
readonly key: StreamIndexKey<TEvent>;
|
|
612
|
+
readonly key: StreamIndexKey<TEvent, TStreamId>;
|
|
613
|
+
/** The derived partition — see {@link StreamIndexPartition}. */
|
|
614
|
+
readonly partition?: StreamIndexPartition<TEvent, TStreamId> | undefined;
|
|
615
|
+
/** The snapshot's entry — see {@link StreamIndexSnapshotEntry}. Requires `partition`. */
|
|
616
|
+
readonly snapshot?: StreamIndexSnapshotEntry<TState, TStreamId> | undefined;
|
|
434
617
|
/**
|
|
435
618
|
* What the index copies from each event — see {@link StreamIndexProjection}.
|
|
436
619
|
* Default `"all"`. Must match the deployed index.
|
|
@@ -442,7 +625,7 @@ export interface GlobalStreamIndexConfig<TEvent> {
|
|
|
442
625
|
* ordered by a key derived from each event. See {@link LocalStreamIndexConfig}
|
|
443
626
|
* and {@link GlobalStreamIndexConfig}.
|
|
444
627
|
*/
|
|
445
|
-
export type StreamIndexConfig<TEvent> = LocalStreamIndexConfig<TEvent> | GlobalStreamIndexConfig<TEvent>;
|
|
628
|
+
export type StreamIndexConfig<TEvent, TStreamId = StreamIndexStreamId, TState = unknown> = LocalStreamIndexConfig<TEvent, TStreamId> | GlobalStreamIndexConfig<TEvent, TStreamId, TState>;
|
|
446
629
|
/**
|
|
447
630
|
* What a stream index copies from each event (`projection` on an entry of
|
|
448
631
|
* `makeStream({ indexes })`). {@link indexDefinitions} emits the matching
|
|
@@ -451,7 +634,7 @@ export type StreamIndexConfig<TEvent> = LocalStreamIndexConfig<TEvent> | GlobalS
|
|
|
451
634
|
* | Value | Emitted `Projection` | Read |
|
|
452
635
|
* |---|---|---|
|
|
453
636
|
* | `"all"` (default) | `ALL` | one query |
|
|
454
|
-
* | `"withoutMetadata"` | `INCLUDE` of every event attribute except `metadata` | one query; `metadata` is `undefined` (and typed so) |
|
|
637
|
+
* | `"withoutMetadata"` | `INCLUDE` of every event attribute except `metadata` (plus the snapshot's `asOfVersion` and `state` when a stream indexes its snapshot in it) | one query; `metadata` is `undefined` (and typed so) |
|
|
455
638
|
* | `"keys"` | `INCLUDE ["__edd_e__"]` — **not** `KEYS_ONLY` | LSI: one query with `Select: ALL_ATTRIBUTES` (DynamoDB fetches each event from the table). GSI: the query, then `BatchGetItem` for the events of each page, in index order |
|
|
456
639
|
*
|
|
457
640
|
* `__edd_e__` is always projected because every read filters on it, and
|
|
@@ -484,6 +667,18 @@ export interface StreamIndexSettings {
|
|
|
484
667
|
readonly pk: string;
|
|
485
668
|
readonly sk: string;
|
|
486
669
|
readonly projection: StreamIndexProjection;
|
|
670
|
+
/**
|
|
671
|
+
* Where the index's partition comes from: `"stream"` — the stream's own
|
|
672
|
+
* partition key (every LSI, and a GSI without `partition`) — or
|
|
673
|
+
* `"derived"`, a GSI's `partition` function (#149). A stream always sets
|
|
674
|
+
* it; absent (a settings object built by hand) reads as `"stream"`.
|
|
675
|
+
*/
|
|
676
|
+
readonly partition?: "stream" | "derived" | undefined;
|
|
677
|
+
/**
|
|
678
|
+
* Whether the stream's snapshot is in the index (a GSI's `snapshot`, #149).
|
|
679
|
+
* A stream always sets it; absent reads as `false`.
|
|
680
|
+
*/
|
|
681
|
+
readonly snapshot?: boolean | undefined;
|
|
487
682
|
}
|
|
488
683
|
/**
|
|
489
684
|
* The `metadata` type of an event read through a stream index of projection
|
|
@@ -504,6 +699,16 @@ type ProjectionOf<C> = C extends {
|
|
|
504
699
|
export type IndexProjectionsOf<TIndexes> = {
|
|
505
700
|
readonly [K in keyof TIndexes & string]: ProjectionOf<TIndexes[K]>;
|
|
506
701
|
};
|
|
702
|
+
/**
|
|
703
|
+
* The names of the indexes of a `makeStream({ indexes })` config that derive
|
|
704
|
+
* their partition (`partition`, #149) — those {@link EventStream.readIndex}
|
|
705
|
+
* and `query.index` read with an explicit `partition`.
|
|
706
|
+
*/
|
|
707
|
+
export type PartitionedIndexesOf<TIndexes> = {
|
|
708
|
+
readonly [K in keyof TIndexes & string]: TIndexes[K] extends {
|
|
709
|
+
readonly partition: (...args: ReadonlyArray<any>) => unknown;
|
|
710
|
+
} ? K : never;
|
|
711
|
+
}[keyof TIndexes & string];
|
|
507
712
|
/** @internal The members a stream index entry may declare. */
|
|
508
713
|
type StreamIndexConfigKey = keyof LocalStreamIndexConfig<unknown> | keyof GlobalStreamIndexConfig<unknown>;
|
|
509
714
|
/**
|
|
@@ -513,8 +718,8 @@ type StreamIndexConfigKey = keyof LocalStreamIndexConfig<unknown> | keyof Global
|
|
|
513
718
|
* `TIndexes` from the entries turns off TypeScript's own excess-property check,
|
|
514
719
|
* which would otherwise catch it.
|
|
515
720
|
*/
|
|
516
|
-
export type StreamIndexesConfig<TIndexes, TEvent> = {
|
|
517
|
-
readonly [K in keyof TIndexes]: StreamIndexConfig<TEvent> & {
|
|
721
|
+
export type StreamIndexesConfig<TIndexes, TEvent, TStreamId = StreamIndexStreamId, TState = unknown> = {
|
|
722
|
+
readonly [K in keyof TIndexes]: StreamIndexConfig<TEvent, TStreamId, TState> & {
|
|
518
723
|
readonly [P in Exclude<keyof TIndexes[K], StreamIndexConfigKey>]: never;
|
|
519
724
|
};
|
|
520
725
|
};
|
|
@@ -532,6 +737,9 @@ export type StreamIndexesConfig<TIndexes, TEvent> = {
|
|
|
532
737
|
* - `consistentRead` — strongly consistent read. DynamoDB rejects it on a
|
|
533
738
|
* GSI-backed index, which it reads only eventually consistently: the read
|
|
534
739
|
* fails with a `ValidationError` naming the index.
|
|
740
|
+
*
|
|
741
|
+
* An index that derives its partition (#149) also takes `partition` — see
|
|
742
|
+
* {@link ReadIndexArgs}.
|
|
535
743
|
*/
|
|
536
744
|
export type ReadIndexOptions = {
|
|
537
745
|
readonly reverse?: boolean | undefined;
|
|
@@ -544,9 +752,47 @@ export type ReadIndexOptions = {
|
|
|
544
752
|
readonly between?: readonly [string, string] | undefined;
|
|
545
753
|
readonly beginsWith?: undefined;
|
|
546
754
|
});
|
|
755
|
+
/**
|
|
756
|
+
* The options argument of {@link EventStream.readIndex} for index `N`. An
|
|
757
|
+
* index that derives its partition (`partition`, #149) is read one partition
|
|
758
|
+
* at a time, so `partition` — the value to read, as the index's `partition`
|
|
759
|
+
* function returns it — is required, and the read returns the events of the
|
|
760
|
+
* given stream in it. Any other index is scoped to the stream already: a
|
|
761
|
+
* `partition` passed for it fails the read with `ValidationError` (it stays
|
|
762
|
+
* in the type so that a stream whose indexes derive their partition remains
|
|
763
|
+
* assignable to one whose indexes do not, as every stream is to
|
|
764
|
+
* `EventStream<E, F, M, S>`).
|
|
765
|
+
*/
|
|
766
|
+
export type ReadIndexArgs<N extends string, TPartitionedIndexName extends string> = [
|
|
767
|
+
TPartitionedIndexName
|
|
768
|
+
] extends [never] ? [
|
|
769
|
+
options?: (ReadIndexOptions & {
|
|
770
|
+
readonly partition?: string | undefined;
|
|
771
|
+
}) | undefined
|
|
772
|
+
] : [N] extends [TPartitionedIndexName] ? [options: ReadIndexOptions & {
|
|
773
|
+
readonly partition: string;
|
|
774
|
+
}] : [options?: (ReadIndexOptions & {
|
|
775
|
+
readonly partition?: string | undefined;
|
|
776
|
+
}) | undefined];
|
|
777
|
+
/**
|
|
778
|
+
* The options argument of `query.index` for index `N` — `partition`, required
|
|
779
|
+
* for an index that derives its partition, as for {@link ReadIndexArgs}; on
|
|
780
|
+
* any other index, one passed throws `[EDD-9080]`.
|
|
781
|
+
*/
|
|
782
|
+
export type IndexQueryArgs<N extends string, TPartitionedIndexName extends string> = [
|
|
783
|
+
TPartitionedIndexName
|
|
784
|
+
] extends [never] ? [options?: {
|
|
785
|
+
readonly partition?: string | undefined;
|
|
786
|
+
} | undefined] : [N] extends [TPartitionedIndexName] ? [options: {
|
|
787
|
+
readonly partition: string;
|
|
788
|
+
}] : [options?: {
|
|
789
|
+
readonly partition?: string | undefined;
|
|
790
|
+
} | undefined];
|
|
547
791
|
type StreamIdInput<T extends ReadonlyArray<string>> = {
|
|
548
792
|
readonly [K in T[number]]: string;
|
|
549
793
|
};
|
|
794
|
+
/** @internal The state type of a `makeStream({ snapshot })` config; `never` without one. */
|
|
795
|
+
type SnapshotStateOf<TSnapshot> = TSnapshot extends SnapshotConfig<infer TStateSchema> ? Schema.Schema.Type<TStateSchema> : never;
|
|
550
796
|
declare const EventStreamTypeId: unique symbol;
|
|
551
797
|
export type EventStreamTypeId = typeof EventStreamTypeId;
|
|
552
798
|
/**
|
|
@@ -560,12 +806,14 @@ export type EventStreamTypeId = typeof EventStreamTypeId;
|
|
|
560
806
|
* `query.index` only accept a declared name. `TIndexProjections` is each
|
|
561
807
|
* index's {@link StreamIndexProjection} by name, so an event read through a
|
|
562
808
|
* `"withoutMetadata"` index has `metadata: undefined`.
|
|
809
|
+
* `TPartitionedIndexName` names the indexes that derive their partition
|
|
810
|
+
* (#149), which are read with an explicit `partition`.
|
|
563
811
|
*/
|
|
564
812
|
export interface EventStream<TEvent, TStreamIdFields extends ReadonlyArray<string>, TMetadata, TState = never, TIndexName extends string = never, TIndexProjections extends {
|
|
565
813
|
readonly [K in TIndexName]: StreamIndexProjection;
|
|
566
814
|
} = {
|
|
567
815
|
readonly [K in TIndexName]: "all";
|
|
568
|
-
}> extends Pipeable.Pipeable {
|
|
816
|
+
}, TPartitionedIndexName extends string = never> extends Pipeable.Pipeable {
|
|
569
817
|
readonly [EventStreamTypeId]: EventStreamTypeId;
|
|
570
818
|
readonly streamName: string;
|
|
571
819
|
readonly eventSchema: Schema.Top;
|
|
@@ -584,6 +832,11 @@ export interface EventStream<TEvent, TStreamIdFields extends ReadonlyArray<strin
|
|
|
584
832
|
* snapshots.
|
|
585
833
|
*/
|
|
586
834
|
readonly snapshotConfig: SnapshotSettings | undefined;
|
|
835
|
+
/**
|
|
836
|
+
* How the stream stores event `data` and snapshot `state` — see
|
|
837
|
+
* {@link StreamStorageConfig} and {@link SnapshotConfig.storedAs}.
|
|
838
|
+
*/
|
|
839
|
+
readonly storedAs: StreamStorageSettings;
|
|
587
840
|
/**
|
|
588
841
|
* Write (or overwrite) this stream's snapshot.
|
|
589
842
|
*
|
|
@@ -673,8 +926,71 @@ export interface EventStream<TEvent, TStreamIdFields extends ReadonlyArray<strin
|
|
|
673
926
|
* `metadata: undefined`, as the type says); for `"keys"`, one query with
|
|
674
927
|
* `Select: ALL_ATTRIBUTES` on an LSI, or the query followed by `BatchGetItem`
|
|
675
928
|
* (unprocessed keys retried, index order kept) on a GSI.
|
|
929
|
+
*
|
|
930
|
+
* An index that derives its partition (`partition`, #149) is read one
|
|
931
|
+
* partition at a time: pass the partition as `options.partition` (required
|
|
932
|
+
* by the types — see {@link ReadIndexArgs}). The read keeps the events of
|
|
933
|
+
* `streamId` in it — not the other streams sharing the partition, nor any
|
|
934
|
+
* snapshot — by filtering on the table's `pk`, which every index projects.
|
|
935
|
+
*/
|
|
936
|
+
readIndex<N extends TIndexName>(name: N, streamId: StreamIdInput<TStreamIdFields>, ...options: ReadIndexArgs<N, TPartitionedIndexName>): Effect.Effect<ReadonlyArray<StreamEvent<TEvent, IndexEventMetadata<TMetadata, TIndexProjections[N]>>>, DynamoClientError | ValidationError, DynamoClient | TableConfig>;
|
|
937
|
+
/**
|
|
938
|
+
* The bytes {@link EventStream.append} would write for these arguments, by
|
|
939
|
+
* DynamoDB's own size accounting — for planning chunks against the 400 KB
|
|
940
|
+
* item and 4 MB transaction limits without re-implementing the stream's
|
|
941
|
+
* item layout or packing. The items are built by the same code `append`
|
|
942
|
+
* uses (envelope, keys, packed and compressed `data`, metadata, stream-index
|
|
943
|
+
* attributes, the sentinel, the inline snapshot), so the numbers are exact
|
|
944
|
+
* for the request `append` would send now with this runtime's compressors.
|
|
945
|
+
*
|
|
946
|
+
* Nothing is read or written, and nothing is refused for its size: this is
|
|
947
|
+
* caller-side planning, not a pre-check. `additionalItems` are not accepted
|
|
948
|
+
* — add their size yourself. Fails as `append` would on invalid options
|
|
949
|
+
* (`ValidationError`), and — with a `CompressorNotRegistered` cause — when a
|
|
950
|
+
* value crosses its compression threshold without a registered compressor.
|
|
951
|
+
*/
|
|
952
|
+
measureAppend(streamId: StreamIdInput<TStreamIdFields>, events: ReadonlyArray<TEvent>, expectedVersion: number, options?: MeasureAppendOptions<TMetadata, TState> | undefined): Effect.Effect<AppendMeasurement, ValidationError, TableConfig>;
|
|
953
|
+
/**
|
|
954
|
+
* The item size, in bytes, of the snapshot {@link EventStream.writeSnapshot}
|
|
955
|
+
* (or an inline snapshot at `asOfVersion`) would write — packed `state` as
|
|
956
|
+
* stored. Caller-side planning, like {@link EventStream.measureAppend}. Dies
|
|
957
|
+
* with `[EDD-9026]` on a stream without a `snapshot` config.
|
|
958
|
+
*/
|
|
959
|
+
measureSnapshot(streamId: StreamIdInput<TStreamIdFields>, state: TState, asOfVersion: number): Effect.Effect<number, ValidationError, TableConfig>;
|
|
960
|
+
/**
|
|
961
|
+
* Decode a DynamoDB Streams record image (`NewImage` / `OldImage`) of one
|
|
962
|
+
* of this stream's events into exactly the {@link StreamEvent} the read
|
|
963
|
+
* paths return — `data` decoded through the event schemas (by its stored
|
|
964
|
+
* form: native map, JSON string or compressed binary), `metadata` through
|
|
965
|
+
* the metadata schema when the stream declares one.
|
|
966
|
+
*
|
|
967
|
+
* Accepts DynamoDB JSON as Lambda and EventBridge Pipes deliver it (`B` /
|
|
968
|
+
* `BS` members as base64 text, or as bytes), or an already-unmarshalled
|
|
969
|
+
* item (a packed `data` as bytes or base64 text). `Option.none()` for an
|
|
970
|
+
* image that is not one of this stream's events — its snapshot, an
|
|
971
|
+
* idempotency sentinel, another stream's or an entity's item — so one
|
|
972
|
+
* handler can fan a table's records across decoders. The type is read off
|
|
973
|
+
* the raw image before anything is unmarshalled, so nothing in another
|
|
974
|
+
* item's image (a number the SDK cannot represent, say) fails the decode.
|
|
975
|
+
* An event image that does not decode fails with `ValidationError`.
|
|
976
|
+
*
|
|
977
|
+
* With `{ raw: true }`, each event also carries `rawData`: its `data` as
|
|
978
|
+
* stored, in wire form, unmodelled fields included (see
|
|
979
|
+
* {@link RawStreamEvent}).
|
|
980
|
+
*
|
|
981
|
+
* The compressor registry is not in the type (`R = never`): this unbound
|
|
982
|
+
* method reads the `Compression` service from the running fiber. When the
|
|
983
|
+
* stream's `storedAs.data` or `snapshot.storedAs` names a codec that
|
|
984
|
+
* registry does not hold — including no `Compression` at all, for example
|
|
985
|
+
* one lost behind `Layer.provide` — it dies with the `[EDD-9078]`
|
|
986
|
+
* configuration defect on first use, whatever the image, rather than
|
|
987
|
+
* failing with a typed error. Provide `Compression` where the handler runs,
|
|
988
|
+
* or decode through {@link EventStore.bind}, which captures it.
|
|
676
989
|
*/
|
|
677
|
-
|
|
990
|
+
decodeStreamImage(image: StreamImage, options: DecodeStreamImageOptions & {
|
|
991
|
+
readonly raw: true;
|
|
992
|
+
}): Effect.Effect<Option.Option<RawStreamEvent<TEvent, StreamMetadata<TMetadata>>>, ValidationError, never>;
|
|
993
|
+
decodeStreamImage(image: StreamImage, options?: DecodeStreamImageOptions): Effect.Effect<Option.Option<StreamEvent<TEvent, StreamMetadata<TMetadata>>>, ValidationError, never>;
|
|
678
994
|
readonly query: {
|
|
679
995
|
events(streamId: StreamIdInput<TStreamIdFields>): Query.Query<StreamEvent<TEvent, StreamMetadata<TMetadata>>>;
|
|
680
996
|
/**
|
|
@@ -692,8 +1008,12 @@ export interface EventStream<TEvent, TStreamIdFields extends ReadonlyArray<strin
|
|
|
692
1008
|
* counts index entries and fetches nothing. On a `"keys"` or
|
|
693
1009
|
* `"withoutMetadata"` index, a `Query.filter` on an attribute the index
|
|
694
1010
|
* does not project is refused by DynamoDB — use `Query.filterBy`.
|
|
1011
|
+
*
|
|
1012
|
+
* An index that derives its partition (#149) takes the partition to read
|
|
1013
|
+
* as `options.partition` (see {@link IndexQueryArgs}), and the query keeps
|
|
1014
|
+
* `streamId`'s events in it, as {@link EventStream.readIndex} does.
|
|
695
1015
|
*/
|
|
696
|
-
index<N extends TIndexName>(name: N, streamId: StreamIdInput<TStreamIdFields>): Query.Query<StreamEvent<TEvent, IndexEventMetadata<TMetadata, TIndexProjections[N]>>>;
|
|
1016
|
+
index<N extends TIndexName>(name: N, streamId: StreamIdInput<TStreamIdFields>, ...options: IndexQueryArgs<N, TPartitionedIndexName>): Query.Query<StreamEvent<TEvent, IndexEventMetadata<TMetadata, TIndexProjections[N]>>>;
|
|
697
1017
|
};
|
|
698
1018
|
}
|
|
699
1019
|
/**
|
|
@@ -753,11 +1073,20 @@ export interface EventStream<TEvent, TStreamIdFields extends ReadonlyArray<strin
|
|
|
753
1073
|
* `indexes` declares sub-streams ordered by a key derived from each event
|
|
754
1074
|
* (#140), on a local (default) or global secondary index. `append` writes each
|
|
755
1075
|
* event's derived key (and, for a GSI, the stream's partition key) on the
|
|
756
|
-
* event item
|
|
757
|
-
*
|
|
758
|
-
* `query.index`, and create the
|
|
759
|
-
* index's `projection` (default
|
|
760
|
-
* and how it is read — see
|
|
1076
|
+
* event item. Idempotency sentinels never carry them; snapshots carry an
|
|
1077
|
+
* entry only in a GSI that derives its partition and declares `snapshot`.
|
|
1078
|
+
* Read with {@link EventStream.readIndex} or `query.index`, and create the
|
|
1079
|
+
* indexes with {@link indexDefinitions}. Each index's `projection` (default
|
|
1080
|
+
* `"all"`) sets what it copies from each event and how it is read — see
|
|
1081
|
+
* {@link StreamIndexProjection}.
|
|
1082
|
+
*
|
|
1083
|
+
* A GSI's `partition` (#149) derives the partition from the stream id and the
|
|
1084
|
+
* event instead, stored raw, so several streams declaring the same GSI write
|
|
1085
|
+
* one partition of it — an order's line, payment and shipment streams under
|
|
1086
|
+
* `ORDER#<orderId>`, read by one query — and its `snapshot` puts each stream's
|
|
1087
|
+
* snapshot item in that partition too, in the same `Put` (so an inline
|
|
1088
|
+
* snapshot enters the index in the append's own transaction). See
|
|
1089
|
+
* {@link GlobalStreamIndexConfig}.
|
|
761
1090
|
*
|
|
762
1091
|
* Index attributes are written only at append time and events are never
|
|
763
1092
|
* rewritten. Declaring an index on a stream that already holds events, or
|
|
@@ -769,6 +1098,36 @@ export interface EventStream<TEvent, TStreamIdFields extends ReadonlyArray<strin
|
|
|
769
1098
|
*
|
|
770
1099
|
* @example
|
|
771
1100
|
* ```typescript
|
|
1101
|
+
* // Lines and payments of an order share `gsi1` under `ORDER#<orderId>`.
|
|
1102
|
+
* const OrderLines = EventStore.makeStream({
|
|
1103
|
+
* table: EventsTable,
|
|
1104
|
+
* streamName: "OrderLine",
|
|
1105
|
+
* events: [LineAdded, LineRemoved],
|
|
1106
|
+
* streamId: { composite: ["orderId", "lineId"] },
|
|
1107
|
+
* snapshot: { schema: LineState, mode: "inline" },
|
|
1108
|
+
* indexes: {
|
|
1109
|
+
* byOrder: {
|
|
1110
|
+
* type: "gsi",
|
|
1111
|
+
* index: "gsi1",
|
|
1112
|
+
* pk: "gsi1pk",
|
|
1113
|
+
* sk: "gsi1sk",
|
|
1114
|
+
* partition: (_event, _version, streamId) => `ORDER#${streamId.orderId}`,
|
|
1115
|
+
* key: (_event, version, streamId) => `LINE#${streamId.lineId}#EVT#${pad(version)}`,
|
|
1116
|
+
* snapshot: (_state, _asOfVersion, streamId) => ({
|
|
1117
|
+
* partition: `ORDER#${streamId.orderId}`,
|
|
1118
|
+
* key: `LINE#${streamId.lineId}#STATE`,
|
|
1119
|
+
* }),
|
|
1120
|
+
* },
|
|
1121
|
+
* },
|
|
1122
|
+
* })
|
|
1123
|
+
*
|
|
1124
|
+
* const lineEvents = yield* OrderLines.readIndex("byOrder", { orderId, lineId }, {
|
|
1125
|
+
* partition: `ORDER#${orderId}`,
|
|
1126
|
+
* })
|
|
1127
|
+
* ```
|
|
1128
|
+
*
|
|
1129
|
+
* @example
|
|
1130
|
+
* ```typescript
|
|
772
1131
|
* const Entries = EventStore.makeStream({
|
|
773
1132
|
* table: EventsTable,
|
|
774
1133
|
* streamName: "Ledger",
|
|
@@ -793,10 +1152,12 @@ export interface EventStream<TEvent, TStreamIdFields extends ReadonlyArray<strin
|
|
|
793
1152
|
* @throws `[EDD-9062]` when `snapshot.mode` is neither `"after-append"` nor
|
|
794
1153
|
* `"inline"`.
|
|
795
1154
|
* @throws `[EDD-9063]` for a malformed index: a `type` other than `"lsi"` /
|
|
796
|
-
* `"gsi"`, a `gsi` without `pk`, an `lsi` with `pk`,
|
|
797
|
-
*
|
|
798
|
-
* `
|
|
799
|
-
*
|
|
1155
|
+
* `"gsi"`, a `gsi` without `pk`, an `lsi` with `pk`, `partition` or
|
|
1156
|
+
* `snapshot`, an empty `index` / `sk` / `pk`, a `key` / `partition` /
|
|
1157
|
+
* `snapshot` that is not a function, a `snapshot` without `partition` or on
|
|
1158
|
+
* a stream without a `snapshot` config, a `projection` other than `"all"` /
|
|
1159
|
+
* `"withoutMetadata"` / `"keys"`, or a member an index entry does not
|
|
1160
|
+
* declare (such as a misspelled `projection`, also a type error).
|
|
800
1161
|
* @throws `[EDD-9064]` when an index attribute is one the stream writes itself
|
|
801
1162
|
* (`pk`, `sk`, `__edd_e__`, `streamId`, `version`, `eventType`, `data`,
|
|
802
1163
|
* `metadata`, `timestamp`, `asOfVersion`, `state`, `commandId`, or the
|
|
@@ -805,10 +1166,22 @@ export interface EventStream<TEvent, TStreamIdFields extends ReadonlyArray<strin
|
|
|
805
1166
|
* with `ValidationError`.
|
|
806
1167
|
* @throws `[EDD-9065]` when two indexes share a physical index name or an
|
|
807
1168
|
* attribute (or a GSI's `pk` and `sk` are the same attribute).
|
|
1169
|
+
* @throws `[EDD-9079]` when `storedAs` declares a key other than `data`
|
|
1170
|
+
* (metadata always stays a map), or a `storedAs.data` / `snapshot.storedAs`
|
|
1171
|
+
* value is not a `DynamoModel.Packed(...)` config.
|
|
1172
|
+
* @throws `[EDD-9073]` when `snapshot.storedAs` is set and the state schema's
|
|
1173
|
+
* encoded side is not object- or array-shaped.
|
|
1174
|
+
*
|
|
1175
|
+
* `storedAs: { data: DynamoModel.Packed(...) }` stores event `data` packed, and
|
|
1176
|
+
* `snapshot.storedAs` the snapshot `state` — see {@link StreamStorageConfig}.
|
|
1177
|
+
* Register the codecs a compressed config names through the `Compression`
|
|
1178
|
+
* service (`Compression.layer([...])` merged into the application layer): a
|
|
1179
|
+
* bound stream refuses to bind without them, and an unbound one to run its
|
|
1180
|
+
* first operation (`[EDD-9078]`).
|
|
808
1181
|
*/
|
|
809
1182
|
export declare const makeStream: <const TEvents extends ReadonlyArray<Schema.Top>, TTable extends Table, const TStreamName extends string, const TStreamId extends {
|
|
810
1183
|
readonly composite: ReadonlyArray<string>;
|
|
811
|
-
}, TMetadata extends Schema.Top | undefined = undefined, TSnapshot extends SnapshotConfig | undefined = undefined, const TIndexes extends { readonly [K in keyof TIndexes]: StreamIndexConfig<Schema.Schema.Type<TEvents[number]>>; } = {}>(config: {
|
|
1184
|
+
}, TMetadata extends Schema.Top | undefined = undefined, TSnapshot extends SnapshotConfig | undefined = undefined, const TIndexes extends { readonly [K in keyof TIndexes]: StreamIndexConfig<Schema.Schema.Type<TEvents[number]>, StreamIdInput<TStreamId["composite"]>, SnapshotStateOf<TSnapshot>>; } = {}>(config: {
|
|
812
1185
|
readonly table: TTable;
|
|
813
1186
|
readonly streamName: TStreamName;
|
|
814
1187
|
readonly events: TEvents;
|
|
@@ -816,8 +1189,10 @@ export declare const makeStream: <const TEvents extends ReadonlyArray<Schema.Top
|
|
|
816
1189
|
readonly metadata?: TMetadata;
|
|
817
1190
|
readonly snapshot?: TSnapshot;
|
|
818
1191
|
readonly casing?: DynamoSchema.Casing | undefined;
|
|
819
|
-
readonly indexes?: TIndexes & StreamIndexesConfig<TIndexes, Schema.Schema.Type<TEvents[number]>>;
|
|
820
|
-
|
|
1192
|
+
readonly indexes?: TIndexes & StreamIndexesConfig<TIndexes, Schema.Schema.Type<TEvents[number]>, StreamIdInput<TStreamId["composite"]>, SnapshotStateOf<TSnapshot>>;
|
|
1193
|
+
/** How event `data` is stored — see {@link StreamStorageConfig}. */
|
|
1194
|
+
readonly storedAs?: StreamStorageConfig | undefined;
|
|
1195
|
+
}) => EventStream<Schema.Schema.Type<TEvents[number]>, TStreamId["composite"], TMetadata extends Schema.Top ? Schema.Schema.Type<TMetadata> : undefined, SnapshotStateOf<TSnapshot>, keyof TIndexes & string, IndexProjectionsOf<TIndexes>, PartitionedIndexesOf<TIndexes>>;
|
|
821
1196
|
/**
|
|
822
1197
|
* An EventStream whose operations have `DynamoClient` and `TableConfig` already
|
|
823
1198
|
* resolved, so all methods return `Effect<A, E, never>`.
|
|
@@ -842,12 +1217,14 @@ export interface BoundEventStream<TEvent, TStreamIdFields extends ReadonlyArray<
|
|
|
842
1217
|
readonly [K in TIndexName]: StreamIndexProjection;
|
|
843
1218
|
} = {
|
|
844
1219
|
readonly [K in TIndexName]: "all";
|
|
845
|
-
}> extends Pipeable.Pipeable {
|
|
1220
|
+
}, TPartitionedIndexName extends string = never> extends Pipeable.Pipeable {
|
|
846
1221
|
readonly [EventStreamTypeId]: EventStreamTypeId;
|
|
847
1222
|
readonly streamName: string;
|
|
848
1223
|
readonly eventSchema: Schema.Top;
|
|
849
1224
|
/** See {@link EventStream.snapshotConfig}. */
|
|
850
1225
|
readonly snapshotConfig: SnapshotSettings | undefined;
|
|
1226
|
+
/** See {@link EventStream.storedAs}. */
|
|
1227
|
+
readonly storedAs: StreamStorageSettings;
|
|
851
1228
|
/** See {@link EventStream.indexes}. */
|
|
852
1229
|
readonly indexes: {
|
|
853
1230
|
readonly [K in TIndexName]: StreamIndexSettings;
|
|
@@ -867,15 +1244,27 @@ export interface BoundEventStream<TEvent, TStreamIdFields extends ReadonlyArray<
|
|
|
867
1244
|
/** See {@link EventStream.readLatest}. */
|
|
868
1245
|
readLatest(streamId: StreamIdInput<TStreamIdFields>, options?: ReadLatestOptions | undefined): Effect.Effect<LatestState<TState, TEvent, StreamMetadata<TMetadata>>, DynamoClientError | ValidationError, never>;
|
|
869
1246
|
/** See {@link EventStream.readIndex}. */
|
|
870
|
-
readIndex<N extends TIndexName>(name: N, streamId: StreamIdInput<TStreamIdFields>, options
|
|
1247
|
+
readIndex<N extends TIndexName>(name: N, streamId: StreamIdInput<TStreamIdFields>, ...options: ReadIndexArgs<N, TPartitionedIndexName>): Effect.Effect<ReadonlyArray<StreamEvent<TEvent, IndexEventMetadata<TMetadata, TIndexProjections[N]>>>, DynamoClientError | ValidationError, never>;
|
|
1248
|
+
/** See {@link EventStream.measureAppend}. */
|
|
1249
|
+
measureAppend(streamId: StreamIdInput<TStreamIdFields>, events: ReadonlyArray<TEvent>, expectedVersion: number, options?: MeasureAppendOptions<TMetadata, TState> | undefined): Effect.Effect<AppendMeasurement, ValidationError, never>;
|
|
1250
|
+
/** See {@link EventStream.measureSnapshot}. */
|
|
1251
|
+
measureSnapshot(streamId: StreamIdInput<TStreamIdFields>, state: TState, asOfVersion: number): Effect.Effect<number, ValidationError, never>;
|
|
1252
|
+
/** See {@link EventStream.decodeStreamImage}. */
|
|
1253
|
+
decodeStreamImage(image: StreamImage, options: DecodeStreamImageOptions & {
|
|
1254
|
+
readonly raw: true;
|
|
1255
|
+
}): Effect.Effect<Option.Option<RawStreamEvent<TEvent, StreamMetadata<TMetadata>>>, ValidationError, never>;
|
|
1256
|
+
decodeStreamImage(image: StreamImage, options?: DecodeStreamImageOptions): Effect.Effect<Option.Option<StreamEvent<TEvent, StreamMetadata<TMetadata>>>, ValidationError, never>;
|
|
871
1257
|
/**
|
|
872
1258
|
* The stream's query builders — unbound: run them with {@link provide}, or
|
|
873
|
-
* where `DynamoClient` and `TableConfig` are provided.
|
|
1259
|
+
* where `DynamoClient` and `TableConfig` are provided. They do not use the
|
|
1260
|
+
* context this bound stream captured unless run through {@link provide}:
|
|
1261
|
+
* run directly, raw-row unpacking (`Query.select`) and decoding read the
|
|
1262
|
+
* `Compression` registry of the caller's context, not the bound one.
|
|
874
1263
|
*/
|
|
875
1264
|
readonly query: {
|
|
876
1265
|
events(streamId: StreamIdInput<TStreamIdFields>): Query.Query<StreamEvent<TEvent, StreamMetadata<TMetadata>>>;
|
|
877
1266
|
/** See `EventStream.query.index`. */
|
|
878
|
-
index<N extends TIndexName>(name: N, streamId: StreamIdInput<TStreamIdFields>): Query.Query<StreamEvent<TEvent, IndexEventMetadata<TMetadata, TIndexProjections[N]>>>;
|
|
1267
|
+
index<N extends TIndexName>(name: N, streamId: StreamIdInput<TStreamIdFields>, ...options: IndexQueryArgs<N, TPartitionedIndexName>): Query.Query<StreamEvent<TEvent, IndexEventMetadata<TMetadata, TIndexProjections[N]>>>;
|
|
879
1268
|
};
|
|
880
1269
|
/** Escape hatch: provide DynamoClient | TableConfig to an arbitrary effect. */
|
|
881
1270
|
readonly provide: <A, E>(effect: Effect.Effect<A, E, DynamoClient | TableConfig>) => Effect.Effect<A, E, never>;
|
|
@@ -894,7 +1283,7 @@ export interface BoundEventStream<TEvent, TStreamIdFields extends ReadonlyArray<
|
|
|
894
1283
|
* yield* stream.append({ matchId: "m-1" }, [event], 0) // R = never
|
|
895
1284
|
* ```
|
|
896
1285
|
*/
|
|
897
|
-
export declare const bind: <TEvent, TStreamIdFields extends ReadonlyArray<string>, TMetadata, TState, TIndexName extends string = never, TIndexProjections extends { readonly [K in TIndexName]: StreamIndexProjection; } = { readonly [K in TIndexName]: "all"; }>(stream: EventStream<TEvent, TStreamIdFields, TMetadata, TState, TIndexName, TIndexProjections>) => Effect.Effect<BoundEventStream<TEvent, TStreamIdFields, TMetadata, TState, TIndexName, TIndexProjections>, never, DynamoClient | TableConfig>;
|
|
1286
|
+
export declare const bind: <TEvent, TStreamIdFields extends ReadonlyArray<string>, TMetadata, TState, TIndexName extends string = never, TIndexProjections extends { readonly [K in TIndexName]: StreamIndexProjection; } = { readonly [K in TIndexName]: "all"; }, TPartitionedIndexName extends string = never>(stream: EventStream<TEvent, TStreamIdFields, TMetadata, TState, TIndexName, TIndexProjections, TPartitionedIndexName>) => Effect.Effect<BoundEventStream<TEvent, TStreamIdFields, TMetadata, TState, TIndexName, TIndexProjections, TPartitionedIndexName>, never, DynamoClient | TableConfig>;
|
|
898
1287
|
/**
|
|
899
1288
|
* `CreateTable` fragments for the stream indexes of one or more streams — see
|
|
900
1289
|
* {@link indexDefinitions}. The lists are absent when empty, because
|
|
@@ -920,7 +1309,7 @@ export interface StreamIndexDefinitions {
|
|
|
920
1309
|
* | `projection` | `Projection` |
|
|
921
1310
|
* |---|---|
|
|
922
1311
|
* | `"all"` (default) | `{ ProjectionType: "ALL" }` |
|
|
923
|
-
* | `"withoutMetadata"` | `{ ProjectionType: "INCLUDE", NonKeyAttributes: ["__edd_e__", "data", "eventType", "streamId", "timestamp", "version"] }` |
|
|
1312
|
+
* | `"withoutMetadata"` | `{ ProjectionType: "INCLUDE", NonKeyAttributes: ["__edd_e__", "data", "eventType", "streamId", "timestamp", "version"] }` — plus `"asOfVersion"` and `"state"` when a stream declaring the index puts its snapshot in it (#149) |
|
|
924
1313
|
* | `"keys"` | `{ ProjectionType: "INCLUDE", NonKeyAttributes: ["__edd_e__"] }` — not `KEYS_ONLY`: every read filters on `__edd_e__` |
|
|
925
1314
|
*
|
|
926
1315
|
* The deployed index must project what is declared — copy `Projection` from
|
|
@@ -931,7 +1320,12 @@ export interface StreamIndexDefinitions {
|
|
|
931
1320
|
* table over the limit).
|
|
932
1321
|
*
|
|
933
1322
|
* An index shared by several streams with the same definition is emitted
|
|
934
|
-
* once
|
|
1323
|
+
* once — the way several streams write one GSI whose partition they derive
|
|
1324
|
+
* (`partition`, #149): an order's line, payment and shipment streams under
|
|
1325
|
+
* `ORDER#<orderId>`, say. Whether each declaration derives its partition or
|
|
1326
|
+
* indexes its snapshot is not part of the definition: the index's key schema
|
|
1327
|
+
* and projection kind are. Lists are sorted by index name, attributes by
|
|
1328
|
+
* attribute name.
|
|
935
1329
|
*
|
|
936
1330
|
* - An LSI can only be created with the table, and caps the item collection
|
|
937
1331
|
* of every partition key value in the table at 10 GB — entity partitions
|
|
@@ -959,13 +1353,19 @@ export interface StreamIndexDefinitions {
|
|
|
959
1353
|
*
|
|
960
1354
|
* @throws `[EDD-9067]` when two streams define the same physical index
|
|
961
1355
|
* differently (another type, other key attributes, or another projection).
|
|
1356
|
+
* Whether a declaration derives its partition or indexes its snapshot is not
|
|
1357
|
+
* part of the index's definition: streams that differ only there share it.
|
|
962
1358
|
*/
|
|
963
1359
|
export declare const indexDefinitions: (...streams: ReadonlyArray<{
|
|
964
1360
|
readonly streamName: string;
|
|
965
1361
|
readonly indexes: {
|
|
966
|
-
readonly [name: string]: Omit<StreamIndexSettings, "projection"> & {
|
|
1362
|
+
readonly [name: string]: Omit<StreamIndexSettings, "projection" | "partition" | "snapshot"> & {
|
|
967
1363
|
/** Read as `"all"` when absent (a hand-built settings object). */
|
|
968
1364
|
readonly projection?: StreamIndexProjection | undefined;
|
|
1365
|
+
/** Read as `"stream"` when absent (a hand-built settings object). */
|
|
1366
|
+
readonly partition?: "stream" | "derived" | undefined;
|
|
1367
|
+
/** Read as `false` when absent (a hand-built settings object). */
|
|
1368
|
+
readonly snapshot?: boolean | undefined;
|
|
969
1369
|
};
|
|
970
1370
|
};
|
|
971
1371
|
}>) => StreamIndexDefinitions;
|
|
@@ -1326,7 +1726,7 @@ type BoundCommandHandler<State, Command, TEvent, E, TStreamIdFields extends Read
|
|
|
1326
1726
|
* stream is also structurally an `EventStream`), a {@link CommandHandler}
|
|
1327
1727
|
* otherwise.
|
|
1328
1728
|
*/
|
|
1329
|
-
type CommandHandlerFor<S, State, Command, TEvent, E, TConfig extends CommandHandlerOptions | undefined> = S extends BoundEventStream<any, infer TStreamIdFields, infer TMetadata, any, any> ? BoundCommandHandler<State, Command, TEvent, E, TStreamIdFields, TMetadata, TConfig> : S extends EventStream<any, infer TStreamIdFields, infer TMetadata, any, any> ? CommandHandler<State, Command, TEvent, E, TStreamIdFields, TMetadata, TConfig> : never;
|
|
1729
|
+
type CommandHandlerFor<S, State, Command, TEvent, E, TConfig extends CommandHandlerOptions | undefined> = S extends BoundEventStream<any, infer TStreamIdFields, infer TMetadata, any, any, any, any> ? BoundCommandHandler<State, Command, TEvent, E, TStreamIdFields, TMetadata, TConfig> : S extends EventStream<any, infer TStreamIdFields, infer TMetadata, any, any, any, any> ? CommandHandler<State, Command, TEvent, E, TStreamIdFields, TMetadata, TConfig> : never;
|
|
1330
1730
|
/**
|
|
1331
1731
|
* Create a command handler that reads, decides, and appends atomically.
|
|
1332
1732
|
*
|
|
@@ -1426,13 +1826,13 @@ type CommandHandlerFor<S, State, Command, TEvent, E, TConfig extends CommandHand
|
|
|
1426
1826
|
* `EventStreamTypeId` brand of the second argument.
|
|
1427
1827
|
*/
|
|
1428
1828
|
export declare const commandHandler: {
|
|
1429
|
-
<State, Command, TEvent, E, const TConfig extends CommandHandlerOptions | undefined = undefined>(decider: Decider<State, Command, TEvent, E>, options?: TConfig): <S extends BoundEventStream<TEvent, any, any, State, any, any> | EventStream<TEvent, ReadonlyArray<string>, any, State, any, any>>(stream: S) => CommandHandlerFor<S, State, Command, TEvent, E, TConfig>;
|
|
1829
|
+
<State, Command, TEvent, E, const TConfig extends CommandHandlerOptions | undefined = undefined>(decider: Decider<State, Command, TEvent, E>, options?: TConfig): <S extends BoundEventStream<TEvent, any, any, State, any, any, any> | EventStream<TEvent, ReadonlyArray<string>, any, State, any, any, any>>(stream: S) => CommandHandlerFor<S, State, Command, TEvent, E, TConfig>;
|
|
1430
1830
|
<State, Command, TEvent, E, TStreamIdFields extends ReadonlyArray<string>, TMetadata, TState extends State, TIndexName extends string, TIndexProjections extends {
|
|
1431
1831
|
readonly [K in TIndexName]: StreamIndexProjection;
|
|
1432
|
-
}, const TConfig extends CommandHandlerOptions | undefined = undefined>(decider: Decider<State, Command, TEvent, E>, stream: BoundEventStream<TEvent, TStreamIdFields, TMetadata, TState, TIndexName, TIndexProjections>, options?: TConfig): BoundCommandHandler<State, Command, TEvent, E, TStreamIdFields, TMetadata, TConfig>;
|
|
1832
|
+
}, TPartitionedIndexName extends string, const TConfig extends CommandHandlerOptions | undefined = undefined>(decider: Decider<State, Command, TEvent, E>, stream: BoundEventStream<TEvent, TStreamIdFields, TMetadata, TState, TIndexName, TIndexProjections, TPartitionedIndexName>, options?: TConfig): BoundCommandHandler<State, Command, TEvent, E, TStreamIdFields, TMetadata, TConfig>;
|
|
1433
1833
|
<State, Command, TEvent, E, TStreamIdFields extends ReadonlyArray<string>, TMetadata, TState extends State, TIndexName extends string, TIndexProjections extends {
|
|
1434
1834
|
readonly [K in TIndexName]: StreamIndexProjection;
|
|
1435
|
-
}, const TConfig extends CommandHandlerOptions | undefined = undefined>(decider: Decider<State, Command, TEvent, E>, stream: EventStream<TEvent, TStreamIdFields, TMetadata, TState, TIndexName, TIndexProjections>, options?: TConfig): CommandHandler<State, Command, TEvent, E, TStreamIdFields, TMetadata, TConfig>;
|
|
1835
|
+
}, TPartitionedIndexName extends string, const TConfig extends CommandHandlerOptions | undefined = undefined>(decider: Decider<State, Command, TEvent, E>, stream: EventStream<TEvent, TStreamIdFields, TMetadata, TState, TIndexName, TIndexProjections, TPartitionedIndexName>, options?: TConfig): CommandHandler<State, Command, TEvent, E, TStreamIdFields, TMetadata, TConfig>;
|
|
1436
1836
|
};
|
|
1437
1837
|
/**
|
|
1438
1838
|
* Reconstruct state from events by folding through a decider's `evolve` function.
|
|
@@ -1452,5 +1852,4 @@ export declare const foldFrom: {
|
|
|
1452
1852
|
<A>(startState: unknown, events: ReadonlyArray<StreamEvent<A>>): <S, C, E>(decider: Decider<S, C, A, E>) => S;
|
|
1453
1853
|
<S, C, A, E>(decider: Decider<S, C, A, E>, startState: S, events: ReadonlyArray<StreamEvent<A>>): S;
|
|
1454
1854
|
};
|
|
1455
|
-
export {};
|
|
1456
1855
|
//# sourceMappingURL=EventStore.d.ts.map
|