@photon-grove/evt 0.1.1 → 0.2.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.
Files changed (86) hide show
  1. package/README.md +51 -4
  2. package/dist/aggregate.d.ts +26 -0
  3. package/dist/aggregate.d.ts.map +1 -1
  4. package/dist/aggregate.js.map +1 -1
  5. package/dist/dynamodb/head.d.ts +6 -0
  6. package/dist/dynamodb/head.d.ts.map +1 -1
  7. package/dist/dynamodb/head.js +11 -0
  8. package/dist/dynamodb/head.js.map +1 -1
  9. package/dist/dynamodb/index.d.ts +2 -1
  10. package/dist/dynamodb/index.d.ts.map +1 -1
  11. package/dist/dynamodb/index.js +2 -1
  12. package/dist/dynamodb/index.js.map +1 -1
  13. package/dist/dynamodb/repository.d.ts +9 -2
  14. package/dist/dynamodb/repository.d.ts.map +1 -1
  15. package/dist/dynamodb/repository.js +123 -27
  16. package/dist/dynamodb/repository.js.map +1 -1
  17. package/dist/dynamodb/requests.d.ts +37 -3
  18. package/dist/dynamodb/requests.d.ts.map +1 -1
  19. package/dist/dynamodb/requests.js +67 -9
  20. package/dist/dynamodb/requests.js.map +1 -1
  21. package/dist/dynamodb/views.d.ts +18 -0
  22. package/dist/dynamodb/views.d.ts.map +1 -0
  23. package/dist/dynamodb/views.js +56 -0
  24. package/dist/dynamodb/views.js.map +1 -0
  25. package/dist/errors.d.ts +51 -4
  26. package/dist/errors.d.ts.map +1 -1
  27. package/dist/errors.js +60 -4
  28. package/dist/errors.js.map +1 -1
  29. package/dist/index.d.ts +4 -3
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +3 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/memory/repository.d.ts +3 -2
  34. package/dist/memory/repository.d.ts.map +1 -1
  35. package/dist/memory/repository.js +78 -29
  36. package/dist/memory/repository.js.map +1 -1
  37. package/dist/postgres/client.d.ts +32 -0
  38. package/dist/postgres/client.d.ts.map +1 -0
  39. package/dist/postgres/client.js +4 -0
  40. package/dist/postgres/client.js.map +1 -0
  41. package/dist/postgres/index.d.ts +4 -0
  42. package/dist/postgres/index.d.ts.map +1 -0
  43. package/dist/postgres/index.js +6 -0
  44. package/dist/postgres/index.js.map +1 -0
  45. package/dist/postgres/repository.d.ts +62 -0
  46. package/dist/postgres/repository.d.ts.map +1 -0
  47. package/dist/postgres/repository.js +598 -0
  48. package/dist/postgres/repository.js.map +1 -0
  49. package/dist/postgres/schema.d.ts +81 -0
  50. package/dist/postgres/schema.d.ts.map +1 -0
  51. package/dist/postgres/schema.js +249 -0
  52. package/dist/postgres/schema.js.map +1 -0
  53. package/dist/repository.d.ts +101 -6
  54. package/dist/repository.d.ts.map +1 -1
  55. package/dist/repository.js +108 -6
  56. package/dist/repository.js.map +1 -1
  57. package/dist/staging.d.ts +28 -0
  58. package/dist/staging.d.ts.map +1 -0
  59. package/dist/staging.js +89 -0
  60. package/dist/staging.js.map +1 -0
  61. package/dist/store.d.ts +15 -3
  62. package/dist/store.d.ts.map +1 -1
  63. package/dist/store.js +38 -54
  64. package/dist/store.js.map +1 -1
  65. package/dist/transaction.d.ts +113 -0
  66. package/dist/transaction.d.ts.map +1 -0
  67. package/dist/transaction.js +207 -0
  68. package/dist/transaction.js.map +1 -0
  69. package/package.json +5 -1
  70. package/src/aggregate.ts +28 -0
  71. package/src/dynamodb/head.ts +15 -0
  72. package/src/dynamodb/index.ts +2 -0
  73. package/src/dynamodb/repository.ts +177 -33
  74. package/src/dynamodb/requests.ts +114 -10
  75. package/src/dynamodb/views.ts +67 -0
  76. package/src/errors.ts +86 -4
  77. package/src/index.ts +19 -6
  78. package/src/memory/repository.ts +93 -32
  79. package/src/postgres/client.ts +37 -0
  80. package/src/postgres/index.ts +23 -0
  81. package/src/postgres/repository.ts +858 -0
  82. package/src/postgres/schema.ts +302 -0
  83. package/src/repository.ts +220 -8
  84. package/src/staging.ts +138 -0
  85. package/src/store.ts +66 -65
  86. package/src/transaction.ts +322 -0
package/README.md CHANGED
@@ -11,9 +11,9 @@ held to the fixtures Go and Rust generate, byte for byte.
11
11
  npm install @photon-grove/evt
