@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.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dictionary/all.json +4656 -0
- package/dictionary/all.toon +639 -0
- package/dictionary/core.json +221 -0
- package/dictionary/core.toon +33 -0
- package/dictionary/graphql.json +212 -0
- package/dictionary/graphql.toon +32 -0
- package/dictionary/http.json +113 -0
- package/dictionary/http.toon +21 -0
- package/dictionary/identifiers.json +266 -0
- package/dictionary/identifiers.toon +38 -0
- package/dictionary/index.json +96 -0
- package/dictionary/index.toon +18 -0
- package/dictionary/integration.json +131 -0
- package/dictionary/integration.toon +23 -0
- package/dictionary/money.json +383 -0
- package/dictionary/money.toon +51 -0
- package/dictionary/ports.json +392 -0
- package/dictionary/ports.toon +52 -0
- package/dictionary/postgres.json +473 -0
- package/dictionary/postgres.toon +61 -0
- package/dictionary/publishing.json +131 -0
- package/dictionary/publishing.toon +23 -0
- package/dictionary/queue.json +365 -0
- package/dictionary/queue.toon +49 -0
- package/dictionary/reference/api.mdx +843 -0
- package/dictionary/reference/uk/api.mdx +844 -0
- package/dictionary/storage.json +194 -0
- package/dictionary/storage.toon +30 -0
- package/dictionary/testing.json +707 -0
- package/dictionary/testing.toon +87 -0
- package/dictionary/time.json +311 -0
- package/dictionary/time.toon +43 -0
- package/dictionary/vitest.json +356 -0
- package/dictionary/vitest.toon +48 -0
- package/dictionary/workflow.json +185 -0
- package/dictionary/workflow.toon +29 -0
- package/dist/bin/command.d.ts +45 -0
- package/dist/bin/command.js +113 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +11 -0
- package/dist/core/bigint-json.d.ts +6 -0
- package/dist/core/bigint-json.js +6 -0
- package/dist/core/closed-set.d.ts +14 -0
- package/dist/core/closed-set.js +8 -0
- package/dist/core/domain-error.d.ts +8 -0
- package/dist/core/domain-error.js +11 -0
- package/dist/core/environment.d.ts +124 -0
- package/dist/core/environment.js +115 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.js +13 -0
- package/dist/core/ulid.d.ts +27 -0
- package/dist/core/ulid.js +27 -0
- package/dist/graphql/builder.d.ts +36 -0
- package/dist/graphql/builder.js +35 -0
- package/dist/graphql/context.d.ts +36 -0
- package/dist/graphql/context.js +69 -0
- package/dist/graphql/failure-classification.d.ts +11 -0
- package/dist/graphql/failure-classification.js +1 -0
- package/dist/graphql/failures.d.ts +62 -0
- package/dist/graphql/failures.js +30 -0
- package/dist/graphql/graphiql.d.ts +45 -0
- package/dist/graphql/graphiql.js +56 -0
- package/dist/graphql/index.d.ts +15 -0
- package/dist/graphql/index.js +14 -0
- package/dist/graphql/layer.d.ts +15 -0
- package/dist/graphql/layer.js +23 -0
- package/dist/graphql/request-handler.d.ts +26 -0
- package/dist/graphql/request-handler.js +33 -0
- package/dist/graphql/server.d.ts +19 -0
- package/dist/graphql/server.js +16 -0
- package/dist/graphql/settings.d.ts +55 -0
- package/dist/graphql/settings.js +82 -0
- package/dist/http/health.d.ts +70 -0
- package/dist/http/health.js +60 -0
- package/dist/http/index.d.ts +5 -0
- package/dist/http/index.js +5 -0
- package/dist/identifiers/client-code.d.ts +37 -0
- package/dist/identifiers/client-code.js +41 -0
- package/dist/identifiers/creditor-reference.d.ts +38 -0
- package/dist/identifiers/creditor-reference.js +32 -0
- package/dist/identifiers/damm.d.ts +22 -0
- package/dist/identifiers/damm.js +50 -0
- package/dist/identifiers/document-number.d.ts +68 -0
- package/dist/identifiers/document-number.js +73 -0
- package/dist/identifiers/document-numbers.d.ts +27 -0
- package/dist/identifiers/document-numbers.js +16 -0
- package/dist/identifiers/failures.d.ts +46 -0
- package/dist/identifiers/failures.js +48 -0
- package/dist/identifiers/index.d.ts +14 -0
- package/dist/identifiers/index.js +13 -0
- package/dist/identifiers/next-document-number.d.ts +26 -0
- package/dist/identifiers/next-document-number.js +31 -0
- package/dist/identifiers/ulid.d.ts +28 -0
- package/dist/identifiers/ulid.js +71 -0
- package/dist/integration/external-http-client.d.ts +22 -0
- package/dist/integration/external-http-client.js +74 -0
- package/dist/integration/index.d.ts +12 -0
- package/dist/integration/index.js +12 -0
- package/dist/integration/layer.d.ts +19 -0
- package/dist/integration/layer.js +30 -0
- package/dist/integration/retry.d.ts +65 -0
- package/dist/integration/retry.js +54 -0
- package/dist/integration/settings.d.ts +73 -0
- package/dist/integration/settings.js +92 -0
- package/dist/money/amount.d.ts +60 -0
- package/dist/money/amount.js +67 -0
- package/dist/money/arithmetic.d.ts +60 -0
- package/dist/money/arithmetic.js +73 -0
- package/dist/money/currency.d.ts +25 -0
- package/dist/money/currency.js +24 -0
- package/dist/money/decimal.d.ts +29 -0
- package/dist/money/decimal.js +39 -0
- package/dist/money/exchange-rate.d.ts +32 -0
- package/dist/money/exchange-rate.js +28 -0
- package/dist/money/failures.d.ts +86 -0
- package/dist/money/failures.js +97 -0
- package/dist/money/index.d.ts +15 -0
- package/dist/money/index.js +15 -0
- package/dist/money/percent.d.ts +35 -0
- package/dist/money/percent.js +50 -0
- package/dist/money/schema.d.ts +25 -0
- package/dist/money/schema.js +41 -0
- package/dist/ports/blob-store.d.ts +42 -0
- package/dist/ports/blob-store.js +13 -0
- package/dist/ports/document-number-counter.d.ts +36 -0
- package/dist/ports/document-number-counter.js +12 -0
- package/dist/ports/event-publisher.d.ts +29 -0
- package/dist/ports/event-publisher.js +13 -0
- package/dist/ports/external-http-client.d.ts +60 -0
- package/dist/ports/external-http-client.js +25 -0
- package/dist/ports/index.d.ts +17 -0
- package/dist/ports/index.js +17 -0
- package/dist/ports/integration-event-outbox.d.ts +44 -0
- package/dist/ports/integration-event-outbox.js +13 -0
- package/dist/ports/port-unavailable.d.ts +21 -0
- package/dist/ports/port-unavailable.js +30 -0
- package/dist/ports/processed-event-registry.d.ts +57 -0
- package/dist/ports/processed-event-registry.js +24 -0
- package/dist/ports/queue-message-producer.d.ts +26 -0
- package/dist/ports/queue-message-producer.js +12 -0
- package/dist/ports/queue-message-source.d.ts +39 -0
- package/dist/ports/queue-message-source.js +13 -0
- package/dist/ports/readiness-probe.d.ts +27 -0
- package/dist/ports/readiness-probe.js +13 -0
- package/dist/ports/transaction-boundary.d.ts +28 -0
- package/dist/ports/transaction-boundary.js +13 -0
- package/dist/postgres/client.d.ts +51 -0
- package/dist/postgres/client.js +81 -0
- package/dist/postgres/document-number-counter.d.ts +14 -0
- package/dist/postgres/document-number-counter.js +27 -0
- package/dist/postgres/failures.d.ts +28 -0
- package/dist/postgres/failures.js +18 -0
- package/dist/postgres/index.d.ts +18 -0
- package/dist/postgres/index.js +18 -0
- package/dist/postgres/integration-event-outbox.d.ts +15 -0
- package/dist/postgres/integration-event-outbox.js +60 -0
- package/dist/postgres/jsonb-value.d.ts +10 -0
- package/dist/postgres/jsonb-value.js +9 -0
- package/dist/postgres/layer.d.ts +57 -0
- package/dist/postgres/layer.js +72 -0
- package/dist/postgres/migrations/advisory-lock.d.ts +47 -0
- package/dist/postgres/migrations/advisory-lock.js +59 -0
- package/dist/postgres/migrations/runner.d.ts +56 -0
- package/dist/postgres/migrations/runner.js +69 -0
- package/dist/postgres/migrations/sql-file-loader.d.ts +59 -0
- package/dist/postgres/migrations/sql-file-loader.js +99 -0
- package/dist/postgres/processed-event-registry.d.ts +13 -0
- package/dist/postgres/processed-event-registry.js +44 -0
- package/dist/postgres/readiness-probe.d.ts +27 -0
- package/dist/postgres/readiness-probe.js +33 -0
- package/dist/postgres/settings.d.ts +134 -0
- package/dist/postgres/settings.js +210 -0
- package/dist/postgres/sql/0001_processed_event.sql +4 -0
- package/dist/postgres/sql/0002_integration_event_outbox.sql +13 -0
- package/dist/postgres/sql/0003_document_number_counter.sql +6 -0
- package/dist/postgres/sql-failure.d.ts +19 -0
- package/dist/postgres/sql-failure.js +22 -0
- package/dist/postgres/store-failure.d.ts +17 -0
- package/dist/postgres/store-failure.js +16 -0
- package/dist/postgres/transaction-boundary.d.ts +13 -0
- package/dist/postgres/transaction-boundary.js +21 -0
- package/dist/publishing/event-publisher.sns.d.ts +66 -0
- package/dist/publishing/event-publisher.sns.js +75 -0
- package/dist/publishing/event-publisher.stdout.d.ts +15 -0
- package/dist/publishing/event-publisher.stdout.js +24 -0
- package/dist/publishing/index.d.ts +10 -0
- package/dist/publishing/index.js +10 -0
- package/dist/publishing/layer.d.ts +25 -0
- package/dist/publishing/layer.js +39 -0
- package/dist/publishing/settings.d.ts +60 -0
- package/dist/publishing/settings.js +85 -0
- package/dist/queue/consumer.d.ts +91 -0
- package/dist/queue/consumer.js +92 -0
- package/dist/queue/envelope.d.ts +39 -0
- package/dist/queue/envelope.js +39 -0
- package/dist/queue/failures.d.ts +33 -0
- package/dist/queue/failures.js +15 -0
- package/dist/queue/index.d.ts +14 -0
- package/dist/queue/index.js +14 -0
- package/dist/queue/layer.d.ts +33 -0
- package/dist/queue/layer.js +58 -0
- package/dist/queue/message-codec.d.ts +43 -0
- package/dist/queue/message-codec.js +33 -0
- package/dist/queue/queue-message-producer.d.ts +61 -0
- package/dist/queue/queue-message-producer.js +52 -0
- package/dist/queue/queue-message-source.d.ts +93 -0
- package/dist/queue/queue-message-source.js +86 -0
- package/dist/queue/readiness.d.ts +78 -0
- package/dist/queue/readiness.js +86 -0
- package/dist/queue/settings.d.ts +106 -0
- package/dist/queue/settings.js +160 -0
- package/dist/storage/blob-store.d.ts +84 -0
- package/dist/storage/blob-store.js +109 -0
- package/dist/storage/index.d.ts +9 -0
- package/dist/storage/index.js +9 -0
- package/dist/storage/layer.d.ts +17 -0
- package/dist/storage/layer.js +30 -0
- package/dist/storage/settings.d.ts +48 -0
- package/dist/storage/settings.js +65 -0
- package/dist/testing/blob-store.d.ts +27 -0
- package/dist/testing/blob-store.js +45 -0
- package/dist/testing/clock.d.ts +29 -0
- package/dist/testing/clock.js +41 -0
- package/dist/testing/dna-perturbator/attention.d.ts +55 -0
- package/dist/testing/dna-perturbator/attention.js +49 -0
- package/dist/testing/dna-perturbator/chains.d.ts +31 -0
- package/dist/testing/dna-perturbator/chains.js +53 -0
- package/dist/testing/dna-perturbator/report.d.ts +12 -0
- package/dist/testing/dna-perturbator/report.js +22 -0
- package/dist/testing/dna-perturbator/verdict.d.ts +26 -0
- package/dist/testing/dna-perturbator/verdict.js +28 -0
- package/dist/testing/dna-perturbator.d.ts +48 -0
- package/dist/testing/dna-perturbator.js +145 -0
- package/dist/testing/document-number-counter.d.ts +29 -0
- package/dist/testing/document-number-counter.js +39 -0
- package/dist/testing/event-publisher.d.ts +28 -0
- package/dist/testing/event-publisher.js +33 -0
- package/dist/testing/external-http-client.d.ts +29 -0
- package/dist/testing/external-http-client.js +49 -0
- package/dist/testing/index.d.ts +24 -0
- package/dist/testing/index.js +24 -0
- package/dist/testing/integration-event-outbox.d.ts +37 -0
- package/dist/testing/integration-event-outbox.js +67 -0
- package/dist/testing/mutation-settings.d.ts +71 -0
- package/dist/testing/mutation-settings.js +57 -0
- package/dist/testing/postgres-rows.d.ts +12 -0
- package/dist/testing/postgres-rows.js +19 -0
- package/dist/testing/processed-event-registry.d.ts +25 -0
- package/dist/testing/processed-event-registry.js +39 -0
- package/dist/testing/queue-message-producer.d.ts +27 -0
- package/dist/testing/queue-message-producer.js +41 -0
- package/dist/testing/queue-message-source.d.ts +54 -0
- package/dist/testing/queue-message-source.js +71 -0
- package/dist/testing/quiet-reporter.d.ts +86 -0
- package/dist/testing/quiet-reporter.js +81 -0
- package/dist/testing/readiness-probe.d.ts +27 -0
- package/dist/testing/readiness-probe.js +33 -0
- package/dist/testing/scenario.d.ts +94 -0
- package/dist/testing/scenario.js +81 -0
- package/dist/testing/transaction-boundary.d.ts +30 -0
- package/dist/testing/transaction-boundary.js +36 -0
- package/dist/testing/typed-failure.d.ts +8 -0
- package/dist/testing/typed-failure.js +15 -0
- package/dist/time/calendar-day.d.ts +83 -0
- package/dist/time/calendar-day.js +112 -0
- package/dist/time/failures.d.ts +51 -0
- package/dist/time/failures.js +56 -0
- package/dist/time/index.d.ts +11 -0
- package/dist/time/index.js +11 -0
- package/dist/time/instant.d.ts +64 -0
- package/dist/time/instant.js +67 -0
- package/dist/time/period.d.ts +45 -0
- package/dist/time/period.js +55 -0
- package/dist/vitest/container-reuse.d.ts +14 -0
- package/dist/vitest/container-reuse.js +15 -0
- package/dist/vitest/dotenv.d.ts +16 -0
- package/dist/vitest/dotenv.js +48 -0
- package/dist/vitest/effect-single-instance.d.ts +36 -0
- package/dist/vitest/effect-single-instance.js +125 -0
- package/dist/vitest/fast-check-single-instance.d.ts +11 -0
- package/dist/vitest/fast-check-single-instance.js +26 -0
- package/dist/vitest/index.d.ts +31 -0
- package/dist/vitest/index.js +30 -0
- package/dist/vitest/localstack-container.d.ts +32 -0
- package/dist/vitest/localstack-container.js +55 -0
- package/dist/vitest/port-contract-tester.d.ts +8 -0
- package/dist/vitest/port-contract-tester.js +1 -0
- package/dist/vitest/port-contracts/blob-store.contract.d.ts +22 -0
- package/dist/vitest/port-contracts/blob-store.contract.js +108 -0
- package/dist/vitest/port-contracts/document-number-counter.contract.d.ts +18 -0
- package/dist/vitest/port-contracts/document-number-counter.contract.js +90 -0
- package/dist/vitest/port-contracts/event-publisher.contract.d.ts +35 -0
- package/dist/vitest/port-contracts/event-publisher.contract.js +67 -0
- package/dist/vitest/port-contracts/external-http-client.contract.d.ts +35 -0
- package/dist/vitest/port-contracts/external-http-client.contract.js +52 -0
- package/dist/vitest/port-contracts/integration-event-outbox.contract.d.ts +23 -0
- package/dist/vitest/port-contracts/integration-event-outbox.contract.js +75 -0
- package/dist/vitest/port-contracts/processed-event-registry.contract.d.ts +18 -0
- package/dist/vitest/port-contracts/processed-event-registry.contract.js +84 -0
- package/dist/vitest/port-contracts/queue-message-producer.contract.d.ts +26 -0
- package/dist/vitest/port-contracts/queue-message-producer.contract.js +48 -0
- package/dist/vitest/port-contracts/queue-message-source.contract.d.ts +29 -0
- package/dist/vitest/port-contracts/queue-message-source.contract.js +71 -0
- package/dist/vitest/port-contracts/readiness-probe.contract.d.ts +18 -0
- package/dist/vitest/port-contracts/readiness-probe.contract.js +41 -0
- package/dist/vitest/port-contracts/transaction-boundary.contract.d.ts +21 -0
- package/dist/vitest/port-contracts/transaction-boundary.contract.js +64 -0
- package/dist/vitest/postgres-container.d.ts +57 -0
- package/dist/vitest/postgres-container.js +118 -0
- package/dist/vitest/reader-port-contract.d.ts +41 -0
- package/dist/vitest/reader-port-contract.js +43 -0
- package/dist/vitest/repo-source-files.d.ts +37 -0
- package/dist/vitest/repo-source-files.js +61 -0
- package/dist/vitest/reset-tables.d.ts +18 -0
- package/dist/vitest/reset-tables.js +30 -0
- package/dist/vitest/settings-contract.d.ts +38 -0
- package/dist/vitest/settings-contract.js +77 -0
- package/dist/vitest/stub-server.d.ts +43 -0
- package/dist/vitest/stub-server.js +60 -0
- package/dist/vitest/term-registry.json +730 -0
- package/dist/vitest/testclock-fork-isolated-files.d.ts +13 -0
- package/dist/vitest/testclock-fork-isolated-files.js +18 -0
- package/dist/vitest/testclock-fork-isolation.d.ts +77 -0
- package/dist/vitest/testclock-fork-isolation.js +188 -0
- package/dist/workflow/claim-once.d.ts +16 -0
- package/dist/workflow/claim-once.js +9 -0
- package/dist/workflow/failures.d.ts +47 -0
- package/dist/workflow/failures.js +22 -0
- package/dist/workflow/index.d.ts +12 -0
- package/dist/workflow/index.js +12 -0
- package/dist/workflow/on-claim.d.ts +17 -0
- package/dist/workflow/on-claim.js +8 -0
- package/dist/workflow/publish-pending-events.d.ts +31 -0
- package/dist/workflow/publish-pending-events.js +31 -0
- package/dist/workflow/transactionally.d.ts +8 -0
- package/dist/workflow/transactionally.js +8 -0
- package/dist/workflow/versioned-payload.d.ts +35 -0
- package/dist/workflow/versioned-payload.js +43 -0
- package/package.json +309 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
package: @satorio/machinery
|
|
2
|
+
version: 0.2.1
|
|
3
|
+
mechanism:
|
|
4
|
+
mechanism: workflow
|
|
5
|
+
import: @satorio/machinery/workflow
|
|
6
|
+
kind: foundation
|
|
7
|
+
page: mechanisms/workflow
|
|
8
|
+
what: "Public entry to the workflow mechanism: the combinators a use case composes its own beat from."
|
|
9
|
+
whatUk: "Публічний вхід до механізму workflow: комбінатори, з яких процедура складає власний такт."
|
|
10
|
+
rows[19]{name,kind,signature,fallback,caption,captionUk,anchor}:
|
|
11
|
+
ClaimOnce,value,type,"","The exactly-once claim: shared ground for every use case that takes an event or command once.","Резервація рівно один раз: спільний ґрунт для кожної процедури, яка бере подію або команду один раз.",workflow-ClaimOnce-value
|
|
12
|
+
DEFAULT_PUBLISH_PENDING_EVENTS_SETTINGS,value,PublishPendingEventsSettings,"",The pace the relay keeps when a service names none of its own.,"Темп, який тримає трансмітер вихідних подій, коли сервіс не називає власного.",workflow-DEFAULT-PUBLISH-PENDING-EVENTS-SETTINGS-value
|
|
13
|
+
OnClaimMatchers,value,interface,"","The two branches `onClaim` takes: what to do when the event was already claimed, and what to do when this run claims it.","Дві гілки, які бере `onClaim`: що робити, коли подію вже зарезервовано, і що робити, коли її резервує цей прогін.",workflow-OnClaimMatchers-value
|
|
14
|
+
PublishPendingEventsSettings,value,interface,"","How the relay paces itself: how many messages one beat takes, and how long it waits between beats.","Як трансмітер вихідних подій задає собі темп: скільки повідомлень бере один такт і скільки він чекає між тактами.",workflow-PublishPendingEventsSettings-value
|
|
15
|
+
UpcastStep,value,interface,"","One lift, from the version it names to the next one up.","Один підйом, від названої версії до наступної вгору.",workflow-UpcastStep-value
|
|
16
|
+
VersionedEventDefinition,value,interface,"",An event stored yesterday is read by today's code.,"Подію, збережену вчора, читає сьогоднішній код.",workflow-VersionedEventDefinition-value
|
|
17
|
+
claimAsProcessedAt,procedure,"(idempotencyKey: string, processedAt: DateTime.Utc) => ClaimOnce","","Claims with a processedAt supplied by the caller, for a handler that has already read the clock and must store that exact moment.","Резервує з `processedAt`, поданим викликачем, — для обробника, який уже прочитав годинник і мусить зберегти саме ту мить.",workflow-claimAsProcessedAt-procedure
|
|
18
|
+
claimAsProcessedNow,procedure,"(idempotencyKey: string) => ClaimOnce","","Claims with the current time, for a caller that only needs the claim.","Резервує з поточним часом — для викликача, якому потрібна лише резервація.",workflow-claimAsProcessedNow-procedure
|
|
19
|
+
decodeStoredPayload,procedure,"<S extends Schema.Schema.AnyNoContext>(definition: VersionedEventDefinition<S>, storedVersion: number, payload: unknown) => Effect.Effect<Schema.Schema.Type<S>, VersionedPayloadFailure | ParseResult.ParseError>","",Lifts a stored payload to the current version and decodes it with the current schema.,Підіймає збережений payload до поточної версії і декодує його поточною схемою.,workflow-decodeStoredPayload-procedure
|
|
20
|
+
keepPublishingPendingEvents,procedure,"(settings: PublishPendingEventsSettings = DEFAULT_PUBLISH_PENDING_EVENTS_SETTINGS) => Effect.Effect<never, never, IntegrationEventOutbox | EventPublisher>","",Runs the outbox relay for as long as the service lives.,"Крутить трансмітер вихідних подій стільки, скільки живе сервіс.",workflow-keepPublishingPendingEvents-procedure
|
|
21
|
+
onClaim,procedure,"<A1, E1, R1, A2, E2, R2, EClaim, RClaim>(claim: Effect.Effect<ProcessedEventClaim, EClaim, RClaim>, matchers: OnClaimMatchers<A1, E1, R1, A2, E2, R2>) => Effect.Effect<A1 | A2, EClaim | E1 | E2, RClaim | R1 | R2>","","Branches on the state of the exactly-once claim: AlreadyProcessed goes one way, ClaimAccepted the other.","Розгалужується на стані резервації: `AlreadyProcessed` іде одним шляхом, `ClaimAccepted` — іншим.",workflow-onClaim-procedure
|
|
22
|
+
publishOneBatch,procedure,"(batchLimit: number) => Effect.Effect<number, PublishPendingEventsFailure, IntegrationEventOutbox | EventPublisher>","","One outbox relay beat: take a batch of undelivered messages, hand it to the publisher, then mark it dispatched.","Один такт трансмітера вихідних подій: узяти пачку недоставлених повідомлень, віддати її публікаторові, тоді позначити її відправленою.",workflow-publishOneBatch-procedure
|
|
23
|
+
transactionally,procedure,"<A, E, R>(work: Effect.Effect<A, E, R>)","","Wraps work in a transaction boundary: pulls TransactionBoundary from context and runs the work through it.","Загортає роботу в межу транзакції: дістає `TransactionBoundary` з контексту і проганяє роботу крізь неї.",workflow-transactionally-procedure
|
|
24
|
+
upcastToCurrentVersion,procedure,"<S extends Schema.Schema.AnyNoContext>(definition: VersionedEventDefinition<S>, storedVersion: number, payload: unknown) => Effect.Effect<unknown, VersionedPayloadFailure>","","Lifts a stored payload from the version it was written at to the version this code reads, one step per version, in order.","Підіймає збережений payload від версії, якою його записали, до версії, яку читає цей код, по одному кроку на версію, по порядку.",workflow-upcastToCurrentVersion-procedure
|
|
25
|
+
MissingUpcastStepError,failure,"Data.TaggedError<{ readonly eventType: string; readonly fromVersion: number; readonly currentVersion: number }>","","One version between the stored payload and the current one has no upcast step, so the lift cannot finish.","Одна версія між збереженим payload і поточною не має кроку підйому, тож підйом не доходить до кінця.",workflow-MissingUpcastStepError-failure
|
|
26
|
+
PublishPendingEventsFailure,failure,type,"","What one beat of the relay can meet: the outbox did not answer, or the publisher did not.","Що може зустріти один такт трансмітера: буфер трансмітера не відповів або публікатор не відповів.",workflow-PublishPendingEventsFailure-failure
|
|
27
|
+
SchemaVersionFromTheFutureError,failure,"Data.TaggedError<{ readonly eventType: string; readonly storedVersion: number; readonly currentVersion: number }>","",The stored payload says it was written by a newer version of this code than the one reading it.,"Збережений payload каже, що його записала новіша версія цього коду, ніж та, що його читає.",workflow-SchemaVersionFromTheFutureError-failure
|
|
28
|
+
UpcastStepFailedError,failure,"Data.TaggedError<{ readonly eventType: string; readonly fromVersion: number; readonly cause: unknown }>","",An upcast step threw while lifting the payload one version up.,"Крок підйому кинув помилку, поки підіймав payload на версію вгору.",workflow-UpcastStepFailedError-failure
|
|
29
|
+
VersionedPayloadFailure,failure,type,"",Every way reading a stored payload can refuse.,"Кожен спосіб, яким читання збереженого payload може відмовити.",workflow-VersionedPayloadFailure-failure
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What one run of the command comes back with: the whole of what it
|
|
3
|
+
* prints, and the code it leaves behind. A refusal and an answer travel
|
|
4
|
+
* in the same shape, so the runner never decides for itself which of the
|
|
5
|
+
* two it is holding.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export interface CommandAnswer {
|
|
10
|
+
readonly text: string;
|
|
11
|
+
readonly code: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The three lines a refusal is written in here: what happened, why that
|
|
15
|
+
* stops this command, and what to do about it.
|
|
16
|
+
*
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
export declare const refusalOf: (happened: string, matters: string, todo: string) => string;
|
|
20
|
+
/**
|
|
21
|
+
* Every command this runner answers, one line each, in the order a
|
|
22
|
+
* caller meets them.
|
|
23
|
+
*
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export declare const USAGE: string;
|
|
27
|
+
/**
|
|
28
|
+
* One run of the command, against the dictionary directory this package
|
|
29
|
+
* ships at `root`. It prints a file that already exists rather than
|
|
30
|
+
* building one, so it asks for no test runner, no compiler and no
|
|
31
|
+
* network — an agent gets the same answer from a node_modules directory
|
|
32
|
+
* as a reader gets from the repository.
|
|
33
|
+
*
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
export declare const runMachinery: (argv: readonly string[], root: string) => CommandAnswer;
|
|
37
|
+
/**
|
|
38
|
+
* Whether a write failed because the reader on the other end of the pipe
|
|
39
|
+
* closed it. A caller who pipes an answer into `head` closes it on
|
|
40
|
+
* purpose, and a runner that dies with a stack trace over that reports a
|
|
41
|
+
* fault that never happened.
|
|
42
|
+
*
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
export declare const isBrokenPipe: (error: unknown) => boolean;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* The three lines a refusal is written in here: what happened, why that
|
|
5
|
+
* stops this command, and what to do about it.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export const refusalOf = (happened, matters, todo) => [
|
|
10
|
+
`what happened: ${happened}`,
|
|
11
|
+
`why this matters: ${matters}`,
|
|
12
|
+
`what to do: ${todo}`,
|
|
13
|
+
].join('\n');
|
|
14
|
+
/**
|
|
15
|
+
* Every command this runner answers, one line each, in the order a
|
|
16
|
+
* caller meets them.
|
|
17
|
+
*
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
export const USAGE = [
|
|
21
|
+
'satorio-machinery surface [mechanism] [--json|--toon]',
|
|
22
|
+
'satorio-machinery mechanisms [--json|--toon]',
|
|
23
|
+
'satorio-machinery reference [--uk]',
|
|
24
|
+
].join('\n');
|
|
25
|
+
const missingFileRefusal = (path) => refusalOf(`this install carries no ${path}, so there is nothing to print.`, 'the dictionary is what an agent reads instead of the source. An ' +
|
|
26
|
+
'empty answer reads like a package with nothing in it, which is a ' +
|
|
27
|
+
'worse lie than a refusal.', 'reinstall @satorio/machinery from a version that ships its ' +
|
|
28
|
+
'dictionary, or read the same thing in the repository with ' +
|
|
29
|
+
'bun run surface.');
|
|
30
|
+
const unknownMechanismRefusal = (wanted, known) => refusalOf(`this package has no mechanism called "${wanted}", so there is no ` +
|
|
31
|
+
'card to hand you.', 'a card is what an agent reads before it writes a line against a ' +
|
|
32
|
+
'mechanism. A silent empty answer would read as a mechanism with ' +
|
|
33
|
+
'nothing in it.', `ask for one of ${known.join(', ')}, or run satorio-machinery ` +
|
|
34
|
+
'mechanisms to read the index first.');
|
|
35
|
+
const unknownCommandRefusal = (wanted) => refusalOf(wanted === ''
|
|
36
|
+
? 'this command was run with nothing to do.'
|
|
37
|
+
: `"${wanted}" is not a command this runner answers.`, 'a runner that guesses what you meant prints something you did not ' +
|
|
38
|
+
'ask for, and an agent believes it.', `run one of these instead:\n${USAGE}`);
|
|
39
|
+
const twoFormsRefusal = () => refusalOf('both --json and --toon were asked for in one run.', 'the two forms say the same thing in different shapes, so a run that ' +
|
|
40
|
+
'takes both has to pick one behind your back.', 'ask for one of them, or neither: the tabular form is what you get ' +
|
|
41
|
+
'when you say nothing, because it is the cheaper one to read.');
|
|
42
|
+
const fileText = (root, path) => {
|
|
43
|
+
try {
|
|
44
|
+
return readFileSync(join(root, path), 'utf8');
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const mechanismNames = (root) => {
|
|
51
|
+
const text = fileText(root, 'index.json');
|
|
52
|
+
if (text === null)
|
|
53
|
+
return null;
|
|
54
|
+
const index = JSON.parse(text);
|
|
55
|
+
return (index.index ?? []).map((entry) => entry.mechanism);
|
|
56
|
+
};
|
|
57
|
+
const shippedFile = (root, path) => {
|
|
58
|
+
const text = fileText(root, path);
|
|
59
|
+
if (text === null)
|
|
60
|
+
return { text: missingFileRefusal(path), code: 1 };
|
|
61
|
+
return { text: text.replace(/\n$/, ''), code: 0 };
|
|
62
|
+
};
|
|
63
|
+
const surfaceAnswer = (root, wanted, extension) => {
|
|
64
|
+
if (wanted === undefined)
|
|
65
|
+
return shippedFile(root, `all.${extension}`);
|
|
66
|
+
const known = mechanismNames(root);
|
|
67
|
+
if (known === null)
|
|
68
|
+
return { text: missingFileRefusal('index.json'), code: 1 };
|
|
69
|
+
if (!known.includes(wanted)) {
|
|
70
|
+
return { text: unknownMechanismRefusal(wanted, known), code: 1 };
|
|
71
|
+
}
|
|
72
|
+
return shippedFile(root, `${wanted}.${extension}`);
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* One run of the command, against the dictionary directory this package
|
|
76
|
+
* ships at `root`. It prints a file that already exists rather than
|
|
77
|
+
* building one, so it asks for no test runner, no compiler and no
|
|
78
|
+
* network — an agent gets the same answer from a node_modules directory
|
|
79
|
+
* as a reader gets from the repository.
|
|
80
|
+
*
|
|
81
|
+
* @internal
|
|
82
|
+
*/
|
|
83
|
+
export const runMachinery = (argv, root) => {
|
|
84
|
+
const words = argv.filter((argument) => !argument.startsWith('--'));
|
|
85
|
+
const flags = argv.filter((argument) => argument.startsWith('--'));
|
|
86
|
+
if (flags.includes('--help') || words[0] === 'help') {
|
|
87
|
+
return { text: USAGE, code: 0 };
|
|
88
|
+
}
|
|
89
|
+
if (flags.includes('--json') && flags.includes('--toon')) {
|
|
90
|
+
return { text: twoFormsRefusal(), code: 1 };
|
|
91
|
+
}
|
|
92
|
+
const extension = flags.includes('--json') ? 'json' : 'toon';
|
|
93
|
+
if (words[0] === 'surface')
|
|
94
|
+
return surfaceAnswer(root, words[1], extension);
|
|
95
|
+
if (words[0] === 'mechanisms') {
|
|
96
|
+
return shippedFile(root, `index.${extension}`);
|
|
97
|
+
}
|
|
98
|
+
if (words[0] === 'reference') {
|
|
99
|
+
return shippedFile(root, flags.includes('--uk') ? 'reference/uk/api.mdx' : 'reference/api.mdx');
|
|
100
|
+
}
|
|
101
|
+
return { text: unknownCommandRefusal(words[0] ?? ''), code: 1 };
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Whether a write failed because the reader on the other end of the pipe
|
|
105
|
+
* closed it. A caller who pipes an answer into `head` closes it on
|
|
106
|
+
* purpose, and a runner that dies with a stack trace over that reports a
|
|
107
|
+
* fault that never happened.
|
|
108
|
+
*
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
export const isBrokenPipe = (error) => typeof error === 'object' &&
|
|
112
|
+
error !== null &&
|
|
113
|
+
error.code === 'EPIPE';
|
package/dist/bin.d.ts
ADDED
package/dist/bin.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { isBrokenPipe, runMachinery } from './bin/command.js';
|
|
5
|
+
const dictionaryRoot = join(fileURLToPath(new URL('.', import.meta.url)), '..', 'dictionary');
|
|
6
|
+
const answer = runMachinery(process.argv.slice(2), dictionaryRoot);
|
|
7
|
+
process.stdout.on('error', (error) => {
|
|
8
|
+
process.exitCode = isBrokenPipe(error) ? answer.code : 1;
|
|
9
|
+
});
|
|
10
|
+
process.stdout.write(`${answer.text}\n`);
|
|
11
|
+
process.exitCode = answer.code;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Use as the replacer argument to `JSON.stringify`. It turns bigint into a
|
|
3
|
+
* decimal string, so the exact integer survives instead of being silently
|
|
4
|
+
* rounded by a floating-point number.
|
|
5
|
+
*/
|
|
6
|
+
export declare const bigIntsAsDecimalStrings: (_key: string, value: unknown) => unknown;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Use as the replacer argument to `JSON.stringify`. It turns bigint into a
|
|
3
|
+
* decimal string, so the exact integer survives instead of being silently
|
|
4
|
+
* rounded by a floating-point number.
|
|
5
|
+
*/
|
|
6
|
+
export const bigIntsAsDecimalStrings = (_key, value) => (typeof value === 'bigint' ? value.toString(10) : value);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns an array of string literals into a closed set: the values, a union
|
|
3
|
+
* type of their members, and an `is` guard, all from one source. The guard
|
|
4
|
+
* can never drift from the list it guards.
|
|
5
|
+
*/
|
|
6
|
+
export interface ClosedSet<T extends readonly string[]> {
|
|
7
|
+
readonly values: T;
|
|
8
|
+
readonly is: (value: string) => value is T[number];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Builds a closed set from an array of string literals, so the guard and the
|
|
12
|
+
* list it guards come from one source.
|
|
13
|
+
*/
|
|
14
|
+
export declare const closedSet: <const T extends readonly string[]>(values: T) => ClosedSet<T>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for domain errors. It sets `error.name` to the subclass's own
|
|
3
|
+
* class name via `new.target`, so a subclass never repeats
|
|
4
|
+
* `this.name = '...'`. Pass the message through `super(...)`.
|
|
5
|
+
*/
|
|
6
|
+
export declare class DomainError extends Error {
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for domain errors. It sets `error.name` to the subclass's own
|
|
3
|
+
* class name via `new.target`, so a subclass never repeats
|
|
4
|
+
* `this.name = '...'`. Pass the message through `super(...)`.
|
|
5
|
+
*/
|
|
6
|
+
export class DomainError extends Error {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = new.target.name;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Config, ConfigError, Option, Redacted } from 'effect';
|
|
2
|
+
/**
|
|
3
|
+
* A refusal in the three parts a reader needs: what happened, why this
|
|
4
|
+
* mechanism cannot go on without it, and what to do next. All three
|
|
5
|
+
* fields are required, so a one-line refusal cannot be expressed here.
|
|
6
|
+
*/
|
|
7
|
+
export interface Refusal {
|
|
8
|
+
readonly what: string;
|
|
9
|
+
readonly why: string;
|
|
10
|
+
readonly whatToDo: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Sews a refusal into the one line an operator reads in a log or a
|
|
14
|
+
* startup failure.
|
|
15
|
+
*/
|
|
16
|
+
export declare const refusalText: (refusal: Refusal) => string;
|
|
17
|
+
/**
|
|
18
|
+
* The one failure a refusing variable raises, with its three parts sewn into a
|
|
19
|
+
* single line.
|
|
20
|
+
*
|
|
21
|
+
* The failure a reader meets when one named variable refuses: the three parts,
|
|
22
|
+
* sewn into the one line, against the name of the variable that asked for
|
|
23
|
+
* them. A mechanism that chooses between two variables builds its own refusal
|
|
24
|
+
* from here rather than writing a second `ConfigError` of its own.
|
|
25
|
+
*/
|
|
26
|
+
export declare const refusalFor: (name: string, refusal: Refusal) => ConfigError.ConfigError;
|
|
27
|
+
/**
|
|
28
|
+
* Reads a variable that a mechanism cannot work without, and refuses in
|
|
29
|
+
* three parts when it is missing or blank. The reading goes through
|
|
30
|
+
* `Config.option` on purpose: `Config.validate` fires only on a value
|
|
31
|
+
* that is present and invalid, so a variable nobody set comes back as
|
|
32
|
+
* `MissingData` with the runtime's own wording and the three parts
|
|
33
|
+
* written here never reach the newcomer who simply forgot to set it.
|
|
34
|
+
*/
|
|
35
|
+
export declare const requiredText: (name: string, refusal: Refusal) => Config.Config<string>;
|
|
36
|
+
/**
|
|
37
|
+
* Reads a secret a mechanism cannot work without, and refuses in the same
|
|
38
|
+
* three parts when it is missing or blank. The value comes back redacted,
|
|
39
|
+
* so it never reaches a log line by accident.
|
|
40
|
+
*/
|
|
41
|
+
export declare const requiredSecret: (name: string, refusal: Refusal) => Config.Config<Redacted.Redacted>;
|
|
42
|
+
/**
|
|
43
|
+
* Reads a whole number inside a range, and refuses in three parts when it is
|
|
44
|
+
* not one.
|
|
45
|
+
*
|
|
46
|
+
* Reads a whole number a mechanism cannot work without, and refuses in the
|
|
47
|
+
* same three parts when it is missing, blank, not a whole number, or outside
|
|
48
|
+
* the range the mechanism can act on. `lowest` and `highest` are both
|
|
49
|
+
* inclusive.
|
|
50
|
+
*/
|
|
51
|
+
export declare const wholeNumberWithin: (name: string, lowest: number, highest: number, refusal: Refusal) => Config.Config<number>;
|
|
52
|
+
/**
|
|
53
|
+
* Reads a whole number a mechanism is happy to do without, and falls
|
|
54
|
+
* back to the value it names when nobody set the variable. A value that
|
|
55
|
+
* *is* set still has to be a whole number inside the range, and refuses
|
|
56
|
+
* in the same three parts when it is not: a fallback covers the operator
|
|
57
|
+
* who left the variable alone, never the one who typed something the
|
|
58
|
+
* mechanism cannot act on.
|
|
59
|
+
*/
|
|
60
|
+
export declare const wholeNumberFallingBackTo: (name: string, lowest: number, highest: number, fallback: number, refusal: Refusal) => Config.Config<number>;
|
|
61
|
+
/**
|
|
62
|
+
* Reads a variable that is legitimately optional with no default,
|
|
63
|
+
* because somebody outside this process decides it. The answer is
|
|
64
|
+
* `Option.none` when the variable is unset or blank, so a caller reads
|
|
65
|
+
* one absence rather than two.
|
|
66
|
+
*/
|
|
67
|
+
export declare const optionalText: (name: string) => Config.Config<Option.Option<string>>;
|
|
68
|
+
/**
|
|
69
|
+
* Reads a secret that is legitimately optional, and hands back
|
|
70
|
+
* `Option.none` when it is unset or blank. The value stays redacted the
|
|
71
|
+
* whole way, so a mechanism that chooses between two ways of proving who
|
|
72
|
+
* it is can ask whether the secret is there without ever holding it in
|
|
73
|
+
* the clear.
|
|
74
|
+
*/
|
|
75
|
+
export declare const optionalSecret: (name: string) => Config.Config<Option.Option<Redacted.Redacted>>;
|
|
76
|
+
/**
|
|
77
|
+
* Which of three states a variable stands in. There is no fourth state
|
|
78
|
+
* and no silence: a variable either has a safe fallback, refuses without
|
|
79
|
+
* one, or is handed on to somebody outside this process.
|
|
80
|
+
*/
|
|
81
|
+
export type EnvironmentState = {
|
|
82
|
+
readonly state: 'fallsBackTo';
|
|
83
|
+
readonly value: string;
|
|
84
|
+
} | {
|
|
85
|
+
readonly state: 'refuses';
|
|
86
|
+
readonly refusal: Refusal;
|
|
87
|
+
} | {
|
|
88
|
+
readonly state: 'handedOn';
|
|
89
|
+
readonly consequence: string;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* The variable is safe to omit, and this is what happens instead.
|
|
93
|
+
*/
|
|
94
|
+
export declare const fallsBackTo: (value: string) => EnvironmentState;
|
|
95
|
+
/**
|
|
96
|
+
* The mechanism cannot work without the variable, and this is the
|
|
97
|
+
* three-part text a reader meets when it is missing.
|
|
98
|
+
*/
|
|
99
|
+
export declare const refuses: (refusal: Refusal) => EnvironmentState;
|
|
100
|
+
/**
|
|
101
|
+
* The variable is legitimately optional with no default, because
|
|
102
|
+
* somebody else decides it, and this is what follows from leaving it
|
|
103
|
+
* unset.
|
|
104
|
+
*/
|
|
105
|
+
export declare const handedOn: (consequence: string) => EnvironmentState;
|
|
106
|
+
/**
|
|
107
|
+
* One variable a mechanism reads, and the one state it stands in.
|
|
108
|
+
*
|
|
109
|
+
* One variable a mechanism reads: its name — whole, or the suffix an
|
|
110
|
+
* application prefixes at mount time — what it is for, and the one state it
|
|
111
|
+
* stands in. A mechanism's `ENVIRONMENT` is the list of these, and it is the
|
|
112
|
+
* only place a variable name is written.
|
|
113
|
+
*/
|
|
114
|
+
export interface EnvironmentEntry {
|
|
115
|
+
readonly name: string;
|
|
116
|
+
readonly prefixed?: boolean;
|
|
117
|
+
readonly what: string;
|
|
118
|
+
readonly state: EnvironmentState;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The name an operator sets, with `<PREFIX>` standing in front of a
|
|
122
|
+
* suffix the application chooses at mount time.
|
|
123
|
+
*/
|
|
124
|
+
export declare const variableNameOf: (entry: EnvironmentEntry) => string;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Config, ConfigError, Either, Option, Redacted } from 'effect';
|
|
2
|
+
/**
|
|
3
|
+
* Sews a refusal into the one line an operator reads in a log or a
|
|
4
|
+
* startup failure.
|
|
5
|
+
*/
|
|
6
|
+
export const refusalText = (refusal) => `${refusal.what} ${refusal.why} ${refusal.whatToDo}`;
|
|
7
|
+
/**
|
|
8
|
+
* The one failure a refusing variable raises, with its three parts sewn into a
|
|
9
|
+
* single line.
|
|
10
|
+
*
|
|
11
|
+
* The failure a reader meets when one named variable refuses: the three parts,
|
|
12
|
+
* sewn into the one line, against the name of the variable that asked for
|
|
13
|
+
* them. A mechanism that chooses between two variables builds its own refusal
|
|
14
|
+
* from here rather than writing a second `ConfigError` of its own.
|
|
15
|
+
*/
|
|
16
|
+
export const refusalFor = (name, refusal) => ConfigError.InvalidData([name], refusalText(refusal));
|
|
17
|
+
const invalid = (name, refusal) => Either.left(refusalFor(name, refusal));
|
|
18
|
+
const blank = (value) => value.trim().length === 0;
|
|
19
|
+
/**
|
|
20
|
+
* Reads a variable that a mechanism cannot work without, and refuses in
|
|
21
|
+
* three parts when it is missing or blank. The reading goes through
|
|
22
|
+
* `Config.option` on purpose: `Config.validate` fires only on a value
|
|
23
|
+
* that is present and invalid, so a variable nobody set comes back as
|
|
24
|
+
* `MissingData` with the runtime's own wording and the three parts
|
|
25
|
+
* written here never reach the newcomer who simply forgot to set it.
|
|
26
|
+
*/
|
|
27
|
+
export const requiredText = (name, refusal) => Config.option(Config.string(name)).pipe(Config.mapOrFail((held) => Option.match(held, {
|
|
28
|
+
onNone: () => invalid(name, refusal),
|
|
29
|
+
onSome: (value) => blank(value) ? invalid(name, refusal) : Either.right(value.trim()),
|
|
30
|
+
})));
|
|
31
|
+
/**
|
|
32
|
+
* Reads a secret a mechanism cannot work without, and refuses in the same
|
|
33
|
+
* three parts when it is missing or blank. The value comes back redacted,
|
|
34
|
+
* so it never reaches a log line by accident.
|
|
35
|
+
*/
|
|
36
|
+
export const requiredSecret = (name, refusal) => Config.option(Config.redacted(name)).pipe(Config.mapOrFail((held) => Option.match(held, {
|
|
37
|
+
onNone: () => invalid(name, refusal),
|
|
38
|
+
onSome: (secret) => blank(Redacted.value(secret))
|
|
39
|
+
? invalid(name, refusal)
|
|
40
|
+
: Either.right(secret),
|
|
41
|
+
})));
|
|
42
|
+
const wholeNumberIn = (name, text, lowest, highest, refusal) => {
|
|
43
|
+
const value = Number(text);
|
|
44
|
+
return Number.isInteger(value) && value >= lowest && value <= highest
|
|
45
|
+
? Either.right(value)
|
|
46
|
+
: invalid(name, refusal);
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Reads a whole number inside a range, and refuses in three parts when it is
|
|
50
|
+
* not one.
|
|
51
|
+
*
|
|
52
|
+
* Reads a whole number a mechanism cannot work without, and refuses in the
|
|
53
|
+
* same three parts when it is missing, blank, not a whole number, or outside
|
|
54
|
+
* the range the mechanism can act on. `lowest` and `highest` are both
|
|
55
|
+
* inclusive.
|
|
56
|
+
*/
|
|
57
|
+
export const wholeNumberWithin = (name, lowest, highest, refusal) => requiredText(name, refusal).pipe(Config.mapOrFail((text) => wholeNumberIn(name, text, lowest, highest, refusal)));
|
|
58
|
+
/**
|
|
59
|
+
* Reads a whole number a mechanism is happy to do without, and falls
|
|
60
|
+
* back to the value it names when nobody set the variable. A value that
|
|
61
|
+
* *is* set still has to be a whole number inside the range, and refuses
|
|
62
|
+
* in the same three parts when it is not: a fallback covers the operator
|
|
63
|
+
* who left the variable alone, never the one who typed something the
|
|
64
|
+
* mechanism cannot act on.
|
|
65
|
+
*/
|
|
66
|
+
export const wholeNumberFallingBackTo = (name, lowest, highest, fallback, refusal) => Config.option(Config.string(name)).pipe(Config.mapOrFail((held) => Option.match(held, {
|
|
67
|
+
onNone: () => Either.right(fallback),
|
|
68
|
+
onSome: (text) => blank(text)
|
|
69
|
+
? Either.right(fallback)
|
|
70
|
+
: wholeNumberIn(name, text.trim(), lowest, highest, refusal),
|
|
71
|
+
})));
|
|
72
|
+
/**
|
|
73
|
+
* Reads a variable that is legitimately optional with no default,
|
|
74
|
+
* because somebody outside this process decides it. The answer is
|
|
75
|
+
* `Option.none` when the variable is unset or blank, so a caller reads
|
|
76
|
+
* one absence rather than two.
|
|
77
|
+
*/
|
|
78
|
+
export const optionalText = (name) => Config.option(Config.string(name)).pipe(Config.map(Option.flatMap((value) => blank(value) ? Option.none() : Option.some(value.trim()))));
|
|
79
|
+
/**
|
|
80
|
+
* Reads a secret that is legitimately optional, and hands back
|
|
81
|
+
* `Option.none` when it is unset or blank. The value stays redacted the
|
|
82
|
+
* whole way, so a mechanism that chooses between two ways of proving who
|
|
83
|
+
* it is can ask whether the secret is there without ever holding it in
|
|
84
|
+
* the clear.
|
|
85
|
+
*/
|
|
86
|
+
export const optionalSecret = (name) => Config.option(Config.redacted(name)).pipe(Config.map(Option.flatMap((secret) => blank(Redacted.value(secret)) ? Option.none() : Option.some(secret))));
|
|
87
|
+
/**
|
|
88
|
+
* The variable is safe to omit, and this is what happens instead.
|
|
89
|
+
*/
|
|
90
|
+
export const fallsBackTo = (value) => ({
|
|
91
|
+
state: 'fallsBackTo',
|
|
92
|
+
value,
|
|
93
|
+
});
|
|
94
|
+
/**
|
|
95
|
+
* The mechanism cannot work without the variable, and this is the
|
|
96
|
+
* three-part text a reader meets when it is missing.
|
|
97
|
+
*/
|
|
98
|
+
export const refuses = (refusal) => ({
|
|
99
|
+
state: 'refuses',
|
|
100
|
+
refusal,
|
|
101
|
+
});
|
|
102
|
+
/**
|
|
103
|
+
* The variable is legitimately optional with no default, because
|
|
104
|
+
* somebody else decides it, and this is what follows from leaving it
|
|
105
|
+
* unset.
|
|
106
|
+
*/
|
|
107
|
+
export const handedOn = (consequence) => ({
|
|
108
|
+
state: 'handedOn',
|
|
109
|
+
consequence,
|
|
110
|
+
});
|
|
111
|
+
/**
|
|
112
|
+
* The name an operator sets, with `<PREFIX>` standing in front of a
|
|
113
|
+
* suffix the application chooses at mount time.
|
|
114
|
+
*/
|
|
115
|
+
export const variableNameOf = (entry) => entry.prefixed === true ? `<PREFIX>${entry.name}` : entry.name;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public entry to the core mechanism: the few pieces every other mechanism
|
|
3
|
+
* leans on, and which lean on nothing themselves. It gives you the pieces
|
|
4
|
+
* every other mechanism leans on and which lean on nothing themselves — a safe
|
|
5
|
+
* unique id, a closed set of allowed values, exact bigints on the wire, the
|
|
6
|
+
* base domain error, and the three-part refusal an adapter uses to say what a
|
|
7
|
+
* variable is for and what to do when it is missing.
|
|
8
|
+
*/
|
|
9
|
+
export { bigIntsAsDecimalStrings } from './bigint-json.js';
|
|
10
|
+
export { type ClosedSet, closedSet } from './closed-set.js';
|
|
11
|
+
export { DomainError } from './domain-error.js';
|
|
12
|
+
export { type EnvironmentEntry, type EnvironmentState, fallsBackTo, handedOn, optionalSecret, optionalText, type Refusal, refusalFor, refusalText, refuses, requiredSecret, requiredText, variableNameOf, wholeNumberFallingBackTo, wholeNumberWithin, } from './environment.js';
|
|
13
|
+
export { isUlid, NotAUlidError, type Ulid, ulid } from './ulid.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public entry to the core mechanism: the few pieces every other mechanism
|
|
3
|
+
* leans on, and which lean on nothing themselves. It gives you the pieces
|
|
4
|
+
* every other mechanism leans on and which lean on nothing themselves — a safe
|
|
5
|
+
* unique id, a closed set of allowed values, exact bigints on the wire, the
|
|
6
|
+
* base domain error, and the three-part refusal an adapter uses to say what a
|
|
7
|
+
* variable is for and what to do when it is missing.
|
|
8
|
+
*/
|
|
9
|
+
export { bigIntsAsDecimalStrings } from './bigint-json.js';
|
|
10
|
+
export { closedSet } from './closed-set.js';
|
|
11
|
+
export { DomainError } from './domain-error.js';
|
|
12
|
+
export { fallsBackTo, handedOn, optionalSecret, optionalText, refusalFor, refusalText, refuses, requiredSecret, requiredText, variableNameOf, wholeNumberFallingBackTo, wholeNumberWithin, } from './environment.js';
|
|
13
|
+
export { isUlid, NotAUlidError, ulid } from './ulid.js';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { DomainError } from './domain-error.js';
|
|
2
|
+
declare const ulidBrand: unique symbol;
|
|
3
|
+
/**
|
|
4
|
+
* A ULID: the identifier a machine mints for a record, sortable by the moment
|
|
5
|
+
* it was minted, and a plain string everywhere it travels.
|
|
6
|
+
*/
|
|
7
|
+
export type Ulid = string & {
|
|
8
|
+
readonly [ulidBrand]: 'Ulid';
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Whether a string is a ULID, as a type guard, so a caller narrows the type
|
|
12
|
+
* without raising anything.
|
|
13
|
+
*/
|
|
14
|
+
export declare const isUlid: (value: string) => value is Ulid;
|
|
15
|
+
/**
|
|
16
|
+
* What a caller meets when a string that is not a ULID reaches `ulid`; the
|
|
17
|
+
* message quotes the exact value received.
|
|
18
|
+
*/
|
|
19
|
+
export declare class NotAUlidError extends DomainError {
|
|
20
|
+
constructor(value: string);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Reads a string as a ULID and throws `NotAUlidError` when it is not one, so a
|
|
24
|
+
* raw string comes through one door.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ulid: (raw: string) => Ulid;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { DomainError } from './domain-error.js';
|
|
2
|
+
const TWENTY_SIX_UPPERCASE_CROCKFORD_SYMBOLS_STARTING_BELOW_EIGHT = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
|
|
3
|
+
/**
|
|
4
|
+
* Whether a string is a ULID, as a type guard, so a caller narrows the type
|
|
5
|
+
* without raising anything.
|
|
6
|
+
*/
|
|
7
|
+
export const isUlid = (value) => TWENTY_SIX_UPPERCASE_CROCKFORD_SYMBOLS_STARTING_BELOW_EIGHT.test(value);
|
|
8
|
+
/**
|
|
9
|
+
* What a caller meets when a string that is not a ULID reaches `ulid`; the
|
|
10
|
+
* message quotes the exact value received.
|
|
11
|
+
*/
|
|
12
|
+
export class NotAUlidError extends DomainError {
|
|
13
|
+
constructor(value) {
|
|
14
|
+
super(`A ULID is 26 Crockford Base32 characters in upper case: the first ` +
|
|
15
|
+
`character is 0-7, and the rest exclude I, L, O, and U. ` +
|
|
16
|
+
`Received ${JSON.stringify(value)}.`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Reads a string as a ULID and throws `NotAUlidError` when it is not one, so a
|
|
21
|
+
* raw string comes through one door.
|
|
22
|
+
*/
|
|
23
|
+
export const ulid = (raw) => {
|
|
24
|
+
if (!isUlid(raw))
|
|
25
|
+
throw new NotAUlidError(raw);
|
|
26
|
+
return raw;
|
|
27
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import '@pothos/plugin-directives';
|
|
2
|
+
import '@pothos/plugin-sub-graph';
|
|
3
|
+
import { hasResolvableKey } from '@pothos/plugin-federation';
|
|
4
|
+
/**
|
|
5
|
+
* The two variations of one schema: `Public` (external) and `Internal`
|
|
6
|
+
* (admin and service operations). Federation (`@key` + `resolveReference`)
|
|
7
|
+
* turns each variation into a subgraph for the gateway.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export declare const SUB_GRAPH_NAMES: readonly ["Public", "Internal"];
|
|
12
|
+
/**
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export type SubGraphName = (typeof SUB_GRAPH_NAMES)[number];
|
|
16
|
+
/**
|
|
17
|
+
* The federation and subgraph settings every schema in this package
|
|
18
|
+
* shares, fixed here so two services cannot drift apart.
|
|
19
|
+
*
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
export declare const subGraphOptions: {
|
|
23
|
+
defaultForTypes: ("Public" | "Internal")[];
|
|
24
|
+
fieldsInheritFromTypes: boolean;
|
|
25
|
+
explicitlyIncludeType: (type: Parameters<typeof hasResolvableKey>[0]) => boolean;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Returns a builder factory carrying the resolver context type. The context
|
|
29
|
+
* itself belongs to your service, so this returns a factory, not a ready
|
|
30
|
+
* instance. Every service shares the same plugins and subgraph settings,
|
|
31
|
+
* fixed here.
|
|
32
|
+
*/
|
|
33
|
+
export declare const makeSubGraphSchemaBuilder: <Context extends object>() => PothosSchemaTypes.SchemaBuilder<PothosSchemaTypes.ExtendDefaultTypes<{
|
|
34
|
+
Context: Context;
|
|
35
|
+
SubGraphs: SubGraphName;
|
|
36
|
+
}>>;
|