@semiont/event-sourcing 0.5.2 → 0.5.4

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/README.md CHANGED
@@ -136,6 +136,17 @@ await eventStore.views.rebuildAll(eventStore.log);
136
136
 
137
137
  The two paths use the same materialization primitives, so replaying event 1..N via `rebuildAll` produces the same final state as the live path walking 1..N over time.
138
138
 
139
+ #### Pure projection reducers
140
+
141
+ The `__system__` projections (`entitytypes.json`, `tagschemas.json`) are written by a thin I/O shell wrapping pure functions that own the merge/dedup/sort/conflict semantics. The pure reducers live in [`src/views/projection-reducers.ts`](src/views/projection-reducers.ts):
142
+
143
+ - `applyEntityTypeAdded(view, tag)` → `string[]` — dedup + locale-aware sort.
144
+ - `applyTagSchemaAdded(view, schema)` → `{ next; warning? }` — most-recent-wins by id, warning on overwrite-with-different-content.
145
+
146
+ The shell methods on `ViewMaterializer` (`materializeEntityTypes`, `materializeTagSchemas`) read the projection file, call the reducer, then write the result. The semantics are the reducer's; the disk I/O is the shell's.
147
+
148
+ This split keeps projection-update tests pure (single-digit milliseconds, no filesystem) and gives load-bearing invariants — sortedness, uniqueness, idempotence, most-recent-wins — a property-based-test home using fast-check. The full architectural narrative, the axiom catalog, and guidance for adding new projections lives in [`docs/system/PROJECTION-PATTERN.md`](../../docs/system/PROJECTION-PATTERN.md).
149
+
139
150
  #### Why startup rebuild exists
140
151
 
141
152
  The materialized views directory (`stateDir`) is **ephemeral by design** — it's safe to wipe (container recreation, `semiont destroy`, dev cleanup), and the event log under `.semiont/events/` is the single source of truth. `rebuildAll` is what makes "ephemeral" safe: any time `stateDir` goes empty, the next process start repopulates it from the event log.