12
12
  ```
13
13
 
14
- So far it has the core, the in-memory and DynamoDB backends, and the testing helpers. Views on
15
- DynamoDB, the PostgreSQL backend, command outcomes and multi-stream transactions come in later
16
- releases, and the
14
+ So far it has the core, multi-stream transactions with read fences, the in-memory, DynamoDB and
15
+ PostgreSQL backends, and the testing helpers. Commit-wide projectors and command outcomes come in
16
+ later releases, and the
17
17
  [feature matrix](https://github.com/photon-grove/evt/blob/main/docs/reference/feature-matrix.md)
18
18
  tracks each one. The
19
19
  [getting started guide](https://github.com/photon-grove/evt/blob/main/docs/guide/getting-started.md#the-same-model-in-typescript)
@@ -26,7 +26,8 @@ builds a bank account aggregate with it, end to end.
26
26
  | `@photon-grove/evt` | Aggregates, codecs, upcasters, the `Store`, retry, and the wire codecs |
27
27
  | `@photon-grove/evt/memory` | `MemRepository`, the in-memory backend for tests and local development |
28
28
  | `@photon-grove/evt/testing` | `scenario` and `Fixture`, the given/when/then helpers over `MemRepository` |
29
- | `@photon-grove/evt/dynamodb` | `DynamoRepository`, and the DynamoDB item and request codecs |
29
+ | `@photon-grove/evt/dynamodb` | `DynamoRepository`, `viewProjector`, and the item and request codecs |
30
+ | `@photon-grove/evt/postgres` | `PgRepository`, and the schema: table names, relations and `schemaDdl` |
30
31
 
31
32
  ## DynamoDB
32
33
 
@@ -59,6 +60,35 @@ hasn't loaded the stream gets a conflict, and its retry passes. TTL deletes item
59
60
  use retention only for short-lived streams that nothing replays, and declare the retained types in
60
61
  the feature manifest from Go or Rust, so that their writers refuse read fences on them.
61
62
 
63
+ `viewProjector('views')`, listed in an aggregate's `projectors`, puts the entity's view row with
64
+ every commit, in the same transaction: `pk = {type}#{id}`, `sk = VIEW`, and the state as its
65
+ codec encodes it, as Go's `dynamo.NewViewProjector` does.
66
+
67
+ ## PostgreSQL
68
+
69
+ `PgRepository` takes a pool shaped like `pg`'s `Pool`, and writes the tables the
70
+ [PostgreSQL guide](https://github.com/photon-grove/evt/blob/main/docs/guide/postgres.md) describes,
71
+ as Go's and Rust's repositories do:
72
+
73
+ ```ts
74
+ import {Store} from '@photon-grove/evt'
75
+ import {PgRepository} from '@photon-grove/evt/postgres'
76
+ import {Pool} from 'pg'
77
+
78
+ const repository = new PgRepository(new Pool({connectionString: process.env.DATABASE_URL}))
79
+ await repository.ensureSchema() // idempotent, safe on every start
80
+ const store = new Store(repository)
81
+ ```
82
+
83
+ The package imports no driver: Neon's serverless `Pool` works too, and so does a single connection
84
+ without `connect()`, such as PGlite, which the repository runs one operation at a time on. Every
85
+ value it sends is a string and every column it reads is text, so no driver's type parsing touches a
86
+ stored value. `{eventsTable, snapshotsTable}` let several event logs share a database. A writer
87
+ initializes a fresh store itself on its first write, as Go's and Rust's do, and `initialize()` is
88
+ there for a deploy step that treats every backend alike. Every transaction runs at `READ
89
+ COMMITTED`, whatever the server's default. An abort is checked between statements and rolls the
90
+ transaction back.
91
+
62
92
  ## Runtimes
63
93
 
64
94
  The package is ESM only and has no runtime dependencies. CI runs it on Node 26, Node 22.12, Bun and
@@ -103,9 +133,26 @@ of `AbortSignal`, which the DOM library, `@types/node`, and Bun's and Deno's typ
103
133
  refuses an entity type the manifest declares history-dependent. `MemRepository` declares the
104
134
  required `stream-heads` feature on a fresh store's first write, and `initialize()` declares it
105
135
  up front.
136
+ - **Projectors.** An aggregate's `projectors` run on the state after each commit's events, and
137
+ the operations they return are written in the commit's own transaction, by the backend they
138
+ name. `MemRepository` budgets them and writes none, as Go's in-memory repository does, so code
139
+ with projectors runs on it unchanged. A commit with operations but no events is refused with a
140
+ `ProjectorOnlyCommitError`.
106
141
  - **Rebuilds.** While a projection rebuild holds the store, every commit fails with a
107
142
  `RebuildInProgressError`. It's retryable, so `executeWithRetry` runs the command again after its
108
143
  backoff, though a rebuild usually outlasts the default policy's attempts.
144
+ - **Transactions.** `store.transaction()` commits several streams at once. `read(aggregate, id)`
145
+ loads a stream of any aggregate type and returns a typed handle, `append(handle, events)` stages
146
+ the events decided for it, and `commit({metadata})` stamps one commit ID on every event and
147
+ writes them all atomically, with each stream's snapshot and projector operations, or writes
148
+ nothing. A stream read but never appended to is fenced at the head it was read at, so the commit
149
+ fails with a `FenceConflictError` once another writer moves it. `fence({entityId, entityType,
150
+ head})` adds a fence of your own on a stream the transaction doesn't read. A fence the engine
151
+ filled is retryable, and one the caller stamped isn't. A transaction commits once, and `execute`
152
+ stays the shortcut for a single stream. Underneath, every repository is a
153
+ `TransactionalRepository`, whose `commitTransaction({streams, fences})` takes the streams and
154
+ fences directly. A fence on an entity type with retention is refused with a
155
+ `RetentionConflictError`.
109
156
  - **Sequences** are `bigint`s, because Go and Rust store them as 64-bit integers.
110
157
 
111
158
  Errors are `EvtError`s with a `code`. `isConflict` and `isRetryable` tell them apart.
@@ -1,6 +1,7 @@
1
1
  import type { Codec } from './codec.js';
2
2
  import type { SerializedEvent } from './event.js';
3
3
  import { type JsonValue } from './json.js';
4
+ import type { ProjectorGroup } from './repository.js';
4
5
  /**
5
6
  * Migrates a stored payload of its event type before it is decoded, taking an event at the
6
7
  * upcaster's from version to its to version. An event type's upcasters run in declaration order,
@@ -68,6 +69,31 @@ export interface AggregateDefinition<S, C, D extends EventDefinitions> {
68
69
  * snapshots.
69
70
  */
70
71
  readonly snapshotInterval?: number | undefined;
72
+ /**
73
+ * The projectors whose operations every commit of this type writes in the same transaction as
74
+ * its events, in this order, such as the DynamoDB backend's `viewProjector`.
75
+ */
76
+ readonly projectors?: readonly Projector<S>[] | undefined;
77
+ }
78
+ /** What a projector sees of a commit: its entity, the state after its events, and the events. */
79
+ export interface Projection<S> {
80
+ readonly entityType: string;
81
+ readonly entityId: string;
82
+ /** The state after the commit's events. */
83
+ readonly state: S;
84
+ /** The state as the aggregate's state codec encodes it, as a snapshot stores it. */
85
+ readonly encodedState: Uint8Array;
86
+ /** Copies of the commit's events, as they are stored. */
87
+ readonly events: readonly Readonly<SerializedEvent>[];
88
+ }
89
+ /**
90
+ * Builds the operations a commit writes beside its events, in the same transaction, such as a
91
+ * view of the entity's state. Its group names the backend that writes it. `project` returns null
92
+ * when the commit needs none. It runs only for a commit with events, again on every retry, and it
93
+ * must not fail or change the state.
94
+ */
95
+ export interface Projector<S> {
96
+ project(projection: Projection<S>): ProjectorGroup | null;
71
97
  }
