are-engine-core 1.0.0 → 1.0.1

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
@@ -1,40 +1,40 @@
1
- # are-core — Action Rule Event Engine
1
+ # are-engine-core — Action Rule Event Engine
2
2
 
3
- Sıfır bağımlılık, hafif olay-kural-eylem motoru.
3
+ Zero dependency, lightweight event-rule-action engine.
4
4
 
5
- Browser, Node.js, React, Vue, React Native, Electron — her JS/TS ortamında çalışır.
5
+ Browser, Node.js, React, Vue, React Native, Electron — works in any JS/TS environment.
6
6
 
7
- > Bu paket, C# [ARE.Core](../ARE.Core/) motorunun JavaScript/TypeScript portudur.
8
- > Aynı mimari, aynı API, aynı davranış.
7
+ > This package is the JavaScript/TypeScript port of the C# [ARE.Core](../ARE.Core/) engine.
8
+ > Same architecture, same API, same behavior.
9
9
 
10
10
  ---
11
11
 
12
- ## Kurulum
12
+ ## Installation
13
13
 
14
14
  ```bash
15
- npm install are-core
15
+ npm install are-engine-core
16
16
  ```
17
17
 
18
- Build adımı yok. TypeScript tip tanımları dahil gelir.
18
+ No build step required. TypeScript type definitions are included out of the box.
19
19
 
20
20
  ---
21
21
 
22
- ## Hızlı Başlangıç
22
+ ## Quick Start
23
23
 
24
24
  ```javascript
25
- const { AreEngine, Rule } = require('are-core');
26
- // veya
27
- // import { AreEngine, Rule } from 'are-core';
25
+ const { AreEngine, Rule } = require('are-engine-core');
26
+ // or
27
+ // import { AreEngine, Rule } from 'are-engine-core';
28
28
 
29
- // 1) Engine oluştur
29
+ // 1) Create the engine
30
30
  const engine = new AreEngine();
31
31
 
32
- // 2) Action kaydet
32
+ // 2) Register an action
33
33
  engine.registerAction('send_email', async (ctx, s) => {
34
- console.log('Email gönderildi:', s.get('template'));
34
+ console.log('Email sent:', s.get('template'));
35
35
  });
36
36
 
37
- // 3) Kural tanımla
37
+ // 3) Define a rule
38
38
  engine.addRule(
39
39
  Rule.create('vip_order')
40
40
  .on('order.created')
@@ -42,23 +42,23 @@ engine.addRule(
42
42
  .then('send_email', s => s.set('template', 'vip_welcome'))
43
43
  );
44
44
 
45
- // 4) Event fırlat
45
+ // 4) Fire an event
46
46
  await engine.fire('order.created', e => e.set('total', 7500));
47
- // Çıktı: Email gönderildi: vip_welcome
47
+ // Output: Email sent: vip_welcome
48
48
  ```
49
49
 
50
50
  ---
51
51
 
52
- ## Detaylı Kullanım
52
+ ## Detailed Usage
53
53
 
54
- ### Action Tanımlama Obje ile
54
+ ### Defining an Action — Object-Based
55
55
 
56
56
  ```javascript
57
57
  const damageAction = {
58
58
  actionType: 'damage',
59
59
  execute: async (ctx, settings) => {
60
60
  const amount = settings.get('amount');
61
- console.log(`${amount} hasar verildi!`);
61
+ console.log(`${amount} damage dealt!`);
62
62
  ctx.set('lastDamage', amount);
63
63
  }
64
64
  };
@@ -66,7 +66,7 @@ const damageAction = {
66
66
  engine.registerAction(damageAction);
67
67
  ```
68
68
 
69
- ### Action Tanımlama — Inline
69
+ ### Defining an Action — Inline
70
70
 
71
71
  ```javascript
72
72
  engine.registerAction('log', async (ctx, s) => {
@@ -74,7 +74,7 @@ engine.registerAction('log', async (ctx, s) => {
74
74
  });
75
75
  ```
76
76
 
77
- ### Kural Tanımlama — Fluent Builder
77
+ ### Defining a Rule — Fluent Builder
78
78
 
79
79
  ```javascript
80
80
  engine.addRule(
@@ -90,63 +90,63 @@ engine.addRule(
90
90
  );
91
91
  ```
92
92
 
93
- ### Birden Fazla Event Dinleme
93
+ ### Listening to Multiple Events
94
94
 
