effect-dynamodb 1.30.1 → 1.32.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/EventStore.d.ts +143 -9
- package/dist/EventStore.d.ts.map +1 -1
- package/dist/EventStore.js +216 -15
- package/dist/EventStore.js.map +1 -1
- package/dist/internal/EventProjection.d.ts +19 -6
- package/dist/internal/EventProjection.d.ts.map +1 -1
- package/dist/internal/EventProjection.js +4 -0
- package/dist/internal/EventProjection.js.map +1 -1
- package/dist/internal/MultiProjection.d.ts +11 -4
- package/dist/internal/MultiProjection.d.ts.map +1 -1
- package/dist/internal/MultiProjection.js.map +1 -1
- package/dist/internal/ProjectionEngine.d.ts +19 -0
- package/dist/internal/ProjectionEngine.d.ts.map +1 -1
- package/dist/internal/ProjectionEngine.js +133 -57
- package/dist/internal/ProjectionEngine.js.map +1 -1
- package/dist/internal/TransactWriteOps.d.ts +13 -0
- package/dist/internal/TransactWriteOps.d.ts.map +1 -1
- package/dist/internal/TransactWriteOps.js +22 -1
- package/dist/internal/TransactWriteOps.js.map +1 -1
- package/package.json +2 -2
package/dist/EventStore.js
CHANGED
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
* - `commandHandler` combinator for the load-decide-fold-append cycle
|
|
12
12
|
* (snapshot-aware single-request loads, consistent loads, an optional
|
|
13
13
|
* `VersionConflict` retry policy, caller-supplied `expectedVersion`,
|
|
14
|
-
* decision-derived `additionalItems`, inline snapshots
|
|
14
|
+
* decision-derived `additionalItems`, inline snapshots, a caller's own
|
|
15
|
+
* state record as the load via `loadState`)
|
|
16
|
+
* - Head proofs (`append({ headProof })`, #164): an additional item or the
|
|
17
|
+
* inline snapshot proves the stream head in place of the version-contiguity
|
|
18
|
+
* `ConditionCheck`
|
|
15
19
|
* - Stream indexes (`makeStream({ indexes })`): sub-streams ordered by a key
|
|
16
20
|
* derived from each event, on an LSI or GSI, each projecting all of the
|
|
17
21
|
* event, all but its metadata, or only its keys — `readIndex`,
|
|
@@ -48,7 +52,7 @@ import { itemBytes, transactItemBytes } from "./internal/ItemSize.js";
|
|
|
48
52
|
import { isMultiProjection, } from "./internal/MultiProjection.js";
|
|
49
53
|
import { codecRequirements, currentRegistry, decodeImage, imageEntityType, runtimeCodecCheck, unregisteredCodecError, withRegistry, } from "./internal/Packed.js";
|
|
50
54
|
import { createPathBuilder } from "./internal/PathBuilder.js";
|
|
51
|
-
import { buildTransactWriteItems, GUARDED_TRANSACTION_ATTEMPTS, judgeCancellation, transactItemTarget, } from "./internal/TransactWriteOps.js";
|
|
55
|
+
import { buildTransactWriteItems, GUARDED_TRANSACTION_ATTEMPTS, judgeCancellation, opConditionFailed, transactItemTarget, } from "./internal/TransactWriteOps.js";
|
|
52
56
|
import { diagnosedValidationError, diagnoseFailure, mergeKeyAttributes, withCause, } from "./internal/WriteDiagnosis.js";
|
|
53
57
|
import { fromAttributeMap, toAttributeMap } from "./Marshaller.js";
|
|
54
58
|
import * as Query from "./Query.js";
|
|
@@ -410,6 +414,38 @@ const unverifiedSnapshotRefusal = (streamName, settings, operation) => settings
|
|
|
410
414
|
`snapshot mode "inline" without "every" — the only mode that writes the snapshot in every ` +
|
|
411
415
|
`append, so it is normally at the head. This stream's snapshot is mode "${settings.mode}"` +
|
|
412
416
|
`${settings.every === undefined ? "" : ` with every: ${settings.every}`}.`;
|
|
417
|
+
/**
|
|
418
|
+
* @internal `[EDD-9085]`: a snapshot head proof (`headProof: "snapshot"`) is
|
|
419
|
+
* accepted only by a stream whose snapshot is written in every append
|
|
420
|
+
* (`mode: "inline"` without `every`) — any other snapshot lags the head by
|
|
421
|
+
* design, and would fail the proof's condition. Returns the refusal message,
|
|
422
|
+
* or `undefined` when the stream accepts it.
|
|
423
|
+
*/
|
|
424
|
+
const snapshotProofRefusal = (streamName, settings, operation) => settings !== undefined && settings.mode === "inline" && settings.every === undefined
|
|
425
|
+
? undefined
|
|
426
|
+
: `[EDD-9085] EventStream "${streamName}": ${operation} with headProof: "snapshot" requires ` +
|
|
427
|
+
`snapshot mode "inline" without "every" — the only mode that writes the snapshot in every ` +
|
|
428
|
+
`append, so it can prove the head. ` +
|
|
429
|
+
(settings === undefined
|
|
430
|
+
? "This stream has no snapshot config."
|
|
431
|
+
: `This stream's snapshot is mode "${settings.mode}"` +
|
|
432
|
+
`${settings.every === undefined ? "" : ` with every: ${settings.every}`}.`);
|
|
433
|
+
/**
|
|
434
|
+
* @internal Why a `headProof` is malformed — not `"snapshot"` nor
|
|
435
|
+
* `{ additionalItem: n }` with `n` a non-negative integer — or `undefined`
|
|
436
|
+
* when it is well-formed.
|
|
437
|
+
*/
|
|
438
|
+
const headProofShapeProblem = (headProof) => {
|
|
439
|
+
if (headProof === "snapshot")
|
|
440
|
+
return undefined;
|
|
441
|
+
const n = typeof headProof === "object" && headProof !== null
|
|
442
|
+
? headProof.additionalItem
|
|
443
|
+
: undefined;
|
|
444
|
+
return typeof n === "number" && Number.isInteger(n) && n >= 0
|
|
445
|
+
? undefined
|
|
446
|
+
: `headProof must be "snapshot" or { additionalItem: n } with n a non-negative integer; ` +
|
|
447
|
+
`received ${JSON.stringify(headProof) ?? String(headProof)}. Nothing was written.`;
|
|
448
|
+
};
|
|
413
449
|
/**
|
|
414
450
|
* @internal Why an `eventTimestamps` value cannot be written for `count`
|
|
415
451
|
* events — not an array, the wrong length, or an entry that is neither a
|
|
@@ -946,6 +982,20 @@ export const makeStream = (config) => {
|
|
|
946
982
|
}));
|
|
947
983
|
}
|
|
948
984
|
}
|
|
985
|
+
// The head proof (#164) stands in for the contiguity check, so its
|
|
986
|
+
// own condition failing shares their verdict — ahead of the other
|
|
987
|
+
// additional items.
|
|
988
|
+
const proofFailed = tx.headProof === "snapshot"
|
|
989
|
+
? failedAt(tx.snapshotIndex)
|
|
990
|
+
: tx.headProof !== undefined &&
|
|
991
|
+
opConditionFailed(tx.additional, rawReasons, tx.headProof.additionalItem, tx.guardCount + tx.eventCount);
|
|
992
|
+
if (proofFailed) {
|
|
993
|
+
return Effect.fail(new VersionConflict({
|
|
994
|
+
streamName: config.streamName,
|
|
995
|
+
streamId: streamIdStr,
|
|
996
|
+
expectedVersion: tx.expectedVersion,
|
|
997
|
+
}));
|
|
998
|
+
}
|
|
949
999
|
// The additional items, attributed back to the caller OPS that
|
|
950
1000
|
// produced them (several items can belong to one op). Only an op's
|
|
951
1001
|
// own condition is `AdditionalItemConditionFailed`: a guarded put
|
|
@@ -1153,6 +1203,33 @@ export const makeStream = (config) => {
|
|
|
1153
1203
|
if (snapshotState !== undefined && snapshot === undefined) {
|
|
1154
1204
|
return yield* snapshotUnavailable("append({ snapshot })");
|
|
1155
1205
|
}
|
|
1206
|
+
// The head proof (#164), checked ahead of the no-op fast path, as the
|
|
1207
|
+
// per-event options are. An index into `additionalItems` is checked by
|
|
1208
|
+
// `append`, which has them (`measureAppend` does not take them).
|
|
1209
|
+
const headProof = options?.headProof;
|
|
1210
|
+
if (headProof !== undefined) {
|
|
1211
|
+
const problem = headProofShapeProblem(headProof);
|
|
1212
|
+
if (problem !== undefined) {
|
|
1213
|
+
return yield* new ValidationError({
|
|
1214
|
+
entityType,
|
|
1215
|
+
operation: "EventStore.append.headProof",
|
|
1216
|
+
cause: problem,
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
if (headProof === "snapshot") {
|
|
1220
|
+
const refusal = snapshotProofRefusal(config.streamName, snapshotSettings, 'append({ headProof: "snapshot" })');
|
|
1221
|
+
if (refusal !== undefined)
|
|
1222
|
+
return yield* Effect.die(new Error(refusal));
|
|
1223
|
+
if (snapshotState === undefined) {
|
|
1224
|
+
return yield* new ValidationError({
|
|
1225
|
+
entityType: snapshotEntityType,
|
|
1226
|
+
operation: "EventStore.append.headProof",
|
|
1227
|
+
cause: 'headProof: "snapshot" requires the inline snapshot it names: pass `snapshot` ' +
|
|
1228
|
+
"with the state as of this append. Nothing was written.",
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1156
1233
|
// Nothing at all to write — preserve the historical no-op fast path.
|
|
1157
1234
|
// With additional items or a dedup sentinel the transaction still runs:
|
|
1158
1235
|
// a caller who asked for a side write means it, and silently dropping it
|
|
@@ -1180,7 +1257,8 @@ export const makeStream = (config) => {
|
|
|
1180
1257
|
// (#144): an append is never split — splitting would break its
|
|
1181
1258
|
// atomicity — so one over the limit is rejected whole by DynamoDB and
|
|
1182
1259
|
// reported as `AppendTooLarge`, with the expanded item count.
|
|
1183
|
-
|
|
1260
|
+
// A head proof (#164) proves the same thing, so it replaces the check.
|
|
1261
|
+
const needsContiguityCheck = expectedVersion > 0 && events.length > 0 && headProof === undefined;
|
|
1184
1262
|
const tableConfig = yield* config.table.Tag;
|
|
1185
1263
|
const tableName = tableConfig.name;
|
|
1186
1264
|
// A custom TTL attribute is only known at runtime. An index key attribute
|
|
@@ -1347,16 +1425,31 @@ export const makeStream = (config) => {
|
|
|
1347
1425
|
},
|
|
1348
1426
|
};
|
|
1349
1427
|
}
|
|
1350
|
-
// The inline snapshot (#138): unconditional — see AppendOptions.snapshot
|
|
1351
|
-
//
|
|
1352
|
-
//
|
|
1428
|
+
// The inline snapshot (#138): unconditional — see AppendOptions.snapshot
|
|
1429
|
+
// — unless it is the head proof (#164), when it is conditioned on being
|
|
1430
|
+
// at `expectedVersion`. Packed per `snapshot.storedAs` (#147), so the
|
|
1431
|
+
// transaction-size and item-size diagnosis count its stored bytes.
|
|
1353
1432
|
let snapshotPut;
|
|
1354
1433
|
if (snapshotState !== undefined) {
|
|
1434
|
+
const item = yield* buildSnapshotItem(streamId, snapshotState, expectedVersion + events.length, now, "EventStore.append.snapshot", eventPartitions);
|
|
1355
1435
|
snapshotPut = {
|
|
1356
|
-
Put:
|
|
1357
|
-
TableName: tableName,
|
|
1358
|
-
|
|
1359
|
-
|
|
1436
|
+
Put: headProof !== "snapshot"
|
|
1437
|
+
? { TableName: tableName, Item: item }
|
|
1438
|
+
: expectedVersion === 0
|
|
1439
|
+
? {
|
|
1440
|
+
TableName: tableName,
|
|
1441
|
+
Item: item,
|
|
1442
|
+
ConditionExpression: "attribute_not_exists(pk)",
|
|
1443
|
+
}
|
|
1444
|
+
: {
|
|
1445
|
+
TableName: tableName,
|
|
1446
|
+
Item: item,
|
|
1447
|
+
ConditionExpression: "#asOfVersion = :expectedVersion",
|
|
1448
|
+
ExpressionAttributeNames: { "#asOfVersion": "asOfVersion" },
|
|
1449
|
+
ExpressionAttributeValues: toAttributeMap({
|
|
1450
|
+
":expectedVersion": expectedVersion,
|
|
1451
|
+
}),
|
|
1452
|
+
},
|
|
1360
1453
|
};
|
|
1361
1454
|
}
|
|
1362
1455
|
/**
|
|
@@ -1393,6 +1486,22 @@ export const makeStream = (config) => {
|
|
|
1393
1486
|
const append = (streamId, events, expectedVersion, options) => Effect.gen(function* () {
|
|
1394
1487
|
const additionalOps = options?.additionalItems ?? [];
|
|
1395
1488
|
const idempotency = options?.idempotency;
|
|
1489
|
+
const headProof = options?.headProof;
|
|
1490
|
+
// The additional item a head proof names (#164) must exist — checked
|
|
1491
|
+
// before anything is read, and ahead of the no-op fast path. Its shape
|
|
1492
|
+
// is checked first, by `buildAppend`'s own rule.
|
|
1493
|
+
const proofOp = typeof headProof === "object" && headProofShapeProblem(headProof) === undefined
|
|
1494
|
+
? headProof.additionalItem
|
|
1495
|
+
: undefined;
|
|
1496
|
+
if (proofOp !== undefined && proofOp >= additionalOps.length) {
|
|
1497
|
+
return yield* new ValidationError({
|
|
1498
|
+
entityType,
|
|
1499
|
+
operation: "EventStore.append.headProof",
|
|
1500
|
+
cause: `headProof names additionalItems[${proofOp}], but the append has ` +
|
|
1501
|
+
`${additionalOps.length} additional item${additionalOps.length === 1 ? "" : "s"}. ` +
|
|
1502
|
+
"Nothing was written.",
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1396
1505
|
const parts = yield* buildAppend(streamId, events, expectedVersion, options);
|
|
1397
1506
|
if (parts === undefined)
|
|
1398
1507
|
return { version: expectedVersion, events: [] };
|
|
@@ -1449,6 +1558,7 @@ export const makeStream = (config) => {
|
|
|
1449
1558
|
sentinelIndex,
|
|
1450
1559
|
snapshotIndex,
|
|
1451
1560
|
expectedVersion,
|
|
1561
|
+
headProof,
|
|
1452
1562
|
};
|
|
1453
1563
|
};
|
|
1454
1564
|
// A guarded put among the additional items reads its item; a race with
|
|
@@ -1458,6 +1568,17 @@ export const makeStream = (config) => {
|
|
|
1458
1568
|
// Caller-owned items, compiled through the same builder
|
|
1459
1569
|
// `Transaction.transactWrite` uses, so the two APIs cannot drift.
|
|
1460
1570
|
const additional = yield* buildTransactWriteItems(additionalOps, "EventStore.append.additionalItems");
|
|
1571
|
+
// An item without a condition of its own proves nothing about the
|
|
1572
|
+
// head: accepting it would drop the gap protection silently.
|
|
1573
|
+
if (proofOp !== undefined && !additional.conditioned.has(proofOp)) {
|
|
1574
|
+
return yield* new ValidationError({
|
|
1575
|
+
entityType,
|
|
1576
|
+
operation: "EventStore.append.headProof",
|
|
1577
|
+
cause: `headProof names additionalItems[${proofOp}], which carries no condition of its ` +
|
|
1578
|
+
"own: the proof must be conditioned on the stream being at expectedVersion " +
|
|
1579
|
+
"(`.condition()`, `create()` or `Transaction.check`). Nothing was written.",
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1461
1582
|
const tx = layout(additional);
|
|
1462
1583
|
// DynamoDB's rules — one operation per item (an additional item
|
|
1463
1584
|
// repeating an event, the contiguity check, the idempotency sentinel
|
|
@@ -2264,6 +2385,34 @@ const makeCommandHandlerImpl = (decider, stream, options) => {
|
|
|
2264
2385
|
}
|
|
2265
2386
|
// Ignored on a stream without a snapshot config: there is no snapshot to trust.
|
|
2266
2387
|
const unverifiedLoads = options?.verifySnapshot === false && snapshotSettings !== undefined;
|
|
2388
|
+
// The head proof and a caller's load (#164), refused up front (EDD-9085)
|
|
2389
|
+
// when they cannot work together or on this stream.
|
|
2390
|
+
const loadState = options?.loadState;
|
|
2391
|
+
const headProof = options?.headProof;
|
|
2392
|
+
const refuse = (problem) => {
|
|
2393
|
+
throw new Error(`[EDD-9085] EventStream "${stream.streamName}": commandHandler ${problem}`);
|
|
2394
|
+
};
|
|
2395
|
+
if (loadState !== undefined && typeof loadState !== "function") {
|
|
2396
|
+
refuse("loadState must be a function of the stream id.");
|
|
2397
|
+
}
|
|
2398
|
+
if (headProof !== undefined) {
|
|
2399
|
+
const problem = headProofShapeProblem(headProof);
|
|
2400
|
+
if (problem !== undefined)
|
|
2401
|
+
refuse(problem.replace(" Nothing was written.", ""));
|
|
2402
|
+
if (headProof === "snapshot") {
|
|
2403
|
+
const refusal = snapshotProofRefusal(stream.streamName, snapshotSettings, "commandHandler");
|
|
2404
|
+
if (refusal !== undefined)
|
|
2405
|
+
throw new Error(refusal);
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
if (loadState !== undefined && options?.verifySnapshot === false) {
|
|
2409
|
+
refuse("with loadState and verifySnapshot: false — loadState replaces the stream load, so " +
|
|
2410
|
+
"there is no snapshot load to leave unverified.");
|
|
2411
|
+
}
|
|
2412
|
+
if (loadState !== undefined && headProof === "snapshot") {
|
|
2413
|
+
refuse('with loadState and headProof: "snapshot" — the handler never reads the snapshot, so ' +
|
|
2414
|
+
"it cannot know the snapshot is at the head.");
|
|
2415
|
+
}
|
|
2267
2416
|
const retryPolicy = options?.retry;
|
|
2268
2417
|
const schedule = retryPolicy === undefined
|
|
2269
2418
|
? undefined
|
|
@@ -2282,6 +2431,43 @@ const makeCommandHandlerImpl = (decider, stream, options) => {
|
|
|
2282
2431
|
// replays from the beginning.
|
|
2283
2432
|
const load = (verify) => Effect.gen(function* () {
|
|
2284
2433
|
let state = decider.initialState;
|
|
2434
|
+
// The caller's own record (#164), trusted as the head: the append's
|
|
2435
|
+
// check (or the head proof) catches one that is not.
|
|
2436
|
+
if (loadState !== undefined) {
|
|
2437
|
+
const result = yield* loadState(streamId);
|
|
2438
|
+
const refused = (cause) => new ValidationError({
|
|
2439
|
+
entityType: stream.streamName,
|
|
2440
|
+
operation: "EventStore.commandHandler.loadState",
|
|
2441
|
+
cause,
|
|
2442
|
+
});
|
|
2443
|
+
if (!Option.isOption(result)) {
|
|
2444
|
+
return yield* refused("loadState must return an Option of { state, version }: None for a new stream. " +
|
|
2445
|
+
"Nothing was written.");
|
|
2446
|
+
}
|
|
2447
|
+
if (Option.isNone(result)) {
|
|
2448
|
+
const fresh = {
|
|
2449
|
+
state,
|
|
2450
|
+
version: 0,
|
|
2451
|
+
snapshotAsOfVersion: undefined,
|
|
2452
|
+
verified: true,
|
|
2453
|
+
eventsAfterSnapshot: 0,
|
|
2454
|
+
};
|
|
2455
|
+
return fresh;
|
|
2456
|
+
}
|
|
2457
|
+
const record = result.value;
|
|
2458
|
+
if (!Number.isInteger(record.version) || record.version < 0) {
|
|
2459
|
+
return yield* refused(`loadState returned version ${String(record.version)}: it must be a non-negative ` +
|
|
2460
|
+
"integer, the stream version the state is as of. Nothing was written.");
|
|
2461
|
+
}
|
|
2462
|
+
const loaded = {
|
|
2463
|
+
state: record.state,
|
|
2464
|
+
version: record.version,
|
|
2465
|
+
snapshotAsOfVersion: undefined,
|
|
2466
|
+
verified: true,
|
|
2467
|
+
eventsAfterSnapshot: 0,
|
|
2468
|
+
};
|
|
2469
|
+
return loaded;
|
|
2470
|
+
}
|
|
2285
2471
|
if (snapshotSettings !== undefined) {
|
|
2286
2472
|
const latest = (yield* stream.readLatest(streamId, verify ? readOptions : unverifiedReadOptions));
|
|
2287
2473
|
let snapshotAsOfVersion = 0;
|
|
@@ -2400,14 +2586,31 @@ const makeCommandHandlerImpl = (decider, stream, options) => {
|
|
|
2400
2586
|
}
|
|
2401
2587
|
// Inline snapshots (#138) ride in the append transaction: on every
|
|
2402
2588
|
// append, or once `every` events have accumulated since the snapshot.
|
|
2589
|
+
// A `loadState` load never saw the snapshot (#164), so its cadence is
|
|
2590
|
+
// an append that crosses a multiple of `every`.
|
|
2403
2591
|
const mode = snapshotSettings?.mode ?? "after-append";
|
|
2404
2592
|
const every = snapshotSettings?.every;
|
|
2405
2593
|
const newVersion = baseVersion + newEvents.length;
|
|
2594
|
+
const snapshotDue = (version) => every !== undefined &&
|
|
2595
|
+
(loaded.snapshotAsOfVersion === undefined
|
|
2596
|
+
? Math.floor(version / every) > Math.floor(baseVersion / every)
|
|
2597
|
+
: version - loaded.snapshotAsOfVersion >= every);
|
|
2406
2598
|
if (snapshotSettings !== undefined &&
|
|
2407
2599
|
mode === "inline" &&
|
|
2408
|
-
(every === undefined || newVersion
|
|
2600
|
+
(every === undefined || snapshotDue(newVersion))) {
|
|
2409
2601
|
appendOptions.snapshot = next;
|
|
2410
2602
|
}
|
|
2603
|
+
// The head proof (#164). The caller's record proves it on every
|
|
2604
|
+
// append; the inline snapshot only when this load found it at the
|
|
2605
|
+
// head — a lagging one would fail the condition, so the check stays.
|
|
2606
|
+
if (typeof headProof === "object") {
|
|
2607
|
+
appendOptions.headProof = headProof;
|
|
2608
|
+
}
|
|
2609
|
+
else if (headProof === "snapshot" &&
|
|
2610
|
+
appendOptions.snapshot !== undefined &&
|
|
2611
|
+
loaded.snapshotAsOfVersion === baseVersion) {
|
|
2612
|
+
appendOptions.headProof = "snapshot";
|
|
2613
|
+
}
|
|
2411
2614
|
const appended = stream.append(streamId, newEvents, baseVersion, appendOptions);
|
|
2412
2615
|
const outcome = yield* returnAppendConflict
|
|
2413
2616
|
? appended.pipe(Effect.catchIf((error) => error instanceof VersionConflict, (conflict) => Effect.succeed(conflict)))
|
|
@@ -2419,9 +2622,7 @@ const makeCommandHandlerImpl = (decider, stream, options) => {
|
|
|
2419
2622
|
// Best-effort: the events are already durable, so a snapshot-write
|
|
2420
2623
|
// failure must not report the command as failed. The next threshold
|
|
2421
2624
|
// crossing retries it.
|
|
2422
|
-
if (mode === "after-append" &&
|
|
2423
|
-
every !== undefined &&
|
|
2424
|
-
result.version - loaded.snapshotAsOfVersion >= every) {
|
|
2625
|
+
if (mode === "after-append" && snapshotDue(result.version)) {
|
|
2425
2626
|
yield* stream
|
|
2426
2627
|
.writeSnapshot(streamId, next, result.version)
|
|
2427
2628
|
.pipe(Effect.catchCause((cause) => Effect.logWarning(`EventStore: snapshot write failed for stream "${stream.streamName}" at version ${result.version}`, cause)));
|
|
@@ -2754,7 +2955,7 @@ export const foldFrom = Function.dual(3, (decider, startState, events) => {
|
|
|
2754
2955
|
// ---------------------------------------------------------------------------
|
|
2755
2956
|
export { batchResponse, decoder, eventFilterPattern, processBatch, projection, } from "./internal/EventProjection.js";
|
|
2756
2957
|
export { multiProjection, } from "./internal/MultiProjection.js";
|
|
2757
|
-
export { removeRow, } from "./internal/ProjectionEngine.js";
|
|
2958
|
+
export { removeRow, skipEvent, } from "./internal/ProjectionEngine.js";
|
|
2758
2959
|
/**
|
|
2759
2960
|
* Bind a projection to the services its operations need — `DynamoClient`,
|
|
2760
2961
|
* the `TableConfig`s of the stream's table and the read model's, `evolve`'s
|