72
98
  /** A checked aggregate type. */
73
99
  export type Aggregate<S, C, D extends EventDefinitions> = Readonly<AggregateDefinition<S, C, D>>;
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../src/aggregate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../src/aggregate.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../src/aggregate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../src/aggregate.ts"],"names":[],"mappings":"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"}
@@ -25,6 +25,12 @@ export interface HeadAction {
25
25
  * retention.
26
26
  */
27
27
  export declare function headUpdate(entityId: string, first: bigint, last: bigint, ttl?: number): HeadAction;
28
+ /** The condition of a fence on a stream with no head: it still has none. */
29
+ export declare const FENCE_ABSENT_CONDITION = "attribute_not_exists(head)";
30
+ /** The condition of a fence on a stream with a head: it still equals the one observed. */
31
+ export declare const FENCE_CONDITION = "head = :head";
32
+ /** The `ConditionCheck` a transaction fences a stream it read at `head` with. */
33
+ export declare function headFence(entityId: string, head: bigint): HeadAction;
28
34
  /**
29
35
  * An action's size in the commit budget, by the rule for any update, delete or condition check:
30
36
  * its key attributes plus its expression strings, name placeholders (the placeholder and the
@@ -1 +1 @@
1
- {"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../../src/dynamodb/head.ts"],"names":[],"mappings":"AAQA,4EAA4E;AAC5E,eAAO,MAAM,WAAW,qBAAqB,CAAA;AAE7C,iGAAiG;AACjG,eAAO,MAAM,oBAAoB,gCAAgC,CAAA;AAEjE,0FAA0F;AAC1F,eAAO,MAAM,qBAAqB,+BAA+B,CAAA;AAEjE,gGAAgG;AAChG,eAAO,MAAM,qBAAqB,iBAAiB,CAAA;AAEnD,oFAAoF;AACpF,eAAO,MAAM,8BAA8B,gEACoB,CAAA;AAE/D,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,6DAA6D;IAC7D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,mBAAmB,EAAE,MAAM,CAAA;IAC3B,kEAAkE;IAClE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;IACzB,2DAA2D;IAC3D,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE,MAAM,GACX,UAAU,CA0BZ;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAazD"}
1
+ {"version":3,"file":"head.d.ts","sourceRoot":"","sources":["../../src/dynamodb/head.ts"],"names":[],"mappings":"AAQA,4EAA4E;AAC5E,eAAO,MAAM,WAAW,qBAAqB,CAAA;AAE7C,iGAAiG;AACjG,eAAO,MAAM,oBAAoB,gCAAgC,CAAA;AAEjE,0FAA0F;AAC1F,eAAO,MAAM,qBAAqB,+BAA+B,CAAA;AAEjE,gGAAgG;AAChG,eAAO,MAAM,qBAAqB,iBAAiB,CAAA;AAEnD,oFAAoF;AACpF,eAAO,MAAM,8BAA8B,gEACoB,CAAA;AAE/D,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,6DAA6D;IAC7D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,mBAAmB,EAAE,MAAM,CAAA;IAC3B,kEAAkE;IAClE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;IACzB,2DAA2D;IAC3D,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE,MAAM,GACX,UAAU,CA0BZ;AAED,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB,+BAA+B,CAAA;AAElE,0FAA0F;AAC1F,eAAO,MAAM,eAAe,iBAAiB,CAAA;AAE7C,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAMpE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAazD"}
@@ -43,6 +43,17 @@ export function headUpdate(entityId, first, last, ttl) {
43
43
  values,
44
44
  };
45
45
  }
46
+ /** The condition of a fence on a stream with no head: it still has none. */
47
+ export const FENCE_ABSENT_CONDITION = 'attribute_not_exists(head)';
48
+ /** The condition of a fence on a stream with a head: it still equals the one observed. */
49
+ export const FENCE_CONDITION = 'head = :head';
50
+ /** The `ConditionCheck` a transaction fences a stream it read at `head` with. */
51
+ export function headFence(entityId, head) {
52
+ const check = { entityId, updateExpression: null, names: [] };
53
+ return head === 0n
54
+ ? { ...check, conditionExpression: FENCE_ABSENT_CONDITION, values: [] }
55
+ : { ...check, conditionExpression: FENCE_CONDITION, values: [[':head', head]] };
56
+ }
46
57
  /**
47
58
  * An action's size in the commit budget, by the rule for any update, delete or condition check:
48
59
  * its key attributes plus its expression strings, name placeholders (the placeholder and the
@@ -1 +1 @@
1
- {"version":3,"file":"head.js","sourceRoot":"","sources":["../../src/dynamodb/head.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,4FAA4F;AAC5F,wFAAwF;AAExF,OAAO,EAAC,mBAAmB,EAAC,MAAM,cAAc,CAAA;AAChD,OAAO,EAAC,UAAU,EAAC,MAAM,YAAY,CAAA;AACrC,OAAO,EAAC,aAAa,EAAE,QAAQ,EAAC,MAAM,WAAW,CAAA;AAEjD,4EAA4E;AAC5E,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,CAAA;AAE7C,iGAAiG;AACjG,MAAM,CAAC,MAAM,oBAAoB,GAAG,6BAA6B,CAAA;AAEjE,0FAA0F;AAC1F,MAAM,CAAC,MAAM,qBAAqB,GAAG,4BAA4B,CAAA;AAEjE,gGAAgG;AAChG,MAAM,CAAC,MAAM,qBAAqB,GAAG,cAAc,CAAA;AAEnD,oFAAoF;AACpF,MAAM,CAAC,MAAM,8BAA8B,GACzC,6DAA6D,CAAA;AAc/D;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,QAAgB,EAChB,KAAa,EACb,IAAY,EACZ,GAAY;IAEZ,MAAM,MAAM,GAAG,KAAK,IAAI,EAAE,CAAA;IAC1B,MAAM,MAAM,GAAuB,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAA;IACpD,MAAM,KAAK,GAAuB,EAAE,CAAA;IAEpC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,KAAK,GAAG,EAAE,CAAC,CAAC,CAAA;IACpC,CAAC;IACD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;QAC3B,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,mBAAmB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9D,CAAC;IAED,IAAI,mBAAmB,GAAG,qBAAqB,CAAA;IAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,mBAAmB;YACjB,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,8BAA8B,CAAA;IAC9E,CAAC;IAED,OAAO;QACL,QAAQ;QACR,gBAAgB,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,oBAAoB;QACxE,mBAAmB;QACnB,KAAK;QACL,MAAM;KACP,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAkB;IAC/C,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAC,EAAE,EAAE,EAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,EAAC,EAAE,EAAE,EAAE,EAAC,CAAC,EAAE,aAAa,CAAC,QAAQ,EAAE,EAAC,EAAC,CAAC,CAAA;IACnF,IAAI,IAAI,GACN,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAA;IAE1F,KAAK,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAC/C,IAAI,IAAI,UAAU,CAAC,WAAW,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;IACpD,CAAC;IACD,KAAK,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClD,IAAI,IAAI,UAAU,CAAC,WAAW,CAAC,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAA;IAC5D,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC"}
1
+ {"version":3,"file":"head.js","sourceRoot":"","sources":["../../src/dynamodb/head.ts"],"names":[],"mappings":"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"}
@@ -2,5 +2,6 @@ export { type AttributeValue, type Item, DEFAULT_VIEW_SORT_KEY, HEAD_SORT_KEY, S
2
2
  export { type ConditionCheckAction, type DeleteAction, type DynamoClient, type Expressions, type GetItemInput, type Page, type PutAction, type PutItemInput, type QueryInput, type RequestOptions, type ScanInput, type TransactItem, type UpdateAction, } from './client.js';
3
3
  export { FEATURES_PK, REBUILD_LOCK_PK, manifestFromItem, markerItem } from './marker.js';
4
4
  export { DynamoRepository, type DynamoRepositoryOptions } from './repository.js';
5
- export { type CommitTtl, type SnapshotTtl, commitTransaction, snapshotTransaction, } from './requests.js';
5
+ export { DYNAMODB_BACKEND, transactItemSize, viewProjector } from './views.js';
6
+ export { type CommitTtl, type SnapshotTtl, commitTransaction, snapshotTransaction, transactionItems, } from './requests.js';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dynamodb/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,IAAI,EACT,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,MAAM,EACN,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,WAAW,EACX,QAAQ,GACT,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,IAAI,EACT,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAC,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,UAAU,EAAC,MAAM,aAAa,CAAA;AACtF,OAAO,EAAC,gBAAgB,EAAE,KAAK,uBAAuB,EAAC,MAAM,iBAAiB,CAAA;AAC9E,OAAO,EACL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dynamodb/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,IAAI,EACT,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,MAAM,EACN,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,WAAW,EACX,QAAQ,GACT,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,IAAI,EACT,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAC,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,UAAU,EAAC,MAAM,aAAa,CAAA;AACtF,OAAO,EAAC,gBAAgB,EAAE,KAAK,uBAAuB,EAAC,MAAM,iBAAiB,CAAA;AAC9E,OAAO,EAAC,gBAAgB,EAAE,gBAAgB,EAAE,aAAa,EAAC,MAAM,YAAY,CAAA;AAC5E,OAAO,EACL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,eAAe,CAAA"}
@@ -4,5 +4,6 @@ export { DEFAULT_VIEW_SORT_KEY, HEAD_SORT_KEY, SNAPSHOT_SORT_KEY, eventFromItem,
4
4
  export {} from './client.js';
5
5
  export { FEATURES_PK, REBUILD_LOCK_PK, manifestFromItem, markerItem } from './marker.js';
6
6
  export { DynamoRepository } from './repository.js';
7
- export { commitTransaction, snapshotTransaction, } from './requests.js';
7
+ export { DYNAMODB_BACKEND, transactItemSize, viewProjector } from './views.js';
8
+ export { commitTransaction, snapshotTransaction, transactionItems, } from './requests.js';
8
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dynamodb/index.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,+FAA+F;AAE/F,OAAO,EAGL,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,MAAM,EACN,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,WAAW,EACX,QAAQ,GACT,MAAM,WAAW,CAAA;AAClB,OAAO,EAcN,MAAM,aAAa,CAAA;AACpB,OAAO,EAAC,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,UAAU,EAAC,MAAM,aAAa,CAAA;AACtF,OAAO,EAAC,gBAAgB,EAA+B,MAAM,iBAAiB,CAAA;AAC9E,OAAO,EAGL,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dynamodb/index.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,+FAA+F;AAE/F,OAAO,EAGL,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,MAAM,EACN,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,WAAW,EACX,QAAQ,GACT,MAAM,WAAW,CAAA;AAClB,OAAO,EAcN,MAAM,aAAa,CAAA;AACpB,OAAO,EAAC,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,UAAU,EAAC,MAAM,aAAa,CAAA;AACtF,OAAO,EAAC,gBAAgB,EAA+B,MAAM,iBAAiB,CAAA;AAC9E,OAAO,EAAC,gBAAgB,EAAE,gBAAgB,EAAE,aAAa,EAAC,MAAM,YAAY,CAAA;AAC5E,OAAO,EAGL,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,eAAe,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import type { SerializedEvent, SerializedSnapshot } from '../event.js';
2
- import { type CallOptions, type Commit, type Repository, type StoreInitializer } from '../repository.js';
2
+ import { type CallOptions, type Commit, type StoreInitializer, type TransactionCommit, type TransactionalRepository } from '../repository.js';
3
3
  import type { DynamoClient } from './client.js';
4
4
  /** How a {@link DynamoRepository} reads and initializes its store. */
5
5
  export interface DynamoRepositoryOptions {
@@ -39,7 +39,7 @@ export interface DynamoRepositoryOptions {
39
39
  * declares a feature, the next write fails, validates the manifest again, and either refuses an
40
40
  * unsupported feature or fails once with a retryable {@link FeatureManifestChangedError}.
41
41
  */
42
- export declare class DynamoRepository implements Repository, StoreInitializer {
42
+ export declare class DynamoRepository implements TransactionalRepository, StoreInitializer {
43
43
  #private;
44
44
  constructor(client: DynamoClient, eventsTable: string, options?: DynamoRepositoryOptions);
45
45
  /** The events table this repository writes. */
@@ -51,6 +51,13 @@ export declare class DynamoRepository implements Repository, StoreInitializer {
51
51
  */
52
52
  initialize(options?: CallOptions): Promise<boolean>;
53
53
  commit(commit: Commit, options?: CallOptions): Promise<void>;
54
+ /**
55
+ * Commits every stream and checks every fence in one `TransactWriteItems` call: the control
56
+ * checks, the projector operations, each written stream's actions, then a check on each fenced
57
+ * stream's head. A fence on a type this repository retains is refused before any request, and
58
+ * one on a type the store's manifest retains after the marker's validation.
59
+ */
60
+ commitTransaction(transaction: TransactionCommit, options?: CallOptions): Promise<void>;
54
61
  events(entityId: string, options?: CallOptions): Promise<SerializedEvent[]>;
55
62
  eventsAfter(entityId: string, after: bigint, options?: CallOptions): Promise<SerializedEvent[]>;
56
63
  latest(entityId: string, n: number, options?: CallOptions): Promise<SerializedEvent[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/dynamodb/repository.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAC,eAAe,EAAE,kBAAkB,EAAC,MAAM,aAAa,CAAA;AASpE,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,KAAK,UAAU,EACf,KAAK,gBAAgB,EAMtB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAC,YAAY,EAAqC,MAAM,aAAa,CAAA;AAoBjF,sEAAsE;AACtE,MAAM,WAAW,uBAAuB;IACtC;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1E;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,MAAM,CAAA;CACrB;AAoBD;;;;;;;;GAQG;AACH,qBAAa,gBAAiB,YAAW,UAAU,EAAE,gBAAgB;;IAgBnE,YAAY,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,EAO3F;IAED,+CAA+C;IAC/C,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;;;OAIG;IACG,UAAU,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAyBxD;IAEK,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBjE;IAEK,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAEhF;IAEK,WAAW,CACf,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC,CAa5B;IAEK,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAmB3F;IAEK,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAI1F;IAEK,WAAW,CAAC,QAAQ,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAgEvF;IAEK,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CA+C5F;CAyjBF"}
1
+ {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/dynamodb/repository.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAC,eAAe,EAAE,kBAAkB,EAAC,MAAM,aAAa,CAAA;AASpE,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,MAAM,EAEX,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAS7B,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAC,YAAY,EAAqC,MAAM,aAAa,CAAA;AAwBjF,sEAAsE;AACtE,MAAM,WAAW,uBAAuB;IACtC;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1E;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,MAAM,CAAA;CACrB;AAoBD;;;;;;;;GAQG;AACH,qBAAa,gBAAiB,YAAW,uBAAuB,EAAE,gBAAgB;;IAgBhF,YAAY,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,EAO3F;IAED,+CAA+C;IAC/C,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;;;OAIG;IACG,UAAU,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAyBxD;IAEK,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAOjE;IAED;;;;;OAKG;IACG,iBAAiB,CAAC,WAAW,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAM5F;IA4CK,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAEhF;IAEK,WAAW,CACf,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC,CAa5B;IAEK,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAmB3F;IAEK,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAI1F;IAEK,WAAW,CAAC,QAAQ,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAsEvF;IAEK,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CA+C5F;CAunBF"}
@@ -8,13 +8,14 @@
8
8
  // Pages, retries and the probe's reads each depend on the call before them, so they await in loops
9
9
  // on purpose.
10
10
  /* oxlint-disable no-await-in-loop */
11
- import { CompactionUncoveredError, ConflictError, EvtError, FeatureManifestChangedError, HistoryDependentProjectionError, RebuildInProgressError, SequenceGapError, StoreNotInitializedError, StreamHeadDriftError, StreamHeadsNotMigratedError, TransientError, } from '../errors.js';
11
+ import { CompactionUncoveredError, ConflictError, EvtError, FeatureManifestChangedError, FenceConflictError, HistoryDependentProjectionError, RebuildInProgressError, SequenceGapError, StoreNotInitializedError, StreamHeadDriftError, StreamHeadsNotMigratedError, TransientError, } from '../errors.js';
12
12
  import { STREAM_HEADS_FEATURE, STREAM_HEADS_VERSION, checkSupported, declaresStreamHeads, } from '../features.js';
13
13
  import { int64Argument } from '../fields.js';
14
- import { checkBudget, checkNotReserved, commitSizes, isReserved, validateCommit, } from '../repository.js';
14
+ import { checkBudget, checkFenceRetention, checkNotReserved, isReserved, normalizeTransaction, rankFailures, transactionSizes, validateCommit, } from '../repository.js';
15
15
  import { HEAD_SORT_KEY, eventFromItem, expiry, headFromItem, snapshotFromItem, sortKey, ttlFromItem, } from './item.js';
16
16
  import { FEATURES_PK, manifestFromItem, markerItem } from './marker.js';
17
- import { commitTransaction, controlKey, headKey, snapshotTransaction, } from './requests.js';
17
+ import { DYNAMODB_BACKEND, transactItemSize } from './views.js';
18
+ import { controlKey, headKey, projectorItems, buildTransaction, snapshotTransaction, } from './requests.js';
18
19
  // A declaration is a read-modify-write; racing declarations retry this many times.
19
20
  const MAX_DECLARE_ATTEMPTS = 8;
20
21
  // Each page of a scan that only has to find one item.
@@ -94,22 +95,56 @@ export class DynamoRepository {
94
95
  options?.signal?.throwIfAborted();
95
96
  validateCommit(commit);
96
97
  checkNotReserved(commit.entityId);
97
- if (commit.events.length === 0) {
98
- return;
98
+ if (commit.events.length > 0) {
99
+ await this.#write(normalizeTransaction({ streams: [commit] }), options);
99
100
  }
100
- // One `ttl` for the whole commit, so its head's is never below one of its own items.
101
- const entityType = commit.events[0].entityType;
102
- const ttl = this.#ttlFor(entityType);
101
+ }
102
+ /**
103
+ * Commits every stream and checks every fence in one `TransactWriteItems` call: the control
104
+ * checks, the projector operations, each written stream's actions, then a check on each fenced
105
+ * stream's head. A fence on a type this repository retains is refused before any request, and
106
+ * one on a type the store's manifest retains after the marker's validation.
107
+ */
108
+ async commitTransaction(transaction, options) {
109
+ options?.signal?.throwIfAborted();
110
+ const normalized = normalizeTransaction(transaction);
111
+ if (normalized.streams.length > 0 || normalized.fences.length > 0) {
112
+ await this.#write(normalized, options);
113
+ }
114
+ }
115
+ async #write(transaction, options) {
116
+ // One `ttl` per entity type for the whole transaction, so a stream's head's is never below one
117
+ // of its own items.
118
+ const ttls = new Map();
119
+ const ttlFor = (entityType) => {
120
+ if (!ttls.has(entityType)) {
121
+ ttls.set(entityType, this.#ttlFor(entityType));
122
+ }
123
+ return ttls.get(entityType);
124
+ };
103
125
  // Every backend budgets a commit the same way, so an oversized one fails before any request.
104
- checkBudget(commitSizes(commit, (type) => (type === entityType ? ttl : undefined)));
126
+ // The projector operations are sized from the actions this repository sends, which refuses a
127
+ // group for another backend here too.
128
+ const sizes = projectorItems(transaction.projections).map(transactItemSize);
129
+ const projections = [{ backend: DYNAMODB_BACKEND, operations: sizes, sizes }];
130
+ checkBudget(transactionSizes({ ...transaction, projections }, ttlFor));
131
+ // A fence on a type this repository retains needs no request to refuse.
132
+ checkFenceRetention(transaction, (entityType) => this.#retention.has(entityType));
105
133
  const request = requestOptions(options);
106
134
  const version = await this.#markerVersion(request);
107
- const items = commitTransaction(this.#table, commit, version, this.#commitTtl(commit, ttl));
135
+ if (transaction.fences.length > 0) {
136
+ // The store's manifest may retain a type this repository doesn't. The marker check holds it
137
+ // at the version read here, or fails the transaction.
138
+ const manifest = await this.#readManifest(request);
139
+ const retention = manifest?.retention ?? [];
140
+ checkFenceRetention(transaction, (entityType) => retention.includes(entityType));
141
+ }
142
+ const { items, layout } = buildTransaction(this.#table, transaction, version, (commit) => this.#commitTtl(commit, ttlFor(commit.events[0].entityType)));
108
143
  try {
109
144
  await this.#client.transactWriteItems({ TransactItems: items }, request);
110
145
  }
111
146
  catch (error) {
112
- throw await this.#classifyCommit(commit, error, request);
147
+ throw await this.#classifyCommit(layout, error, request);
113
148
  }
114
149
  }
115
150
  async events(entityId, options) {
@@ -176,6 +211,9 @@ export class DynamoRepository {
176
211
  if (codes?.some((code) => THROTTLED.has(code))) {
177
212
  throw new TransientError('the snapshot write was throttled', { cause: error });
178
213
  }
214
+ if (codes?.includes('TransactionConflict')) {
215
+ throw new TransientError(`transaction conflict on ${snapshot.entityId}: another writer was committing at the same time`, { cause: error });
216
+ }
179
217
  throw error;
180
218
  }
181
219
  // The head guard failed. A refreshing guard can fail on its ttl clause alone, when another
@@ -357,15 +395,22 @@ export class DynamoRepository {
357
395
  }
358
396
  return new FeatureManifestChangedError({ cause });
359
397
  }
360
- async #classifyCommit(commit, error, request) {
398
+ // Maps a cancelled transaction to the error it reports. The control checks outrank everything.
399
+ // Then every failed stream and fence is classified, and the ranking reports the first drift, else
400
+ // the first gap, else the first other failure, in transaction order.
401
+ async #classifyCommit(layout, error, request) {
361
402
  const codes = cancellationCodes(error);
362
403
  if (codes === null) {
363
404
  return error;
364
405
  }
365
406
  const failed = codes.indexOf(CONDITION_FAILED);
366
407
  if (failed === -1) {
367
- if (codes.includes('TransactionConflict')) {
368
- return new ConflictError(commit.entityId, 'transaction conflict: another writer was committing at the same time');
408
+ // Another transaction was writing one of the items, perhaps one this commit never changes
409
+ // (a control check): nothing was written and nothing is known to be stale, so it is
410
+ // transient, like throttling, and never a conflict, as in Go and Rust.
411
+ const colliding = codes.indexOf('TransactionConflict');
412
+ if (colliding !== -1) {
413
+ return new TransientError(`transaction conflict on ${entityAt(layout, colliding)}: another writer was committing at the same time`, { cause: error });
369
414
  }
370
415
  if (codes.some((code) => THROTTLED.has(code))) {
371
416
  return new TransientError('the commit was throttled', { cause: error });
@@ -379,14 +424,42 @@ export class DynamoRepository {
379
424
  if (codes[1] === CONDITION_FAILED) {
380
425
  return new RebuildInProgressError();
381
426
  }
382
- // The stream's head update, then its event puts and its snapshot put.
383
- const head = 2;
384
- const eventsEnd = head + 1 + commit.events.length;
385
- const eventFailed = codes.slice(head + 1, eventsEnd).includes(CONDITION_FAILED);
427
+ const failures = [];
428
+ for (const stream of layout.streams) {
429
+ const failure = await this.#streamFailure(stream, codes, request);
430
+ if (failure !== null) {
431
+ failures.push(failure);
432
+ }
433
+ }
434
+ for (const { index, fence } of layout.fences) {
435
+ if (codes[index] !== CONDITION_FAILED) {
436
+ continue;
437
+ }
438
+ const verdict = await this.#probe(fence.entityId, false, request);
439
+ failures.push(verdict.drift ?? new FenceConflictError(fence.entityId, fence.head, fence.provenance));
440
+ }
441
+ const ranked = rankFailures(failures);
442
+ if (ranked !== undefined) {
443
+ return ranked;
444
+ }
445
+ // Only a projector operation's condition failed: its own error, as Go reports it.
446
+ if (failed < layout.projectorEnd) {
447
+ return error;
448
+ }
449
+ return new ConflictError(entityAt(layout, failed), 'conditional check failed: the sequence or snapshot changed since it was read');
450
+ }
451
+ // What a written stream contributes to a failed transaction, or null when none of its actions
452
+ // failed. A failed head update is classified by the probe. A head update that passed while an
453
+ // event put failed is an event above the head, unless retention explains it. A failed snapshot
454
+ // put alone is an ordinary conflict.
455
+ async #streamFailure(stream, codes, request) {
456
+ const { commit } = stream;
386
457
  const { entityId } = commit;
387
458
  const first = commit.events[0].sequence;
388
459
  const retained = this.#retention.has(commit.events[0].entityType);
389
- if (codes[head] === CONDITION_FAILED) {
460
+ const eventFailed = codes.slice(stream.head + 1, stream.eventsEnd).includes(CONDITION_FAILED);
461
+ const snapshotFailed = stream.snapshot !== null && codes[stream.snapshot] === CONDITION_FAILED;
462
+ if (codes[stream.head] === CONDITION_FAILED) {
390
463
  const verdict = await this.#probe(entityId, retained, request);
391
464
  if (verdict.drift) {
392
465
  return verdict.drift;
@@ -394,17 +467,21 @@ export class DynamoRepository {
394
467
  if (verdict.healthy && first > 1n && verdict.stored < first - 1n) {
395
468
  return new SequenceGapError(entityId, verdict.stored, first);
396
469
  }
470
+ return conflict(entityId);
397
471
  }
398
- else if (codes[head] === 'None' && eventFailed) {
399
- // The head update passed and an event put failed: an event sits above the head. With
400
- // retention, TTL can delete the head before an expired event, so a create passes its head
401
- // condition and fails on the leftover item: a conflict until TTL deletes the rest.
472
+ if (eventFailed && codes[stream.head] === 'None') {
473
+ // With retention, TTL can delete the head before an expired event, so a create passes its
474
+ // head condition and fails on the leftover item: a conflict until TTL deletes the rest. The
475
+ // probe already read the remaining items when it found the head item absent below the head
476
+ // its log derives; read them only if it didn't.
402
477
  const verdict = await this.#probe(entityId, retained, request);
403
- if (!retained || !(await this.#remainingItemsExpired(entityId, request))) {
478
+ const expired = retained && (verdict.expired ?? (await this.#remainingItemsExpired(entityId, request)));
479
+ if (!expired) {
404
480
  return verdict.drift ?? drift(entityId, verdict.head, verdict.derived);
405
481
  }
482
+ return conflict(entityId);
406
483
  }
407
- return new ConflictError(entityId, 'conditional check failed: the sequence or snapshot changed since it was read');
484
+ return eventFailed || snapshotFailed ? conflict(entityId) : null;
408
485
  }
409
486
  // Classifies a stream whose head update failed, as the failure-path probe does: a stored head
410
487
  // below the head its log derives is drift, an equal one is healthy, and one above it is either a
@@ -416,8 +493,10 @@ export class DynamoRepository {
416
493
  const head = (await this.#readHead(entityId, request))?.head ?? null;
417
494
  const stored = head ?? 0n;
418
495
  if (stored < derived) {
419
- if (retained && head === null && (await this.#remainingItemsExpired(entityId, request))) {
420
- return { healthy: false, drift: null, stored, head, derived };
496
+ if (retained && head === null) {
497
+ const expired = await this.#remainingItemsExpired(entityId, request);
498
+ const drifted = expired ? null : drift(entityId, head, derived);
499
+ return { healthy: false, drift: drifted, stored, head, derived, expired };
421
500
  }
422
501
  return { healthy: false, drift: drift(entityId, head, derived), stored, head, derived };
423
502
  }
@@ -678,6 +757,23 @@ export class DynamoRepository {
678
757
  const CONDITION_FAILED = 'ConditionalCheckFailed';
679
758
  // The cancellation reasons of a transaction DynamoDB throttled, which Go and Rust retry too.
680
759
  const THROTTLED = new Set(['ThrottlingError', 'ProvisionedThroughputExceeded']);
760
+ function conflict(entityId) {
761
+ return new ConflictError(entityId, 'conditional check failed: the sequence or snapshot changed since it was read');
762
+ }
763
+ // The stream an action of a transaction belongs to, for a failure no stream check explains: the
764
+ // written or fenced stream at that index, or else the transaction's first.
765
+ function entityAt(layout, index) {
766
+ for (const stream of layout.streams) {
767
+ if (index >= stream.head && (index < stream.eventsEnd || index === stream.snapshot)) {
768
+ return stream.commit.entityId;
769
+ }
770
+ }
771
+ const fenced = layout.fences.find((each) => each.index === index);
772
+ if (fenced !== undefined) {
773
+ return fenced.fence.entityId;
774
+ }
775
+ return layout.streams[0]?.commit.entityId ?? layout.fences[0]?.fence.entityId ?? '';
776
+ }
681
777
  function drift(entityId, head, derived) {
682
778
  return new StreamHeadDriftError(entityId, head, derived);
683
779
  }