@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.
Files changed (118) hide show
  1. package/.eslintrc.js +16 -0
  2. package/README.md +103 -0
  3. package/__tests__/integration/ecommerce-flow.test.ts +247 -0
  4. package/__tests__/unit/EventBus.test.ts +154 -0
  5. package/__tests__/unit/PluginManager.test.ts +111 -0
  6. package/__tests__/unit/concepts/User.test.ts +130 -0
  7. package/dist/core/EventBus.d.ts +16 -0
  8. package/dist/core/EventBus.d.ts.map +1 -0
  9. package/dist/core/EventBus.js +44 -0
  10. package/dist/core/EventBus.js.map +1 -0
  11. package/dist/core/PluginManager.d.ts +16 -0
  12. package/dist/core/PluginManager.d.ts.map +1 -0
  13. package/dist/core/PluginManager.js +37 -0
  14. package/dist/core/PluginManager.js.map +1 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +145 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/plugins/analytics/concepts/Analytics.d.ts +3 -0
  20. package/dist/plugins/analytics/concepts/Analytics.d.ts.map +1 -0
  21. package/dist/plugins/analytics/concepts/Analytics.js +43 -0
  22. package/dist/plugins/analytics/concepts/Analytics.js.map +1 -0
  23. package/dist/plugins/analytics/index.d.ts +3 -0
  24. package/dist/plugins/analytics/index.d.ts.map +1 -0
  25. package/dist/plugins/analytics/index.js +16 -0
  26. package/dist/plugins/analytics/index.js.map +1 -0
  27. package/dist/plugins/analytics/syncs/analytics-events.sync.d.ts +3 -0
  28. package/dist/plugins/analytics/syncs/analytics-events.sync.d.ts.map +1 -0
  29. package/dist/plugins/analytics/syncs/analytics-events.sync.js +101 -0
  30. package/dist/plugins/analytics/syncs/analytics-events.sync.js.map +1 -0
  31. package/dist/plugins/inventory/concepts/Inventory.d.ts +3 -0
  32. package/dist/plugins/inventory/concepts/Inventory.d.ts.map +1 -0
  33. package/dist/plugins/inventory/concepts/Inventory.js +60 -0
  34. package/dist/plugins/inventory/concepts/Inventory.js.map +1 -0
  35. package/dist/plugins/inventory/index.d.ts +3 -0
  36. package/dist/plugins/inventory/index.d.ts.map +1 -0
  37. package/dist/plugins/inventory/index.js +15 -0
  38. package/dist/plugins/inventory/index.js.map +1 -0
  39. package/dist/plugins/notifications/concepts/Notification.d.ts +3 -0
  40. package/dist/plugins/notifications/concepts/Notification.d.ts.map +1 -0
  41. package/dist/plugins/notifications/concepts/Notification.js +42 -0
  42. package/dist/plugins/notifications/concepts/Notification.js.map +1 -0
  43. package/dist/plugins/notifications/index.d.ts +3 -0
  44. package/dist/plugins/notifications/index.d.ts.map +1 -0
  45. package/dist/plugins/notifications/index.js +15 -0
  46. package/dist/plugins/notifications/index.js.map +1 -0
  47. package/dist/plugins/orders/concepts/Order.d.ts +3 -0
  48. package/dist/plugins/orders/concepts/Order.d.ts.map +1 -0
  49. package/dist/plugins/orders/concepts/Order.js +98 -0
  50. package/dist/plugins/orders/concepts/Order.js.map +1 -0
  51. package/dist/plugins/orders/index.d.ts +3 -0
  52. package/dist/plugins/orders/index.d.ts.map +1 -0
  53. package/dist/plugins/orders/index.js +16 -0
  54. package/dist/plugins/orders/index.js.map +1 -0
  55. package/dist/plugins/orders/syncs/order-workflow.sync.d.ts +3 -0
  56. package/dist/plugins/orders/syncs/order-workflow.sync.d.ts.map +1 -0
  57. package/dist/plugins/orders/syncs/order-workflow.sync.js +168 -0
  58. package/dist/plugins/orders/syncs/order-workflow.sync.js.map +1 -0
  59. package/dist/plugins/payments/concepts/Payment.d.ts +3 -0
  60. package/dist/plugins/payments/concepts/Payment.d.ts.map +1 -0
  61. package/dist/plugins/payments/concepts/Payment.js +156 -0
  62. package/dist/plugins/payments/concepts/Payment.js.map +1 -0
  63. package/dist/plugins/payments/index.d.ts +3 -0
  64. package/dist/plugins/payments/index.d.ts.map +1 -0
  65. package/dist/plugins/payments/index.js +16 -0
  66. package/dist/plugins/payments/index.js.map +1 -0
  67. package/dist/plugins/payments/syncs/payment-workflow.sync.d.ts +3 -0
  68. package/dist/plugins/payments/syncs/payment-workflow.sync.d.ts.map +1 -0
  69. package/dist/plugins/payments/syncs/payment-workflow.sync.js +264 -0
  70. package/dist/plugins/payments/syncs/payment-workflow.sync.js.map +1 -0
  71. package/dist/plugins/products/concepts/Product.d.ts +3 -0
  72. package/dist/plugins/products/concepts/Product.d.ts.map +1 -0
  73. package/dist/plugins/products/concepts/Product.js +85 -0
  74. package/dist/plugins/products/concepts/Product.js.map +1 -0
  75. package/dist/plugins/products/index.d.ts +3 -0
  76. package/dist/plugins/products/index.d.ts.map +1 -0
  77. package/dist/plugins/products/index.js +16 -0
  78. package/dist/plugins/products/index.js.map +1 -0
  79. package/dist/plugins/products/syncs/product-events.sync.d.ts +3 -0
  80. package/dist/plugins/products/syncs/product-events.sync.d.ts.map +1 -0
  81. package/dist/plugins/products/syncs/product-events.sync.js +77 -0
  82. package/dist/plugins/products/syncs/product-events.sync.js.map +1 -0
  83. package/dist/plugins/users/concepts/User.d.ts +3 -0
  84. package/dist/plugins/users/concepts/User.d.ts.map +1 -0
  85. package/dist/plugins/users/concepts/User.js +81 -0
  86. package/dist/plugins/users/concepts/User.js.map +1 -0
  87. package/dist/plugins/users/index.d.ts +3 -0
  88. package/dist/plugins/users/index.d.ts.map +1 -0
  89. package/dist/plugins/users/index.js +16 -0
  90. package/dist/plugins/users/index.js.map +1 -0
  91. package/dist/plugins/users/syncs/user-events.sync.d.ts +3 -0
  92. package/dist/plugins/users/syncs/user-events.sync.d.ts.map +1 -0
  93. package/dist/plugins/users/syncs/user-events.sync.js +75 -0
  94. package/dist/plugins/users/syncs/user-events.sync.js.map +1 -0
  95. package/package.json +40 -0
  96. package/src/core/EventBus.ts +55 -0
  97. package/src/core/PluginManager.ts +51 -0
  98. package/src/index.ts +169 -0
  99. package/src/plugins/analytics/concepts/Analytics.ts +53 -0
  100. package/src/plugins/analytics/index.ts +15 -0
  101. package/src/plugins/analytics/syncs/analytics-events.sync.ts +103 -0
  102. package/src/plugins/inventory/concepts/Inventory.ts +73 -0
  103. package/src/plugins/inventory/index.ts +14 -0
  104. package/src/plugins/notifications/concepts/Notification.ts +49 -0
  105. package/src/plugins/notifications/index.ts +14 -0
  106. package/src/plugins/orders/concepts/Order.ts +118 -0
  107. package/src/plugins/orders/index.ts +15 -0
  108. package/src/plugins/orders/syncs/order-workflow.sync.ts +173 -0
  109. package/src/plugins/payments/concepts/Payment.ts +186 -0
  110. package/src/plugins/payments/index.ts +15 -0
  111. package/src/plugins/payments/syncs/payment-workflow.sync.ts +274 -0
  112. package/src/plugins/products/concepts/Product.ts +102 -0
  113. package/src/plugins/products/index.ts +15 -0
  114. package/src/plugins/products/syncs/product-events.sync.ts +78 -0
  115. package/src/plugins/users/concepts/User.ts +97 -0
  116. package/src/plugins/users/index.ts +15 -0
  117. package/src/plugins/users/syncs/user-events.sync.ts +76 -0
  118. package/tsconfig.json +9 -0
