@ossy/event-store 1.0.1 → 1.2.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.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/event-store",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "Ossy Event Store — Aggregate, EventStore, and MongoDB client",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,11 +23,12 @@
23
23
  "author": "Ossy <yourfriends@ossy.se> (https://ossy.se)",
24
24
  "license": "MIT",
25
25
  "dependencies": {
26
+ "@ossy/observability": "^1.2.0",
26
27
  "mongodb": "^7.2.0",
27
28
  "nanoid": "^5.1.11"
28
29
  },
29
30
  "files": [
30
31
  "src"
31
32
  ],
32
- "gitHead": "b8cbdc545a659e96af8948aa08a89316a2c2d193"
33
+ "gitHead": "46cdd391ec01ea9210543ee7b14661f77079a7e7"
33
34
  }
@@ -1,14 +1,8 @@
1
1
  import { Aggregate } from './aggregate.js'
2
2
  import { EventStore } from './event-store.js'
3
+ import { createLogger } from '@ossy/observability'
3
4
 
4
- const _debug = Boolean(process.env.DEBUG)
5
-
6
- function _log(message) { console.log(message) }
7
- function _warn(message) { console.warn(message) }
8
- function _logDebug(message) {
9
- if (!_debug) return
10
- console.log(message)
11
- }
5
+ const log = createLogger('event-store')
12
6
 
