@cratis/pi 7.34.0 → 7.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package/corpus/agents/security-reviewer.md +1 -0
- package/package/corpus/agents/spec-writer.md +1 -1
- package/package/corpus/harnesses/pi/extensions/shared/rules.ts +3 -1
- package/package/corpus/hooks/scripts/cratis-nuget-pins.txt +7 -6
- package/package/corpus/hooks/scripts/cratis-release-notes-reviewed.cjs +7 -6
- package/package/corpus/hooks/scripts/cratis-store-mutations.json +7 -3
- package/package/corpus/product-sync.json +896 -0
- package/package/corpus/prompts/add-business-rule.prompt.md +1 -1
- package/package/corpus/prompts/ship-changes.prompt.md +2 -2
- package/package/corpus/rules/ai-distribution.md +31 -3
- package/package/corpus/rules/application-profile.md +2 -1
- package/package/corpus/rules/components.md +66 -7
- package/package/corpus/rules/concepts.md +11 -3
- package/package/corpus/rules/csharp.md +3 -1
- package/package/corpus/rules/dialogs.md +9 -0
- package/package/corpus/rules/efcore.md +1 -1
- package/package/corpus/rules/elixir.md +49 -0
- package/package/corpus/rules/framework.md +5 -3
- package/package/corpus/rules/general.md +1 -0
- package/package/corpus/rules/glossary.md +4 -4
- package/package/corpus/rules/go-engineering.md +60 -0
- package/package/corpus/rules/go.md +115 -0
- package/package/corpus/rules/java.md +5 -2
- package/package/corpus/rules/kotlin.md +7 -5
- package/package/corpus/rules/orleans.md +65 -9
- package/package/corpus/rules/profiles.md +43 -6
- package/package/corpus/rules/pull-requests.md +3 -3
- package/package/corpus/rules/react.md +8 -1
- package/package/corpus/rules/reactors.md +27 -3
- package/package/corpus/rules/specs.csharp.md +11 -4
- package/package/corpus/rules/specs.scenarios.csharp.md +35 -4
- package/package/corpus/rules/specs.typescript.md +2 -1
- package/package/corpus/rules/storybook.md +1 -1
- package/package/corpus/rules/typescript.md +1 -1
- package/package/corpus/rules/vertical-slices.md +37 -5
- package/package/corpus/skills/cratis-application-react-specifications/SKILL.md +4 -2
- package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +18 -8
- package/package/corpus/skills/cratis-application-slice-specifications/references/query-and-decision-scenarios.md +65 -0
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +53 -57
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +38 -42
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +18 -7
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authproxy.md +30 -0
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +12 -9
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +9 -9
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/observable-emission-guards.md +8 -5
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +20 -4
- package/package/corpus/skills/cratis-arc-command/SKILL.md +32 -10
- package/package/corpus/skills/cratis-arc-command/references/command-result.md +12 -5
- package/package/corpus/skills/cratis-arc-command/references/decisions-and-routing.md +95 -0
- package/package/corpus/skills/cratis-arc-command/references/generated-values.md +3 -2
- package/package/corpus/skills/cratis-arc-command/references/handler-shapes.md +8 -8
- package/package/corpus/skills/cratis-arc-command/references/proxy-generation.md +22 -3
- package/package/corpus/skills/cratis-arc-command/references/read-model-injection.md +13 -1
- package/package/corpus/skills/cratis-arc-command-execution/SKILL.md +19 -9
- package/package/corpus/skills/cratis-arc-command-kotlin/SKILL.md +44 -18
- package/package/corpus/skills/cratis-arc-command-kotlin/references/read-model-naming.md +29 -0
- package/package/corpus/skills/cratis-arc-command-operation/SKILL.md +10 -9
- package/package/corpus/skills/cratis-arc-command-operation/references/execution-and-recovery.md +2 -2
- package/package/corpus/skills/cratis-arc-command-operation/references/testing.md +4 -3
- package/package/corpus/skills/cratis-arc-command-typescript/SKILL.md +42 -22
- package/package/corpus/skills/cratis-arc-command-typescript/references/chronicle-events.md +36 -5
- package/package/corpus/skills/cratis-arc-command-typescript/references/command-operations.md +114 -0
- package/package/corpus/skills/cratis-arc-command-typescript/references/hosting-and-storage.md +37 -16
- package/package/corpus/skills/cratis-arc-command-typescript/references/identity-and-introspection.md +88 -0
- package/package/corpus/skills/cratis-arc-command-typescript/references/observability-and-host-scopes.md +25 -0
- package/package/corpus/skills/cratis-arc-command-typescript/references/proxy-generation.md +35 -4
- package/package/corpus/skills/cratis-arc-command-typescript/references/slice-layout.md +11 -0
- package/package/corpus/skills/cratis-arc-command-typescript/verification.json +8 -1
- package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +49 -19
- package/package/corpus/skills/cratis-arc-ef-core-migration/SKILL.md +12 -6
- package/package/corpus/skills/cratis-arc-go/SKILL.md +148 -0
- package/package/corpus/skills/cratis-arc-go/references/queries.md +80 -0
- package/package/corpus/skills/cratis-arc-hosting/SKILL.md +96 -0
- package/package/corpus/skills/cratis-arc-hosting/references/integrations-and-publish.md +80 -0
- package/package/corpus/skills/cratis-arc-identity-and-tenancy-kotlin/SKILL.md +137 -0
- package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +20 -2
- package/package/corpus/skills/cratis-arc-query-kotlin/SKILL.md +26 -6
- package/package/corpus/skills/cratis-arc-query-paging/SKILL.md +25 -20
- package/package/corpus/skills/cratis-arc-query-typescript/SKILL.md +32 -6
- package/package/corpus/skills/cratis-arc-query-typescript/references/storage-observation-and-compliance.md +66 -0
- package/package/corpus/skills/cratis-arc-query-typescript/verification.json +5 -1
- package/package/corpus/skills/cratis-arc-react-page/SKILL.md +24 -13
- package/package/corpus/skills/cratis-arc-react-page/references/data-page.md +20 -10
- package/package/corpus/skills/cratis-arc-react-page/references/data-tables.md +17 -3
- package/package/corpus/skills/cratis-arc-react-page/references/dialogs.md +14 -3
- package/package/corpus/skills/cratis-arc-react-page/references/from-screenplay.md +6 -1
- package/package/corpus/skills/cratis-arc-react-page/references/provenance.md +1 -1
- package/package/corpus/skills/cratis-arc-react-page/references/queries-and-commands.md +11 -1
- package/package/corpus/skills/cratis-arc-react-page/verification.json +6 -1
- package/package/corpus/skills/cratis-arc-spring-data-kotlin/SKILL.md +163 -0
- package/package/corpus/skills/cratis-arc-testing-kotlin/SKILL.md +131 -0
- package/package/corpus/skills/cratis-arc-validation-kotlin/SKILL.md +44 -11
- package/package/corpus/skills/cratis-arc-validation-typescript/SKILL.md +5 -3
- package/package/corpus/skills/cratis-chronicle-cli-operations/SKILL.md +79 -25
- package/package/corpus/skills/cratis-chronicle-cli-operations/references/command-effects.md +57 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/SKILL.md +87 -99
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/additional-surfaces.md +65 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/client-construction.md +42 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/compatibility-check.md +12 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/discovery.md +43 -19
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/hosting.md +29 -1
- package/package/corpus/skills/cratis-chronicle-client-elixir/SKILL.md +158 -162
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/append-api.md +36 -15
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/compliance.md +32 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/connections.md +56 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/event-sources.md +64 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/pitfalls.md +25 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/projections.md +48 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/upgrading.md +34 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/variants.md +13 -5
- package/package/corpus/skills/cratis-chronicle-client-elixir/verification.json +23 -0
- package/package/corpus/skills/cratis-chronicle-client-go/SKILL.md +162 -0
- package/package/corpus/skills/cratis-chronicle-client-go/references/projections.md +73 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +87 -88
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/connection-lifecycle.md +13 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/discovery-and-registration.md +14 -4
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/event-sources.md +65 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/options.md +29 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/spring-boot.md +13 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/variants.md +5 -4
- package/package/corpus/skills/cratis-chronicle-client-typescript/SKILL.md +165 -156
- package/package/corpus/skills/cratis-chronicle-client-typescript/references/read-models-and-projections.md +54 -11
- package/package/corpus/skills/cratis-chronicle-client-typescript/references/testing.md +126 -0
- package/package/corpus/skills/cratis-chronicle-client-typescript/references/variants.md +3 -5
- package/package/corpus/skills/cratis-chronicle-client-typescript/verification.json +19 -0
- package/package/corpus/skills/cratis-chronicle-compliance/SKILL.md +36 -13
- package/package/corpus/skills/cratis-chronicle-decision-reads/SKILL.md +110 -0
- package/package/corpus/skills/cratis-chronicle-decision-reads/references/admission-and-ownership.md +57 -0
- package/package/corpus/skills/cratis-chronicle-decision-reads/verification.json +12 -0
- package/package/corpus/skills/cratis-chronicle-event-constraints/SKILL.md +58 -19
- package/package/corpus/skills/cratis-chronicle-event-constraints/references/closing-streams.md +73 -0
- package/package/corpus/skills/cratis-chronicle-event-modeling/SKILL.md +32 -3
- package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +18 -5
- package/package/corpus/skills/cratis-chronicle-event-type-migration/SKILL.md +32 -2
- package/package/corpus/skills/cratis-chronicle-hosting/SKILL.md +134 -0
- package/package/corpus/skills/cratis-chronicle-hosting/references/operations.md +89 -0
- package/package/corpus/skills/cratis-chronicle-hosting/verification.json +14 -0
- package/package/corpus/skills/cratis-chronicle-mcp-inspection/SKILL.md +93 -66
- package/package/corpus/skills/cratis-chronicle-mcp-inspection/references/blocked-tools.md +21 -7
- package/package/corpus/skills/cratis-chronicle-mcp-inspection/references/deployment.md +77 -0
- package/package/corpus/skills/cratis-chronicle-mcp-inspection/references/observational-tools.md +46 -4
- package/package/corpus/skills/cratis-chronicle-multi-tenancy/SKILL.md +15 -1
- package/package/corpus/skills/cratis-chronicle-projection/SKILL.md +44 -10
- package/package/corpus/skills/cratis-chronicle-projection/references/fluent-builder.md +7 -1
- package/package/corpus/skills/cratis-chronicle-projection/references/model-bound-attributes.md +8 -0
- package/package/corpus/skills/cratis-chronicle-reactor/SKILL.md +85 -105
- package/package/corpus/skills/cratis-chronicle-reactor/references/slice-delivery-checklist.md +1 -1
- package/package/corpus/skills/cratis-chronicle-reactor/references/specifications.md +20 -1
- package/package/corpus/skills/cratis-chronicle-read-model/SKILL.md +2 -2
- package/package/corpus/skills/cratis-chronicle-read-model/references/queries.md +35 -16
- package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +15 -3
- package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +23 -4
- package/package/corpus/skills/cratis-chronicle-web-workbench/SKILL.md +40 -15
- package/package/corpus/skills/cratis-cli-terminal-workbench/SKILL.md +20 -6
- package/package/corpus/skills/cratis-code-review/SKILL.md +25 -3
- package/package/corpus/skills/cratis-components-accessibility/SKILL.md +20 -4
- package/package/corpus/skills/cratis-components-schema-editor/SKILL.md +40 -28
- package/package/corpus/skills/cratis-components-schema-editor/references/tree-layout.md +62 -0
- package/package/corpus/skills/cratis-components-schema-editor/verification.json +14 -0
- package/package/corpus/skills/cratis-components-stepper-command-dialog/SKILL.md +16 -8
- package/package/corpus/skills/cratis-components-styling/SKILL.md +57 -20
- package/package/corpus/skills/cratis-components-styling/verification.json +13 -0
- package/package/corpus/skills/cratis-components-toolbar/SKILL.md +37 -7
- package/package/corpus/skills/cratis-components-toolbar/verification.json +12 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/SKILL.md +6 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/references/exceptions-logging-and-di.md +5 -3
- package/package/corpus/skills/cratis-fundamentals-concept/SKILL.md +19 -6
- package/package/corpus/skills/cratis-fundamentals-concept/references/typescript-concepts.md +97 -0
- package/package/corpus/skills/cratis-fundamentals-concept/verification.json +4 -1
- package/package/corpus/skills/cratis-fundamentals-concept-go/SKILL.md +112 -0
- package/package/corpus/skills/cratis-fundamentals-type-discovery/SKILL.md +54 -17
- package/package/corpus/skills/cratis-fundamentals-type-discovery/verification.json +14 -0
- package/package/corpus/skills/cratis-lens-browser-extension/SKILL.md +55 -28
- package/package/corpus/skills/cratis-performance-review/SKILL.md +5 -2
- package/package/corpus/skills/cratis-release-notes/SKILL.md +3 -1
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/SKILL.md +5 -4
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/realization-and-gap-fill.md +1 -1
- package/package/corpus/skills/cratis-screenplay-captures-and-reactions/SKILL.md +1 -1
- package/package/corpus/skills/cratis-screenplay-command-surface/SKILL.md +5 -6
- package/package/corpus/skills/cratis-screenplay-command-surface/references/compliance.md +25 -7
- package/package/corpus/skills/cratis-screenplay-command-surface/references/enum-numbers.md +4 -2
- package/package/corpus/skills/cratis-screenplay-command-surface/references/generated-values-and-responses.md +4 -1
- package/package/corpus/skills/cratis-screenplay-event-modeling/SKILL.md +4 -3
- package/package/corpus/skills/cratis-screenplay-event-modeling/references/nine-steps.md +1 -1
- package/package/corpus/skills/cratis-screenplay-event-modeling/references/parsed-not-runnable.md +2 -2
- package/package/corpus/skills/cratis-screenplay-legacy-extraction/SKILL.md +3 -2
- package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/generate-notes.md +34 -4
- package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +5 -3
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md +1 -1
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-tools.md +4 -2
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/property-links.md +42 -3
- package/package/corpus/skills/cratis-screenplay-model-authoring/verification.json +3 -1
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/vocabulary-map.md +2 -2
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/SKILL.md +6 -6
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/drift-and-ledger.md +1 -1
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/gap-fill-handoff.md +6 -4
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/renderability-gate.md +1 -1
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/SKILL.md +3 -1
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/invoicing-dues-design.md +1 -1
- package/package/corpus/skills/cratis-screenplay-slice-design/SKILL.md +1 -1
- package/package/corpus/skills/cratis-screenplay-slice-design/references/rule-layers.md +2 -2
- package/package/corpus/skills/cratis-screenplay-slice-design/references/worked-example.md +1 -1
- package/package/corpus/skills/cratis-screenplay-specifications/references/examples-and-routes.md +48 -0
- package/package/corpus/skills/cratis-screenplay-specifications/verification.json +3 -1
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/SKILL.md +7 -5
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/chronicle-boundaries.md +2 -2
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/consistency-and-concurrency.md +2 -2
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/streams-example.md +5 -4
- package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +8 -6
- package/package/corpus/skills/cratis-screenplay-toolchain/references/cheat-sheet-example.md +2 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/references/diagnostics.md +4 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/references/doc-contradictions.md +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/executable-subset.md +16 -11
- package/package/corpus/skills/cratis-screenplay-toolchain/references/renderable-subset.md +8 -5
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +1329 -32
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/verdicts.md +2 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/references/versions.md +23 -7
- package/package/corpus/skills/cratis-screenplay-toolchain/verification.json +1 -1
- package/package/corpus/skills/cratis-screenplay-ui-composition/SKILL.md +2 -0
- package/package/corpus/skills/cratis-screenplay-ui-composition/references/screen-composition-contract.md +27 -9
- package/package/corpus/skills/cratis-security-review/SKILL.md +9 -3
- package/package/corpus/skills/cratis-security-review/references/arc-boundaries.md +59 -0
- package/package/corpus/skills/cratis-specification-by-example/SKILL.md +7 -0
- package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +43 -11
- package/package/corpus/skills/cratis-specifications-csharp/references/application-scenarios.md +16 -4
- package/package/corpus/skills/cratis-specifications-csharp/references/csharp-patterns.md +18 -5
- package/package/corpus/skills/cratis-specifications-csharp/references/integration-specs.md +21 -0
- package/package/corpus/skills/cratis-specifications-csharp/references/shared-lifecycle.md +34 -0
- package/package/corpus/skills/cratis-specifications-csharp/verification.json +13 -0
- package/package/corpus/skills/cratis-specifications-typescript/SKILL.md +14 -5
- package/package/corpus/skills/cratis-specifications-typescript/verification.json +12 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +11 -5
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +7 -4
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/rendered-application.md +10 -1
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/scene-runtime.md +75 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/verification.json +6 -1
- package/package/corpus/skills/cratis-studio-mcp-safety-guidance/SKILL.md +14 -1
- package/package/profile-catalog.json +68 -1
- package/package.json +1 -1
- package/src/index.ts +3 -1
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cratis-chronicle-client-elixir
|
|
3
|
-
description: Talk to a Chronicle server from an Elixir application with the cratis_chronicle Hex package -
|
|
3
|
+
description: Talk to a Chronicle server from an Elixir application with the cratis_chronicle Hex package - Chronicle.Client supervision and connection lifecycle, EventType structs, append errors, event sources and streams, Chronicle.Concept, PII and encrypted secrets, reactors, read models/projections with composite keys, variants and GlobalHandler, and observer inspection. Use when an OTP application appends to or observes a Chronicle event store. Do not use for the .NET, TypeScript, or Kotlin clients.
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
8
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
9
|
+
|
|
7
10
|
# The Chronicle client for Elixir
|
|
8
11
|
|
|
9
12
|
`cratis_chronicle` is a **standalone client SDK**. It is a library, not a
|
|
@@ -12,18 +15,17 @@ to yours, and everything after that is ordinary OTP.
|
|
|
12
15
|
|
|
13
16
|
## Verified product sources
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
the `file:line` citations below were taken at `v2.2.0` and may have shifted.
|
|
18
|
+
Re-verified against `Cratis/Chronicle.Elixir` tag **`v3.10.2`** (Hex `3.10.2`).
|
|
19
|
+
Source paths below are relative to `Source/chronicle/` at that tag.
|
|
20
|
+
|
|
21
|
+
Read [references/upgrading.md](references/upgrading.md) when upgrading from 2.x or earlier 3.x releases; wire compatibility and corrected behavior both matter.
|
|
20
22
|
|
|
21
23
|
| Fact | Value | Source |
|
|
22
24
|
| --- | --- | --- |
|
|
23
25
|
| OTP app / Hex package | `:cratis_chronicle` | `Source/chronicle/mix.exs:14` |
|
|
24
|
-
| Published versions | `3.
|
|
25
|
-
| Elixir requirement | `~> 1.
|
|
26
|
-
| Contracts dependency | `cratis_chronicle_contracts` resolved to `18.2.0` at `v3.1.0
|
|
26
|
+
| Published versions | `3.10.2`, earlier `3.x`, `3.0.0`, `2.2.0`, `2.1.x`, `2.0.0`, `1.0.x`, `0.x` — **no `2.3.0`** | hex.pm |
|
|
27
|
+
| Elixir requirement | `~> 1.18` | `Source/chronicle/mix.exs` |
|
|
28
|
+
| Contracts dependency | `cratis_chronicle_contracts` resolved to `19.30.0` (`18.2.0` at `v3.1.0`, `16.13.4` at `v2.2.0`); pair it with a compatible kernel | `Source/chronicle/mix.lock` |
|
|
27
29
|
|
|
28
30
|
> **The `v2.3.0` git tag was never published to Hex.** `mix deps.get` cannot
|
|
29
31
|
> fetch it. What it introduced — notably the `Chronicle.Concept` macro
|
|
@@ -35,15 +37,12 @@ the `file:line` citations below were taken at `v2.2.0` and may have shifted.
|
|
|
35
37
|
> **The repository's `VERSION` file says `0.0.5` and is a build-time
|
|
36
38
|
> placeholder** overwritten by the publish workflow. Never quote it.
|
|
37
39
|
|
|
38
|
-
>
|
|
39
|
-
> `
|
|
40
|
-
>
|
|
41
|
-
> `README.md` used unnamespaced names that do not exist (`use Chronicle.EventType`,
|
|
42
|
-
> …); the `3.x` README is correct. When in doubt, copy from
|
|
43
|
-
> `Documentation/client-snippets/` — those are CI-validated at every version.
|
|
40
|
+
> Use namespaced modules (`Chronicle.Events.EventType`, not `Chronicle.EventType`).
|
|
41
|
+
> The `v2.2.0` README got this wrong; the `3.x` README is correct. Maintained
|
|
42
|
+
> snippets live in `Documentation/client-snippets/`.
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
The current README and get-started guide use `~> 3.5`; select the intended
|
|
45
|
+
published version from hex.pm rather than treating a guide's range as a pin.
|
|
47
46
|
|
|
48
47
|
## Adding it
|
|
49
48
|
|
|
@@ -51,7 +50,7 @@ the `file:line` citations below were taken at `v2.2.0` and may have shifted.
|
|
|
51
50
|
# mix.exs
|
|
52
51
|
defp deps do
|
|
53
52
|
[
|
|
54
|
-
{:cratis_chronicle, "~>
|
|
53
|
+
{:cratis_chronicle, "~> 3.10"}
|
|
55
54
|
]
|
|
56
55
|
end
|
|
57
56
|
```
|
|
@@ -62,8 +61,8 @@ The library declares `extra_applications: [:logger, :inets, :ssl]` and **no
|
|
|
62
61
|
|
|
63
62
|
## Starting the client
|
|
64
63
|
|
|
65
|
-
`Chronicle.Client` is a `Supervisor` (`lib/chronicle/client.ex:
|
|
66
|
-
`start_link(opts \\ [])` (`:
|
|
64
|
+
`Chronicle.Client` is a `Supervisor` (`lib/chronicle/client.ex:135`) with
|
|
65
|
+
`start_link(opts \\ [])` (`:145`). Put it in your application's tree — this is
|
|
67
66
|
the CI-validated snippet from
|
|
68
67
|
`Documentation/client-snippets/get-started/console/connect.md`:
|
|
69
68
|
|
|
@@ -75,7 +74,7 @@ defmodule MyApp.Application do
|
|
|
75
74
|
def start(_type, _args) do
|
|
76
75
|
children = [
|
|
77
76
|
{Chronicle.Client,
|
|
78
|
-
connection_string:
|
|
77
|
+
connection_string: Chronicle.Connections.ConnectionString.development(),
|
|
79
78
|
event_store: "quickstart",
|
|
80
79
|
otp_app: :my_app}
|
|
81
80
|
]
|
|
@@ -87,38 +86,38 @@ end
|
|
|
87
86
|
|
|
88
87
|
### Options
|
|
89
88
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
`config.exs` does nothing for this client. Read the options at
|
|
93
|
-
`lib/chronicle/client.ex`:
|
|
89
|
+
Pass **child-spec keyword options**. The client reads no application env;
|
|
90
|
+
your app must read its own configuration and pass it in (`lib/chronicle/client.ex`).
|
|
94
91
|
|
|
95
92
|
| Option | Default | Line |
|
|
96
93
|
| --- | --- | --- |
|
|
97
|
-
| `:name` | `Chronicle.Client` | `:
|
|
94
|
+
| `:name` | `Chronicle.Client` | `:146` |
|
|
98
95
|
| `:connection_string` | `ConnectionString.default/0` | — |
|
|
99
|
-
| `:event_store` | `"default"` | `:
|
|
100
|
-
| `:namespace` | `"Default"` — capital D | `:
|
|
101
|
-
| `:discover` | `true` | `:
|
|
102
|
-
| `:otp_app` | none; falls back to scanning loaded modules | `:
|
|
103
|
-
| `:event_types`, `:migrations`, `:reactors`, `:reducers`, `:read_models`, `:projections`, `:seeders`, `:webhooks`, `:event_store_subscriptions`, `:global_handlers` | `[]`, merged with discovered | `:
|
|
104
|
-
| `:
|
|
96
|
+
| `:event_store` | `"default"` | `:165` |
|
|
97
|
+
| `:namespace` | `"Default"` — capital D | `:166` |
|
|
98
|
+
| `:discover` | `true` | `:167` |
|
|
99
|
+
| `:otp_app` | none; falls back to scanning loaded modules | `:174` |
|
|
100
|
+
| `:event_types`, `:event_sources`, `:migrations`, `:reactors`, `:reducers`, `:read_models`, `:projections`, `:seeders`, `:webhooks`, `:event_store_subscriptions`, `:global_handlers` | `[]`, merged with discovered | `:194-214` |
|
|
101
|
+
| `:default_sink_type_id` | `:mongodb`; accepts `:mongodb`, `:sql`, `:in_memory`, `:none` or a raw string | `:169-170` |
|
|
102
|
+
| `:server_address` | alternative `"host:port"` address | connection option |
|
|
103
|
+
| `:skip_tls_validation`, `:load_balancer`, `:grpc_options`, `:reconnect_base_delay`, `:reconnect_max_delay` | forwarded to the connection | — |
|
|
104
|
+
| `:retry_attempts` | accepted for compatibility, **ignored** | `connections/connection.ex:48-49` |
|
|
105
105
|
|
|
106
106
|
> The moduledoc at `client.ex:81` says the namespace defaults to `"default"`.
|
|
107
|
-
> The code at `:
|
|
107
|
+
> The code at `:166` uses `"Default"`. **Trust the code.**
|
|
108
108
|
|
|
109
109
|
**Set `:otp_app`.** With it, discovery is scoped to your application's modules;
|
|
110
110
|
without it the client falls back to scanning every loaded module.
|
|
111
111
|
|
|
112
112
|
Configuration is stashed in `:persistent_term` and read back with
|
|
113
|
-
`Chronicle.Client.config/1` (`client.ex:
|
|
114
|
-
`read_models`, `projections`,
|
|
113
|
+
`Chronicle.Client.config/1` (`client.ex:156`). Its subset includes `event_sources`,
|
|
114
|
+
but not `read_models`, `projections`, or `migrations` (`:218-228`).
|
|
115
115
|
|
|
116
116
|
### The supervision tree
|
|
117
117
|
|
|
118
|
-
`Supervisor.init(children, strategy: :rest_for_one)` — `client.ex:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
depends on it, and nothing is left holding a dead channel.
|
|
118
|
+
`Supervisor.init(children, strategy: :rest_for_one)` — `client.ex:328`.
|
|
119
|
+
The lifecycle hub is the root dependency; restarting it restarts its subscribers.
|
|
120
|
+
The connection, session and registration precede observers.
|
|
122
121
|
|
|
123
122
|
### Multiple clients
|
|
124
123
|
|
|
@@ -134,7 +133,7 @@ broadcasts `{:chronicle_lifecycle, phase, connection_id}` with three phases
|
|
|
134
133
|
| Phase | Meaning |
|
|
135
134
|
| --- | --- |
|
|
136
135
|
| `:disconnected` | no live session with the kernel |
|
|
137
|
-
| `:connected` | the
|
|
136
|
+
| `:connected` | the kernel acknowledged the connection id with its first keepalive |
|
|
138
137
|
| `:registered` | the registration coordinator registered the base artifacts |
|
|
139
138
|
|
|
140
139
|
**Wait for `:registered`, never merely `:connected`** — the module's own docs say
|
|
@@ -142,90 +141,78 @@ so at `:27-29`, because reducers and reactors are not attached until registratio
|
|
|
142
141
|
finishes.
|
|
143
142
|
|
|
144
143
|
```elixir
|
|
145
|
-
|
|
144
|
+
alias Chronicle.Connections.Lifecycle
|
|
146
145
|
|
|
147
|
-
case
|
|
148
|
-
:ok -> :
|
|
149
|
-
{:error, :timeout} ->
|
|
146
|
+
case Lifecycle.wait_until(Lifecycle.name_for(Chronicle.Client), :registered, 30_000) do
|
|
147
|
+
:ok -> :ready
|
|
148
|
+
{:error, :timeout} -> :not_ready
|
|
150
149
|
end
|
|
151
150
|
```
|
|
152
151
|
|
|
153
152
|
`wait_until(lifecycle, target_phase, timeout \\ 30_000)` is at `:141`.
|
|
154
|
-
`subscribe/1` (`:90`) returns
|
|
153
|
+
`subscribe/1` (`:90`) returns `{:ok, phase, connection_id}` **and** sends it as a message,
|
|
155
154
|
which closes the subscribe-after-transition race; `phase/1` (`:98`) is the plain
|
|
156
155
|
read.
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
stream is evicted once the kernel's `LastSeen` falls more than five seconds
|
|
165
|
-
behind, and the kernel then unsubscribes its observers.
|
|
166
|
-
|
|
167
|
-
The consequence is stated in that same comment and is worth carrying into any
|
|
168
|
-
diagnosis: **reactors and reducers go quiet while the `Connect` stream stays open
|
|
169
|
-
and every append keeps working.** Nothing raises. If observers stop firing but
|
|
170
|
-
appends succeed, look at the connection before you look at the observer.
|
|
171
|
-
|
|
172
|
-
Reconnect is exponential backoff — `:retry_attempts` 5, `:reconnect_base_delay`
|
|
173
|
-
1000 ms, `:reconnect_max_delay` 10000 ms — re-resolving addresses on every
|
|
174
|
-
attempt.
|
|
157
|
+
Reconnect never gives up: exponential backoff starts at 1000 ms and caps at
|
|
158
|
+
10000 ms, re-resolving addresses each time. `:retry_attempts` is ignored.
|
|
159
|
+
Registration failures leave the client in `:connected`, log
|
|
160
|
+
`Chronicle registration failed`, and retry every five seconds. Appends before a
|
|
161
|
+
channel exists return `{:error, :not_connected}`; read model queries wait up to
|
|
162
|
+
30 seconds for `:registered`.
|
|
175
163
|
|
|
176
|
-
##
|
|
164
|
+
## Authentication and TLS
|
|
177
165
|
|
|
178
|
-
`
|
|
179
|
-
|
|
180
|
-
|
|
166
|
+
Use `ConnectionString.development()` locally: `default()` supplies **no
|
|
167
|
+
credentials**, so an authentication-required development kernel never lets it
|
|
168
|
+
reach `:registered`. Production uses URL userinfo `client-id:secret@host` or
|
|
169
|
+
`?apiKey=...` (the API key wins when both are given). Load real credentials from
|
|
170
|
+
the application's approved secret mechanism; never log the connection string.
|
|
181
171
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
| `default/0` — `chronicle://localhost:35000` | `:144` |
|
|
185
|
-
| `development/0` — adds the dev client credentials | `:154` |
|
|
186
|
-
| `parse/1` | `:186` |
|
|
187
|
-
|
|
188
|
-
Query parameters include `apiKey`, `disableTls`, `skipTlsValidation`,
|
|
189
|
-
`certificatePath`, `certificatePassword`, `authPort`, `loadBalancer`,
|
|
190
|
-
`srvNameServer`. A `chronicle+srv://` string accepts only one host and raises
|
|
191
|
-
otherwise (`:197-198`).
|
|
172
|
+
**`skip_tls_validation` defaults to `true`.** Production must set
|
|
173
|
+
`skipTlsValidation=false`; a client certificate does not establish server trust.
|
|
192
174
|
|
|
193
|
-
|
|
194
|
-
> the query parse at `:214`. TLS is on but the certificate chain is not checked,
|
|
195
|
-
> because a development kernel serves a self-signed certificate. **A production
|
|
196
|
-
> connection string must carry `?skipTlsValidation=false`.**
|
|
175
|
+
Read [references/connections.md](references/connections.md) when configuring connection strings, mTLS, grpc/Mint options or credential-safe logging, or diagnosing keepalive and quiet observers.
|
|
197
176
|
|
|
198
177
|
## Defining event types
|
|
199
178
|
|
|
200
179
|
```elixir
|
|
201
|
-
|
|
202
|
-
|
|
180
|
+
# Copyright (c) Cratis. All rights reserved.
|
|
181
|
+
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
182
|
+
|
|
183
|
+
defmodule MyApp.Events.FundsDeposited do
|
|
184
|
+
@moduledoc "Funds were deposited into an account."
|
|
203
185
|
|
|
204
|
-
use Chronicle.Events.EventType, id: "
|
|
186
|
+
use Chronicle.Events.EventType, id: "funds-deposited"
|
|
205
187
|
|
|
206
|
-
defstruct
|
|
188
|
+
defstruct amount: 0, active: false, title: ""
|
|
207
189
|
end
|
|
208
190
|
```
|
|
209
191
|
|
|
210
192
|
`use Chronicle.Events.EventType` takes `:id` through `Keyword.fetch!`
|
|
211
|
-
(`lib/chronicle/events/event_type.ex:
|
|
193
|
+
(`lib/chronicle/events/event_type.ex:71`) — **the id is required**, unlike the
|
|
212
194
|
.NET, Kotlin, and TypeScript clients where it defaults to the type name. Give it
|
|
213
195
|
a stable, kebab-case string and never change it. `:generation` defaults to `1`
|
|
214
|
-
(`:
|
|
196
|
+
(`:72`).
|
|
215
197
|
|
|
216
|
-
|
|
198
|
+
**Use typed defaults on events and read models.** Schemas come from struct
|
|
199
|
+
defaults: `nil` registers as string, numbers as number, booleans as boolean;
|
|
200
|
+
list element schemas use the first default element. Nested structs and concepts
|
|
201
|
+
are descended into (`schemas/json_schema_generator.ex:159-201`).
|
|
202
|
+
|
|
203
|
+
The macro imports constraint macros into your module (`:83-93`):
|
|
217
204
|
`unique/1`, `unique/2`, `unique_event_type/0`, `unique_event_type/1`,
|
|
218
|
-
`remove_constraint/1`, plus `Chronicle.Compliance.pii/1,2`
|
|
205
|
+
`remove_constraint/1`, plus `Chronicle.Compliance.pii/1,2` and
|
|
206
|
+
`Chronicle.Confidentiality.encrypted/1,2,3`. The
|
|
219
207
|
equivalent accumulating module attributes `@unique`, `@unique_event_type`,
|
|
220
208
|
`@remove_constraint` also work.
|
|
221
209
|
|
|
222
|
-
**
|
|
223
|
-
|
|
224
|
-
|
|
210
|
+
**Only top-level event keys are camelCased.** `encode_event/1` transforms them
|
|
211
|
+
before `Jason.encode!` (`event_sequences/event_log.ex:927-933`); nested keys are
|
|
212
|
+
not transformed. Event structs get no `Jason.Encoder` implementation;
|
|
213
|
+
`Chronicle.Concept` does, encoding its wrapped value rather than a wrapper.
|
|
225
214
|
|
|
226
|
-
|
|
227
|
-
> automatically. There is no `defimpl Jason.Encoder` in the macro — encoding
|
|
228
|
-
> happens in `encode_event/1`. Do not rely on the claim.
|
|
215
|
+
Read [references/compliance.md](references/compliance.md) when defining personal data, data subjects, concepts or encrypted operational secrets. `pii` and `encrypted` are not interchangeable and cannot mark the same leaf.
|
|
229
216
|
|
|
230
217
|
Schema evolution is
|
|
231
218
|
`use Chronicle.Events.Migration, from: {Mod, generation: n}, to: {Mod, generation: n + 1}`
|
|
@@ -235,10 +222,11 @@ raises at compile time.
|
|
|
235
222
|
## Appending
|
|
236
223
|
|
|
237
224
|
```elixir
|
|
238
|
-
:
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
225
|
+
case Chronicle.append("account-1", %MyApp.Events.FundsDeposited{amount: 50}) do
|
|
226
|
+
:ok -> :ok
|
|
227
|
+
{:error, {:constraint_violations, violations}} -> {:rejected, violations}
|
|
228
|
+
{:error, reason} -> {:error, reason}
|
|
229
|
+
end
|
|
242
230
|
```
|
|
243
231
|
|
|
244
232
|
`Chronicle.append/3` delegates to `Chronicle.EventSequences.EventLog`
|
|
@@ -253,46 +241,49 @@ raises at compile time.
|
|
|
253
241
|
sequence number, and `append_and_wait_for_completion/3` exists for when you need
|
|
254
242
|
the outcome.
|
|
255
243
|
|
|
256
|
-
The other append functions (`append_many/3`, `append_many_for_event_sources/2`, `append_and_wait_for_completion/3`, the `get_*` readers) and the full option list are in the reference.
|
|
257
244
|
Read [references/append-api.md](references/append-api.md) when you need an append variant, a read-back function, or an append option such as `:event_stream_type`, `:concurrency_scope` or `:tags`.
|
|
258
245
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
246
|
+
Failed appends return `{:error, reason}` (before 3.2.0 rejection could incorrectly
|
|
247
|
+
return `:ok`). Reasons include `{:constraint_violations, violations}`,
|
|
248
|
+
`{:concurrency_violations, violations}`, `{:append_errors, errors}`,
|
|
249
|
+
`:not_connected`, and `{:incompatible_server, response}` from the first append's
|
|
250
|
+
compatibility preflight per channel. Pair the kernel and installed contracts;
|
|
251
|
+
do not blindly retry a timed-out write.
|
|
265
252
|
|
|
266
253
|
**Match on them.** A bare `:ok = Chronicle.append(...)` raises a `MatchError` on
|
|
267
254
|
a constraint violation, which is a fine choice in a script and the wrong one in a
|
|
268
255
|
GenServer.
|
|
269
256
|
|
|
257
|
+
Read [references/event-sources.md](references/event-sources.md) when declaring named event sources/streams, deriving concurrency scopes or routing mixed-source batches; definitions require Chronicle 19.30.0+.
|
|
258
|
+
|
|
270
259
|
A unit of work is a process: `Chronicle.begin_unit_of_work/1` starts an `Agent`,
|
|
271
260
|
and while one is current for the calling process `append`/`append_many` buffer
|
|
272
261
|
instead of sending, until `UnitOfWork.commit/1` or `rollback/1`.
|
|
262
|
+
`commit/1` returns `:ok | {:error, reason}`. `Chronicle.with_unit_of_work(fun, opts)`
|
|
263
|
+
passes the unit to an arity-one function, commits on return, and raises on commit
|
|
264
|
+
failure. Source/stream options apply to buffered appends too.
|
|
273
265
|
|
|
274
266
|
## Observing
|
|
275
267
|
|
|
276
268
|
### Reactors
|
|
277
269
|
|
|
278
270
|
```elixir
|
|
279
|
-
defmodule MyApp.Reactors
|
|
271
|
+
defmodule MyApp.Reactors.DepositLogger do
|
|
280
272
|
use Chronicle.Reactors.Reactor
|
|
273
|
+
require Logger
|
|
281
274
|
|
|
282
|
-
alias MyApp.Events
|
|
283
|
-
|
|
284
|
-
@handles <EventName>
|
|
275
|
+
alias MyApp.Events.FundsDeposited
|
|
276
|
+
@handles FundsDeposited
|
|
285
277
|
|
|
286
278
|
@impl true
|
|
287
|
-
def handle(
|
|
288
|
-
|
|
279
|
+
def handle(%FundsDeposited{}, %{sequence_number: number}) do
|
|
280
|
+
Logger.info("Deposit observed", sequence_number: number)
|
|
289
281
|
:ok
|
|
290
282
|
end
|
|
291
283
|
end
|
|
292
284
|
```
|
|
293
285
|
|
|
294
|
-
|
|
295
|
-
(`Documentation/client-snippets/get-started/common/reactor.md`).
|
|
286
|
+
This follows `Documentation/client-snippets/get-started/common/reactor.md`.
|
|
296
287
|
|
|
297
288
|
- **`@handles` is an accumulating module attribute** declaring the subscription;
|
|
298
289
|
dispatch is then ordinary Elixir pattern matching in `handle/2`. This is the
|
|
@@ -307,36 +298,38 @@ That is the CI-validated snippet
|
|
|
307
298
|
- Optional replay callbacks: `on_replay_begin/0`, `on_replay_end/0`,
|
|
308
299
|
`on_partition_replay_begin/1`, `on_partition_replay_end/1`.
|
|
309
300
|
|
|
310
|
-
**The context
|
|
311
|
-
|
|
312
|
-
(`lib/chronicle/reactors/handler.ex:
|
|
313
|
-
|
|
301
|
+
**The reactor context has six keys**: `event_source_id`, `sequence_number`,
|
|
302
|
+
`occurred`, `event_source`, `event_store`, `namespace`
|
|
303
|
+
(`lib/chronicle/reactors/handler.ex:421-428`). `event_source` is the registered
|
|
304
|
+
source name or `nil` for legacy appends. The moduledoc mentions `:correlation_id`,
|
|
305
|
+
but **it is not in the map**. Reducer context differs: `event_source_id`,
|
|
306
|
+
`sequence_number`, `occurred`, `event_source`, `observation_state`
|
|
307
|
+
(`reducers/handler.ex:381-386`).
|
|
314
308
|
|
|
315
309
|
### Read models and projections
|
|
316
310
|
|
|
317
311
|
```elixir
|
|
318
|
-
defmodule MyApp.ReadModels
|
|
312
|
+
defmodule MyApp.ReadModels.AccountBalance do
|
|
319
313
|
use Chronicle.ReadModels.ReadModel
|
|
314
|
+
defstruct id: "", balance: 0
|
|
320
315
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
from MyApp.Events.<OtherEvent>,
|
|
327
|
-
set: [<flag>: true]
|
|
316
|
+
from(MyApp.Events.FundsDeposited,
|
|
317
|
+
set: [id: :event_source_id],
|
|
318
|
+
add: [balance: :amount]
|
|
319
|
+
)
|
|
328
320
|
end
|
|
329
321
|
```
|
|
330
322
|
|
|
331
|
-
|
|
332
|
-
(`Documentation/client-snippets/get-started/common/book-read-model.md`).
|
|
323
|
+
This is the model-bound shape; use typed defaults for its registered schema.
|
|
333
324
|
|
|
334
325
|
The DSL macros are `from/1`, `from/2`, `join/2`, `removed_with/2`, `from_every/1`.
|
|
335
326
|
`from/2` options: `:key` (defaults to `"$eventSourceId"`), `:parent_key`, `:set`,
|
|
336
|
-
`:add`, `:subtract`, `:count`.
|
|
327
|
+
`:add`, `:subtract`, `:count`. `join/2` requires `:on`.
|
|
328
|
+
|
|
329
|
+
Read [references/projections.md](references/projections.md) when choosing `passive`, `not_rewindable`, `event_sequence`, `no_auto_map`, composite/event-context keys or dictionary counters. Snake_case field atoms now map correctly; no camelCase-string workaround is needed.
|
|
337
330
|
|
|
338
331
|
> **The read model id sent to the kernel is the last module segment** —
|
|
339
|
-
> `Module.split() |> List.last()` at `lib/chronicle/read_models/read_model.ex:
|
|
332
|
+
> `Module.split() |> List.last()` at `lib/chronicle/read_models/read_model.ex:197`.
|
|
340
333
|
> `MyApp.ReadModels.Account` registers as `"Account"`, so two read models with the
|
|
341
334
|
> same final segment collide. Pass `id:` to disambiguate.
|
|
342
335
|
|
|
@@ -348,28 +341,45 @@ process**, so the reduction is Elixir code you own.
|
|
|
348
341
|
|
|
349
342
|
#### Variants — mutually exclusive read models for one entity's lifecycle
|
|
350
343
|
|
|
351
|
-
Requires `cratis_chronicle` `3.4.0` or later
|
|
344
|
+
Requires `cratis_chronicle` `3.4.0` or later; verified at `3.10.2`. `variant_of/2` requires `:key`; a variant with no `enters_on` raises `Chronicle.Projections.VariantMustDeclareEntersOnEvent` at registration.
|
|
352
345
|
|
|
353
346
|
Read [references/variants.md](references/variants.md) when an entity has mutually exclusive lifecycle shapes (for example a backlog entry that becomes a pull request) and you need variant read models or a `GlobalHandler`.
|
|
354
347
|
|
|
355
348
|
Querying (`lib/chronicle/read_models.ex`):
|
|
356
349
|
|
|
357
350
|
```elixir
|
|
358
|
-
{:ok,
|
|
359
|
-
{:ok,
|
|
351
|
+
{:ok, accounts} = Chronicle.all(MyApp.ReadModels.AccountBalance)
|
|
352
|
+
{:ok, account} = Chronicle.read_model(MyApp.ReadModels.AccountBalance, "account-1")
|
|
360
353
|
```
|
|
361
354
|
|
|
362
355
|
`Chronicle.read_model/3` delegates to `ReadModels.get/3` (`lib/chronicle.ex:211`,
|
|
363
|
-
`read_models.ex:
|
|
364
|
-
`Chronicle.all/2` delegates to `ReadModels.
|
|
365
|
-
(`lib/chronicle.ex:219`, `read_models.ex:
|
|
356
|
+
`read_models.ex:251-252`), returning `{:ok, struct() | nil} | {:error, term()}`.
|
|
357
|
+
`Chronicle.all/2` delegates to `ReadModels.all/2`
|
|
358
|
+
(`lib/chronicle.ex:219`, `read_models.ex:320-330`).
|
|
359
|
+
|
|
360
|
+
Use `Chronicle.ReadModels.query(MyApp.ReadModels.AccountBalance, page: 0)` for paging:
|
|
361
|
+
`:page` is zero-based (default `0`), `:page_size` defaults to `50`, and
|
|
362
|
+
`:occurrence` selects a replay container. Before 3.5.1, page 0 was rejected and
|
|
363
|
+
the default was 1; **page 1 now means the second page**. `ReadModels.snapshots/3`,
|
|
364
|
+
`occurrences/2`, and `definitions/1` expose history, replay containers, and
|
|
365
|
+
registered definitions.
|
|
366
366
|
|
|
367
367
|
**Live updates arrive as messages, not as a stream.** `ReadModels.watch/2`
|
|
368
|
-
(`read_models.ex:
|
|
368
|
+
(`read_models.ex:608`) sends
|
|
369
369
|
`{:chronicle_read_model_changed, module, %Changeset{}}` and
|
|
370
|
-
`{:chronicle_read_model_watch_error, module, reason}` to the calling process.
|
|
371
|
-
|
|
372
|
-
|
|
370
|
+
`{:chronicle_read_model_watch_error, module, reason}` to the calling process. The receiving process needs a `handle_info` for both.
|
|
371
|
+
|
|
372
|
+
## Operational inspection
|
|
373
|
+
|
|
374
|
+
`Chronicle.Observers.get_all/1` lists namespace observers; `remove/2` removes an
|
|
375
|
+
orphaned observer across the store, refuses running/subscribed observers in any
|
|
376
|
+
namespace, and leaves read model data intact (`observers.ex:84,120`). Obtain
|
|
377
|
+
explicit authority before removal and inspect `RemovalResult.outcome` —
|
|
378
|
+
`{:ok, result}` alone does not mean removed. `Chronicle.FailedPartitions` and
|
|
379
|
+
`Chronicle.Jobs` provide failure/job inspection; `Jobs.get/2` lists then finds
|
|
380
|
+
client-side. Observer types include `:reactor`, `:projection`, `:reducer`,
|
|
381
|
+
`:external`; states include `:active`, `:suspended`, `:replaying`, `:disconnected`,
|
|
382
|
+
`:quarantined` and `:unknown`.
|
|
373
383
|
|
|
374
384
|
## Idioms worth knowing
|
|
375
385
|
|
|
@@ -382,8 +392,8 @@ receiving process must have a `handle_info` for both.
|
|
|
382
392
|
- **Discovery is compile-time reflection.** The `use` macros generate
|
|
383
393
|
`__chronicle_*__/1` functions via `__before_compile__`, and discovery is
|
|
384
394
|
`function_exported?/3` over the application's modules — not runtime scanning.
|
|
385
|
-
- **No
|
|
386
|
-
|
|
395
|
+
- **No client-owned telemetry events.** `telemetry` is a direct dependency;
|
|
396
|
+
transport failure detection subscribes to grpc events. Logging is `Logger`.
|
|
387
397
|
- **No formatter export.** `.formatter.exs` declares no `locals_without_parens`
|
|
388
398
|
for `from`, `join`, `removed_with`, `from_every`, `pii`, `subject`, `unique`, or
|
|
389
399
|
`unique_event_type`, and there is no `import_deps: [:cratis_chronicle]` to
|
|
@@ -393,28 +403,13 @@ receiving process must have a `handle_info` for both.
|
|
|
393
403
|
|
|
394
404
|
## Common pitfalls
|
|
395
405
|
|
|
396
|
-
|
|
397
|
-
| --- | --- |
|
|
398
|
-
| Copying the `v2.2.0` root `README.md` | Its module names do not exist; the `3.x` README is correct |
|
|
399
|
-
| Using `Chronicle.Concept` on `2.2.0` | It ships from `3.0.0`; on `2.2.0` annotate fields with `pii/1,2` instead |
|
|
400
|
-
| Copying the dependency line from the README | It lags the published version; take the version from hex.pm |
|
|
401
|
-
| Quoting the `VERSION` file | It is a `0.0.5` build-time placeholder |
|
|
402
|
-
| Putting configuration in `config.exs` | The client reads no application env; options are child-spec keywords |
|
|
403
|
-
| Omitting `:otp_app` | Discovery falls back to scanning every loaded module |
|
|
404
|
-
| Assuming the namespace default is `"default"` | It is `"Default"`; the moduledoc is wrong |
|
|
405
|
-
| `:ok = Chronicle.append(...)` in a server | A constraint violation returns `{:error, _}` and raises `MatchError` |
|
|
406
|
-
| Waiting for `:connected` | Observers attach at `:registered` |
|
|
407
|
-
| Reading `:correlation_id` from a reactor context | The map has five keys and that is not one |
|
|
408
|
-
| Two read models with the same final module segment | The id is the last segment only |
|
|
409
|
-
| Appending from a `Task` and expecting the correlation id | Ambient context is per process |
|
|
410
|
-
| Treating quiet observers as "no events" | Keepalive eviction silences observers while appends still succeed |
|
|
411
|
-
| Shipping the default TLS behavior | `skip_tls_validation` defaults to `true` |
|
|
412
|
-
| Forgetting `global_handlers:` or `:otp_app` discovery | A `GlobalHandler` module is never merged into its variants unless registered one way or the other |
|
|
413
|
-
| A shared handler mapping a field one variant lacks | `GlobalHandlerPropertyNotOnVariant` at registration, not a silently skipped mapping |
|
|
406
|
+
Read [references/pitfalls.md](references/pitfalls.md) when troubleshooting discovery, defaults, process context, variant registration or older-version examples.
|
|
414
407
|
|
|
415
408
|
## Verify
|
|
416
409
|
|
|
417
|
-
- `mix deps.get`
|
|
410
|
+
- Elixir 1.18+ and `mix deps.get` resolve the intended `3.x` client; contracts
|
|
411
|
+
and kernel are compatible (event source definitions need kernel 19.30.0+).
|
|
412
|
+
- Local development uses `ConnectionString.development()` or explicit credentials.
|
|
418
413
|
- `Chronicle.Client` is in the supervision tree with an explicit `:otp_app`, and
|
|
419
414
|
the tree starts clean.
|
|
420
415
|
- The lifecycle reaches `:registered` before the application claims readiness.
|
|
@@ -425,6 +420,7 @@ receiving process must have a `handle_info` for both.
|
|
|
425
420
|
- Read model module names have distinct final segments, or explicit ids.
|
|
426
421
|
- Every variant group has at least one `enters_on` per variant, and every
|
|
427
422
|
`GlobalHandler` member exists on every variant it targets.
|
|
428
|
-
-
|
|
429
|
-
the
|
|
423
|
+
- Production logging filters credential-bearing gRPC/Mint crash reports; see
|
|
424
|
+
the connections reference for limits.
|
|
425
|
+
- Examples match the APIs and schema defaults at the installed tag.
|
|
430
426
|
- `mix compile --warnings-as-errors` and `mix test` are clean.
|
|
@@ -1,19 +1,40 @@
|
|
|
1
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
2
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
3
|
+
|
|
1
4
|
# Append API
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
Verified at `Cratis/Chronicle.Elixir v3.10.2`; paths below are under `Source/chronicle/lib/chronicle/`.
|
|
7
|
+
|
|
8
|
+
## Append functions and readers
|
|
4
9
|
|
|
5
|
-
| Function |
|
|
10
|
+
| Function on `Chronicle.EventSequences.EventLog` | Arguments / result | Line in `event_log.ex` |
|
|
6
11
|
| --- | --- | --- |
|
|
7
|
-
| `append/3` | `(event_source_id, event, opts)` | `:
|
|
8
|
-
| `append_many/3` | `(event_source_id, events, opts)` | `:
|
|
9
|
-
| `append_many_for_event_sources/2` | `(
|
|
10
|
-
| `append_and_wait_for_completion/3` |
|
|
11
|
-
| `get_for_event_source/2` | | `:
|
|
12
|
-
| `get_from_sequence_number/2` | | `:
|
|
13
|
-
| `get_tail_sequence_number/2` | | `:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
`
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
| `append/3` | `(event_source_id, event, opts)` → `:ok \| {:error, reason}` | `:126` |
|
|
13
|
+
| `append_many/3` | `(event_source_id, events, opts)` → `:ok \| {:error, reason}` | `:147` |
|
|
14
|
+
| `append_many_for_event_sources/2` | `(EventForEventSourceId entries, opts)` → `:ok \| {:error, reason}` | `:184` |
|
|
15
|
+
| `append_and_wait_for_completion/3` | `(event_source_id, event, opts)` → `{:ok, %{success: _, failed_partitions: _}} \| {:error, reason}` | `:264` |
|
|
16
|
+
| `get_for_event_source/2` | `(event_source_id, opts)` | `:402` |
|
|
17
|
+
| `get_from_sequence_number/2` | `(sequence_number, opts)` | `:445` |
|
|
18
|
+
| `get_tail_sequence_number/2` | `(event_source_id_or_nil, opts)` | `:484` |
|
|
19
|
+
| `get_next_sequence_number/2` | `(event_source_id_or_nil, opts)` | `:502` |
|
|
20
|
+
| `get_tail_sequence_number_for_observer/2` | `(observer_module, opts)` | `:523` |
|
|
21
|
+
| `complete_stream/3` | `(event_stream_type, event_stream_id, opts)` | `:548` |
|
|
22
|
+
| `redact/3` | `(sequence_number, reason, opts)` | `:587` |
|
|
23
|
+
| `redact_for_event_source/4` | `(event_source_id, reason, event_types, opts)` | `:622` |
|
|
24
|
+
| `has_events_for?/2` | `(event_source_id, opts)` | `:658` |
|
|
25
|
+
|
|
26
|
+
Read operations observe state; completion and redaction mutate it and require authority for the specific target. `append_many` variants are atomic batches. The cross-source variant uses entries for per-event routing/metadata and accepts batch `:client`, `:namespace`, `:event_sequence_id` and source/stream defaults.
|
|
27
|
+
|
|
28
|
+
## Options
|
|
29
|
+
|
|
30
|
+
Append options (`event_log.ex:79-113`): `:client`, `:namespace`, `:event_sequence_id` (default `"event-log"`), `:event_source_type`, `:event_stream_type`, `:event_stream_id`, `:event_source`, `:event_stream`, `:tags`, `:subject`, `:correlation_id`, `:identity`, `:causation`, `:concurrency_scope`, `:occurred`.
|
|
31
|
+
|
|
32
|
+
Omitted, `nil`, or empty routing types/ids travel as empty strings; the **kernel**, not client-side defaults, resolves them to `"Default"` / `"All"` / `"Default"`. Nonempty values are exact. Concurrency filters remain independent of routing.
|
|
33
|
+
|
|
34
|
+
Read [event-sources.md](event-sources.md) for definitions, stream routing and derived guards. Definition concurrency applies only without an explicit scope. Incompatible derived guards for one id fail with `{:error, {:incompatible_concurrency_scopes, id, guards}}` before sending.
|
|
35
|
+
|
|
36
|
+
## Errors and completion
|
|
37
|
+
|
|
38
|
+
`append_response.ex:65-83` returns `{:error, {:constraint_violations, violations}}`, `{:error, {:concurrency_violations, violations}}` or `{:error, {:append_errors, errors}}` on rejection. Channel/preflight errors include `{:error, :not_connected}` and `{:error, {:incompatible_server, response}}`. A transport timeout does not prove that no event was stored.
|
|
39
|
+
|
|
40
|
+
`append_and_wait_for_completion/3` has **no many-event variant**. `{:ok, result}` does not imply every observer succeeded: inspect `result.success` and `failed_partitions`. Its wait happens **after** the append (`event_log.ex:267-275`), so `{:error, reason}` can follow a stored event if waiting fails. Do not retry the write blindly; reconcile the stored outcome first.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
2
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
3
|
+
|
|
4
|
+
# Personal data, concepts and operational secrets
|
|
5
|
+
|
|
6
|
+
Verified at `Cratis/Chronicle.Elixir v3.10.2`: `Source/chronicle/lib/chronicle/{concept,compliance,confidentiality}.ex`, `events/event_type.ex:92-93`, and `read_models/read_model.ex`.
|
|
7
|
+
|
|
8
|
+
`pii/1,2` marks personal data for compliance encryption and right-to-erasure. `encrypted/1,2,3` (since 3.5.0) marks non-personal operational secrets for separate confidentiality encryption with no compliance erasure operation. Its scopes are `:subject` (default), `:namespace`, `:global`. They are not interchangeable.
|
|
9
|
+
|
|
10
|
+
```elixir
|
|
11
|
+
# Copyright (c) Cratis. All rights reserved.
|
|
12
|
+
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
13
|
+
|
|
14
|
+
defmodule MyApp.PersonName do
|
|
15
|
+
use Chronicle.Concept, type: :string
|
|
16
|
+
pii("Legal name of the data subject")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
defmodule MyApp.Events.PartnerIntegrationConfigured do
|
|
20
|
+
use Chronicle.Events.EventType, id: "partner-integration-configured"
|
|
21
|
+
defstruct contact_name: %MyApp.PersonName{}, email: "", api_key: ""
|
|
22
|
+
|
|
23
|
+
pii(:email, "Partner contact's email address")
|
|
24
|
+
encrypted(:api_key, :subject, "Partner API key")
|
|
25
|
+
end
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`Chronicle.Concept` ships on Hex from **3.0.0**, not 2.2.0 (the 2.3.0 tag was unpublished). `type:` is required: `:string`, `:integer`, `:float`, `:boolean`, `:uuid`. Typed concept defaults let the schema generator carry the wrapped type and classification onto every leaf that uses it. The concept's `Jason.Encoder` serializes only its wrapped value.
|
|
29
|
+
|
|
30
|
+
On a concept use `pii/0,1` to classify its sole value, not the field-oriented `Chronicle.Compliance.pii/1,2`. Concept `encrypted/0,1,2` similarly marks that value. Neither classification is allowed on a concept with `event_source_id: true`; use a non-sensitive surrogate stream identity instead. Both classifications on one schema leaf raise `Chronicle.Confidentiality.PiiAndEncryptedCombinedNotSupported`.
|
|
31
|
+
|
|
32
|
+
For a read model whose subject is not its `:id`, declare `subject(:customer_id)` in the module using `Chronicle.ReadModels.ReadModel`. It identifies the natural person for PII release; there is only one subject declaration per module. `subject/1` is not imported by `EventType`; append-time `:subject` is an append option, not this read-model macro.
|