@@ -0,0 +1,15 @@
1
+ // plugins/analytics/index.ts
2
+ import { Plugin } from '../../core/PluginManager';
3
+ import { Analytics } from './concepts/Analytics';
4
+ import { analyticsEventSyncs } from './syncs/analytics-events.sync';
5
+
6
+ export const analyticsPlugin: Plugin = {
7
+ name: 'analytics',
8
+ concepts: {
9
+ Analytics
10
+ },
11
+ syncs: analyticsEventSyncs,
12
+ initialize: async (_engine) => {
13
+ console.log('📈 Analytics plugin initialized');
14
+ }
15
+ };
@@ -0,0 +1,103 @@
1
+ // plugins/analytics/syncs/analytics-events.sync.ts
2
+ import { SyncRule } from '@legible-sync/core';
3
+
4
+ export const analyticsEventSyncs: SyncRule[] = [
5
+ // Track user registration events
6
+ {
7
+ name: "TrackUserRegistration",
8
+ when: [
9
+ {
10
+ concept: "User",
11
+ action: "register"
12
+ }
13
+ ],
14
+ then: [
15
+ {
16
+ concept: "Analytics",
17
+ action: "track",
18
+ input: {
19
+ event: "user_registered",
20
+ data: {
21
+ userId: "?userId",
22
+ username: "?username",
23
+ email: "?email"
24
+ }
25
+ }
26
+ }
27
+ ]
28
+ },
29
+
30
+ // Track product creation events
31
+ {
32
+ name: "TrackProductCreation",
33
+ when: [
34
+ {
35
+ concept: "Product",
36
+ action: "create"
37
+ }
38
+ ],
39
+ then: [
40
+ {
41
+ concept: "Analytics",
42
+ action: "track",
43
+ input: {
44
+ event: "product_created",
45
+ data: {
46
+ productId: "?productId",
47
+ name: "?name",
48
+ category: "?category",
49
+ price: "?price"
50
+ }
51
+ }
52
+ }
53
+ ]
54
+ },
55
+
56
+ // Track order events
57
+ {
58
+ name: "TrackOrderCreation",
59
+ when: [
60
+ {
61
+ concept: "Order",
62
+ action: "create"
63
+ }
64
+ ],
65
+ then: [
66
+ {
67
+ concept: "Analytics",
68
+ action: "track",
69
+ input: {
70
+ event: "order_created",
71
+ data: {
72
+ orderId: "?orderId",
73
+ userId: "?userId",
74
+ itemCount: "?items.length"
75
+ }
76
+ }
77
+ }
78
+ ]
79
+ },
80
+
81
+ {
82
+ name: "TrackOrderConfirmation",
83
+ when: [
84
+ {
85
+ concept: "Order",
86
+ action: "confirm"
87
+ }
88
+ ],
89
+ then: [
90
+ {
91
+ concept: "Analytics",
92
+ action: "track",
93
+ input: {
94
+ event: "order_confirmed",
95
+ data: {
96
+ orderId: "?orderId",
97
+ total: "?total"
98
+ }
99
+ }
100
+ }
101
+ ]
102
+ }
103
+ ];
@@ -0,0 +1,73 @@
1
+ // plugins/inventory/concepts/Inventory.ts
2
+ import { Concept } from '@legible-sync/core';
3
+
4
+ export const Inventory: Concept = {
5
+ state: {
6
+ stock: new Map<string, number>(),
7
+ reservations: new Map<string, any[]>(),
8
+ },
9
+
10
+ async execute(action: string, input: any) {
11
+ const state = this.state;
12
+
13
+ switch (action) {
14
+ case 'setStock': {
15
+ const { productId, quantity } = input;
16
+
17
+ if (!productId || quantity < 0) {
18
+ throw new Error('Valid productId and non-negative quantity required');
19
+ }
20
+
21
+ state.stock.set(productId, quantity);
22
+ return { productId, quantity };
23
+ }
24
+
25
+ case 'checkAvailability': {
26
+ const { orderId, items } = input;
27
+
28
+ let total = 0;
29
+ const unavailable: any[] = [];
30
+
31
+ for (const item of items) {
32
+ const stock = state.stock.get(item.productId) || 0;
33
+ if (stock < item.quantity) {
34
+ unavailable.push({
35
+ productId: item.productId,
36
+ requested: item.quantity,
37
+ available: stock
38
+ });
39
+ }
40
+ // Calculate total from input items (prices should be provided)
41
+ total += item.quantity * (item.price || 0);
42
+ }
43
+
44
+ const available = unavailable.length === 0;
45
+ return { available, total, unavailable };
46
+ }
47
+
48
+ case 'deduct': {
49
+ const { orderId, items } = input;
50
+
51
+ for (const item of items) {
52
+ const currentStock = state.stock.get(item.productId) || 0;
53
+ if (currentStock < item.quantity) {
54
+ throw new Error(`Insufficient stock for product ${item.productId}`);
55
+ }
56
+
57
+ state.stock.set(item.productId, currentStock - item.quantity);
58
+ }
59
+
60
+ return { success: true };
61
+ }
62
+
63
+ case 'getStock': {
64
+ const { productId } = input;
65
+ const quantity = state.stock.get(productId) || 0;
66
+ return { productId, quantity };
67
+ }
68
+
69
+ default:
70
+ throw new Error(`Unknown action: ${action}`);
71
+ }
72
+ }
73
+ };
@@ -0,0 +1,14 @@
1
+ // plugins/inventory/index.ts
2
+ import { Plugin } from '../../core/PluginManager';
3
+ import { Inventory } from './concepts/Inventory';
4
+
5
+ export const inventoryPlugin: Plugin = {
6
+ name: 'inventory',
7
+ concepts: {
8
+ Inventory
9
+ },
10
+ syncs: [],
11
+ initialize: async (_engine) => {
12
+ console.log('📊 Inventory plugin initialized');
13
+ }
14
+ };
@@ -0,0 +1,49 @@
1
+ // plugins/notifications/concepts/Notification.ts
2
+ import { Concept } from '@legible-sync/core';
3
+
4
+ export const Notification: Concept = {
5
+ state: {
6
+ sent: new Map<string, any[]>(),
7
+ },
8
+
9
+ async execute(action: string, input: any) {
10
+ const state = this.state;
11
+
12
+ switch (action) {
13
+ case 'send': {
14
+ const { type, to, template, data } = input;
15
+
16
+ // Simulate sending notification
17
+ const notificationId = `notif_${Date.now()}`;
18
+ const notification = {
19
+ id: notificationId,
20
+ type,
21
+ to,
22
+ template,
23
+ data,
24
+ sentAt: new Date(),
25
+ status: 'sent'
26
+ };
27
+
28
+ // Store sent notifications
29
+ if (!state.sent.has(to)) {
30
+ state.sent.set(to, []);
31
+ }
32
+ state.sent.get(to)!.push(notification);
33
+
34
+ console.log(`📧 ${type.toUpperCase()} sent to ${to}: ${template}`);
35
+
36
+ return { notificationId, notification };
37
+ }
38
+
39
+ case 'getHistory': {
40
+ const { recipient } = input;
41
+ const history = state.sent.get(recipient) || [];
42
+ return { notifications: history };
43
+ }
44
+
45
+ default:
46
+ throw new Error(`Unknown action: ${action}`);
47
+ }
48
+ }
49
+ };
@@ -0,0 +1,14 @@
1
+ // plugins/notifications/index.ts
2
+ import { Plugin } from '../../core/PluginManager';
3
+ import { Notification } from './concepts/Notification';
4
+
5
+ export const notificationsPlugin: Plugin = {
6
+ name: 'notifications',
7
+ concepts: {
8
+ Notification
9
+ },
10
+ syncs: [],
11
+ initialize: async (_engine) => {
12
+ console.log('🔔 Notifications plugin initialized');
13
+ }
14
+ };
@@ -0,0 +1,118 @@
1
+ // plugins/orders/concepts/Order.ts
2
+ import { Concept } from '@legible-sync/core';
3
+ import { v4 as uuidv4 } from 'uuid';
4
+
5
+ export const Order: Concept = {
6
+ state: {
7
+ orders: new Map<string, any>(),
8
+ orderItems: new Map<string, any[]>(),
9
+ },
10
+
11
+ async execute(action: string, input: any) {
12
+ const state = this.state;
13
+
14
+ switch (action) {
15
+ case 'create': {
16
+ const { userId, items } = input;
17
+
18
+ // Validation
19
+ if (!userId || !items || !Array.isArray(items) || items.length === 0) {
20
+ throw new Error('User ID and items array are required');
21
+ }
22
+
23
+ // Validate items
24
+ for (const item of items) {
25
+ if (!item.productId || !item.quantity || item.quantity <= 0) {
26
+ throw new Error('Each item must have productId and positive quantity');
27
+ }
28
+ }
29
+
30
+ // Create order
31
+ const orderId = uuidv4();
32
+ const order = {
33
+ id: orderId,
34
+ userId,
35
+ status: 'pending',
36
+ total: 0, // Will be calculated after inventory check
37
+ createdAt: new Date(),
38
+ items: items.map((item: any) => ({
39
+ productId: item.productId,
40
+ quantity: item.quantity,
41
+ price: item.price || 0
42
+ }))
43
+ };
44
+
45
+ state.orders.set(orderId, order);
46
+ state.orderItems.set(orderId, order.items);
47
+
48
+ return { orderId, order };
49
+ }
50
+
51
+ case 'confirm': {
52
+ const { orderId, total } = input;
53
+ const order = state.orders.get(orderId);
54
+ if (!order) {
55
+ throw new Error('Order not found');
56
+ }
57
+
58
+ if (order.status !== 'pending') {
59
+ throw new Error('Order is not in pending status');
60
+ }
61
+
62
+ order.status = 'confirmed';
63
+ order.total = total;
64
+ order.confirmedAt = new Date();
65
+ state.orders.set(orderId, order);
66
+
67
+ return { order };
68
+ }
69
+
70
+ case 'cancel': {
71
+ const { orderId } = input;
72
+ const order = state.orders.get(orderId);
73
+ if (!order) {
74
+ throw new Error('Order not found');
75
+ }
76
+
77
+ order.status = 'cancelled';
78
+ order.cancelledAt = new Date();
79
+ state.orders.set(orderId, order);
80
+
81
+ return { order };
82
+ }
83
+
84
+ case 'ship': {
85
+ const { orderId, trackingNumber } = input;
86
+ const order = state.orders.get(orderId);
87
+ if (!order) {
88
+ throw new Error('Order not found');
89
+ }
90
+
91
+ if (order.status !== 'confirmed') {
92
+ throw new Error('Order must be confirmed before shipping');
93
+ }
94
+
95
+ order.status = 'shipped';
96
+ order.trackingNumber = trackingNumber;
97
+ order.shippedAt = new Date();
98
+ state.orders.set(orderId, order);
99
+
100
+ return { order };
101
+ }
102
+
103
+ case 'get': {
104
+ const { orderId } = input;
105
+ const order = state.orders.get(orderId);
106
+ if (!order) {
107
+ throw new Error('Order not found');
108
+ }
109
+
110
+ const items = state.orderItems.get(orderId) || [];
111
+ return { order: { ...order, items } };
112
+ }
113
+
114
+ default:
115
+ throw new Error(`Unknown action: ${action}`);
116
+ }
117
+ }
118
+ };
@@ -0,0 +1,15 @@
1
+ // plugins/orders/index.ts
2
+ import { Plugin } from '../../core/PluginManager';
3
+ import { Order } from './concepts/Order';
4
+ import { orderWorkflowSyncs } from './syncs/order-workflow.sync';
5
+
6
+ export const ordersPlugin: Plugin = {
7
+ name: 'orders',
8
+ concepts: {
9
+ Order
10
+ },
11
+ syncs: orderWorkflowSyncs,
12
+ initialize: async (_engine) => {
13
+ console.log('🛒 Orders plugin initialized');
14
+ }
15
+ };
@@ -0,0 +1,173 @@
1
+ // plugins/orders/syncs/order-workflow.sync.ts
2
+ import { SyncRule } from '@legible-sync/core';
3
+
4
+ export const orderWorkflowSyncs: SyncRule[] = [
5
+ // After order creation, check inventory availability
6
+ {
7
+ name: "CheckInventoryAfterOrderCreation",
8
+ when: [
9
+ {
10
+ concept: "Order",
11
+ action: "create"
12
+ }
13
+ ],
14
+ then: [
15
+ {
16
+ concept: "Inventory",
17
+ action: "checkAvailability",
18
+ input: {
19
+ orderId: "?orderId",
20
+ items: "?order.items"
21
+ }
22
+ }
23
+ ]
24
+ },
25
+
26
+ // If inventory is available, confirm the order
27
+ {
28
+ name: "ConfirmOrderIfInventoryAvailable",
29
+ when: [
30
+ {
31
+ concept: "Inventory",
32
+ action: "checkAvailability"
33
+ }
34
+ ],
35
+ where: {
36
+ filter: (bindings) => bindings.available === true
37
+ },
38
+ then: [
39
+ {
40
+ concept: "Order",
41
+ action: "confirm",
42
+ input: {
43
+ orderId: "?orderId",
44
+ total: "?total"
45
+ }
46
+ }
47
+ ]
48
+ },
49
+
50
+ // After order confirmation, get user details for notifications
51
+ {
52
+ name: "GetUserDetailsAfterOrderConfirm",
53
+ when: [
54
+ {
55
+ concept: "Order",
56
+ action: "confirm"
57
+ }
58
+ ],
59
+ then: [
60
+ {
61
+ concept: "User",
62
+ action: "get",
63
+ input: {
64
+ userId: "?order.userId"
65
+ }
66
+ }
67
+ ]
68
+ },
69
+
70
+ // If inventory is not available, cancel the order
71
+ {
72
+ name: "CancelOrderIfInventoryUnavailable",
73
+ when: [
74
+ {
75
+ concept: "Inventory",
76
+ action: "checkAvailability"
77
+ }
78
+ ],
79
+ where: {
80
+ filter: (bindings) => bindings.available === false
81
+ },
82
+ then: [
83
+ {
84
+ concept: "Order",
85
+ action: "cancel",
86
+ input: {
87
+ orderId: "?orderId"
88
+ }
89
+ }
90
+ ]
91
+ },
92
+
93
+ // When order is confirmed, deduct inventory
94
+ {
95
+ name: "DeductInventoryOnOrderConfirm",
96
+ when: [
97
+ {
98
+ concept: "Order",
99
+ action: "confirm"
100
+ }
101
+ ],
102
+ then: [
103
+ {
104
+ concept: "Inventory",
105
+ action: "deduct",
106
+ input: {
107
+ orderId: "?orderId",
108
+ items: "?order.items"
109
+ }
110
+ }
111
+ ]
112
+ },
113
+
114
+ // When user details are retrieved after order confirmation, send notification
115
+ {
116
+ name: "SendOrderConfirmationNotification",
117
+ when: [
118
+ {
119
+ concept: "Order",
120
+ action: "confirm"
121
+ },
122
+ {
123
+ concept: "User",
124
+ action: "get"
125
+ }
126
+ ],
127
+ where: {
128
+ filter: (bindings) => bindings.order.userId === bindings.user.id
129
+ },
130
+ then: [
131
+ {
132
+ concept: "Notification",
133
+ action: "send",
134
+ input: {
135
+ type: "email",
136
+ to: "?user.email",
137
+ template: "order-confirmation",
138
+ data: {
139
+ orderId: "?order.id",
140
+ total: "?order.total",
141
+ items: "?order.items"
142
+ }
143
+ }
144
+ }
145
+ ]
146
+ },
147
+
148
+ // When order is shipped, send shipping notification
149
+ {
150
+ name: "SendShippingNotification",
151
+ when: [
152
+ {
153
+ concept: "Order",
154
+ action: "ship"
155
+ }
156
+ ],
157
+ then: [
158
+ {
159
+ concept: "Notification",
160
+ action: "send",
161
+ input: {
162
+ type: "email",
163
+ to: "?user.email",
164
+ template: "order-shipped",
165
+ data: {
166
+ orderId: "?orderId",
167
+ trackingNumber: "?order.trackingNumber"
168
+ }
169
+ }
170
+ }
171
+ ]
172
+ }
173
+ ];