@ddd-ts-kit/kernel 0.9.0 → 0.10.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 (260) hide show
  1. package/README.md +138 -2
  2. package/dist/application/criteria/index.d.ts +33 -0
  3. package/dist/application/criteria/index.d.ts.map +1 -0
  4. package/dist/application/criteria/index.js +33 -0
  5. package/dist/application/criteria/index.js.map +1 -0
  6. package/dist/application/event-sourcing/appended.d.ts +8 -0
  7. package/dist/application/event-sourcing/appended.d.ts.map +1 -0
  8. package/dist/application/event-sourcing/appended.js +2 -0
  9. package/dist/application/event-sourcing/appended.js.map +1 -0
  10. package/dist/application/event-sourcing/define-stored-event.d.ts +10 -0
  11. package/dist/application/event-sourcing/define-stored-event.d.ts.map +1 -0
  12. package/dist/application/event-sourcing/define-stored-event.js +22 -0
  13. package/dist/application/event-sourcing/define-stored-event.js.map +1 -0
  14. package/dist/application/event-sourcing/erased-personal-data.d.ts +3 -0
  15. package/dist/application/event-sourcing/erased-personal-data.d.ts.map +1 -0
  16. package/dist/application/event-sourcing/erased-personal-data.js +3 -0
  17. package/dist/application/event-sourcing/erased-personal-data.js.map +1 -0
  18. package/dist/application/event-sourcing/event-envelope.d.ts +9 -0
  19. package/dist/application/event-sourcing/event-envelope.d.ts.map +1 -0
  20. package/dist/application/event-sourcing/event-envelope.js +2 -0
  21. package/dist/application/event-sourcing/event-envelope.js.map +1 -0
  22. package/dist/application/event-sourcing/event-to-append.d.ts +15 -0
  23. package/dist/application/event-sourcing/event-to-append.d.ts.map +1 -0
  24. package/dist/application/event-sourcing/event-to-append.js +2 -0
  25. package/dist/application/event-sourcing/event-to-append.js.map +1 -0
  26. package/dist/application/event-sourcing/index.d.ts +21 -0
  27. package/dist/application/event-sourcing/index.d.ts.map +1 -0
  28. package/dist/application/event-sourcing/index.js +21 -0
  29. package/dist/application/event-sourcing/index.js.map +1 -0
  30. package/dist/application/event-sourcing/is-stored-data.d.ts +5 -0
  31. package/dist/application/event-sourcing/is-stored-data.d.ts.map +1 -0
  32. package/dist/application/event-sourcing/is-stored-data.js +5 -0
  33. package/dist/application/event-sourcing/is-stored-data.js.map +1 -0
  34. package/dist/application/event-sourcing/is-stored-list.d.ts +4 -0
  35. package/dist/application/event-sourcing/is-stored-list.d.ts.map +1 -0
  36. package/dist/application/event-sourcing/is-stored-list.js +5 -0
  37. package/dist/application/event-sourcing/is-stored-list.js.map +1 -0
  38. package/dist/application/event-sourcing/personal-data.d.ts +14 -0
  39. package/dist/application/event-sourcing/personal-data.d.ts.map +1 -0
  40. package/dist/application/event-sourcing/personal-data.js +9 -0
  41. package/dist/application/event-sourcing/personal-data.js.map +1 -0
  42. package/dist/application/event-sourcing/stored-data-of.d.ts +9 -0
  43. package/dist/application/event-sourcing/stored-data-of.d.ts.map +1 -0
  44. package/dist/application/event-sourcing/stored-data-of.js +40 -0
  45. package/dist/application/event-sourcing/stored-data-of.js.map +1 -0
  46. package/dist/application/event-sourcing/stored-data.d.ts +6 -0
  47. package/dist/application/event-sourcing/stored-data.d.ts.map +1 -0
  48. package/dist/application/event-sourcing/stored-data.js +2 -0
  49. package/dist/application/event-sourcing/stored-data.js.map +1 -0
  50. package/dist/application/event-sourcing/stored-event-data.d.ts +25 -0
  51. package/dist/application/event-sourcing/stored-event-data.d.ts.map +1 -0
  52. package/dist/application/event-sourcing/stored-event-data.js +82 -0
  53. package/dist/application/event-sourcing/stored-event-data.js.map +1 -0
  54. package/dist/application/event-sourcing/stored-event-spec.d.ts +20 -0
  55. package/dist/application/event-sourcing/stored-event-spec.d.ts.map +1 -0
  56. package/dist/application/event-sourcing/stored-event-spec.js +2 -0
  57. package/dist/application/event-sourcing/stored-event-spec.js.map +1 -0
  58. package/dist/application/event-sourcing/stored-event-type.d.ts +14 -0
  59. package/dist/application/event-sourcing/stored-event-type.d.ts.map +1 -0
  60. package/dist/application/event-sourcing/stored-event-type.js +2 -0
  61. package/dist/application/event-sourcing/stored-event-type.js.map +1 -0
  62. package/dist/application/event-sourcing/stored-event-types.d.ts +9 -0
  63. package/dist/application/event-sourcing/stored-event-types.d.ts.map +1 -0
  64. package/dist/application/event-sourcing/stored-event-types.js +2 -0
  65. package/dist/application/event-sourcing/stored-event-types.js.map +1 -0
  66. package/dist/application/event-sourcing/stored-event.d.ts +12 -0
  67. package/dist/application/event-sourcing/stored-event.d.ts.map +1 -0
  68. package/dist/application/event-sourcing/stored-event.js +2 -0
  69. package/dist/application/event-sourcing/stored-event.js.map +1 -0
  70. package/dist/application/event-sourcing/stored-json.d.ts +5 -0
  71. package/dist/application/event-sourcing/stored-json.d.ts.map +1 -0
  72. package/dist/application/event-sourcing/stored-json.js +2 -0
  73. package/dist/application/event-sourcing/stored-json.js.map +1 -0
  74. package/dist/application/event-sourcing/stored-snapshot.d.ts +11 -0
  75. package/dist/application/event-sourcing/stored-snapshot.d.ts.map +1 -0
  76. package/dist/application/event-sourcing/stored-snapshot.js +2 -0
  77. package/dist/application/event-sourcing/stored-snapshot.js.map +1 -0
  78. package/dist/application/event-sourcing/subject-key.d.ts +10 -0
  79. package/dist/application/event-sourcing/subject-key.d.ts.map +1 -0
  80. package/dist/application/event-sourcing/subject-key.js +2 -0
  81. package/dist/application/event-sourcing/subject-key.js.map +1 -0
  82. package/dist/application/event-sourcing/unreadable-event-error.d.ts +5 -0
  83. package/dist/application/event-sourcing/unreadable-event-error.d.ts.map +1 -0
  84. package/dist/application/event-sourcing/unreadable-event-error.js +8 -0
  85. package/dist/application/event-sourcing/unreadable-event-error.js.map +1 -0
  86. package/dist/application/event-sourcing/upcaster.d.ts +4 -0
  87. package/dist/application/event-sourcing/upcaster.d.ts.map +1 -0
  88. package/dist/application/event-sourcing/upcaster.js +2 -0
  89. package/dist/application/event-sourcing/upcaster.js.map +1 -0
  90. package/dist/application/persistence/append-changes.d.ts +18 -0
  91. package/dist/application/persistence/append-changes.d.ts.map +1 -0
  92. package/dist/application/persistence/append-changes.js +17 -0
  93. package/dist/application/persistence/append-changes.js.map +1 -0
  94. package/dist/application/ports/checkpoint-store.d.ts +14 -0
  95. package/dist/application/ports/checkpoint-store.d.ts.map +1 -0
  96. package/dist/application/ports/checkpoint-store.js +10 -0
  97. package/dist/application/ports/checkpoint-store.js.map +1 -0
  98. package/dist/application/ports/event-store.d.ts +24 -0
  99. package/dist/application/ports/event-store.d.ts.map +1 -0
  100. package/dist/application/ports/event-store.js +12 -0
  101. package/dist/application/ports/event-store.js.map +1 -0
  102. package/dist/application/ports/snapshot-store.d.ts +8 -0
  103. package/dist/application/ports/snapshot-store.d.ts.map +1 -0
  104. package/dist/application/ports/snapshot-store.js +7 -0
  105. package/dist/application/ports/snapshot-store.js.map +1 -0
  106. package/dist/application/ports/subject-key-store.d.ts +17 -0
  107. package/dist/application/ports/subject-key-store.d.ts.map +1 -0
  108. package/dist/application/ports/subject-key-store.js +13 -0
  109. package/dist/application/ports/subject-key-store.js.map +1 -0
  110. package/dist/declarations/decorators/projection-options.d.ts +5 -0
  111. package/dist/declarations/decorators/projection-options.d.ts.map +1 -0
  112. package/dist/declarations/decorators/projection-options.js +2 -0
  113. package/dist/declarations/decorators/projection-options.js.map +1 -0
  114. package/dist/declarations/decorators/projection.d.ts +4 -0
  115. package/dist/declarations/decorators/projection.d.ts.map +1 -1
  116. package/dist/declarations/decorators/projection.js +16 -8
  117. package/dist/declarations/decorators/projection.js.map +1 -1
  118. package/dist/declarations/model/projection-declaration.d.ts +3 -0
  119. package/dist/declarations/model/projection-declaration.d.ts.map +1 -1
  120. package/dist/declarations/model/projection-feed.d.ts +8 -0
  121. package/dist/declarations/model/projection-feed.d.ts.map +1 -0
  122. package/dist/declarations/model/projection-feed.js +2 -0
  123. package/dist/declarations/model/projection-feed.js.map +1 -0
  124. package/dist/declarations/projection/applying-projection.d.ts +14 -0
  125. package/dist/declarations/projection/applying-projection.d.ts.map +1 -0
  126. package/dist/declarations/projection/applying-projection.js +32 -0
  127. package/dist/declarations/projection/applying-projection.js.map +1 -0
  128. package/dist/declarations.d.ts +3 -0
  129. package/dist/declarations.d.ts.map +1 -1
  130. package/dist/declarations.js +1 -0
  131. package/dist/declarations.js.map +1 -1
  132. package/dist/domain/event-sourcing/aggregate-history.d.ts +10 -0
  133. package/dist/domain/event-sourcing/aggregate-history.d.ts.map +1 -0
  134. package/dist/domain/event-sourcing/aggregate-history.js +2 -0
  135. package/dist/domain/event-sourcing/aggregate-history.js.map +1 -0
  136. package/dist/domain/event-sourcing/aggregate-snapshot.d.ts +6 -0
  137. package/dist/domain/event-sourcing/aggregate-snapshot.d.ts.map +1 -0
  138. package/dist/domain/event-sourcing/aggregate-snapshot.js +2 -0
  139. package/dist/domain/event-sourcing/aggregate-snapshot.js.map +1 -0
  140. package/dist/domain/event-sourcing/appliers.d.ts +12 -0
  141. package/dist/domain/event-sourcing/appliers.d.ts.map +1 -0
  142. package/dist/domain/event-sourcing/appliers.js +2 -0
  143. package/dist/domain/event-sourcing/appliers.js.map +1 -0
  144. package/dist/domain/event-sourcing/event-sourced-aggregate.d.ts +28 -0
  145. package/dist/domain/event-sourcing/event-sourced-aggregate.d.ts.map +1 -0
  146. package/dist/domain/event-sourcing/event-sourced-aggregate.js +51 -0
  147. package/dist/domain/event-sourcing/event-sourced-aggregate.js.map +1 -0
  148. package/dist/domain/event-sourcing/unstored-events.d.ts +8 -0
  149. package/dist/domain/event-sourcing/unstored-events.d.ts.map +1 -0
  150. package/dist/domain/event-sourcing/unstored-events.js +7 -0
  151. package/dist/domain/event-sourcing/unstored-events.js.map +1 -0
  152. package/dist/index.d.ts +32 -32
  153. package/dist/index.d.ts.map +1 -1
  154. package/dist/index.js +32 -32
  155. package/dist/index.js.map +1 -1
  156. package/dist/infrastructure/event-sourcing/catch-up-subscription.d.ts +33 -0
  157. package/dist/infrastructure/event-sourcing/catch-up-subscription.d.ts.map +1 -0
  158. package/dist/infrastructure/event-sourcing/catch-up-subscription.js +39 -0
  159. package/dist/infrastructure/event-sourcing/catch-up-subscription.js.map +1 -0
  160. package/dist/infrastructure/event-sourcing/event-serializer.d.ts +35 -0
  161. package/dist/infrastructure/event-sourcing/event-serializer.d.ts.map +1 -0
  162. package/dist/infrastructure/event-sourcing/event-serializer.js +104 -0
  163. package/dist/infrastructure/event-sourcing/event-serializer.js.map +1 -0
  164. package/dist/infrastructure/event-sourcing/event-sourced-repository.d.ts +32 -0
  165. package/dist/infrastructure/event-sourcing/event-sourced-repository.d.ts.map +1 -0
  166. package/dist/infrastructure/event-sourcing/event-sourced-repository.js +68 -0
  167. package/dist/infrastructure/event-sourcing/event-sourced-repository.js.map +1 -0
  168. package/dist/infrastructure/event-sourcing/event-sourcing-stores.d.ts +12 -0
  169. package/dist/infrastructure/event-sourcing/event-sourcing-stores.d.ts.map +1 -0
  170. package/dist/infrastructure/event-sourcing/event-sourcing-stores.js +2 -0
  171. package/dist/infrastructure/event-sourcing/event-sourcing-stores.js.map +1 -0
  172. package/dist/infrastructure/event-sourcing/event-store-projection-rebuild.d.ts +34 -0
  173. package/dist/infrastructure/event-sourcing/event-store-projection-rebuild.d.ts.map +1 -0
  174. package/dist/infrastructure/event-sourcing/event-store-projection-rebuild.js +48 -0
  175. package/dist/infrastructure/event-sourcing/event-store-projection-rebuild.js.map +1 -0
  176. package/dist/infrastructure/event-sourcing/personal-data-cipher.d.ts +19 -0
  177. package/dist/infrastructure/event-sourcing/personal-data-cipher.d.ts.map +1 -0
  178. package/dist/infrastructure/event-sourcing/personal-data-cipher.js +84 -0
  179. package/dist/infrastructure/event-sourcing/personal-data-cipher.js.map +1 -0
  180. package/dist/infrastructure/event-sourcing/postgres/event-store-tables-options.d.ts +6 -0
  181. package/dist/infrastructure/event-sourcing/postgres/event-store-tables-options.d.ts.map +1 -0
  182. package/dist/infrastructure/event-sourcing/postgres/event-store-tables-options.js +2 -0
  183. package/dist/infrastructure/event-sourcing/postgres/event-store-tables-options.js.map +1 -0
  184. package/dist/infrastructure/event-sourcing/postgres/event-store-tables-sql.d.ts +9 -0
  185. package/dist/infrastructure/event-sourcing/postgres/event-store-tables-sql.d.ts.map +1 -0
  186. package/dist/infrastructure/event-sourcing/postgres/event-store-tables-sql.js +32 -0
  187. package/dist/infrastructure/event-sourcing/postgres/event-store-tables-sql.js.map +1 -0
  188. package/dist/infrastructure/event-sourcing/postgres/event-store-tables.d.ts +9 -0
  189. package/dist/infrastructure/event-sourcing/postgres/event-store-tables.d.ts.map +1 -0
  190. package/dist/infrastructure/event-sourcing/postgres/event-store-tables.js +9 -0
  191. package/dist/infrastructure/event-sourcing/postgres/event-store-tables.js.map +1 -0
  192. package/dist/infrastructure/event-sourcing/postgres/is-append-conflict.d.ts +3 -0
  193. package/dist/infrastructure/event-sourcing/postgres/is-append-conflict.d.ts.map +1 -0
  194. package/dist/infrastructure/event-sourcing/postgres/is-append-conflict.js +22 -0
  195. package/dist/infrastructure/event-sourcing/postgres/is-append-conflict.js.map +1 -0
  196. package/dist/infrastructure/event-sourcing/postgres/postgres-checkpoint-store.d.ts +12 -0
  197. package/dist/infrastructure/event-sourcing/postgres/postgres-checkpoint-store.d.ts.map +1 -0
  198. package/dist/infrastructure/event-sourcing/postgres/postgres-checkpoint-store.js +26 -0
  199. package/dist/infrastructure/event-sourcing/postgres/postgres-checkpoint-store.js.map +1 -0
  200. package/dist/infrastructure/event-sourcing/postgres/postgres-event-sourcing.d.ts +6 -0
  201. package/dist/infrastructure/event-sourcing/postgres/postgres-event-sourcing.d.ts.map +1 -0
  202. package/dist/infrastructure/event-sourcing/postgres/postgres-event-sourcing.js +14 -0
  203. package/dist/infrastructure/event-sourcing/postgres/postgres-event-sourcing.js.map +1 -0
  204. package/dist/infrastructure/event-sourcing/postgres/postgres-event-store.d.ts +31 -0
  205. package/dist/infrastructure/event-sourcing/postgres/postgres-event-store.d.ts.map +1 -0
  206. package/dist/infrastructure/event-sourcing/postgres/postgres-event-store.js +120 -0
  207. package/dist/infrastructure/event-sourcing/postgres/postgres-event-store.js.map +1 -0
  208. package/dist/infrastructure/event-sourcing/postgres/postgres-snapshot-store.d.ts +13 -0
  209. package/dist/infrastructure/event-sourcing/postgres/postgres-snapshot-store.d.ts.map +1 -0
  210. package/dist/infrastructure/event-sourcing/postgres/postgres-snapshot-store.js +28 -0
  211. package/dist/infrastructure/event-sourcing/postgres/postgres-snapshot-store.js.map +1 -0
  212. package/dist/infrastructure/event-sourcing/postgres/postgres-subject-key-store.d.ts +21 -0
  213. package/dist/infrastructure/event-sourcing/postgres/postgres-subject-key-store.d.ts.map +1 -0
  214. package/dist/infrastructure/event-sourcing/postgres/postgres-subject-key-store.js +48 -0
  215. package/dist/infrastructure/event-sourcing/postgres/postgres-subject-key-store.js.map +1 -0
  216. package/dist/infrastructure/event-sourcing/postgres/qualified-table.d.ts +4 -0
  217. package/dist/infrastructure/event-sourcing/postgres/qualified-table.d.ts.map +1 -0
  218. package/dist/infrastructure/event-sourcing/postgres/qualified-table.js +10 -0
  219. package/dist/infrastructure/event-sourcing/postgres/qualified-table.js.map +1 -0
  220. package/dist/infrastructure/event-sourcing/postgres/stored-data-from.d.ts +4 -0
  221. package/dist/infrastructure/event-sourcing/postgres/stored-data-from.d.ts.map +1 -0
  222. package/dist/infrastructure/event-sourcing/postgres/stored-data-from.js +23 -0
  223. package/dist/infrastructure/event-sourcing/postgres/stored-data-from.js.map +1 -0
  224. package/dist/infrastructure/event-sourcing/rewrite-objects.d.ts +8 -0
  225. package/dist/infrastructure/event-sourcing/rewrite-objects.d.ts.map +1 -0
  226. package/dist/infrastructure/event-sourcing/rewrite-objects.js +16 -0
  227. package/dist/infrastructure/event-sourcing/rewrite-objects.js.map +1 -0
  228. package/dist/infrastructure/event-sourcing/snapshot-policy.d.ts +14 -0
  229. package/dist/infrastructure/event-sourcing/snapshot-policy.d.ts.map +1 -0
  230. package/dist/infrastructure/event-sourcing/snapshot-policy.js +2 -0
  231. package/dist/infrastructure/event-sourcing/snapshot-policy.js.map +1 -0
  232. package/dist/infrastructure/event-sourcing/stored-origins.d.ts +5 -0
  233. package/dist/infrastructure/event-sourcing/stored-origins.d.ts.map +1 -0
  234. package/dist/infrastructure/event-sourcing/stored-origins.js +3 -0
  235. package/dist/infrastructure/event-sourcing/stored-origins.js.map +1 -0
  236. package/dist/infrastructure/event-sourcing/stored-stamp-of.d.ts +10 -0
  237. package/dist/infrastructure/event-sourcing/stored-stamp-of.d.ts.map +1 -0
  238. package/dist/infrastructure/event-sourcing/stored-stamp-of.js +14 -0
  239. package/dist/infrastructure/event-sourcing/stored-stamp-of.js.map +1 -0
  240. package/dist/infrastructure/in-memory/in-memory-checkpoint-store.d.ts +8 -0
  241. package/dist/infrastructure/in-memory/in-memory-checkpoint-store.d.ts.map +1 -0
  242. package/dist/infrastructure/in-memory/in-memory-checkpoint-store.js +15 -0
  243. package/dist/infrastructure/in-memory/in-memory-checkpoint-store.js.map +1 -0
  244. package/dist/infrastructure/in-memory/in-memory-event-store.d.ts +14 -0
  245. package/dist/infrastructure/in-memory/in-memory-event-store.d.ts.map +1 -0
  246. package/dist/infrastructure/in-memory/in-memory-event-store.js +37 -0
  247. package/dist/infrastructure/in-memory/in-memory-event-store.js.map +1 -0
  248. package/dist/infrastructure/in-memory/in-memory-snapshot-store.d.ts +9 -0
  249. package/dist/infrastructure/in-memory/in-memory-snapshot-store.d.ts.map +1 -0
  250. package/dist/infrastructure/in-memory/in-memory-snapshot-store.js +15 -0
  251. package/dist/infrastructure/in-memory/in-memory-snapshot-store.js.map +1 -0
  252. package/dist/infrastructure/in-memory/in-memory-subject-key-store.d.ts +10 -0
  253. package/dist/infrastructure/in-memory/in-memory-subject-key-store.d.ts.map +1 -0
  254. package/dist/infrastructure/in-memory/in-memory-subject-key-store.js +21 -0
  255. package/dist/infrastructure/in-memory/in-memory-subject-key-store.js.map +1 -0
  256. package/dist/infrastructure/read-model/shadow-read-model-store.d.ts +20 -0
  257. package/dist/infrastructure/read-model/shadow-read-model-store.d.ts.map +1 -0
  258. package/dist/infrastructure/read-model/shadow-read-model-store.js +25 -0
  259. package/dist/infrastructure/read-model/shadow-read-model-store.js.map +1 -0
  260. package/package.json +1 -1
