@eventualize/core 1.0.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/dist/EvDbEventStore.d.ts +123 -0
- package/dist/EvDbEventStore.js +182 -0
- package/dist/EvDbEventStore.js.map +1 -0
- package/dist/EvDbStream.d.ts +49 -0
- package/dist/EvDbStream.js +115 -0
- package/dist/EvDbStream.js.map +1 -0
- package/dist/EvDbStreamFactory.d.ts +97 -0
- package/dist/EvDbStreamFactory.js +155 -0
- package/dist/EvDbStreamFactory.js.map +1 -0
- package/dist/EvDbView.d.ts +34 -0
- package/dist/EvDbView.js +64 -0
- package/dist/EvDbView.js.map +1 -0
- package/dist/EvDbViewFactory.d.ts +45 -0
- package/dist/EvDbViewFactory.js +58 -0
- package/dist/EvDbViewFactory.js.map +1 -0
- package/package.json +25 -0
- package/src/EvDbEventStore.ts +302 -0
- package/src/EvDbStream.ts +184 -0
- package/src/EvDbStreamFactory.ts +290 -0
- package/src/EvDbView.ts +100 -0
- package/src/EvDbViewFactory.ts +123 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import EvDbStreamCursor from '@eventualize/types/EvDbStreamCursor';
|
|
2
|
+
import EvDbStreamAddress from '@eventualize/types/EvDbStreamAddress';
|
|
3
|
+
import EvDbStream from './EvDbStream.js';
|
|
4
|
+
import { createViewFactory } from './EvDbViewFactory.js';
|
|
5
|
+
/**
|
|
6
|
+
* Stream Factory - creates stream instances with configured views and dynamic event methods
|
|
7
|
+
*/
|
|
8
|
+
export class EvDbStreamFactory {
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
this.DynamicStreamClass = this.createDynamicStreamClass();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates a dynamic stream class with event-specific methods and view accessors
|
|
15
|
+
*/
|
|
16
|
+
createDynamicStreamClass() {
|
|
17
|
+
const eventTypes = this.config.eventTypes;
|
|
18
|
+
const viewNames = this.config.viewNames;
|
|
19
|
+
const messagesProducer = (event, viewsState) => {
|
|
20
|
+
const eventType = eventTypes.find(e => e.eventName === event.eventType);
|
|
21
|
+
if (!eventType || !eventType.eventMessagesProducer)
|
|
22
|
+
return [];
|
|
23
|
+
return eventType.eventMessagesProducer(event, viewsState);
|
|
24
|
+
};
|
|
25
|
+
class DynamicStream extends EvDbStream {
|
|
26
|
+
constructor(streamType, views, streamStorageAdapter, streamId, lastStreamOffset) {
|
|
27
|
+
super(streamType, views, streamStorageAdapter, streamId, lastStreamOffset, messagesProducer);
|
|
28
|
+
this.views = {};
|
|
29
|
+
// Create view accessors
|
|
30
|
+
views.forEach((view, index) => {
|
|
31
|
+
const viewName = viewNames[index];
|
|
32
|
+
if (viewName) {
|
|
33
|
+
this.views[viewName] = view;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Add dynamic methods for each event type
|
|
39
|
+
eventTypes.forEach(({ eventName }) => {
|
|
40
|
+
const methodName = `appendEvent${eventName}`;
|
|
41
|
+
DynamicStream.prototype[methodName] = async function (event) {
|
|
42
|
+
return this.appendEvent(event);
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
return DynamicStream;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Creates a stream instance with all configured views and dynamic event methods
|
|
49
|
+
*/
|
|
50
|
+
create(streamId, streamStorageAdapter, snapshotStorageAdapter, lastStreamOffset = 0) {
|
|
51
|
+
const views = this.createViews(streamId, snapshotStorageAdapter);
|
|
52
|
+
return new this.DynamicStreamClass(this.config.streamType, views, streamStorageAdapter, streamId, lastStreamOffset);
|
|
53
|
+
}
|
|
54
|
+
createViews(streamId, snapshotStorageAdapter) {
|
|
55
|
+
const views = this.config.viewFactories.map(factory => factory.create(streamId, snapshotStorageAdapter));
|
|
56
|
+
return views;
|
|
57
|
+
}
|
|
58
|
+
getViews(streamId, snapshotStorageAdapter) {
|
|
59
|
+
const getViewPromises = this.config.viewFactories.map(viewFactory => viewFactory.get(streamId, snapshotStorageAdapter));
|
|
60
|
+
return getViewPromises;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Fetches from storage a stream instance with all configured views and dynamic event methods
|
|
64
|
+
*/
|
|
65
|
+
async get(streamId, streamStorageAdapter, snapshotStorageAdapter) {
|
|
66
|
+
const streamAddress = new EvDbStreamAddress(this.config.streamType, streamId);
|
|
67
|
+
const views = await Promise.all(this.getViews(streamId, snapshotStorageAdapter));
|
|
68
|
+
if (!views.length) {
|
|
69
|
+
const lastStreamOffset = await streamStorageAdapter.getLastOffsetAsync(streamAddress);
|
|
70
|
+
const stream = this.create(streamId, streamStorageAdapter, snapshotStorageAdapter, lastStreamOffset);
|
|
71
|
+
return stream;
|
|
72
|
+
}
|
|
73
|
+
const lowestViewOffset = views.reduce((lowestOffset, currentView) => Math.min(lowestOffset, currentView.storeOffset), Number.MAX_VALUE);
|
|
74
|
+
const streamCursor = new EvDbStreamCursor(streamAddress, lowestViewOffset + 1);
|
|
75
|
+
const events = await streamStorageAdapter.getEventsAsync(streamCursor);
|
|
76
|
+
let streamOffset = lowestViewOffset;
|
|
77
|
+
for await (const event of events) {
|
|
78
|
+
views.forEach(view => view.applyEvent(event));
|
|
79
|
+
streamOffset = event.streamCursor.offset;
|
|
80
|
+
}
|
|
81
|
+
return new this.DynamicStreamClass(this.config.streamType, views, streamStorageAdapter, streamId, streamOffset);
|
|
82
|
+
}
|
|
83
|
+
getStreamType() {
|
|
84
|
+
return this.config.streamType;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Factory function to create a StreamFactory
|
|
89
|
+
*/
|
|
90
|
+
export function createEvDbStreamFactory(config) {
|
|
91
|
+
return new EvDbStreamFactory(config);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Fluent builder for creating stream factories with inferred event types
|
|
95
|
+
*/
|
|
96
|
+
export class StreamFactoryBuilder {
|
|
97
|
+
constructor(streamType) {
|
|
98
|
+
this.streamType = streamType;
|
|
99
|
+
this.viewFactories = [];
|
|
100
|
+
this.eventTypes = [];
|
|
101
|
+
this.viewNames = [];
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Register event type for dynamic method generation - infers the event name from class name
|
|
105
|
+
*/
|
|
106
|
+
withEventType(eventClass, eventMessagesProducer) {
|
|
107
|
+
// Use the class name as the event name
|
|
108
|
+
const eventName = eventClass.name;
|
|
109
|
+
this.eventTypes.push({ eventClass, eventName, eventMessagesProducer });
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Add a view with inline handler definition
|
|
114
|
+
* This can only be called AFTER withEventType calls to ensure type safety
|
|
115
|
+
*/
|
|
116
|
+
withView(viewName, stateClass, handlers) {
|
|
117
|
+
// Create default state instance
|
|
118
|
+
const defaultState = new stateClass();
|
|
119
|
+
// Create the view factory
|
|
120
|
+
const viewFactory = createViewFactory({
|
|
121
|
+
viewName,
|
|
122
|
+
streamType: this.streamType,
|
|
123
|
+
defaultState,
|
|
124
|
+
handlers
|
|
125
|
+
});
|
|
126
|
+
this.viewFactories.push(viewFactory);
|
|
127
|
+
this.viewNames.push(viewName);
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Add a pre-created view factory (legacy support)
|
|
132
|
+
*/
|
|
133
|
+
withViewFactory(viewName, viewFactory) {
|
|
134
|
+
this.viewFactories.push(viewFactory);
|
|
135
|
+
this.viewNames.push(viewName);
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Build the stream factory
|
|
140
|
+
*/
|
|
141
|
+
build() {
|
|
142
|
+
const factory = new EvDbStreamFactory({
|
|
143
|
+
streamType: this.streamType,
|
|
144
|
+
viewFactories: this.viewFactories,
|
|
145
|
+
eventTypes: this.eventTypes,
|
|
146
|
+
viewNames: this.viewNames
|
|
147
|
+
});
|
|
148
|
+
// Return factory with type helper for stream type extraction
|
|
149
|
+
return Object.assign(factory, {
|
|
150
|
+
// This is a type-only property for extracting the stream type
|
|
151
|
+
StreamType: null
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=EvDbStreamFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EvDbStreamFactory.js","sourceRoot":"","sources":["../src/EvDbStreamFactory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import IEvDbViewStore, { IEvDbViewStoreGeneric } from "@eventualize/types/IEvDbViewStore";
|
|
2
|
+
import EvDbViewAddress from "@eventualize/types/EvDbViewAddress";
|
|
3
|
+
import IEvDbStorageSnapshotAdapter from "@eventualize/types/IEvDbStorageSnapshotAdapter";
|
|
4
|
+
import EvDbEvent from "@eventualize/types/src/EvDbEvent";
|
|
5
|
+
import { EvDbStoredSnapshotData } from "@eventualize/types/EvDbStoredSnapshotData";
|
|
6
|
+
import { EvDbStoredSnapshotResult } from "@eventualize/types/EvDbStoredSnapshotResult";
|
|
7
|
+
import IEvDbEventMetadata from "@eventualize/types/IEvDbEventMetadata";
|
|
8
|
+
import IEvDbEventPayload from "@eventualize/types/IEvDbEventPayload";
|
|
9
|
+
export declare abstract class EvDbViewRaw implements IEvDbViewStore {
|
|
10
|
+
private readonly _storageAdapter;
|
|
11
|
+
readonly address: EvDbViewAddress;
|
|
12
|
+
private _memoryOffset;
|
|
13
|
+
private _storeOffset;
|
|
14
|
+
private _storedAt;
|
|
15
|
+
protected constructor(_storageAdapter: IEvDbStorageSnapshotAdapter, address: EvDbViewAddress, snapshot: EvDbStoredSnapshotResult<any>);
|
|
16
|
+
abstract getSnapshotData(): EvDbStoredSnapshotData;
|
|
17
|
+
get storedAt(): Date;
|
|
18
|
+
get storeOffset(): number;
|
|
19
|
+
get memoryOffset(): number;
|
|
20
|
+
shouldStoreSnapshot(offsetGapFromLastSave: number, durationSinceLastSaveMs: number): boolean;
|
|
21
|
+
applyEvent(e: EvDbEvent): void;
|
|
22
|
+
store(): Promise<void>;
|
|
23
|
+
protected abstract onApplyEvent(e: EvDbEvent): void;
|
|
24
|
+
}
|
|
25
|
+
export declare abstract class EvDbView<TState> extends EvDbViewRaw implements IEvDbViewStoreGeneric<TState> {
|
|
26
|
+
readonly defaultState: TState;
|
|
27
|
+
protected getDefaultState(): TState;
|
|
28
|
+
protected _state: TState;
|
|
29
|
+
get state(): TState;
|
|
30
|
+
constructor(address: EvDbViewAddress, storageAdapter: IEvDbStorageSnapshotAdapter, snapshot: EvDbStoredSnapshotResult<TState>, defaultState: TState);
|
|
31
|
+
getSnapshotData(): EvDbStoredSnapshotData;
|
|
32
|
+
abstract handleOnApply(oldState: TState, event: IEvDbEventPayload, metadata: IEvDbEventMetadata): TState;
|
|
33
|
+
protected onApplyEvent(e: EvDbEvent): void;
|
|
34
|
+
}
|
package/dist/EvDbView.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { EvDbStoredSnapshotData } from "@eventualize/types/EvDbStoredSnapshotData";
|
|
2
|
+
export class EvDbViewRaw {
|
|
3
|
+
constructor(_storageAdapter, address, snapshot) {
|
|
4
|
+
this._storageAdapter = _storageAdapter;
|
|
5
|
+
this.address = address;
|
|
6
|
+
const storeOffset = snapshot.offset ?? 0;
|
|
7
|
+
this._memoryOffset = storeOffset;
|
|
8
|
+
this._storeOffset = storeOffset;
|
|
9
|
+
this._storedAt = snapshot.storedAt ?? new Date();
|
|
10
|
+
}
|
|
11
|
+
get storedAt() { return this._storedAt; }
|
|
12
|
+
;
|
|
13
|
+
get storeOffset() { return this._storeOffset; }
|
|
14
|
+
;
|
|
15
|
+
get memoryOffset() { return this._memoryOffset; }
|
|
16
|
+
;
|
|
17
|
+
shouldStoreSnapshot(offsetGapFromLastSave, durationSinceLastSaveMs) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
applyEvent(e) {
|
|
21
|
+
const offset = e.streamCursor.offset;
|
|
22
|
+
if (this.memoryOffset >= offset) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
this.onApplyEvent(e);
|
|
26
|
+
this._memoryOffset = offset;
|
|
27
|
+
}
|
|
28
|
+
async store() {
|
|
29
|
+
const eventsSinceLatestSnapshot = this.memoryOffset - this.storeOffset;
|
|
30
|
+
const secondsSinceLatestSnapshot = new Date().getTime() - this.storedAt.getTime();
|
|
31
|
+
if (!this.shouldStoreSnapshot(eventsSinceLatestSnapshot, secondsSinceLatestSnapshot)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const snapshotData = this.getSnapshotData();
|
|
35
|
+
await this._storageAdapter.storeSnapshotAsync(snapshotData);
|
|
36
|
+
this._storeOffset = this._memoryOffset;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export class EvDbView extends EvDbViewRaw {
|
|
40
|
+
getDefaultState() {
|
|
41
|
+
return this.defaultState;
|
|
42
|
+
}
|
|
43
|
+
;
|
|
44
|
+
get state() { return this._state; }
|
|
45
|
+
// public getState(): TState {
|
|
46
|
+
// return this._state;
|
|
47
|
+
// }
|
|
48
|
+
constructor(address, storageAdapter, snapshot, defaultState) {
|
|
49
|
+
super(storageAdapter, address, snapshot);
|
|
50
|
+
this.defaultState = defaultState;
|
|
51
|
+
this._state = this.getDefaultState();
|
|
52
|
+
if (snapshot.offset === 0)
|
|
53
|
+
this._state = this.getDefaultState();
|
|
54
|
+
else
|
|
55
|
+
this._state = snapshot.state;
|
|
56
|
+
}
|
|
57
|
+
getSnapshotData() {
|
|
58
|
+
return EvDbStoredSnapshotData.fromAddress(this.address, this.memoryOffset, this.storeOffset, this._state);
|
|
59
|
+
}
|
|
60
|
+
onApplyEvent(e) {
|
|
61
|
+
this._state = this.handleOnApply(this._state, e.payload, e);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=EvDbView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EvDbView.js","sourceRoot":"","sources":["../src/EvDbView.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { EvDbView } from './EvDbView.js';
|
|
2
|
+
import IEvDbEventPayload from "@eventualize/types/IEvDbEventPayload";
|
|
3
|
+
import IEvDbEventMetadata from '@eventualize/types/IEvDbEventMetadata';
|
|
4
|
+
import IEvDbStorageSnapshotAdapter from '@eventualize/types/IEvDbStorageSnapshotAdapter';
|
|
5
|
+
/**
|
|
6
|
+
* Handler function type for applying an event to state
|
|
7
|
+
*/
|
|
8
|
+
export type EvDbViewEventHandler<TState, TEvent extends IEvDbEventPayload> = (oldState: TState, event: TEvent, metadata: IEvDbEventMetadata) => TState;
|
|
9
|
+
/**
|
|
10
|
+
* Map of event handlers - one handler per event type in the union
|
|
11
|
+
* Key is the payloadType string, value is the handler function
|
|
12
|
+
*/
|
|
13
|
+
export type EvDbStreamEventHandlersMap<TState, TEvents extends IEvDbEventPayload> = {
|
|
14
|
+
[K in TEvents['payloadType']]: EvDbViewEventHandler<TState, Extract<TEvents, {
|
|
15
|
+
payloadType: K;
|
|
16
|
+
}>>;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Configuration for creating a view
|
|
20
|
+
*/
|
|
21
|
+
export interface ViewConfig<TState, TEvents extends IEvDbEventPayload> {
|
|
22
|
+
viewName: string;
|
|
23
|
+
streamType: string;
|
|
24
|
+
defaultState: TState;
|
|
25
|
+
handlers: Partial<EvDbStreamEventHandlersMap<TState, TEvents>>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* View Factory - creates view instances with the handlers map
|
|
29
|
+
*/
|
|
30
|
+
export declare class ViewFactory<TState, TEvents extends IEvDbEventPayload> {
|
|
31
|
+
private readonly config;
|
|
32
|
+
constructor(config: ViewConfig<TState, TEvents>);
|
|
33
|
+
/**
|
|
34
|
+
* Creates a view instance
|
|
35
|
+
*/
|
|
36
|
+
create(streamId: string, storageAdapter: IEvDbStorageSnapshotAdapter): EvDbView<TState>;
|
|
37
|
+
/**
|
|
38
|
+
* Get a view instance from event store
|
|
39
|
+
*/
|
|
40
|
+
get(streamId: string, storageAdapter: IEvDbStorageSnapshotAdapter): Promise<EvDbView<TState>>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Factory function to create a ViewFactory
|
|
44
|
+
*/
|
|
45
|
+
export declare function createViewFactory<TState, TEvents extends IEvDbEventPayload>(config: ViewConfig<TState, TEvents>): ViewFactory<TState, TEvents>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { EvDbView } from './EvDbView.js';
|
|
2
|
+
import EvDbViewAddress from '@eventualize/types/EvDbViewAddress';
|
|
3
|
+
import EvDbStreamAddress from '@eventualize/types/EvDbStreamAddress';
|
|
4
|
+
import { EvDbStoredSnapshotResult } from '@eventualize/types/EvDbStoredSnapshotResult';
|
|
5
|
+
/**
|
|
6
|
+
* Generic View class that uses the handlers map
|
|
7
|
+
*/
|
|
8
|
+
class GenericView extends EvDbView {
|
|
9
|
+
constructor(viewAddress, storageAdapter, snapshot, config) {
|
|
10
|
+
super(viewAddress, storageAdapter, snapshot, config.defaultState);
|
|
11
|
+
this.config = config;
|
|
12
|
+
this.config = config;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Dynamically applies events based on the handlers map
|
|
16
|
+
*/
|
|
17
|
+
handleOnApply(oldState, event, metadata) {
|
|
18
|
+
const payloadType = event.payloadType;
|
|
19
|
+
const handler = this.config.handlers[payloadType];
|
|
20
|
+
if (!handler) {
|
|
21
|
+
// console.warn(`No handler found for event type: ${event.payloadType}`);
|
|
22
|
+
return oldState;
|
|
23
|
+
}
|
|
24
|
+
return handler(oldState, event, metadata);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* View Factory - creates view instances with the handlers map
|
|
29
|
+
*/
|
|
30
|
+
export class ViewFactory {
|
|
31
|
+
constructor(config) {
|
|
32
|
+
this.config = config;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Creates a view instance
|
|
36
|
+
*/
|
|
37
|
+
create(streamId, storageAdapter) {
|
|
38
|
+
const streamAddress = new EvDbStreamAddress(this.config.streamType, streamId);
|
|
39
|
+
const viewAddress = new EvDbViewAddress(streamAddress, this.config.viewName);
|
|
40
|
+
return new GenericView(viewAddress, storageAdapter, EvDbStoredSnapshotResult.getEmptyState(), this.config);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get a view instance from event store
|
|
44
|
+
*/
|
|
45
|
+
async get(streamId, storageAdapter) {
|
|
46
|
+
const streamAddress = new EvDbStreamAddress(this.config.streamType, streamId);
|
|
47
|
+
const viewAddress = new EvDbViewAddress(streamAddress, this.config.viewName);
|
|
48
|
+
const snapshot = await storageAdapter.getSnapshotAsync(viewAddress);
|
|
49
|
+
return new GenericView(viewAddress, storageAdapter, snapshot, this.config);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Factory function to create a ViewFactory
|
|
54
|
+
*/
|
|
55
|
+
export function createViewFactory(config) {
|
|
56
|
+
return new ViewFactory(config);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=EvDbViewFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EvDbViewFactory.js","sourceRoot":"","sources":["../src/EvDbViewFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAIzC,OAAO,eAAe,MAAM,oCAAoC,CAAC;AACjE,OAAO,iBAAiB,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AA+BvF;;GAEG;AACH,MAAM,WAAuD,SAAQ,QAAgB;IAEjF,YACI,WAA4B,EAC5B,cAA2C,EAC3C,QAA0C,EAC1B,MAAmC;QAEnD,KAAK,CAAC,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;QAFlD,WAAM,GAAN,MAAM,CAA6B;QAGnD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAGD;;OAEG;IACI,aAAa,CAAC,QAAgB,EAAE,KAAc,EAAE,QAA4B;QAC/E,MAAM,WAAW,GAAG,KAAK,CAAC,WAAgD,CAAC;QAC3E,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAElD,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,yEAAyE;YACzE,OAAO,QAAQ,CAAC;QACpB,CAAC;QAED,OAAO,OAAO,CAAC,QAAQ,EAAE,KAAY,EAAE,QAAQ,CAAC,CAAC;IACrD,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,OAAO,WAAW;IACpB,YAA6B,MAAmC;QAAnC,WAAM,GAAN,MAAM,CAA6B;IAAI,CAAC;IAErE;;OAEG;IACI,MAAM,CACT,QAAgB,EAChB,cAA2C;QAE3C,MAAM,aAAa,GAAG,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC9E,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAE7E,OAAO,IAAI,WAAW,CAClB,WAAW,EACX,cAAc,EACd,wBAAwB,CAAC,aAAa,EAAU,EAChD,IAAI,CAAC,MAAM,CACd,CAAC;IACN,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,GAAG,CACZ,QAAgB,EAChB,cAA2C;QAE3C,MAAM,aAAa,GAAG,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC9E,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAE7E,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAA;QAEnE,OAAO,IAAI,WAAW,CAClB,WAAW,EACX,cAAc,EACd,QAAQ,EACR,IAAI,CAAC,MAAM,CACd,CAAC;IACN,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC7B,MAAmC;IAEnC,OAAO,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eventualize/core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./*": "./dist/*.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc --build",
|
|
16
|
+
"clean": "rimraf dist",
|
|
17
|
+
"rebuild": "npm run clean && npm run build",
|
|
18
|
+
"test": "echo not tests implemented yet."
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@eventualize/types": "1.0.0"
|
|
22
|
+
},
|
|
23
|
+
"license": "ISC",
|
|
24
|
+
"type": "module"
|
|
25
|
+
}
|