13
7
  export class AggregateRebuild {
14
8
 
@@ -25,18 +19,18 @@ export class AggregateRebuild {
25
19
  static registerAggregate(mod) {
26
20
  const { id, Aggregate: AggregateClass } = mod
27
21
  if (typeof AggregateClass !== 'function' || !id) {
28
- _warn('[AggregateRebuild] registerAggregate: invalid module — expected { id, Aggregate }')
22
+ log.warn('[AggregateRebuild] registerAggregate: invalid module — expected { id, Aggregate }')
29
23
  return
30
24
  }
31
25
  AggregateRebuild._aggregateMap[id] = AggregateClass
32
- _logDebug(`[AggregateRebuild] Registered aggregate: ${id}`)
26
+ log.debug(`[AggregateRebuild] Registered aggregate: ${id}`)
33
27
  }
34
28
 
35
29
  static async BuildAndSave(aggregateType, aggregateId) {
36
30
  const AggregateRoot = AggregateRebuild._aggregateMap[aggregateType]
37
31
 
38
32
  if (!AggregateRoot) {
39
- _logDebug(`[AggregateRebuild][BuildAndSave] Skipping unregistered aggregate type: ${aggregateType}`)
33
+ log.debug(`[AggregateRebuild][BuildAndSave] Skipping unregistered aggregate type: ${aggregateType}`)
40
34
  return
41
35
  }
42
36
 
@@ -45,12 +39,12 @@ export class AggregateRebuild {
45
39
  }
46
40
 
47
41
  static async BuildAndSaveAll() {
48
- _logDebug('[AggregateRebuild][BuildAndSaveAll] Starting building aggregates')
42
+ log.debug('[AggregateRebuild][BuildAndSaveAll] Starting building aggregates')
49
43
 
50
44
  const knownTypes = new Set(Object.keys(AggregateRebuild._aggregateMap))
51
45
 
52
46
  if (knownTypes.size === 0) {
53
- _warn('[AggregateRebuild][BuildAndSaveAll] No aggregates registered — skipping rebuild')
47
+ log.warn('[AggregateRebuild][BuildAndSaveAll] No aggregates registered — skipping rebuild')
54
48
  return
55
49
  }
56
50
 
@@ -63,9 +57,9 @@ export class AggregateRebuild {
63
57
  .then(results => {
64
58
  const failed = results.filter(result => result.status === 'rejected')
65
59
  const success = results.filter(result => result.status === 'fulfilled')
66
- _log('[AggregateRebuild][BuildAndSaveAll] Finished building aggregates')
67
- _log(`[AggregateRebuild][BuildAndSaveAll] Failed: ${failed.length}`)
68
- _log(`[AggregateRebuild][BuildAndSaveAll] Success: ${success.length}`)
60
+ log.info('[AggregateRebuild][BuildAndSaveAll] Finished building aggregates')
61
+ log.info(`[AggregateRebuild][BuildAndSaveAll] Failed: ${failed.length}`)
62
+ log.info(`[AggregateRebuild][BuildAndSaveAll] Success: ${success.length}`)
69
63
  })
70
64
  }
71
65
  }
package/src/aggregate.js CHANGED
@@ -1,15 +1,9 @@
1
1
  import { nanoid } from 'nanoid'
2
2
  import { Mongo, withMongoReconnect } from './mongodb.js'
3
3
  import { EventStore } from './event-store.js'
4
+ import { createLogger } from '@ossy/observability'
4
5
 
5
- const _debug = Boolean(process.env.DEBUG)
6
-
7
- function _log(message) { console.log(message) }
8
- function _logError(message) { console.error(message) }
9
- function _logDebug(message) {
10
- if (!_debug) return
11
- console.log(message)
12
- }
6
+ const log = createLogger('event-store')
13
7
 
14
8
  /**
15
9
  * Utility class that helps interact with event streams.
@@ -35,7 +29,7 @@ export class Aggregate {
35
29
  */
36
30
  static Of(AggregateRoot, identifier) {
37
31
 
38
- _log(`[Aggregate][Of()][${AggregateRoot?.AggregateType}] Creating aggregate root`)
32
+ log.info(`[Aggregate][Of()][${AggregateRoot?.AggregateType}] Creating aggregate root`)
39
33
 
40
34
  if (typeof identifier === 'string') {
41
35
 
@@ -47,20 +41,20 @@ export class Aggregate {
47
41
  }
48
42
 
49
43
  if (typeof identifier === 'object') {
50
- _log('[Aggregate] Creating new aggregate event stream')
44
+ log.info('[Aggregate] Creating new aggregate event stream')
51
45
  return Aggregate.Add(identifier)(new Aggregate(AggregateRoot, identifier.aggregateId || nanoid(), []))
52
46
  }
53
47
 
54
- _logError('[Aggregate] No recognizable identifier provided')
48
+ log.error('[Aggregate] No recognizable identifier provided')
55
49
  return Promise.reject()
56
50
  }
57
51
 
58
52
  static Find(identifier) {
59
- _log(`[Aggregate][Find()] Fetching aggregate for ${identifier}`)
53
+ log.info(`[Aggregate][Find()] Fetching aggregate for ${identifier}`)
60
54
  return withMongoReconnect(() => Aggregate.Collection.findOne({ id: identifier }))
61
55
  .then(aggregate => {
62
56
  if (!aggregate) {
63
- _logDebug(`[Aggregate][Find()] No aggregate found for ${identifier}`)
57
+ log.debug(`[Aggregate][Find()] No aggregate found for ${identifier}`)
64
58
  }
65
59
  return aggregate
66
60
  })
@@ -89,17 +83,17 @@ export class Aggregate {
89
83
 
90
84
  static Save() {
91
85
  return aggregate => {
92
- _logDebug(`[Aggregate][Save] Saving state for ${aggregate.type} ${aggregate.id}`)
86
+ log.debug(`[Aggregate][Save] Saving state for ${aggregate.type} ${aggregate.id}`)
93
87
 
94
88
  if (!aggregate.events || aggregate.events.length === 0) {
95
- _logDebug(`[Aggregate][Save] No events to save for ${aggregate.type} ${aggregate.id}`)
89
+ log.debug(`[Aggregate][Save] No events to save for ${aggregate.type} ${aggregate.id}`)
96
90
  return Promise.resolve()
97
91
  }
98
92
 
99
93
  const latestEventVersion = [...aggregate.events].sort((a, b) => a.aggregateVersion - b.aggregateVersion).pop()?.aggregateVersion
100
94
 
101
95
  if (latestEventVersion < aggregate.version) {
102
- _logDebug(`[Aggregate][Save] No new events to save for ${aggregate.type} ${aggregate.id}`)
96
+ log.debug(`[Aggregate][Save] No new events to save for ${aggregate.type} ${aggregate.id}`)
103
97
  return Promise.resolve()
104
98
  }
105
99
 
@@ -116,8 +110,7 @@ export class Aggregate {
116
110
  }, { upsert: true })
117
111
  )
118
112
  .catch(error => {
119
- _logError(`[Aggregate][Save] Failed to save state for ${aggregate.type} ${aggregate.id}`)
120
- console.error(error)
113
+ log.error(`[Aggregate][Save] Failed to save state for ${aggregate.type} ${aggregate.id}`, undefined, error)
121
114
  })
122
115
  }
123
116
  }
@@ -130,7 +123,7 @@ export class Aggregate {
130
123
 
131
124
  static View(view) {
132
125
  return aggregate => {
133
- _logDebug(`[Aggregate][View] Building view for ${aggregate.type} ${aggregate.id}`)
126
+ log.debug(`[Aggregate][View] Building view for ${aggregate.type} ${aggregate.id}`)
134
127
 
135
128
  if (typeof view === 'function') {
136
129
  return view(aggregate.events, aggregate.state)
@@ -150,7 +143,7 @@ export class Aggregate {
150
143
  * @param {Object} aggregate - The object representing the aggregate of all the events
151
144
  */
152
145
  constructor(AggregateRoot, id, events, aggregate) {
153
- _logDebug(`[Aggregate][Constructor] Assembling ${AggregateRoot.AggregateType} ${id}`)
146
+ log.debug(`[Aggregate][Constructor] Assembling ${AggregateRoot.AggregateType} ${id}`)
154
147
 
155
148
  this.id = id;
156
149
  this.version = aggregate?.version || 0
@@ -1,17 +1,7 @@
1
1
  import { Mongo, withMongoReconnect } from './mongodb.js'
2
+ import { createLogger } from '@ossy/observability'
2
3
 
3
- const _debug = Boolean(process.env.DEBUG)
4
-
5
- function _log(message) { console.log(message) }
6
- function _logError(message, error) {
7
- console.error(message)
8
- if (error) console.error('[Reason]:', error)
9
- }
10
- function _logDebug(message, data) {
11
- if (!_debug) return
12
- console.log(message)
13
- if (data !== undefined) console.log('[DEBUG DATA]:', data)
14
- }
4
+ const log = createLogger('event-store')
15
5
 
16
6
  /**
17
7
  * Database queries related to workspace events
@@ -24,8 +14,8 @@ export class EventStore {
24
14
  }
25
15
 
26
16
  static AppendEvent(event) {
27
- _log('[EventStore] Appending event')
28
- _logDebug('[EventStore] Event', event)
17
+ log.info('[EventStore] Appending event')
18
+ log.debug('[EventStore] Event', { event })
29
19
 
30
20
  return withMongoReconnect(() => EventStore.Collection.insertOne(event))
31
21
  .then(insertResult => {
@@ -34,33 +24,33 @@ export class EventStore {
34
24
  : Promise.reject()
35
25
  })
36
26
  .catch(error => {
37
- _logError('[EventStore] Could not append event', error)
27
+ log.error('[EventStore] Could not append event', undefined, error)
38
28
  return Promise.reject()
39
29
  })
40
30
  }
41
31
 
42
32
  static FindEvent(query) {
43
- _log('[EventStore] Searching for event')
44
- _logDebug('[EventStore] Query', query)
33
+ log.info('[EventStore] Searching for event')
34
+ log.debug('[EventStore] Query', { query })
45
35
 
46
36
  return withMongoReconnect(() => EventStore.Collection.findOne(query))
47
37
  .then(event => !!event ? event : Promise.reject())
48
38
  .catch(error => {
49
- _logError('[EventStore] No event found', error)
39
+ log.error('[EventStore] No event found', undefined, error)
50
40
  return Promise.reject()
51
41
  })
52
42
  }
53
43
 
54
44
  static FindEvents(query) {
55
- _log('[EventStore] Searching for events')
56
- _logDebug('[EventStore] Query', query)
45
+ log.info('[EventStore] Searching for events')
46
+ log.debug('[EventStore] Query', { query })
57
47
 
58
48
  return withMongoReconnect(() =>
59
49
  EventStore.Collection.find(query, { sort: { aggregateVersion: 1 } }).toArray()
60
50
  )
61
51
  .then(events => !!events?.length ? events : Promise.reject())
62
52
  .catch(error => {
63
- _logError('[EventStore] No events found', error)
53
+ log.error('[EventStore] No events found', undefined, error)
64
54
  return Promise.reject()
65
55
  })
66
56
  }
@@ -69,7 +59,7 @@ export class EventStore {
69
59
  aggregateId,
70
60
  fromVersion = 0
71
61
  }) {
72
- _logDebug(`[EventStore][GetEventStream()] Fetching stream ${aggregateId} from version ${fromVersion}`)
62
+ log.debug(`[EventStore][GetEventStream()] Fetching stream ${aggregateId} from version ${fromVersion}`)
73
63
 
74
64
  const query = {
75
65
  aggregateId: aggregateId,
@@ -82,13 +72,13 @@ export class EventStore {
82
72
 
83
73
  return withMongoReconnect(() => EventStore.Collection.find(query, options).toArray())
84
74
  .then(events => {
85
- _logDebug(`[EventStore][GetEventStream()] Found ${events.length} events for ${aggregateId}`)
75
+ log.debug(`[EventStore][GetEventStream()] Found ${events.length} events for ${aggregateId}`)
86
76
  return events
87
77
  })
88
78
  }
89
79
 
90
80
  static GetEventStreams() {
91
- _log('[EventStore][GetEventStreams()] Fetching event streams')
81
+ log.info('[EventStore][GetEventStreams()] Fetching event streams')
92
82
 
93
83
  return EventStore.Aggregate([
94
84
  {
@@ -108,18 +98,18 @@ export class EventStore {
108
98
  }
109
99
  ])
110
100
  .then(result => {
111
- _logDebug(`[EventStore][GetEventStreams()] found ${result.length} streams`)
101
+ log.debug(`[EventStore][GetEventStreams()] found ${result.length} streams`)
112
102
  return result
113
103
  })
114
104
  }
115
105
 
116
106
  static Aggregate(pipeline) {
117
- _log('[EventStore] Running aggregation pipeline')
107
+ log.info('[EventStore] Running aggregation pipeline')
118
108
 
119
109
  return withMongoReconnect(() => EventStore.Collection.aggregate(pipeline).toArray())
120
110
  .then(events => (Array.isArray(events) ? events : []))
121
111
  .catch(error => {
122
- _logError('[EventStore] Aggregation failed', error)
112
+ log.error('[EventStore] Aggregation failed', undefined, error)
123
113
  return Promise.reject(error)
124
114
  })
125
115
  }
package/src/mongodb.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createRequire } from 'module'
2
2
  import { MongoClient } from 'mongodb'
3
+ import { createLogger } from '@ossy/observability'
3
4
 
4
5
  // When bundled as ESM by @ossy/app (Rollup + @rollup/plugin-commonjs), the
5
6
  // MongoDB driver's resolveRuntimeAdapters references `require` as a free
@@ -9,6 +10,8 @@ if (!globalThis.require) {
9
10
  globalThis.require = createRequire(import.meta.url)
10
11
  }
11
12
 
13
+ const log = createLogger('event-store')
14
+
12
15
  const MONGO_URL = process.env.DB_URL || 'mongodb://mongodb:27017/'
13
16
  const DB_NAME = process.env.DB_NAME || 'test'
14
17
 
@@ -33,7 +36,7 @@ export function isMongoTopologyClosedError(error) {
33
36
  export function withMongoReconnect(run) {
34
37
  return run().catch((err) => {
35
38
  if (isMongoTopologyClosedError(err)) {
36
- console.log('[Mongo] Topology closed — resetting client and retrying once')
39
+ log.info('[Mongo] Topology closed — resetting client and retrying once')
37
40
  Mongo.resetClient()
38
41
  return run()
39
42
  }
@@ -59,7 +62,7 @@ export class Mongo {
59
62
  if (prev) {
60
63
  prev.close().catch(() => {})
61
64
  }
62
- console.log('[Mongo] New MongoClient instance created')
65
+ log.info('[Mongo] New MongoClient instance created')
63
66
  }
64
67
 
65
68
  /** Used by tests / graceful shutdown; resets singleton so a later operation opens a new client. */