@satorio/machinery 0.2.1

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 (341) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -0
  3. package/dictionary/all.json +4656 -0
  4. package/dictionary/all.toon +639 -0
  5. package/dictionary/core.json +221 -0
  6. package/dictionary/core.toon +33 -0
  7. package/dictionary/graphql.json +212 -0
  8. package/dictionary/graphql.toon +32 -0
  9. package/dictionary/http.json +113 -0
  10. package/dictionary/http.toon +21 -0
  11. package/dictionary/identifiers.json +266 -0
  12. package/dictionary/identifiers.toon +38 -0
  13. package/dictionary/index.json +96 -0
  14. package/dictionary/index.toon +18 -0
  15. package/dictionary/integration.json +131 -0
  16. package/dictionary/integration.toon +23 -0
  17. package/dictionary/money.json +383 -0
  18. package/dictionary/money.toon +51 -0
  19. package/dictionary/ports.json +392 -0
  20. package/dictionary/ports.toon +52 -0
  21. package/dictionary/postgres.json +473 -0
  22. package/dictionary/postgres.toon +61 -0
  23. package/dictionary/publishing.json +131 -0
  24. package/dictionary/publishing.toon +23 -0
  25. package/dictionary/queue.json +365 -0
  26. package/dictionary/queue.toon +49 -0
  27. package/dictionary/reference/api.mdx +843 -0
  28. package/dictionary/reference/uk/api.mdx +844 -0
  29. package/dictionary/storage.json +194 -0
  30. package/dictionary/storage.toon +30 -0
  31. package/dictionary/testing.json +707 -0
  32. package/dictionary/testing.toon +87 -0
  33. package/dictionary/time.json +311 -0
  34. package/dictionary/time.toon +43 -0
  35. package/dictionary/vitest.json +356 -0
  36. package/dictionary/vitest.toon +48 -0
  37. package/dictionary/workflow.json +185 -0
  38. package/dictionary/workflow.toon +29 -0
  39. package/dist/bin/command.d.ts +45 -0
  40. package/dist/bin/command.js +113 -0
  41. package/dist/bin.d.ts +2 -0
  42. package/dist/bin.js +11 -0
  43. package/dist/core/bigint-json.d.ts +6 -0
  44. package/dist/core/bigint-json.js +6 -0
  45. package/dist/core/closed-set.d.ts +14 -0
  46. package/dist/core/closed-set.js +8 -0
  47. package/dist/core/domain-error.d.ts +8 -0
  48. package/dist/core/domain-error.js +11 -0
  49. package/dist/core/environment.d.ts +124 -0
  50. package/dist/core/environment.js +115 -0
  51. package/dist/core/index.d.ts +13 -0
  52. package/dist/core/index.js +13 -0
  53. package/dist/core/ulid.d.ts +27 -0
  54. package/dist/core/ulid.js +27 -0
  55. package/dist/graphql/builder.d.ts +36 -0
  56. package/dist/graphql/builder.js +35 -0
  57. package/dist/graphql/context.d.ts +36 -0
  58. package/dist/graphql/context.js +69 -0
  59. package/dist/graphql/failure-classification.d.ts +11 -0
  60. package/dist/graphql/failure-classification.js +1 -0
  61. package/dist/graphql/failures.d.ts +62 -0
  62. package/dist/graphql/failures.js +30 -0
  63. package/dist/graphql/graphiql.d.ts +45 -0
  64. package/dist/graphql/graphiql.js +56 -0
  65. package/dist/graphql/index.d.ts +15 -0
  66. package/dist/graphql/index.js +14 -0
  67. package/dist/graphql/layer.d.ts +15 -0
  68. package/dist/graphql/layer.js +23 -0
  69. package/dist/graphql/request-handler.d.ts +26 -0
  70. package/dist/graphql/request-handler.js +33 -0
  71. package/dist/graphql/server.d.ts +19 -0
  72. package/dist/graphql/server.js +16 -0
  73. package/dist/graphql/settings.d.ts +55 -0
  74. package/dist/graphql/settings.js +82 -0
  75. package/dist/http/health.d.ts +70 -0
  76. package/dist/http/health.js +60 -0
  77. package/dist/http/index.d.ts +5 -0
  78. package/dist/http/index.js +5 -0
  79. package/dist/identifiers/client-code.d.ts +37 -0
  80. package/dist/identifiers/client-code.js +41 -0
  81. package/dist/identifiers/creditor-reference.d.ts +38 -0
  82. package/dist/identifiers/creditor-reference.js +32 -0
  83. package/dist/identifiers/damm.d.ts +22 -0
  84. package/dist/identifiers/damm.js +50 -0
  85. package/dist/identifiers/document-number.d.ts +68 -0
  86. package/dist/identifiers/document-number.js +73 -0
  87. package/dist/identifiers/document-numbers.d.ts +27 -0
  88. package/dist/identifiers/document-numbers.js +16 -0
  89. package/dist/identifiers/failures.d.ts +46 -0
  90. package/dist/identifiers/failures.js +48 -0
  91. package/dist/identifiers/index.d.ts +14 -0
  92. package/dist/identifiers/index.js +13 -0
  93. package/dist/identifiers/next-document-number.d.ts +26 -0
  94. package/dist/identifiers/next-document-number.js +31 -0
  95. package/dist/identifiers/ulid.d.ts +28 -0
  96. package/dist/identifiers/ulid.js +71 -0
  97. package/dist/integration/external-http-client.d.ts +22 -0
  98. package/dist/integration/external-http-client.js +74 -0
  99. package/dist/integration/index.d.ts +12 -0
  100. package/dist/integration/index.js +12 -0
  101. package/dist/integration/layer.d.ts +19 -0
  102. package/dist/integration/layer.js +30 -0
  103. package/dist/integration/retry.d.ts +65 -0
  104. package/dist/integration/retry.js +54 -0
  105. package/dist/integration/settings.d.ts +73 -0
  106. package/dist/integration/settings.js +92 -0
  107. package/dist/money/amount.d.ts +60 -0
  108. package/dist/money/amount.js +67 -0
  109. package/dist/money/arithmetic.d.ts +60 -0
  110. package/dist/money/arithmetic.js +73 -0
  111. package/dist/money/currency.d.ts +25 -0
  112. package/dist/money/currency.js +24 -0
  113. package/dist/money/decimal.d.ts +29 -0
  114. package/dist/money/decimal.js +39 -0
  115. package/dist/money/exchange-rate.d.ts +32 -0
  116. package/dist/money/exchange-rate.js +28 -0
  117. package/dist/money/failures.d.ts +86 -0
  118. package/dist/money/failures.js +97 -0
  119. package/dist/money/index.d.ts +15 -0
  120. package/dist/money/index.js +15 -0
  121. package/dist/money/percent.d.ts +35 -0
  122. package/dist/money/percent.js +50 -0
  123. package/dist/money/schema.d.ts +25 -0
  124. package/dist/money/schema.js +41 -0
  125. package/dist/ports/blob-store.d.ts +42 -0
  126. package/dist/ports/blob-store.js +13 -0
  127. package/dist/ports/document-number-counter.d.ts +36 -0
  128. package/dist/ports/document-number-counter.js +12 -0
  129. package/dist/ports/event-publisher.d.ts +29 -0
  130. package/dist/ports/event-publisher.js +13 -0
  131. package/dist/ports/external-http-client.d.ts +60 -0
  132. package/dist/ports/external-http-client.js +25 -0
  133. package/dist/ports/index.d.ts +17 -0
  134. package/dist/ports/index.js +17 -0
  135. package/dist/ports/integration-event-outbox.d.ts +44 -0
  136. package/dist/ports/integration-event-outbox.js +13 -0
  137. package/dist/ports/port-unavailable.d.ts +21 -0
  138. package/dist/ports/port-unavailable.js +30 -0
  139. package/dist/ports/processed-event-registry.d.ts +57 -0
  140. package/dist/ports/processed-event-registry.js +24 -0
  141. package/dist/ports/queue-message-producer.d.ts +26 -0
  142. package/dist/ports/queue-message-producer.js +12 -0
  143. package/dist/ports/queue-message-source.d.ts +39 -0
  144. package/dist/ports/queue-message-source.js +13 -0
  145. package/dist/ports/readiness-probe.d.ts +27 -0
  146. package/dist/ports/readiness-probe.js +13 -0
  147. package/dist/ports/transaction-boundary.d.ts +28 -0
  148. package/dist/ports/transaction-boundary.js +13 -0
  149. package/dist/postgres/client.d.ts +51 -0
  150. package/dist/postgres/client.js +81 -0
  151. package/dist/postgres/document-number-counter.d.ts +14 -0
  152. package/dist/postgres/document-number-counter.js +27 -0
  153. package/dist/postgres/failures.d.ts +28 -0
  154. package/dist/postgres/failures.js +18 -0
  155. package/dist/postgres/index.d.ts +18 -0
  156. package/dist/postgres/index.js +18 -0
  157. package/dist/postgres/integration-event-outbox.d.ts +15 -0
  158. package/dist/postgres/integration-event-outbox.js +60 -0
  159. package/dist/postgres/jsonb-value.d.ts +10 -0
  160. package/dist/postgres/jsonb-value.js +9 -0
  161. package/dist/postgres/layer.d.ts +57 -0
  162. package/dist/postgres/layer.js +72 -0
  163. package/dist/postgres/migrations/advisory-lock.d.ts +47 -0
  164. package/dist/postgres/migrations/advisory-lock.js +59 -0
  165. package/dist/postgres/migrations/runner.d.ts +56 -0
  166. package/dist/postgres/migrations/runner.js +69 -0
  167. package/dist/postgres/migrations/sql-file-loader.d.ts +59 -0
  168. package/dist/postgres/migrations/sql-file-loader.js +99 -0
  169. package/dist/postgres/processed-event-registry.d.ts +13 -0
  170. package/dist/postgres/processed-event-registry.js +44 -0
  171. package/dist/postgres/readiness-probe.d.ts +27 -0
  172. package/dist/postgres/readiness-probe.js +33 -0
  173. package/dist/postgres/settings.d.ts +134 -0
  174. package/dist/postgres/settings.js +210 -0
  175. package/dist/postgres/sql/0001_processed_event.sql +4 -0
  176. package/dist/postgres/sql/0002_integration_event_outbox.sql +13 -0
  177. package/dist/postgres/sql/0003_document_number_counter.sql +6 -0
  178. package/dist/postgres/sql-failure.d.ts +19 -0
  179. package/dist/postgres/sql-failure.js +22 -0
  180. package/dist/postgres/store-failure.d.ts +17 -0
  181. package/dist/postgres/store-failure.js +16 -0
  182. package/dist/postgres/transaction-boundary.d.ts +13 -0
  183. package/dist/postgres/transaction-boundary.js +21 -0
  184. package/dist/publishing/event-publisher.sns.d.ts +66 -0
  185. package/dist/publishing/event-publisher.sns.js +75 -0
  186. package/dist/publishing/event-publisher.stdout.d.ts +15 -0
  187. package/dist/publishing/event-publisher.stdout.js +24 -0
  188. package/dist/publishing/index.d.ts +10 -0
  189. package/dist/publishing/index.js +10 -0
  190. package/dist/publishing/layer.d.ts +25 -0
  191. package/dist/publishing/layer.js +39 -0
  192. package/dist/publishing/settings.d.ts +60 -0
  193. package/dist/publishing/settings.js +85 -0
  194. package/dist/queue/consumer.d.ts +91 -0
  195. package/dist/queue/consumer.js +92 -0
  196. package/dist/queue/envelope.d.ts +39 -0
  197. package/dist/queue/envelope.js +39 -0
  198. package/dist/queue/failures.d.ts +33 -0
  199. package/dist/queue/failures.js +15 -0
  200. package/dist/queue/index.d.ts +14 -0
  201. package/dist/queue/index.js +14 -0
  202. package/dist/queue/layer.d.ts +33 -0
  203. package/dist/queue/layer.js +58 -0
  204. package/dist/queue/message-codec.d.ts +43 -0
  205. package/dist/queue/message-codec.js +33 -0
  206. package/dist/queue/queue-message-producer.d.ts +61 -0
  207. package/dist/queue/queue-message-producer.js +52 -0
  208. package/dist/queue/queue-message-source.d.ts +93 -0
  209. package/dist/queue/queue-message-source.js +86 -0
  210. package/dist/queue/readiness.d.ts +78 -0
  211. package/dist/queue/readiness.js +86 -0
  212. package/dist/queue/settings.d.ts +106 -0
  213. package/dist/queue/settings.js +160 -0
  214. package/dist/storage/blob-store.d.ts +84 -0
  215. package/dist/storage/blob-store.js +109 -0
  216. package/dist/storage/index.d.ts +9 -0
  217. package/dist/storage/index.js +9 -0
  218. package/dist/storage/layer.d.ts +17 -0
  219. package/dist/storage/layer.js +30 -0
  220. package/dist/storage/settings.d.ts +48 -0
  221. package/dist/storage/settings.js +65 -0
  222. package/dist/testing/blob-store.d.ts +27 -0
  223. package/dist/testing/blob-store.js +45 -0
  224. package/dist/testing/clock.d.ts +29 -0
  225. package/dist/testing/clock.js +41 -0
  226. package/dist/testing/dna-perturbator/attention.d.ts +55 -0
  227. package/dist/testing/dna-perturbator/attention.js +49 -0
  228. package/dist/testing/dna-perturbator/chains.d.ts +31 -0
  229. package/dist/testing/dna-perturbator/chains.js +53 -0
  230. package/dist/testing/dna-perturbator/report.d.ts +12 -0
  231. package/dist/testing/dna-perturbator/report.js +22 -0
  232. package/dist/testing/dna-perturbator/verdict.d.ts +26 -0
  233. package/dist/testing/dna-perturbator/verdict.js +28 -0
  234. package/dist/testing/dna-perturbator.d.ts +48 -0
  235. package/dist/testing/dna-perturbator.js +145 -0
  236. package/dist/testing/document-number-counter.d.ts +29 -0
  237. package/dist/testing/document-number-counter.js +39 -0
  238. package/dist/testing/event-publisher.d.ts +28 -0
  239. package/dist/testing/event-publisher.js +33 -0
  240. package/dist/testing/external-http-client.d.ts +29 -0
  241. package/dist/testing/external-http-client.js +49 -0
  242. package/dist/testing/index.d.ts +24 -0
  243. package/dist/testing/index.js +24 -0
  244. package/dist/testing/integration-event-outbox.d.ts +37 -0
  245. package/dist/testing/integration-event-outbox.js +67 -0
  246. package/dist/testing/mutation-settings.d.ts +71 -0
  247. package/dist/testing/mutation-settings.js +57 -0
  248. package/dist/testing/postgres-rows.d.ts +12 -0
  249. package/dist/testing/postgres-rows.js +19 -0
  250. package/dist/testing/processed-event-registry.d.ts +25 -0
  251. package/dist/testing/processed-event-registry.js +39 -0
  252. package/dist/testing/queue-message-producer.d.ts +27 -0
  253. package/dist/testing/queue-message-producer.js +41 -0
  254. package/dist/testing/queue-message-source.d.ts +54 -0
  255. package/dist/testing/queue-message-source.js +71 -0
  256. package/dist/testing/quiet-reporter.d.ts +86 -0
  257. package/dist/testing/quiet-reporter.js +81 -0
  258. package/dist/testing/readiness-probe.d.ts +27 -0
  259. package/dist/testing/readiness-probe.js +33 -0
  260. package/dist/testing/scenario.d.ts +94 -0
  261. package/dist/testing/scenario.js +81 -0
  262. package/dist/testing/transaction-boundary.d.ts +30 -0
  263. package/dist/testing/transaction-boundary.js +36 -0
  264. package/dist/testing/typed-failure.d.ts +8 -0
  265. package/dist/testing/typed-failure.js +15 -0
  266. package/dist/time/calendar-day.d.ts +83 -0
  267. package/dist/time/calendar-day.js +112 -0
  268. package/dist/time/failures.d.ts +51 -0
  269. package/dist/time/failures.js +56 -0
  270. package/dist/time/index.d.ts +11 -0
  271. package/dist/time/index.js +11 -0
  272. package/dist/time/instant.d.ts +64 -0
  273. package/dist/time/instant.js +67 -0
  274. package/dist/time/period.d.ts +45 -0
  275. package/dist/time/period.js +55 -0
  276. package/dist/vitest/container-reuse.d.ts +14 -0
  277. package/dist/vitest/container-reuse.js +15 -0
  278. package/dist/vitest/dotenv.d.ts +16 -0
  279. package/dist/vitest/dotenv.js +48 -0
  280. package/dist/vitest/effect-single-instance.d.ts +36 -0
  281. package/dist/vitest/effect-single-instance.js +125 -0
  282. package/dist/vitest/fast-check-single-instance.d.ts +11 -0
  283. package/dist/vitest/fast-check-single-instance.js +26 -0
  284. package/dist/vitest/index.d.ts +31 -0
  285. package/dist/vitest/index.js +30 -0
  286. package/dist/vitest/localstack-container.d.ts +32 -0
  287. package/dist/vitest/localstack-container.js +55 -0
  288. package/dist/vitest/port-contract-tester.d.ts +8 -0
  289. package/dist/vitest/port-contract-tester.js +1 -0
  290. package/dist/vitest/port-contracts/blob-store.contract.d.ts +22 -0
  291. package/dist/vitest/port-contracts/blob-store.contract.js +108 -0
  292. package/dist/vitest/port-contracts/document-number-counter.contract.d.ts +18 -0
  293. package/dist/vitest/port-contracts/document-number-counter.contract.js +90 -0
  294. package/dist/vitest/port-contracts/event-publisher.contract.d.ts +35 -0
  295. package/dist/vitest/port-contracts/event-publisher.contract.js +67 -0
  296. package/dist/vitest/port-contracts/external-http-client.contract.d.ts +35 -0
  297. package/dist/vitest/port-contracts/external-http-client.contract.js +52 -0
  298. package/dist/vitest/port-contracts/integration-event-outbox.contract.d.ts +23 -0
  299. package/dist/vitest/port-contracts/integration-event-outbox.contract.js +75 -0
  300. package/dist/vitest/port-contracts/processed-event-registry.contract.d.ts +18 -0
  301. package/dist/vitest/port-contracts/processed-event-registry.contract.js +84 -0
  302. package/dist/vitest/port-contracts/queue-message-producer.contract.d.ts +26 -0
  303. package/dist/vitest/port-contracts/queue-message-producer.contract.js +48 -0
  304. package/dist/vitest/port-contracts/queue-message-source.contract.d.ts +29 -0
  305. package/dist/vitest/port-contracts/queue-message-source.contract.js +71 -0
  306. package/dist/vitest/port-contracts/readiness-probe.contract.d.ts +18 -0
  307. package/dist/vitest/port-contracts/readiness-probe.contract.js +41 -0
  308. package/dist/vitest/port-contracts/transaction-boundary.contract.d.ts +21 -0
  309. package/dist/vitest/port-contracts/transaction-boundary.contract.js +64 -0
  310. package/dist/vitest/postgres-container.d.ts +57 -0
  311. package/dist/vitest/postgres-container.js +118 -0
  312. package/dist/vitest/reader-port-contract.d.ts +41 -0
  313. package/dist/vitest/reader-port-contract.js +43 -0
  314. package/dist/vitest/repo-source-files.d.ts +37 -0
  315. package/dist/vitest/repo-source-files.js +61 -0
  316. package/dist/vitest/reset-tables.d.ts +18 -0
  317. package/dist/vitest/reset-tables.js +30 -0
  318. package/dist/vitest/settings-contract.d.ts +38 -0
  319. package/dist/vitest/settings-contract.js +77 -0
  320. package/dist/vitest/stub-server.d.ts +43 -0
  321. package/dist/vitest/stub-server.js +60 -0
  322. package/dist/vitest/term-registry.json +730 -0
  323. package/dist/vitest/testclock-fork-isolated-files.d.ts +13 -0
  324. package/dist/vitest/testclock-fork-isolated-files.js +18 -0
  325. package/dist/vitest/testclock-fork-isolation.d.ts +77 -0
  326. package/dist/vitest/testclock-fork-isolation.js +188 -0
  327. package/dist/workflow/claim-once.d.ts +16 -0
  328. package/dist/workflow/claim-once.js +9 -0
  329. package/dist/workflow/failures.d.ts +47 -0
  330. package/dist/workflow/failures.js +22 -0
  331. package/dist/workflow/index.d.ts +12 -0
  332. package/dist/workflow/index.js +12 -0
  333. package/dist/workflow/on-claim.d.ts +17 -0
  334. package/dist/workflow/on-claim.js +8 -0
  335. package/dist/workflow/publish-pending-events.d.ts +31 -0
  336. package/dist/workflow/publish-pending-events.js +31 -0
  337. package/dist/workflow/transactionally.d.ts +8 -0
  338. package/dist/workflow/transactionally.js +8 -0
  339. package/dist/workflow/versioned-payload.d.ts +35 -0
  340. package/dist/workflow/versioned-payload.js +43 -0
  341. package/package.json +309 -0
