@cratis/pi 7.34.1 → 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/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-command-surface/SKILL.md +5 -6
- package/package/corpus/skills/cratis-screenplay-command-surface/references/compliance.md +24 -6
- 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 +3 -2
- 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 +1 -0
- 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-render-and-gap-fill/references/gap-fill-handoff.md +3 -1
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/SKILL.md +3 -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 +4 -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 +5 -2
- 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 +8 -2
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +6 -3
- 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 +5 -0
- 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
|
@@ -0,0 +1,114 @@
|
|
|
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
|
+
# Inline command operations and aggregate decisions
|
|
5
|
+
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- Several business outcomes
|
|
9
|
+
- Declare work, do not execute it twice
|
|
10
|
+
- Recovery follows commit facts
|
|
11
|
+
- Chronicle aggregates (experimental)
|
|
12
|
+
|
|
13
|
+
Verified at `Cratis/Arc.TypeScript` **v0.57.2** (source preview, not on npm):
|
|
14
|
+
`Source/Core/commands/CommandOperation.ts`, operation execution/recovery code,
|
|
15
|
+
`Source/Chronicle/ChronicleCommandScope.ts`, `commandAggregate.ts`,
|
|
16
|
+
`Documentation/commands/operations/reference.md`, and
|
|
17
|
+
`Documentation/chronicle/aggregates/**`.
|
|
18
|
+
|
|
19
|
+
## Several business outcomes
|
|
20
|
+
|
|
21
|
+
Proxy generation accepts alternatives resolving to the same client type:
|
|
22
|
+
literal unions retain their type; distinct concepts may fall back to their shared
|
|
23
|
+
primitive. Different client types, even identical-shaped DTO classes, fail
|
|
24
|
+
generation: use one response DTO with a status field. A .NET-style arbitrary
|
|
25
|
+
Result error DTO is ordinary response data, not rejection; use `response(...)`
|
|
26
|
+
for data and `rejected(...)`/`denied(...)` for failures.
|
|
27
|
+
|
|
28
|
+
## Declare work, do not execute it twice
|
|
29
|
+
|
|
30
|
+
Extend `CommandOperation` from `@cratis/arc.core`. Return an instance from
|
|
31
|
+
`handle()`; a direct call only returns the declaration. Arc executes it in the
|
|
32
|
+
pipeline, never sends it to the client. Use `operations(first, second)` for an
|
|
33
|
+
explicit sequential batch — a plain array is not a batch. Combine with one
|
|
34
|
+
client response using `tuple(response(value), operation)`.
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
38
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
39
|
+
|
|
40
|
+
import { CommandOperation, command, response, tuple } from '@cratis/arc.core';
|
|
41
|
+
|
|
42
|
+
// In-memory demonstration, not a durable external-effect implementation.
|
|
43
|
+
const calls: string[] = [];
|
|
44
|
+
class Notify extends CommandOperation {
|
|
45
|
+
execute(signal: AbortSignal): void {
|
|
46
|
+
signal.throwIfAborted();
|
|
47
|
+
calls.push('notify');
|
|
48
|
+
}
|
|
49
|
+
compensate(_failure: unknown, signal: AbortSignal): void {
|
|
50
|
+
signal.throwIfAborted();
|
|
51
|
+
calls.push('undo notification');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@command()
|
|
56
|
+
export class SendNotification {
|
|
57
|
+
handle() { return tuple(response('declared'), new Notify()); }
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`executeDependencies` / `compensateDependencies` are service-token arrays;
|
|
62
|
+
Arc resolves dependencies before starting any operation and passes them after
|
|
63
|
+
the signal (after `failure, signal` for compensation). Do not call the same write
|
|
64
|
+
in `handle()` as well as returning the operation.
|
|
65
|
+
|
|
66
|
+
## Recovery follows commit facts
|
|
67
|
+
|
|
68
|
+
Arc records entry **before** calling execute, completes scopes before deciding
|
|
69
|
+
recovery, and compensates entered operations in reverse order, including one
|
|
70
|
+
that threw. The separate cooperative `commandCompensationTimeoutMs` budget
|
|
71
|
+
(default 30,000 ms) is shared by compensations, not the cancelled request signal.
|
|
72
|
+
A callback ignoring its signal cannot be forcibly stopped; late completion is
|
|
73
|
+
`BudgetExpired`.
|
|
74
|
+
|
|
75
|
+
| `CommandCommitDisposition` | Compensation after failure |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `NoCommit`, `NotCommitted` | Attempt |
|
|
78
|
+
| `Committed` | Suppress |
|
|
79
|
+
| `Unknown`, `Mixed` | Do not guess: indeterminate recovery, not success |
|
|
80
|
+
|
|
81
|
+
Direct-call `result.recovery` / `operationOutcomes` describe execution and
|
|
82
|
+
recovery; they are not serialized to HTTP. `CommandScenario` results offer
|
|
83
|
+
`shouldHaveExecutedOperation(Type)`, `shouldHaveCompensatedOperation(Type)`,
|
|
84
|
+
`shouldHaveNoOperationInvocations()`, and `shouldHaveIndeterminateRecovery()`.
|
|
85
|
+
Specify success, throwing/partially-entered operations, cancellation, compensation
|
|
86
|
+
failure/budget expiry, and unknown commit facts.
|
|
87
|
+
|
|
88
|
+
With Chronicle, `tuple(event, operation)` stages the event, executes the operation,
|
|
89
|
+
then completes the append scope before deciding compensation. Known append
|
|
90
|
+
rejection compensates; unknown/partial acknowledgment does not claim rollback.
|
|
91
|
+
The tagged `Documentation/commands/operations/index.md` still says events and
|
|
92
|
+
operations cannot combine; `ChronicleCommandScope.ts` and the live integration
|
|
93
|
+
specs establish the supported combination at this tag.
|
|
94
|
+
|
|
95
|
+
There is no distributed transaction, automatic retry, durable recovery journal
|
|
96
|
+
or crash recovery. Compensation success is not proof an external effect was
|
|
97
|
+
undone. After-commit durable work belongs in an idempotent reactor/outbox/workflow.
|
|
98
|
+
|
|
99
|
+
## Chronicle aggregates (experimental)
|
|
100
|
+
|
|
101
|
+
`AggregateRoot` and `commandAggregate` come from `@cratis/arc.chronicle`.
|
|
102
|
+
Bind `@inject(commandAggregate(Order))` to an `Order` extending AggregateRoot.
|
|
103
|
+
Arc rehydrates the command's **explicit key**, in the tenant namespace and the
|
|
104
|
+
command's source/stream route. No key fails before handle; loading another id is
|
|
105
|
+
not supported. Each aggregate type loads once per command.
|
|
106
|
+
|
|
107
|
+
`apply(event)` stages events in the command's batch; a successful command appends
|
|
108
|
+
them even when handle returns nothing. `return order.commit()` is optional;
|
|
109
|
+
never return the same event again. Rejection discards staged events. The route's
|
|
110
|
+
tail is captured at load and appended as an exact concurrency scope; another
|
|
111
|
+
append to the route rejects the entire batch. Projection reads may lag this
|
|
112
|
+
rehydrated state. Use ChronicleKernelScenario for aggregate/concurrency specs,
|
|
113
|
+
not the in-memory scenario. Registered event-source-definition routing for Arc
|
|
114
|
+
commands/aggregates is **not** part of v0.57.2.
|
package/package/corpus/skills/cratis-arc-command-typescript/references/hosting-and-storage.md
CHANGED
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
- Observable-query WebSockets from a dev server
|
|
11
11
|
- Storage integrations
|
|
12
12
|
|
|
13
|
-
Verified against `Cratis/Arc.TypeScript`
|
|
14
|
-
`v0.33.0` plus three commits): `Documentation/overview.md`, `hosts/**`,
|
|
13
|
+
Verified against `Cratis/Arc.TypeScript` tag **v0.57.2** (GitHub pre-release): `Documentation/overview.md`, `hosts/**`,
|
|
15
14
|
`mongodb/getting-started.md`, `sql/getting-started.md`,
|
|
16
15
|
`chronicle/registration-options.md`, and the `Express`, `Fastify`, `Hono`,
|
|
17
16
|
`MongoDB`, `Drizzle` and `Chronicle` package exports. None of these packages is
|
|
@@ -48,7 +47,14 @@ export const arc = await builder.build();
|
|
|
48
47
|
Every adapter dispatches only exact raw-path matches on a fixed internal origin
|
|
49
48
|
(a crafted `Host` header cannot select an operation), hands Arc the unparsed
|
|
50
49
|
body, and passes a cancellation signal that becomes `context.signal`. The
|
|
51
|
-
framework owns its listener
|
|
50
|
+
framework owns its listener. With shutdown participants, coordinate listener and
|
|
51
|
+
service shutdown instead of closing first: `host.shutdown({ timeoutMs, registry? })`
|
|
52
|
+
for the standalone host, `middleware.shutdown(listener, registry?)` for Express,
|
|
53
|
+
or `shutdownArcHost(server, closeListener, registry?)` from
|
|
54
|
+
`@cratis/arc.core/hosting`. `close()` remains listener-only. Register participants
|
|
55
|
+
with `server.services.addShutdownParticipant({ stop, drain })`; `app.stop()` /
|
|
56
|
+
`dispose()` coordinate the same order when Arc owns the registry. Repeated
|
|
57
|
+
`server.dispose()` preserves the first outcome.
|
|
52
58
|
|
|
53
59
|
## Express
|
|
54
60
|
|
|
@@ -65,10 +71,7 @@ const listener = app.listen(3000, '127.0.0.1');
|
|
|
65
71
|
middleware.injectWebSocket(listener);
|
|
66
72
|
|
|
67
73
|
process.once('SIGTERM', () => {
|
|
68
|
-
void (
|
|
69
|
-
try { await middleware.close(listener); }
|
|
70
|
-
finally { await arc.dispose(); }
|
|
71
|
-
})();
|
|
74
|
+
void middleware.shutdown(listener);
|
|
72
75
|
});
|
|
73
76
|
```
|
|
74
77
|
|
|
@@ -86,12 +89,13 @@ process.once('SIGTERM', () => {
|
|
|
86
89
|
```typescript
|
|
87
90
|
import Fastify from 'fastify';
|
|
88
91
|
import cratisArc from '@cratis/arc.fastify';
|
|
92
|
+
import { shutdownArcHost } from '@cratis/arc.core/hosting';
|
|
89
93
|
import { arc } from './arc.js';
|
|
90
94
|
|
|
91
95
|
const app = Fastify();
|
|
92
96
|
await app.register(cratisArc, { arc });
|
|
93
97
|
await app.listen({ port: 3000, host: '127.0.0.1' });
|
|
94
|
-
process.once('SIGTERM', () => { void
|
|
98
|
+
process.once('SIGTERM', () => { void shutdownArcHost(arc.server, () => app.close()); });
|
|
95
99
|
```
|
|
96
100
|
|
|
97
101
|
- The plugin is encapsulated, with its own catch-all raw-body parser; your
|
|
@@ -107,13 +111,14 @@ process.once('SIGTERM', () => { void app.close().then(() => arc.dispose()); });
|
|
|
107
111
|
```typescript
|
|
108
112
|
import { Hono } from 'hono';
|
|
109
113
|
import { cratisArc, serveCratisArc } from '@cratis/arc.hono';
|
|
114
|
+
import { shutdownArcHost } from '@cratis/arc.core/hosting';
|
|
110
115
|
import { arc } from './arc.js';
|
|
111
116
|
|
|
112
117
|
const app = new Hono();
|
|
113
118
|
app.use(cratisArc(arc));
|
|
114
119
|
app.get('/health', context => context.text('ok'));
|
|
115
120
|
const hosted = await serveCratisArc(app, arc, { port: 3000, hostname: '127.0.0.1' });
|
|
116
|
-
process.once('SIGTERM', () => { void
|
|
121
|
+
process.once('SIGTERM', () => { void shutdownArcHost(arc.server, () => hosted.dispose()); });
|
|
117
122
|
```
|
|
118
123
|
|
|
119
124
|
- Running on Node needs the optional `@hono/node-server`. There the adapter also
|
|
@@ -143,9 +148,11 @@ export const app = await builder.build();
|
|
|
143
148
|
Import `command`, `query`, `readModel` from `@cratis/arc.core/fetch` in artifacts
|
|
144
149
|
served this way. Arc targets **Node-compatible** servers: Chronicle uses gRPC,
|
|
145
150
|
MongoDB uses TCP, and live queries need long-lived SSE/WebSocket connections.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
151
|
+
The Fetch entry supports Next.js App Router route handlers on the **Node.js**
|
|
152
|
+
runtime; Bun and Deno have smoke checks. Cloudflare Workers and Next.js Edge
|
|
153
|
+
are unsupported. Its only Node import is `node:async_hooks`; that does not make
|
|
154
|
+
TCP/gRPC integrations portable to an edge host. The host owns ingress limits,
|
|
155
|
+
trusted principal metadata, and cancelling long-lived streams on disconnect.
|
|
149
156
|
|
|
150
157
|
## Observable-query WebSockets from a dev server
|
|
151
158
|
|
|
@@ -164,18 +171,23 @@ adds a builder method when imported; the exported function form is equivalent.
|
|
|
164
171
|
| Integration | Register | Inject |
|
|
165
172
|
| --- | --- | --- |
|
|
166
173
|
| MongoDB (`@cratis/arc.mongodb`, peer `mongodb` `^6.21.0`) | `import '@cratis/arc.mongodb'; builder.withMongoDB({ client, databaseNameResolver, readModels: [TaskRecord] })` or `withMongoDB(builder, options)` | `service(mongoCollection(TaskRecord))` in `@query`, or `@inject(mongoCollection(TaskRecord))`; `commandReadModel(TaskRecord)` in commands |
|
|
167
|
-
| SQL through Drizzle (`@cratis/arc.drizzle`,
|
|
168
|
-
| Chronicle (`@cratis/arc.chronicle`, experimental, peer `@cratis/chronicle` `^6.
|
|
174
|
+
| SQL through Drizzle (`@cratis/arc.drizzle`, peers `drizzle-orm` `^0.45.0` and `rxjs` `^7.8.2`) | `import '@cratis/arc.drizzle'; builder.withDrizzle({ dialect: DrizzleDialect.SQLite, database, readModels: [{ type: TaskRecord, table: tasks }] })` | `service(drizzleReadModel(TaskRecord))` for read-only query handles; `@inject(drizzleDatabase<SQLJsDatabase>())` for a writable handle in commands; `commandReadModel(TaskRecord)` |
|
|
175
|
+
| Chronicle (`@cratis/arc.chronicle`, experimental, peer `@cratis/chronicle` `^6.29.0`) | `import '@cratis/arc.chronicle'; builder.withChronicle({ connectionString, eventStore })` or `withChronicle(builder, options)` | Commands return events; queries take `service(ChronicleReadModels)`; `commandReadModel(Type)` |
|
|
169
176
|
|
|
170
177
|
- **MongoDB**: list every injected model in `readModels`; a model left out has
|
|
171
178
|
no collection token. `@key()` marks the field stored as `_id` (otherwise a
|
|
172
179
|
field named `id`). Only `server` and `database` bind from `Cratis:MongoDB`
|
|
173
|
-
configuration. Change-stream observation needs a replica set.
|
|
180
|
+
configuration. Change-stream observation needs a replica set. Protected raw
|
|
181
|
+
documents need typed `MongoReadModels` with a Chronicle-registered `readModel`
|
|
182
|
+
(optional `subjectFor`); untyped reads remain ciphertext. Read
|
|
183
|
+
[the observation/compliance reference](../../cratis-arc-query-typescript/references/storage-observation-and-compliance.md)
|
|
184
|
+
before exposing protected documents.
|
|
174
185
|
- **Drizzle**: `withDrizzle` never creates or changes tables — own the schema
|
|
175
186
|
with migrations. Every SQL access needs a tenant (`A tenant is required for
|
|
176
187
|
Drizzle access` otherwise); a single-database setup resolves one fixed tenant.
|
|
177
188
|
Command read-model injection needs exactly one column-level `.primaryKey()`
|
|
178
|
-
declared as an Arc `@field`.
|
|
189
|
+
declared as an Arc `@field`. Observation is opt-in; the query skill's
|
|
190
|
+
storage reference covers in-process announcements and PostgreSQLNotify.
|
|
179
191
|
- **Chronicle**: `withChronicle` needs `eventStore` and exactly one of
|
|
180
192
|
`connectionString` or `client` (`Chronicle requires eventStore and exactly one
|
|
181
193
|
of connectionString or client`). It reads `Cratis:Chronicle` `connectionString`
|
|
@@ -187,6 +199,15 @@ adds a builder method when imported; the exported function form is equivalent.
|
|
|
187
199
|
reachable makes Chronicle-backed requests wait, not fail.
|
|
188
200
|
`chronicle://localhost:35000` without credentials uses the development client —
|
|
189
201
|
local kernels only.
|
|
202
|
+
- **MongoDB + Chronicle naming**: Arc-owned `withChronicle` stores projected
|
|
203
|
+
models in the collection `withMongoDB` reads (pluralized by default), in either
|
|
204
|
+
registration order. The former `collectionName: type => type.name` workaround
|
|
205
|
+
is no longer needed. A throwing/empty collection-name callback fails
|
|
206
|
+
registration. Override with `withChronicle({ ..., readModelNamingPolicy })`;
|
|
207
|
+
with a caller-owned client set it in that client's `ChronicleOptions` instead.
|
|
208
|
+
Existing deployments must replay projections into the aligned collection;
|
|
209
|
+
the old collection is left intact. Core exports `readModelCollectionNameResolver`,
|
|
210
|
+
MongoDB exports `resolveMongoCollectionName`.
|
|
190
211
|
- `@cratis/cratis` (experimental) registers Arc and Chronicle together through
|
|
191
212
|
`CratisApplication.createBuilder()` and `builder.addCratis(...)`. Unlike C#
|
|
192
213
|
`AddCratis`, it installs no authentication handler; protected routes still
|
package/package/corpus/skills/cratis-arc-command-typescript/references/identity-and-introspection.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
# Identity, authorization and discovery access
|
|
5
|
+
|
|
6
|
+
Verified at `Cratis/Arc.TypeScript` **v0.57.2** (GitHub pre-release, not on npm):
|
|
7
|
+
`Source/Core/authentication/**`, `identity/**`, `introspection/**`,
|
|
8
|
+
`Documentation/core/authentication.md`, `authorization.md`,
|
|
9
|
+
`Documentation/identity/**`, `hosts/native-principal.md`, and
|
|
10
|
+
`upgrading/secure-defaults.md`. This is the **TypeScript server** contract,
|
|
11
|
+
not the .NET authentication skill.
|
|
12
|
+
|
|
13
|
+
## Authenticate at one trusted boundary
|
|
14
|
+
|
|
15
|
+
| Boundary | Builder options | Trust requirement |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| Arc verifies bearer JWTs | `authentication: [jwtBearer({ jwksUrl, issuer, audience, algorithms })]` | HTTPS JWKS URL; pin issuer, API audience and asymmetric algorithms; validate API tokens, not an ID token |
|
|
18
|
+
| Host middleware already verified identity | `nativePrincipal: true`, plus the adapter's verified native-context callback | Bridge HTTP and WebSocket upgrades explicitly; a raw request/header is not a principal |
|
|
19
|
+
| EasyAuth/AuthProxy ingress | `authentication: [microsoftIdentityPlatform()]` | Strip client `x-ms-client-principal*` headers, set trusted ones and block direct backend access |
|
|
20
|
+
| Application credential check | `authentication: [handler]` | Implement `AuthenticationHandler`; do not trust the display cookie |
|
|
21
|
+
|
|
22
|
+
`nativePrincipal` and Arc authentication handlers are mutually exclusive.
|
|
23
|
+
Handlers return `AuthenticationStatus.Anonymous` (try next), `Authenticated`
|
|
24
|
+
(with principal), or `Failed` (401 even for an anonymous operation). Principals
|
|
25
|
+
require authenticated status, string `id`, string-array `roles`; name, claims
|
|
26
|
+
and scheme are optional. Arc freezes the principal, roles and own claims.
|
|
27
|
+
With no handler, protected operations answer 403 rather than authenticating.
|
|
28
|
+
|
|
29
|
+
For Entra bearer tokens, verify against the tenant-specific issuer and API
|
|
30
|
+
audience. App roles and delegated scopes are different; require the one the API
|
|
31
|
+
actually uses. Do not put bearer tokens in query-string URLs. Native identity
|
|
32
|
+
must also be provided on upgrades/SSE; configuring HTTP middleware alone does
|
|
33
|
+
not authenticate a WebSocket upgrade. Named schemes belong in
|
|
34
|
+
`authenticationSchemes: { Verified: handler }` and are selected with
|
|
35
|
+
`@authorize({ schemes: ['Verified'] })`.
|
|
36
|
+
|
|
37
|
+
## Authorize the operation, not the UI
|
|
38
|
+
|
|
39
|
+
With no decorator, anonymous callers are permitted. `@authorize()` requires authentication;
|
|
40
|
+
`@authorize({ policy, roles, schemes })` adds requirements, and `@roles('A', 'B')`
|
|
41
|
+
accepts any listed role. Stacked requirements all pass. `@allowAnonymous()` cannot
|
|
42
|
+
coexist with an authenticated requirement on the same declaration. Commands
|
|
43
|
+
carry decorators on the **class**, never `handle`/`provide`; query methods may
|
|
44
|
+
override the read-model class's declaration. Policies and row filters use the
|
|
45
|
+
verified principal, not an input role/tenant or a hidden button.
|
|
46
|
+
Observable authorization runs at subscription opening; changing a role later
|
|
47
|
+
needs an emission guard to stop further disclosure.
|
|
48
|
+
|
|
49
|
+
## Supply identity details
|
|
50
|
+
|
|
51
|
+
`@identityDetailsProvider()` on an exported provider lets discovery register it;
|
|
52
|
+
`builder.add(Provider)` is also supported. Implement `IdentityDetailsProvider`
|
|
53
|
+
with `detailsType` (a `@field` model) or `schema` and
|
|
54
|
+
`provide(principal, context)`. Returning undefined denies the authenticated
|
|
55
|
+
caller; output must match the schema.
|
|
56
|
+
|
|
57
|
+
`GET /.cratis/me` serves principal id/name/roles plus provider `details` and sets
|
|
58
|
+
`.cratis-identity`. This unsigned, JavaScript-readable cookie is **display data,
|
|
59
|
+
never a credential**; provider details never flow into authorization. The
|
|
60
|
+
published React identity hooks can display it, but server protection still
|
|
61
|
+
belongs on every operation. Local users/tenants are development fixtures, not
|
|
62
|
+
production credentials; keep simulated identities on loopback hosts.
|
|
63
|
+
|
|
64
|
+
## Secure introspection defaults (0.56.0+)
|
|
65
|
+
|
|
66
|
+
`/.cratis/commands`, `/.cratis/queries`, `/.cratis/identity-details/schema`,
|
|
67
|
+
`/.cratis/users`, `/.cratis/tenants`, and `/openapi.json` are anonymous only in
|
|
68
|
+
Development by default. Elsewhere they require an authenticated principal from
|
|
69
|
+
Arc handlers or the native host. Without authentication, protected discovery is
|
|
70
|
+
unmapped (normally 404) with a startup warning. Explicitly requiring auth with
|
|
71
|
+
none configured fails startup.
|
|
72
|
+
|
|
73
|
+
- `introspection.requireAuthentication: false` deliberately restores anonymous
|
|
74
|
+
discovery; outside Development it logs a warning. Prefer authenticating tools.
|
|
75
|
+
- `introspection.roles: 'Admin,Operator'` accepts any listed role, implies auth,
|
|
76
|
+
and cannot combine with `requireAuthentication: false`; empty roles fail.
|
|
77
|
+
- `introspection.enabled: false` unmaps **catalogs and HTTP OpenAPI**, not identity
|
|
78
|
+
discovery. Its access policy/diagnostics and in-process metadata remain.
|
|
79
|
+
- Node config: `Cratis:Arc:Introspection:Enabled/RequireAuthentication/Roles` or
|
|
80
|
+
`Cratis__Arc__Introspection__Enabled/RequireAuthentication/Roles` environment keys.
|
|
81
|
+
- `environmentName` is code-only. Otherwise the first defined
|
|
82
|
+
`DOTNET_ENVIRONMENT`, `ASPNETCORE_ENVIRONMENT`, `NODE_ENV` decides; no environment
|
|
83
|
+
is Production. `development: true` enables fixture providers, not this bypass.
|
|
84
|
+
|
|
85
|
+
`/.cratis/me`, command/query invocation and observable transports are unaffected
|
|
86
|
+
by the discovery policy. Deployed Lens/curl/OpenAPI consumers must authenticate
|
|
87
|
+
or rely on a deliberate anonymous opt-out. Source-based proxy generation needs
|
|
88
|
+
no running host. Verify both anonymous and authenticated access after upgrades.
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
# Observability and trusted host scopes
|
|
5
|
+
|
|
6
|
+
Verified at `Cratis/Arc.TypeScript` **v0.57.2** (source preview):
|
|
7
|
+
`Source/Core/execution/WellKnownTelemetryNames.ts`, `observability.ts`,
|
|
8
|
+
`runInScope.ts`, `RunInScopeOptions.ts`, and `Documentation/observability.md`.
|
|
9
|
+
|
|
10
|
+
- `cratis.arc.command.duration` / `cratis.arc.query.duration` measure **seconds**.
|
|
11
|
+
`cratis.arc.command.outcomes` counts command outcomes; the outcome vocabulary
|
|
12
|
+
is `success`, `validation`, `authorization`, `append_rejected`, `cancelled`,
|
|
13
|
+
`error`. `cratis.arc.query.transport` distinguishes query transports.
|
|
14
|
+
- Core exports `WellKnownTelemetryNames`, `OperationOutcome`, `QueryTransport`;
|
|
15
|
+
`@opentelemetry/api ^1.9.0` is a peer. Instrumentation caps command/query names
|
|
16
|
+
at 1,000 to bound cardinality. Do not add raw user/tenant keys to metric names.
|
|
17
|
+
- `cratis.arc.operation.duration` and the `query_name` attribute are deprecated;
|
|
18
|
+
migrate dashboards to command/query-specific names.
|
|
19
|
+
|
|
20
|
+
`ArcServer.runInScope(scope, callback, options?)` runs trusted host integration
|
|
21
|
+
work in a scope created with `server.services.createScope(context)`.
|
|
22
|
+
Service scopes snapshot the execution context at creation. Per-invocation
|
|
23
|
+
options accept only a correlation id (valid UUID) and extra abort signal.
|
|
24
|
+
This is **not** authentication or authorization: never turn untrusted request
|
|
25
|
+
values into a trusted principal by creating a scope.
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
## Generate metadata and proxies
|
|
2
2
|
|
|
3
|
+
Verified at `Cratis/Arc.TypeScript` **v0.57.2** (source preview):
|
|
4
|
+
`Source/Tools/ProxyGenerator/parseSourceOptions.ts`,
|
|
5
|
+
`Documentation/proxy-generation/getting-started.md`, and sample generator scripts.
|
|
6
|
+
|
|
3
7
|
`arc-proxygenerator` (package `@cratis/arc.proxygenerator`, also unpublished)
|
|
4
8
|
reads the TypeScript source with the compiler API — it never queries a running
|
|
5
9
|
server. It writes server metadata and client proxies in one run. The CLI
|
|
@@ -32,7 +36,34 @@ process.exitCode = result.status ?? 1;
|
|
|
32
36
|
(`--api-prefix`, `--segments-to-skip`, `--root-namespace`) to the server's
|
|
33
37
|
`generatedApis` options, or proxies call URLs the server does not serve.
|
|
34
38
|
- Generated proxies import the **published** `@cratis/arc` and
|
|
35
|
-
`@cratis/arc.react` (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
`@cratis/arc.react` (docs/sample pins **22.45.0**) plus `@cratis/fundamentals`;
|
|
40
|
+
the UI samples pin `@cratis/components` 4.23.0. Compile in strict `Bundler`
|
|
41
|
+
resolution with `skipLibCheck: false`, `experimentalDecorators: true`, and
|
|
42
|
+
import `reflect-metadata` once in the frontend entry. These published client
|
|
43
|
+
declarations do not support NodeNext consumer compilation. Consuming proxies
|
|
44
|
+
in React is `cratis-arc-react-page`.
|
|
45
|
+
|
|
46
|
+
### Co-locate proxies with the slice
|
|
47
|
+
|
|
48
|
+
The separate-output form above remains supported. To put proxies next to backend
|
|
49
|
+
artifacts, set `--output` to `Features` (or its ancestor/nested folder) and add
|
|
50
|
+
**`--use-proxy-file-suffix`**. This is required for overlapping output. The
|
|
51
|
+
samples emit `RegisterAuthor.proxy.ts` beside `Registration.ts` and
|
|
52
|
+
`RegisterAuthorForm.tsx`, without barrels. `discover()` skips `*.proxy.ts/.tsx`;
|
|
53
|
+
server tsconfig excludes `Features/**/*.proxy.ts` and `Features/**/*.tsx`.
|
|
54
|
+
Never give a `.tsx` file the same basename as the backend slice `.ts`.
|
|
55
|
+
|
|
56
|
+
`--watch` regenerates on backend edits; the generator overwrites/deletes only
|
|
57
|
+
its own output. A web bundler reading slice files outside the web project must
|
|
58
|
+
still lower the proxies' legacy decorators: Vite 8 uses
|
|
59
|
+
`oxc: { decorator: { legacy: true } }`.
|
|
60
|
+
|
|
61
|
+
| Additional flag | Effect |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `--skip-react-hooks` | No React import or generated `use*`/`when` hooks |
|
|
64
|
+
| `--type-mapping <Type>=<package>[#<export>]` | Repeatable external mappings (`typeMappings` in programmatic options) |
|
|
65
|
+
| `--watch-poll-interval <ms>` | Poll fallback; requires `--watch`; 0 disables polling |
|
|
66
|
+
| `--js-import-specifiers` | `.js` specifiers for compiled NodeNext ESM; does not fix client declaration compatibility |
|
|
67
|
+
| `--emit-interfaces` | Emit model interfaces |
|
|
68
|
+
| `--skip-index-generation` | Do not emit barrels |
|
|
69
|
+
| `--skip-output-deletion` | Retain stale generated output; use deliberately, not to hide drift |
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
## Lay out the slice
|
|
2
2
|
|
|
3
|
+
Verified at `Cratis/Arc.TypeScript` **v0.57.2**:
|
|
4
|
+
`Samples/Library/Features/Authors/Registration` and sample generator scripts.
|
|
5
|
+
|
|
3
6
|
House convention (not a runtime requirement): one folder per behavior, one
|
|
4
7
|
TypeScript file named for the behavior holding the command, its validator and —
|
|
5
8
|
for Chronicle — its events. Concepts shared by several slices live one level up.
|
|
@@ -13,7 +16,15 @@ Features/Tasks/
|
|
|
13
16
|
├── Tasks.ts
|
|
14
17
|
├── Registration/
|
|
15
18
|
│ ├── Registration.ts
|
|
19
|
+
│ ├── RegisterTask.proxy.ts
|
|
20
|
+
│ ├── RegisterTaskForm.tsx
|
|
16
21
|
│ └── for_RegisterTask/when_registering/with_valid_title.ts
|
|
17
22
|
└── Listing/
|
|
18
23
|
└── Listing.ts
|
|
19
24
|
```
|
|
25
|
+
|
|
26
|
+
Co-location needs `--use-proxy-file-suffix` with the artifacts folder as
|
|
27
|
+
`--output`; no barrels. Exclude proxies and TSX from server compilation and
|
|
28
|
+
lower proxy decorators in the frontend bundler. Read
|
|
29
|
+
[proxy-generation.md](proxy-generation.md) when configuring the overlapping
|
|
30
|
+
output or watch mode. Separate output is still supported.
|
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
{ "kind": "skill-contains", "value": "Authorization belongs on the **class**." },
|
|
11
11
|
{ "kind": "skill-contains", "value": "is created through its **constructor** in `handle()`" },
|
|
12
12
|
{ "kind": "skill-contains", "value": "`--check-metadata`" },
|
|
13
|
-
{ "kind": "skill-contains", "value": "CommandScenario.for(RegisterTask, RegisterTaskValidator)", "file": "references/command-specifications.md" }
|
|
13
|
+
{ "kind": "skill-contains", "value": "CommandScenario.for(RegisterTask, RegisterTaskValidator)", "file": "references/command-specifications.md" },
|
|
14
|
+
{ "kind": "skill-contains", "value": "tag **v0.57.2**" },
|
|
15
|
+
{ "kind": "skill-contains", "value": "peer `^6.29.0`" },
|
|
16
|
+
{ "kind": "skill-contains", "value": "Outside **Development**" },
|
|
17
|
+
{ "kind": "skill-contains", "value": "not identity\n discovery", "file": "references/identity-and-introspection.md" },
|
|
18
|
+
{ "kind": "skill-contains", "value": "Unknown`, `Mixed", "file": "references/command-operations.md" },
|
|
19
|
+
{ "kind": "skill-contains", "value": "**`--use-proxy-file-suffix`**", "file": "references/proxy-generation.md" },
|
|
20
|
+
{ "kind": "skill-contains", "value": "projections (>=6.19.0)", "file": "references/chronicle-events.md" }
|
|
14
21
|
]
|
|
15
22
|
}
|
|
@@ -23,12 +23,18 @@ rejection on it.)
|
|
|
23
23
|
|
|
24
24
|
| Package | Version | Purpose |
|
|
25
25
|
| --- | --- | --- |
|
|
26
|
-
| `Cratis.Arc.Core` | `22.
|
|
27
|
-
| `Cratis.Fundamentals` | `7.
|
|
26
|
+
| `Cratis.Arc.Core` | `22.75.0` | `CommandValidator<T>`, `ConceptValidator<T>`, `ValidationResult`, `CommandResult`, the command pipeline, `ARC0004`–`ARC0013` |
|
|
27
|
+
| `Cratis.Fundamentals` | `7.23.0` | `Cratis.Monads.Result<TResult, TError>` |
|
|
28
28
|
| `Cratis.Chronicle` | `18.3.0` | Read-model resolution for command-side injection |
|
|
29
29
|
| `FluentValidation` | `12.1.1` | `RuleFor(...)`, `.WithMessage(...)` |
|
|
30
30
|
|
|
31
|
-
>
|
|
31
|
+
> Arc validation and decision boundaries rechecked at v22.75.0. Arc depends on
|
|
32
|
+
> Chronicle 19.39.1 / Fundamentals 7.23.0; separate product claims retain their
|
|
33
|
+
> version-qualified verification.
|
|
34
|
+
|
|
35
|
+
Fundamentals rechecked at v7.23.0 in
|
|
36
|
+
[`Result<TResult, TError>`](https://github.com/Cratis/Fundamentals/blob/v7.23.0/Source/DotNET/Fundamentals/Monads/Result%7BTResult,TError%7D.cs):
|
|
37
|
+
success is first, error second, with implicit conversions from both types.
|
|
32
38
|
|
|
33
39
|
Reverify against the owning product repository before claiming support for
|
|
34
40
|
another version.
|
|
@@ -62,7 +68,7 @@ another version.
|
|
|
62
68
|
| A reusable value invariant — length, format, range | `ConceptValidator<T>` on the concept type |
|
|
63
69
|
| Command input, cross-field, or a pre-handler check | `CommandValidator<TCommand>` with `RuleFor(...)` |
|
|
64
70
|
| A dependency on data the handler must fetch first | `Provide()` — fetch, and short-circuit if unusable |
|
|
65
|
-
| State-dependent and must hold **under concurrency** |
|
|
71
|
+
| State-dependent and must hold **under concurrency** | Guard the read with `IStreamReads`/`StreamDecision`, or `[ProtectedDecision]` + `DecisionRead<T>`; return events through Arc |
|
|
66
72
|
| Uniqueness | A Chronicle constraint, never a command-side read |
|
|
67
73
|
| A genuine defect or missing infrastructure | `throw` a domain exception |
|
|
68
74
|
|
|
@@ -144,27 +150,49 @@ public async Task<Result<Customer, ValidationResult>> Provide(IReadModels readMo
|
|
|
144
150
|
directly, and do not duplicate one rejection in both the validator and
|
|
145
151
|
`Provide()` — pick one owner.
|
|
146
152
|
|
|
147
|
-
## Step 5 —
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
153
|
+
## Step 5 — Guard a concurrency-sensitive decision
|
|
154
|
+
|
|
155
|
+
Plain read-model injection or `IReadModels.GetInstanceById` is **advisory**, even
|
|
156
|
+
inside `Handle()` returning `Result<,>`: it enrolls no guard against a competing
|
|
157
|
+
append. Choose the first boundary that fits at Arc v22.75.0:
|
|
158
|
+
|
|
159
|
+
1. One routed stream: `Provide(IStreamReads reads) => reads.ForCommand()` returns
|
|
160
|
+
`Task<StreamDecision>`; `Handle(StreamDecision decision)` returns
|
|
161
|
+
`decision.Append(...)` (`EventsWithConcurrencyScopes`), guarding the read tail.
|
|
162
|
+
2. Projected state: mark the command `[ProtectedDecision]` and take
|
|
163
|
+
`DecisionRead<T>` (`.Exists`/`.Instance`) or command-aware `IDecisionReads`.
|
|
164
|
+
Only Chronicle-admitted, direct event-source-keyed projections with known
|
|
165
|
+
event types qualify; reducers, joins, children and passive shapes are refused.
|
|
166
|
+
Only parameterless validators constructed/certified by Arc run. Constructor
|
|
167
|
+
dependencies are refused; static/service-locator decision state is still your
|
|
168
|
+
responsibility. Command-level `[Unprotected]` opts out, not into safety.
|
|
169
|
+
3. Stream-level automatic scopes: `concurrency: true` on routing attributes
|
|
170
|
+
(`EventStreamId`, `EventStreamType`, `EventSourceType`). Exact returned scopes
|
|
171
|
+
take precedence; do not combine automatic capture with exact scopes.
|
|
172
|
+
|
|
173
|
+
`ARCCHR0011/0012` flag unprotected reads and immediate append bypasses. Return
|
|
174
|
+
events, never append them directly. Read
|
|
175
|
+
[protected and stream decisions](../cratis-arc-command/references/decisions-and-routing.md)
|
|
176
|
+
when choosing the guard. This handler excerpt assumes `CartSummary` is an
|
|
177
|
+
admitted projection; domain types are the slice's existing types.
|
|
154
178
|
|
|
155
179
|
```csharp
|
|
156
180
|
using Cratis.Monads;
|
|
181
|
+
using Cratis.Arc.Chronicle.ReadModels;
|
|
157
182
|
|
|
158
183
|
[Command]
|
|
184
|
+
[ProtectedDecision]
|
|
159
185
|
public record AddItemToCart(CartId CartId, ItemId ItemId)
|
|
160
186
|
{
|
|
161
187
|
/// <summary>Adds the item; rejects when the cart already holds the maximum.</summary>
|
|
162
188
|
/// <param name="cart">The current cart summary, resolved by the command's event-source id.</param>
|
|
163
189
|
/// <returns>The event on success, or a validation error.</returns>
|
|
164
|
-
public Result<ItemAddedToCart, ValidationResult> Handle(CartSummary cart) =>
|
|
165
|
-
cart.
|
|
166
|
-
? ValidationResult.Error("
|
|
167
|
-
:
|
|
190
|
+
public Result<ItemAddedToCart, ValidationResult> Handle(DecisionRead<CartSummary> cart) =>
|
|
191
|
+
!cart.Exists
|
|
192
|
+
? ValidationResult.Error("Cart must exist.")
|
|
193
|
+
: cart.Instance.ItemCount >= 3
|
|
194
|
+
? ValidationResult.Error("A cart can hold at most 3 items.")
|
|
195
|
+
: new ItemAddedToCart(ItemId);
|
|
168
196
|
}
|
|
169
197
|
```
|
|
170
198
|
|
|
@@ -205,7 +233,9 @@ where absence is *not* `null`.
|
|
|
205
233
|
## Step 6 — Specify the rejection
|
|
206
234
|
|
|
207
235
|
Every rule gets a specification of its failure case, using
|
|
208
|
-
`CommandScenario<TCommand>`.
|
|
236
|
+
`CommandScenario<TCommand>`. For protected reads, call `UseDecisionReads()`
|
|
237
|
+
before seeding actual events; pinned read models cannot supply a guard. Queue a
|
|
238
|
+
competing append on the relevant route and assert the conflict. Assert **both** that the command did not succeed
|
|
209
239
|
and that it failed *as a validation*:
|
|
210
240
|
|
|
211
241
|
```csharp
|
|
@@ -231,9 +261,9 @@ the fact. See
|
|
|
231
261
|
- **The rule never fires.** The read-model type has no backing Chronicle
|
|
232
262
|
projection, reducer, `DbSet<T>`, or MongoDB collection, so nothing registered
|
|
233
263
|
it for command-side resolution.
|
|
234
|
-
- **Two requests both win.**
|
|
235
|
-
|
|
236
|
-
|
|
264
|
+
- **Two requests both win.** A plain read was mistaken for a protected decision;
|
|
265
|
+
moving it to `Handle()` or returning `Result<,>` does not guard it. Enroll a
|
|
266
|
+
stream/projection decision read, or use an append-time uniqueness constraint.
|
|
237
267
|
- **`ARC0005` on build.** `Provide()` returns a value no `Handle()` parameter
|
|
238
268
|
consumes.
|
|
239
269
|
|
|
@@ -15,11 +15,12 @@ Everything else is ordinary EF Core.
|
|
|
15
15
|
|
|
16
16
|
| Package | Version | Purpose |
|
|
17
17
|
| --- | --- | --- |
|
|
18
|
-
| `Cratis.Arc.EntityFrameworkCore` | `22.
|
|
19
|
-
| `Cratis.Arc.Core` | `22.
|
|
18
|
+
| `Cratis.Arc.EntityFrameworkCore` | `22.75.0` | `BaseDbContext`, `ReadOnlyDbContext`, `WithEntityFrameworkCore`, column helpers, `EntityFrameworkReadModelForCommandResolver` |
|
|
19
|
+
| `Cratis.Arc.Core` | `22.75.0` | `Cratis.Arc.Queries.ModelBound.ReadModelAttribute`, `ICanResolveReadModelForCommand` |
|
|
20
20
|
| `Microsoft.EntityFrameworkCore` | `10.0.11` | EF Core itself, including all migration tooling |
|
|
21
21
|
|
|
22
|
-
>
|
|
22
|
+
> Arc EF context, builder and observation surface rechecked at v22.75.0.
|
|
23
|
+
> Verify provider-specific migration behavior against the configured provider.
|
|
23
24
|
|
|
24
25
|
Reverify before claiming support for another version.
|
|
25
26
|
|
|
@@ -189,9 +190,14 @@ and `ObserveById<TEntity, TId>(id, configure?)`, returning
|
|
|
189
190
|
`AddEntityFrameworkCoreObservation()` and `options.AddObservation(serviceProvider)`
|
|
190
191
|
on the context — `WithEntityFrameworkCore` already does the first for you.
|
|
191
192
|
|
|
192
|
-
Paging and sorting are applied at the source
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
Paging and sorting are applied at the source; the query pipeline validates
|
|
194
|
+
`page >= 0` and `pageSize > 0`. Unknown observation sort fields are ignored.
|
|
195
|
+
Auxiliary observations can pass `ignoreQueryContext: true` so only the primary
|
|
196
|
+
uses client paging/sorting and writes totals. `ObserveSingle`/`ObserveById` emit
|
|
197
|
+
`default` when the row is absent/deleted/outside the filter, remaining subscribed.
|
|
198
|
+
Indexer-property keys (shared-type entities) are supported; composite and
|
|
199
|
+
shadow-only keys are refused at setup. Source at Arc v22.75.0:
|
|
200
|
+
`Source/DotNET/EntityFrameworkCore/Observe/DbSetObserveExtensions.cs`.
|
|
195
201
|
|
|
196
202
|
⚠️ Database-level change notification differs by provider: PostgreSQL uses
|
|
197
203
|
LISTEN/NOTIFY, SQL Server uses Service Broker, and **SQLite has none at all** —
|