95
95
  ```javascript
96
96
  Rule.create('license_warning')
97
97
  .on('app.started', 'license.checked')
98
98
  .when('expiring', (evt) => (evt.data.days_remaining ?? 999) <= 7)
99
99
  .then('show_notification', s => s
100
- .set('title', 'Lisans Uyarısı')
101
- .set('message', '7 gün kaldı!'))
100
+ .set('title', 'License Warning')
101
+ .set('message', '7 days remaining!'))
102
102
  ```
103
103
 
104
- ### Koşul Tipleri
104
+ ### Condition Types
105
105
 
106
106
  ```javascript
107
- // Alan karşılaştırma (deklaratif)
107
+ // Field comparison (declarative)
108
108
  .whenEquals('status', 'active')
109
109
  .whenGreaterThan('score', 100)
110
110
  .whenLessThan('stock', 10)
111
111
  .whenField('category', CompareOp.Contains, 'premium')
112
112
  .whenField('role', CompareOp.In, ['admin', 'moderator'])
113
113
 
114
- // Lambda (esnek)
114
+ // Lambda (flexible)
115
115
  .when('custom_check', (evt, ctx) => {
116
116
  return evt.data.total > 1000 && ctx.get('user_type') === 'vip';
117
117
  })
118
118
  ```
119
119
 
120
- ### MatchMode — Koşul Eşleşme Modları
120
+ ### MatchMode — Condition Matching Modes
121
121
 
122
122
  ```javascript
123
- const { MatchMode } = require('are-core');
123
+ const { MatchMode } = require('are-engine-core');
124
124
 
125
- // Tüm koşullar doğru olmalı (AND) — varsayılan
125
+ // All conditions must be true (AND) — default
126
126
  .withMatchMode(MatchMode.All)
127
127
 
128
- // En az bir koşul doğru olmalı (OR)
128
+ // At least one condition must be true (OR)
129
129
  .withMatchMode(MatchMode.Any)
130
130
 
131
- // Hiçbir koşul doğru olmamalı (NOT)
131
+ // No conditions should be true (NOT)
132
132
  .withMatchMode(MatchMode.None)
133
133
 
134
- // Tam olarak bir koşul doğru olmalı
134
+ // Exactly one condition must be true
135
135
  .withMatchMode(MatchMode.ExactlyOne)
136
136
  ```
137
137
 
138
138
  ### Middleware
139
139
 
140
140
  ```javascript
141
- // Loglama
141
+ // Logging middleware
142
142
  engine.use(0, async (ctx, next) => {
143
- console.log('Event başladı:', ctx.currentEvent.eventType);
143
+ console.log('Event started:', ctx.currentEvent.eventType);
144
144
  const start = Date.now();
145
145
  await next();
146
- console.log('Event bitti:', (Date.now() - start) + 'ms');
146
+ console.log('Event completed:', (Date.now() - start) + 'ms');
147
147
  });
148
148
 
149
- // Auth kontrolü
149
+ // Auth middleware
150
150
  engine.use(-10, async (ctx, next) => {
151
151
  if (!ctx.get('isAuthenticated')) {
152
152
  ctx.stopPipeline = true;
@@ -156,27 +156,27 @@ engine.use(-10, async (ctx, next) => {
156
156
  });
157
157
  ```
158
158
 
159
- ### Doğrudan Listener (Kural Olmadan)
159
+ ### Direct Listener (Without Rules)
160
160
 
161
161
  ```javascript
162
162
  engine.on('order.created', async (evt, ctx) => {
163
- console.log('Sipariş:', evt.data.order_id);
163
+ console.log('Order received:', evt.data.order_id);
164
164
  });
165
165
  ```
166
166
 
167
- ### Dinamik Kural Yönetimi
167
+ ### Dynamic Rule Management
168
168
 
169
169
  ```javascript
170
- // Tek kural
170
+ // Individual rules
171
171
  engine.disableRule('seasonal_discount');
172
172
  engine.enableRule('seasonal_discount');
173
173
  engine.removeRule('old_rule');
174
174
 
175
- // Grup toplu
175
+ // Entire groups
176
176
  engine.disableGroup('marketing');
177
177
  engine.enableGroup('marketing');
178
178
 
179
- // Runtime'da yeni kural ekle
179
+ // Add a new rule at runtime
180
180
  engine.addRule(
181
181
  Rule.create('flash_sale')
182
182
  .inGroup('marketing')
@@ -186,17 +186,17 @@ engine.addRule(
186
186
  );
187
187
  ```
188
188
 
189
- ### Akış Kontrolü
189
+ ### Flow Control
190
190
 
