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,136 @@
1
+ /**
2
+ * ConfigurationManager - Manages view configuration, data, and property definitions
3
+ *
4
+ * Responsibilities:
5
+ * - Process defined properties and methods from config
6
+ * - Commit constructor data
7
+ * - Update view data and variables
8
+ * - Handle userDefined properties
9
+ *
10
+ * Extracted from ViewController.js (Phase 9) to reduce complexity
11
+ * @author GitHub Copilot
12
+ * @date 2025-12-29
13
+ */
14
+
15
+ import { __defineProps, __defineMethods, hasData } from '../../helpers/utils.js';
16
+ import { FORBIDDEN_KEYS } from '../ViewConfig.js';
17
+
18
+ export class ConfigurationManager {
19
+ /**
20
+ * @param {ViewController} controller - Parent controller instance
21
+ */
22
+ constructor(controller) {
23
+ this.controller = controller;
24
+ }
25
+
26
+ /**
27
+ * Process defined properties and methods from config
28
+ * Extracts properties and methods from config.__props__ and userDefined
29
+ * Binds methods to controller context
30
+ *
31
+ * @param {Object} config - View configuration
32
+ */
33
+ processDefinedProperties(config) {
34
+ let definedProps = {};
35
+ let definedMethods = {};
36
+
37
+ // Process config.__props__
38
+ if (config.__props__ && config.__props__.length > 0) {
39
+ config.__props__.forEach(prop => {
40
+ if (typeof config[prop] === 'function') {
41
+ definedMethods[prop] = config[prop].bind(this.controller);
42
+ }
43
+ else if (typeof config[prop] !== 'undefined') {
44
+ definedProps[prop] = config[prop];
45
+ }
46
+ });
47
+ }
48
+
49
+ // Define properties and methods on controller
50
+ __defineProps(this.controller, definedProps, {
51
+ writable: true,
52
+ configurable: true,
53
+ enumerable: true,
54
+ });
55
+
56
+ __defineMethods(this.controller, definedMethods);
57
+ }
58
+
59
+ /**
60
+ * Commit constructor data to view
61
+ * Updates variable data and calls commitConstructorData callback
62
+ */
63
+ commitConstructorData() {
64
+ if (this.controller.isCommitedConstructorData) {
65
+ return;
66
+ }
67
+
68
+ if (hasData(this.controller.data) && typeof this.controller.config.updateVariableData === 'function') {
69
+ this.controller.config.updateVariableData.apply(this.controller, [
70
+ { ...this.controller.App.View.data, ...this.controller.data }
71
+ ]);
72
+ this.controller.isCommitedConstructorData = true;
73
+ }
74
+
75
+ if (typeof this.controller.config.commitConstructorData === 'function') {
76
+ this.controller.config.commitConstructorData.apply(this.controller, []);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Update view data
82
+ * Merges new data into existing data
83
+ *
84
+ * @param {Object} __data - New data to merge
85
+ * @returns {ViewController} Controller instance for chaining
86
+ */
87
+ updateData(__data = {}) {
88
+ this.controller.data = { ...this.controller.data, ...__data };
89
+ return this.controller;
90
+ }
91
+
92
+ /**
93
+ * Update variable data via config callback
94
+ *
95
+ * @param {Object} data - Variable data to update
96
+ * @returns {ViewController} Controller instance for chaining
97
+ */
98
+ updateVariableData(data = {}) {
99
+ if (typeof this.controller.config.updateVariableData === 'function') {
100
+ this.controller.config.updateVariableData.call(this.controller, data);
101
+ this.controller.isCommitedConstructorData = true;
102
+ }
103
+ return this.controller;
104
+ }
105
+
106
+ /**
107
+ * Update single variable item via config callback
108
+ *
109
+ * @param {string} key - Variable key
110
+ * @param {*} value - Variable value
111
+ * @returns {ViewController} Controller instance for chaining
112
+ */
113
+ updateVariableItem(key, value) {
114
+ if (typeof this.controller.config.updateVariableItemData === 'function') {
115
+ this.controller.config.updateVariableItemData.call(this.controller, key, value);
116
+ }
117
+ return this.controller;
118
+ }
119
+
120
+ /**
121
+ * Set scope for the view
122
+ *
123
+ * @param {Object} scope - Scope object containing name, id, index, etc.
124
+ */
125
+ setScope(scope) {
126
+ this.controller.__scope = scope;
127
+ }
128
+
129
+ get App() {
130
+ return this.controller.App;
131
+ }
132
+
133
+ set App(value) {
134
+ devLog('ConfigurationManager.App is read-only.');
135
+ }
136
+ }
@@ -0,0 +1,309 @@
1
+ /**
2
+ * EventManager - Quản lý tất cả chức năng liên quan đến events cho ViewController
3
+ *
4
+ * Trách nhiệm:
5
+ * - Khởi động/dừng event listeners
6
+ * - Parse event handlers và parameters
7
+ * - Dọn dẹp orphaned event data
8
+ * - Phân giải event handler (view methods, controller methods, window functions, etc.)
9
+ *
10
+ * Sử dụng EventDelegator để tối ưu hiệu suất:
11
+ * - Giảm số lượng listeners từ O(n) xuống O(1) mỗi event type
12
+ * - Tự động hỗ trợ dynamic content
13
+ * - Giảm memory footprint
14
+ *
15
+ * Extracted from ViewController.js to improve separation of concerns
16
+ * @module core/managers/EventManager
17
+ * @author OneLaravel Team
18
+ * @since 2025-12-29
19
+ * @updated 2025-01-06 - Integrated EventDelegator
20
+ */
21
+
22
+ import logger from '../services/LoggerService.js';
23
+ import EventDelegator from '../EventDelegator.js';
24
+
25
+ export class EventManager {
26
+ constructor(controller) {
27
+ this.controller = controller;
28
+ this.view = controller.view;
29
+ this.path = controller.path;
30
+ this.id = controller.id;
31
+
32
+ /**
33
+ * Track delegated event unsubscribers
34
+ * @type {Map<string, Function>}
35
+ */
36
+ this.delegatedUnsubscribers = new Map();
37
+ }
38
+
39
+ updateController(newController) {
40
+ this.controller = newController;
41
+ this.view = newController.view;
42
+ this.path = newController.path;
43
+ this.id = newController.id;
44
+ }
45
+
46
+ /**
47
+ * Khởi động event listeners cho tất cả registered events
48
+ * Sử dụng event delegation thay vì attach listener riêng cho từng element
49
+ *
50
+ * Cải tiến hiệu suất:
51
+ * - Trước: O(n) listeners (mỗi element một listener)
52
+ * - Sau: O(1) listeners (một listener cho mỗi event type)
53
+ * - Tự động hỗ trợ dynamic content (ReactiveComponent updates)
54
+ * - Giảm memory usage đáng kể
55
+ */
56
+ startEventListener() {
57
+ const needDeleted = [];
58
+
59
+ Object.entries(this.controller.events).forEach(([eventType, eventMap]) => {
60
+ Object.entries(eventMap).forEach(([eventID, handlers]) => {
61
+ const selector = `[data-${eventType}-id="${eventID}"]`;
62
+ const elements = document.querySelectorAll(selector);
63
+ const parsedHandlers = this.parseEventHandlerFunctions(handlers);
64
+
65
+ if (parsedHandlers.length !== 0 && elements.length > 0) {
66
+ // Tạo unique key cho delegated handler
67
+ const delegateKey = `${eventType}:${eventID}:${this.id}`;
68
+
69
+ // Unsubscribe nếu đã tồn tại (prevent duplicates)
70
+ if (this.delegatedUnsubscribers.has(delegateKey)) {
71
+ this.delegatedUnsubscribers.get(delegateKey)();
72
+ }
73
+
74
+ // Sử dụng EventDelegator để attach delegated listener
75
+ const unsubscribe = EventDelegator.on(eventType, selector, (event) => {
76
+ let returnValue = null;
77
+ for (let i = 0; i < parsedHandlers.length; i++) {
78
+ const handlerFn = parsedHandlers[i];
79
+ try {
80
+ returnValue = handlerFn(event);
81
+ } catch (error) {
82
+ console.error('[EventManager] Handler execution error:', error, {
83
+ eventType,
84
+ selector,
85
+ handlerIndex: i,
86
+ event: event.type
87
+ });
88
+ throw error; // Re-throw để EventDelegator catch
89
+ }
90
+ if (returnValue === false || event.defaultPrevented) {
91
+ event.preventDefault();
92
+ event.stopPropagation();
93
+ break;
94
+ }
95
+ }
96
+ if (returnValue === false || returnValue === true) {
97
+ return returnValue;
98
+ }
99
+ });
100
+
101
+ // Lưu unsubscriber để cleanup sau này
102
+ this.delegatedUnsubscribers.set(delegateKey, unsubscribe);
103
+ } else {
104
+ needDeleted.push({ eventType, eventID });
105
+ }
106
+ });
107
+ });
108
+ needDeleted.forEach(({ eventType, eventID }) => {
109
+ delete this.controller.events[eventType][eventID];
110
+ // If no more event handlers for this eventType, remove the entire eventType
111
+ if (Object.keys(this.controller.events[eventType]).length === 0) {
112
+ delete this.controller.events[eventType];
113
+ }
114
+ });
115
+ this.controller.eventListenerStatus = true;
116
+ }
117
+
118
+ /**
119
+ * Dừng tất cả event listeners
120
+ * Gỡ bỏ tất cả delegated event listeners và clear tracking structures
121
+ *
122
+ * Với event delegation:
123
+ * - Không cần loop qua từng element
124
+ * - Chỉ cần unsubscribe delegated handlers
125
+ * - Nhanh hơn và đơn giản hơn
126
+ */
127
+ stopEventListener() {
128
+ // Unsubscribe tất cả delegated handlers
129
+ this.delegatedUnsubscribers.forEach(unsubscribe => {
130
+ unsubscribe();
131
+ });
132
+ this.delegatedUnsubscribers.clear();
133
+
134
+ // Keep backward compatibility: clean up old individual listeners (nếu có)
135
+ if (this.controller.eventListeners && this.controller.eventListeners.length > 0) {
136
+ this.controller.eventListeners.forEach(({ element, eventType, handler }) => {
137
+ element.removeEventListener(eventType, handler);
138
+ });
139
+ this.controller.eventListeners = [];
140
+ }
141
+
142
+ this.controller.eventListenerStatus = false;
143
+ }
144
+
145
+ /**
146
+ * Clear all event data when elements are not found
147
+ * This helps prevent memory leaks from orphaned event handlers
148
+ */
149
+ clearOrphanedEventData() {
150
+ const needDeleted = [];
151
+ Object.entries(this.controller.events).forEach(([eventType, eventMap]) => {
152
+ Object.entries(eventMap).forEach(([eventID, handlers]) => {
153
+ const selector = `[data-${eventType}-id="${eventID}"]`;
154
+ const elements = document.querySelectorAll(selector);
155
+ if (elements.length === 0) {
156
+ needDeleted.push({ eventType, eventID });
157
+ }
158
+ });
159
+ });
160
+
161
+ needDeleted.forEach(({ eventType, eventID }) => {
162
+ delete this.controller.events[eventType][eventID];
163
+ // If no more event handlers for this eventType, remove the entire eventType
164
+ if (Object.keys(this.controller.events[eventType]).length === 0) {
165
+ delete this.controller.events[eventType];
166
+ }
167
+ });
168
+
169
+ if (needDeleted.length > 0) {
170
+ logger.log(`🗑️ ViewEngine.clearOrphanedEventData: Cleaned up ${needDeleted.length} orphaned event handlers`);
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Parse event handler functions from handler objects
176
+ * @param {Array} handlers - Array of handler objects or functions
177
+ * @returns {Array} Array of parsed handler functions
178
+ */
179
+ parseEventHandlerFunctions(handlers) {
180
+ let parsedHandlers = [];
181
+ handlers.forEach(handlerObj => {
182
+ const handlerName = typeof handlerObj == "function" ? handlerObj : handlerObj.handler;
183
+ const params = typeof handlerObj == "function" ? [] : handlerObj.params || [];
184
+ const func = this.parseHandlerFunction(handlerName);
185
+ const fn = (event) => func(...this.parseEventHandlerParams(event, params));
186
+ parsedHandlers.push(fn);
187
+ });
188
+ return parsedHandlers;
189
+ }
190
+
191
+ /**
192
+ * Parse handler function
193
+ * @param {string|Function} funcName - Function name or function
194
+ * @returns {Function} Parsed function bound to correct context
195
+ */
196
+ parseHandlerFunction(funcName) {
197
+ if (!(typeof funcName === 'string' || typeof funcName === 'function')) {
198
+ return null;
199
+ }
200
+
201
+ // Handle function directly passed
202
+ if (typeof funcName === 'function') {
203
+ // Always bind to view to maintain context
204
+ return funcName.bind(this.view);
205
+ }
206
+
207
+ // Handle function name as string - check in order: view, controller, setters, data, window
208
+
209
+ // Check in view
210
+ if (typeof this.view[funcName] === 'function') {
211
+ return this.view[funcName].bind(this.view);
212
+ }
213
+
214
+ // Check in controller
215
+ if (typeof this.controller[funcName] === 'function') {
216
+ return this.controller[funcName].bind(this.controller);
217
+ }
218
+
219
+ // Check in states setters
220
+ if (typeof this.controller.states.__.setters[funcName] === 'function') {
221
+ return this.controller.states.__.setters[funcName].bind(this.view);
222
+ }
223
+
224
+ // Check in data
225
+ if (typeof this.controller.data[funcName] === 'function') {
226
+ return this.controller.data[funcName].bind(this.controller.data);
227
+ }
228
+
229
+ // Check in window (global functions)
230
+ if (window && typeof window[funcName] === 'function') {
231
+ return window[funcName].bind(window);
232
+ }
233
+
234
+ // Function not found
235
+ return (event) => logger.warn(`⚠️ Event handler ${funcName} is not defined`, event);
236
+ }
237
+
238
+ /**
239
+ * Parse event handler parameters
240
+ * @param {Event} event - DOM event object
241
+ * @param {Array} params - Array of parameter definitions
242
+ * @returns {Array} Parsed parameters
243
+ */
244
+ parseEventHandlerParams(event, params = []) {
245
+ return params.map(param => {
246
+ if (param === '@EVENT') {
247
+ return event;
248
+ }
249
+ else if (typeof param === 'object' && param !== null) {
250
+ if (typeof param.handler === 'string' && (Array.isArray(param.params) || (typeof param.params == 'object' && param.params !== null && param.params.constructor === Array))) {
251
+ const func = this.parseHandlerFunction(param.handler);
252
+ return func(...this.parseEventHandlerParams(event, param.params));
253
+ }
254
+ return this.parseEventHandlerObject(event, param);
255
+ }
256
+ else if (Array.isArray(param) || (typeof param === 'object' && param !== null && param.constructor === Array)) {
257
+ return this.parseEventHandlerParams(event, param);
258
+ }
259
+ else if (typeof param === 'function') {
260
+ // Bind function to view context before calling
261
+ const boundFunc = param.bind(this.view);
262
+ return boundFunc(event);
263
+ }
264
+ return param;
265
+ });
266
+ }
267
+
268
+ /**
269
+ * Parse event handler object parameters
270
+ * @param {Event} event - DOM event object
271
+ * @param {Object} object - Object with parameter definitions
272
+ * @returns {Object} Parsed object with evaluated parameters
273
+ */
274
+ parseEventHandlerObject(event, object = {}) {
275
+ let parsedObject = {};
276
+ Object.entries(object).forEach(([key, value]) => {
277
+ if (value === '@EVENT') {
278
+ parsedObject[key] = event;
279
+ }
280
+ else if (typeof value === 'object' && value !== null) {
281
+ if (typeof value.handler === 'string' && (Array.isArray(value.params) || (typeof value.params == 'object' && value.params !== null && value.params.constructor === Array))) {
282
+ const func = this.parseHandlerFunction(value.handler);
283
+ parsedObject[key] = func(...this.parseEventHandlerParams(event, value.params));
284
+ }
285
+ else {
286
+ parsedObject[key] = this.parseEventHandlerObject(event, value);
287
+ }
288
+ }
289
+ else if (Array.isArray(value) || (typeof value === 'object' && value !== null && value.constructor === Array)) {
290
+ parsedObject[key] = this.parseEventHandlerParams(event, value);
291
+ }
292
+ else if (typeof value === 'function') {
293
+ parsedObject[key] = value(event);
294
+ }
295
+ else {
296
+ parsedObject[key] = value;
297
+ }
298
+ });
299
+ return parsedObject;
300
+ }
301
+
302
+ get App() {
303
+ return this.controller.App;
304
+ }
305
+
306
+ set App(value) {
307
+ devLog('EventManager.App is read-only.');
308
+ }
309
+ }