@@ -0,0 +1,28 @@
1
+ import { EventPublisher, type EventPublisherShape, type OutboxMessage } from '@satorio/machinery/ports';
2
+ import { Effect, Layer, Ref } from 'effect';
3
+ /**
4
+ * What an in-memory publisher holds: every message it was handed, in the
5
+ * order the batches arrived. A scenario keeps the `Ref` and reads this
6
+ * list to see what a procedure published.
7
+ */
8
+ export type PublishedMessages = readonly OutboxMessage[];
9
+ /**
10
+ * An in-memory publisher over a `Ref` you keep, so a test reads what was
11
+ * published after the run.
12
+ */
13
+ export declare const makeInMemoryEventPublisherWith: (published: Ref.Ref<PublishedMessages>) => EventPublisherShape;
14
+ /**
15
+ * An in-memory publisher over a `Ref` of its own, empty at the start.
16
+ */
17
+ export declare const makeInMemoryEventPublisher: Effect.Effect<EventPublisherShape, never, never>;
18
+ /**
19
+ * The in-memory publisher, mounted.
20
+ */
21
+ export declare const InMemoryEventPublisherLive: Layer.Layer<EventPublisher, never, never>;
22
+ /**
23
+ * A publisher that takes nothing: every publish fails with
24
+ * `EventPublisherUnavailableError`. Mount it to walk the branch a
25
+ * working stand-in never reaches — the outbox relay that has to leave
26
+ * its messages where they are.
27
+ */
28
+ export declare const EventPublisherUnavailableLive: Layer.Layer<EventPublisher>;
@@ -0,0 +1,33 @@
1
+ import { EventPublisher, EventPublisherUnavailableError, } from '@satorio/machinery/ports';
2
+ import { Effect, Layer, Ref } from 'effect';
3
+ const NOT_MOUNTED = 'event publisher unavailable (test)';
4
+ /**
5
+ * An in-memory publisher over a `Ref` you keep, so a test reads what was
6
+ * published after the run.
7
+ */
8
+ export const makeInMemoryEventPublisherWith = (published) => ({
9
+ publish: (messages) => Ref.update(published, (current) => [...current, ...messages]),
10
+ });
11
+ /**
12
+ * An in-memory publisher over a `Ref` of its own, empty at the start.
13
+ */
14
+ export const makeInMemoryEventPublisher = Effect.gen(function* () {
15
+ const published = yield* Ref.make([]);
16
+ return makeInMemoryEventPublisherWith(published);
17
+ });
18
+ /**
19
+ * The in-memory publisher, mounted.
20
+ */
21
+ export const InMemoryEventPublisherLive = Layer.effect(EventPublisher, makeInMemoryEventPublisher);
22
+ /**
23
+ * A publisher that takes nothing: every publish fails with
24
+ * `EventPublisherUnavailableError`. Mount it to walk the branch a
25
+ * working stand-in never reaches — the outbox relay that has to leave
26
+ * its messages where they are.
27
+ */
28
+ export const EventPublisherUnavailableLive = Layer.succeed(EventPublisher, {
29
+ publish: () => Effect.fail(new EventPublisherUnavailableError({
30
+ operation: 'publish',
31
+ cause: NOT_MOUNTED,
32
+ })),
33
+ });
@@ -0,0 +1,29 @@
1
+ import { ExternalHttpClient, type ExternalHttpClientShape } from '@satorio/machinery/ports';
2
+ import { Effect, Layer, Ref } from 'effect';
3
+ /**
4
+ * What an in-memory client answers with: one already-decoded body per
5
+ * path. A path nobody wrote an answer for is a 404, the way a real
6
+ * service refuses a URL it does not serve.
7
+ */
8
+ export type AnswersByPath = Readonly<Record<string, unknown>>;
9
+ /**
10
+ * An in-memory client over a `Ref` you keep, so a test rewrites what the
11
+ * service answers between two calls of the same scenario.
12
+ */
13
+ export declare const makeInMemoryExternalHttpClientWith: (answers: Ref.Ref<AnswersByPath>) => ExternalHttpClientShape;
14
+ /**
15
+ * An in-memory client over a `Ref` of its own, seeded with the answers
16
+ * you hand it.
17
+ */
18
+ export declare const makeInMemoryExternalHttpClient: (answers: AnswersByPath) => Effect.Effect<ExternalHttpClientShape>;
19
+ /**
20
+ * The in-memory client, mounted, answering the paths you seeded it with.
21
+ */
22
+ export declare const inMemoryExternalHttpClientLive: (answers: AnswersByPath) => Layer.Layer<ExternalHttpClient>;
23
+ /**
24
+ * A client that reaches nothing: every call fails with
25
+ * `ExternalServiceUnavailableError`. Mount it to walk the branch a
26
+ * working stand-in never reaches — the use case that has to carry on
27
+ * without the reference service it wanted.
28
+ */
29
+ export declare const ExternalHttpClientUnavailableLive: Layer.Layer<ExternalHttpClient>;
@@ -0,0 +1,49 @@
1
+ import { ExternalHttpClient, ExternalServiceRefusedError, ExternalServiceResponseMalformedError, ExternalServiceUnavailableError, } from '@satorio/machinery/ports';
2
+ import { Effect, Layer, Ref, Schema } from 'effect';
3
+ const NOT_MOUNTED = 'external http client unavailable (test)';
4
+ const IN_MEMORY_SERVICE = 'in-memory';
5
+ const GET_JSON = 'getJson';
6
+ const NOT_ANSWERED_STATUS = 404;
7
+ /**
8
+ * An in-memory client over a `Ref` you keep, so a test rewrites what the
9
+ * service answers between two calls of the same scenario.
10
+ */
11
+ export const makeInMemoryExternalHttpClientWith = (answers) => ({
12
+ getJson: (path, schema) => Effect.gen(function* () {
13
+ const answered = yield* Ref.get(answers);
14
+ if (!Object.hasOwn(answered, path)) {
15
+ return yield* Effect.fail(new ExternalServiceRefusedError({
16
+ service: IN_MEMORY_SERVICE,
17
+ operation: GET_JSON,
18
+ status: NOT_ANSWERED_STATUS,
19
+ }));
20
+ }
21
+ return yield* Schema.decodeUnknown(schema)(answered[path]).pipe(Effect.mapError((cause) => new ExternalServiceResponseMalformedError({
22
+ service: IN_MEMORY_SERVICE,
23
+ operation: GET_JSON,
24
+ cause,
25
+ })));
26
+ }),
27
+ });
28
+ /**
29
+ * An in-memory client over a `Ref` of its own, seeded with the answers
30
+ * you hand it.
31
+ */
32
+ export const makeInMemoryExternalHttpClient = (answers) => Effect.map(Ref.make(answers), makeInMemoryExternalHttpClientWith);
33
+ /**
34
+ * The in-memory client, mounted, answering the paths you seeded it with.
35
+ */
36
+ export const inMemoryExternalHttpClientLive = (answers) => Layer.effect(ExternalHttpClient, makeInMemoryExternalHttpClient(answers));
37
+ /**
38
+ * A client that reaches nothing: every call fails with
39
+ * `ExternalServiceUnavailableError`. Mount it to walk the branch a
40
+ * working stand-in never reaches — the use case that has to carry on
41
+ * without the reference service it wanted.
42
+ */
43
+ export const ExternalHttpClientUnavailableLive = Layer.succeed(ExternalHttpClient, {
44
+ getJson: () => Effect.fail(new ExternalServiceUnavailableError({
45
+ service: IN_MEMORY_SERVICE,
46
+ operation: GET_JSON,
47
+ cause: NOT_MOUNTED,
48
+ })),
49
+ });
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Public entry to the testing mechanism: a stand-in for every port, a clock a
3
+ * test drives, and the scenario player. It gives you an in-memory stand-in and
4
+ * a deliberately unavailable one for every port, a clock a test drives, the
5
+ * scenario player, the small readers a test uses to look inside what it just
6
+ * ran, and the settings a test runner and a mutation runner read before either
7
+ * runtime exists. No production file imports any of it.
8
+ */
9
+ export { BlobStoreUnavailableLive, type BlobsByKey, InMemoryBlobStoreLive, makeInMemoryBlobStore, makeInMemoryBlobStoreWith, } from './blob-store.js';
10
+ export { advanceBy, atTheMoment, clockPinnedAt, nowAsDateTime, nowAsIsoString, setNowTo, } from './clock.js';
11
+ export { type DocumentCountsByClientAndMonth, DocumentNumberCounterUnavailableLive, InMemoryDocumentNumberCounterLive, makeInMemoryDocumentNumberCounter, makeInMemoryDocumentNumberCounterWith, } from './document-number-counter.js';
12
+ export { EventPublisherUnavailableLive, InMemoryEventPublisherLive, makeInMemoryEventPublisher, makeInMemoryEventPublisherWith, type PublishedMessages, } from './event-publisher.js';
13
+ export { type AnswersByPath, ExternalHttpClientUnavailableLive, inMemoryExternalHttpClientLive, makeInMemoryExternalHttpClient, makeInMemoryExternalHttpClientWith, } from './external-http-client.js';
14
+ export { type DispatchedMessageIds, InMemoryIntegrationEventOutboxLive, IntegrationEventOutboxUnavailableLive, IntegrationEventOutboxUnreadableLive, makeInMemoryIntegrationEventOutbox, makeInMemoryIntegrationEventOutboxWith, type QueuedOutboxMessages, } from './integration-event-outbox.js';
15
+ export { mutationSettings, SUITES_THAT_START_A_STAND, } from './mutation-settings.js';
16
+ export { distinctMomentsIn, emptiedTables, } from './postgres-rows.js';
17
+ export { InMemoryProcessedEventRegistryLive, makeInMemoryProcessedEventRegistry, makeInMemoryProcessedEventRegistryWith, ProcessedEventRegistryUnavailableLive, type ProcessedEventsById, } from './processed-event-registry.js';
18
+ export { InMemoryQueueMessageProducerLive, makeInMemoryQueueMessageProducer, makeInMemoryQueueMessageProducerWith, QueueMessageProducerUnavailableLive, } from './queue-message-producer.js';
19
+ export { InMemoryQueueMessageSourceLive, makeInMemoryQueueMessageSource, makeInMemoryQueueMessageSourceWith, makeScriptedMessageSource, messageOf, type PendingQueueMessages, QueueMessageSourceUnavailableLive, scriptedQueueMessageSourceLive, } from './queue-message-source.js';
20
+ export { QuietReporter, quietReporters, verboseOutputWanted, } from './quiet-reporter.js';
21
+ export { InMemoryReadinessProbeLive, ReadinessProbeUnavailableLive, } from './readiness-probe.js';
22
+ export { contextOf, ensureStepsAreInOrder, firstPassLabelsOf, labelOf, labelOfFailure, momentsOfSteps, playScenario, replayLabelsOf, type Scenario, type ScenarioPlayback, type ScenarioService, type ScenarioStep, } from './scenario.js';
23
+ export { inMemoryTransactionBoundaryLive, makeInMemoryTransactionBoundary, stateOfRef, type TransactionalState, TransactionBoundaryUnavailableLive, } from './transaction-boundary.js';
24
+ export { typedFailureOf } from './typed-failure.js';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Public entry to the testing mechanism: a stand-in for every port, a clock a
3
+ * test drives, and the scenario player. It gives you an in-memory stand-in and
4
+ * a deliberately unavailable one for every port, a clock a test drives, the
5
+ * scenario player, the small readers a test uses to look inside what it just
6
+ * ran, and the settings a test runner and a mutation runner read before either
7
+ * runtime exists. No production file imports any of it.
8
+ */
9
+ export { BlobStoreUnavailableLive, InMemoryBlobStoreLive, makeInMemoryBlobStore, makeInMemoryBlobStoreWith, } from './blob-store.js';
10
+ export { advanceBy, atTheMoment, clockPinnedAt, nowAsDateTime, nowAsIsoString, setNowTo, } from './clock.js';
11
+ export { DocumentNumberCounterUnavailableLive, InMemoryDocumentNumberCounterLive, makeInMemoryDocumentNumberCounter, makeInMemoryDocumentNumberCounterWith, } from './document-number-counter.js';
12
+ export { EventPublisherUnavailableLive, InMemoryEventPublisherLive, makeInMemoryEventPublisher, makeInMemoryEventPublisherWith, } from './event-publisher.js';
13
+ export { ExternalHttpClientUnavailableLive, inMemoryExternalHttpClientLive, makeInMemoryExternalHttpClient, makeInMemoryExternalHttpClientWith, } from './external-http-client.js';
14
+ export { InMemoryIntegrationEventOutboxLive, IntegrationEventOutboxUnavailableLive, IntegrationEventOutboxUnreadableLive, makeInMemoryIntegrationEventOutbox, makeInMemoryIntegrationEventOutboxWith, } from './integration-event-outbox.js';
15
+ export { mutationSettings, SUITES_THAT_START_A_STAND, } from './mutation-settings.js';
16
+ export { distinctMomentsIn, emptiedTables, } from './postgres-rows.js';
17
+ export { InMemoryProcessedEventRegistryLive, makeInMemoryProcessedEventRegistry, makeInMemoryProcessedEventRegistryWith, ProcessedEventRegistryUnavailableLive, } from './processed-event-registry.js';
18
+ export { InMemoryQueueMessageProducerLive, makeInMemoryQueueMessageProducer, makeInMemoryQueueMessageProducerWith, QueueMessageProducerUnavailableLive, } from './queue-message-producer.js';
19
+ export { InMemoryQueueMessageSourceLive, makeInMemoryQueueMessageSource, makeInMemoryQueueMessageSourceWith, makeScriptedMessageSource, messageOf, QueueMessageSourceUnavailableLive, scriptedQueueMessageSourceLive, } from './queue-message-source.js';
20
+ export { QuietReporter, quietReporters, verboseOutputWanted, } from './quiet-reporter.js';
21
+ export { InMemoryReadinessProbeLive, ReadinessProbeUnavailableLive, } from './readiness-probe.js';
22
+ export { contextOf, ensureStepsAreInOrder, firstPassLabelsOf, labelOf, labelOfFailure, momentsOfSteps, playScenario, replayLabelsOf, } from './scenario.js';
23
+ export { inMemoryTransactionBoundaryLive, makeInMemoryTransactionBoundary, stateOfRef, TransactionBoundaryUnavailableLive, } from './transaction-boundary.js';
24
+ export { typedFailureOf } from './typed-failure.js';
@@ -0,0 +1,37 @@
1
+ import { IntegrationEventOutbox, type IntegrationEventOutboxShape, type OutboxMessage } from '@satorio/machinery/ports';
2
+ import { Effect, Layer, Ref } from 'effect';
3
+ /**
4
+ * What an in-memory outbox holds on the write side: every message
5
+ * enqueued, in the order it arrived.
6
+ */
7
+ export type QueuedOutboxMessages = readonly OutboxMessage[];
8
+ /**
9
+ * What an in-memory outbox holds on the read side: the ids a relay has
10
+ * already marked as sent.
11
+ */
12
+ export type DispatchedMessageIds = ReadonlySet<string>;
13
+ /**
14
+ * An in-memory outbox over two `Ref`s you keep, so a test can seed what
15
+ * is waiting and read back what the relay dispatched.
16
+ */
17
+ export declare const makeInMemoryIntegrationEventOutboxWith: (queued: Ref.Ref<QueuedOutboxMessages>, dispatched: Ref.Ref<DispatchedMessageIds>) => IntegrationEventOutboxShape;
18
+ /**
19
+ * An in-memory outbox over `Ref`s of its own, empty at the start.
20
+ */
21
+ export declare const makeInMemoryIntegrationEventOutbox: Effect.Effect<IntegrationEventOutboxShape, never, never>;
22
+ /**
23
+ * The in-memory outbox, mounted.
24
+ */
25
+ export declare const InMemoryIntegrationEventOutboxLive: Layer.Layer<IntegrationEventOutbox, never, never>;
26
+ /**
27
+ * An outbox that accepts nothing: `enqueue` refuses, so a use case's
28
+ * write and its message go down together. Mount it to prove a caller
29
+ * that records an event keeps the refusal instead of losing it.
30
+ */
31
+ export declare const IntegrationEventOutboxUnavailableLive: Layer.Layer<IntegrationEventOutbox>;
32
+ /**
33
+ * An outbox that takes messages and then refuses to hand them back:
34
+ * `pending` fails while `enqueue` succeeds. Mount it to prove a relay
35
+ * reports a read it could not make instead of reporting an empty beat.
36
+ */
37
+ export declare const IntegrationEventOutboxUnreadableLive: Layer.Layer<IntegrationEventOutbox>;
@@ -0,0 +1,67 @@
1
+ import { IntegrationEventOutbox, IntegrationEventOutboxUnavailableError, } from '@satorio/machinery/ports';
2
+ import { Effect, Layer, Ref } from 'effect';
3
+ const NOT_MOUNTED = 'integration event outbox unavailable (test)';
4
+ /**
5
+ * An in-memory outbox over two `Ref`s you keep, so a test can seed what
6
+ * is waiting and read back what the relay dispatched.
7
+ */
8
+ export const makeInMemoryIntegrationEventOutboxWith = (queued, dispatched) => {
9
+ const enqueue = (messages) => Ref.update(queued, (current) => {
10
+ const knownIds = new Set(current.map((message) => message.messageId));
11
+ const firstAppearance = messages.filter((message) => !knownIds.has(message.messageId));
12
+ return firstAppearance.length === 0
13
+ ? current
14
+ : [...current, ...firstAppearance];
15
+ });
16
+ const pending = (limit) => Effect.gen(function* () {
17
+ const current = yield* Ref.get(queued);
18
+ const alreadySent = yield* Ref.get(dispatched);
19
+ return current
20
+ .filter((message) => !alreadySent.has(message.messageId))
21
+ .slice(0, limit);
22
+ });
23
+ const markDispatched = (messageIds) => Ref.update(dispatched, (current) => new Set([...current, ...messageIds]));
24
+ return {
25
+ enqueue,
26
+ pending,
27
+ markDispatched,
28
+ };
29
+ };
30
+ /**
31
+ * An in-memory outbox over `Ref`s of its own, empty at the start.
32
+ */
33
+ export const makeInMemoryIntegrationEventOutbox = Effect.gen(function* () {
34
+ const queued = yield* Ref.make([]);
35
+ const dispatched = yield* Ref.make(new Set());
36
+ return makeInMemoryIntegrationEventOutboxWith(queued, dispatched);
37
+ });
38
+ /**
39
+ * The in-memory outbox, mounted.
40
+ */
41
+ export const InMemoryIntegrationEventOutboxLive = Layer.effect(IntegrationEventOutbox, makeInMemoryIntegrationEventOutbox);
42
+ /**
43
+ * An outbox that accepts nothing: `enqueue` refuses, so a use case's
44
+ * write and its message go down together. Mount it to prove a caller
45
+ * that records an event keeps the refusal instead of losing it.
46
+ */
47
+ export const IntegrationEventOutboxUnavailableLive = Layer.succeed(IntegrationEventOutbox, {
48
+ enqueue: () => new IntegrationEventOutboxUnavailableError({
49
+ operation: 'enqueue',
50
+ cause: NOT_MOUNTED,
51
+ }),
52
+ pending: () => Effect.succeed([]),
53
+ markDispatched: () => Effect.void,
54
+ });
55
+ /**
56
+ * An outbox that takes messages and then refuses to hand them back:
57
+ * `pending` fails while `enqueue` succeeds. Mount it to prove a relay
58
+ * reports a read it could not make instead of reporting an empty beat.
59
+ */
60
+ export const IntegrationEventOutboxUnreadableLive = Layer.succeed(IntegrationEventOutbox, {
61
+ enqueue: () => Effect.void,
62
+ pending: () => new IntegrationEventOutboxUnavailableError({
63
+ operation: 'pending',
64
+ cause: NOT_MOUNTED,
65
+ }),
66
+ markDispatched: () => Effect.void,
67
+ });
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The suites that start a live stand, by the file name A11 gives them.
3
+ * A mutation run never wants these: every mutant would start its own
4
+ * database or its own AWS stand, and a container that never comes up
5
+ * looks exactly like a mutant that never returns.
6
+ */
7
+ export declare const SUITES_THAT_START_A_STAND: readonly string[];
8
+ /**
9
+ * What a service tells the mutation runner about itself: which files to
10
+ * mutate, which test-runner configuration the mutant runs against, and
11
+ * the two numbers a bigger machine may raise.
12
+ *
13
+ * @internal
14
+ */
15
+ export interface MutationSettingsOptions {
16
+ readonly mutate: readonly string[];
17
+ readonly vitestConfigFile: string;
18
+ readonly tsconfigFile?: string;
19
+ readonly concurrency?: number;
20
+ }
21
+ /**
22
+ * The settings of one mutation run, in the shape the runner's own
23
+ * configuration file takes.
24
+ *
25
+ * @internal
26
+ */
27
+ export interface MutationSettings {
28
+ readonly testRunner: string;
29
+ readonly plugins: readonly string[];
30
+ readonly vitest: {
31
+ readonly configFile: string;
32
+ };
33
+ readonly checkers: readonly string[];
34
+ readonly tsconfigFile: string;
35
+ readonly mutate: readonly string[];
36
+ readonly reporters: readonly string[];
37
+ readonly thresholds: {
38
+ readonly high: number;
39
+ readonly low: number;
40
+ readonly break: number | null;
41
+ };
42
+ readonly concurrency: number;
43
+ readonly timeoutMS: number;
44
+ readonly timeoutFactor: number;
45
+ readonly dryRunTimeoutMinutes: number;
46
+ readonly tempDirName: string;
47
+ readonly cleanTempDir: boolean;
48
+ }
49
+ /**
50
+ * How many mutants run beside each other when nobody says. The runner
51
+ * starts one type checker and one test runner per unit of concurrency,
52
+ * and each type checker holds a whole TypeScript program in memory, so
53
+ * the default here stays well under the core count a machine reports.
54
+ * Raise it deliberately, on a machine you know has the memory.
55
+ *
56
+ * @internal
57
+ */
58
+ export declare const defaultConcurrency: (cores: number) => number;
59
+ /**
60
+ * Everything a mutation run needs, with the waits bounded and the live
61
+ * stands kept out. Hand it the files to mutate and the test-runner
62
+ * configuration the mutants run against; take the rest as it comes.
63
+ *
64
+ * Three of these numbers exist because a run that hangs reports no score
65
+ * at all. `timeoutMS` and `timeoutFactor` bound one mutant, so a mutant
66
+ * that turns a loop endless is killed as a timeout rather than waited
67
+ * on. `dryRunTimeoutMinutes` bounds the run that happens before any
68
+ * mutant, where a suite that needs a stand nobody started would
69
+ * otherwise wait for ever.
70
+ */
71
+ export declare const mutationSettings: (options: MutationSettingsOptions) => MutationSettings;
@@ -0,0 +1,57 @@
1
+ import { availableParallelism } from 'node:os';
2
+ /**
3
+ * The suites that start a live stand, by the file name A11 gives them.
4
+ * A mutation run never wants these: every mutant would start its own
5
+ * database or its own AWS stand, and a container that never comes up
6
+ * looks exactly like a mutant that never returns.
7
+ */
8
+ export const SUITES_THAT_START_A_STAND = [
9
+ '**/*.postgres.test.ts',
10
+ '**/*.localstack.test.ts',
11
+ ];
12
+ const HIGHEST_DEFAULT_CONCURRENCY = 4;
13
+ /**
14
+ * How many mutants run beside each other when nobody says. The runner
15
+ * starts one type checker and one test runner per unit of concurrency,
16
+ * and each type checker holds a whole TypeScript program in memory, so
17
+ * the default here stays well under the core count a machine reports.
18
+ * Raise it deliberately, on a machine you know has the memory.
19
+ *
20
+ * @internal
21
+ */
22
+ export const defaultConcurrency = (cores) => Math.max(1, Math.min(HIGHEST_DEFAULT_CONCURRENCY, cores - 1));
23
+ /**
24
+ * Everything a mutation run needs, with the waits bounded and the live
25
+ * stands kept out. Hand it the files to mutate and the test-runner
26
+ * configuration the mutants run against; take the rest as it comes.
27
+ *
28
+ * Three of these numbers exist because a run that hangs reports no score
29
+ * at all. `timeoutMS` and `timeoutFactor` bound one mutant, so a mutant
30
+ * that turns a loop endless is killed as a timeout rather than waited
31
+ * on. `dryRunTimeoutMinutes` bounds the run that happens before any
32
+ * mutant, where a suite that needs a stand nobody started would
33
+ * otherwise wait for ever.
34
+ */
35
+ export const mutationSettings = (options) => ({
36
+ testRunner: 'vitest',
37
+ plugins: [
38
+ '@stryker-mutator/vitest-runner',
39
+ '@stryker-mutator/typescript-checker',
40
+ ],
41
+ vitest: { configFile: options.vitestConfigFile },
42
+ checkers: ['typescript'],
43
+ tsconfigFile: options.tsconfigFile ?? 'tsconfig.json',
44
+ mutate: [
45
+ ...options.mutate,
46
+ '!**/*.test.ts',
47
+ ...SUITES_THAT_START_A_STAND.map((suite) => `!${suite}`),
48
+ ],
49
+ reporters: ['clear-text', 'progress', 'html', 'json'],
50
+ thresholds: { high: 80, low: 60, break: null },
51
+ concurrency: options.concurrency ?? defaultConcurrency(availableParallelism()),
52
+ timeoutMS: 15_000,
53
+ timeoutFactor: 2,
54
+ dryRunTimeoutMinutes: 5,
55
+ tempDirName: '.stryker-tmp',
56
+ cleanTempDir: true,
57
+ });
@@ -0,0 +1,12 @@
1
+ import { SqlClient } from '@effect/sql';
2
+ import { Effect } from 'effect';
3
+ /**
4
+ * Empties the given tables on the live stand so a scenario starts
5
+ * from zero, the way a freshly reset database does for the seeder.
6
+ */
7
+ export declare const emptiedTables: (tables: readonly string[]) => Effect.Effect<void, never, SqlClient.SqlClient>;
8
+ /**
9
+ * Reads the distinct moments in one timestamp column, as sorted ISO
10
+ * strings in the same format `momentsOfSteps` returns for a scenario.
11
+ */
12
+ export declare const distinctMomentsIn: (table: string, column: string) => Effect.Effect<readonly string[], never, SqlClient.SqlClient>;
@@ -0,0 +1,19 @@
1
+ import { SqlClient } from '@effect/sql';
2
+ import { DateTime, Effect } from 'effect';
3
+ /**
4
+ * Empties the given tables on the live stand so a scenario starts
5
+ * from zero, the way a freshly reset database does for the seeder.
6
+ */
7
+ export const emptiedTables = (tables) => Effect.flatMap(SqlClient.SqlClient, (sql) => sql.unsafe(`TRUNCATE TABLE ${tables.join(', ')}`)).pipe(Effect.asVoid, Effect.orDie);
8
+ /**
9
+ * Reads the distinct moments in one timestamp column, as sorted ISO
10
+ * strings in the same format `momentsOfSteps` returns for a scenario.
11
+ */
12
+ export const distinctMomentsIn = (table, column) => Effect.flatMap(SqlClient.SqlClient, (sql) => sql `
13
+ SELECT DISTINCT ${sql.unsafe(column)} AS moment
14
+ FROM ${sql.unsafe(table)}
15
+ `).pipe(Effect.map((rows) => rows
16
+ .flatMap((row) => row.moment === null
17
+ ? []
18
+ : [DateTime.formatIso(DateTime.unsafeMake(row.moment))])
19
+ .toSorted()), Effect.orDie);
@@ -0,0 +1,25 @@
1
+ import { ProcessedEventRegistry, type ProcessedEventRegistryShape } from '@satorio/machinery/ports';
2
+ import { type DateTime, Effect, Layer, Ref } from 'effect';
3
+ /**
4
+ * What an in-memory registry holds: the moment each event id was first
5
+ * claimed. A scenario seeds this map to say an event was handled before.
6
+ */
7
+ export type ProcessedEventsById = ReadonlyMap<string, DateTime.Utc>;
8
+ /**
9
+ * An in-memory registry over a `Ref` you keep, so a test can seed what
10
+ * was already claimed and read back what the run added.
11
+ */
12
+ export declare const makeInMemoryProcessedEventRegistryWith: (processed: Ref.Ref<ProcessedEventsById>) => ProcessedEventRegistryShape;
13
+ /**
14
+ * An in-memory registry over a `Ref` of its own, empty at the start.
15
+ */
16
+ export declare const makeInMemoryProcessedEventRegistry: Effect.Effect<ProcessedEventRegistryShape, never, never>;
17
+ /**
18
+ * The in-memory registry, mounted.
19
+ */
20
+ export declare const InMemoryProcessedEventRegistryLive: Layer.Layer<ProcessedEventRegistry, never, never>;
21
+ /**
22
+ * A registry that claims nothing: every claim comes back as a refusal.
23
+ * Mount it to walk the branch a working stand-in never reaches.
24
+ */
25
+ export declare const ProcessedEventRegistryUnavailableLive: Layer.Layer<ProcessedEventRegistry>;
@@ -0,0 +1,39 @@
1
+ import { alreadyProcessed, claimAccepted, ProcessedEventRegistry, ProcessedEventRegistryUnavailableError, } from '@satorio/machinery/ports';
2
+ import { Effect, Layer, Ref } from 'effect';
3
+ const NOT_MOUNTED = 'processed event registry unavailable (test)';
4
+ /**
5
+ * An in-memory registry over a `Ref` you keep, so a test can seed what
6
+ * was already claimed and read back what the run added.
7
+ */
8
+ export const makeInMemoryProcessedEventRegistryWith = (processed) => ({
9
+ claim: (input) => Ref.modify(processed, (current) => {
10
+ const seenAt = current.get(input.eventId);
11
+ return seenAt === undefined
12
+ ? [
13
+ claimAccepted,
14
+ new Map(current).set(input.eventId, input.processedAt),
15
+ ]
16
+ : [alreadyProcessed(seenAt), current];
17
+ }),
18
+ });
19
+ /**
20
+ * An in-memory registry over a `Ref` of its own, empty at the start.
21
+ */
22
+ export const makeInMemoryProcessedEventRegistry = Effect.gen(function* () {
23
+ const processed = yield* Ref.make(new Map());
24
+ return makeInMemoryProcessedEventRegistryWith(processed);
25
+ });
26
+ /**
27
+ * The in-memory registry, mounted.
28
+ */
29
+ export const InMemoryProcessedEventRegistryLive = Layer.effect(ProcessedEventRegistry, makeInMemoryProcessedEventRegistry);
30
+ /**
31
+ * A registry that claims nothing: every claim comes back as a refusal.
32
+ * Mount it to walk the branch a working stand-in never reaches.
33
+ */
34
+ export const ProcessedEventRegistryUnavailableLive = Layer.succeed(ProcessedEventRegistry, {
35
+ claim: () => new ProcessedEventRegistryUnavailableError({
36
+ operation: 'claim',
37
+ cause: NOT_MOUNTED,
38
+ }),
39
+ });
@@ -0,0 +1,27 @@
1
+ import { QueueMessageProducer, type QueueMessageProducerShape } from '@satorio/machinery/ports';
2
+ import { Effect, Layer, Ref } from 'effect';
3
+ import { type PendingQueueMessages } from './queue-message-source.js';
4
+ /**
5
+ * An in-memory producer over the same reference an in-memory source reads.
6
+ *
7
+ * An in-memory producer over the same `Ref` an in-memory source reads, so a
8
+ * test sends on one side and receives on the other, the way a real queue pairs
9
+ * the two. Each send gets its own message id, counted from what is already on
10
+ * the queue.
11
+ */
12
+ export declare const makeInMemoryQueueMessageProducerWith: (pending: Ref.Ref<PendingQueueMessages>) => QueueMessageProducerShape;
13
+ /**
14
+ * An in-memory producer over a `Ref` of its own, whose messages nobody
15
+ * receives. Use it where a test only cares that sending succeeded.
16
+ */
17
+ export declare const makeInMemoryQueueMessageProducer: Effect.Effect<QueueMessageProducerShape, never, never>;
18
+ /**
19
+ * The in-memory producer, mounted.
20
+ */
21
+ export declare const InMemoryQueueMessageProducerLive: Layer.Layer<QueueMessageProducer, never, never>;
22
+ /**
23
+ * A producer that answers nothing: every send fails with
24
+ * `QueueMessageProducerUnavailableError`. Mount it to walk the branch a
25
+ * working stand-in never reaches.
26
+ */
27
+ export declare const QueueMessageProducerUnavailableLive: Layer.Layer<QueueMessageProducer>;
@@ -0,0 +1,41 @@
1
+ import { QueueMessageProducer, QueueMessageProducerUnavailableError, } from '@satorio/machinery/ports';
2
+ import { Effect, Layer, Ref } from 'effect';
3
+ import { messageOf } from './queue-message-source.js';
4
+ const NOT_MOUNTED = 'queue unavailable (test)';
5
+ /**
6
+ * An in-memory producer over the same reference an in-memory source reads.
7
+ *
8
+ * An in-memory producer over the same `Ref` an in-memory source reads, so a
9
+ * test sends on one side and receives on the other, the way a real queue pairs
10
+ * the two. Each send gets its own message id, counted from what is already on
11
+ * the queue.
12
+ */
13
+ export const makeInMemoryQueueMessageProducerWith = (pending) => ({
14
+ send: (body) => Ref.update(pending, (current) => [
15
+ ...current,
16
+ messageOf(`message-${current.length + 1}`, body),
17
+ ]),
18
+ });
19
+ /**
20
+ * An in-memory producer over a `Ref` of its own, whose messages nobody
21
+ * receives. Use it where a test only cares that sending succeeded.
22
+ */
23
+ export const makeInMemoryQueueMessageProducer = Effect.gen(function* () {
24
+ const pending = yield* Ref.make([]);
25
+ return makeInMemoryQueueMessageProducerWith(pending);
26
+ });
27
+ /**
28
+ * The in-memory producer, mounted.
29
+ */
30
+ export const InMemoryQueueMessageProducerLive = Layer.effect(QueueMessageProducer, makeInMemoryQueueMessageProducer);
31
+ /**
32
+ * A producer that answers nothing: every send fails with
33
+ * `QueueMessageProducerUnavailableError`. Mount it to walk the branch a
34
+ * working stand-in never reaches.
35
+ */
36
+ export const QueueMessageProducerUnavailableLive = Layer.succeed(QueueMessageProducer, {
37
+ send: () => Effect.fail(new QueueMessageProducerUnavailableError({
38
+ operation: 'send',
39
+ cause: NOT_MOUNTED,
40
+ })),
41
+ });
@@ -0,0 +1,54 @@
1
+ import { QueueMessageSource, type QueueMessageSourceShape, type RawQueueMessage } from '@satorio/machinery/ports';
2
+ import { Effect, Layer, Ref } from 'effect';
3
+ /**
4
+ * What an in-memory queue holds: the messages waiting to be received, in
5
+ * the order they were put there. A scenario keeps the `Ref` and reads it
6
+ * to see what is still on the queue.
7
+ */
8
+ export type PendingQueueMessages = readonly RawQueueMessage[];
9
+ /**
10
+ * One raw message, built the way a queue hands it over: an id, the
11
+ * receipt an acknowledgement quotes, and the body.
12
+ */
13
+ export declare const messageOf: (messageId: string, body: string) => RawQueueMessage;
14
+ /**
15
+ * An in-memory message source over a `Ref` you keep, so a test seeds the
16
+ * queue before the run and reads what is left after it. `receive` hands
17
+ * back everything waiting; `acknowledge` takes one message off for good,
18
+ * as a real queue does when the receipt is deleted.
19
+ */
20
+ export declare const makeInMemoryQueueMessageSourceWith: (pending: Ref.Ref<PendingQueueMessages>) => QueueMessageSourceShape;
21
+ /**
22
+ * An in-memory message source over a `Ref` of its own, empty at the
23
+ * start.
24
+ */
25
+ export declare const makeInMemoryQueueMessageSource: Effect.Effect<QueueMessageSourceShape, never, never>;
26
+ /**
27
+ * The in-memory message source, mounted.
28
+ */
29
+ export declare const InMemoryQueueMessageSourceLive: Layer.Layer<QueueMessageSource, never, never>;
30
+ /**
31
+ * A message source that answers nothing: every operation fails with
32
+ * `QueueMessageSourceUnavailableError`. Mount it to walk the branch a
33
+ * working stand-in never reaches.
34
+ */
35
+ export declare const QueueMessageSourceUnavailableLive: Layer.Layer<QueueMessageSource>;
36
+ /**
37
+ * A queue source scripted ahead of time: the test writes what each
38
+ * `receive` call returns. Once the script runs out, it stays silent.
39
+ *
40
+ * @internal
41
+ */
42
+ export interface ScriptedQueue {
43
+ readonly source: QueueMessageSourceShape;
44
+ readonly acknowledged: Effect.Effect<readonly string[]>;
45
+ }
46
+ /**
47
+ * Builds the scripted source and the reader for what it acknowledged.
48
+ */
49
+ export declare const makeScriptedMessageSource: (script: readonly (readonly RawQueueMessage[])[]) => Effect.Effect<ScriptedQueue>;
50
+ /**
51
+ * The scripted source, mounted: each `receive` answers with the next
52
+ * batch of the script.
53
+ */
54
+ export declare const scriptedQueueMessageSourceLive: (script: readonly (readonly RawQueueMessage[])[]) => Layer.Layer<QueueMessageSource>;