@legible-sync/example-eda 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/.eslintrc.js +16 -0
- package/README.md +103 -0
- package/__tests__/integration/ecommerce-flow.test.ts +247 -0
- package/__tests__/unit/EventBus.test.ts +154 -0
- package/__tests__/unit/PluginManager.test.ts +111 -0
- package/__tests__/unit/concepts/User.test.ts +130 -0
- package/dist/core/EventBus.d.ts +16 -0
- package/dist/core/EventBus.d.ts.map +1 -0
- package/dist/core/EventBus.js +44 -0
- package/dist/core/EventBus.js.map +1 -0
- package/dist/core/PluginManager.d.ts +16 -0
- package/dist/core/PluginManager.d.ts.map +1 -0
- package/dist/core/PluginManager.js +37 -0
- package/dist/core/PluginManager.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +145 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/analytics/concepts/Analytics.d.ts +3 -0
- package/dist/plugins/analytics/concepts/Analytics.d.ts.map +1 -0
- package/dist/plugins/analytics/concepts/Analytics.js +43 -0
- package/dist/plugins/analytics/concepts/Analytics.js.map +1 -0
- package/dist/plugins/analytics/index.d.ts +3 -0
- package/dist/plugins/analytics/index.d.ts.map +1 -0
- package/dist/plugins/analytics/index.js +16 -0
- package/dist/plugins/analytics/index.js.map +1 -0
- package/dist/plugins/analytics/syncs/analytics-events.sync.d.ts +3 -0
- package/dist/plugins/analytics/syncs/analytics-events.sync.d.ts.map +1 -0
- package/dist/plugins/analytics/syncs/analytics-events.sync.js +101 -0
- package/dist/plugins/analytics/syncs/analytics-events.sync.js.map +1 -0
- package/dist/plugins/inventory/concepts/Inventory.d.ts +3 -0
- package/dist/plugins/inventory/concepts/Inventory.d.ts.map +1 -0
- package/dist/plugins/inventory/concepts/Inventory.js +60 -0
- package/dist/plugins/inventory/concepts/Inventory.js.map +1 -0
- package/dist/plugins/inventory/index.d.ts +3 -0
- package/dist/plugins/inventory/index.d.ts.map +1 -0
- package/dist/plugins/inventory/index.js +15 -0
- package/dist/plugins/inventory/index.js.map +1 -0
- package/dist/plugins/notifications/concepts/Notification.d.ts +3 -0
- package/dist/plugins/notifications/concepts/Notification.d.ts.map +1 -0
- package/dist/plugins/notifications/concepts/Notification.js +42 -0
- package/dist/plugins/notifications/concepts/Notification.js.map +1 -0
- package/dist/plugins/notifications/index.d.ts +3 -0
- package/dist/plugins/notifications/index.d.ts.map +1 -0
- package/dist/plugins/notifications/index.js +15 -0
- package/dist/plugins/notifications/index.js.map +1 -0
- package/dist/plugins/orders/concepts/Order.d.ts +3 -0
- package/dist/plugins/orders/concepts/Order.d.ts.map +1 -0
- package/dist/plugins/orders/concepts/Order.js +98 -0
- package/dist/plugins/orders/concepts/Order.js.map +1 -0
- package/dist/plugins/orders/index.d.ts +3 -0
- package/dist/plugins/orders/index.d.ts.map +1 -0
- package/dist/plugins/orders/index.js +16 -0
- package/dist/plugins/orders/index.js.map +1 -0
- package/dist/plugins/orders/syncs/order-workflow.sync.d.ts +3 -0
- package/dist/plugins/orders/syncs/order-workflow.sync.d.ts.map +1 -0
- package/dist/plugins/orders/syncs/order-workflow.sync.js +168 -0
- package/dist/plugins/orders/syncs/order-workflow.sync.js.map +1 -0
- package/dist/plugins/payments/concepts/Payment.d.ts +3 -0
- package/dist/plugins/payments/concepts/Payment.d.ts.map +1 -0
- package/dist/plugins/payments/concepts/Payment.js +156 -0
- package/dist/plugins/payments/concepts/Payment.js.map +1 -0
- package/dist/plugins/payments/index.d.ts +3 -0
- package/dist/plugins/payments/index.d.ts.map +1 -0
- package/dist/plugins/payments/index.js +16 -0
- package/dist/plugins/payments/index.js.map +1 -0
- package/dist/plugins/payments/syncs/payment-workflow.sync.d.ts +3 -0
- package/dist/plugins/payments/syncs/payment-workflow.sync.d.ts.map +1 -0
- package/dist/plugins/payments/syncs/payment-workflow.sync.js +264 -0
- package/dist/plugins/payments/syncs/payment-workflow.sync.js.map +1 -0
- package/dist/plugins/products/concepts/Product.d.ts +3 -0
- package/dist/plugins/products/concepts/Product.d.ts.map +1 -0
- package/dist/plugins/products/concepts/Product.js +85 -0
- package/dist/plugins/products/concepts/Product.js.map +1 -0
- package/dist/plugins/products/index.d.ts +3 -0
- package/dist/plugins/products/index.d.ts.map +1 -0
- package/dist/plugins/products/index.js +16 -0
- package/dist/plugins/products/index.js.map +1 -0
- package/dist/plugins/products/syncs/product-events.sync.d.ts +3 -0
- package/dist/plugins/products/syncs/product-events.sync.d.ts.map +1 -0
- package/dist/plugins/products/syncs/product-events.sync.js +77 -0
- package/dist/plugins/products/syncs/product-events.sync.js.map +1 -0
- package/dist/plugins/users/concepts/User.d.ts +3 -0
- package/dist/plugins/users/concepts/User.d.ts.map +1 -0
- package/dist/plugins/users/concepts/User.js +81 -0
- package/dist/plugins/users/concepts/User.js.map +1 -0
- package/dist/plugins/users/index.d.ts +3 -0
- package/dist/plugins/users/index.d.ts.map +1 -0
- package/dist/plugins/users/index.js +16 -0
- package/dist/plugins/users/index.js.map +1 -0
- package/dist/plugins/users/syncs/user-events.sync.d.ts +3 -0
- package/dist/plugins/users/syncs/user-events.sync.d.ts.map +1 -0
- package/dist/plugins/users/syncs/user-events.sync.js +75 -0
- package/dist/plugins/users/syncs/user-events.sync.js.map +1 -0
- package/package.json +40 -0
- package/src/core/EventBus.ts +55 -0
- package/src/core/PluginManager.ts +51 -0
- package/src/index.ts +169 -0
- package/src/plugins/analytics/concepts/Analytics.ts +53 -0
- package/src/plugins/analytics/index.ts +15 -0
- package/src/plugins/analytics/syncs/analytics-events.sync.ts +103 -0
- package/src/plugins/inventory/concepts/Inventory.ts +73 -0
- package/src/plugins/inventory/index.ts +14 -0
- package/src/plugins/notifications/concepts/Notification.ts +49 -0
- package/src/plugins/notifications/index.ts +14 -0
- package/src/plugins/orders/concepts/Order.ts +118 -0
- package/src/plugins/orders/index.ts +15 -0
- package/src/plugins/orders/syncs/order-workflow.sync.ts +173 -0
- package/src/plugins/payments/concepts/Payment.ts +186 -0
- package/src/plugins/payments/index.ts +15 -0
- package/src/plugins/payments/syncs/payment-workflow.sync.ts +274 -0
- package/src/plugins/products/concepts/Product.ts +102 -0
- package/src/plugins/products/index.ts +15 -0
- package/src/plugins/products/syncs/product-events.sync.ts +78 -0
- package/src/plugins/users/concepts/User.ts +97 -0
- package/src/plugins/users/index.ts +15 -0
- package/src/plugins/users/syncs/user-events.sync.ts +76 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.productEventSyncs = void 0;
|
|
4
|
+
exports.productEventSyncs = [
|
|
5
|
+
// When a product is created, publish product.created event
|
|
6
|
+
{
|
|
7
|
+
name: "PublishProductCreatedEvent",
|
|
8
|
+
when: [
|
|
9
|
+
{
|
|
10
|
+
concept: "Product",
|
|
11
|
+
action: "create"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
then: [
|
|
15
|
+
{
|
|
16
|
+
concept: "EventBus",
|
|
17
|
+
action: "publish",
|
|
18
|
+
input: {
|
|
19
|
+
event: "product.created",
|
|
20
|
+
data: {
|
|
21
|
+
productId: "?productId",
|
|
22
|
+
name: "?product.name",
|
|
23
|
+
sku: "?product.sku",
|
|
24
|
+
price: "?product.price",
|
|
25
|
+
category: "?product.category"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
// When a product is updated, publish product.updated event
|
|
32
|
+
{
|
|
33
|
+
name: "PublishProductUpdatedEvent",
|
|
34
|
+
when: [
|
|
35
|
+
{
|
|
36
|
+
concept: "Product",
|
|
37
|
+
action: "update"
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
then: [
|
|
41
|
+
{
|
|
42
|
+
concept: "EventBus",
|
|
43
|
+
action: "publish",
|
|
44
|
+
input: {
|
|
45
|
+
event: "product.updated",
|
|
46
|
+
data: {
|
|
47
|
+
productId: "?productId",
|
|
48
|
+
updates: "?updates"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
// When a product is deactivated, publish product.deactivated event
|
|
55
|
+
{
|
|
56
|
+
name: "PublishProductDeactivatedEvent",
|
|
57
|
+
when: [
|
|
58
|
+
{
|
|
59
|
+
concept: "Product",
|
|
60
|
+
action: "deactivate"
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
then: [
|
|
64
|
+
{
|
|
65
|
+
concept: "EventBus",
|
|
66
|
+
action: "publish",
|
|
67
|
+
input: {
|
|
68
|
+
event: "product.deactivated",
|
|
69
|
+
data: {
|
|
70
|
+
productId: "?productId"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
];
|
|
77
|
+
//# sourceMappingURL=product-events.sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"product-events.sync.js","sourceRoot":"","sources":["../../../../src/plugins/products/syncs/product-events.sync.ts"],"names":[],"mappings":";;;AAGa,QAAA,iBAAiB,GAAe;IAC3C,2DAA2D;IAC3D;QACE,IAAI,EAAE,4BAA4B;QAClC,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE,SAAS;gBAClB,MAAM,EAAE,QAAQ;aACjB;SACF;QACD,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE,UAAU;gBACnB,MAAM,EAAE,SAAS;gBACjB,KAAK,EAAE;oBACL,KAAK,EAAE,iBAAiB;oBACxB,IAAI,EAAE;wBACJ,SAAS,EAAE,YAAY;wBACvB,IAAI,EAAE,eAAe;wBACrB,GAAG,EAAE,cAAc;wBACnB,KAAK,EAAE,gBAAgB;wBACvB,QAAQ,EAAE,mBAAmB;qBAC9B;iBACF;aACF;SACF;KACF;IAED,2DAA2D;IAC3D;QACE,IAAI,EAAE,4BAA4B;QAClC,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE,SAAS;gBAClB,MAAM,EAAE,QAAQ;aACjB;SACF;QACD,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE,UAAU;gBACnB,MAAM,EAAE,SAAS;gBACjB,KAAK,EAAE;oBACL,KAAK,EAAE,iBAAiB;oBACxB,IAAI,EAAE;wBACJ,SAAS,EAAE,YAAY;wBACvB,OAAO,EAAE,UAAU;qBACpB;iBACF;aACF;SACF;KACF;IAED,mEAAmE;IACnE;QACE,IAAI,EAAE,gCAAgC;QACtC,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE,SAAS;gBAClB,MAAM,EAAE,YAAY;aACrB;SACF;QACD,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE,UAAU;gBACnB,MAAM,EAAE,SAAS;gBACjB,KAAK,EAAE;oBACL,KAAK,EAAE,qBAAqB;oBAC5B,IAAI,EAAE;wBACJ,SAAS,EAAE,YAAY;qBACxB;iBACF;aACF;SACF;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"User.d.ts","sourceRoot":"","sources":["../../../../src/plugins/users/concepts/User.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,eAAO,MAAM,IAAI,EAAE,OA4FlB,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.User = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
exports.User = {
|
|
6
|
+
state: {
|
|
7
|
+
users: new Map(),
|
|
8
|
+
emails: new Set(),
|
|
9
|
+
usernames: new Set(),
|
|
10
|
+
},
|
|
11
|
+
async execute(action, input) {
|
|
12
|
+
const state = this.state;
|
|
13
|
+
switch (action) {
|
|
14
|
+
case 'register': {
|
|
15
|
+
const { username, email, password } = input;
|
|
16
|
+
// Validation
|
|
17
|
+
if (!username || !email || !password) {
|
|
18
|
+
throw new Error('Username, email, and password are required');
|
|
19
|
+
}
|
|
20
|
+
if (state.emails.has(email)) {
|
|
21
|
+
throw new Error('Email already registered');
|
|
22
|
+
}
|
|
23
|
+
if (state.usernames.has(username)) {
|
|
24
|
+
throw new Error('Username already taken');
|
|
25
|
+
}
|
|
26
|
+
// Create user
|
|
27
|
+
const userId = (0, uuid_1.v4)();
|
|
28
|
+
const user = {
|
|
29
|
+
id: userId,
|
|
30
|
+
username,
|
|
31
|
+
email,
|
|
32
|
+
createdAt: new Date(),
|
|
33
|
+
status: 'active'
|
|
34
|
+
};
|
|
35
|
+
state.users.set(userId, user);
|
|
36
|
+
state.emails.add(email);
|
|
37
|
+
state.usernames.add(username);
|
|
38
|
+
return { userId, user };
|
|
39
|
+
}
|
|
40
|
+
case 'get': {
|
|
41
|
+
const { userId } = input;
|
|
42
|
+
const user = state.users.get(userId);
|
|
43
|
+
if (!user) {
|
|
44
|
+
throw new Error('User not found');
|
|
45
|
+
}
|
|
46
|
+
return { user };
|
|
47
|
+
}
|
|
48
|
+
case 'update': {
|
|
49
|
+
const { userId, updates } = input;
|
|
50
|
+
const user = state.users.get(userId);
|
|
51
|
+
if (!user) {
|
|
52
|
+
throw new Error('User not found');
|
|
53
|
+
}
|
|
54
|
+
// Update user
|
|
55
|
+
const updatedUser = { ...user, ...updates, updatedAt: new Date() };
|
|
56
|
+
state.users.set(userId, updatedUser);
|
|
57
|
+
return { user: updatedUser };
|
|
58
|
+
}
|
|
59
|
+
case 'deactivate': {
|
|
60
|
+
const { userId } = input;
|
|
61
|
+
const user = state.users.get(userId);
|
|
62
|
+
if (!user) {
|
|
63
|
+
throw new Error('User not found');
|
|
64
|
+
}
|
|
65
|
+
user.status = 'inactive';
|
|
66
|
+
user.deactivatedAt = new Date();
|
|
67
|
+
state.users.set(userId, user);
|
|
68
|
+
return { user };
|
|
69
|
+
}
|
|
70
|
+
case 'reset': {
|
|
71
|
+
state.users.clear();
|
|
72
|
+
state.emails.clear();
|
|
73
|
+
state.usernames.clear();
|
|
74
|
+
return { reset: true };
|
|
75
|
+
}
|
|
76
|
+
default:
|
|
77
|
+
throw new Error(`Unknown action: ${action}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=User.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"User.js","sourceRoot":"","sources":["../../../../src/plugins/users/concepts/User.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/users/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAIlD,eAAO,MAAM,WAAW,EAAE,MASzB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usersPlugin = void 0;
|
|
4
|
+
const User_1 = require("./concepts/User");
|
|
5
|
+
const user_events_sync_1 = require("./syncs/user-events.sync");
|
|
6
|
+
exports.usersPlugin = {
|
|
7
|
+
name: 'users',
|
|
8
|
+
concepts: {
|
|
9
|
+
User: User_1.User
|
|
10
|
+
},
|
|
11
|
+
syncs: user_events_sync_1.userEventSyncs,
|
|
12
|
+
initialize: async (_engine) => {
|
|
13
|
+
console.log('š¤ Users plugin initialized');
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugins/users/index.ts"],"names":[],"mappings":";;;AAEA,0CAAuC;AACvC,+DAA0D;AAE7C,QAAA,WAAW,GAAW;IACjC,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE;QACR,IAAI,EAAJ,WAAI;KACL;IACD,KAAK,EAAE,iCAAc;IACrB,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC5B,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC7C,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-events.sync.d.ts","sourceRoot":"","sources":["../../../../src/plugins/users/syncs/user-events.sync.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,eAAO,MAAM,cAAc,EAAE,QAAQ,EAwEpC,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.userEventSyncs = void 0;
|
|
4
|
+
exports.userEventSyncs = [
|
|
5
|
+
// When a user registers, publish user.registered event
|
|
6
|
+
{
|
|
7
|
+
name: "PublishUserRegisteredEvent",
|
|
8
|
+
when: [
|
|
9
|
+
{
|
|
10
|
+
concept: "User",
|
|
11
|
+
action: "register"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
then: [
|
|
15
|
+
{
|
|
16
|
+
concept: "EventBus",
|
|
17
|
+
action: "publish",
|
|
18
|
+
input: {
|
|
19
|
+
event: "user.registered",
|
|
20
|
+
data: {
|
|
21
|
+
userId: "?userId",
|
|
22
|
+
username: "?user.username",
|
|
23
|
+
email: "?user.email"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
// When a user is updated, publish user.updated event
|
|
30
|
+
{
|
|
31
|
+
name: "PublishUserUpdatedEvent",
|
|
32
|
+
when: [
|
|
33
|
+
{
|
|
34
|
+
concept: "User",
|
|
35
|
+
action: "update"
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
then: [
|
|
39
|
+
{
|
|
40
|
+
concept: "EventBus",
|
|
41
|
+
action: "publish",
|
|
42
|
+
input: {
|
|
43
|
+
event: "user.updated",
|
|
44
|
+
data: {
|
|
45
|
+
userId: "?userId",
|
|
46
|
+
updates: "?updates"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
// When a user is deactivated, publish user.deactivated event
|
|
53
|
+
{
|
|
54
|
+
name: "PublishUserDeactivatedEvent",
|
|
55
|
+
when: [
|
|
56
|
+
{
|
|
57
|
+
concept: "User",
|
|
58
|
+
action: "deactivate"
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
then: [
|
|
62
|
+
{
|
|
63
|
+
concept: "EventBus",
|
|
64
|
+
action: "publish",
|
|
65
|
+
input: {
|
|
66
|
+
event: "user.deactivated",
|
|
67
|
+
data: {
|
|
68
|
+
userId: "?userId"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
];
|
|
75
|
+
//# sourceMappingURL=user-events.sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-events.sync.js","sourceRoot":"","sources":["../../../../src/plugins/users/syncs/user-events.sync.ts"],"names":[],"mappings":";;;AAGa,QAAA,cAAc,GAAe;IACxC,uDAAuD;IACvD;QACE,IAAI,EAAE,4BAA4B;QAClC,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,UAAU;aACnB;SACF;QACD,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE,UAAU;gBACnB,MAAM,EAAE,SAAS;gBACjB,KAAK,EAAE;oBACL,KAAK,EAAE,iBAAiB;oBACxB,IAAI,EAAE;wBACJ,MAAM,EAAE,SAAS;wBACjB,QAAQ,EAAE,gBAAgB;wBAC1B,KAAK,EAAE,aAAa;qBACrB;iBACF;aACF;SACF;KACF;IAED,qDAAqD;IACrD;QACE,IAAI,EAAE,yBAAyB;QAC/B,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,QAAQ;aACjB;SACF;QACD,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE,UAAU;gBACnB,MAAM,EAAE,SAAS;gBACjB,KAAK,EAAE;oBACL,KAAK,EAAE,cAAc;oBACrB,IAAI,EAAE;wBACJ,MAAM,EAAE,SAAS;wBACjB,OAAO,EAAE,UAAU;qBACpB;iBACF;aACF;SACF;KACF;IAED,6DAA6D;IAC7D;QACE,IAAI,EAAE,6BAA6B;QACnC,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,YAAY;aACrB;SACF;QACD,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE,UAAU;gBACnB,MAAM,EAAE,SAAS;gBACjB,KAAK,EAAE;oBACL,KAAK,EAAE,kBAAkB;oBACzB,IAAI,EAAE;wBACJ,MAAM,EAAE,SAAS;qBAClB;iBACF;aACF;SACF;KACF;CACF,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@legible-sync/example-eda",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Event-Driven Architecture example with plugin-based modules",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "ts-node src/index.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"dev": "ts-node src/index.ts",
|
|
10
|
+
"typecheck": "tsc --noEmit",
|
|
11
|
+
"lint": "eslint src/**/*.ts",
|
|
12
|
+
"test": "jest",
|
|
13
|
+
"test:watch": "jest --watch",
|
|
14
|
+
"test:coverage": "jest --coverage"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@legible-sync/core": "^1.2.0"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/mastepanoski/legiblesync.git",
|
|
22
|
+
"directory": "packages/example-eda"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/mastepanoski/legiblesync",
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/jest": "^29.5.0",
|
|
30
|
+
"@types/node": "^20.0.0",
|
|
31
|
+
"jest": "^29.7.0",
|
|
32
|
+
"ts-jest": "^29.1.0",
|
|
33
|
+
"ts-node": "^10.9.0",
|
|
34
|
+
"typescript": "^5.0.0"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"gitHead": "0ac4e896d835d961c7efee0c7295f287d08d92a4"
|
|
40
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// core/EventBus.ts
|
|
2
|
+
|
|
3
|
+
export interface Event {
|
|
4
|
+
type: string;
|
|
5
|
+
payload: Record<string, any>;
|
|
6
|
+
source: string;
|
|
7
|
+
timestamp: Date;
|
|
8
|
+
flowId: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type EventHandler = (event: Event) => Promise<void>;
|
|
12
|
+
|
|
13
|
+
export class EventBus {
|
|
14
|
+
private handlers: Map<string, EventHandler[]> = new Map();
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
// EventBus is independent of the LegibleEngine
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
subscribe(eventType: string, handler: EventHandler): void {
|
|
21
|
+
if (!this.handlers.has(eventType)) {
|
|
22
|
+
this.handlers.set(eventType, []);
|
|
23
|
+
}
|
|
24
|
+
this.handlers.get(eventType)!.push(handler);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async publish(event: Omit<Event, 'timestamp'>): Promise<void> {
|
|
28
|
+
const fullEvent: Event = {
|
|
29
|
+
...event,
|
|
30
|
+
timestamp: new Date()
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
console.log(`š¢ Event: ${fullEvent.type} from ${fullEvent.source}`, fullEvent.payload);
|
|
34
|
+
|
|
35
|
+
const handlers = this.handlers.get(event.type) || [];
|
|
36
|
+
for (const handler of handlers) {
|
|
37
|
+
try {
|
|
38
|
+
await handler(fullEvent);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
// Handler errors are silently ignored to prevent cascading failures
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Helper to create events from engine actions
|
|
46
|
+
createEventFromAction(concept: string, action: string, input: any, output: any, flowId: string): Event {
|
|
47
|
+
return {
|
|
48
|
+
type: `${concept}.${action}`,
|
|
49
|
+
payload: { input, output },
|
|
50
|
+
source: concept,
|
|
51
|
+
timestamp: new Date(),
|
|
52
|
+
flowId
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// core/PluginManager.ts
|
|
2
|
+
import { LegibleEngine, Concept, SyncRule } from '@legible-sync/core';
|
|
3
|
+
|
|
4
|
+
export interface Plugin {
|
|
5
|
+
name: string;
|
|
6
|
+
concepts: Record<string, Concept>;
|
|
7
|
+
syncs: SyncRule[];
|
|
8
|
+
initialize?: (engine: LegibleEngine) => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class PluginManager {
|
|
12
|
+
private engine: LegibleEngine;
|
|
13
|
+
private loadedPlugins: Map<string, Plugin> = new Map();
|
|
14
|
+
|
|
15
|
+
constructor(engine: LegibleEngine) {
|
|
16
|
+
this.engine = engine;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async loadPlugin(plugin: Plugin): Promise<void> {
|
|
20
|
+
console.log(`š Loading plugin: ${plugin.name}`);
|
|
21
|
+
|
|
22
|
+
// Register concepts
|
|
23
|
+
for (const [name, concept] of Object.entries(plugin.concepts)) {
|
|
24
|
+
this.engine.registerConcept(name, concept);
|
|
25
|
+
console.log(` ā Registered concept: ${name}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Register sync rules
|
|
29
|
+
for (const sync of plugin.syncs) {
|
|
30
|
+
this.engine.registerSync(sync);
|
|
31
|
+
console.log(` ā Registered sync: ${sync.name}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Initialize plugin if needed
|
|
35
|
+
if (plugin.initialize) {
|
|
36
|
+
await plugin.initialize(this.engine);
|
|
37
|
+
console.log(` ā Initialized plugin: ${plugin.name}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.loadedPlugins.set(plugin.name, plugin);
|
|
41
|
+
console.log(`ā
Plugin ${plugin.name} loaded successfully\n`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getLoadedPlugins(): string[] {
|
|
45
|
+
return Array.from(this.loadedPlugins.keys());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getPlugin(name: string): Plugin | undefined {
|
|
49
|
+
return this.loadedPlugins.get(name);
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// example-eda/src/index.ts
|
|
2
|
+
import { LegibleEngine } from '@legible-sync/core';
|
|
3
|
+
import { PluginManager } from './core/PluginManager';
|
|
4
|
+
import { EventBus } from './core/EventBus';
|
|
5
|
+
|
|
6
|
+
// Import all plugins
|
|
7
|
+
import { usersPlugin } from './plugins/users';
|
|
8
|
+
import { productsPlugin } from './plugins/products';
|
|
9
|
+
import { ordersPlugin } from './plugins/orders';
|
|
10
|
+
import { inventoryPlugin } from './plugins/inventory';
|
|
11
|
+
import { notificationsPlugin } from './plugins/notifications';
|
|
12
|
+
import { analyticsPlugin } from './plugins/analytics';
|
|
13
|
+
import { paymentsPlugin } from './plugins/payments';
|
|
14
|
+
|
|
15
|
+
async function main() {
|
|
16
|
+
console.log('š Starting Event-Driven Architecture Example\n');
|
|
17
|
+
|
|
18
|
+
// Initialize core systems
|
|
19
|
+
const engine = new LegibleEngine();
|
|
20
|
+
const pluginManager = new PluginManager(engine);
|
|
21
|
+
const eventBus = new EventBus();
|
|
22
|
+
|
|
23
|
+
// Register EventBus as a concept so plugins can publish events
|
|
24
|
+
engine.registerConcept('EventBus', {
|
|
25
|
+
state: {},
|
|
26
|
+
async execute(action: string, input: any) {
|
|
27
|
+
if (action === 'publish') {
|
|
28
|
+
const { event, data } = input;
|
|
29
|
+
await eventBus.publish({
|
|
30
|
+
type: event,
|
|
31
|
+
payload: data,
|
|
32
|
+
source: 'system',
|
|
33
|
+
flowId: input.flowId || 'system'
|
|
34
|
+
});
|
|
35
|
+
return { published: true };
|
|
36
|
+
}
|
|
37
|
+
throw new Error(`Unknown EventBus action: ${action}`);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Load all plugins
|
|
42
|
+
const plugins = [
|
|
43
|
+
usersPlugin,
|
|
44
|
+
productsPlugin,
|
|
45
|
+
inventoryPlugin,
|
|
46
|
+
ordersPlugin,
|
|
47
|
+
paymentsPlugin,
|
|
48
|
+
notificationsPlugin,
|
|
49
|
+
analyticsPlugin
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
for (const plugin of plugins) {
|
|
53
|
+
await pluginManager.loadPlugin(plugin);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log('\nā
All plugins loaded successfully!');
|
|
57
|
+
console.log('Loaded plugins:', pluginManager.getLoadedPlugins());
|
|
58
|
+
|
|
59
|
+
// Demonstrate the EDA system with a complete e-commerce flow
|
|
60
|
+
console.log('\nšļø Demonstrating E-commerce Flow:\n');
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// 1. Create a user
|
|
64
|
+
console.log('1. š¤ Creating user...');
|
|
65
|
+
const userFlow = 'user-flow-1';
|
|
66
|
+
const userResult = await engine.invoke('User', 'register', {
|
|
67
|
+
username: 'johndoe',
|
|
68
|
+
email: 'john@example.com',
|
|
69
|
+
password: 'password123'
|
|
70
|
+
}, userFlow);
|
|
71
|
+
|
|
72
|
+
console.log(' User created:', userResult.user.username);
|
|
73
|
+
|
|
74
|
+
// 2. Create a product
|
|
75
|
+
console.log('\n2. š¦ Creating product...');
|
|
76
|
+
const productFlow = 'product-flow-1';
|
|
77
|
+
const productResult = await engine.invoke('Product', 'create', {
|
|
78
|
+
name: 'Wireless Headphones',
|
|
79
|
+
sku: 'WH-001',
|
|
80
|
+
price: 99.99,
|
|
81
|
+
description: 'High-quality wireless headphones',
|
|
82
|
+
category: 'electronics'
|
|
83
|
+
}, productFlow);
|
|
84
|
+
|
|
85
|
+
console.log(' Product created:', productResult.product.name);
|
|
86
|
+
|
|
87
|
+
// 3. Set inventory for the product
|
|
88
|
+
console.log('\n3. š Setting inventory...');
|
|
89
|
+
await engine.invoke('Inventory', 'setStock', {
|
|
90
|
+
productId: productResult.productId,
|
|
91
|
+
quantity: 10
|
|
92
|
+
}, 'inventory-flow-1');
|
|
93
|
+
|
|
94
|
+
console.log(' Inventory set: 10 units');
|
|
95
|
+
|
|
96
|
+
// 4. Create an order
|
|
97
|
+
console.log('\n4. š Creating order...');
|
|
98
|
+
const orderFlow = 'order-flow-1';
|
|
99
|
+
const orderResult = await engine.invoke('Order', 'create', {
|
|
100
|
+
userId: userResult.userId,
|
|
101
|
+
items: [
|
|
102
|
+
{
|
|
103
|
+
productId: productResult.productId,
|
|
104
|
+
quantity: 2,
|
|
105
|
+
price: productResult.product.price
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}, orderFlow);
|
|
109
|
+
|
|
110
|
+
console.log(' Order created, checking inventory...');
|
|
111
|
+
|
|
112
|
+
// The system should automatically:
|
|
113
|
+
// - Check inventory availability
|
|
114
|
+
// - Confirm the order if available
|
|
115
|
+
// - Initiate and process payment
|
|
116
|
+
// - Deduct inventory
|
|
117
|
+
// - Send confirmation notification
|
|
118
|
+
// - Track analytics events
|
|
119
|
+
|
|
120
|
+
// Wait a bit for async operations
|
|
121
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
122
|
+
|
|
123
|
+
// 5. Check order status
|
|
124
|
+
console.log('\n5. š Checking order status...');
|
|
125
|
+
const orderStatus = await engine.invoke('Order', 'get', {
|
|
126
|
+
orderId: orderResult.orderId
|
|
127
|
+
}, 'status-flow-1');
|
|
128
|
+
|
|
129
|
+
console.log(' Order status:', orderStatus.order.status);
|
|
130
|
+
console.log(' Order total: $' + orderStatus.order.total);
|
|
131
|
+
|
|
132
|
+
// 6. Check payment status
|
|
133
|
+
console.log('\n6. š³ Checking payment status...');
|
|
134
|
+
const paymentStatus = await engine.invoke('Payment', 'getByOrderId', {
|
|
135
|
+
orderId: orderResult.orderId
|
|
136
|
+
}, 'payment-check-flow-1');
|
|
137
|
+
|
|
138
|
+
console.log(' Payment status:', paymentStatus.payment.status);
|
|
139
|
+
console.log(' Payment amount: $' + paymentStatus.payment.amount);
|
|
140
|
+
|
|
141
|
+
// 7. Check inventory after order
|
|
142
|
+
console.log('\n7. š Checking inventory after order...');
|
|
143
|
+
const inventoryStatus = await engine.invoke('Inventory', 'getStock', {
|
|
144
|
+
productId: productResult.productId
|
|
145
|
+
}, 'inventory-check-flow-1');
|
|
146
|
+
|
|
147
|
+
console.log(' Remaining stock:', inventoryStatus.quantity, 'units');
|
|
148
|
+
|
|
149
|
+
// 8. Check analytics
|
|
150
|
+
console.log('\n8. š Checking analytics...');
|
|
151
|
+
const analytics = await engine.invoke('Analytics', 'getMetrics', {}, 'analytics-flow-1');
|
|
152
|
+
console.log(' Events tracked:', analytics.metrics);
|
|
153
|
+
|
|
154
|
+
console.log('\nš E-commerce flow completed successfully!');
|
|
155
|
+
console.log('The system automatically handled:');
|
|
156
|
+
console.log(' ā Inventory validation');
|
|
157
|
+
console.log(' ā Order confirmation');
|
|
158
|
+
console.log(' ā Payment processing');
|
|
159
|
+
console.log(' ā Stock deduction');
|
|
160
|
+
console.log(' ā Email notifications');
|
|
161
|
+
console.log(' ā Analytics tracking');
|
|
162
|
+
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error('ā Error in demo:', error);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Run the demo
|
|
169
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// plugins/analytics/concepts/Analytics.ts
|
|
2
|
+
import { Concept } from '@legible-sync/core';
|
|
3
|
+
|
|
4
|
+
export const Analytics: Concept = {
|
|
5
|
+
state: {
|
|
6
|
+
events: new Map<string, any[]>(),
|
|
7
|
+
metrics: new Map<string, number>(),
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
async execute(action: string, input: any) {
|
|
11
|
+
const state = this.state;
|
|
12
|
+
|
|
13
|
+
switch (action) {
|
|
14
|
+
case 'track': {
|
|
15
|
+
const { event, data } = input;
|
|
16
|
+
|
|
17
|
+
if (!state.events.has(event)) {
|
|
18
|
+
state.events.set(event, []);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const eventData = {
|
|
22
|
+
...data,
|
|
23
|
+
timestamp: new Date(),
|
|
24
|
+
id: `event_${Date.now()}`
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
state.events.get(event)!.push(eventData);
|
|
28
|
+
|
|
29
|
+
// Update metrics
|
|
30
|
+
const count = state.metrics.get(event) || 0;
|
|
31
|
+
state.metrics.set(event, count + 1);
|
|
32
|
+
|
|
33
|
+
console.log(`š Tracked: ${event}`, data);
|
|
34
|
+
|
|
35
|
+
return { eventId: eventData.id };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
case 'getMetrics': {
|
|
39
|
+
const metrics = Object.fromEntries(state.metrics);
|
|
40
|
+
return { metrics };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
case 'getEvents': {
|
|
44
|
+
const { event, limit = 10 } = input;
|
|
45
|
+
const events = state.events.get(event) || [];
|
|
46
|
+
return { events: events.slice(-limit) };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
default:
|
|
50
|
+
throw new Error(`Unknown action: ${action}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|