Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
1a09825
Add TypeScript snippets for configuration docs
einari Sep 1, 2026
db56cda
Add TypeScript snippets for connection-strings docs
einari Sep 1, 2026
d6d318b
Add TypeScript snippets for hosting docs
einari Sep 1, 2026
0fabfa1
Add TypeScript snippets for namespaces docs
einari Sep 1, 2026
367a9e3
Declare compliance metadata on the JsonSchema type
einari Sep 1, 2026
afdacaa
Fix PII compliance metadata to cover declaring class and leaves
einari Sep 1, 2026
28e3e07
Add specs for JsonSchemaGenerator PII compliance metadata
einari Sep 1, 2026
a5b9adc
Reject @pii() on the event source identifier property
einari Sep 1, 2026
42ed625
Add IPIIManager.allowNewEncryptionKeyFor
einari Sep 1, 2026
1610de2
Add missing compliance client-snippets
einari Sep 1, 2026
77f0bdb
Add TypeScript snippets for get-started docs
einari Sep 1, 2026
a2e60cb
Add TypeScript snippets for scenarios docs
einari Sep 1, 2026
025cdcf
Add TypeScript snippets for tutorial docs
einari Sep 1, 2026
8b9c971
Add TypeScript snippets for events docs
einari Sep 1, 2026
effbde0
Add TypeScript snippets for reducers docs
einari Sep 1, 2026
7de0bb0
Add TypeScript snippets for reactors docs
einari Sep 1, 2026
afcba56
Add TypeScript snippets for read-models docs
einari Sep 1, 2026
503c82c
Add TypeScript snippets for testing docs
einari Sep 1, 2026
d09e9fc
Add TypeScript snippets for constraints docs
einari Sep 1, 2026
28219cb
Add TypeScript snippets for migrations docs
einari Sep 1, 2026
911d227
Add TypeScript snippets for subscriptions docs
einari Sep 1, 2026
c4da500
Add TypeScript snippets for sinks docs
einari Sep 1, 2026
3d4ef2d
Add TypeScript snippets for concepts docs
einari Sep 1, 2026
0ebbf7c
Add TypeScript snippets for declarative children projections
einari Sep 1, 2026
5d8a4ea
Add TypeScript snippets for declarative projections (constant-key, fu…
einari Sep 1, 2026
f970d6f
Add TypeScript snippets for declarative nested projections
einari Sep 1, 2026
ecfbdfa
Add TypeScript snippets for declarative remove-with-join projections
einari Sep 1, 2026
da176c8
Add TypeScript snippets for declarative auto-map and filtering docs
einari Sep 1, 2026
9b3adea
Add TypeScript snippets for model-bound and PDL projections docs
einari Sep 1, 2026
2477b7d
Add TypeScript snippets for contributing/kernel docs
einari Sep 1, 2026
a0f5f1c
Merge absent-snippet fill
einari Sep 1, 2026
88f44a2
Add @subject() decorator for compliance subject resolution
einari Sep 1, 2026
4e70dd4
Add ReadModelSubjectResolver for read-model subject lookup
einari Sep 1, 2026
b3ce70f
Replace hardcoded 'id' subject lookups with ReadModelSubjectResolver
einari Sep 1, 2026
3bc971a
Demonstrate @subject() in the releasing-PII TypeScript snippet
einari Sep 1, 2026
84c77fc
Add IProjections operational surface
einari Sep 1, 2026
f3c2a96
Add model-bound completeness: fromAll, noAutoMap, event sequence, cle…
einari Sep 1, 2026
dc4c70d
Un-fence projection client snippets now supported in TypeScript
einari Sep 1, 2026
5ed9c3d
Add event tags to the TypeScript client
einari Sep 1, 2026
2b0e157
Add specs for event tags
einari Sep 1, 2026
bf20ddb
Merge subject-resolver
einari Sep 1, 2026
09f0d96
Merge projections-surface
einari Sep 1, 2026
4c5835c
Merge tags
einari Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
19 changes: 19 additions & 0 deletions Documentation/client-snippets/compliance/client/combining.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
```typescript
import { eventType, pii } from '@cratis/chronicle';
import { ConceptAs } from '@cratis/fundamentals';

@pii()
class ComplianceClientEmailAddress extends ConceptAs<string> {
constructor(value: string) {
super(value);
}
}

@eventType()
class ComplianceClientCustomerRegistered {
name: ComplianceClientPersonName = new ComplianceClientPersonName(''); // encrypted via concept type
email: ComplianceClientEmailAddress = new ComplianceClientEmailAddress(''); // encrypted via concept type
@pii() phoneNumber = ''; // encrypted via property annotation
country = ''; // plaintext
}
```
13 changes: 13 additions & 0 deletions Documentation/client-snippets/compliance/client/concept-usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
```typescript
import { eventType } from '@cratis/chronicle';

@eventType()
class ComplianceClientEmployeeRegisteredWithConcept {
constructor(readonly name: ComplianceClientPersonName, readonly department: string) {}
}

@eventType()
class ComplianceClientEmployeeNameChanged {
constructor(readonly newName: ComplianceClientPersonName) {} // also encrypted
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
```typescript
import { pii } from '@cratis/chronicle';

// TypeScript represents the event source identifier as the conventional 'eventSourceId'
// property rather than a dedicated EventSourceId<T> type. Marking it @pii() throws
// PIINotSupportedOnEventSourceId - event source identifiers are required for key lookup and
// cannot be encrypted.
class ComplianceClientCustomerId {
@pii() eventSourceId = '';
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
```typescript
import { ChronicleClient } from '@cratis/chronicle';

// TypeScript has no DI-container registration step for compliance support - the PII manager
// is available automatically as soon as you have an event store, with no separate wiring.
async function getPIIManager(chronicleClient: ChronicleClient) {
const eventStore = await chronicleClient.getEventStore('Sales');
return eventStore.pii;
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
```typescript
import { ChronicleClient } from '@cratis/chronicle';

async function allowNewEncryptionKeyForPerson(chronicleClient: ChronicleClient): Promise<void> {
const eventStore = await chronicleClient.getEventStore('Sales');
await eventStore.pii.allowNewEncryptionKeyFor('person-42');
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
```typescript
import { ChronicleClient } from '@cratis/chronicle';

async function deletePersonEncryptionKey(chronicleClient: ChronicleClient): Promise<void> {
const eventStore = await chronicleClient.getEventStore('Sales');
await eventStore.pii.deleteEncryptionKey('person-42');
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
```typescript
import { pii } from '@cratis/chronicle';

// TypeScript has no dedicated EventSourceId<T> type to mark PII on directly - the event
// source identifier is always the conventional 'eventSourceId' property, and marking it
// @pii() throws PIINotSupportedOnEventSourceId for the same reason C# forbids [PII] on a
// concept deriving from EventSourceId<T>.
class PiiConceptsEmployeeId {
@pii() eventSourceId = '';
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
```typescript
import { eventType, Guid } from '@cratis/chronicle';

// ✅ Surrogate key as event source identifier - TypeScript event source identifiers are
// plain strings, so a randomly generated Guid works well with no dedicated identity type
// required.
function createSurrogateEmployeeId(): string {
return Guid.create().toString();
}

// ✅ Sensitive values stored in PII-marked concept properties instead
@eventType()
class PiiConceptsSurrogateEmployeeRegistered {
constructor(readonly nationalId: PiiConceptsNationalIdNumber, readonly name: PiiConceptsPersonName) {}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
```typescript
import { eventType, pii } from '@cratis/chronicle';
import { ConceptAs } from '@cratis/fundamentals';

// ❌ Property-level: requires repetition across every event
@eventType()
class PiiConceptsComparisonEmployeeRegistered {
@pii() name = '';
department = '';
}

@eventType()
class PiiConceptsComparisonEmployeeNameChanged {
@pii() newName = ''; // must remember @pii() again
}

// ✅ Concept-level: declare once, apply everywhere automatically
@pii()
class PiiConceptsComparisonPersonName extends ConceptAs<string> {
constructor(value: string) {
super(value);
}
}

@eventType()
class PiiConceptsComparisonEmployeeRegisteredGood {
name: PiiConceptsComparisonPersonName = new PiiConceptsComparisonPersonName(''); // encrypted
department = '';
}

@eventType()
class PiiConceptsComparisonEmployeeNameChangedGood {
newName: PiiConceptsComparisonPersonName = new PiiConceptsComparisonPersonName(''); // also encrypted, no extra annotation needed
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
```typescript
import { pii } from '@cratis/chronicle';

// TypeScript has no dedicated EventSourceId<T> type - the event source identifier is always
// the conventional 'eventSourceId' property. Marking it @pii() throws
// PIINotSupportedOnEventSourceId at decoration time, for the same reason C# forbids [PII] on
// EventSourceId<T>: encrypting it would make its own decryption key unfindable.
class PiiAttrEmployeeId {
@pii() eventSourceId = '';
}
```
3 changes: 3 additions & 0 deletions Documentation/client-snippets/compliance/pii/import.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```typescript
import { pii } from '@cratis/chronicle';
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
```typescript
import { pii } from '@cratis/chronicle';
import { ConceptAs } from '@cratis/fundamentals';

@pii()
class PiiAttrDateOfBirth extends ConceptAs<string> {
constructor(value: string) {
super(value);
}
}

// The concept sits one level down, inside a value object.
class PiiAttrVerifiedDateOfBirth {
dateOfBirth: PiiAttrDateOfBirth = new PiiAttrDateOfBirth('');
verifiedBy = '';
}

// Chronicle still finds it: dateOfBirth.dateOfBirth is encrypted, verifiedBy is not.
class PiiAttrExpressVerification {
name = '';
dateOfBirth: PiiAttrVerifiedDateOfBirth = new PiiAttrVerifiedDateOfBirth();
}
```
16 changes: 16 additions & 0 deletions Documentation/client-snippets/compliance/pii/value-object-class.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
```typescript
import { pii } from '@cratis/chronicle';

// Every value this type holds is personal, so mark the type once.
@pii()
class PiiAttrDiagnosis {
condition = '';
diagnosedBy = '';
}

// Both condition and diagnosedBy are encrypted wherever a PiiAttrDiagnosis appears.
class PiiAttrPatientRecord {
name = '';
diagnosis: PiiAttrDiagnosis = new PiiAttrDiagnosis();
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
```typescript
import { eventType, fromEvent, pii, readModel } from '@cratis/chronicle';
import { ConceptAs } from '@cratis/fundamentals';

@pii()
class ComplianceReadModelsPersonName extends ConceptAs<string> {
constructor(value: string) {
super(value);
}
}

@eventType()
class ComplianceReadModelsEmployeeRegistered {
constructor(readonly name: ComplianceReadModelsPersonName, readonly department: string) {}
}

// Chronicle's projection pipeline carries PII lineage automatically from the source event
// property into the read model - no @pii() is needed here even though `name` is a plain
// string. It is still encrypted at rest because it came from a PII-marked event property.
@readModel()
@fromEvent(ComplianceReadModelsEmployeeRegistered)
class ComplianceReadModelsEmployee {
name = '';
department = '';
}
```
11 changes: 11 additions & 0 deletions Documentation/client-snippets/compliance/read-models/querying.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
```typescript
import { IEventStore } from '@cratis/chronicle';

class ComplianceReadModelsEmployeeService {
constructor(private readonly eventStore: IEventStore) {}

getEmployee(id: string): Promise<ComplianceReadModelsEmployee> {
return this.eventStore.readModels.getInstanceById(ComplianceReadModelsEmployee, id);
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
```typescript
import { eventType, pii, reducer } from '@cratis/chronicle';

@eventType()
class ComplianceReadModelsPatientAdmitted {
constructor(readonly name: string, readonly admittedAt: Date) {}
}

// Reducer-backed read models do not inherit PII lineage from the source event automatically -
// mark the property explicitly.
class ComplianceReadModelsPatientSummary {
@pii() name = '';
lastAdmittedAt = new Date();
}

@reducer('PatientSummaryReducer', undefined, ComplianceReadModelsPatientSummary)
class ComplianceReadModelsPatientSummaryReducer {
async patientAdmitted(
event: ComplianceReadModelsPatientAdmitted,
current?: ComplianceReadModelsPatientSummary
): Promise<ComplianceReadModelsPatientSummary> {
return { name: event.name, lastAdmittedAt: event.admittedAt };
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```typescript
import { IEventStore } from '@cratis/chronicle';

async function renameAnIdentity(eventStore: IEventStore): Promise<void> {
await eventStore.identities.rename('subject-42', 'Jane Austen');
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```text
TypeScript does not support this workflow yet.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```text
TypeScript does not support this workflow yet.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```text
TypeScript does not support this workflow yet.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```text
TypeScript does not support this workflow yet.
```
3 changes: 3 additions & 0 deletions Documentation/client-snippets/concepts/geospatial/types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```text
TypeScript does not support this workflow yet.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```text
TypeScript does not support this workflow yet.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
```typescript
import { EventContext, eventType, reactor, tag } from '@cratis/chronicle';

@eventType()
class TaggingReactorsOrderShipped {
constructor(readonly phoneNumber: string, readonly trackingNumber: string) {}
}

interface TaggingReactorsSmsService {
sendShippingNotification(phoneNumber: string, trackingNumber: string): Promise<void>;
}

@reactor()
@tag('Notifications', 'SMS')
@tag('Customer')
class TaggingReactorsSmsNotificationReactor {
constructor(private readonly smsService: TaggingReactorsSmsService) {}

async taggingReactorsOrderShipped(event: TaggingReactorsOrderShipped, _context: EventContext): Promise<void> {
await this.smsService.sendShippingNotification(event.phoneNumber, event.trackingNumber);
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
```typescript
import { EventContext, eventType, reactor, tag } from '@cratis/chronicle';

@eventType()
class TaggingReactorsProductStockChanged {
constructor(readonly productId: string, readonly newQuantity: number) {}
}

interface TaggingReactorsInventoryApi {
updateStock(productId: string, newQuantity: number): Promise<void>;
}

@reactor()
@tag('Integration')
@tag('ExternalAPI')
@tag('Inventory')
class TaggingReactorsInventorySyncReactor {
constructor(private readonly inventoryApi: TaggingReactorsInventoryApi) {}

async taggingReactorsProductStockChanged(event: TaggingReactorsProductStockChanged, _context: EventContext): Promise<void> {
await this.inventoryApi.updateStock(event.productId, event.newQuantity);
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
```typescript
import { EventContext, eventType, reactor, tag } from '@cratis/chronicle';

@eventType()
class TaggingReactorsCustomerRegistered {
constructor(readonly email: string, readonly name: string) {}
}

interface TaggingReactorsWelcomeEmailService {
sendWelcomeEmail(email: string, name: string): Promise<void>;
}

@reactor()
@tag('Notifications', 'Customer', 'Email')
class TaggingReactorsCustomerNotificationReactor {
constructor(private readonly emailService: TaggingReactorsWelcomeEmailService) {}

async taggingReactorsCustomerRegistered(event: TaggingReactorsCustomerRegistered, _context: EventContext): Promise<void> {
await this.emailService.sendWelcomeEmail(event.email, event.name);
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
```typescript
import { EventContext, eventType, reactor, tag } from '@cratis/chronicle';

@eventType()
class TaggingReactorsOrderPlaced {
constructor(readonly customerId: string, readonly orderId: string) {}
}

interface TaggingReactorsEmailService {
sendOrderConfirmation(customerId: string, orderId: string): Promise<void>;
}

@reactor()
@tag('Notifications')
class TaggingReactorsOrderConfirmationReactor {
constructor(private readonly emailService: TaggingReactorsEmailService) {}

async taggingReactorsOrderPlaced(event: TaggingReactorsOrderPlaced, _context: EventContext): Promise<void> {
await this.emailService.sendOrderConfirmation(event.customerId, event.orderId);
}
}
```
Loading
Loading