191
191
  ```javascript
192
- // Pipeline'ı tamamen durdur (sonraki kurallar çalışmaz)
192
+ // Stop the entire pipeline (remaining rules will not execute)
193
193
  engine.registerAction('validate', async (ctx) => {
194
194
  if (!ctx.currentEvent.data.valid) {
195
195
  ctx.stopPipeline = true;
196
196
  }
197
197
  });
198
198
 
199
- // Sadece mevcut kuralın kalan action'larını atla
199
+ // Skip only the remaining actions of the current rule
200
200
  engine.registerAction('conditional_skip', async (ctx) => {
201
201
  if (someCondition) {
202
202
  ctx.skipRemainingActions = true;
@@ -204,7 +204,7 @@ engine.registerAction('conditional_skip', async (ctx) => {
204
204
  });
205
205
  ```
206
206
 
207
- ### Context — Action'lar Arası Veri Paylaşımı
207
+ ### Context — Sharing Data Between Actions
208
208
 
209
209
  ```javascript
210
210
  engine.registerAction('calculate', async (ctx) => {
@@ -216,35 +216,35 @@ engine.registerAction('apply_tax', async (ctx) => {
216
216
  ctx.set('totalWithTax', total * 1.18);
217
217
  });
218
218
 
219
- // İkisi aynı event'te sırayla çalışırsa, context üzerinden veri paylaşır
219
+ // When both run sequentially in the same event, they share data via context
220
220
  ```
221
221
 
222
- ### Sonuç Okuma
222
+ ### Reading Results
223
223
 
224
224
  ```javascript
225
225
  const result = await engine.fire('order.created', e => e.set('total', 7500));
226
226
 
227
- console.log('Tetiklenen:', result.firedRules.length);
228
- console.log('Atlanan:', result.skippedRules.length);
229
- console.log('Pipeline durdu mu:', result.pipelineStopped);
230
- console.log('Süre:', result.duration + 'ms');
227
+ console.log('Fired:', result.firedRules.length);
228
+ console.log('Skipped:', result.skippedRules.length);
229
+ console.log('Pipeline stopped:', result.pipelineStopped);
230
+ console.log('Duration:', result.duration + 'ms');
231
231
 
232
232
  result.firedRules.forEach(r => {
233
233
  console.log(` ${r.ruleId} → ${r.executedActions.join(', ')}`);
234
234
  });
235
235
 
236
236
  result.skippedRules.forEach(r => {
237
- console.log(` ${r.ruleId} → sağlanmayan: ${r.failedConditions.join(', ')}`);
237
+ console.log(` ${r.ruleId} → failed: ${r.failedConditions.join(', ')}`);
238
238
  });
239
239
  ```
240
240
 
241
241
  ---
242
242
 
243
- ## React Kullanımı
243
+ ## React Usage
244
244
 
245
245
  ```jsx
246
- import { AreEngine, Rule, GameEvent, AreContext } from 'are-core';
247
- import { useRef, useState } from 'react';
246
+ import { AreEngine, Rule, GameEvent, AreContext } from 'are-engine-core';
247
+ import { useRef } from 'react';
248
248
 
249
249
  function useAreEngine(setup) {
250
250
  const engineRef = useRef(null);
@@ -263,7 +263,7 @@ function useAreEngine(setup) {
263
263
  return { fire, engine: engineRef.current };
264
264
  }
265
265
 
266
- // Kullanım
266
+ // Usage
267
267
  function App() {
268
268
  const { fire } = useAreEngine((engine) => {
269
269
  engine.registerAction('toast', async (ctx, s) => {
@@ -274,45 +274,45 @@ function App() {
274
274
  Rule.create('big_order')
275
275
  .on('cart.checkout')
276
276
  .whenGreaterThan('total', 500)
277
- .then('toast', s => s.set('message', '🎉 Kargo bedava!'))
277
+ .then('toast', s => s.set('message', 'Free shipping!'))
278
278
  );
279
279
  });
280
280
 
281
- return <button onClick={() => fire('cart.checkout', { total: 700 })}>Ödeme;
281
+ return <button onClick={() => fire('cart.checkout', { total: 700 })}>Checkout</button>;
282
282
  }
283
283
  ```
284
284
 
285
285
  ---
286
286
 
287
- ## Export Listesi
287
+ ## Export List
288
288
 