@@ -0,0 +1,51 @@
1
+ /**
2
+ * EventLog - Event Persistence Layer
3
+ *
4
+ * Single Responsibility: Event persistence only
5
+ * - Appends events to storage (JSONL files)
6
+ * - Retrieves events by resource
7
+ * - Queries events with filters
8
+ *
9
+ * Does NOT handle:
10
+ * - Pub/sub notifications (see EventBus)
11
+ * - View updates (see ViewManager)
12
+ */
13
+ import { type ResourceId, type StoredEvent, type EventQuery, type EventInput, type Logger } from '@semiont/core';
14
+ import type { SemiontProject } from '@semiont/core/node';
15
+ import { EventStorage } from './storage/event-storage';
16
+ export interface EventLogConfig {
17
+ project: SemiontProject;
18
+ enableSharding?: boolean;
19
+ maxEventsPerFile?: number;
20
+ }
21
+ export declare class EventLog {
22
+ readonly storage: EventStorage;
23
+ constructor(config: EventLogConfig, logger?: Logger);
24
+ /**
25
+ * Append event to log
26
+ * @param event - Resource event (from @semiont/core)
27
+ * @param resourceId - Branded ResourceId (from @semiont/core)
28
+ * @param options.correlationId - Optional command correlation id (stored on metadata)
29
+ * @returns Stored event with metadata (sequence number, timestamp, checksum)
30
+ */
31
+ append(event: EventInput, resourceId: ResourceId, options?: {
32
+ correlationId?: string;
33
+ }): Promise<StoredEvent>;
34
+ /**
35
+ * Get all events for a resource
36
+ * @param resourceId - Branded ResourceId (from @semiont/core)
37
+ */
38
+ getEvents(resourceId: ResourceId): Promise<StoredEvent[]>;
39
+ /**
40
+ * Get all resource IDs
41
+ * @returns Array of branded ResourceId types
42
+ */
43
+ getAllResourceIds(): Promise<ResourceId[]>;
44
+ /**
45
+ * Query events with filter
46
+ * @param resourceId - Branded ResourceId (from @semiont/core)
47
+ * @param filter - Optional event filter
48
+ */
49
+ queryEvents(resourceId: ResourceId, filter?: EventQuery): Promise<StoredEvent[]>;
50
+ }
51
+ //# sourceMappingURL=event-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-log.d.ts","sourceRoot":"","sources":["../src/event-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AACjH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,cAAc,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,QAAQ;IAEnB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;gBAEnB,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,MAAM;IAOnD;;;;;;OAMG;IACG,MAAM,CACV,KAAK,EAAE,UAAU,EACjB,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GACnC,OAAO,CAAC,WAAW,CAAC;IAIvB;;;OAGG;IACG,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAI/D;;;OAGG;IACG,iBAAiB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAIhD;;;;OAIG;IACG,WAAW,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;CAavF"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Event Store Factory
3
+ *
4
+ * Factory function for creating EventStore instances with standard configuration.
5
+ * This is the canonical way to instantiate an EventStore.
6
+ */
7
+ import type { SemiontProject } from '@semiont/core/node';
8
+ import type { EventBus as CoreEventBus, Logger } from '@semiont/core';
9
+ import { EventStore } from './event-store';
10
+ /**
11
+ * Create and initialize an EventStore instance
12
+ *
13
+ * @param project - SemiontProject instance
14
+ * @param eventBus - @semiont/core EventBus for publishing domain events
15
+ * @param logger - Optional logger for structured logging
16
+ * @returns Configured EventStore instance ready for use
17
+ */
18
+ export declare function createEventStore(project: SemiontProject, eventBus: CoreEventBus, logger?: Logger): EventStore;
19
+ //# sourceMappingURL=event-store-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-store-factory.d.ts","sourceRoot":"","sources":["../src/event-store-factory.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG3C;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,YAAY,EACtB,MAAM,CAAC,EAAE,MAAM,GACd,UAAU,CAUZ"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * EventStore - Orchestration Layer
3
+ *
4
+ * Coordinates event sourcing operations:
5
+ * - EventLog: Event persistence (append, retrieve, query)
6
+ * - ViewManager: View materialization (resource and system)
7
+ * - Core EventBus: Publishes StoredEvent to typed channels after persistence
8
+ *
9
+ * appendEvent() is the single write path:
10
+ * 1. Persist to EventLog
11
+ * 2. Materialize views
12
+ * 3. Enrich (optional callback — attach post-materialization data)
13
+ * 4. Publish StoredEvent to global and resource-scoped typed channels
14
+ */
15
+ import type { EventInput, StoredEvent, ResourceId, Logger } from '@semiont/core';
16
+ import { EventBus as CoreEventBus } from '@semiont/core';
17
+ import type { SemiontProject } from '@semiont/core/node';
18
+ import type { ViewStorage } from './storage/view-storage';
19
+ import { EventLog } from './event-log';
20
+ import { ViewManager } from './view-manager';
21
+ export type EnrichEvent = (event: StoredEvent, resourceId: ResourceId) => Promise<StoredEvent>;
22
+ export declare class EventStore {
23
+ readonly log: EventLog;
24
+ readonly views: ViewManager;
25
+ readonly viewStorage: ViewStorage;
26
+ readonly coreEventBus: CoreEventBus;
27
+ private enrichEvent;
28
+ constructor(project: SemiontProject, stateDir: string, viewStorage: ViewStorage, coreEventBus: CoreEventBus, logger?: Logger);
29
+ setEnrichEvent(fn: EnrichEvent): void;
30
+ /**
31
+ * Append an event to the store
32
+ * Coordinates: persistence → view → enrich → notification
33
+ *
34
+ * @param options.correlationId - Optional id propagated from a command.
35
+ */
36
+ appendEvent(event: EventInput, options?: {
37
+ correlationId?: string;
38
+ }): Promise<StoredEvent>;
39
+ }
40
+ //# sourceMappingURL=event-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-store.d.ts","sourceRoot":"","sources":["../src/event-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACV,MAAM,EACP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,WAAW,EAA0B,MAAM,gBAAgB,CAAC;AAErE,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;AAE/F,qBAAa,UAAU;IACrB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,OAAO,CAAC,WAAW,CAA4B;gBAG7C,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,YAAY,EAC1B,MAAM,CAAC,EAAE,MAAM;IAajB,cAAc,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI;IAIrC;;;;;OAKG;IACG,WAAW,CACf,KAAK,EAAE,UAAU,EACjB,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GACnC,OAAO,CAAC,WAAW,CAAC;CAoCxB"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Identifier utilities for event sourcing
3
+ */
4
+ /**
5
+ * Generate a unique annotation ID (bare nanoid)
6
+ *
7
+ * @returns A bare annotation ID (e.g., "V1StGXR8_Z5jdHi6B-myT")
8
+ */
9
+ export declare function generateAnnotationId(): string;
10
+ //# sourceMappingURL=identifier-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifier-utils.d.ts","sourceRoot":"","sources":["../src/identifier-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C"}