@@ -0,0 +1,48 @@
1
+ import { ShadowReadModelStore } from '../read-model/shadow-read-model-store.js';
2
+ /**
3
+ * Rebuilds a read model fed by the event store from position zero, without stopping its catch-up
4
+ * subscription. An empty copy of the table is filled by replaying the whole store through the
5
+ * projection; then, in one transaction, the writers are held, the events appended meanwhile are
6
+ * replayed, and the copy takes the place of the table. The subscription goes on from its
7
+ * checkpoint: what it applies again is stale against the stamps of the copy.
8
+ */
9
+ export class EventStoreProjectionRebuild {
10
+ parts;
11
+ constructor(parts) {
12
+ this.parts = parts;
13
+ }
14
+ async run() {
15
+ const { table, unitOfWork } = this.parts;
16
+ await table.abandonRebuild();
17
+ await table.beginRebuild();
18
+ try {
19
+ const apply = this.parts.applierOn(new ShadowReadModelStore(table.shadow()));
20
+ const copied = await this.replay(apply, 0);
21
+ const replayed = await unitOfWork.run(async () => {
22
+ await table.holdWriters();
23
+ const caughtUp = await this.replay(apply, copied.position);
24
+ await table.promote();
25
+ return copied.read + caughtUp.read;
26
+ });
27
+ return { replayed };
28
+ }
29
+ catch (error) {
30
+ await table.abandonRebuild();
31
+ throw error;
32
+ }
33
+ }
34
+ async replay(apply, from) {
35
+ const { store, batchSize = 500 } = this.parts;
36
+ let position = from;
37
+ let read = 0;
38
+ for (let events = await store.readAll(position, batchSize); events.length > 0; events = await store.readAll(position, batchSize)) {
39
+ for (const event of events) {
40
+ await apply(event);
41
+ position = event.position;
42
+ }
43
+ read += events.length;
44
+ }
45
+ return { position, read };
46
+ }
47
+ }
48
+ //# sourceMappingURL=event-store-projection-rebuild.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-store-projection-rebuild.js","sourceRoot":"","sources":["../../../src/infrastructure/event-sourcing/event-store-projection-rebuild.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AAmBhF;;;;;;GAMG;AACH,MAAM,OAAO,2BAA2B;IACT,KAAK;IAAlC,YAA6B,KAA4C;qBAA5C,KAAK;IAA0C,CAAC;IAE7E,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAEzC,MAAM,KAAK,CAAC,cAAc,EAAE,CAAC;QAC7B,MAAM,KAAK,CAAC,YAAY,EAAE,CAAC;QAE3B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,oBAAoB,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YAC7E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAE3C,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;gBAC/C,MAAM,KAAK,CAAC,WAAW,EAAE,CAAC;gBAE1B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAE3D,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;gBAEtB,OAAO,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;YACrC,CAAC,CAAC,CAAC;YAEH,OAAO,EAAE,QAAQ,EAAE,CAAC;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,CAAC,cAAc,EAAE,CAAC;YAE7B,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,MAAM,CAClB,KAA4C,EAC5C,IAAY;QAEZ,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC9C,IAAI,QAAQ,GAAG,IAAI,CAAC;QACpB,IAAI,IAAI,GAAG,CAAC,CAAC;QAEb,KACE,IAAI,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,EACrD,MAAM,CAAC,MAAM,GAAG,CAAC,EACjB,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,EACjD,CAAC;YACD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;gBACnB,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;YAC5B,CAAC;YAED,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC;QACxB,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC5B,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ import type { StoredData } from '../../application/event-sourcing/stored-data.js';
2
+ import type { SubjectKeyStore } from '../../application/ports/subject-key-store.js';
3
+ /**
4
+ * Crypto-shredding of the personal values of stored data. `seal` replaces each value marked with
5
+ * `personalData(subject, value)` by its encryption under the current key of its subject (AES-256-GCM,
6
+ * a fresh nonce per value, the key id written next to it); `open` gives the values back, and
7
+ * `ERASED_PERSONAL_DATA` for those whose key was destroyed by `SubjectKeyStore.erase`.
8
+ */
9
+ export declare class PersonalDataCipher {
10
+ private readonly keys;
11
+ constructor(keys: SubjectKeyStore);
12
+ /** Whether the data holds a personal value, sealed or not. */
13
+ static holdsPersonalData(data: StoredData): boolean;
14
+ seal(data: StoredData): Promise<StoredData>;
15
+ open(data: StoredData): Promise<StoredData>;
16
+ private sealed;
17
+ private opened;
18
+ }
19
+ //# sourceMappingURL=personal-data-cipher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"personal-data-cipher.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/event-sourcing/personal-data-cipher.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iDAAiD,CAAC;AAGlF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8CAA8C,CAAC;AAOpF;;;;;GAKG;AACH,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,eAAe,EAAI;IAEtD,8DAA8D;IAC9D,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAElD;IAED,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAM1C;IAED,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAM1C;YAEa,MAAM;YAYN,MAAM;CAiBrB"}
@@ -0,0 +1,84 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
2
+ import { ERASED_PERSONAL_DATA } from '../../application/event-sourcing/erased-personal-data.js';
3
+ import { isStoredData } from '../../application/event-sourcing/is-stored-data.js';
4
+ import { rewriteObjects } from './rewrite-objects.js';
5
+ const ALGORITHM = 'aes-256-gcm';
6
+ const IV_BYTES = 12;
7
+ const TAG_BYTES = 16;
8
+ /**
9
+ * Crypto-shredding of the personal values of stored data. `seal` replaces each value marked with
10
+ * `personalData(subject, value)` by its encryption under the current key of its subject (AES-256-GCM,
11
+ * a fresh nonce per value, the key id written next to it); `open` gives the values back, and
12
+ * `ERASED_PERSONAL_DATA` for those whose key was destroyed by `SubjectKeyStore.erase`.
13
+ */
14
+ export class PersonalDataCipher {
15
+ keys;
16
+ constructor(keys) {
17
+ this.keys = keys;
18
+ }
19
+ /** Whether the data holds a personal value, sealed or not. */
20
+ static holdsPersonalData(data) {
21
+ return holdsIn(data);
22
+ }
23
+ seal(data) {
24
+ return rewriteObjects(data, (object) => {
25
+ const personal = personalOf(object);
26
+ return personal === undefined ? undefined : this.sealed(personal.subject, personal.value);
27
+ });
28
+ }
29
+ open(data) {
30
+ return rewriteObjects(data, (object) => {
31
+ const sealed = sealedOf(object);
32
+ return sealed === undefined ? undefined : this.opened(sealed);
33
+ });
34
+ }
35
+ async sealed(subject, value) {
36
+ const key = await this.keys.keyFor(subject);
37
+ const iv = randomBytes(IV_BYTES);
38
+ const cipher = createCipheriv(ALGORITHM, materialOf(key), iv);
39
+ cipher.setAAD(Buffer.from(key.id));
40
+ const encrypted = Buffer.concat([cipher.update(value, 'utf8'), cipher.final(), cipher.getAuthTag()]);
41
+ return { $sealed: { key: key.id, iv: iv.toString('base64'), data: encrypted.toString('base64') } };
42
+ }
43
+ async opened(sealed) {
44
+ const key = await this.keys.find(sealed.key);
45
+ if (key === undefined) {
46
+ return ERASED_PERSONAL_DATA;
47
+ }
48
+ const bytes = Buffer.from(sealed.data, 'base64');
49
+ const decipher = createDecipheriv(ALGORITHM, materialOf(key), Buffer.from(sealed.iv, 'base64'));
50
+ decipher.setAAD(Buffer.from(key.id));
51
+ decipher.setAuthTag(bytes.subarray(bytes.length - TAG_BYTES));
52
+ return Buffer.concat([decipher.update(bytes.subarray(0, bytes.length - TAG_BYTES)), decipher.final()]).toString('utf8');
53
+ }
54
+ }
55
+ function materialOf(key) {
56
+ return Buffer.from(key.material, 'base64');
57
+ }
58
+ function holdsIn(value) {
59
+ if (isStoredData(value)) {
60
+ return isMarked(value) || Object.values(value).some(holdsIn);
61
+ }
62
+ return Array.isArray(value) && value.some((item) => holdsIn(item));
63
+ }
64
+ function isMarked(value) {
65
+ return personalOf(value) !== undefined || sealedOf(value) !== undefined;
66
+ }
67
+ function personalOf(object) {
68
+ const marked = onlyField(object, '$personal');
69
+ const subject = marked?.['subject'];
70
+ const value = marked?.['value'];
71
+ return typeof subject === 'string' && typeof value === 'string' ? { subject, value } : undefined;
72
+ }
73
+ function sealedOf(object) {
74
+ const marked = onlyField(object, '$sealed');
75
+ const key = marked?.['key'];
76
+ const iv = marked?.['iv'];
77
+ const data = marked?.['data'];
78
+ return typeof key === 'string' && typeof iv === 'string' && typeof data === 'string' ? { key, iv, data } : undefined;
79
+ }
80
+ function onlyField(object, field) {
81
+ const value = object[field];
82
+ return Object.keys(object).length === 1 && isStoredData(value) ? value : undefined;
83
+ }
84
+ //# sourceMappingURL=personal-data-cipher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"personal-data-cipher.js","sourceRoot":"","sources":["../../../src/infrastructure/event-sourcing/personal-data-cipher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /** Where the tables of an event store live in PostgreSQL. */
2
+ export interface EventStoreTablesOptions {
3
+ /** The schema of the bounded context; created when missing. Without it, the default schema of the connection. */
4
+ readonly schema?: string;
5
+ }
6
+ //# sourceMappingURL=event-store-tables-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-store-tables-options.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/event-store-tables-options.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,MAAM,WAAW,uBAAuB;IACtC,iHAAiH;IACjH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=event-store-tables-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-store-tables-options.js","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/event-store-tables-options.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ import type { EventStoreTablesOptions } from './event-store-tables-options.js';
2
+ /**
3
+ * The statements that create the tables of an event store on PostgreSQL, for a migration of the
4
+ * project: the events, the head of the store (the position the next append starts after), the
5
+ * snapshots, the checkpoints of the subscriptions and the keys of the data subjects. Each one
6
+ * can run again.
7
+ */
8
+ export declare function eventStoreTablesSql(options?: EventStoreTablesOptions): readonly string[];
9
+ //# sourceMappingURL=event-store-tables-sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-store-tables-sql.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/event-store-tables-sql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAG/E;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,uBAA4B,GAAG,SAAS,MAAM,EAAE,CAwB5F"}
@@ -0,0 +1,32 @@
1
+ import { qualifiedTable } from './qualified-table.js';
2
+ /**
3
+ * The statements that create the tables of an event store on PostgreSQL, for a migration of the
4
+ * project: the events, the head of the store (the position the next append starts after), the
5
+ * snapshots, the checkpoints of the subscriptions and the keys of the data subjects. Each one
6
+ * can run again.
7
+ */
8
+ export function eventStoreTablesSql(options = {}) {
9
+ const { schema } = options;
10
+ const table = (name) => qualifiedTable(schema, name);
11
+ return [
12
+ ...(schema === undefined ? [] : [`create schema if not exists "${schema.replaceAll('"', '""')}"`]),
13
+ `create table if not exists ${table('head')} (id smallint primary key, position bigint not null)`,
14
+ `create table if not exists ${table('events')} (
15
+ position bigint primary key,
16
+ stream text not null,
17
+ version integer not null,
18
+ event_id text not null unique,
19
+ type text not null,
20
+ schema_version integer not null,
21
+ data jsonb not null,
22
+ metadata jsonb not null,
23
+ occurred_at timestamptz(3) not null,
24
+ recorded_at timestamptz(3) not null default now(),
25
+ unique (stream, version)
26
+ )`,
27
+ `create table if not exists ${table('snapshots')} (stream text primary key, version integer not null, schema_version integer not null, data jsonb not null, taken_at timestamptz(3) not null default now())`,
28
+ `create table if not exists ${table('checkpoints')} (subscriber text primary key, position bigint not null, updated_at timestamptz(3) not null default now())`,
29
+ `create table if not exists ${table('subjectKeys')} (subject text primary key, key_id text not null unique, material text not null, created_at timestamptz(3) not null default now())`,
30
+ ];
31
+ }
32
+ //# sourceMappingURL=event-store-tables-sql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-store-tables-sql.js","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/event-store-tables-sql.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAO,GAA4B,EAAE;IACvE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3B,MAAM,KAAK,GAAG,CAAC,IAA0C,EAAU,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAEnG,OAAO;QACL,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,gCAAgC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QAClG,8BAA8B,KAAK,CAAC,MAAM,CAAC,sDAAsD;QACjG,8BAA8B,KAAK,CAAC,QAAQ,CAAC;;;;;;;;;;;;EAY/C;QACE,8BAA8B,KAAK,CAAC,WAAW,CAAC,4JAA4J;QAC5M,8BAA8B,KAAK,CAAC,aAAa,CAAC,4GAA4G;QAC9J,8BAA8B,KAAK,CAAC,aAAa,CAAC,oIAAoI;KACvL,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ /** The names of the tables of an event store, fixed: a context that keeps one gives it a schema of its own. */
2
+ export declare const EVENT_STORE_TABLES: {
3
+ readonly events: 'stored_events';
4
+ readonly head: 'stored_events_head';
5
+ readonly snapshots: 'stream_snapshots';
6
+ readonly checkpoints: 'subscription_checkpoints';
7
+ readonly subjectKeys: 'subject_keys';
8
+ };
9
+ //# sourceMappingURL=event-store-tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-store-tables.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/event-store-tables.ts"],"names":[],"mappings":"AAAA,+GAA+G;AAC/G,eAAO,MAAM,kBAAkB;aAC7B,MAAM,EAAE,eAAe;aACvB,IAAI,EAAE,oBAAoB;aAC1B,SAAS,EAAE,kBAAkB;aAC7B,WAAW,EAAE,0BAA0B;aACvC,WAAW,EAAE,cAAc;CACnB,CAAC"}
@@ -0,0 +1,9 @@
1
+ /** The names of the tables of an event store, fixed: a context that keeps one gives it a schema of its own. */
2
+ export const EVENT_STORE_TABLES = {
3
+ events: 'stored_events',
4
+ head: 'stored_events_head',
5
+ snapshots: 'stream_snapshots',
6
+ checkpoints: 'subscription_checkpoints',
7
+ subjectKeys: 'subject_keys',
8
+ };
9
+ //# sourceMappingURL=event-store-tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-store-tables.js","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/event-store-tables.ts"],"names":[],"mappings":"AAAA,+GAA+G;AAC/G,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,MAAM,EAAE,eAAe;IACvB,IAAI,EAAE,oBAAoB;IAC1B,SAAS,EAAE,kBAAkB;IAC7B,WAAW,EAAE,0BAA0B;IACvC,WAAW,EAAE,cAAc;CACnB,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Whether an append lost to a concurrent one, whatever wraps the error of the driver: the driver alone (knex), `cause` (drizzle), `driverError` (TypeORM) or `meta` (Prisma). */
2
+ export declare function isAppendConflict(error: unknown, depth?: number): boolean;
3
+ //# sourceMappingURL=is-append-conflict.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-append-conflict.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/is-append-conflict.ts"],"names":[],"mappings":"AAWA,kLAAkL;AAClL,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAQ,GAAG,OAAO,CAYvE"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * What PostgreSQL answers when another transaction appended first: a duplicate (stream, version)
3
+ * (23505); a serialization failure (40001) when the unit of work runs at REPEATABLE READ or
4
+ * SERIALIZABLE and the head of the store moved since it began; a deadlock (40P01) when the same
5
+ * transaction took other row locks in another order than a concurrent one. Each is a conflict the
6
+ * caller retries, never a fault of the store.
7
+ */
8
+ const CONFLICTS = new Set(['23505', '40001', '40P01']);
9
+ const WRAPPERS = ['cause', 'driverError', 'meta', 'originalError'];
10
+ const DEPTH = 4;
11
+ /** Whether an append lost to a concurrent one, whatever wraps the error of the driver: the driver alone (knex), `cause` (drizzle), `driverError` (TypeORM) or `meta` (Prisma). */
12
+ export function isAppendConflict(error, depth = DEPTH) {
13
+ if (typeof error !== 'object' || error === null || depth === 0) {
14
+ return false;
15
+ }
16
+ const code = Reflect.get(error, 'code');
17
+ if (typeof code === 'string' && CONFLICTS.has(code)) {
18
+ return true;
19
+ }
20
+ return WRAPPERS.some((key) => isAppendConflict(Reflect.get(error, key), depth - 1));
21
+ }
22
+ //# sourceMappingURL=is-append-conflict.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-append-conflict.js","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/is-append-conflict.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AACvD,MAAM,QAAQ,GAAG,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC;AACnE,MAAM,KAAK,GAAG,CAAC,CAAC;AAEhB,kLAAkL;AAClL,MAAM,UAAU,gBAAgB,CAAC,KAAc,EAAE,KAAK,GAAG,KAAK;IAC5D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAC/D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,IAAI,GAAY,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAEjD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;AACtF,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { CheckpointStore } from '../../../application/ports/checkpoint-store.js';
2
+ import type { SqlRun } from '../../read-model/sql-run.js';
3
+ import type { EventStoreTablesOptions } from './event-store-tables-options.js';
4
+ /** The checkpoints of the subscriptions on PostgreSQL, moved by a statement guarded by the position it starts from. */
5
+ export declare class PostgresCheckpointStore implements CheckpointStore {
6
+ private readonly run;
7
+ private readonly table;
8
+ constructor(run: SqlRun, options?: EventStoreTablesOptions);
9
+ position(subscriber: string): Promise<number>;
10
+ advance(subscriber: string, from: number, to: number): Promise<void>;
11
+ }
12
+ //# sourceMappingURL=postgres-checkpoint-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-checkpoint-store.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/postgres-checkpoint-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAEtF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAG/E,uHAAuH;AACvH,qBAAa,uBAAwB,YAAW,eAAe;IAI3D,OAAO,CAAC,QAAQ,CAAC,GAAG;IAHtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAE/B,YACmB,GAAG,EAAE,MAAM,EAC5B,OAAO,GAAE,uBAA4B,EAGtC;IAEK,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIlD;IAEK,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBzE;CACF"}
@@ -0,0 +1,26 @@
1
+ import { ConcurrencyError } from '../../../domain/errors/concurrency-error.js';
2
+ import { qualifiedTable } from './qualified-table.js';
3
+ /** The checkpoints of the subscriptions on PostgreSQL, moved by a statement guarded by the position it starts from. */
4
+ export class PostgresCheckpointStore {
5
+ run;
6
+ table;
7
+ constructor(run, options = {}) {
8
+ this.run = run;
9
+ this.table = qualifiedTable(options.schema, 'checkpoints');
10
+ }
11
+ async position(subscriber) {
12
+ const [row] = await this.run(`select position from ${this.table} where subscriber = $1::text`, [subscriber]);
13
+ return Number(row?.['position'] ?? 0);
14
+ }
15
+ async advance(subscriber, from, to) {
16
+ const moved = from === 0
17
+ ? await this.run(`insert into ${this.table} as c (subscriber, position) values ($1::text, $2::bigint)
18
+ on conflict (subscriber) do update set position = excluded.position, updated_at = now() where c.position = $3::bigint
19
+ returning subscriber`, [subscriber, to, from])
20
+ : await this.run(`update ${this.table} set position = $1::bigint, updated_at = now() where subscriber = $2::text and position = $3::bigint returning subscriber`, [to, subscriber, from]);
21
+ if (moved.length === 0) {
22
+ throw new ConcurrencyError(subscriber);
23
+ }
24
+ }
25
+ }
26
+ //# sourceMappingURL=postgres-checkpoint-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-checkpoint-store.js","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/postgres-checkpoint-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,6CAA6C,CAAC;AAG/E,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,uHAAuH;AACvH,MAAM,OAAO,uBAAuB;IAIf,GAAG;IAHL,KAAK,CAAS;IAE/B,YACmB,GAAW,EAC5B,OAAO,GAA4B,EAAE;mBADpB,GAAG;QAGpB,IAAI,CAAC,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,UAAkB;QAC/B,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,wBAAwB,IAAI,CAAC,KAAK,8BAA8B,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;QAE7G,OAAO,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,UAAkB,EAAE,IAAY,EAAE,EAAU;QACxD,MAAM,KAAK,GACT,IAAI,KAAK,CAAC;YACR,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CACZ,eAAe,IAAI,CAAC,KAAK;;qBAEhB,EACT,CAAC,UAAU,EAAE,EAAE,EAAE,IAAI,CAAC,CACvB;YACH,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CACZ,UAAU,IAAI,CAAC,KAAK,2HAA2H,EAC/I,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,CACvB,CAAC;QAER,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,gBAAgB,CAAC,UAAU,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ import type { SqlRun } from '../../read-model/sql-run.js';
2
+ import type { EventSourcingStores } from '../event-sourcing-stores.js';
3
+ import type { EventStoreTablesOptions } from './event-store-tables-options.js';
4
+ /** The stores of event sourcing on PostgreSQL over the statements of an ORM adapter: what each adapter package hands out. */
5
+ export declare function postgresEventSourcing(run: SqlRun, options?: EventStoreTablesOptions): EventSourcingStores;
6
+ //# sourceMappingURL=postgres-event-sourcing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-event-sourcing.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/postgres-event-sourcing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAM/E,6HAA6H;AAC7H,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,GAAG,mBAAmB,CAO7G"}
@@ -0,0 +1,14 @@
1
+ import { PostgresCheckpointStore } from './postgres-checkpoint-store.js';
2
+ import { PostgresEventStore } from './postgres-event-store.js';
3
+ import { PostgresSnapshotStore } from './postgres-snapshot-store.js';
4
+ import { PostgresSubjectKeyStore } from './postgres-subject-key-store.js';
5
+ /** The stores of event sourcing on PostgreSQL over the statements of an ORM adapter: what each adapter package hands out. */
6
+ export function postgresEventSourcing(run, options = {}) {
7
+ return {
8
+ events: new PostgresEventStore(run, options),
9
+ snapshots: new PostgresSnapshotStore(run, options),
10
+ checkpoints: new PostgresCheckpointStore(run, options),
11
+ subjectKeys: new PostgresSubjectKeyStore(run, options),
12
+ };
13
+ }
14
+ //# sourceMappingURL=postgres-event-sourcing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-event-sourcing.js","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/postgres-event-sourcing.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,6HAA6H;AAC7H,MAAM,UAAU,qBAAqB,CAAC,GAAW,EAAE,OAAO,GAA4B,EAAE;IACtF,OAAO;QACL,MAAM,EAAE,IAAI,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC;QAC5C,SAAS,EAAE,IAAI,qBAAqB,CAAC,GAAG,EAAE,OAAO,CAAC;QAClD,WAAW,EAAE,IAAI,uBAAuB,CAAC,GAAG,EAAE,OAAO,CAAC;QACtD,WAAW,EAAE,IAAI,uBAAuB,CAAC,GAAG,EAAE,OAAO,CAAC;KACvD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { Appended } from '../../../application/event-sourcing/appended.js';
2
+ import type { EventToAppend } from '../../../application/event-sourcing/event-to-append.js';
3
+ import type { StoredEvent } from '../../../application/event-sourcing/stored-event.js';
4
+ import type { EventStore } from '../../../application/ports/event-store.js';
5
+ import type { SqlRun } from '../../read-model/sql-run.js';
6
+ import type { EventStoreTablesOptions } from './event-store-tables-options.js';
7
+ /**
8
+ * The event store on PostgreSQL, shared by the ORM adapters, which differ only in how they run a
9
+ * statement. An append is one statement on the executor of the unit of work: it takes the head of
10
+ * the store, a row locked until the transaction ends, only when the stream still has the expected
11
+ * version, and inserts the events after it. Appends therefore commit in the order of their
12
+ * positions, and a subscription that read up to a position never meets a smaller one later; the
13
+ * price is that two appends never run side by side, whatever their streams. A conflict that slips
14
+ * between the check and the insert hits the unique (stream, version) and is a ConcurrencyError too,
15
+ * and so are a serialization failure (a unit of work at REPEATABLE READ or SERIALIZABLE) and a
16
+ * deadlock with row locks the same transaction took in another order: the caller retries.
17
+ */
18
+ export declare class PostgresEventStore implements EventStore {
19
+ private readonly run;
20
+ private readonly events;
21
+ private readonly headRow;
22
+ constructor(run: SqlRun, options?: EventStoreTablesOptions);
23
+ append(stream: string, expectedVersion: number, events: readonly EventToAppend[]): Promise<Appended>;
24
+ readStream(stream: string, after?: number): Promise<readonly StoredEvent[]>;
25
+ readAll(after: number, limit: number): Promise<readonly StoredEvent[]>;
26
+ head(): Promise<number>;
27
+ private inserted;
28
+ private appendStatement;
29
+ private unchanged;
30
+ }
31
+ //# sourceMappingURL=postgres-event-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-event-store.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/postgres-event-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iDAAiD,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wDAAwD,CAAC;AAC5F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qDAAqD,CAAC;AACvF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2CAA2C,CAAC;AAE5E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAa/E;;;;;;;;;;GAUG;AACH,qBAAa,kBAAmB,YAAW,UAAU;IAMjD,OAAO,CAAC,QAAQ,CAAC,GAAG;IALtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,YACmB,GAAG,EAAE,MAAM,EAC5B,OAAO,GAAE,uBAA4B,EAItC;IAEK,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAiBzG;IAEK,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC,CAO3E;IAEK,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC,CAO3E;IAEK,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAI5B;YAEa,QAAQ;IAYtB,OAAO,CAAC,eAAe;YAmBT,SAAS;CAYxB"}
@@ -0,0 +1,120 @@
1
+ import { ConcurrencyError } from '../../../domain/errors/concurrency-error.js';
2
+ import { isAppendConflict } from './is-append-conflict.js';
3
+ import { qualifiedTable } from './qualified-table.js';
4
+ import { storedDataFrom } from './stored-data-from.js';
5
+ const COLUMNS = 'position, stream, version, event_id, type, schema_version, data, metadata, occurred_at';
6
+ // The instant as UTC text: drivers disagree on the zone they read a timestamptz in.
7
+ const SELECTED = `position, stream, version, event_id, type, schema_version, data, metadata,
8
+ to_char(occurred_at at time zone 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') as occurred_at`;
9
+ /**
10
+ * The event store on PostgreSQL, shared by the ORM adapters, which differ only in how they run a
11
+ * statement. An append is one statement on the executor of the unit of work: it takes the head of
12
+ * the store, a row locked until the transaction ends, only when the stream still has the expected
13
+ * version, and inserts the events after it. Appends therefore commit in the order of their
14
+ * positions, and a subscription that read up to a position never meets a smaller one later; the
15
+ * price is that two appends never run side by side, whatever their streams. A conflict that slips
16
+ * between the check and the insert hits the unique (stream, version) and is a ConcurrencyError too,
17
+ * and so are a serialization failure (a unit of work at REPEATABLE READ or SERIALIZABLE) and a
18
+ * deadlock with row locks the same transaction took in another order: the caller retries.
19
+ */
20
+ export class PostgresEventStore {
21
+ run;
22
+ events;
23
+ headRow;
24
+ constructor(run, options = {}) {
25
+ this.run = run;
26
+ this.events = qualifiedTable(options.schema, 'events');
27
+ this.headRow = qualifiedTable(options.schema, 'head');
28
+ }
29
+ async append(stream, expectedVersion, events) {
30
+ if (events.length === 0) {
31
+ return this.unchanged(stream, expectedVersion);
32
+ }
33
+ await this.run(`insert into ${this.headRow} (id, position) values (1, 0) on conflict (id) do nothing`, []);
34
+ const inserted = await this.inserted(stream, expectedVersion, events);
35
+ if (inserted.length === 0) {
36
+ throw new ConcurrencyError(stream);
37
+ }
38
+ return {
39
+ version: Math.max(...inserted.map((row) => Number(row['version']))),
40
+ position: Math.max(...inserted.map((row) => Number(row['position']))),
41
+ };
42
+ }
43
+ async readStream(stream, after = 0) {
44
+ const rows = await this.run(`select ${SELECTED} from ${this.events} where stream = $1::text and version > $2::integer order by version`, [stream, after]);
45
+ return rows.map(storedEventOf);
46
+ }
47
+ async readAll(after, limit) {
48
+ const rows = await this.run(`select ${SELECTED} from ${this.events} where position > $1::bigint order by position limit $2::integer`, [after, limit]);
49
+ return rows.map(storedEventOf);
50
+ }
51
+ async head() {
52
+ const [row] = await this.run(`select position from ${this.headRow} where id = 1`, []);
53
+ return Number(row?.['position'] ?? 0);
54
+ }
55
+ async inserted(stream, expected, events) {
56
+ try {
57
+ return await this.run(this.appendStatement(), [stream, expected, JSON.stringify(events.map(recordOf))]);
58
+ }
59
+ catch (error) {
60
+ if (isAppendConflict(error)) {
61
+ throw new ConcurrencyError(stream);
62
+ }
63
+ throw error;
64
+ }
65
+ }
66
+ appendStatement() {
67
+ return `with input as (
68
+ select $1::text as stream, $2::integer as expected, $3::jsonb as events
69
+ ), held as (
70
+ select coalesce(max(e.version), 0) as version from ${this.events} e, input where e.stream = input.stream
71
+ ), moved as (
72
+ update ${this.headRow} h set position = h.position + jsonb_array_length(input.events)
73
+ from input, held
74
+ where h.id = 1 and held.version = input.expected
75
+ returning h.position - jsonb_array_length(input.events) as base
76
+ )
77
+ insert into ${this.events} (${COLUMNS})
78
+ select moved.base + e.n, input.stream, input.expected + e.n, e.id, e.type, e.schema_version, e.data, e.metadata, e.occurred_at
79
+ from moved, input, jsonb_to_recordset(input.events)
80
+ as e(n integer, id text, type text, schema_version integer, data jsonb, metadata jsonb, occurred_at timestamptz)
81
+ order by e.n
82
+ returning position, version`;
83
+ }
84
+ async unchanged(stream, expected) {
85
+ const [row] = await this.run(`select coalesce(max(version), 0) as version from ${this.events} where stream = $1::text`, [stream]);
86
+ if (Number(row?.['version'] ?? 0) !== expected) {
87
+ throw new ConcurrencyError(stream);
88
+ }
89
+ return { version: expected, position: await this.head() };
90
+ }
91
+ }
92
+ function recordOf(event, index) {
93
+ return {
94
+ n: index + 1,
95
+ id: event.id,
96
+ type: event.type,
97
+ schema_version: event.schemaVersion,
98
+ data: event.data,
99
+ metadata: event.metadata ?? {},
100
+ occurred_at: event.occurredAt.toISOString(),
101
+ };
102
+ }
103
+ function storedEventOf(row) {
104
+ const id = String(row['event_id']);
105
+ return {
106
+ id,
107
+ stream: String(row['stream']),
108
+ version: Number(row['version']),
109
+ position: Number(row['position']),
110
+ type: String(row['type']),
111
+ schemaVersion: Number(row['schema_version']),
112
+ data: storedDataFrom(row['data'], `The data of stored event ${id}`),
113
+ metadata: storedDataFrom(row['metadata'], `The metadata of stored event ${id}`),
114
+ occurredAt: instantOf(row['occurred_at']),
115
+ };
116
+ }
117
+ function instantOf(value) {
118
+ return value instanceof Date ? value : new Date(String(value));
119
+ }
120
+ //# sourceMappingURL=postgres-event-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-event-store.js","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/postgres-event-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { StoredSnapshot } from '../../../application/event-sourcing/stored-snapshot.js';
2
+ import type { SnapshotStore } from '../../../application/ports/snapshot-store.js';
3
+ import type { SqlRun } from '../../read-model/sql-run.js';
4
+ import type { EventStoreTablesOptions } from './event-store-tables-options.js';
5
+ /** The snapshots of the streams on PostgreSQL: one row per stream, never replaced by an older version. */
6
+ export declare class PostgresSnapshotStore implements SnapshotStore {
7
+ private readonly run;
8
+ private readonly table;
9
+ constructor(run: SqlRun, options?: EventStoreTablesOptions);
10
+ find(stream: string): Promise<StoredSnapshot | undefined>;
11
+ save(snapshot: StoredSnapshot): Promise<void>;
12
+ }
13
+ //# sourceMappingURL=postgres-snapshot-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-snapshot-store.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/postgres-snapshot-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wDAAwD,CAAC;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAC;AAClF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAI/E,0GAA0G;AAC1G,qBAAa,qBAAsB,YAAW,aAAa;IAIvD,OAAO,CAAC,QAAQ,CAAC,GAAG;IAHtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAE/B,YACmB,GAAG,EAAE,MAAM,EAC5B,OAAO,GAAE,uBAA4B,EAGtC;IAEK,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAc9D;IAEK,IAAI,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAOlD;CACF"}
@@ -0,0 +1,28 @@
1
+ import { qualifiedTable } from './qualified-table.js';
2
+ import { storedDataFrom } from './stored-data-from.js';
3
+ /** The snapshots of the streams on PostgreSQL: one row per stream, never replaced by an older version. */
4
+ export class PostgresSnapshotStore {
5
+ run;
6
+ table;
7
+ constructor(run, options = {}) {
8
+ this.run = run;
9
+ this.table = qualifiedTable(options.schema, 'snapshots');
10
+ }
11
+ async find(stream) {
12
+ const [row] = await this.run(`select stream, version, schema_version, data from ${this.table} where stream = $1::text`, [stream]);
13
+ return row === undefined
14
+ ? undefined
15
+ : {
16
+ stream,
17
+ version: Number(row['version']),
18
+ schemaVersion: Number(row['schema_version']),
19
+ data: storedDataFrom(row['data'], `The snapshot of ${stream}`),
20
+ };
21
+ }
22
+ async save(snapshot) {
23
+ await this.run(`insert into ${this.table} as s (stream, version, schema_version, data) values ($1::text, $2::integer, $3::integer, $4::jsonb)
24
+ on conflict (stream) do update set version = excluded.version, schema_version = excluded.schema_version, data = excluded.data, taken_at = now()
25
+ where s.version < excluded.version`, [snapshot.stream, snapshot.version, snapshot.schemaVersion, JSON.stringify(snapshot.data)]);
26
+ }
27
+ }
28
+ //# sourceMappingURL=postgres-snapshot-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-snapshot-store.js","sourceRoot":"","sources":["../../../../src/infrastructure/event-sourcing/postgres/postgres-snapshot-store.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,0GAA0G;AAC1G,MAAM,OAAO,qBAAqB;IAIb,GAAG;IAHL,KAAK,CAAS;IAE/B,YACmB,GAAW,EAC5B,OAAO,GAA4B,EAAE;mBADpB,GAAG;QAGpB,IAAI,CAAC,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAc;QACvB,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,CAAC,GAAG,CAC1B,qDAAqD,IAAI,CAAC,KAAK,0BAA0B,EACzF,CAAC,MAAM,CAAC,CACT,CAAC;QAEF,OAAO,GAAG,KAAK,SAAS;YACtB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC;gBACE,MAAM;gBACN,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBAC/B,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;gBAC5C,IAAI,EAAE,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,mBAAmB,MAAM,EAAE,CAAC;aAC/D,CAAC;IACR,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAwB;QACjC,MAAM,IAAI,CAAC,GAAG,CACZ,eAAe,IAAI,CAAC,KAAK;;mCAEI,EAC7B,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAC3F,CAAC;IACJ,CAAC;CACF"}