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