289
289
  ```javascript
290
290
  const {
291
- AreEngine, // Çekirdek motor
292
- AreContext, // Paylaşılan veri çantası
293
- GameEvent, // Varsayılan event implementasyonu
294
- Rule, // Fluent kural builder
295
- ActionSettings, // Action parametreleri
296
- FieldCondition, // Alan karşılaştırma koşulu
291
+ AreEngine, // Core engine
292
+ AreContext, // Shared data bag
293
+ GameEvent, // Default event implementation
294
+ Rule, // Fluent rule builder
295
+ ActionSettings, // Action parameters
296
+ FieldCondition, // Field comparison condition
297
297
  MatchMode, // All, Any, None, ExactlyOne
298
- CompareOp, // Equal, GreaterThan, Contains, In vb.
299
- } = require('are-core');
298
+ CompareOp, // Equal, GreaterThan, Contains, In, etc.
299
+ } = require('are-engine-core');
300
300
  ```
301
301
 
302
302
  ---
303
303
 
304
- ## TypeScript Desteği
304
+ ## TypeScript Support
305
305
 
306
- Tip tanımları (`are-core.d.ts`) pakete dahildir. Ek kurulum gerekmez.
306
+ Type definitions (`are-core.d.ts`) are included in the package. No additional setup required.
307
307
 
308
308
  ```typescript
309
- import { AreEngine, Rule, IAction, IEvent, AreContext, ActionSettings } from 'are-core';
309
+ import { AreEngine, Rule, IAction, IEvent, AreContext, ActionSettings } from 'are-engine-core';
310
310
 
311
- // Kendi action tipini tanımla
311
+ // Define a custom action type
312
312
  const myAction: IAction = {
313
313
  actionType: 'my_action',
314
- execute: async (ctx: AreContext, settings: ActionSettings): Promise => {
315
- const value = settings.get('key');
314
+ execute: async (ctx: AreContext, settings: ActionSettings): Promise<void> => {
315
+ const value = settings.get<string>('key');
316
316
  ctx.set('result', value);
317
317
  }
318
318
  };
@@ -320,34 +320,33 @@ const myAction: IAction = {
320
320
 
321
321
  ---
322
322
 
323
- ## Farklı Ortamlarda Import
323
+ ## Import Patterns
324
324
 
325
325
  ```javascript
326
326
  // CommonJS (Node.js, Electron)
327
- const { AreEngine, Rule } = require('are-core');
327
+ const { AreEngine, Rule } = require('are-engine-core');
328
328
 
329
329
  // ES Module (React, Vue, Angular, Vite, Next.js)
330
- import { AreEngine, Rule } from 'are-core';
330
+ import { AreEngine, Rule } from 'are-engine-core';
331
331
 
332
332
  // Script tag (browser - global)
333
- // dist/are-core.js dosyasını doğrudan kullanabilirsin
334
-
333
+ // You can use the dist/are-core.js file directly
335
334
  ```
336
335
 
337
336
  ---
338
337
 
339
- ## Testler
338
+ ## Tests
340
339
 
341
340
  ```bash
342
341
  npm test
343
- # veya
342
+ # or
344
343
  node test/test.js
