@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,71 @@
1
+ import { QueueMessageSource, QueueMessageSourceUnavailableError, } from '@satorio/machinery/ports';
2
+ import { Effect, Layer, Ref } from 'effect';
3
+ const NOT_MOUNTED = 'queue unavailable (test)';
4
+ /**
5
+ * One raw message, built the way a queue hands it over: an id, the
6
+ * receipt an acknowledgement quotes, and the body.
7
+ */
8
+ export const messageOf = (messageId, body) => ({
9
+ messageId,
10
+ receiptHandle: `receipt-${messageId}`,
11
+ body,
12
+ });
13
+ /**
14
+ * An in-memory message source over a `Ref` you keep, so a test seeds the
15
+ * queue before the run and reads what is left after it. `receive` hands
16
+ * back everything waiting; `acknowledge` takes one message off for good,
17
+ * as a real queue does when the receipt is deleted.
18
+ */
19
+ export const makeInMemoryQueueMessageSourceWith = (pending) => ({
20
+ receive: Ref.get(pending),
21
+ acknowledge: (receiptHandle) => Ref.update(pending, (current) => current.filter((message) => message.receiptHandle !== receiptHandle)),
22
+ });
23
+ /**
24
+ * An in-memory message source over a `Ref` of its own, empty at the
25
+ * start.
26
+ */
27
+ export const makeInMemoryQueueMessageSource = Effect.gen(function* () {
28
+ const pending = yield* Ref.make([]);
29
+ return makeInMemoryQueueMessageSourceWith(pending);
30
+ });
31
+ /**
32
+ * The in-memory message source, mounted.
33
+ */
34
+ export const InMemoryQueueMessageSourceLive = Layer.effect(QueueMessageSource, makeInMemoryQueueMessageSource);
35
+ /**
36
+ * A message source that answers nothing: every operation fails with
37
+ * `QueueMessageSourceUnavailableError`. Mount it to walk the branch a
38
+ * working stand-in never reaches.
39
+ */
40
+ export const QueueMessageSourceUnavailableLive = Layer.succeed(QueueMessageSource, {
41
+ receive: Effect.fail(new QueueMessageSourceUnavailableError({
42
+ operation: 'receive',
43
+ cause: NOT_MOUNTED,
44
+ })),
45
+ acknowledge: () => Effect.fail(new QueueMessageSourceUnavailableError({
46
+ operation: 'acknowledge',
47
+ cause: NOT_MOUNTED,
48
+ })),
49
+ });
50
+ /**
51
+ * Builds the scripted source and the reader for what it acknowledged.
52
+ */
53
+ export const makeScriptedMessageSource = (script) => Effect.gen(function* () {
54
+ const remaining = yield* Ref.make(script);
55
+ const acknowledged = yield* Ref.make([]);
56
+ const source = {
57
+ receive: Ref.modify(remaining, (current) => {
58
+ const [head, ...tail] = current;
59
+ return head === undefined
60
+ ? [Effect.never, current]
61
+ : [Effect.succeed(head), tail];
62
+ }).pipe(Effect.flatten),
63
+ acknowledge: (receiptHandle) => Ref.update(acknowledged, (current) => [...current, receiptHandle]),
64
+ };
65
+ return { source, acknowledged: Ref.get(acknowledged) };
66
+ });
67
+ /**
68
+ * The scripted source, mounted: each `receive` answers with the next
69
+ * batch of the script.
70
+ */
71
+ export const scriptedQueueMessageSourceLive = (script) => Layer.effect(QueueMessageSource, Effect.map(makeScriptedMessageSource(script), (queue) => queue.source));
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The one test case a summary reads: its full name, and the result the
3
+ * runner recorded for it. Declared here rather than imported from the
4
+ * runner, because this file is read while a test-runner configuration is
5
+ * built and the runner's own runtime does not exist yet.
6
+ *
7
+ * @internal
8
+ */
9
+ export interface ReportedTestCase {
10
+ readonly fullName: string;
11
+ readonly result: () => {
12
+ readonly state: string;
13
+ readonly errors?: readonly {
14
+ readonly message?: string;
15
+ }[];
16
+ };
17
+ }
18
+ /**
19
+ * The one test module a summary reads: the file it came from and the
20
+ * test cases inside it.
21
+ *
22
+ * @internal
23
+ */
24
+ export interface ReportedTestModule {
25
+ readonly moduleId: string;
26
+ readonly children: {
27
+ readonly allTests: () => Iterable<ReportedTestCase>;
28
+ };
29
+ }
30
+ /**
31
+ * How the quiet reporter names itself and where it writes. `label` opens
32
+ * the summary line, so a run of several packages says which one spoke.
33
+ *
34
+ * @internal
35
+ */
36
+ export interface QuietReporterOptions {
37
+ readonly label?: string;
38
+ readonly write?: (line: string) => void;
39
+ }
40
+ /**
41
+ * A test-runner reporter that prints one summary line for a whole run:
42
+ * how many files, how many tests, how long it took. A run where nothing
43
+ * failed says nothing else. A run with a failure prints each failed test
44
+ * by its full name with the message the runner recorded, so the reason
45
+ * is in the same output as the count.
46
+ *
47
+ * It forwards whatever a suite printed on purpose. A guard that prints
48
+ * the number of files it read is a floor somebody watches move, and a
49
+ * reporter that swallows it hides the one line that matters. What it
50
+ * drops is the per-test tick list, which is the part that grows with the
51
+ * suite and says nothing a green summary does not.
52
+ */
53
+ export declare class QuietReporter {
54
+ private readonly label;
55
+ private readonly write;
56
+ private startedAt;
57
+ private root;
58
+ constructor(options?: QuietReporterOptions);
59
+ onInit(context: {
60
+ readonly config?: {
61
+ readonly root?: string;
62
+ };
63
+ }): void;
64
+ onTestRunStart(): void;
65
+ onUserConsoleLog(log: {
66
+ readonly content: string;
67
+ }): void;
68
+ onTestRunEnd(testModules: readonly ReportedTestModule[], unhandledErrors?: readonly {
69
+ readonly message?: string;
70
+ }[]): void;
71
+ }
72
+ /**
73
+ * Whether the runner should print everything. A run asks for it with
74
+ * `VITEST_VERBOSE=1`; unset, empty, `0`, `false` and `no` all mean no.
75
+ */
76
+ export declare const verboseOutputWanted: (environment?: Readonly<Record<string, string | undefined>>) => boolean;
77
+ /**
78
+ * The `reporters` a test-runner configuration takes. By default it is
79
+ * the quiet reporter, so a run says how many files and tests passed and
80
+ * nothing else; `VITEST_VERBOSE=1` hands the run back to the runner's
81
+ * own reporter, whole. The array is mutable on purpose: the runner's
82
+ * own configuration type takes no readonly array.
83
+ */
84
+ export declare const quietReporters: (options?: QuietReporterOptions & {
85
+ readonly verbose?: boolean;
86
+ }) => ("default" | QuietReporter)[];
@@ -0,0 +1,81 @@
1
+ const DEFAULT_LABEL = 'tests';
2
+ const secondsOf = (millis) => `${(millis / 1000).toFixed(1)}s`;
3
+ const relativeTo = (root, moduleId) => moduleId.startsWith(root) ? moduleId.slice(root.length + 1) : moduleId;
4
+ /**
5
+ * A test-runner reporter that prints one summary line for a whole run:
6
+ * how many files, how many tests, how long it took. A run where nothing
7
+ * failed says nothing else. A run with a failure prints each failed test
8
+ * by its full name with the message the runner recorded, so the reason
9
+ * is in the same output as the count.
10
+ *
11
+ * It forwards whatever a suite printed on purpose. A guard that prints
12
+ * the number of files it read is a floor somebody watches move, and a
13
+ * reporter that swallows it hides the one line that matters. What it
14
+ * drops is the per-test tick list, which is the part that grows with the
15
+ * suite and says nothing a green summary does not.
16
+ */
17
+ // @effect-diagnostics effect/globalDate:off
18
+ export class QuietReporter {
19
+ label;
20
+ write;
21
+ startedAt = Date.now();
22
+ root = '';
23
+ constructor(options = {}) {
24
+ this.label = options.label ?? DEFAULT_LABEL;
25
+ this.write = options.write ?? ((line) => process.stdout.write(`${line}\n`));
26
+ }
27
+ onInit(context) {
28
+ this.root = context.config?.root ?? '';
29
+ }
30
+ onTestRunStart() {
31
+ this.startedAt = Date.now();
32
+ }
33
+ onUserConsoleLog(log) {
34
+ this.write(log.content.replace(/\n$/, ''));
35
+ }
36
+ onTestRunEnd(testModules, unhandledErrors = []) {
37
+ const failed = [];
38
+ let tests = 0;
39
+ let failures = 0;
40
+ for (const testModule of testModules) {
41
+ for (const testCase of testModule.children.allTests()) {
42
+ tests++;
43
+ const result = testCase.result();
44
+ if (result.state !== 'failed')
45
+ continue;
46
+ failures++;
47
+ failed.push(`${relativeTo(this.root, testModule.moduleId)} > ${testCase.fullName}`);
48
+ for (const error of result.errors ?? []) {
49
+ failed.push(` ${error.message ?? 'no message'}`);
50
+ }
51
+ }
52
+ }
53
+ const outcome = failures === 0 && unhandledErrors.length === 0
54
+ ? 'green'
55
+ : `${failures} failed`;
56
+ this.write(`${this.label}: ${testModules.length} files, ${tests} tests, ` +
57
+ `${secondsOf(Date.now() - this.startedAt)} — ${outcome}`);
58
+ for (const line of failed)
59
+ this.write(line);
60
+ for (const error of unhandledErrors) {
61
+ this.write(` unhandled: ${error.message ?? 'no message'}`);
62
+ }
63
+ }
64
+ }
65
+ // @effect-diagnostics effect/globalDate:error
66
+ const OFF = new Set(['', '0', 'false', 'no']);
67
+ /**
68
+ * Whether the runner should print everything. A run asks for it with
69
+ * `VITEST_VERBOSE=1`; unset, empty, `0`, `false` and `no` all mean no.
70
+ */
71
+ export const verboseOutputWanted = (environment = process.env) => !OFF.has((environment.VITEST_VERBOSE ?? '').toLowerCase());
72
+ /**
73
+ * The `reporters` a test-runner configuration takes. By default it is
74
+ * the quiet reporter, so a run says how many files and tests passed and
75
+ * nothing else; `VITEST_VERBOSE=1` hands the run back to the runner's
76
+ * own reporter, whole. The array is mutable on purpose: the runner's
77
+ * own configuration type takes no readonly array.
78
+ */
79
+ export const quietReporters = (options = {}) => (options.verbose ?? verboseOutputWanted())
80
+ ? ['default']
81
+ : [new QuietReporter(options)];
@@ -0,0 +1,27 @@
1
+ import { ReadinessProbe, type ReadinessProbeShape } from '@satorio/machinery/ports';
2
+ import { Layer } from 'effect';
3
+ /**
4
+ * The reason an unavailable probe reports, so a test asserts on a
5
+ * sentence rather than on the shape of a failure alone.
6
+ *
7
+ * @internal
8
+ */
9
+ export declare const READINESS_REASON_IN_A_TEST = "database did not answer (test)";
10
+ /**
11
+ * A probe that always says the database answered. It is the only thing
12
+ * an in-memory probe can honestly say: there is no database behind it to
13
+ * fall silent.
14
+ *
15
+ * @internal
16
+ */
17
+ export declare const makeInMemoryReadinessProbe: ReadinessProbeShape;
18
+ /**
19
+ * The in-memory probe, mounted.
20
+ */
21
+ export declare const InMemoryReadinessProbeLive: Layer.Layer<ReadinessProbe>;
22
+ /**
23
+ * A probe whose database never answers. Mount it to walk the branch a
24
+ * working stand-in never reaches — the one a readiness endpoint turns
25
+ * into "not ready".
26
+ */
27
+ export declare const ReadinessProbeUnavailableLive: Layer.Layer<ReadinessProbe>;
@@ -0,0 +1,33 @@
1
+ import { DatabaseDidNotAnswerError, ReadinessProbe, } from '@satorio/machinery/ports';
2
+ import { Effect, Layer } from 'effect';
3
+ /**
4
+ * The reason an unavailable probe reports, so a test asserts on a
5
+ * sentence rather than on the shape of a failure alone.
6
+ *
7
+ * @internal
8
+ */
9
+ export const READINESS_REASON_IN_A_TEST = 'database did not answer (test)';
10
+ /**
11
+ * A probe that always says the database answered. It is the only thing
12
+ * an in-memory probe can honestly say: there is no database behind it to
13
+ * fall silent.
14
+ *
15
+ * @internal
16
+ */
17
+ export const makeInMemoryReadinessProbe = {
18
+ verifyDatabaseAnswers: Effect.void,
19
+ };
20
+ /**
21
+ * The in-memory probe, mounted.
22
+ */
23
+ export const InMemoryReadinessProbeLive = Layer.succeed(ReadinessProbe, makeInMemoryReadinessProbe);
24
+ /**
25
+ * A probe whose database never answers. Mount it to walk the branch a
26
+ * working stand-in never reaches — the one a readiness endpoint turns
27
+ * into "not ready".
28
+ */
29
+ export const ReadinessProbeUnavailableLive = Layer.succeed(ReadinessProbe, {
30
+ verifyDatabaseAnswers: new DatabaseDidNotAnswerError({
31
+ reason: READINESS_REASON_IN_A_TEST,
32
+ }),
33
+ });
@@ -0,0 +1,94 @@
1
+ import { Cause, Effect } from 'effect';
2
+ /**
3
+ * One step in a scenario. `at` is the step's technical moment — it becomes
4
+ * a stored row's `processed_at`, `recorded_at`, or `occurred_at`. The
5
+ * business date, such as a movement's day or a loan's day, lives in
6
+ * `command` instead.
7
+ */
8
+ export interface ScenarioStep<Command> {
9
+ readonly at: string;
10
+ readonly what: string;
11
+ readonly command: Command;
12
+ readonly firstPass: string;
13
+ readonly onReplay: string;
14
+ }
15
+ /**
16
+ * A story told as data: the steps a world walks through, and the numbers
17
+ * that world ends on. `context` carries what every step of that scenario
18
+ * shares and no step states — the currency of an account, the account
19
+ * itself, the tenant. A snapshot then reads those from the scenario
20
+ * rather than deriving them from whatever the first row happened to
21
+ * hold. A scenario that shares nothing leaves it out.
22
+ */
23
+ export interface Scenario<Command, Snapshot, Context = never> {
24
+ readonly name: string;
25
+ readonly story: string;
26
+ readonly steps: readonly ScenarioStep<Command>[];
27
+ readonly expectedSnapshot: Snapshot;
28
+ readonly context?: Context;
29
+ }
30
+ /**
31
+ * Reads the cross-cutting data of a scenario that declares some. A
32
+ * scenario whose type promises it and whose data leaves it out dies
33
+ * here, naming itself, rather than handing `undefined` on to a snapshot
34
+ * that then reports a wrong number.
35
+ */
36
+ export declare const contextOf: <Command, Snapshot, Context>(scenario: Scenario<Command, Snapshot, Context>) => Context;
37
+ /**
38
+ * What a service gives the scenario player: how to run a step, and how to
39
+ * snapshot what followed.
40
+ *
41
+ * What a service gives the player: how to run a command and name its result,
42
+ * and how to take a snapshot of the numbers after the run. The failure channel
43
+ * is deliberately wide — a label only NAMES a failure, it never parses it.
44
+ */
45
+ export interface ScenarioService<Command, Snapshot, R, Context = never> {
46
+ readonly perform: (command: Command) => Effect.Effect<string, unknown, R>;
47
+ readonly snapshot: (scenario: Scenario<Command, Snapshot, Context>) => Effect.Effect<Snapshot, never, R>;
48
+ }
49
+ /**
50
+ * What one run of a scenario leaves behind: the label of every step, and the
51
+ * snapshot the run ended on.
52
+ */
53
+ export interface ScenarioPlayback<Snapshot> {
54
+ readonly labels: readonly string[];
55
+ readonly snapshot: Snapshot;
56
+ }
57
+ /**
58
+ * The labels a scenario expects the first time it runs, in order.
59
+ */
60
+ export declare const firstPassLabelsOf: <C, S, X>(scenario: Scenario<C, S, X>) => readonly string[];
61
+ /**
62
+ * The labels a scenario expects when the same steps run a second time, in
63
+ * order.
64
+ */
65
+ export declare const replayLabelsOf: <C, S, X>(scenario: Scenario<C, S, X>) => readonly string[];
66
+ /**
67
+ * Labels a failure, so a scenario reads the same on every run.
68
+ *
69
+ * Labels a failure: a domain failure by its named reason, a storage conflict
70
+ * by its tag, a defect on its own so it stands out from both.
71
+ */
72
+ export declare const labelOfFailure: (cause: Cause.Cause<unknown>) => string;
73
+ /**
74
+ * Runs one step and labels it: what it returned, or the name of the failure it
75
+ * met.
76
+ */
77
+ export declare const labelOf: <R>(program: Effect.Effect<string, unknown, R>) => Effect.Effect<string, never, R>;
78
+ /**
79
+ * Fails loudly when a scenario's steps are not in strictly increasing
80
+ * time order — that is a broken scenario, not a run outcome.
81
+ */
82
+ export declare const ensureStepsAreInOrder: <C, S, X>(scenario: Scenario<C, S, X>) => Effect.Effect<void>;
83
+ /**
84
+ * Plays a scenario: each step runs at its own moment, and the
85
+ * snapshot is taken at the last step's moment. Replaying the same
86
+ * scenario against the same world should produce `onReplay` labels
87
+ * and leave the snapshot unchanged — that is how claim-once holds.
88
+ */
89
+ export declare const playScenario: <C, S, R, X>(scenario: Scenario<C, S, X>, service: ScenarioService<C, S, R, X>) => Effect.Effect<ScenarioPlayback<S>, never, R>;
90
+ /**
91
+ * Returns the moments of the chosen steps as ISO strings, in the
92
+ * format a database round-trip returns them.
93
+ */
94
+ export declare const momentsOfSteps: <C, S, X>(scenario: Scenario<C, S, X>, chosen: (step: ScenarioStep<C>) => boolean) => readonly string[];
@@ -0,0 +1,81 @@
1
+ import { Cause, DateTime, Effect, Exit } from 'effect';
2
+ import { atTheMoment } from './clock.js';
3
+ /**
4
+ * Reads the cross-cutting data of a scenario that declares some. A
5
+ * scenario whose type promises it and whose data leaves it out dies
6
+ * here, naming itself, rather than handing `undefined` on to a snapshot
7
+ * that then reports a wrong number.
8
+ */
9
+ export const contextOf = (scenario) => {
10
+ if (scenario.context === undefined) {
11
+ throw new Error(`Scenario "${scenario.name}" declares cross-cutting data and ` +
12
+ `carries none: give it a context, or drop the type it promises.`);
13
+ }
14
+ return scenario.context;
15
+ };
16
+ /**
17
+ * The labels a scenario expects the first time it runs, in order.
18
+ */
19
+ export const firstPassLabelsOf = (scenario) => scenario.steps.map((step) => step.firstPass);
20
+ /**
21
+ * The labels a scenario expects when the same steps run a second time, in
22
+ * order.
23
+ */
24
+ export const replayLabelsOf = (scenario) => scenario.steps.map((step) => step.onReplay);
25
+ /**
26
+ * Labels a failure, so a scenario reads the same on every run.
27
+ *
28
+ * Labels a failure: a domain failure by its named reason, a storage conflict
29
+ * by its tag, a defect on its own so it stands out from both.
30
+ */
31
+ export const labelOfFailure = (cause) => {
32
+ const failure = Cause.failureOption(cause);
33
+ if (failure._tag === 'None')
34
+ return `DEFECT: ${Cause.pretty(cause)}`;
35
+ const tagged = failure.value;
36
+ return tagged.reason ?? tagged._tag ?? String(failure.value);
37
+ };
38
+ /**
39
+ * Runs one step and labels it: what it returned, or the name of the failure it
40
+ * met.
41
+ */
42
+ export const labelOf = (program) => Effect.exit(program).pipe(Effect.map((exit) => Exit.isSuccess(exit) ? exit.value : labelOfFailure(exit.cause)));
43
+ const millisOf = (isoTimestamp) => DateTime.toEpochMillis(DateTime.unsafeMake(isoTimestamp));
44
+ /**
45
+ * Fails loudly when a scenario's steps are not in strictly increasing
46
+ * time order — that is a broken scenario, not a run outcome.
47
+ */
48
+ export const ensureStepsAreInOrder = (scenario) => Effect.forEach(scenario.steps, (step, index) => {
49
+ const previous = scenario.steps[index - 1];
50
+ return previous !== undefined &&
51
+ millisOf(step.at) <= millisOf(previous.at)
52
+ ? Effect.die(new Error(`Scenario "${scenario.name}": step ${index + 1} ` +
53
+ `(${step.at}) is not later than step ${index} ` +
54
+ `(${previous.at})`))
55
+ : Effect.void;
56
+ }, { discard: true });
57
+ const lastMomentOf = (scenario) => {
58
+ const last = scenario.steps.at(-1);
59
+ return last === undefined
60
+ ? DateTime.formatIso(DateTime.unsafeMake(0))
61
+ : last.at;
62
+ };
63
+ /**
64
+ * Plays a scenario: each step runs at its own moment, and the
65
+ * snapshot is taken at the last step's moment. Replaying the same
66
+ * scenario against the same world should produce `onReplay` labels
67
+ * and leave the snapshot unchanged — that is how claim-once holds.
68
+ */
69
+ export const playScenario = (scenario, service) => Effect.gen(function* () {
70
+ yield* ensureStepsAreInOrder(scenario);
71
+ const labels = yield* Effect.forEach(scenario.steps, (step) => atTheMoment(step.at)(labelOf(service.perform(step.command))));
72
+ const snapshot = yield* atTheMoment(lastMomentOf(scenario))(service.snapshot(scenario));
73
+ return { labels, snapshot };
74
+ });
75
+ /**
76
+ * Returns the moments of the chosen steps as ISO strings, in the
77
+ * format a database round-trip returns them.
78
+ */
79
+ export const momentsOfSteps = (scenario, chosen) => scenario.steps
80
+ .filter(chosen)
81
+ .map((step) => DateTime.formatIso(DateTime.unsafeMake(step.at)));
@@ -0,0 +1,30 @@
1
+ import { TransactionBoundary, type TransactionBoundaryShape } from '@satorio/machinery/ports';
2
+ import { Effect, Layer, Ref } from 'effect';
3
+ /**
4
+ * One participant in an in-memory transaction: it can capture its own
5
+ * state now and hand back the effect that puts it back.
6
+ */
7
+ export interface TransactionalState {
8
+ readonly capture: Effect.Effect<Effect.Effect<void>>;
9
+ }
10
+ /**
11
+ * Makes a `Ref` a participant, so an in-memory boundary can put it back
12
+ * where it was when the work fails.
13
+ */
14
+ export declare const stateOfRef: <A>(ref: Ref.Ref<A>) => TransactionalState;
15
+ /**
16
+ * Builds an in-memory transaction boundary: it captures each
17
+ * participant's state on entry and restores it if the work fails.
18
+ */
19
+ export declare const makeInMemoryTransactionBoundary: (participants: readonly TransactionalState[]) => TransactionBoundaryShape;
20
+ /**
21
+ * The in-memory transaction boundary, mounted over the participants you
22
+ * name.
23
+ */
24
+ export declare const inMemoryTransactionBoundaryLive: (participants: readonly TransactionalState[]) => Layer.Layer<TransactionBoundary>;
25
+ /**
26
+ * A transaction boundary that opens nothing: the work never runs and the
27
+ * caller reads the refusal. Mount it to walk the branch a working
28
+ * stand-in never reaches.
29
+ */
30
+ export declare const TransactionBoundaryUnavailableLive: Layer.Layer<TransactionBoundary>;
@@ -0,0 +1,36 @@
1
+ import { TransactionBoundary, TransactionBoundaryUnavailableError, } from '@satorio/machinery/ports';
2
+ import { Effect, Layer, Ref } from 'effect';
3
+ const NOT_MOUNTED = 'transaction boundary unavailable (test)';
4
+ /**
5
+ * Makes a `Ref` a participant, so an in-memory boundary can put it back
6
+ * where it was when the work fails.
7
+ */
8
+ export const stateOfRef = (ref) => ({
9
+ capture: Ref.get(ref).pipe(Effect.map((valueAtEntry) => Ref.set(ref, valueAtEntry))),
10
+ });
11
+ /**
12
+ * Builds an in-memory transaction boundary: it captures each
13
+ * participant's state on entry and restores it if the work fails.
14
+ */
15
+ export const makeInMemoryTransactionBoundary = (participants) => ({
16
+ inTransaction: (work) => Effect.gen(function* () {
17
+ const restores = yield* Effect.forEach(participants, (state) => state.capture);
18
+ return yield* work.pipe(Effect.tapError(() => Effect.forEach(restores, (restore) => restore, { discard: true })));
19
+ }),
20
+ });
21
+ /**
22
+ * The in-memory transaction boundary, mounted over the participants you
23
+ * name.
24
+ */
25
+ export const inMemoryTransactionBoundaryLive = (participants) => Layer.succeed(TransactionBoundary, makeInMemoryTransactionBoundary(participants));
26
+ /**
27
+ * A transaction boundary that opens nothing: the work never runs and the
28
+ * caller reads the refusal. Mount it to walk the branch a working
29
+ * stand-in never reaches.
30
+ */
31
+ export const TransactionBoundaryUnavailableLive = Layer.succeed(TransactionBoundary, {
32
+ inTransaction: () => new TransactionBoundaryUnavailableError({
33
+ operation: 'inTransaction',
34
+ cause: NOT_MOUNTED,
35
+ }),
36
+ });
@@ -0,0 +1,8 @@
1
+ import { Effect } from 'effect';
2
+ /**
3
+ * Extracts a use case's typed failure (Fail) through `Effect.exit`
4
+ * and `Cause`, rather than by matching an error message string.
5
+ * Fails loudly if the effect succeeded, or if it died or was
6
+ * interrupted instead of producing a typed Fail.
7
+ */
8
+ export declare const typedFailureOf: <A, E>(effect: Effect.Effect<A, E>) => Effect.Effect<E>;
@@ -0,0 +1,15 @@
1
+ import { Cause, Effect, Exit, Option } from 'effect';
2
+ /**
3
+ * Extracts a use case's typed failure (Fail) through `Effect.exit`
4
+ * and `Cause`, rather than by matching an error message string.
5
+ * Fails loudly if the effect succeeded, or if it died or was
6
+ * interrupted instead of producing a typed Fail.
7
+ */
8
+ export const typedFailureOf = (effect) => Effect.exit(effect).pipe(Effect.flatMap((exit) => Exit.isSuccess(exit)
9
+ ? Effect.die(new Error(`expected a typed failure — the use case succeeded ` +
10
+ `instead: ${JSON.stringify(exit.value)}`))
11
+ : Cause.failureOption(exit.cause).pipe(Option.match({
12
+ onNone: () => Effect.die(new Error(`expected a typed failure (Fail) — the Cause ` +
13
+ `is not a Fail: ${Cause.pretty(exit.cause)}`)),
14
+ onSome: Effect.succeed,
15
+ }))));
@@ -0,0 +1,83 @@
1
+ import { Either, Order, Schema } from 'effect';
2
+ import { NotACalendarDayError } from './failures.js';
3
+ declare const calendarDayBrand: unique symbol;
4
+ /**
5
+ * One day of the calendar, with no time and no zone in it. The value
6
+ * carries a brand, so a plain string never passes for a day until it has
7
+ * been read by one of the two readers below.
8
+ */
9
+ export type CalendarDay = string & {
10
+ readonly [calendarDayBrand]: 'CalendarDay';
11
+ };
12
+ /**
13
+ * The day named by parts a real moment already produced. This is the one
14
+ * place a calendar day is built, so a value of that type never comes
15
+ * from anywhere else.
16
+ *
17
+ * @internal
18
+ */
19
+ export declare const dayOfDateParts: (year: number, month: number, day: number) => CalendarDay;
20
+ /**
21
+ * Reads the form a person writes a day in: `19.03.2026`. A text of any
22
+ * other shape, a month outside the twelve, or a day the month does not
23
+ * have all come back as a refusal that names which rule was broken.
24
+ */
25
+ export declare const calendarDayFromHumanForm: (text: string) => Either.Either<CalendarDay, NotACalendarDayError>;
26
+ /**
27
+ * Reads the form a machine writes a day in: `2026-03-19`. It refuses
28
+ * exactly what the human reader refuses, for exactly the same reasons.
29
+ */
30
+ export declare const calendarDayFromIsoForm: (text: string) => Either.Either<CalendarDay, NotACalendarDayError>;
31
+ /**
32
+ * Writes a day the way a person reads it: `19.03.2026`.
33
+ */
34
+ export declare const dayInHumanForm: (day: CalendarDay) => string;
35
+ /**
36
+ * Writes a day the way a machine reads it: `2026-03-19`. This is the
37
+ * one seam between a day and its text, so a change of how a day is held
38
+ * inside touches this function and nothing a consumer wrote.
39
+ */
40
+ export declare const dayInIsoForm: (day: CalendarDay) => string;
41
+ /**
42
+ * Puts two days in calendar order, earliest first.
43
+ */
44
+ export declare const CalendarDayOrder: Order.Order<CalendarDay>;
45
+ /**
46
+ * Counts whole days from the first day to the second. The count runs
47
+ * negative when the second day stands earlier.
48
+ */
49
+ export declare const daysBetween: (earlier: CalendarDay, later: CalendarDay) => number;
50
+ /**
51
+ * Moves a day by whole days. A negative count moves it back.
52
+ */
53
+ export declare const addDays: (day: CalendarDay, days: number) => CalendarDay;
54
+ /**
55
+ * Moves a day by whole months, landing on the last day of a month too short to
56
+ * hold it.
57
+ *
58
+ * Moves a day by whole months, and lands on the last day of the month it
59
+ * arrives in when that month is too short to hold the same number.
60
+ */
61
+ export declare const addMonths: (day: CalendarDay, months: number) => CalendarDay;
62
+ /**
63
+ * The last day of the month the given day falls in. Ask the calendar
64
+ * rather than a table of month lengths: February in a leap year answers
65
+ * correctly here because the runtime's own calendar answers correctly.
66
+ */
67
+ export declare const lastDayOfTheMonthOf: (day: CalendarDay) => CalendarDay;
68
+ /**
69
+ * The first day of the month the given day falls in. Every month starts
70
+ * on its first, so this one needs no calendar; it stays here beside its
71
+ * twin, and `monthOf` is the name a consumer reaches for.
72
+ *
73
+ * @internal
74
+ */
75
+ export declare const firstDayOfTheMonthOf: (day: CalendarDay) => CalendarDay;
76
+ /**
77
+ * Reads a day off the wire from the form a person writes, and writes it
78
+ * back in that same form. A text this schema refuses carries the whole
79
+ * three-part refusal into the parse error, so the reader of a failed
80
+ * command sees what to do next rather than a bare type name.
81
+ */
82
+ export declare const CalendarDayFromHumanString: Schema.Schema<CalendarDay, string>;
83
+ export {};