onelaraveljs 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.
Files changed (67) hide show
  1. package/README.md +87 -0
  2. package/docs/integration_analysis.md +116 -0
  3. package/docs/onejs_analysis.md +108 -0
  4. package/docs/optimization_implementation_group2.md +458 -0
  5. package/docs/optimization_plan.md +130 -0
  6. package/index.js +16 -0
  7. package/package.json +13 -0
  8. package/src/app.js +61 -0
  9. package/src/core/API.js +72 -0
  10. package/src/core/ChildrenRegistry.js +410 -0
  11. package/src/core/DOMBatcher.js +207 -0
  12. package/src/core/ErrorBoundary.js +226 -0
  13. package/src/core/EventDelegator.js +416 -0
  14. package/src/core/Helper.js +817 -0
  15. package/src/core/LoopContext.js +97 -0
  16. package/src/core/OneDOM.js +246 -0
  17. package/src/core/OneMarkup.js +444 -0
  18. package/src/core/Router.js +996 -0
  19. package/src/core/SEOConfig.js +321 -0
  20. package/src/core/SectionEngine.js +75 -0
  21. package/src/core/TemplateEngine.js +83 -0
  22. package/src/core/View.js +273 -0
  23. package/src/core/ViewConfig.js +229 -0
  24. package/src/core/ViewController.js +1410 -0
  25. package/src/core/ViewControllerOptimized.js +164 -0
  26. package/src/core/ViewIdentifier.js +361 -0
  27. package/src/core/ViewLoader.js +272 -0
  28. package/src/core/ViewManager.js +1962 -0
  29. package/src/core/ViewState.js +761 -0
  30. package/src/core/ViewSystem.js +301 -0
  31. package/src/core/ViewTemplate.js +4 -0
  32. package/src/core/helpers/BindingHelper.js +239 -0
  33. package/src/core/helpers/ConfigHelper.js +37 -0
  34. package/src/core/helpers/EventHelper.js +172 -0
  35. package/src/core/helpers/LifecycleHelper.js +17 -0
  36. package/src/core/helpers/ReactiveHelper.js +169 -0
  37. package/src/core/helpers/RenderHelper.js +15 -0
  38. package/src/core/helpers/ResourceHelper.js +89 -0
  39. package/src/core/helpers/TemplateHelper.js +11 -0
  40. package/src/core/managers/BindingManager.js +671 -0
  41. package/src/core/managers/ConfigurationManager.js +136 -0
  42. package/src/core/managers/EventManager.js +309 -0
  43. package/src/core/managers/LifecycleManager.js +356 -0
  44. package/src/core/managers/ReactiveManager.js +334 -0
  45. package/src/core/managers/RenderEngine.js +292 -0
  46. package/src/core/managers/ResourceManager.js +441 -0
  47. package/src/core/managers/ViewHierarchyManager.js +258 -0
  48. package/src/core/managers/ViewTemplateManager.js +127 -0
  49. package/src/core/reactive/ReactiveComponent.js +592 -0
  50. package/src/core/services/EventService.js +418 -0
  51. package/src/core/services/HttpService.js +106 -0
  52. package/src/core/services/LoggerService.js +57 -0
  53. package/src/core/services/StateService.js +512 -0
  54. package/src/core/services/StorageService.js +856 -0
  55. package/src/core/services/StoreService.js +258 -0
  56. package/src/core/services/TemplateDetectorService.js +361 -0
  57. package/src/core/services/Test.js +18 -0
  58. package/src/helpers/devWarnings.js +205 -0
  59. package/src/helpers/performance.js +226 -0
  60. package/src/helpers/utils.js +287 -0
  61. package/src/init.js +343 -0
  62. package/src/plugins/auto-plugin.js +34 -0
  63. package/src/services/Test.js +18 -0
  64. package/src/types/index.js +193 -0
  65. package/src/utils/date-helper.js +51 -0
  66. package/src/utils/helpers.js +39 -0
  67. package/src/utils/validation.js +32 -0
