Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Documentation/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Welcome to the Chronicle TypeScript client documentation.

- Appending events to event sequences
- Managing event stores and namespaces
- Defining reactors (side-effect handlers) and reducers (state-folders) using TypeScript decorators
- Defining reactors, reducers, projections, and constraints using TypeScript decorators

## Guides

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A TypeScript-idiomatic client for [Cratis Chronicle](https://github.com/Cratis/C

`@cratis/chronicle` provides a clean, type-safe TypeScript API for interacting with the Chronicle Kernel. It builds on top of [`@cratis/chronicle.contracts`](https://www.npmjs.com/package/@cratis/chronicle.contracts) (the gRPC contracts package) and exposes idiomatic TypeScript constructs including:

- **Decorators** — `@eventType`, `@reactor`, `@reducer` mirror the C# attribute-based API
- **Decorators** — `@eventType`, `@readModel`, `@reactor`, `@reducer`, `@constraint`, `@projection`, `@modelBoundProjection`
- **Value objects** — `EventSequenceNumber`, `EventTypeId`, `EventStoreName`, etc.
- **Fluent client** — `ChronicleClient` → `EventStore` → `EventLog` → `append()`

Expand Down
10 changes: 10 additions & 0 deletions Source/EventSequences/AppendError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

/**
* Represents an error that occurred while appending an event.
*/
export interface AppendError {
/** The error message describing the failure. */
readonly message: string;
}
15 changes: 15 additions & 0 deletions Source/EventSequences/AppendOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

import { Guid } from '@cratis/fundamentals';

/**
* Options for appending an event to an event sequence.
*/
export interface AppendOptions {
/** Optional correlation identifier for tracking the append operation. */
correlationId?: string | Guid;

/** Optional explicit sequence number to use for the event. */
eventSourceId?: string;
}
24 changes: 2 additions & 22 deletions Source/EventSequences/AppendResult.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,10 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

import { AppendError } from './AppendError';
import { ConstraintViolation } from './ConstraintViolation';
import { EventSequenceNumber } from './EventSequenceNumber';

/**
* Represents an error that occurred while appending an event.
*/
export interface AppendError {
/** The error message describing the failure. */
readonly message: string;
}

/**
* Represents a constraint violation that occurred while appending an event.
*/
export interface ConstraintViolation {
/** The constraint identifier that was violated. */
readonly constraintId: string;

/** The violation message. */
readonly message: string;

/** Additional details about the violation. */
readonly details: Readonly<Record<string, string>>;
}

/**
* Represents the result of appending a single event to an event sequence.
*/
Expand Down
16 changes: 16 additions & 0 deletions Source/EventSequences/ConstraintViolation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

/**
* Represents a constraint violation that occurred while appending an event.
*/
export interface ConstraintViolation {
/** The constraint identifier that was violated. */
readonly constraintId: string;

/** The violation message. */
readonly message: string;

/** Additional details about the violation. */
readonly details: Readonly<Record<string, string>>;
}
6 changes: 4 additions & 2 deletions Source/EventSequences/EventSequence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import { SpanStatusCode } from '@opentelemetry/api';
import { Guid } from '@cratis/fundamentals';
import { getEventTypeFor } from '../Events/eventTypeDecorator';
import { Grpc } from '../Grpc';
import { AppendOptions, IEventSequence } from './IEventSequence';
import { AppendResult, ConstraintViolation } from './AppendResult';
import { AppendOptions } from './AppendOptions';
import { AppendResult } from './AppendResult';
import { ConstraintViolation } from './ConstraintViolation';
import { IEventSequence } from './IEventSequence';
import { EventSequenceId } from './EventSequenceId';
import { EventSequenceNumber } from './EventSequenceNumber';
import { ChronicleTracer } from '../Tracing';
Expand Down
13 changes: 1 addition & 12 deletions Source/EventSequences/IEventSequence.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

import { AppendOptions } from './AppendOptions';
import { AppendResult } from './AppendResult';
import { EventSequenceId } from './EventSequenceId';
import { EventSequenceNumber } from './EventSequenceNumber';
import { Guid } from '@cratis/fundamentals';

/**
* Options for appending an event to an event sequence.
*/
export interface AppendOptions {
/** Optional correlation identifier for tracking the append operation. */
correlationId?: string | Guid;

/** Optional explicit sequence number to use for the event. */
eventSourceId?: string;
}

/**
* Defines the API surface for an event sequence.
Expand Down
7 changes: 5 additions & 2 deletions Source/EventSequences/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

export { EventSequenceId } from './EventSequenceId';
export { EventSequenceNumber } from './EventSequenceNumber';
export type { AppendResult, AppendError, ConstraintViolation } from './AppendResult';
export type { IEventSequence, AppendOptions } from './IEventSequence';
export type { AppendError } from './AppendError';
export type { ConstraintViolation } from './ConstraintViolation';
export type { AppendResult } from './AppendResult';
export type { AppendOptions } from './AppendOptions';
export type { IEventSequence } from './IEventSequence';
export type { IEventLog } from './IEventLog';
export { EventSequence } from './EventSequence';
export { EventLog } from './EventLog';
40 changes: 39 additions & 1 deletion Source/EventStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,30 @@ import { Grpc } from './Grpc';
import { EventStoreName } from './EventStoreName';
import { EventStoreNamespaceName } from './EventStoreNamespaceName';
import { IEventStore } from './IEventStore';
import { ChronicleTracer } from './Tracing';
import { EventTypes } from './Events/EventTypes';
import { IEventTypes } from './Events/IEventTypes';
import { Constraints } from './Events/Constraints/Constraints';
import { IConstraints } from './Events/Constraints/IConstraints';
import { Projections } from './Projections/Projections';
import { IProjections } from './Projections/IProjections';
import { Reactors } from './Reactors/Reactors';
import { IReactors } from './Reactors/IReactors';
import { Reducers } from './Reducers/Reducers';
import { IReducers } from './Reducers/IReducers';
import { DefaultClientArtifactsProvider } from './artifacts/DefaultClientArtifactsProvider';

/**
* Implements {@link IEventStore} by communicating with the Chronicle Kernel
* via gRPC using the provided {@link ChronicleConnection}.
*/
export class EventStore implements IEventStore {
readonly eventLog: IEventLog;
readonly eventTypes: IEventTypes;
readonly constraints: IConstraints;
readonly projections: IProjections;
readonly reactors: IReactors;
readonly reducers: IReducers;

private readonly _sequences: Map<string, IEventSequence> = new Map();

constructor(
Expand All @@ -29,6 +45,28 @@ export class EventStore implements IEventStore {
) {
this.eventLog = new EventLog(name.value, namespace.value, _connection);
this._sequences.set(EventSequenceId.eventLog.value, this.eventLog);

const artifacts = DefaultClientArtifactsProvider.default;
this.eventTypes = new EventTypes(name.value, _connection, artifacts);
this.constraints = new Constraints(name.value, _connection, artifacts);
this.projections = new Projections(artifacts);
this.reactors = new Reactors(artifacts);
this.reducers = new Reducers(artifacts);
}

/**
* Registers all discovered artifacts with the Chronicle Kernel.
* Called on initial connect and on reconnect.
* @returns A promise that resolves when all registrations are complete.
*/
async registerArtifacts(): Promise<void> {
await Promise.all([
this.eventTypes.register(),
this.constraints.register(),
this.projections.register(),
this.reactors.register(),
this.reducers.register()
]);
}

/** @inheritdoc */
Expand Down
13 changes: 13 additions & 0 deletions Source/Events/CausationEntry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

/**
* Represents a single entry in the causation chain of an event.
*/
export interface CausationEntry {
/** The type identifier of the causing operation. */
readonly type: string;

/** The properties associated with the causation entry. */
readonly properties: Readonly<Record<string, string>>;
}
86 changes: 86 additions & 0 deletions Source/Events/Constraints/ConstraintBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

import { getEventTypeFor } from '../eventTypeDecorator';
import { IConstraintBuilder } from './IConstraintBuilder';
import { IUniqueConstraintBuilder } from './IUniqueConstraintBuilder';
import { UniqueConstraintBuilder, UniqueConstraintCapture } from './UniqueConstraintBuilder';

/** Represents the captured scope for a constraint. */
export interface ConstraintScopeCapture {
perEventSourceType: boolean;
perEventStreamType: boolean;
perEventStreamId: boolean;
}

/** Represents the captured definition of a unique event type constraint. */
export interface UniqueEventTypeCapture {
eventTypeId: string;
message?: string;
name?: string;
}

/** Represents the full captured definition of a constraint. */
export interface ConstraintCapture {
name: string;
scope: ConstraintScopeCapture;
uniqueConstraint?: UniqueConstraintCapture;
uniqueEventType?: UniqueEventTypeCapture;
}

/**
* Implements {@link IConstraintBuilder}, capturing the constraint definition
* for later serialization and registration with the Kernel.
*/
export class ConstraintBuilder implements IConstraintBuilder {
readonly capture: ConstraintCapture;
private _uniqueBuilder?: UniqueConstraintBuilder;

constructor(name: string) {
this.capture = {
name,
scope: { perEventSourceType: false, perEventStreamType: false, perEventStreamId: false }
};
}

/** @inheritdoc */
perEventSourceType(): IConstraintBuilder {
this.capture.scope.perEventSourceType = true;
return this;
}

/** @inheritdoc */
perEventStreamType(): IConstraintBuilder {
this.capture.scope.perEventStreamType = true;
return this;
}

/** @inheritdoc */
perEventStreamId(): IConstraintBuilder {
this.capture.scope.perEventStreamId = true;
return this;
}

/** @inheritdoc */
unique(callback: (builder: IUniqueConstraintBuilder) => void): IConstraintBuilder {
const uniqueCapture: UniqueConstraintCapture = {
eventDefinitions: [],
ignoreCasing: false
};
this.capture.uniqueConstraint = uniqueCapture;
this._uniqueBuilder = new UniqueConstraintBuilder(uniqueCapture);
callback(this._uniqueBuilder);
return this;
}

/** @inheritdoc */
uniqueFor(eventType: Function, message?: string, name?: string): IConstraintBuilder {
const et = getEventTypeFor(eventType);
this.capture.uniqueEventType = {
eventTypeId: et.id.value,
message,
name
};
return this;
}
}
14 changes: 14 additions & 0 deletions Source/Events/Constraints/ConstraintId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

/**
* Unique identifier for a constraint.
*/
export class ConstraintId {
constructor(readonly value: string) {}

/** @inheritdoc */
toString(): string {
return this.value;
}
}
Loading
Loading