345
344
  ```
346
345
 
347
- 17 test: engine, koşullar, MatchMode, middleware, pipeline kontrolü, grup yönetimi, context paylaşımı.
346
+ 17 tests covering: engine, conditions, MatchMode, middleware, pipeline control, group management, and context sharing.
348
347
 
349
348
  ---
350
349
 
351
- ## Lisans
350
+ ## License
352
351
 
353
- MIT
352
+ MIT
package/dist/are-core.js CHANGED
@@ -165,7 +165,7 @@ class AreEngine {
165
165
  this.onLog = null;
166
166
  }
167
167
 
168
- // -- Kayıt --
168
+ // -- Registration --
169
169
 
170
170
  registerAction(actionTypeOrObj, handler) {
171
171
  if (typeof actionTypeOrObj === 'string') {
@@ -195,7 +195,7 @@ class AreEngine {
195
195
  return this;
196
196
  }
197
197
 
198
- // -- Kural Yönetimi --
198
+ // -- Rule Management --
199
199
 
200
200
  enableRule(id) { var r = this._rules.find(function (r) { return r.ruleId === id; }); if (r) r.isEnabled = true; return this; }
201
201
  disableRule(id) { var r = this._rules.find(function (r) { return r.ruleId === id; }); if (r) r.isEnabled = false; return this; }
@@ -239,23 +239,23 @@ class AreEngine {
239
239
  var self = this;
240
240
 
241
241
  var coreProcess = async function () {
242
- // 1) Doğrudan dinleyiciler
242
+ // 1) Direct listeners
243
243
  var listeners = self._listeners.get(evt.eventType) || [];
244
244
  for (var i = 0; i < listeners.length; i++) {
245
245
  if (ctx.stopPipeline) break;
246
246
  await listeners[i](evt, ctx);
247
247
  }
248
248
 
249
- // 2) Eşleşen kurallar (önceliğe göre)
249
+ // 2) Matching rules (by priority)
250
250
  var matching = self._rules
251
251
  .filter(function (r) { return r.isEnabled && r.eventTypes.indexOf(evt.eventType) !== -1; })
252
252
  .sort(function (a, b) { return b.priority - a.priority; });
253
253
 
254
- self._log('[ARE] Event \'' + evt.eventType + '\' → ' + matching.length + ' aday kural');
254
+ self._log('[ARE] Event \'' + evt.eventType + '\' → ' + matching.length + ' candidate rules');
255
255
 
256
256
  for (var j = 0; j < matching.length; j++) {
257
257
  if (ctx.stopPipeline) {
258
- self._log('[ARE] Pipeline durduruldu');
258
+ self._log('[ARE] Pipeline stopped');
259
259
  break;
260
260
  }
261
261
  var rule = matching[j];
@@ -268,7 +268,7 @@ class AreEngine {
268
268
  }
269
269
  };
270
270
 
271
- // Middleware zinciri
271
+ // Middleware chain
272
272
  var pipeline = coreProcess;
273
273
  for (var i = this._middlewares.length - 1; i >= 0; i--) {
274
274
  (function (mw, next) {
@@ -280,21 +280,21 @@ class AreEngine {
280
280
 
281
281
  result.pipelineStopped = ctx.stopPipeline;
282
282
  result.duration = Date.now() - start;
283
- this._log('[ARE] Event \'' + evt.eventType + '\' tamamlandı: ' +
284
- result.firedRules.length + ' tetiklendi, ' +
285
- result.skippedRules.length + ' atlandı, ' +
283
+ this._log('[ARE] Event \'' + evt.eventType + '\' completed: ' +
284
+ result.firedRules.length + ' fired, ' +
285
+ result.skippedRules.length + ' skipped, ' +
286
286
  result.duration + 'ms');
287
287
  return result;
288
288
  }
289
289
 
290
- // -- İç mekanizma --
290
+ // -- Internal --
291
291
 
292
292
  async _evaluateAndExecute(rule, evt, ctx) {
293
293
  var failedConditions = [];
294
294
  var conditionsMet = this._evaluateConditions(rule, evt, ctx, failedConditions);
295
295
 
296
296
  if (!conditionsMet) {
297
- this._log('[ARE] Kural \'' + rule.ruleId + '\' → koşullar sağlanmadı [' + failedConditions.join(', ') + ']');
297
+ this._log('[ARE] Rule \'' + rule.ruleId + '\' → conditions not met [' + failedConditions.join(', ') + ']');
298
298
  return { ruleId: rule.ruleId, conditionsMet: false, executedActions: [], failedConditions: failedConditions };
299
299
  }
300
300
 
@@ -307,16 +307,16 @@ class AreEngine {
307
307
  var action = this._actions.get(binding.actionType);
308
308
 
309
309
  if (!action) {
310
- this._log('[ARE] Action \'' + binding.actionType + '\' bulunamadı!');
310
+ this._log('[ARE] Action \'' + binding.actionType + '\' not found!');
311
311
  continue;
312
312
  }
313
313
 
314
314
  try {
315
- this._log('[ARE] → Action \'' + binding.actionType + '\' çalıştırılıyor');
315
+ this._log('[ARE] → Executing action \'' + binding.actionType + '\'');
316
316
  await action.execute(ctx, binding.settings);
317
317
  executedActions.push(binding.actionType);
318
318
  } catch (err) {
319
- this._log('[ARE] Action \'' + binding.actionType + '\' hata: ' + err.message);
319
+ this._log('[ARE] Action \'' + binding.actionType + '\' error: ' + err.message);
320
320
  break;
321
321
  }
322
322
  }
package/dist/are-core.mjs CHANGED
@@ -0,0 +1,358 @@
1
+ // ============================================================================
2
+ // ARE.Core - Action Rule Event Engine (ES Module)
3
+ // Zero dependency - Browser, Node.js, React Native, Electron
4
+ // ============================================================================
5
+
6
+ // ── Enums ──
7
+
8
+ const MatchMode = Object.freeze({
9
+ All: 'all',
10
+ Any: 'any',
11
+ None: 'none',
12
+ ExactlyOne: 'exactlyOne',
13
+ });
14
+
15
+ const CompareOp = Object.freeze({
16
+ Equal: 'eq',
17
+ NotEqual: 'neq',
18
+ GreaterThan: 'gt',
19
+ GreaterOrEqual: 'gte',
20
+ LessThan: 'lt',
21
+ LessOrEqual: 'lte',
22
+ Contains: 'contains',
23
+ StartsWith: 'startsWith',
24
+ In: 'in',
25
+ });
26
+
27
+ // ── AreContext ──
28
+
29
+ class AreContext {
30
+ constructor() {
31
+ this._data = new Map();
32
+ this.currentEvent = null;
33
+ this.currentRule = null;
34
+ this.stopPipeline = false;
35
+ this.skipRemainingActions = false;
36
+ }
37
+ set(key, value) { this._data.set(key, value); }
38
+ get(key) { return this._data.get(key); }
39
+ has(key) { return this._data.has(key); }
40
+ remove(key) { this._data.delete(key); }
41
+ clear() { this._data.clear(); }
42
+ }
43
+
44
+ // ── ActionSettings ──
45
+
46
+ class ActionSettings {
47
+ constructor() { this._values = {}; }
48
+ set(key, value) { this._values[key] = value; return this; }
49
+ get(key) { return this._values[key]; }
50
+ has(key) { return key in this._values; }
51
+ all() { return Object.assign({}, this._values); }
52
+ }
53
+
54
+ // ── GameEvent ──
55
+
56
+ class GameEvent {
57
+ constructor(eventType) {
58
+ this.eventType = eventType;
59
+ this.data = {};
60
+ this.timestamp = new Date();
61
+ }
62
+ set(key, value) { this.data[key] = value; return this; }
63
+ }
64
+
65
+ // ── FieldCondition ──
66
+
67
+ class FieldCondition {
68
+ constructor(fieldName, operator, expected) {
69
+ this.name = fieldName + ' ' + operator + ' ' + expected;
70
+ this.fieldName = fieldName;
71
+ this.operator = operator;
72
+ this.expected = expected;
73
+ }
74
+
75
+ evaluate(evt, _context) {
76
+ var actual = evt.data[this.fieldName];
77
+ if (actual === undefined) return false;
78
+
79
+ switch (this.operator) {
80
+ case CompareOp.Equal: return actual === this.expected;
81
+ case CompareOp.NotEqual: return actual !== this.expected;
82
+ case CompareOp.GreaterThan: return actual > this.expected;
83
+ case CompareOp.GreaterOrEqual: return actual >= this.expected;
84
+ case CompareOp.LessThan: return actual < this.expected;
85
+ case CompareOp.LessOrEqual: return actual <= this.expected;
86
+ case CompareOp.Contains: return String(actual).includes(String(this.expected));
87
+ case CompareOp.StartsWith: return String(actual).startsWith(String(this.expected));
88
+ case CompareOp.In: return Array.isArray(this.expected) && this.expected.includes(actual);
89
+ default: return false;
90
+ }
91
+ }
92
+ }
93
+
94
+ // ── Rule (Fluent Builder) ──
95
+
96
+ class Rule {
97
+ constructor(ruleId) {
98
+ this.ruleId = ruleId;
99
+ this.group = null;
100
+ this.priority = 0;
101
+ this.isEnabled = true;
102
+ this.eventTypes = [];
103
+ this.conditions = [];
104
+ this.matchMode = MatchMode.All;
105
+ this.actions = [];
106
+ }
107
+
108
+ static create(ruleId) { return new Rule(ruleId); }
109
+
110
+ inGroup(group) { this.group = group; return this; }
111
+ withPriority(p) { this.priority = p; return this; }
112
+ enable() { this.isEnabled = true; return this; }
113
+ disable() { this.isEnabled = false; return this; }
114
+
115
+ on() {
116
+ for (var i = 0; i < arguments.length; i++) {
117
+ var et = arguments[i];
118
+ if (this.eventTypes.indexOf(et) === -1) this.eventTypes.push(et);
119
+ }
120
+ return this;
121
+ }
122
+
123
+ withMatchMode(mode) { this.matchMode = mode; return this; }
124
+
125
+ when(nameOrCondition, predicate) {
126
+ if (typeof nameOrCondition === 'object' && nameOrCondition.evaluate) {
127
+ this.conditions.push(nameOrCondition);
128
+ } else {
129
+ this.conditions.push({ name: nameOrCondition, evaluate: predicate });
130
+ }
131
+ return this;
132
+ }
133
+
134
+ whenField(field, op, expected) {
135
+ this.conditions.push(new FieldCondition(field, op, expected));
136
+ return this;
137
+ }
138
+
139
+ whenEquals(field, value) { return this.whenField(field, CompareOp.Equal, value); }
140
+ whenGreaterThan(field, value) { return this.whenField(field, CompareOp.GreaterThan, value); }
141
+ whenLessThan(field, value) { return this.whenField(field, CompareOp.LessThan, value); }
142
+
143
+ then(actionType, configure, order) {
144
+ var settings = new ActionSettings();
145
+ if (typeof configure === 'function') {
146
+ configure(settings);
147
+ } else if (typeof configure === 'number') {
148
+ order = configure;
149
+ }
150
+ this.actions.push({ actionType: actionType, settings: settings, order: order || 0 });
151
+ return this;
152
+ }
153
+ }
154
+
155
+ // ── AreEngine ──
156
+
157
+ class AreEngine {
158
+ constructor() {
159
+ this._actions = new Map();
160
+ this._rules = [];
161
+ this._middlewares = [];
162
+ this._listeners = new Map();
163
+ this.onLog = null;
164
+ }
165
+
166
+ // -- Registration --
167
+
168
+ registerAction(actionTypeOrObj, handler) {
169
+ if (typeof actionTypeOrObj === 'string') {
170
+ this._actions.set(actionTypeOrObj, { actionType: actionTypeOrObj, execute: handler });
171
+ } else {
172
+ this._actions.set(actionTypeOrObj.actionType, actionTypeOrObj);
173
+ }
174
+ return this;
175
+ }
176
+
177
+ addRule(rule) { this._rules.push(rule); return this; }
178
+
179
+ addRules() {
180
+ for (var i = 0; i < arguments.length; i++) this._rules.push(arguments[i]);
181
+ return this;
182
+ }
183
+
184
+ use(order, handler) {
185
+ this._middlewares.push({ order: order, process: handler });
186
+ this._middlewares.sort(function (a, b) { return a.order - b.order; });
187
+ return this;
188
+ }
189
+
190
+ on(eventType, handler) {
191
+ if (!this._listeners.has(eventType)) this._listeners.set(eventType, []);
192
+ this._listeners.get(eventType).push(handler);
193
+ return this;
194
+ }
195
+
196
+ // -- Rule Management --
197
+
198
+ enableRule(id) { var r = this._rules.find(function (r) { return r.ruleId === id; }); if (r) r.isEnabled = true; return this; }
199
+ disableRule(id) { var r = this._rules.find(function (r) { return r.ruleId === id; }); if (r) r.isEnabled = false; return this; }
200
+ removeRule(id) { this._rules = this._rules.filter(function (r) { return r.ruleId !== id; }); return this; }
201
+
202
+ enableGroup(g) {
203
+ this._rules.forEach(function (r) { if (r.group === g) r.isEnabled = true; });
204
+ return this;
205
+ }
206
+
207
+ disableGroup(g) {
208
+ this._rules.forEach(function (r) { if (r.group === g) r.isEnabled = false; });
209
+ return this;
210
+ }
211
+
212
+ // -- Event Firing --
213
+
214
+ async fire(eventTypeOrEvent, configure, context) {
215
+ var evt;
216
+ if (typeof eventTypeOrEvent === 'string') {
217
+ evt = new GameEvent(eventTypeOrEvent);
218
+ if (typeof configure === 'function') configure(evt);
219
+ } else {
220
+ evt = eventTypeOrEvent;
221
+ if (configure instanceof AreContext) context = configure;
222
+ }
223
+
224
+ var start = Date.now();
225
+ var ctx = context || new AreContext();
226
+ ctx.currentEvent = evt;
227
+ ctx.stopPipeline = false;
228
+
229
+ var result = {
230
+ event: evt,
231
+ firedRules: [],
232
+ skippedRules: [],
233
+ pipelineStopped: false,
234
+ duration: 0
235
+ };
236
+
237
+ var self = this;
238
+
239
+ var coreProcess = async function () {
240
+ // 1) Direct listeners
241
+ var listeners = self._listeners.get(evt.eventType) || [];
242
+ for (var i = 0; i < listeners.length; i++) {
243
+ if (ctx.stopPipeline) break;
244
+ await listeners[i](evt, ctx);
245
+ }
246
+
247
+ // 2) Matching rules (by priority)
248
+ var matching = self._rules
249
+ .filter(function (r) { return r.isEnabled && r.eventTypes.indexOf(evt.eventType) !== -1; })
250
+ .sort(function (a, b) { return b.priority - a.priority; });
251
+
252
+ self._log('[ARE] Event \'' + evt.eventType + '\' → ' + matching.length + ' candidate rules');
253
+
254
+ for (var j = 0; j < matching.length; j++) {
255
+ if (ctx.stopPipeline) {
256
+ self._log('[ARE] Pipeline stopped');
257
+ break;
258
+ }
259
+ var rule = matching[j];
260
+ ctx.currentRule = rule;
261
+ ctx.skipRemainingActions = false;
262
+
263
+ var rr = await self._evaluateAndExecute(rule, evt, ctx);
264
+ if (rr.conditionsMet) result.firedRules.push(rr);
265
+ else result.skippedRules.push(rr);
266
+ }
267
+ };
268
+
269
+ // Middleware chain
270
+ var pipeline = coreProcess;
271
+ for (var i = this._middlewares.length - 1; i >= 0; i--) {
272
+ (function (mw, next) {
273
+ pipeline = function () { return mw.process(ctx, next); };
274
+ })(this._middlewares[i], pipeline);
275
+ }
276
+
277
+ await pipeline();
278
+
279
+ result.pipelineStopped = ctx.stopPipeline;
280
+ result.duration = Date.now() - start;
281
+ this._log('[ARE] Event \'' + evt.eventType + '\' completed: ' +
282
+ result.firedRules.length + ' fired, ' +
283
+ result.skippedRules.length + ' skipped, ' +
284
+ result.duration + 'ms');
285
+ return result;
286
+ }
287
+
288
+ // -- Internal --
289
+
290
+ async _evaluateAndExecute(rule, evt, ctx) {
291
+ var failedConditions = [];
292
+ var conditionsMet = this._evaluateConditions(rule, evt, ctx, failedConditions);
293
+
294
+ if (!conditionsMet) {
295
+ this._log('[ARE] Rule \'' + rule.ruleId + '\' → conditions not met [' + failedConditions.join(', ') + ']');
296
+ return { ruleId: rule.ruleId, conditionsMet: false, executedActions: [], failedConditions: failedConditions };
297
+ }
298
+
299
+ var executedActions = [];
300
+ var ordered = rule.actions.slice().sort(function (a, b) { return a.order - b.order; });
301
+
302
+ for (var i = 0; i < ordered.length; i++) {
303
+ if (ctx.skipRemainingActions || ctx.stopPipeline) break;
304
+ var binding = ordered[i];
305
+ var action = this._actions.get(binding.actionType);
306
+
307
+ if (!action) {
308
+ this._log('[ARE] Action \'' + binding.actionType + '\' not found!');
309
+ continue;
310
+ }
311
+
312
+ try {
313
+ this._log('[ARE] → Executing action \'' + binding.actionType + '\'');
314
+ await action.execute(ctx, binding.settings);
315
+ executedActions.push(binding.actionType);
316
+ } catch (err) {
317
+ this._log('[ARE] Action \'' + binding.actionType + '\' error: ' + err.message);
318
+ break;
319
+ }
320
+ }
321
+
322
+ return { ruleId: rule.ruleId, conditionsMet: true, executedActions: executedActions, failedConditions: [] };
323
+ }
324
+
325
+ _evaluateConditions(rule, evt, ctx, failed) {
326
+ if (rule.conditions.length === 0) return true;
327
+
328
+ var results = [];
329
+ for (var i = 0; i < rule.conditions.length; i++) {
330
+ var c = rule.conditions[i];
331
+ var r = c.evaluate(evt, ctx);
332
+ if (!r) failed.push(c.name);
333
+ results.push(r);
334
+ }
335
+
336
+ switch (rule.matchMode) {
337
+ case MatchMode.All: return results.every(function (r) { return r; });
338
+ case MatchMode.Any: return results.some(function (r) { return r; });
339
+ case MatchMode.None: return results.every(function (r) { return !r; });
340
+ case MatchMode.ExactlyOne: return results.filter(function (r) { return r; }).length === 1;
341
+ default: return false;
342
+ }
343
+ }
344
+
345
+ _log(msg) { if (this.onLog) this.onLog(msg); }
346
+ }
347
+
348
+ // ── Export (ESM) ──
349
+ export {
350
+ AreEngine,
351
+ AreContext,
352
+ GameEvent,
353
+ Rule,
354
+ ActionSettings,
355
+ FieldCondition,
356
+ MatchMode,
357
+ CompareOp
358
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "are-engine-core",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Action Rule Event Engine - Zero dependency, cross-platform, lightweight event-rule-action engine",
5
5
  "main": "dist/are-core.js",
6
6
  "types": "dist/are-core.d.ts",