@@ -0,0 +1,512 @@
1
+ import logger from "./LoggerService";
2
+
3
+ /**
4
+ * StateService - Service quản lý state toàn cục với khả năng theo dõi thay đổi
5
+ * Hỗ trợ chia sẻ dữ liệu giữa các view và component
6
+ */
7
+ export class StateService {
8
+ static instance = null;
9
+ static privateProperties = [
10
+ '__state', '__listeners', '__middlewares', '__isUpdating', 'instance',
11
+ 'get', 'set', 'delete', 'has', 'clear', 'subscribe', 'unsubscribe',
12
+ 'addMiddleware', 'removeMiddleware'
13
+ ];
14
+ dynamicProperties = [];
15
+
16
+ constructor(initialState = {}) {
17
+ this.__state = { ...initialState };
18
+ this.__listeners = new Map(); // key -> Set of callbacks
19
+ this.__middlewares = []; // Middleware functions
20
+ this.__isUpdating = false;
21
+
22
+ logger.log('🏪 StateService: Initialized with state:', this.__state);
23
+ this.__init();
24
+ }
25
+
26
+ // ==========================================
27
+ // PUBLIC STATIC METHODS
28
+ // ==========================================
29
+
30
+ /**
31
+ * Get the singleton instance of StateService
32
+ * @param {object} initialState - Initial state (only used on first call)
33
+ * @returns {StateService} - The singleton instance
34
+ */
35
+ static getInstance(initialState = {}) {
36
+ if (!StateService.instance) {
37
+ StateService.instance = new StateService(initialState);
38
+ }
39
+ return StateService.instance;
40
+ }
41
+
42
+ __init() {
43
+ Object.keys(this.__state).forEach(key => {
44
+ this.__addDynamicProperty(key);
45
+ });
46
+
47
+ }
48
+
49
+ // ==========================================
50
+ // PUBLIC INSTANCE METHODS - Core Operations
51
+ // ==========================================
52
+
53
+ /**
54
+ * Get state value by key
55
+ * @param {string} key - The key to get
56
+ * @param {any} defaultValue - Default value if key not found
57
+ * @returns {any} - The state value
58
+ */
59
+ get(key, defaultValue = undefined) {
60
+ if (typeof key !== 'string') {
61
+ throw new Error('State key must be a string');
62
+ }
63
+
64
+ const value = this.__getNestedValue(this.__state, key);
65
+ logger.log(`📖 StateService: Get ${key}:`, value);
66
+ return value !== undefined ? value : defaultValue;
67
+ }
68
+
69
+ /**
70
+ * Set state value by key
71
+ * @param {string|object} key - The key to set, or object with multiple key-value pairs
72
+ * @param {any} value - The value to set (ignored if key is object)
73
+ * @param {boolean} silent - If true, don't trigger listeners
74
+ * @returns {StateService} - Chainable
75
+ */
76
+ set(key, value = null, silent = false) {
77
+ // If key is object, set multiple values
78
+ if (typeof key === 'object' && key !== null) {
79
+ for (const k in key) {
80
+ if (Object.prototype.hasOwnProperty.call(key, k)) {
81
+ this.set(k, key[k], silent);
82
+ }
83
+ }
84
+ return this;
85
+ }
86
+
87
+ if (typeof key !== 'string') {
88
+ throw new Error('State key must be a string');
89
+ }
90
+
91
+ const oldValue = this.__getNestedValue(this.__state, key);
92
+
93
+ // Apply middlewares
94
+ const processedValue = this.__applyMiddlewares(key, oldValue, value, 'set');
95
+
96
+ // Set the value
97
+ this.__setNestedValue(this.__state, key, processedValue);
98
+
99
+ logger.log(`💾 StateService: Set ${key}:`, processedValue);
100
+
101
+ if (!silent) {
102
+ this.__notifyListeners(key, processedValue, oldValue);
103
+ }
104
+ if (!this.dynamicProperties.includes(key)) {
105
+ this.__addDynamicProperty(key);
106
+ }
107
+
108
+ return this;
109
+ }
110
+
111
+ /**
112
+ * Delete state value by key
113
+ * @param {string} key - The key to delete
114
+ * @param {boolean} silent - If true, don't trigger listeners
115
+ * @returns {boolean} - True if key was deleted
116
+ */
117
+ delete(key, silent = false) {
118
+ if (typeof key !== 'string') {
119
+ throw new Error('State key must be a string');
120
+ }
121
+
122
+ const oldValue = this.__getNestedValue(this.__state, key);
123
+ const deleted = this.__deleteNestedValue(this.__state, key);
124
+
125
+ if (deleted) {
126
+ logger.log(`🗑️ StateService: Delete ${key}`);
127
+
128
+ if (!silent) {
129
+ this.__notifyListeners(key, undefined, oldValue);
130
+ }
131
+ }
132
+ if (this.dynamicProperties.includes(key)) {
133
+ this.__removeDynamicProperty(key);
134
+ }
135
+ return deleted;
136
+ }
137
+
138
+ /**
139
+ * Check if state key exists
140
+ * @param {string} key - The key to check
141
+ * @returns {boolean} - True if key exists
142
+ */
143
+ has(key) {
144
+ if (typeof key !== 'string') {
145
+ throw new Error('State key must be a string');
146
+ }
147
+
148
+ const value = this.__getNestedValue(this.__state, key);
149
+ return value !== undefined;
150
+ }
151
+
152
+ /**
153
+ * Get all state
154
+ * @returns {object} - Copy of all state
155
+ */
156
+ getAll() {
157
+ return JSON.parse(JSON.stringify(this.__state));
158
+ }
159
+
160
+ /**
161
+ * Clear all state
162
+ * @param {boolean} silent - If true, don't trigger listeners
163
+ * @returns {StateService} - Chainable
164
+ */
165
+ clear(silent = false) {
166
+ const oldState = { ...this.__state };
167
+ this.__state = {};
168
+
169
+ logger.log('🧹 StateService: Cleared all state');
170
+
171
+ if (!silent) {
172
+ // Notify all listeners that their keys are deleted
173
+ for (const key of this.__listeners.keys()) {
174
+ const oldValue = this.__getNestedValue(oldState, key);
175
+ if (oldValue !== undefined) {
176
+ this.__notifyListeners(key, undefined, oldValue);
177
+ }
178
+ }
179
+ }
180
+
181
+ return this;
182
+ }
183
+
184
+ // ==========================================
185
+ // PUBLIC INSTANCE METHODS - Event System
186
+ // ==========================================
187
+
188
+ /**
189
+ * Subscribe to state changes
190
+ * @param {string} key - The key to watch
191
+ * @param {function} callback - Callback function
192
+ * @param {object} options - Options for subscription
193
+ * @returns {function} - Unsubscribe function
194
+ */
195
+ subscribe(key, callback, options = {}) {
196
+ if (typeof key !== 'string') {
197
+ throw new Error('State key must be a string');
198
+ }
199
+
200
+ if (typeof callback !== 'function') {
201
+ throw new Error('Callback must be a function');
202
+ }
203
+
204
+ const {
205
+ immediate = false, // Call immediately with current value
206
+ once = false // Call only once
207
+ } = options;
208
+
209
+ if (!this.__listeners.has(key)) {
210
+ this.__listeners.set(key, new Set());
211
+ }
212
+
213
+ const listener = {
214
+ callback,
215
+ once,
216
+ id: Date.now() + Math.random()
217
+ };
218
+
219
+ this.__listeners.get(key).add(listener);
220
+
221
+ logger.log(`🎧 StateService: Subscribed to ${key}`);
222
+
223
+ // Call immediately if requested
224
+ if (immediate) {
225
+ const currentValue = this.get(key);
226
+ if (currentValue !== undefined) {
227
+ callback(currentValue, undefined, key);
228
+ }
229
+ }
230
+
231
+ // Return unsubscribe function
232
+ return () => this.unsubscribe(key, listener.id);
233
+ }
234
+
235
+ /**
236
+ * Unsubscribe from state changes
237
+ * @param {string} key - The key to stop watching
238
+ * @param {string|function} listenerId - Listener ID or callback function
239
+ * @returns {boolean} - True if unsubscribed
240
+ */
241
+ unsubscribe(key, listenerId) {
242
+ if (!this.__listeners.has(key)) {
243
+ return false;
244
+ }
245
+
246
+ const listeners = this.__listeners.get(key);
247
+
248
+ if (typeof listenerId === 'string') {
249
+ // Remove by ID
250
+ for (const listener of listeners) {
251
+ if (listener.id === listenerId) {
252
+ listeners.delete(listener);
253
+ logger.log(`🎧 StateService: Unsubscribed from ${key}`);
254
+ return true;
255
+ }
256
+ }
257
+ } else if (typeof listenerId === 'function') {
258
+ // Remove by callback
259
+ for (const listener of listeners) {
260
+ if (listener.callback === listenerId) {
261
+ listeners.delete(listener);
262
+ logger.log(`🎧 StateService: Unsubscribed from ${key}`);
263
+ return true;
264
+ }
265
+ }
266
+ }
267
+
268
+ return false;
269
+ }
270
+
271
+ /**
272
+ * Unsubscribe all listeners for a key
273
+ * @param {string} key - The key to clear listeners for
274
+ */
275
+ unsubscribeAll(key) {
276
+ if (key) {
277
+ this.__listeners.delete(key);
278
+ logger.log(`🎧 StateService: Unsubscribed all from ${key}`);
279
+ } else {
280
+ this.__listeners.clear();
281
+ logger.log('🎧 StateService: Unsubscribed all listeners');
282
+ }
283
+ }
284
+
285
+
286
+ // ==========================================
287
+ // PUBLIC INSTANCE METHODS - Middleware
288
+ // ==========================================
289
+
290
+ /**
291
+ * Add middleware function
292
+ * @param {function} middleware - Middleware function
293
+ * @returns {StateService} - Chainable
294
+ */
295
+ addMiddleware(middleware) {
296
+ if (typeof middleware !== 'function') {
297
+ throw new Error('Middleware must be a function');
298
+ }
299
+
300
+ this.__middlewares.push(middleware);
301
+ logger.log('🔧 StateService: Added middleware');
302
+
303
+ return this;
304
+ }
305
+
306
+ /**
307
+ * Remove middleware function
308
+ * @param {function} middleware - Middleware function to remove
309
+ * @returns {boolean} - True if removed
310
+ */
311
+ removeMiddleware(middleware) {
312
+ const index = this.__middlewares.indexOf(middleware);
313
+ if (index > -1) {
314
+ this.__middlewares.splice(index, 1);
315
+ logger.log('🔧 StateService: Removed middleware');
316
+ return true;
317
+ }
318
+ return false;
319
+ }
320
+
321
+
322
+ // ==========================================
323
+ // PUBLIC INSTANCE METHODS - Utilities
324
+ // ==========================================
325
+
326
+ /**
327
+ * Debug state service
328
+ */
329
+ debug() {
330
+ // Get listeners info manually for debug
331
+ const listeners = {};
332
+ for (const [key, listenerSet] of this.__listeners) {
333
+ listeners[key] = listenerSet.size;
334
+ }
335
+
336
+ logger.log('🔍 StateService Debug:', {
337
+ state: this.__state,
338
+ listeners: listeners,
339
+ middlewares: this.__middlewares.length,
340
+ isUpdating: this.__isUpdating
341
+ });
342
+ }
343
+
344
+ /**
345
+ * Export state to JSON
346
+ * @returns {string} - JSON string
347
+ */
348
+ export() {
349
+ return JSON.stringify(this.__state, null, 2);
350
+ }
351
+
352
+ /**
353
+ * Import state from JSON
354
+ * @param {string} jsonString - JSON string
355
+ * @returns {boolean} - True if successful
356
+ */
357
+ import(jsonString) {
358
+ try {
359
+ const importedState = JSON.parse(jsonString);
360
+ this.set(importedState); // Use set method for multiple keys
361
+ logger.log('📥 StateService: Imported state successfully');
362
+ return true;
363
+ } catch (error) {
364
+ logger.error('❌ StateService: Failed to import state:', error);
365
+ return false;
366
+ }
367
+ }
368
+
369
+ // ==========================================
370
+ // PRIVATE METHODS
371
+ // ==========================================
372
+
373
+ /**
374
+ * Get nested value from object using dot notation
375
+ * @param {object} obj - The object
376
+ * @param {string} path - The path (e.g., 'user.profile.name')
377
+ * @returns {any} - The value
378
+ */
379
+ __getNestedValue(obj, path) {
380
+ return path.split('.').reduce((current, key) => {
381
+ return current && current[key] !== undefined ? current[key] : undefined;
382
+ }, obj);
383
+ }
384
+
385
+ /**
386
+ * Set nested value in object using dot notation
387
+ * @param {object} obj - The object
388
+ * @param {string} path - The path (e.g., 'user.profile.name')
389
+ * @param {any} value - The value to set
390
+ */
391
+ __setNestedValue(obj, path, value) {
392
+ const keys = path.split('.');
393
+ const lastKey = keys.pop();
394
+ const target = keys.reduce((current, key) => {
395
+ if (!current[key] || typeof current[key] !== 'object') {
396
+ current[key] = {};
397
+ }
398
+ return current[key];
399
+ }, obj);
400
+ target[lastKey] = value;
401
+ }
402
+
403
+ /**
404
+ * Delete nested value from object using dot notation
405
+ * @param {object} obj - The object
406
+ * @param {string} path - The path (e.g., 'user.profile.name')
407
+ * @returns {boolean} - True if deleted
408
+ */
409
+ __deleteNestedValue(obj, path) {
410
+ const keys = path.split('.');
411
+ const lastKey = keys.pop();
412
+ const target = keys.reduce((current, key) => {
413
+ return current && current[key] ? current[key] : undefined;
414
+ }, obj);
415
+
416
+ if (target && target.hasOwnProperty(lastKey)) {
417
+ delete target[lastKey];
418
+ return true;
419
+ }
420
+ return false;
421
+ }
422
+
423
+ /**
424
+ * Check if value is an object
425
+ * @param {any} value - The value to check
426
+ * @returns {boolean} - True if object
427
+ */
428
+ __isObject(value) {
429
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
430
+ }
431
+
432
+ /**
433
+ * Apply middlewares to value change
434
+ * @param {string} key - The key
435
+ * @param {any} oldValue - Old value
436
+ * @param {any} newValue - New value
437
+ * @param {string} action - Action type
438
+ * @returns {any} - Processed value
439
+ */
440
+ __applyMiddlewares(key, oldValue, newValue, action) {
441
+ let processedValue = newValue;
442
+
443
+ for (const middleware of this.__middlewares) {
444
+ try {
445
+ processedValue = middleware(key, oldValue, processedValue, action);
446
+ } catch (error) {
447
+ logger.error(`❌ StateService: Middleware error for ${key}:`, error);
448
+ }
449
+ }
450
+
451
+ return processedValue;
452
+ }
453
+
454
+ /**
455
+ * Notify all listeners for a key
456
+ * @param {string} key - The key
457
+ * @param {any} newValue - New value
458
+ * @param {any} oldValue - Old value
459
+ */
460
+ __notifyListeners(key, newValue, oldValue) {
461
+ if (this.__isUpdating) return;
462
+
463
+ const listeners = this.__listeners.get(key);
464
+ if (!listeners) return;
465
+
466
+ const listenersToRemove = [];
467
+
468
+ for (const listener of listeners) {
469
+ try {
470
+ listener.callback(newValue, oldValue, key);
471
+
472
+ if (listener.once) {
473
+ listenersToRemove.push(listener);
474
+ }
475
+ } catch (error) {
476
+ logger.error(`❌ StateService: Listener error for ${key}:`, error);
477
+ }
478
+ }
479
+
480
+ // Remove once listeners
481
+ for (const listener of listenersToRemove) {
482
+ listeners.delete(listener);
483
+ }
484
+
485
+ // Remove empty listener sets
486
+ if (listeners.size === 0) {
487
+ this.__listeners.delete(key);
488
+ }
489
+ }
490
+
491
+ __addDynamicProperty(key) {
492
+ if (StateService.privateProperties.includes(key) || this.dynamicProperties.includes(key)) {
493
+ return;
494
+ }
495
+ this.dynamicProperties.push(key);
496
+ Object.defineProperty(this, key, {
497
+ get: () => this.get(key),
498
+ set: (value) => this.set(key, value)
499
+ });
500
+ }
501
+
502
+ __removeDynamicProperty(key) {
503
+ if (this.dynamicProperties.includes(key)) {
504
+ delete this[key];
505
+ this.dynamicProperties = this.dynamicProperties.filter(k => k !== key);
506
+ }
507
+ }
508
+ }
509
+
510
+ // Export singleton instance
511
+ const state = StateService.getInstance();
512
+ export default state;