native-document 1.0.94 → 1.0.98

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 (52) hide show
  1. package/{src/devtools/hrm → devtools}/ComponentRegistry.js +2 -2
  2. package/devtools/index.js +8 -0
  3. package/{src/devtools/plugin.js → devtools/plugin/dev-tools-plugin.js} +2 -2
  4. package/{src/devtools/hrm/nd-vite-hot-reload.js → devtools/transformers/nd-vite-devtools.js} +16 -6
  5. package/devtools/transformers/src/transformComponentForHrm.js +74 -0
  6. package/devtools/transformers/src/transformJsFile.js +9 -0
  7. package/devtools/transformers/src/utils.js +79 -0
  8. package/devtools/widget/Widget.js +48 -0
  9. package/devtools/widget/widget.css +81 -0
  10. package/devtools/widget.js +23 -0
  11. package/dist/native-document.components.min.js +2441 -1191
  12. package/dist/native-document.dev.js +2710 -1359
  13. package/dist/native-document.dev.js.map +1 -1
  14. package/dist/native-document.devtools.min.js +1 -1
  15. package/dist/native-document.min.js +1 -1
  16. package/docs/cache.md +1 -1
  17. package/docs/core-concepts.md +1 -1
  18. package/docs/native-document-element.md +51 -15
  19. package/docs/observables.md +310 -306
  20. package/docs/state-management.md +198 -193
  21. package/index.def.js +762 -26
  22. package/package.json +1 -1
  23. package/readme.md +1 -1
  24. package/src/core/data/ObservableChecker.js +2 -0
  25. package/src/core/data/ObservableItem.js +97 -0
  26. package/src/core/data/ObservableObject.js +182 -0
  27. package/src/core/data/Store.js +364 -34
  28. package/src/core/data/observable-helpers/object.js +2 -166
  29. package/src/core/elements/anchor.js +28 -20
  30. package/src/core/elements/control/for-each.js +1 -1
  31. package/src/core/utils/formatters.js +91 -0
  32. package/src/core/utils/localstorage.js +57 -0
  33. package/src/core/utils/validator.js +0 -2
  34. package/src/core/wrappers/DocumentObserver.js +102 -31
  35. package/src/core/wrappers/ElementCreator.js +5 -0
  36. package/src/core/wrappers/NDElement.js +32 -1
  37. package/src/core/wrappers/prototypes/nd-element.transition.extensions.js +83 -0
  38. package/src/devtools.js +9 -0
  39. package/src/fetch/NativeFetch.js +5 -2
  40. package/types/elements.d.ts +580 -2
  41. package/types/nd-element.d.ts +6 -0
  42. package/types/observable.d.ts +71 -15
  43. package/types/plugins-manager.d.ts +1 -1
  44. package/types/store.d.ts +33 -6
  45. package/hrm.js +0 -7
  46. package/src/devtools/app/App.js +0 -66
  47. package/src/devtools/app/app.css +0 -0
  48. package/src/devtools/hrm/transformComponent.js +0 -129
  49. package/src/devtools/index.js +0 -18
  50. package/src/devtools/widget/DevToolsWidget.js +0 -26
  51. /package/{src/devtools/hrm → devtools/transformers/templates}/hrm.hook.template.js +0 -0
  52. /package/{src/devtools/hrm → devtools/transformers/templates}/hrm.orbservable.hook.template.js +0 -0
@@ -1,10 +1,10 @@
1
1
  var NativeDocument = (function (exports) {
2
2
  'use strict';
3
3
 
4
- let DebugManager = {};
4
+ let DebugManager$1 = {};
5
5
 
6
6
  {
7
- DebugManager = {
7
+ DebugManager$1 = {
8
8
  enabled: false,
9
9
 
10
10
  enable() {
@@ -35,60 +35,7 @@ var NativeDocument = (function (exports) {
35
35
  };
36
36
 
37
37
  }
38
- var DebugManager$1 = DebugManager;
39
-
40
- const MemoryManager = (function() {
41
-
42
- let $nextObserverId = 0;
43
- const $observables = new Map();
44
-
45
- return {
46
- /**
47
- * Register an observable and return an id.
48
- *
49
- * @param {ObservableItem} observable
50
- * @param {Function} getListeners
51
- * @returns {number}
52
- */
53
- register(observable) {
54
- const id = ++$nextObserverId;
55
- $observables.set(id, new WeakRef(observable));
56
- return id;
57
- },
58
- unregister(id) {
59
- $observables.delete(id);
60
- },
61
- getObservableById(id) {
62
- return $observables.get(id)?.deref();
63
- },
64
- cleanup() {
65
- for (const [_, weakObservableRef] of $observables) {
66
- const observable = weakObservableRef.deref();
67
- if (observable) {
68
- observable.cleanup();
69
- }
70
- }
71
- $observables.clear();
72
- },
73
- /**
74
- * Clean observables that are not referenced anymore.
75
- * @param {number} threshold
76
- */
77
- cleanObservables(threshold) {
78
- if($observables.size < threshold) return;
79
- let cleanedCount = 0;
80
- for (const [id, weakObservableRef] of $observables) {
81
- if (!weakObservableRef.deref()) {
82
- $observables.delete(id);
83
- cleanedCount++;
84
- }
85
- }
86
- if (cleanedCount > 0) {
87
- DebugManager$1.log('Memory Auto Clean', `🧹 Cleaned ${cleanedCount} orphaned observables`);
88
- }
89
- }
90
- };
91
- }());
38
+ var DebugManager = DebugManager$1;
92
39
 
93
40
  class NativeDocumentError extends Error {
94
41
  constructor(message, context = {}) {
@@ -191,10 +138,185 @@ var NativeDocument = (function (exports) {
191
138
  return this.observable.cleanup();
192
139
  };
193
140
 
194
- let PluginsManager = null;
141
+ const DocumentObserver = {
142
+ mounted: new WeakMap(),
143
+ beforeUnmount: new WeakMap(),
144
+ mountedSupposedSize: 0,
145
+ unmounted: new WeakMap(),
146
+ unmountedSupposedSize: 0,
147
+ observer: null,
148
+
149
+ executeMountedCallback(node) {
150
+ const data = DocumentObserver.mounted.get(node);
151
+ if(!data) {
152
+ return;
153
+ }
154
+ data.inDom = true;
155
+ if(!data.mounted) {
156
+ return;
157
+ }
158
+ if(Array.isArray(data.mounted)) {
159
+ for(const cb of data.mounted) {
160
+ cb(node);
161
+ }
162
+ return;
163
+ }
164
+ data.mounted(node);
165
+ },
166
+
167
+ executeUnmountedCallback(node) {
168
+ const data = DocumentObserver.unmounted.get(node);
169
+ if(!data) {
170
+ return;
171
+ }
172
+ data.inDom = false;
173
+ if(!data.unmounted) {
174
+ return;
175
+ }
176
+
177
+ let shouldRemove = false;
178
+ if(Array.isArray(data.unmounted)) {
179
+ for(const cb of data.unmounted) {
180
+ if(cb(node) === true) {
181
+ shouldRemove = true;
182
+ }
183
+ }
184
+ } else {
185
+ shouldRemove = data.unmounted(node) === true;
186
+ }
187
+
188
+ if(shouldRemove) {
189
+ data.disconnect();
190
+ node.nd?.remove();
191
+ }
192
+ },
193
+
194
+ checkMutation: function(mutationsList) {
195
+ for(const mutation of mutationsList) {
196
+ if(DocumentObserver.mountedSupposedSize > 0) {
197
+ for(const node of mutation.addedNodes) {
198
+ DocumentObserver.executeMountedCallback(node);
199
+ if(!node.querySelectorAll) {
200
+ continue;
201
+ }
202
+ const children = node.querySelectorAll('[data--nd-mounted]');
203
+ for(const child of children) {
204
+ DocumentObserver.executeMountedCallback(child);
205
+ }
206
+ }
207
+ }
208
+
209
+ if (DocumentObserver.unmountedSupposedSize > 0) {
210
+ for (const node of mutation.removedNodes) {
211
+ DocumentObserver.executeUnmountedCallback(node);
212
+ if(!node.querySelectorAll) {
213
+ continue;
214
+ }
215
+ const children = node.querySelectorAll('[data--nd-unmounted]');
216
+ for(const child of children) {
217
+ DocumentObserver.executeUnmountedCallback(child);
218
+ }
219
+ }
220
+ }
221
+ }
222
+ },
223
+
224
+ /**
225
+ * @param {HTMLElement} element
226
+ * @param {boolean} inDom
227
+ * @returns {{ disconnect: Function, mounted: Function, unmounted: Function, off: Function }}
228
+ */
229
+ watch: function(element, inDom = false) {
230
+ let mountedRegistered = false;
231
+ let unmountedRegistered = false;
232
+
233
+ let data = {
234
+ inDom,
235
+ mounted: null,
236
+ unmounted: null,
237
+ disconnect: () => {
238
+ if (mountedRegistered) {
239
+ DocumentObserver.mounted.delete(element);
240
+ DocumentObserver.mountedSupposedSize--;
241
+ }
242
+ if (unmountedRegistered) {
243
+ DocumentObserver.unmounted.delete(element);
244
+ DocumentObserver.unmountedSupposedSize--;
245
+ }
246
+ data = null;
247
+ }
248
+ };
249
+
250
+ const addListener = (type, callback) => {
251
+ if (!data[type]) {
252
+ data[type] = callback;
253
+ return;
254
+ }
255
+ if (!Array.isArray(data[type])) {
256
+ data[type] = [data[type], callback];
257
+ return;
258
+ }
259
+ data[type].push(callback);
260
+ };
261
+
262
+ const removeListener = (type, callback) => {
263
+ if(!data?.[type]) {
264
+ return;
265
+ }
266
+ if(Array.isArray(data[type])) {
267
+ const index = data[type].indexOf(callback);
268
+ if(index > -1) {
269
+ data[type].splice(index, 1);
270
+ }
271
+ if(data[type].length === 1) {
272
+ data[type] = data[type][0];
273
+ }
274
+ if(data[type].length === 0) {
275
+ data[type] = null;
276
+ }
277
+ return;
278
+ }
279
+ data[type] = null;
280
+ };
281
+
282
+ return {
283
+ disconnect: () => data?.disconnect(),
284
+
285
+ mounted: (callback) => {
286
+ addListener('mounted', callback);
287
+ DocumentObserver.mounted.set(element, data);
288
+ if (!mountedRegistered) {
289
+ DocumentObserver.mountedSupposedSize++;
290
+ mountedRegistered = true;
291
+ }
292
+ },
293
+
294
+ unmounted: (callback) => {
295
+ addListener('unmounted', callback);
296
+ DocumentObserver.unmounted.set(element, data);
297
+ if (!unmountedRegistered) {
298
+ DocumentObserver.unmountedSupposedSize++;
299
+ unmountedRegistered = true;
300
+ }
301
+ },
302
+
303
+ off: (type, callback) => {
304
+ removeListener(type, callback);
305
+ }
306
+ };
307
+ }
308
+ };
309
+
310
+ DocumentObserver.observer = new MutationObserver(DocumentObserver.checkMutation);
311
+ DocumentObserver.observer.observe(document.body, {
312
+ childList: true,
313
+ subtree: true,
314
+ });
315
+
316
+ let PluginsManager$1 = null;
195
317
 
196
318
  {
197
- PluginsManager = (function() {
319
+ PluginsManager$1 = (function() {
198
320
 
199
321
  const $plugins = new Map();
200
322
  const $pluginByEvents = new Map();
@@ -260,7 +382,7 @@ var NativeDocument = (function (exports) {
260
382
  try{
261
383
  callback.call(plugin, ...data);
262
384
  } catch (error) {
263
- DebugManager$1.error('Plugin Manager', `Error in plugin ${plugin.$name} for event ${eventName}`, error);
385
+ DebugManager.error('Plugin Manager', `Error in plugin ${plugin.$name} for event ${eventName}`, error);
264
386
  }
265
387
  }
266
388
  }
@@ -269,1271 +391,1900 @@ var NativeDocument = (function (exports) {
269
391
  }());
270
392
  }
271
393
 
272
- var PluginsManager$1 = PluginsManager;
394
+ var PluginsManager = PluginsManager$1;
273
395
 
274
- /**
275
- * Creates an ObservableWhen that tracks whether an observable equals a specific value.
276
- *
277
- * @param {ObservableItem} observer - The observable to watch
278
- * @param {*} value - The value to compare against
279
- * @class ObservableWhen
280
- */
281
- const ObservableWhen = function(observer, value) {
282
- this.$target = value;
283
- this.$observer = observer;
284
- };
396
+ function NDElement(element) {
397
+ this.$element = element;
398
+ this.$observer = null;
399
+ {
400
+ PluginsManager.emit('NDElementCreated', element, this);
401
+ }
402
+ }
285
403
 
286
- ObservableWhen.prototype.__$isObservableWhen = true;
404
+ NDElement.prototype.__$isNDElement = true;
287
405
 
288
- /**
289
- * Subscribes to changes in the match status (true when observable equals target value).
290
- *
291
- * @param {Function} callback - Function called with boolean indicating if values match
292
- * @returns {Function} Unsubscribe function
293
- * @example
294
- * const status = Observable('idle');
295
- * const isLoading = status.when('loading');
296
- * isLoading.subscribe(active => console.log('Loading:', active));
297
- */
298
- ObservableWhen.prototype.subscribe = function(callback) {
299
- return this.$observer.on(this.$target, callback);
406
+ NDElement.prototype.valueOf = function() {
407
+ return this.$element;
300
408
  };
301
409
 
302
- /**
303
- * Returns true if the observable's current value equals the target value.
304
- *
305
- * @returns {boolean} True if observable value matches target value
306
- */
307
- ObservableWhen.prototype.val = function() {
308
- return this.$observer.$currentValue === this.$target;
410
+ NDElement.prototype.ref = function(target, name) {
411
+ target[name] = this.$element;
412
+ return this;
309
413
  };
310
414
 
311
- /**
312
- * Returns true if the observable's current value equals the target value.
313
- * Alias for val().
314
- *
315
- * @returns {boolean} True if observable value matches target value
316
- */
317
- ObservableWhen.prototype.isMatch = ObservableWhen.prototype.val;
318
-
319
- /**
320
- * Returns true if the observable's current value equals the target value.
321
- * Alias for val().
322
- *
323
- * @returns {boolean} True if observable value matches target value
324
- */
325
- ObservableWhen.prototype.isActive = ObservableWhen.prototype.val;
326
-
327
- const nextTick = function(fn) {
328
- let pending = false;
329
- return function(...args) {
330
- if (pending) return;
331
- pending = true;
415
+ NDElement.prototype.refSelf = function(target, name) {
416
+ target[name] = this;
417
+ return this;
418
+ };
332
419
 
333
- Promise.resolve().then(() => {
334
- fn.apply(this, args);
335
- pending = false;
336
- });
337
- };
420
+ NDElement.prototype.unmountChildren = function() {
421
+ let element = this.$element;
422
+ for(let i = 0, length = element.children.length; i < length; i++) {
423
+ let elementChildren = element.children[i];
424
+ if(!elementChildren.$ndProx) {
425
+ elementChildren.nd?.remove();
426
+ }
427
+ elementChildren = null;
428
+ }
429
+ element = null;
430
+ return this;
338
431
  };
339
432
 
340
- /**
341
- *
342
- * @param {*} item
343
- * @param {string|null} defaultKey
344
- * @param {?Function} key
345
- * @returns {*}
346
- */
347
- const getKey = (item, defaultKey, key) => {
348
- if (Validator.isString(key)) {
349
- const val = Validator.isObservable(item) ? item.val() : item;
350
- const result = val?.[key];
351
- return Validator.isObservable(result) ? result.val() : (result ?? defaultKey);
352
- }
433
+ NDElement.prototype.remove = function() {
434
+ let element = this.$element;
435
+ element.nd.unmountChildren();
436
+ element.$ndProx = null;
437
+ delete element.nd?.on?.prevent;
438
+ delete element.nd?.on;
439
+ delete element.nd;
440
+ element = null;
441
+ return this;
442
+ };
353
443
 
354
- if (Validator.isFunction(key)) {
355
- return key(item, defaultKey);
444
+ NDElement.prototype.lifecycle = function(states) {
445
+ this.$observer = this.$observer || DocumentObserver.watch(this.$element);
446
+
447
+ if(states.mounted) {
448
+ this.$element.setAttribute('data--nd-mounted', '1');
449
+ this.$observer.mounted(states.mounted);
450
+ }
451
+ if(states.unmounted) {
452
+ this.$element.setAttribute('data--nd-unmounted', '1');
453
+ this.$observer.unmounted(states.unmounted);
356
454
  }
455
+ return this;
456
+ };
357
457
 
358
- const val = Validator.isObservable(item) ? item.val() : item;
359
- return val ?? defaultKey;
458
+ NDElement.prototype.mounted = function(callback) {
459
+ return this.lifecycle({ mounted: callback });
360
460
  };
361
461
 
362
- const trim = function(str, char) {
363
- return str.replace(new RegExp(`^[${char}]+|[${char}]+$`, 'g'), '');
462
+ NDElement.prototype.unmounted = function(callback) {
463
+ return this.lifecycle({ unmounted: callback });
364
464
  };
365
465
 
366
- const deepClone = (value, onObservableFound) => {
367
- // Primitives
368
- if (value === null || typeof value !== 'object') {
369
- return value;
370
- }
466
+ NDElement.prototype.beforeUnmount = function(id, callback) {
467
+ const el = this.$element;
371
468
 
372
- // Dates
373
- if (value instanceof Date) {
374
- return new Date(value.getTime());
375
- }
469
+ if(!DocumentObserver.beforeUnmount.has(el)) {
470
+ DocumentObserver.beforeUnmount.set(el, new Map());
471
+ const originalRemove = el.remove.bind(el);
376
472
 
377
- // Arrays
378
- if (Array.isArray(value)) {
379
- return value.map(item => deepClone(item));
380
- }
473
+ let $isUnmounting = false;
381
474
 
382
- // Observables - keep the référence
383
- if (Validator.isObservable(value)) {
384
- onObservableFound && onObservableFound(value);
385
- return value;
475
+ el.remove = async () => {
476
+ if($isUnmounting) {
477
+ return;
478
+ }
479
+ $isUnmounting = true;
480
+
481
+ try {
482
+ const callbacks = DocumentObserver.beforeUnmount.get(el);
483
+ for (const cb of callbacks.values()) {
484
+ await cb.call(this, el);
485
+ }
486
+ } finally {
487
+ originalRemove();
488
+ $isUnmounting = false;
489
+ }
490
+ };
386
491
  }
387
492
 
388
- // Objects
389
- const cloned = {};
390
- for (const key in value) {
391
- if (value.hasOwnProperty(key)) {
392
- cloned[key] = deepClone(value[key]);
393
- }
493
+ DocumentObserver.beforeUnmount.get(el).set(id, callback);
494
+ return this;
495
+ };
496
+
497
+ NDElement.prototype.htmlElement = function() {
498
+ return this.$element;
499
+ };
500
+
501
+ NDElement.prototype.node = NDElement.prototype.htmlElement;
502
+
503
+ NDElement.prototype.shadow = function(mode, style = null) {
504
+ const $element = this.$element;
505
+ const children = Array.from($element.childNodes);
506
+ const shadowRoot = $element.attachShadow({ mode });
507
+ if(style) {
508
+ const styleNode = document.createElement("style");
509
+ styleNode.textContent = style;
510
+ shadowRoot.appendChild(styleNode);
394
511
  }
395
- return cloned;
512
+ $element.append = shadowRoot.append.bind(shadowRoot);
513
+ $element.appendChild = shadowRoot.appendChild.bind(shadowRoot);
514
+ shadowRoot.append(...children);
515
+
516
+ return this;
517
+ };
518
+
519
+ NDElement.prototype.openShadow = function(style = null) {
520
+ return this.shadow('open', style);
521
+ };
522
+
523
+ NDElement.prototype.closedShadow = function(style = null) {
524
+ return this.shadow('closed', style);
396
525
  };
397
526
 
398
527
  /**
528
+ * Attaches a template binding to the element by hydrating it with the specified method.
399
529
  *
400
- * @param {*} value
401
- * @param {{ propagation: boolean, reset: boolean} | null} configs
402
- * @class ObservableItem
530
+ * @param {string} methodName - Name of the hydration method to call
531
+ * @param {BindingHydrator} bindingHydrator - Template binding with $hydrate method
532
+ * @returns {HTMLElement} The underlying HTML element
533
+ * @example
534
+ * const onClick = $binder.attach((event, data) => console.log(data));
535
+ * element.nd.attach('onClick', onClick);
403
536
  */
404
- function ObservableItem(value, configs = null) {
405
- value = Validator.isObservable(value) ? value.val() : value;
537
+ NDElement.prototype.attach = function(methodName, bindingHydrator) {
538
+ bindingHydrator.$hydrate(this.$element, methodName);
539
+ return this.$element;
540
+ };
406
541
 
407
- this.$previousValue = null;
408
- this.$currentValue = value;
542
+ /**
543
+ * Extends the current NDElement instance with custom methods.
544
+ * Methods are bound to the instance and available for chaining.
545
+ *
546
+ * @param {Object} methods - Object containing method definitions
547
+ * @returns {this} The NDElement instance with added methods for chaining
548
+ * @example
549
+ * element.nd.with({
550
+ * highlight() {
551
+ * this.$element.style.background = 'yellow';
552
+ * return this;
553
+ * }
554
+ * }).highlight().onClick(() => console.log('Clicked'));
555
+ */
556
+ NDElement.prototype.with = function(methods) {
557
+ if (!methods || typeof methods !== 'object') {
558
+ throw new NativeDocumentError('extend() requires an object of methods');
559
+ }
409
560
  {
410
- this.$isCleanedUp = false;
561
+ if (!this.$localExtensions) {
562
+ this.$localExtensions = new Map();
563
+ }
411
564
  }
412
565
 
413
- this.$firstListener = null;
414
- this.$listeners = null;
415
- this.$watchers = null;
416
-
417
- this.$memoryId = null;
566
+ for (const name in methods) {
567
+ const method = methods[name];
418
568
 
419
- if(configs) {
420
- this.configs = configs;
421
- if(configs.reset) {
422
- this.$initialValue = Validator.isObject(value) ? deepClone(value) : value;
569
+ if (typeof method !== 'function') {
570
+ console.warn(`⚠️ extends(): "${name}" is not a function, skipping`);
571
+ continue;
572
+ }
573
+ {
574
+ if (this[name] && !this.$localExtensions.has(name)) {
575
+ DebugManager.warn('NDElement.extend', `Method "${name}" already exists and will be overwritten`);
576
+ }
577
+ this.$localExtensions.set(name, method);
423
578
  }
424
- }
425
- {
426
- PluginsManager$1.emit('CreateObservable', this);
427
- }
428
- }
429
579
 
430
- Object.defineProperty(ObservableItem.prototype, '$value', {
431
- get() {
432
- return this.$currentValue;
433
- },
434
- set(value) {
435
- this.set(value);
436
- },
437
- configurable: true,
438
- });
580
+ this[name] = method.bind(this);
581
+ }
439
582
 
440
- ObservableItem.prototype.__$isObservable = true;
441
- const noneTrigger = function() {};
583
+ return this;
584
+ };
442
585
 
443
586
  /**
444
- * Intercepts and transforms values before they are set on the observable.
445
- * The interceptor can modify the value or return undefined to use the original value.
587
+ * Extends the NDElement prototype with new methods available to all NDElement instances.
588
+ * Use this to add global methods to all NDElements.
446
589
  *
447
- * @param {(value) => any} callback - Interceptor function that receives (newValue, currentValue) and returns the transformed value or undefined
448
- * @returns {ObservableItem} The observable instance for chaining
590
+ * @param {Object} methods - Object containing method definitions to add to prototype
591
+ * @returns {typeof NDElement} The NDElement constructor
592
+ * @throws {NativeDocumentError} If methods is not an object or contains non-function values
449
593
  * @example
450
- * const count = Observable(0);
451
- * count.intercept((newVal, oldVal) => Math.max(0, newVal)); // Prevent negative values
594
+ * NDElement.extend({
595
+ * fadeIn() {
596
+ * this.$element.style.opacity = '1';
597
+ * return this;
598
+ * }
599
+ * });
600
+ * // Now all NDElements have .fadeIn() method
601
+ * Div().nd.fadeIn();
452
602
  */
453
- ObservableItem.prototype.intercept = function(callback) {
454
- this.$interceptor = callback;
455
- this.set = this.$setWithInterceptor;
456
- return this;
457
- };
603
+ NDElement.extend = function(methods) {
604
+ if (!methods || typeof methods !== 'object') {
605
+ throw new NativeDocumentError('NDElement.extend() requires an object of methods');
606
+ }
458
607
 
459
- ObservableItem.prototype.triggerFirstListener = function(operations) {
460
- this.$firstListener(this.$currentValue, this.$previousValue, operations);
461
- };
608
+ if (Array.isArray(methods)) {
609
+ throw new NativeDocumentError('NDElement.extend() requires an object, not an array');
610
+ }
462
611
 
463
- ObservableItem.prototype.triggerListeners = function(operations) {
464
- const $listeners = this.$listeners;
465
- const $previousValue = this.$previousValue;
466
- const $currentValue = this.$currentValue;
612
+ const protectedMethods = new Set([
613
+ 'constructor', 'valueOf', '$element', '$observer',
614
+ 'ref', 'remove', 'cleanup', 'with', 'extend', 'attach',
615
+ 'lifecycle', 'mounted', 'unmounted', 'unmountChildren'
616
+ ]);
467
617
 
468
- for(let i = 0, length = $listeners.length; i < length; i++) {
469
- $listeners[i]($currentValue, $previousValue, operations);
470
- }
471
- };
618
+ for (const name in methods) {
619
+ if (!Object.hasOwn(methods, name)) {
620
+ continue;
621
+ }
472
622
 
473
- ObservableItem.prototype.triggerWatchers = function(operations) {
474
- const $watchers = this.$watchers;
475
- const $previousValue = this.$previousValue;
476
- const $currentValue = this.$currentValue;
623
+ const method = methods[name];
477
624
 
478
- const $currentValueCallbacks = $watchers.get($currentValue);
479
- const $previousValueCallbacks = $watchers.get($previousValue);
480
- if($currentValueCallbacks) {
481
- $currentValueCallbacks(true, $previousValue, operations);
625
+ if (typeof method !== 'function') {
626
+ DebugManager.warn('NDElement.extend', `"${name}" is not a function, skipping`);
627
+ continue;
628
+ }
629
+
630
+ if (protectedMethods.has(name)) {
631
+ DebugManager.error('NDElement.extend', `Cannot override protected method "${name}"`);
632
+ throw new NativeDocumentError(`Cannot override protected method "${name}"`);
633
+ }
634
+
635
+ if (NDElement.prototype[name]) {
636
+ DebugManager.warn('NDElement.extend', `Overwriting existing prototype method "${name}"`);
637
+ }
638
+
639
+ NDElement.prototype[name] = method;
482
640
  }
483
- if($previousValueCallbacks) {
484
- $previousValueCallbacks(false, $currentValue, operations);
641
+ {
642
+ PluginsManager.emit('NDElementExtended', methods);
485
643
  }
486
- };
487
644
 
488
- ObservableItem.prototype.triggerAll = function(operations) {
489
- this.triggerWatchers(operations);
490
- this.triggerListeners(operations);
645
+ return NDElement;
491
646
  };
492
647
 
493
- ObservableItem.prototype.triggerWatchersAndFirstListener = function(operations) {
494
- this.triggerWatchers(operations);
495
- this.triggerFirstListener(operations);
648
+ const COMMON_NODE_TYPES = {
649
+ ELEMENT: 1,
650
+ TEXT: 3,
651
+ COMMENT: 8,
652
+ DOCUMENT_FRAGMENT: 11
496
653
  };
497
654
 
498
- ObservableItem.prototype.assocTrigger = function() {
499
- this.$firstListener = null;
500
- if(this.$watchers?.size && this.$listeners?.length) {
501
- this.trigger = (this.$listeners.length === 1) ? this.triggerWatchersAndFirstListener : this.triggerAll;
502
- return;
503
- }
504
- if(this.$listeners?.length) {
505
- if(this.$listeners.length === 1) {
506
- this.$firstListener = this.$listeners[0];
507
- this.trigger = this.triggerFirstListener;
655
+ const Validator = {
656
+ isObservable(value) {
657
+ return value?.__$isObservable;
658
+ },
659
+ isTemplateBinding(value) {
660
+ return value?.__$isTemplateBinding;
661
+ },
662
+ isObservableWhenResult(value) {
663
+ return value && (value.__$isObservableWhen || (typeof value === 'object' && '$target' in value && '$observer' in value));
664
+ },
665
+ isArrayObservable(value) {
666
+ return value?.__$isObservableArray;
667
+ },
668
+ isProxy(value) {
669
+ return value?.__isProxy__
670
+ },
671
+ isObservableOrProxy(value) {
672
+ return Validator.isObservable(value) || Validator.isProxy(value);
673
+ },
674
+ isAnchor(value) {
675
+ return value?.__Anchor__
676
+ },
677
+ isObservableChecker(value) {
678
+ return value?.__$isObservableChecker || value instanceof ObservableChecker;
679
+ },
680
+ isArray(value) {
681
+ return Array.isArray(value);
682
+ },
683
+ isString(value) {
684
+ return typeof value === 'string';
685
+ },
686
+ isNumber(value) {
687
+ return typeof value === 'number';
688
+ },
689
+ isBoolean(value) {
690
+ return typeof value === 'boolean';
691
+ },
692
+ isFunction(value) {
693
+ return typeof value === 'function';
694
+ },
695
+ isAsyncFunction(value) {
696
+ return typeof value === 'function' && value.constructor.name === 'AsyncFunction';
697
+ },
698
+ isObject(value) {
699
+ return typeof value === 'object' && value !== null;
700
+ },
701
+ isJson(value) {
702
+ return !(typeof value !== 'object' || value === null || Array.isArray(value) || value.constructor.name !== 'Object')
703
+ },
704
+ isElement(value) {
705
+ return value && (
706
+ value.nodeType === COMMON_NODE_TYPES.ELEMENT ||
707
+ value.nodeType === COMMON_NODE_TYPES.TEXT ||
708
+ value.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT ||
709
+ value.nodeType === COMMON_NODE_TYPES.COMMENT
710
+ );
711
+ },
712
+ isFragment(value) {
713
+ return value?.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT;
714
+ },
715
+ isStringOrObservable(value) {
716
+ return this.isString(value) || this.isObservable(value);
717
+ },
718
+ isValidChild(child) {
719
+ return child === null ||
720
+ this.isElement(child) ||
721
+ this.isObservable(child) ||
722
+ this.isNDElement(child) ||
723
+ ['string', 'number', 'boolean'].includes(typeof child);
724
+ },
725
+ isNDElement(child) {
726
+ return child?.__$isNDElement || child instanceof NDElement;
727
+ },
728
+ isValidChildren(children) {
729
+ if (!Array.isArray(children)) {
730
+ children = [children];
508
731
  }
509
- else {
510
- this.trigger = this.triggerListeners;
732
+
733
+ const invalid = children.filter(child => !this.isValidChild(child));
734
+ return invalid.length === 0;
735
+ },
736
+ validateChildren(children) {
737
+ if (!Array.isArray(children)) {
738
+ children = [children];
511
739
  }
512
- return;
513
- }
514
- if(this.$watchers?.size) {
515
- this.trigger = this.triggerWatchers;
516
- return;
517
- }
518
- this.trigger = noneTrigger;
519
- };
520
- ObservableItem.prototype.trigger = noneTrigger;
521
740
 
522
- ObservableItem.prototype.$updateWithNewValue = function(newValue) {
523
- newValue = newValue?.__$isObservable ? newValue.val() : newValue;
524
- if(this.$currentValue === newValue) {
525
- return;
526
- }
527
- this.$previousValue = this.$currentValue;
528
- this.$currentValue = newValue;
529
- {
530
- PluginsManager$1.emit('ObservableBeforeChange', this);
531
- }
532
- this.trigger();
533
- this.$previousValue = null;
534
- {
535
- PluginsManager$1.emit('ObservableAfterChange', this);
741
+ const invalid = children.filter(child => !this.isValidChild(child));
742
+ if (invalid.length > 0) {
743
+ throw new NativeDocumentError(`Invalid children detected: ${invalid.map(i => typeof i).join(', ')}`);
744
+ }
745
+
746
+ return children;
747
+ },
748
+ /**
749
+ * Check if the data contains observables.
750
+ * @param {Array|Object} data
751
+ * @returns {boolean}
752
+ */
753
+ containsObservables(data) {
754
+ if(!data) {
755
+ return false;
756
+ }
757
+ return Validator.isObject(data)
758
+ && Object.values(data).some(value => Validator.isObservable(value));
759
+ },
760
+ /**
761
+ * Check if the data contains an observable reference.
762
+ * @param {string} data
763
+ * @returns {boolean}
764
+ */
765
+ containsObservableReference(data) {
766
+ if(!data || typeof data !== 'string') {
767
+ return false;
768
+ }
769
+ return /\{\{#ObItem::\([0-9]+\)\}\}/.test(data);
770
+ },
771
+ validateAttributes(attributes) {},
772
+
773
+ validateEventCallback(callback) {
774
+ if (typeof callback !== 'function') {
775
+ throw new NativeDocumentError('Event callback must be a function');
776
+ }
536
777
  }
537
778
  };
779
+ {
780
+ Validator.validateAttributes = function(attributes) {
781
+ if (!attributes || typeof attributes !== 'object') {
782
+ return attributes;
783
+ }
538
784
 
539
- /**
540
- * @param {*} data
541
- */
542
- ObservableItem.prototype.$setWithInterceptor = function(data) {
543
- let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
544
- const result = this.$interceptor(newValue, this.$currentValue);
785
+ const reserved = [];
786
+ const foundReserved = Object.keys(attributes).filter(key => reserved.includes(key));
545
787
 
546
- if (result !== undefined) {
547
- newValue = result;
548
- }
788
+ if (foundReserved.length > 0) {
789
+ DebugManager.warn('Validator', `Reserved attributes found: ${foundReserved.join(', ')}`);
790
+ }
549
791
 
550
- this.$updateWithNewValue(newValue);
551
- };
792
+ return attributes;
793
+ };
794
+ }
552
795
 
553
- /**
554
- * @param {*} data
555
- */
556
- ObservableItem.prototype.$basicSet = function(data) {
557
- let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
558
- this.$updateWithNewValue(newValue);
559
- };
796
+ function Anchor(name, isUniqueChild = false) {
797
+ const anchorFragment = document.createDocumentFragment();
798
+ anchorFragment.__Anchor__ = true;
560
799
 
561
- ObservableItem.prototype.set = ObservableItem.prototype.$basicSet;
800
+ const anchorStart = document.createComment('Anchor Start : '+name);
801
+ const anchorEnd = document.createComment('/ Anchor End '+name);
562
802
 
563
- ObservableItem.prototype.val = function() {
564
- return this.$currentValue;
565
- };
803
+ anchorFragment.appendChild(anchorStart);
804
+ anchorFragment.appendChild(anchorEnd);
566
805
 
567
- ObservableItem.prototype.disconnectAll = function() {
568
- this.$listeners?.splice(0);
569
- this.$previousValue = null;
570
- this.$currentValue = null;
571
- if(this.$watchers) {
572
- for (const [_, watchValueList] of this.$watchers) {
573
- if(Validator.isArray(watchValueList)) {
574
- watchValueList.splice(0);
575
- }
806
+ anchorFragment.nativeInsertBefore = anchorFragment.insertBefore;
807
+ anchorFragment.nativeAppendChild = anchorFragment.appendChild;
808
+ anchorFragment.nativeAppend = anchorFragment.append;
809
+
810
+ const isParentUniqueChild = (parent) => (isUniqueChild || (parent.firstChild === anchorStart && parent.lastChild === anchorEnd));
811
+
812
+ const insertBefore = function(parent, child, target) {
813
+ const childElement = Validator.isElement(child) ? child : ElementCreator.getChild(child);
814
+ if(parent === anchorFragment) {
815
+ parent.nativeInsertBefore(childElement, target);
816
+ return;
576
817
  }
577
- }
578
- this.$watchers?.clear();
579
- this.$listeners = null;
580
- this.$watchers = null;
581
- this.trigger = noneTrigger;
582
- };
818
+ if(isParentUniqueChild(parent) && target === anchorEnd) {
819
+ parent.append(childElement, target);
820
+ return;
821
+ }
822
+ parent.insertBefore(childElement, target);
823
+ };
583
824
 
584
- /**
585
- * Registers a cleanup callback that will be executed when the observable is cleaned up.
586
- * Useful for disposing resources, removing event listeners, or other cleanup tasks.
587
- *
588
- * @param {Function} callback - Cleanup function to execute on observable disposal
589
- * @example
590
- * const obs = Observable(0);
591
- * obs.onCleanup(() => console.log('Cleaned up!'));
592
- * obs.cleanup(); // Logs: "Cleaned up!"
593
- */
594
- ObservableItem.prototype.onCleanup = function(callback) {
595
- this.$cleanupListeners = this.$cleanupListeners ?? [];
596
- this.$cleanupListeners.push(callback);
597
- };
825
+ anchorFragment.appendElement = function(child, before = null) {
826
+ const parentNode = anchorStart.parentNode;
827
+ const targetBefore = before || anchorEnd;
828
+ if(parentNode === anchorFragment) {
829
+ parentNode.nativeInsertBefore(child, targetBefore);
830
+ return;
831
+ }
832
+ parentNode?.insertBefore(child, targetBefore);
833
+ };
598
834
 
599
- ObservableItem.prototype.cleanup = function() {
600
- if (this.$cleanupListeners) {
601
- for (let i = 0; i < this.$cleanupListeners.length; i++) {
602
- this.$cleanupListeners[i]();
835
+ anchorFragment.appendChild = function(child, before = null) {
836
+ const parent = anchorEnd.parentNode;
837
+ if(!parent) {
838
+ DebugManager.error('Anchor', 'Anchor : parent not found', child);
839
+ return;
603
840
  }
604
- this.$cleanupListeners = null;
605
- }
606
- MemoryManager.unregister(this.$memoryId);
607
- this.disconnectAll();
608
- {
609
- this.$isCleanedUp = true;
610
- }
611
- delete this.$value;
612
- };
841
+ before = before ?? anchorEnd;
842
+ insertBefore(parent, child, before);
843
+ };
613
844
 
614
- /**
615
- *
616
- * @param {Function} callback
617
- * @returns {(function(): void)}
618
- */
619
- ObservableItem.prototype.subscribe = function(callback) {
620
- {
621
- if (this.$isCleanedUp) {
622
- DebugManager$1.warn('Observable subscription', '⚠️ Attempted to subscribe to a cleaned up observable.');
845
+ anchorFragment.append = function(...args ) {
846
+ return anchorFragment.appendChild(args);
847
+ };
848
+
849
+ anchorFragment.removeChildren = async function() {
850
+ const parent = anchorEnd.parentNode;
851
+ if(parent === anchorFragment) {
852
+ return;
853
+ }
854
+ // if(isParentUniqueChild(parent)) {
855
+ // parent.replaceChildren(anchorStart, anchorEnd);
856
+ // return;
857
+ // }
858
+
859
+ let itemToRemove = anchorStart.nextSibling, tempItem;
860
+ const removes = [];
861
+ while(itemToRemove && itemToRemove !== anchorEnd) {
862
+ tempItem = itemToRemove.nextSibling;
863
+ removes.push(itemToRemove.remove());
864
+ itemToRemove = tempItem;
865
+ }
866
+ await Promise.all(removes);
867
+ };
868
+
869
+ anchorFragment.remove = async function() {
870
+ const parent = anchorEnd.parentNode;
871
+ if(parent === anchorFragment) {
872
+ return;
873
+ }
874
+ let itemToRemove = anchorStart.nextSibling, tempItem;
875
+ const allItemToRemove = [];
876
+ const removes = [];
877
+ while(itemToRemove && itemToRemove !== anchorEnd) {
878
+ tempItem = itemToRemove.nextSibling;
879
+ allItemToRemove.push(itemToRemove);
880
+ removes.push(itemToRemove.remove());
881
+ itemToRemove = tempItem;
882
+ }
883
+ await Promise.all(removes);
884
+ anchorFragment.nativeAppend(...allItemToRemove);
885
+ };
886
+
887
+ anchorFragment.removeWithAnchors = async function() {
888
+ await anchorFragment.removeChildren();
889
+ anchorStart.remove();
890
+ anchorEnd.remove();
891
+ };
892
+
893
+ anchorFragment.replaceContent = async function(child) {
894
+ const childElement = Validator.isElement(child) ? child : ElementCreator.getChild(child);
895
+ const parent = anchorEnd.parentNode;
896
+ if(!parent) {
623
897
  return;
624
898
  }
625
- if (typeof callback !== 'function') {
626
- throw new NativeDocumentError('Callback must be a function');
627
- }
628
- }
629
- this.$listeners = this.$listeners ?? [];
899
+ // if(isParentUniqueChild(parent)) {
900
+ // parent.replaceChildren(anchorStart, childElement, anchorEnd);
901
+ // return;
902
+ // }
903
+ await anchorFragment.removeChildren();
904
+ parent.insertBefore(childElement, anchorEnd);
905
+ };
630
906
 
631
- this.$listeners.push(callback);
632
- this.assocTrigger();
633
- {
634
- PluginsManager$1.emit('ObservableSubscribe', this);
635
- }
636
- };
907
+ anchorFragment.setContent = anchorFragment.replaceContent;
637
908
 
638
- /**
639
- * Watches for a specific value and executes callback when the observable equals that value.
640
- * Creates a watcher that only triggers when the observable changes to the specified value.
641
- *
642
- * @param {*} value - The value to watch for
643
- * @param {(value) => void|ObservableItem} callback - Callback function or observable to set when value matches
644
- * @example
645
- * const status = Observable('idle');
646
- * status.on('loading', () => console.log('Started loading'));
647
- * status.on('error', isError); // Set another observable
648
- */
649
- ObservableItem.prototype.on = function(value, callback) {
650
- this.$watchers = this.$watchers ?? new Map();
909
+ anchorFragment.insertBefore = function(child, anchor = null) {
910
+ anchorFragment.appendChild(child, anchor);
911
+ };
651
912
 
652
- let watchValueList = this.$watchers.get(value);
653
913
 
654
- if(callback.__$isObservable) {
655
- callback = callback.set.bind(callback);
656
- }
914
+ anchorFragment.endElement = function() {
915
+ return anchorEnd;
916
+ };
657
917
 
658
- if(!watchValueList) {
659
- watchValueList = callback;
660
- this.$watchers.set(value, callback);
661
- } else if(!Validator.isArray(watchValueList.list)) {
662
- watchValueList = [watchValueList, callback];
663
- callback = (value) => {
664
- for(let i = 0, length = watchValueList.length; i < length; i++) {
665
- watchValueList[i](value);
666
- }
667
- };
668
- callback.list = watchValueList;
669
- this.$watchers.set(value, callback);
670
- } else {
671
- watchValueList.list.push(callback);
672
- }
918
+ anchorFragment.startElement = function() {
919
+ return anchorStart;
920
+ };
921
+ anchorFragment.restore = function() {
922
+ anchorFragment.appendChild(anchorFragment);
923
+ };
924
+ anchorFragment.clear = anchorFragment.remove;
925
+ anchorFragment.detach = anchorFragment.remove;
673
926
 
674
- this.assocTrigger();
675
- };
927
+ anchorFragment.getByIndex = function(index) {
928
+ let currentNode = anchorStart;
929
+ for(let i = 0; i <= index; i++) {
930
+ if(!currentNode.nextSibling) {
931
+ return null;
932
+ }
933
+ currentNode = currentNode.nextSibling;
934
+ }
935
+ return currentNode !== anchorStart ? currentNode : null;
936
+ };
676
937
 
938
+ return anchorFragment;
939
+ }
677
940
  /**
678
- * Removes a watcher for a specific value. If no callback is provided, removes all watchers for that value.
679
941
  *
680
- * @param {*} value - The value to stop watching
681
- * @param {Function} [callback] - Specific callback to remove. If omitted, removes all watchers for this value
682
- * @example
683
- * const status = Observable('idle');
684
- * const handler = () => console.log('Loading');
685
- * status.on('loading', handler);
686
- * status.off('loading', handler); // Remove specific handler
687
- * status.off('loading'); // Remove all handlers for 'loading'
942
+ * @param {HTMLElement|DocumentFragment|Text|String|Array} children
943
+ * @param {{ parent?: HTMLElement, name?: String}} configs
944
+ * @returns {DocumentFragment}
688
945
  */
689
- ObservableItem.prototype.off = function(value, callback) {
690
- if(!this.$watchers) return;
946
+ function createPortal(children, { parent, name = 'unnamed' } = {}) {
947
+ const anchor = Anchor('Portal '+name);
948
+ anchor.appendChild(ElementCreator.getChild(children));
691
949
 
692
- const watchValueList = this.$watchers.get(value);
693
- if(!watchValueList) return;
950
+ (parent || document.body).appendChild(anchor);
951
+ return anchor;
952
+ }
694
953
 
695
- if(!callback || !Array.isArray(watchValueList.list)) {
696
- this.$watchers?.delete(value);
697
- this.assocTrigger();
698
- return;
699
- }
700
- const index = watchValueList.indexOf(callback);
701
- watchValueList?.splice(index, 1);
702
- if(watchValueList.length === 1) {
703
- this.$watchers.set(value, watchValueList[0]);
704
- }
705
- else if(watchValueList.length === 0) {
706
- this.$watchers?.delete(value);
707
- }
708
- this.assocTrigger();
709
- };
954
+ DocumentFragment.prototype.setAttribute = () => {};
710
955
 
711
- /**
712
- * Subscribes to the observable but automatically unsubscribes after the first time the predicate matches.
713
- *
714
- * @param {(value) => Boolean|any} predicate - Value to match or function that returns true when condition is met
715
- * @param {(value) => void} callback - Callback to execute when predicate matches, receives the matched value
716
- * @example
717
- * const status = Observable('loading');
718
- * status.once('ready', (val) => console.log('Ready!'));
719
- * status.once(val => val === 'error', (val) => console.log('Error occurred'));
720
- */
721
- ObservableItem.prototype.once = function(predicate, callback) {
722
- const fn = typeof predicate === 'function' ? predicate : (v) => v === predicate;
956
+ const BOOLEAN_ATTRIBUTES = new Set([
957
+ 'checked',
958
+ 'selected',
959
+ 'disabled',
960
+ 'readonly',
961
+ 'required',
962
+ 'autofocus',
963
+ 'multiple',
964
+ 'autocomplete',
965
+ 'hidden',
966
+ 'contenteditable',
967
+ 'spellcheck',
968
+ 'translate',
969
+ 'draggable',
970
+ 'async',
971
+ 'defer',
972
+ 'autoplay',
973
+ 'controls',
974
+ 'loop',
975
+ 'muted',
976
+ 'download',
977
+ 'reversed',
978
+ 'open',
979
+ 'default',
980
+ 'formnovalidate',
981
+ 'novalidate',
982
+ 'scoped',
983
+ 'itemscope',
984
+ 'allowfullscreen',
985
+ 'allowpaymentrequest',
986
+ 'playsinline'
987
+ ]);
723
988
 
724
- const handler = (val) => {
725
- if (fn(val)) {
726
- this.unsubscribe(handler);
727
- callback(val);
728
- }
729
- };
730
- this.subscribe(handler);
731
- };
989
+ const MemoryManager = (function() {
732
990
 
733
- /**
734
- * Unsubscribe from an observable.
735
- * @param {Function} callback
736
- */
737
- ObservableItem.prototype.unsubscribe = function(callback) {
738
- if(!this.$listeners) return;
739
- const index = this.$listeners.indexOf(callback);
740
- if (index > -1) {
741
- this.$listeners.splice(index, 1);
742
- }
743
- this.assocTrigger();
744
- {
745
- PluginsManager$1.emit('ObservableUnsubscribe', this);
746
- }
747
- };
991
+ let $nextObserverId = 0;
992
+ const $observables = new Map();
748
993
 
749
- /**
750
- * Create an Observable checker instance
751
- * @param callback
752
- * @returns {ObservableChecker}
753
- */
754
- ObservableItem.prototype.check = function(callback) {
755
- return new ObservableChecker(this, callback)
756
- };
994
+ return {
995
+ /**
996
+ * Register an observable and return an id.
997
+ *
998
+ * @param {ObservableItem} observable
999
+ * @param {Function} getListeners
1000
+ * @returns {number}
1001
+ */
1002
+ register(observable) {
1003
+ const id = ++$nextObserverId;
1004
+ $observables.set(id, new WeakRef(observable));
1005
+ return id;
1006
+ },
1007
+ unregister(id) {
1008
+ $observables.delete(id);
1009
+ },
1010
+ getObservableById(id) {
1011
+ return $observables.get(id)?.deref();
1012
+ },
1013
+ cleanup() {
1014
+ for (const [_, weakObservableRef] of $observables) {
1015
+ const observable = weakObservableRef.deref();
1016
+ if (observable) {
1017
+ observable.cleanup();
1018
+ }
1019
+ }
1020
+ $observables.clear();
1021
+ },
1022
+ /**
1023
+ * Clean observables that are not referenced anymore.
1024
+ * @param {number} threshold
1025
+ */
1026
+ cleanObservables(threshold) {
1027
+ if($observables.size < threshold) return;
1028
+ let cleanedCount = 0;
1029
+ for (const [id, weakObservableRef] of $observables) {
1030
+ if (!weakObservableRef.deref()) {
1031
+ $observables.delete(id);
1032
+ cleanedCount++;
1033
+ }
1034
+ }
1035
+ if (cleanedCount > 0) {
1036
+ DebugManager.log('Memory Auto Clean', `🧹 Cleaned ${cleanedCount} orphaned observables`);
1037
+ }
1038
+ }
1039
+ };
1040
+ }());
757
1041
 
758
1042
  /**
759
- * Gets a property value from the observable's current value.
760
- * If the property is an observable, returns its value.
1043
+ * Creates an ObservableWhen that tracks whether an observable equals a specific value.
761
1044
  *
762
- * @param {string|number} key - Property key to retrieve
763
- * @returns {*} The value of the property, unwrapped if it's an observable
764
- * @example
765
- * const user = Observable({ name: 'John', age: Observable(25) });
766
- * user.get('name'); // 'John'
767
- * user.get('age'); // 25 (unwrapped from observable)
1045
+ * @param {ObservableItem} observer - The observable to watch
1046
+ * @param {*} value - The value to compare against
1047
+ * @class ObservableWhen
768
1048
  */
769
- ObservableItem.prototype.get = function(key) {
770
- const item = this.$currentValue[key];
771
- return Validator.isObservable(item) ? item.val() : item;
1049
+ const ObservableWhen = function(observer, value) {
1050
+ this.$target = value;
1051
+ this.$observer = observer;
772
1052
  };
773
1053
 
1054
+ ObservableWhen.prototype.__$isObservableWhen = true;
1055
+
774
1056
  /**
775
- * Creates an ObservableWhen that represents whether the observable equals a specific value.
776
- * Returns an object that can be subscribed to and will emit true/false.
1057
+ * Subscribes to changes in the match status (true when observable equals target value).
777
1058
  *
778
- * @param {*} value - The value to compare against
779
- * @returns {ObservableWhen} An ObservableWhen instance that tracks when the observable equals the value
1059
+ * @param {Function} callback - Function called with boolean indicating if values match
1060
+ * @returns {Function} Unsubscribe function
780
1061
  * @example
781
1062
  * const status = Observable('idle');
782
1063
  * const isLoading = status.when('loading');
783
1064
  * isLoading.subscribe(active => console.log('Loading:', active));
784
- * status.set('loading'); // Logs: "Loading: true"
785
1065
  */
786
- ObservableItem.prototype.when = function(value) {
787
- return new ObservableWhen(this, value);
1066
+ ObservableWhen.prototype.subscribe = function(callback) {
1067
+ return this.$observer.on(this.$target, callback);
788
1068
  };
789
1069
 
790
1070
  /**
791
- * Compares the observable's current value with another value or observable.
1071
+ * Returns true if the observable's current value equals the target value.
792
1072
  *
793
- * @param {*|ObservableItem} other - Value or observable to compare against
794
- * @returns {boolean} True if values are equal
795
- * @example
796
- * const a = Observable(5);
797
- * const b = Observable(5);
798
- * a.equals(5); // true
799
- * a.equals(b); // true
800
- * a.equals(10); // false
1073
+ * @returns {boolean} True if observable value matches target value
801
1074
  */
802
- ObservableItem.prototype.equals = function(other) {
803
- if(Validator.isObservable(other)) {
804
- return this.$currentValue === other.$currentValue;
805
- }
806
- return this.$currentValue === other;
1075
+ ObservableWhen.prototype.val = function() {
1076
+ return this.$observer.$currentValue === this.$target;
807
1077
  };
808
1078
 
809
1079
  /**
810
- * Converts the observable's current value to a boolean.
1080
+ * Returns true if the observable's current value equals the target value.
1081
+ * Alias for val().
811
1082
  *
812
- * @returns {boolean} The boolean representation of the current value
813
- * @example
814
- * const count = Observable(0);
815
- * count.toBool(); // false
816
- * count.set(5);
817
- * count.toBool(); // true
1083
+ * @returns {boolean} True if observable value matches target value
818
1084
  */
819
- ObservableItem.prototype.toBool = function() {
820
- return !!this.$currentValue;
821
- };
1085
+ ObservableWhen.prototype.isMatch = ObservableWhen.prototype.val;
822
1086
 
823
1087
  /**
824
- * Toggles the boolean value of the observable (false becomes true, true becomes false).
1088
+ * Returns true if the observable's current value equals the target value.
1089
+ * Alias for val().
825
1090
  *
826
- * @example
827
- * const isOpen = Observable(false);
828
- * isOpen.toggle(); // Now true
829
- * isOpen.toggle(); // Now false
1091
+ * @returns {boolean} True if observable value matches target value
830
1092
  */
831
- ObservableItem.prototype.toggle = function() {
832
- this.set(!this.$currentValue);
1093
+ ObservableWhen.prototype.isActive = ObservableWhen.prototype.val;
1094
+
1095
+ const nextTick = function(fn) {
1096
+ let pending = false;
1097
+ return function(...args) {
1098
+ if (pending) return;
1099
+ pending = true;
1100
+
1101
+ Promise.resolve().then(() => {
1102
+ fn.apply(this, args);
1103
+ pending = false;
1104
+ });
1105
+ };
833
1106
  };
834
1107
 
835
1108
  /**
836
- * Resets the observable to its initial value.
837
- * Only works if the observable was created with { reset: true } config.
838
1109
  *
839
- * @example
840
- * const count = Observable(0, { reset: true });
841
- * count.set(10);
842
- * count.reset(); // Back to 0
1110
+ * @param {*} item
1111
+ * @param {string|null} defaultKey
1112
+ * @param {?Function} key
1113
+ * @returns {*}
843
1114
  */
844
- ObservableItem.prototype.reset = function() {
845
- if(!this.configs?.reset) {
846
- return;
1115
+ const getKey = (item, defaultKey, key) => {
1116
+ if (Validator.isString(key)) {
1117
+ const val = Validator.isObservable(item) ? item.val() : item;
1118
+ const result = val?.[key];
1119
+ return Validator.isObservable(result) ? result.val() : (result ?? defaultKey);
847
1120
  }
848
- const resetValue = (Validator.isObject(this.$initialValue))
849
- ? deepClone(this.$initialValue, (observable) => {
850
- observable.reset();
851
- })
852
- : this.$initialValue;
853
- this.set(resetValue);
854
- };
855
1121
 
856
- /**
857
- * Returns a string representation of the observable's current value.
858
- *
859
- * @returns {string} String representation of the current value
860
- */
861
- ObservableItem.prototype.toString = function() {
862
- return String(this.$currentValue);
863
- };
1122
+ if (Validator.isFunction(key)) {
1123
+ return key(item, defaultKey);
1124
+ }
864
1125
 
865
- /**
866
- * Returns the primitive value of the observable (its current value).
867
- * Called automatically in type coercion contexts.
868
- *
869
- * @returns {*} The current value of the observable
870
- */
871
- ObservableItem.prototype.valueOf = function() {
872
- return this.$currentValue;
1126
+ const val = Validator.isObservable(item) ? item.val() : item;
1127
+ return val ?? defaultKey;
873
1128
  };
874
1129
 
875
- const DocumentObserver = {
876
- mounted: new WeakMap(),
877
- mountedSupposedSize: 0,
878
- unmounted: new WeakMap(),
879
- unmountedSupposedSize: 0,
880
- observer: null,
881
- executeMountedCallback(node) {
882
- const data = DocumentObserver.mounted.get(node);
883
- if(!data) {
884
- return;
885
- }
886
- data.inDom = true;
887
- data.mounted && data.mounted(node);
888
- },
889
- executeUnmountedCallback(node) {
890
- const data = DocumentObserver.unmounted.get(node);
891
- if(!data) {
892
- return;
893
- }
894
-
895
- data.inDom = false;
896
- if(data.unmounted && data.unmounted(node) === true) {
897
- data.disconnect();
898
- node.nd?.remove();
899
- }
900
- },
901
- checkMutation: function(mutationsList) {
902
- for(const mutation of mutationsList) {
903
- if(DocumentObserver.mountedSupposedSize > 0 ) {
904
- for(const node of mutation.addedNodes) {
905
- DocumentObserver.executeMountedCallback(node);
906
- if(!node.querySelectorAll) {
907
- return;
908
- }
909
- const children = node.querySelectorAll('[data--nd-mounted]');
910
- if(!children.length) {
911
- return;
912
- }
913
- for(const node of children) {
914
- DocumentObserver.executeMountedCallback(node);
915
- }
916
- }
917
- }
1130
+ const trim = function(str, char) {
1131
+ return str.replace(new RegExp(`^[${char}]+|[${char}]+$`, 'g'), '');
1132
+ };
918
1133
 
919
- if(DocumentObserver.unmountedSupposedSize > 0 ) {
920
- for(const node of mutation.removedNodes) {
921
- DocumentObserver.executeUnmountedCallback(node);
922
- if(!node.querySelectorAll) {
923
- return;
924
- }
925
- const children = node.querySelectorAll('[data--nd-unmounted]');
926
- if(!children.length) {
927
- return;
928
- }
929
- for(const node of children) {
930
- DocumentObserver.executeUnmountedCallback(node);
931
- }
932
- }
933
- }
1134
+ const deepClone = (value, onObservableFound) => {
1135
+ try {
1136
+ if(window.structuredClone !== undefined) {
1137
+ return window.structuredClone(value);
934
1138
  }
935
- },
936
- /**
937
- *
938
- * @param {HTMLElement} element
939
- * @param {boolean} inDom
940
- * @returns {{watch: (function(): Map<any, any>), disconnect: (function(): boolean), mounted: (function(*): Set<any>), unmounted: (function(*): Set<any>)}}
941
- */
942
- watch: function(element, inDom = false) {
943
- let data = {
944
- inDom,
945
- mounted: null,
946
- unmounted: null,
947
- disconnect: () => {
948
- DocumentObserver.mounted.delete(element);
949
- DocumentObserver.unmounted.delete(element);
950
- DocumentObserver.mountedSupposedSize--;
951
- DocumentObserver.unmountedSupposedSize--;
952
- data = null;
953
- }
954
- };
1139
+ } catch (e){}
955
1140
 
956
- return {
957
- disconnect: data.disconnect,
958
- mounted: (callback) => {
959
- data.mounted = callback;
960
- DocumentObserver.mounted.set(element, data);
961
- DocumentObserver.mountedSupposedSize++;
962
- },
963
- unmounted: (callback) => {
964
- data.unmounted = callback;
965
- DocumentObserver.unmounted.set(element, data);
966
- DocumentObserver.unmountedSupposedSize++;
967
- }
968
- };
1141
+ if (value === null || typeof value !== 'object') {
1142
+ return value;
969
1143
  }
970
- };
971
1144
 
972
- DocumentObserver.observer = new MutationObserver(DocumentObserver.checkMutation);
973
- DocumentObserver.observer.observe(document.body, {
974
- childList: true,
975
- subtree: true,
976
- });
977
-
978
- function NDElement(element) {
979
- this.$element = element;
980
- this.$observer = null;
981
- {
982
- PluginsManager$1.emit('NDElementCreated', element, this);
1145
+ // Dates
1146
+ if (value instanceof Date) {
1147
+ return new Date(value.getTime());
983
1148
  }
984
- }
985
-
986
- NDElement.prototype.__$isNDElement = true;
987
1149
 
988
- NDElement.prototype.valueOf = function() {
989
- return this.$element;
990
- };
991
-
992
- NDElement.prototype.ref = function(target, name) {
993
- target[name] = this.$element;
994
- return this;
995
- };
1150
+ // Arrays
1151
+ if (Array.isArray(value)) {
1152
+ return value.map(item => deepClone(item));
1153
+ }
996
1154
 
997
- NDElement.prototype.refSelf = function(target, name) {
998
- target[name] = this;
999
- return this;
1000
- };
1155
+ // Observables - keep the référence
1156
+ if (Validator.isObservable(value)) {
1157
+ onObservableFound && onObservableFound(value);
1158
+ return value;
1159
+ }
1001
1160
 
1002
- NDElement.prototype.unmountChildren = function() {
1003
- let element = this.$element;
1004
- for(let i = 0, length = element.children.length; i < length; i++) {
1005
- let elementChildren = element.children[i];
1006
- if(!elementChildren.$ndProx) {
1007
- elementChildren.nd?.remove();
1161
+ // Objects
1162
+ const cloned = {};
1163
+ for (const key in value) {
1164
+ if (Object.hasOwn(value, key)) {
1165
+ cloned[key] = deepClone(value[key]);
1008
1166
  }
1009
- elementChildren = null;
1010
1167
  }
1011
- element = null;
1012
- return this;
1168
+ return cloned;
1013
1169
  };
1014
1170
 
1015
- NDElement.prototype.remove = function() {
1016
- let element = this.$element;
1017
- element.nd.unmountChildren();
1018
- element.$ndProx = null;
1019
- delete element.nd?.on?.prevent;
1020
- delete element.nd?.on;
1021
- delete element.nd;
1022
- element = null;
1023
- return this;
1171
+ const LocalStorage$1 = {
1172
+ getJson(key) {
1173
+ let value = localStorage.getItem(key);
1174
+ try {
1175
+ return JSON.parse(value);
1176
+ } catch (e) {
1177
+ throw new NativeDocumentError('invalid_json:'+key);
1178
+ }
1179
+ },
1180
+ getNumber(key) {
1181
+ return Number(this.get(key));
1182
+ },
1183
+ getBool(key) {
1184
+ const value = this.get(key);
1185
+ return value === 'true' || value === '1';
1186
+ },
1187
+ setJson(key, value) {
1188
+ localStorage.setItem(key, JSON.stringify(value));
1189
+ },
1190
+ setBool(key, value) {
1191
+ localStorage.setItem(key, value ? 'true' : 'false');
1192
+ },
1193
+ get(key, defaultValue = null) {
1194
+ return localStorage.getItem(key) || defaultValue;
1195
+ },
1196
+ set(key, value) {
1197
+ return localStorage.setItem(key, value);
1198
+ },
1199
+ remove(key) {
1200
+ localStorage.removeItem(key);
1201
+ },
1202
+ has(key) {
1203
+ return localStorage.getItem(key) != null;
1204
+ }
1024
1205
  };
1025
1206
 
1026
- NDElement.prototype.lifecycle = function(states) {
1027
- this.$observer = this.$observer || DocumentObserver.watch(this.$element);
1028
-
1029
- if(states.mounted) {
1030
- this.$element.setAttribute('data--nd-mounted', '1');
1031
- this.$observer.mounted(states.mounted);
1207
+ const $getFromStorage$1 = (key, value) => {
1208
+ if(!LocalStorage$1.has(key)) {
1209
+ return value;
1032
1210
  }
1033
- if(states.unmounted) {
1034
- this.$element.setAttribute('data--nd-unmounted', '1');
1035
- this.$observer.unmounted(states.unmounted);
1211
+ switch (typeof value) {
1212
+ case 'object': return LocalStorage$1.getJson(key) ?? value;
1213
+ case 'boolean': return LocalStorage$1.getBool(key) ?? value;
1214
+ case 'number': return LocalStorage$1.getNumber(key) ?? value;
1215
+ default: return LocalStorage$1.get(key, value) ?? value;
1036
1216
  }
1037
- return this;
1038
1217
  };
1039
1218
 
1040
- NDElement.prototype.mounted = function(callback) {
1041
- return this.lifecycle({ mounted: callback });
1219
+ const $saveToStorage$1 = (value) => {
1220
+ switch (typeof value) {
1221
+ case 'object': return LocalStorage$1.setJson;
1222
+ case 'boolean': return LocalStorage$1.setBool;
1223
+ default: return LocalStorage$1.set;
1224
+ }
1042
1225
  };
1043
1226
 
1044
- NDElement.prototype.unmounted = function(callback) {
1045
- return this.lifecycle({ unmounted: callback });
1046
- };
1227
+ const StoreFactory = function() {
1047
1228
 
1048
- NDElement.prototype.htmlElement = function() {
1049
- return this.$element;
1050
- };
1229
+ const $stores = new Map();
1230
+ const $followersCache = new Map();
1051
1231
 
1052
- NDElement.prototype.node = NDElement.prototype.htmlElement;
1232
+ /**
1233
+ * Internal helper — retrieves a store entry or throws if not found.
1234
+ */
1235
+ const $getStoreOrThrow = (method, name) => {
1236
+ const item = $stores.get(name);
1237
+ if (!item) {
1238
+ DebugManager.error('Store', `Store.${method}('${name}') : store not found. Did you call Store.create('${name}') first?`);
1239
+ throw new NativeDocumentError(
1240
+ `Store.${method}('${name}') : store not found.`
1241
+ );
1242
+ }
1243
+ return item;
1244
+ };
1053
1245
 
1054
- NDElement.prototype.shadow = function(mode, style = null) {
1055
- const $element = this.$element;
1056
- const children = Array.from($element.childNodes);
1057
- const shadowRoot = $element.attachShadow({ mode });
1058
- if(style) {
1059
- const styleNode = document.createElement("style");
1060
- styleNode.textContent = style;
1061
- shadowRoot.appendChild(styleNode);
1062
- }
1063
- $element.append = shadowRoot.append.bind(shadowRoot);
1064
- $element.appendChild = shadowRoot.appendChild.bind(shadowRoot);
1065
- shadowRoot.append(...children);
1246
+ /**
1247
+ * Internal helper — blocks write operations on a read-only observer.
1248
+ */
1249
+ const $applyReadOnly = (observer, name, context) => {
1250
+ const readOnlyError = (method) => () => {
1251
+ DebugManager.error('Store', `Store.${context}('${name}') is read-only. '${method}()' is not allowed.`);
1252
+ throw new NativeDocumentError(
1253
+ `Store.${context}('${name}') is read-only.`
1254
+ );
1255
+ };
1256
+ observer.set = readOnlyError('set');
1257
+ observer.toggle = readOnlyError('toggle');
1258
+ observer.reset = readOnlyError('reset');
1259
+ };
1066
1260
 
1067
- return this;
1068
- };
1261
+ const $createObservable = (value, options = {}) => {
1262
+ if(Array.isArray(value)) {
1263
+ return Observable$1.array(value, options);
1264
+ }
1265
+ if(typeof value === 'object') {
1266
+ return Observable$1.object(value, options);
1267
+ }
1268
+ return Observable$1(value, options);
1269
+ };
1069
1270
 
1070
- NDElement.prototype.openShadow = function(style = null) {
1071
- return this.shadow('open', style);
1072
- };
1271
+ const $api = {
1272
+ /**
1273
+ * Create a new state and return the observer.
1274
+ * Throws if a store with the same name already exists.
1275
+ *
1276
+ * @param {string} name
1277
+ * @param {*} value
1278
+ * @returns {ObservableItem}
1279
+ */
1280
+ create(name, value) {
1281
+ if ($stores.has(name)) {
1282
+ DebugManager.warn('Store', `Store.create('${name}') : a store with this name already exists. Use Store.get('${name}') to retrieve it.`);
1283
+ throw new NativeDocumentError(
1284
+ `Store.create('${name}') : a store with this name already exists.`
1285
+ );
1286
+ }
1287
+ const observer = $createObservable(value);
1288
+ $stores.set(name, { observer, subscribers: new Set(), resettable: false, composed: false });
1289
+ return observer;
1290
+ },
1073
1291
 
1074
- NDElement.prototype.closedShadow = function(style = null) {
1075
- return this.shadow('closed', style);
1076
- };
1292
+ /**
1293
+ * Create a new resettable state and return the observer.
1294
+ * The store can be reset to its initial value via Store.reset(name).
1295
+ * Throws if a store with the same name already exists.
1296
+ *
1297
+ * @param {string} name
1298
+ * @param {*} value
1299
+ * @returns {ObservableItem}
1300
+ */
1301
+ createResettable(name, value) {
1302
+ if ($stores.has(name)) {
1303
+ DebugManager.warn('Store', `Store.createResettable('${name}') : a store with this name already exists.`);
1304
+ throw new NativeDocumentError(
1305
+ `Store.createResettable('${name}') : a store with this name already exists.`
1306
+ );
1307
+ }
1308
+ const observer = $createObservable(value, { reset: true });
1309
+ $stores.set(name, { observer, subscribers: new Set(), resettable: true, composed: false });
1310
+ return observer;
1311
+ },
1077
1312
 
1078
- /**
1079
- * Attaches a template binding to the element by hydrating it with the specified method.
1080
- *
1081
- * @param {string} methodName - Name of the hydration method to call
1082
- * @param {BindingHydrator} bindingHydrator - Template binding with $hydrate method
1083
- * @returns {HTMLElement} The underlying HTML element
1084
- * @example
1085
- * const onClick = $binder.attach((event, data) => console.log(data));
1086
- * element.nd.attach('onClick', onClick);
1087
- */
1088
- NDElement.prototype.attach = function(methodName, bindingHydrator) {
1089
- bindingHydrator.$hydrate(this.$element, methodName);
1090
- return this.$element;
1091
- };
1313
+ /**
1314
+ * Create a computed store derived from other stores.
1315
+ * The value is automatically recalculated when any dependency changes.
1316
+ * This store is read-only Store.use() and Store.set() will throw.
1317
+ * Throws if a store with the same name already exists.
1318
+ *
1319
+ * @param {string} name
1320
+ * @param {() => *} computation - Function that returns the computed value
1321
+ * @param {string[]} dependencies - Names of the stores to watch
1322
+ * @returns {ObservableItem}
1323
+ *
1324
+ * @example
1325
+ * Store.create('products', [{ id: 1, price: 10 }]);
1326
+ * Store.create('cart', [{ productId: 1, quantity: 2 }]);
1327
+ *
1328
+ * Store.createComposed('total', () => {
1329
+ * const products = Store.get('products').val();
1330
+ * const cart = Store.get('cart').val();
1331
+ * return cart.reduce((sum, item) => {
1332
+ * const product = products.find(p => p.id === item.productId);
1333
+ * return sum + (product.price * item.quantity);
1334
+ * }, 0);
1335
+ * }, ['products', 'cart']);
1336
+ */
1337
+ createComposed(name, computation, dependencies) {
1338
+ if ($stores.has(name)) {
1339
+ DebugManager.warn('Store', `Store.createComposed('${name}') : a store with this name already exists.`);
1340
+ throw new NativeDocumentError(
1341
+ `Store.createComposed('${name}') : a store with this name already exists.`
1342
+ );
1343
+ }
1344
+ if (typeof computation !== 'function') {
1345
+ throw new NativeDocumentError(
1346
+ `Store.createComposed('${name}') : computation must be a function.`
1347
+ );
1348
+ }
1349
+ if (!Array.isArray(dependencies) || dependencies.length === 0) {
1350
+ throw new NativeDocumentError(
1351
+ `Store.createComposed('${name}') : dependencies must be a non-empty array of store names.`
1352
+ );
1353
+ }
1092
1354
 
1093
- /**
1094
- * Extends the current NDElement instance with custom methods.
1095
- * Methods are bound to the instance and available for chaining.
1096
- *
1097
- * @param {Object} methods - Object containing method definitions
1098
- * @returns {this} The NDElement instance with added methods for chaining
1099
- * @example
1100
- * element.nd.with({
1101
- * highlight() {
1102
- * this.$element.style.background = 'yellow';
1103
- * return this;
1104
- * }
1105
- * }).highlight().onClick(() => console.log('Clicked'));
1106
- */
1107
- NDElement.prototype.with = function(methods) {
1108
- if (!methods || typeof methods !== 'object') {
1109
- throw new NativeDocumentError('extend() requires an object of methods');
1110
- }
1111
- {
1112
- if (!this.$localExtensions) {
1113
- this.$localExtensions = new Map();
1114
- }
1115
- }
1355
+ // Resolve dependency observers
1356
+ const depObservers = dependencies.map(depName => {
1357
+ if(typeof depName !== 'string') {
1358
+ return depName;
1359
+ }
1360
+ const depItem = $stores.get(depName);
1361
+ if (!depItem) {
1362
+ DebugManager.error('Store', `Store.createComposed('${name}') : dependency '${depName}' not found. Create it first.`);
1363
+ throw new NativeDocumentError(
1364
+ `Store.createComposed('${name}') : dependency store '${depName}' not found.`
1365
+ );
1366
+ }
1367
+ return depItem.observer;
1368
+ });
1116
1369
 
1117
- for (const name in methods) {
1118
- const method = methods[name];
1370
+ // Create computed observable from dependency observers
1371
+ const observer = Observable$1.computed(computation, depObservers);
1119
1372
 
1120
- if (typeof method !== 'function') {
1121
- console.warn(`⚠️ extends(): "${name}" is not a function, skipping`);
1122
- continue;
1123
- }
1124
- {
1125
- if (this[name] && !this.$localExtensions.has(name)) {
1126
- DebugManager$1.warn('NDElement.extend', `Method "${name}" already exists and will be overwritten`);
1373
+ $stores.set(name, { observer, subscribers: new Set(), resettable: false, composed: true });
1374
+ return observer;
1375
+ },
1376
+
1377
+ /**
1378
+ * Returns true if a store with the given name exists.
1379
+ *
1380
+ * @param {string} name
1381
+ * @returns {boolean}
1382
+ */
1383
+ has(name) {
1384
+ return $stores.has(name);
1385
+ },
1386
+
1387
+ /**
1388
+ * Resets a resettable store to its initial value and notifies all subscribers.
1389
+ * Throws if the store was not created with createResettable().
1390
+ *
1391
+ * @param {string} name
1392
+ */
1393
+ reset(name) {
1394
+ const item = $getStoreOrThrow('reset', name);
1395
+ if (item.composed) {
1396
+ DebugManager.error('Store', `Store.reset('${name}') : composed stores cannot be reset. Their value is derived from dependencies.`);
1397
+ throw new NativeDocumentError(
1398
+ `Store.reset('${name}') : composed stores cannot be reset.`
1399
+ );
1127
1400
  }
1128
- this.$localExtensions.set(name, method);
1129
- }
1401
+ if (!item.resettable) {
1402
+ DebugManager.error('Store', `Store.reset('${name}') : this store is not resettable. Use Store.createResettable('${name}', value) instead of Store.create().`);
1403
+ throw new NativeDocumentError(
1404
+ `Store.reset('${name}') : this store is not resettable. Use Store.createResettable('${name}', value) instead of Store.create().`
1405
+ );
1406
+ }
1407
+ item.observer.reset();
1408
+ },
1130
1409
 
1131
- this[name] = method.bind(this);
1132
- }
1410
+ /**
1411
+ * Returns a two-way synchronized follower of the store.
1412
+ * Writing to the follower propagates the value back to the store and all its subscribers.
1413
+ * Throws if called on a composed store — use Store.follow() instead.
1414
+ * Call follower.destroy() or follower.dispose() to unsubscribe.
1415
+ *
1416
+ * @param {string} name
1417
+ * @returns {ObservableItem}
1418
+ */
1419
+ use(name) {
1420
+ const item = $getStoreOrThrow('use', name);
1133
1421
 
1134
- return this;
1135
- };
1422
+ if (item.composed) {
1423
+ DebugManager.error('Store', `Store.use('${name}') : composed stores are read-only. Use Store.follow('${name}') instead.`);
1424
+ throw new NativeDocumentError(
1425
+ `Store.use('${name}') : composed stores are read-only. Use Store.follow('${name}') instead.`
1426
+ );
1427
+ }
1136
1428
 
1137
- /**
1138
- * Extends the NDElement prototype with new methods available to all NDElement instances.
1139
- * Use this to add global methods to all NDElements.
1140
- *
1141
- * @param {Object} methods - Object containing method definitions to add to prototype
1142
- * @returns {typeof NDElement} The NDElement constructor
1143
- * @throws {NativeDocumentError} If methods is not an object or contains non-function values
1144
- * @example
1145
- * NDElement.extend({
1146
- * fadeIn() {
1147
- * this.$element.style.opacity = '1';
1148
- * return this;
1149
- * }
1150
- * });
1151
- * // Now all NDElements have .fadeIn() method
1152
- * Div().nd.fadeIn();
1153
- */
1154
- NDElement.extend = function(methods) {
1155
- if (!methods || typeof methods !== 'object') {
1156
- throw new NativeDocumentError('NDElement.extend() requires an object of methods');
1157
- }
1429
+ const { observer: originalObserver, subscribers } = item;
1430
+ const observerFollower = $createObservable(originalObserver.val());
1158
1431
 
1159
- if (Array.isArray(methods)) {
1160
- throw new NativeDocumentError('NDElement.extend() requires an object, not an array');
1161
- }
1432
+ const onStoreChange = value => observerFollower.set(value);
1433
+ const onFollowerChange = value => originalObserver.set(value);
1162
1434
 
1163
- const protectedMethods = new Set([
1164
- 'constructor', 'valueOf', '$element', '$observer',
1165
- 'ref', 'remove', 'cleanup', 'with', 'extend', 'attach',
1166
- 'lifecycle', 'mounted', 'unmounted', 'unmountChildren'
1167
- ]);
1435
+ originalObserver.subscribe(onStoreChange);
1436
+ observerFollower.subscribe(onFollowerChange);
1437
+
1438
+ observerFollower.destroy = () => {
1439
+ originalObserver.unsubscribe(onStoreChange);
1440
+ observerFollower.unsubscribe(onFollowerChange);
1441
+ subscribers.delete(observerFollower);
1442
+ observerFollower.cleanup();
1443
+ };
1444
+ observerFollower.dispose = observerFollower.destroy;
1445
+
1446
+ subscribers.add(observerFollower);
1447
+ return observerFollower;
1448
+ },
1449
+
1450
+ /**
1451
+ * Returns a read-only follower of the store.
1452
+ * The follower reflects store changes but cannot write back to the store.
1453
+ * Any attempt to call .set(), .toggle() or .reset() will throw.
1454
+ * Call follower.destroy() or follower.dispose() to unsubscribe.
1455
+ *
1456
+ * @param {string} name
1457
+ * @returns {ObservableItem}
1458
+ */
1459
+ follow(name) {
1460
+ const { observer: originalObserver, subscribers } = $getStoreOrThrow('follow', name);
1461
+ const observerFollower = $createObservable(originalObserver.val());
1462
+
1463
+ const onStoreChange = value => observerFollower.set(value);
1464
+ originalObserver.subscribe(onStoreChange);
1465
+
1466
+ $applyReadOnly(observerFollower, name, 'follow');
1467
+
1468
+ observerFollower.destroy = () => {
1469
+ originalObserver.unsubscribe(onStoreChange);
1470
+ subscribers.delete(observerFollower);
1471
+ observerFollower.cleanup();
1472
+ };
1473
+ observerFollower.dispose = observerFollower.destroy;
1474
+
1475
+ subscribers.add(observerFollower);
1476
+ return observerFollower;
1477
+ },
1478
+
1479
+ /**
1480
+ * Returns the raw store observer directly (no follower, no cleanup contract).
1481
+ * Use this for direct read access when you don't need to unsubscribe.
1482
+ * WARNING : mutations on this observer impact all subscribers immediately.
1483
+ *
1484
+ * @param {string} name
1485
+ * @returns {ObservableItem|null}
1486
+ */
1487
+ get(name) {
1488
+ const item = $stores.get(name);
1489
+ if (!item) {
1490
+ DebugManager.warn('Store', `Store.get('${name}') : store not found.`);
1491
+ return null;
1492
+ }
1493
+ return item.observer;
1494
+ },
1495
+
1496
+ /**
1497
+ * @param {string} name
1498
+ * @returns {{ observer: ObservableItem, subscribers: Set } | null}
1499
+ */
1500
+ getWithSubscribers(name) {
1501
+ return $stores.get(name) ?? null;
1502
+ },
1503
+
1504
+ /**
1505
+ * Destroys a store : cleans up the observer, destroys all followers, and removes the entry.
1506
+ *
1507
+ * @param {string} name
1508
+ */
1509
+ delete(name) {
1510
+ const item = $stores.get(name);
1511
+ if (!item) {
1512
+ DebugManager.warn('Store', `Store.delete('${name}') : store not found, nothing to delete.`);
1513
+ return;
1514
+ }
1515
+ item.subscribers.forEach(follower => follower.destroy());
1516
+ item.subscribers.clear();
1517
+ item.observer.cleanup();
1518
+ $stores.delete(name);
1519
+ },
1520
+ /**
1521
+ * Creates an isolated store group with its own state namespace.
1522
+ * Each group is a fully independent StoreFactory instance —
1523
+ * no key conflicts, no shared state with the parent store.
1524
+ *
1525
+ * @param {string | ((group: ReturnType<typeof StoreFactory>) => void)} name - Group name for debugging, or setup callback if no name is provided
1526
+ * @param {((group: ReturnType<typeof StoreFactory>) => void)} [callback] - Setup function receiving the isolated store instance
1527
+ * @returns {ReturnType<typeof StoreFactory>}
1528
+ *
1529
+ * @example
1530
+ * // With name (recommended)
1531
+ * const EventStore = Store.group('events', (group) => {
1532
+ * group.create('catalog', []);
1533
+ * group.create('filters', { category: null, date: null });
1534
+ * group.createResettable('selected', null);
1535
+ * group.createComposed('filtered', () => {
1536
+ * const catalog = EventStore.get('catalog').val();
1537
+ * const filters = EventStore.get('filters').val();
1538
+ * return catalog.filter(event => {
1539
+ * if (filters.category && event.category !== filters.category) return false;
1540
+ * return true;
1541
+ * });
1542
+ * }, ['catalog', 'filters']);
1543
+ * });
1544
+ *
1545
+ * // Without name
1546
+ * const CartStore = Store.group((group) => {
1547
+ * group.create('items', []);
1548
+ * });
1549
+ *
1550
+ * // Usage
1551
+ * EventStore.use('catalog'); // two-way follower
1552
+ * EventStore.follow('filtered'); // read-only follower
1553
+ * EventStore.get('filters'); // raw observable
1554
+ *
1555
+ * // Cross-group composed
1556
+ * const OrderStore = Store.group('orders', (group) => {
1557
+ * group.createComposed('summary', () => {
1558
+ * const items = CartStore.get('items').val();
1559
+ * const events = EventStore.get('catalog').val();
1560
+ * return { items, events };
1561
+ * }, [CartStore.get('items'), EventStore.get('catalog')]);
1562
+ * });
1563
+ */
1564
+ group(name, callback) {
1565
+ if (typeof name === 'function') {
1566
+ callback = name;
1567
+ name = 'anonymous';
1568
+ }
1569
+ const store = StoreFactory();
1570
+ callback && callback(store);
1571
+ return store;
1572
+ },
1573
+ createPersistent(name, value, localstorage_key) {
1574
+ localstorage_key = localstorage_key || name;
1575
+ const observer = this.create(name, $getFromStorage$1(localstorage_key, value));
1576
+ const saver = $saveToStorage$1(value);
1577
+
1578
+ observer.subscribe((val) => saver(localstorage_key, val));
1579
+ return observer;
1580
+ },
1581
+ createPersistentResettable(name, value, localstorage_key) {
1582
+ localstorage_key = localstorage_key || name;
1583
+ const observer = this.createResettable(name, $getFromStorage$1(localstorage_key, value));
1584
+ const saver = $saveToStorage$1(value);
1585
+ observer.subscribe((val) => saver(localstorage_key, val));
1586
+
1587
+ const originalReset = observer.reset.bind(observer);
1588
+ observer.reset = () => {
1589
+ LocalStorage$1.remove(localstorage_key);
1590
+ originalReset();
1591
+ };
1168
1592
 
1169
- for (const name in methods) {
1170
- if (!methods.hasOwnProperty(name)) {
1171
- continue;
1593
+ return observer;
1172
1594
  }
1595
+ };
1173
1596
 
1174
- const method = methods[name];
1175
1597
 
1176
- if (typeof method !== 'function') {
1177
- DebugManager$1.warn('NDElement.extend', `"${name}" is not a function, skipping`);
1178
- continue;
1598
+ return new Proxy($api, {
1599
+ get(target, prop) {
1600
+ if (typeof prop === 'symbol' || prop.startsWith('$') || prop in target) {
1601
+ return target[prop];
1602
+ }
1603
+ if (target.has(prop)) {
1604
+ if ($followersCache.has(prop)) {
1605
+ return $followersCache.get(prop);
1606
+ }
1607
+ const follower = target.follow(prop);
1608
+ $followersCache.set(prop, follower);
1609
+ return follower;
1610
+ }
1611
+ return undefined;
1612
+ },
1613
+ set(target, prop, value) {
1614
+ DebugManager.error('Store', `Forbidden: You cannot overwrite the store key '${String(prop)}'. Use .use('${String(prop)}').set(value) instead.`);
1615
+ throw new NativeDocumentError(`Store structure is immutable. Use .set() on the observable.`);
1616
+ },
1617
+ deleteProperty(target, prop) {
1618
+ throw new NativeDocumentError(`Store keys cannot be deleted.`);
1179
1619
  }
1620
+ });
1621
+ };
1180
1622
 
1181
- if (protectedMethods.has(name)) {
1182
- DebugManager$1.error('NDElement.extend', `Cannot override protected method "${name}"`);
1183
- throw new NativeDocumentError(`Cannot override protected method "${name}"`);
1623
+ const Store = StoreFactory();
1624
+
1625
+ Store.create('locale', 'fr');
1626
+
1627
+ const $parseDateParts = (value, locale) => {
1628
+ const d = new Date(value);
1629
+ return {
1630
+ d,
1631
+ parts: new Intl.DateTimeFormat(locale, {
1632
+ year: 'numeric',
1633
+ month: 'long',
1634
+ day: '2-digit',
1635
+ hour: '2-digit',
1636
+ minute: '2-digit',
1637
+ second: '2-digit',
1638
+ }).formatToParts(d).reduce((acc, { type, value }) => {
1639
+ acc[type] = value;
1640
+ return acc;
1641
+ }, {})
1642
+ };
1643
+ };
1644
+
1645
+ const $applyDatePattern = (pattern, d, parts) => {
1646
+ const pad = n => String(n).padStart(2, '0');
1647
+ return pattern
1648
+ .replace('YYYY', parts.year)
1649
+ .replace('YY', parts.year.slice(-2))
1650
+ .replace('MMMM', parts.month)
1651
+ .replace('MMM', parts.month.slice(0, 3))
1652
+ .replace('MM', pad(d.getMonth() + 1))
1653
+ .replace('DD', pad(d.getDate()))
1654
+ .replace('D', d.getDate())
1655
+ .replace('HH', parts.hour)
1656
+ .replace('mm', parts.minute)
1657
+ .replace('ss', parts.second);
1658
+ };
1659
+
1660
+ const Formatters = {
1661
+
1662
+ currency: (value, locale, { currency = 'XOF', notation, minimumFractionDigits, maximumFractionDigits } = {}) =>
1663
+ new Intl.NumberFormat(locale, {
1664
+ style: 'currency',
1665
+ currency,
1666
+ notation,
1667
+ minimumFractionDigits,
1668
+ maximumFractionDigits
1669
+ }).format(value),
1670
+
1671
+ number: (value, locale, { notation, minimumFractionDigits, maximumFractionDigits } = {}) =>
1672
+ new Intl.NumberFormat(locale, {
1673
+ notation,
1674
+ minimumFractionDigits,
1675
+ maximumFractionDigits
1676
+ }).format(value),
1677
+
1678
+ percent: (value, locale, { decimals = 1 } = {}) =>
1679
+ new Intl.NumberFormat(locale, {
1680
+ style: 'percent',
1681
+ maximumFractionDigits: decimals
1682
+ }).format(value),
1683
+
1684
+ date: (value, locale, { format, dateStyle = 'long' } = {}) => {
1685
+ if (format) {
1686
+ const { d, parts } = $parseDateParts(value, locale);
1687
+ return $applyDatePattern(format, d, parts);
1688
+ }
1689
+ return new Intl.DateTimeFormat(locale, { dateStyle }).format(new Date(value));
1690
+ },
1691
+
1692
+ time: (value, locale, { format, hour = '2-digit', minute = '2-digit', second } = {}) => {
1693
+ if (format) {
1694
+ const { d, parts } = $parseDateParts(value, locale);
1695
+ return $applyDatePattern(format, d, parts);
1184
1696
  }
1697
+ return new Intl.DateTimeFormat(locale, { hour, minute, second }).format(new Date(value));
1698
+ },
1185
1699
 
1186
- if (NDElement.prototype[name]) {
1187
- DebugManager$1.warn('NDElement.extend', `Overwriting existing prototype method "${name}"`);
1700
+ datetime: (value, locale, { format, dateStyle = 'long', hour = '2-digit', minute = '2-digit', second } = {}) => {
1701
+ if (format) {
1702
+ const { d, parts } = $parseDateParts(value, locale);
1703
+ return $applyDatePattern(format, d, parts);
1188
1704
  }
1705
+ return new Intl.DateTimeFormat(locale, { dateStyle, hour, minute, second }).format(new Date(value));
1706
+ },
1189
1707
 
1190
- NDElement.prototype[name] = method;
1708
+ relative: (value, locale, { unit = 'day', numeric = 'auto' } = {}) => {
1709
+ const diff = Math.round((value - Date.now()) / (1000 * 60 * 60 * 24));
1710
+ return new Intl.RelativeTimeFormat(locale, { numeric }).format(diff, unit);
1711
+ },
1712
+
1713
+ plural: (value, locale, { singular, plural } = {}) => {
1714
+ const rule = new Intl.PluralRules(locale).select(value);
1715
+ return `${value} ${rule === 'one' ? singular : plural}`;
1716
+ },
1717
+ };
1718
+
1719
+ /**
1720
+ *
1721
+ * @param {*} value
1722
+ * @param {{ propagation: boolean, reset: boolean} | null} configs
1723
+ * @class ObservableItem
1724
+ */
1725
+ function ObservableItem(value, configs = null) {
1726
+ value = Validator.isObservable(value) ? value.val() : value;
1727
+
1728
+ this.$previousValue = null;
1729
+ this.$currentValue = value;
1730
+ {
1731
+ this.$isCleanedUp = false;
1732
+ }
1733
+
1734
+ this.$firstListener = null;
1735
+ this.$listeners = null;
1736
+ this.$watchers = null;
1737
+
1738
+ this.$memoryId = null;
1739
+
1740
+ if(configs) {
1741
+ this.configs = configs;
1742
+ if(configs.reset) {
1743
+ this.$initialValue = Validator.isObject(value) ? deepClone(value) : value;
1744
+ }
1191
1745
  }
1192
1746
  {
1193
- PluginsManager$1.emit('NDElementExtended', methods);
1747
+ PluginsManager.emit('CreateObservable', this);
1194
1748
  }
1749
+ }
1195
1750
 
1196
- return NDElement;
1751
+ Object.defineProperty(ObservableItem.prototype, '$value', {
1752
+ get() {
1753
+ return this.$currentValue;
1754
+ },
1755
+ set(value) {
1756
+ this.set(value);
1757
+ },
1758
+ configurable: true,
1759
+ });
1760
+
1761
+ ObservableItem.prototype.__$isObservable = true;
1762
+ const noneTrigger = function() {};
1763
+
1764
+ /**
1765
+ * Intercepts and transforms values before they are set on the observable.
1766
+ * The interceptor can modify the value or return undefined to use the original value.
1767
+ *
1768
+ * @param {(value) => any} callback - Interceptor function that receives (newValue, currentValue) and returns the transformed value or undefined
1769
+ * @returns {ObservableItem} The observable instance for chaining
1770
+ * @example
1771
+ * const count = Observable(0);
1772
+ * count.intercept((newVal, oldVal) => Math.max(0, newVal)); // Prevent negative values
1773
+ */
1774
+ ObservableItem.prototype.intercept = function(callback) {
1775
+ this.$interceptor = callback;
1776
+ this.set = this.$setWithInterceptor;
1777
+ return this;
1197
1778
  };
1198
1779
 
1199
- function TemplateBinding(hydrate) {
1200
- this.$hydrate = hydrate;
1201
- }
1780
+ ObservableItem.prototype.triggerFirstListener = function(operations) {
1781
+ this.$firstListener(this.$currentValue, this.$previousValue, operations);
1782
+ };
1202
1783
 
1203
- TemplateBinding.prototype.__$isTemplateBinding = true;
1784
+ ObservableItem.prototype.triggerListeners = function(operations) {
1785
+ const $listeners = this.$listeners;
1786
+ const $previousValue = this.$previousValue;
1787
+ const $currentValue = this.$currentValue;
1204
1788
 
1205
- const COMMON_NODE_TYPES = {
1206
- ELEMENT: 1,
1207
- TEXT: 3,
1208
- COMMENT: 8,
1209
- DOCUMENT_FRAGMENT: 11
1789
+ for(let i = 0, length = $listeners.length; i < length; i++) {
1790
+ $listeners[i]($currentValue, $previousValue, operations);
1791
+ }
1210
1792
  };
1211
1793
 
1212
- const Validator = {
1213
- isObservable(value) {
1214
- return value?.__$isObservable;
1215
- },
1216
- isTemplateBinding(value) {
1217
- return value?.__$isTemplateBinding;
1218
- },
1219
- isObservableWhenResult(value) {
1220
- return value && (value.__$isObservableWhen || (typeof value === 'object' && '$target' in value && '$observer' in value));
1221
- },
1222
- isArrayObservable(value) {
1223
- return value?.__$isObservableArray;
1224
- },
1225
- isProxy(value) {
1226
- return value?.__isProxy__
1227
- },
1228
- isObservableOrProxy(value) {
1229
- return Validator.isObservable(value) || Validator.isProxy(value);
1230
- },
1231
- isAnchor(value) {
1232
- return value?.__Anchor__
1233
- },
1234
- isObservableChecker(value) {
1235
- return value?.__$isObservableChecker || value instanceof ObservableChecker;
1236
- },
1237
- isArray(value) {
1238
- return Array.isArray(value);
1239
- },
1240
- isString(value) {
1241
- return typeof value === 'string';
1242
- },
1243
- isNumber(value) {
1244
- return typeof value === 'number';
1245
- },
1246
- isBoolean(value) {
1247
- return typeof value === 'boolean';
1248
- },
1249
- isFunction(value) {
1250
- return typeof value === 'function';
1251
- },
1252
- isAsyncFunction(value) {
1253
- return typeof value === 'function' && value.constructor.name === 'AsyncFunction';
1254
- },
1255
- isObject(value) {
1256
- return typeof value === 'object' && value !== null;
1257
- },
1258
- isJson(value) {
1259
- return !(typeof value !== 'object' || value === null || Array.isArray(value) || value.constructor.name !== 'Object')
1260
- },
1261
- isElement(value) {
1262
- return value && (
1263
- value.nodeType === COMMON_NODE_TYPES.ELEMENT ||
1264
- value.nodeType === COMMON_NODE_TYPES.TEXT ||
1265
- value.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT ||
1266
- value.nodeType === COMMON_NODE_TYPES.COMMENT
1267
- );
1268
- },
1269
- isFragment(value) {
1270
- return value?.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT;
1271
- },
1272
- isStringOrObservable(value) {
1273
- return this.isString(value) || this.isObservable(value);
1274
- },
1275
- isValidChild(child) {
1276
- return child === null ||
1277
- this.isElement(child) ||
1278
- this.isObservable(child) ||
1279
- this.isNDElement(child) ||
1280
- ['string', 'number', 'boolean'].includes(typeof child);
1281
- },
1282
- isNDElement(child) {
1283
- return child?.__$isNDElement || child instanceof NDElement;
1284
- },
1285
- isValidChildren(children) {
1286
- if (!Array.isArray(children)) {
1287
- children = [children];
1288
- }
1794
+ ObservableItem.prototype.triggerWatchers = function(operations) {
1795
+ const $watchers = this.$watchers;
1796
+ const $previousValue = this.$previousValue;
1797
+ const $currentValue = this.$currentValue;
1289
1798
 
1290
- const invalid = children.filter(child => !this.isValidChild(child));
1291
- return invalid.length === 0;
1292
- },
1293
- validateChildren(children) {
1294
- if (!Array.isArray(children)) {
1295
- children = [children];
1296
- }
1799
+ const $currentValueCallbacks = $watchers.get($currentValue);
1800
+ const $previousValueCallbacks = $watchers.get($previousValue);
1801
+ if($currentValueCallbacks) {
1802
+ $currentValueCallbacks(true, $previousValue, operations);
1803
+ }
1804
+ if($previousValueCallbacks) {
1805
+ $previousValueCallbacks(false, $currentValue, operations);
1806
+ }
1807
+ };
1297
1808
 
1298
- const invalid = children.filter(child => !this.isValidChild(child));
1299
- if (invalid.length > 0) {
1300
- throw new NativeDocumentError(`Invalid children detected: ${invalid.map(i => typeof i).join(', ')}`);
1301
- }
1809
+ ObservableItem.prototype.triggerAll = function(operations) {
1810
+ this.triggerWatchers(operations);
1811
+ this.triggerListeners(operations);
1812
+ };
1302
1813
 
1303
- return children;
1304
- },
1305
- /**
1306
- * Check if the data contains observables.
1307
- * @param {Array|Object} data
1308
- * @returns {boolean}
1309
- */
1310
- containsObservables(data) {
1311
- if(!data) {
1312
- return false;
1313
- }
1314
- return Validator.isObject(data)
1315
- && Object.values(data).some(value => Validator.isObservable(value));
1316
- },
1317
- /**
1318
- * Check if the data contains an observable reference.
1319
- * @param {string} data
1320
- * @returns {boolean}
1321
- */
1322
- containsObservableReference(data) {
1323
- if(!data || typeof data !== 'string') {
1324
- return false;
1325
- }
1326
- return /\{\{#ObItem::\([0-9]+\)\}\}/.test(data);
1327
- },
1328
- validateAttributes(attributes) {},
1814
+ ObservableItem.prototype.triggerWatchersAndFirstListener = function(operations) {
1815
+ this.triggerWatchers(operations);
1816
+ this.triggerFirstListener(operations);
1817
+ };
1329
1818
 
1330
- validateEventCallback(callback) {
1331
- if (typeof callback !== 'function') {
1332
- throw new NativeDocumentError('Event callback must be a function');
1819
+ ObservableItem.prototype.assocTrigger = function() {
1820
+ this.$firstListener = null;
1821
+ if(this.$watchers?.size && this.$listeners?.length) {
1822
+ this.trigger = (this.$listeners.length === 1) ? this.triggerWatchersAndFirstListener : this.triggerAll;
1823
+ return;
1824
+ }
1825
+ if(this.$listeners?.length) {
1826
+ if(this.$listeners.length === 1) {
1827
+ this.$firstListener = this.$listeners[0];
1828
+ this.trigger = this.triggerFirstListener;
1829
+ }
1830
+ else {
1831
+ this.trigger = this.triggerListeners;
1333
1832
  }
1833
+ return;
1834
+ }
1835
+ if(this.$watchers?.size) {
1836
+ this.trigger = this.triggerWatchers;
1837
+ return;
1334
1838
  }
1839
+ this.trigger = noneTrigger;
1335
1840
  };
1336
- {
1337
- Validator.validateAttributes = function(attributes) {
1338
- if (!attributes || typeof attributes !== 'object') {
1339
- return attributes;
1340
- }
1341
-
1342
- const reserved = [];
1343
- const foundReserved = Object.keys(attributes).filter(key => reserved.includes(key));
1841
+ ObservableItem.prototype.trigger = noneTrigger;
1344
1842
 
1345
- if (foundReserved.length > 0) {
1346
- DebugManager$1.warn('Validator', `Reserved attributes found: ${foundReserved.join(', ')}`);
1347
- }
1843
+ ObservableItem.prototype.$updateWithNewValue = function(newValue) {
1844
+ newValue = newValue?.__$isObservable ? newValue.val() : newValue;
1845
+ if(this.$currentValue === newValue) {
1846
+ return;
1847
+ }
1848
+ this.$previousValue = this.$currentValue;
1849
+ this.$currentValue = newValue;
1850
+ {
1851
+ PluginsManager.emit('ObservableBeforeChange', this);
1852
+ }
1853
+ this.trigger();
1854
+ this.$previousValue = null;
1855
+ {
1856
+ PluginsManager.emit('ObservableAfterChange', this);
1857
+ }
1858
+ };
1348
1859
 
1349
- return attributes;
1350
- };
1351
- }
1860
+ /**
1861
+ * @param {*} data
1862
+ */
1863
+ ObservableItem.prototype.$setWithInterceptor = function(data) {
1864
+ let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
1865
+ const result = this.$interceptor(newValue, this.$currentValue);
1352
1866
 
1353
- function Anchor(name, isUniqueChild = false) {
1354
- const anchorFragment = document.createDocumentFragment();
1355
- anchorFragment.__Anchor__ = true;
1867
+ if (result !== undefined) {
1868
+ newValue = result;
1869
+ }
1356
1870
 
1357
- const anchorStart = document.createComment('Anchor Start : '+name);
1358
- const anchorEnd = document.createComment('/ Anchor End '+name);
1871
+ this.$updateWithNewValue(newValue);
1872
+ };
1359
1873
 
1360
- anchorFragment.appendChild(anchorStart);
1361
- anchorFragment.appendChild(anchorEnd);
1874
+ /**
1875
+ * @param {*} data
1876
+ */
1877
+ ObservableItem.prototype.$basicSet = function(data) {
1878
+ let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
1879
+ this.$updateWithNewValue(newValue);
1880
+ };
1362
1881
 
1363
- anchorFragment.nativeInsertBefore = anchorFragment.insertBefore;
1364
- anchorFragment.nativeAppendChild = anchorFragment.appendChild;
1882
+ ObservableItem.prototype.set = ObservableItem.prototype.$basicSet;
1365
1883
 
1366
- const isParentUniqueChild = (parent) => (isUniqueChild || (parent.firstChild === anchorStart && parent.lastChild === anchorEnd));
1884
+ ObservableItem.prototype.val = function() {
1885
+ return this.$currentValue;
1886
+ };
1367
1887
 
1368
- const insertBefore = function(parent, child, target) {
1369
- const childElement = Validator.isElement(child) ? child : ElementCreator.getChild(child);
1370
- if(parent === anchorFragment) {
1371
- parent.nativeInsertBefore(childElement, target);
1372
- return;
1373
- }
1374
- if(isParentUniqueChild(parent) && target === anchorEnd) {
1375
- parent.append(childElement, target);
1376
- return;
1888
+ ObservableItem.prototype.disconnectAll = function() {
1889
+ this.$listeners?.splice(0);
1890
+ this.$previousValue = null;
1891
+ this.$currentValue = null;
1892
+ if(this.$watchers) {
1893
+ for (const [_, watchValueList] of this.$watchers) {
1894
+ if(Validator.isArray(watchValueList)) {
1895
+ watchValueList.splice(0);
1896
+ }
1377
1897
  }
1378
- parent.insertBefore(childElement, target);
1379
- };
1898
+ }
1899
+ this.$watchers?.clear();
1900
+ this.$listeners = null;
1901
+ this.$watchers = null;
1902
+ this.trigger = noneTrigger;
1903
+ };
1380
1904
 
1381
- anchorFragment.appendElement = function(child, before = null) {
1382
- const parentNode = anchorStart.parentNode;
1383
- const targetBefore = before || anchorEnd;
1384
- if(parentNode === anchorFragment) {
1385
- parentNode.nativeInsertBefore(child, targetBefore);
1386
- return;
1387
- }
1388
- parentNode?.insertBefore(child, targetBefore);
1389
- };
1905
+ /**
1906
+ * Registers a cleanup callback that will be executed when the observable is cleaned up.
1907
+ * Useful for disposing resources, removing event listeners, or other cleanup tasks.
1908
+ *
1909
+ * @param {Function} callback - Cleanup function to execute on observable disposal
1910
+ * @example
1911
+ * const obs = Observable(0);
1912
+ * obs.onCleanup(() => console.log('Cleaned up!'));
1913
+ * obs.cleanup(); // Logs: "Cleaned up!"
1914
+ */
1915
+ ObservableItem.prototype.onCleanup = function(callback) {
1916
+ this.$cleanupListeners = this.$cleanupListeners ?? [];
1917
+ this.$cleanupListeners.push(callback);
1918
+ };
1390
1919
 
1391
- anchorFragment.appendChild = function(child, before = null) {
1392
- const parent = anchorEnd.parentNode;
1393
- if(!parent) {
1394
- DebugManager$1.error('Anchor', 'Anchor : parent not found', child);
1395
- return;
1920
+ ObservableItem.prototype.cleanup = function() {
1921
+ if (this.$cleanupListeners) {
1922
+ for (let i = 0; i < this.$cleanupListeners.length; i++) {
1923
+ this.$cleanupListeners[i]();
1396
1924
  }
1397
- before = before ?? anchorEnd;
1398
- insertBefore(parent, child, before);
1399
- };
1400
- anchorFragment.append = function(...args ) {
1401
- return anchorFragment.appendChild(args);
1402
- };
1925
+ this.$cleanupListeners = null;
1926
+ }
1927
+ MemoryManager.unregister(this.$memoryId);
1928
+ this.disconnectAll();
1929
+ {
1930
+ this.$isCleanedUp = true;
1931
+ }
1932
+ delete this.$value;
1933
+ };
1403
1934
 
1404
- anchorFragment.removeChildren = function() {
1405
- const parent = anchorEnd.parentNode;
1406
- if(parent === anchorFragment) {
1935
+ /**
1936
+ *
1937
+ * @param {Function} callback
1938
+ * @returns {(function(): void)}
1939
+ */
1940
+ ObservableItem.prototype.subscribe = function(callback) {
1941
+ {
1942
+ if (this.$isCleanedUp) {
1943
+ DebugManager.warn('Observable subscription', '⚠️ Attempted to subscribe to a cleaned up observable.');
1407
1944
  return;
1408
1945
  }
1409
- if(isParentUniqueChild(parent)) {
1410
- parent.replaceChildren(anchorStart, anchorEnd);
1411
- return;
1946
+ if (typeof callback !== 'function') {
1947
+ throw new NativeDocumentError('Callback must be a function');
1412
1948
  }
1949
+ }
1950
+ this.$listeners = this.$listeners ?? [];
1413
1951
 
1414
- let itemToRemove = anchorStart.nextSibling, tempItem;
1415
- const fragment = document.createDocumentFragment();
1416
- while(itemToRemove && itemToRemove !== anchorEnd) {
1417
- tempItem = itemToRemove.nextSibling;
1418
- fragment.append(itemToRemove);
1419
- itemToRemove = tempItem;
1420
- }
1421
- fragment.replaceChildren();
1422
- };
1423
- anchorFragment.remove = function() {
1424
- const parent = anchorEnd.parentNode;
1425
- if(parent === anchorFragment) {
1426
- return;
1427
- }
1428
- let itemToRemove = anchorStart.nextSibling, tempItem;
1429
- while(itemToRemove && itemToRemove !== anchorEnd) {
1430
- tempItem = itemToRemove.nextSibling;
1431
- anchorFragment.nativeAppendChild(itemToRemove);
1432
- itemToRemove = tempItem;
1952
+ this.$listeners.push(callback);
1953
+ this.assocTrigger();
1954
+ {
1955
+ PluginsManager.emit('ObservableSubscribe', this);
1956
+ }
1957
+ };
1958
+
1959
+ /**
1960
+ * Watches for a specific value and executes callback when the observable equals that value.
1961
+ * Creates a watcher that only triggers when the observable changes to the specified value.
1962
+ *
1963
+ * @param {*} value - The value to watch for
1964
+ * @param {(value) => void|ObservableItem} callback - Callback function or observable to set when value matches
1965
+ * @example
1966
+ * const status = Observable('idle');
1967
+ * status.on('loading', () => console.log('Started loading'));
1968
+ * status.on('error', isError); // Set another observable
1969
+ */
1970
+ ObservableItem.prototype.on = function(value, callback) {
1971
+ this.$watchers = this.$watchers ?? new Map();
1972
+
1973
+ let watchValueList = this.$watchers.get(value);
1974
+
1975
+ if(callback.__$isObservable) {
1976
+ callback = callback.set.bind(callback);
1977
+ }
1978
+
1979
+ if(!watchValueList) {
1980
+ watchValueList = callback;
1981
+ this.$watchers.set(value, callback);
1982
+ } else if(!Validator.isArray(watchValueList.list)) {
1983
+ watchValueList = [watchValueList, callback];
1984
+ callback = (value) => {
1985
+ for(let i = 0, length = watchValueList.length; i < length; i++) {
1986
+ watchValueList[i](value);
1987
+ }
1988
+ };
1989
+ callback.list = watchValueList;
1990
+ this.$watchers.set(value, callback);
1991
+ } else {
1992
+ watchValueList.list.push(callback);
1993
+ }
1994
+
1995
+ this.assocTrigger();
1996
+ };
1997
+
1998
+ /**
1999
+ * Removes a watcher for a specific value. If no callback is provided, removes all watchers for that value.
2000
+ *
2001
+ * @param {*} value - The value to stop watching
2002
+ * @param {Function} [callback] - Specific callback to remove. If omitted, removes all watchers for this value
2003
+ * @example
2004
+ * const status = Observable('idle');
2005
+ * const handler = () => console.log('Loading');
2006
+ * status.on('loading', handler);
2007
+ * status.off('loading', handler); // Remove specific handler
2008
+ * status.off('loading'); // Remove all handlers for 'loading'
2009
+ */
2010
+ ObservableItem.prototype.off = function(value, callback) {
2011
+ if(!this.$watchers) return;
2012
+
2013
+ const watchValueList = this.$watchers.get(value);
2014
+ if(!watchValueList) return;
2015
+
2016
+ if(!callback || !Array.isArray(watchValueList.list)) {
2017
+ this.$watchers?.delete(value);
2018
+ this.assocTrigger();
2019
+ return;
2020
+ }
2021
+ const index = watchValueList.indexOf(callback);
2022
+ watchValueList?.splice(index, 1);
2023
+ if(watchValueList.length === 1) {
2024
+ this.$watchers.set(value, watchValueList[0]);
2025
+ }
2026
+ else if(watchValueList.length === 0) {
2027
+ this.$watchers?.delete(value);
2028
+ }
2029
+ this.assocTrigger();
2030
+ };
2031
+
2032
+ /**
2033
+ * Subscribes to the observable but automatically unsubscribes after the first time the predicate matches.
2034
+ *
2035
+ * @param {(value) => Boolean|any} predicate - Value to match or function that returns true when condition is met
2036
+ * @param {(value) => void} callback - Callback to execute when predicate matches, receives the matched value
2037
+ * @example
2038
+ * const status = Observable('loading');
2039
+ * status.once('ready', (val) => console.log('Ready!'));
2040
+ * status.once(val => val === 'error', (val) => console.log('Error occurred'));
2041
+ */
2042
+ ObservableItem.prototype.once = function(predicate, callback) {
2043
+ const fn = typeof predicate === 'function' ? predicate : (v) => v === predicate;
2044
+
2045
+ const handler = (val) => {
2046
+ if (fn(val)) {
2047
+ this.unsubscribe(handler);
2048
+ callback(val);
1433
2049
  }
1434
2050
  };
2051
+ this.subscribe(handler);
2052
+ };
1435
2053
 
1436
- anchorFragment.removeWithAnchors = function() {
1437
- anchorFragment.removeChildren();
1438
- anchorStart.remove();
1439
- anchorEnd.remove();
1440
- };
2054
+ /**
2055
+ * Unsubscribe from an observable.
2056
+ * @param {Function} callback
2057
+ */
2058
+ ObservableItem.prototype.unsubscribe = function(callback) {
2059
+ if(!this.$listeners) return;
2060
+ const index = this.$listeners.indexOf(callback);
2061
+ if (index > -1) {
2062
+ this.$listeners.splice(index, 1);
2063
+ }
2064
+ this.assocTrigger();
2065
+ {
2066
+ PluginsManager.emit('ObservableUnsubscribe', this);
2067
+ }
2068
+ };
1441
2069
 
1442
- anchorFragment.replaceContent = function(child) {
1443
- const childElement = Validator.isElement(child) ? child : ElementCreator.getChild(child);
1444
- const parent = anchorEnd.parentNode;
1445
- if(!parent) {
1446
- return;
1447
- }
1448
- if(isParentUniqueChild(parent)) {
1449
- parent.replaceChildren(anchorStart, childElement, anchorEnd);
1450
- return;
1451
- }
1452
- anchorFragment.removeChildren();
1453
- parent.insertBefore(childElement, anchorEnd);
1454
- };
2070
+ /**
2071
+ * Create an Observable checker instance
2072
+ * @param callback
2073
+ * @returns {ObservableChecker}
2074
+ */
2075
+ ObservableItem.prototype.check = function(callback) {
2076
+ return new ObservableChecker(this, callback)
2077
+ };
1455
2078
 
1456
- anchorFragment.setContent = anchorFragment.replaceContent;
2079
+ ObservableItem.prototype.transform = ObservableItem.prototype.check;
2080
+ ObservableItem.prototype.pluck = ObservableItem.prototype.check;
2081
+ ObservableItem.prototype.is = ObservableItem.prototype.check;
2082
+ ObservableItem.prototype.select = ObservableItem.prototype.check;
1457
2083
 
1458
- anchorFragment.insertBefore = function(child, anchor = null) {
1459
- anchorFragment.appendChild(child, anchor);
1460
- };
2084
+ /**
2085
+ * Gets a property value from the observable's current value.
2086
+ * If the property is an observable, returns its value.
2087
+ *
2088
+ * @param {string|number} key - Property key to retrieve
2089
+ * @returns {*} The value of the property, unwrapped if it's an observable
2090
+ * @example
2091
+ * const user = Observable({ name: 'John', age: Observable(25) });
2092
+ * user.get('name'); // 'John'
2093
+ * user.get('age'); // 25 (unwrapped from observable)
2094
+ */
2095
+ ObservableItem.prototype.get = function(key) {
2096
+ const item = this.$currentValue[key];
2097
+ return Validator.isObservable(item) ? item.val() : item;
2098
+ };
1461
2099
 
2100
+ /**
2101
+ * Creates an ObservableWhen that represents whether the observable equals a specific value.
2102
+ * Returns an object that can be subscribed to and will emit true/false.
2103
+ *
2104
+ * @param {*} value - The value to compare against
2105
+ * @returns {ObservableWhen} An ObservableWhen instance that tracks when the observable equals the value
2106
+ * @example
2107
+ * const status = Observable('idle');
2108
+ * const isLoading = status.when('loading');
2109
+ * isLoading.subscribe(active => console.log('Loading:', active));
2110
+ * status.set('loading'); // Logs: "Loading: true"
2111
+ */
2112
+ ObservableItem.prototype.when = function(value) {
2113
+ return new ObservableWhen(this, value);
2114
+ };
1462
2115
 
1463
- anchorFragment.endElement = function() {
1464
- return anchorEnd;
1465
- };
2116
+ /**
2117
+ * Compares the observable's current value with another value or observable.
2118
+ *
2119
+ * @param {*|ObservableItem} other - Value or observable to compare against
2120
+ * @returns {boolean} True if values are equal
2121
+ * @example
2122
+ * const a = Observable(5);
2123
+ * const b = Observable(5);
2124
+ * a.equals(5); // true
2125
+ * a.equals(b); // true
2126
+ * a.equals(10); // false
2127
+ */
2128
+ ObservableItem.prototype.equals = function(other) {
2129
+ if(Validator.isObservable(other)) {
2130
+ return this.$currentValue === other.$currentValue;
2131
+ }
2132
+ return this.$currentValue === other;
2133
+ };
1466
2134
 
1467
- anchorFragment.startElement = function() {
1468
- return anchorStart;
1469
- };
1470
- anchorFragment.restore = function() {
1471
- anchorFragment.appendChild(anchorFragment);
1472
- };
1473
- anchorFragment.clear = anchorFragment.remove;
1474
- anchorFragment.detach = anchorFragment.remove;
2135
+ /**
2136
+ * Converts the observable's current value to a boolean.
2137
+ *
2138
+ * @returns {boolean} The boolean representation of the current value
2139
+ * @example
2140
+ * const count = Observable(0);
2141
+ * count.toBool(); // false
2142
+ * count.set(5);
2143
+ * count.toBool(); // true
2144
+ */
2145
+ ObservableItem.prototype.toBool = function() {
2146
+ return !!this.$currentValue;
2147
+ };
1475
2148
 
1476
- anchorFragment.getByIndex = function(index) {
1477
- let currentNode = anchorStart;
1478
- for(let i = 0; i <= index; i++) {
1479
- if(!currentNode.nextSibling) {
1480
- return null;
1481
- }
1482
- currentNode = currentNode.nextSibling;
1483
- }
1484
- return currentNode !== anchorStart ? currentNode : null;
1485
- };
2149
+ /**
2150
+ * Toggles the boolean value of the observable (false becomes true, true becomes false).
2151
+ *
2152
+ * @example
2153
+ * const isOpen = Observable(false);
2154
+ * isOpen.toggle(); // Now true
2155
+ * isOpen.toggle(); // Now false
2156
+ */
2157
+ ObservableItem.prototype.toggle = function() {
2158
+ this.set(!this.$currentValue);
2159
+ };
1486
2160
 
1487
- return anchorFragment;
1488
- }
1489
2161
  /**
2162
+ * Resets the observable to its initial value.
2163
+ * Only works if the observable was created with { reset: true } config.
1490
2164
  *
1491
- * @param {HTMLElement|DocumentFragment|Text|String|Array} children
1492
- * @param {{ parent?: HTMLElement, name?: String}} configs
1493
- * @returns {DocumentFragment}
2165
+ * @example
2166
+ * const count = Observable(0, { reset: true });
2167
+ * count.set(10);
2168
+ * count.reset(); // Back to 0
1494
2169
  */
1495
- function createPortal(children, { parent, name = 'unnamed' } = {}) {
1496
- const anchor = Anchor('Portal '+name);
1497
- anchor.appendChild(ElementCreator.getChild(children));
2170
+ ObservableItem.prototype.reset = function() {
2171
+ if(!this.configs?.reset) {
2172
+ return;
2173
+ }
2174
+ const resetValue = (Validator.isObject(this.$initialValue))
2175
+ ? deepClone(this.$initialValue, (observable) => {
2176
+ observable.reset();
2177
+ })
2178
+ : this.$initialValue;
2179
+ this.set(resetValue);
2180
+ };
1498
2181
 
1499
- (parent || document.body).appendChild(anchor);
1500
- return anchor;
1501
- }
2182
+ /**
2183
+ * Returns a string representation of the observable's current value.
2184
+ *
2185
+ * @returns {string} String representation of the current value
2186
+ */
2187
+ ObservableItem.prototype.toString = function() {
2188
+ return String(this.$currentValue);
2189
+ };
1502
2190
 
1503
- DocumentFragment.prototype.setAttribute = () => {};
2191
+ /**
2192
+ * Returns the primitive value of the observable (its current value).
2193
+ * Called automatically in type coercion contexts.
2194
+ *
2195
+ * @returns {*} The current value of the observable
2196
+ */
2197
+ ObservableItem.prototype.valueOf = function() {
2198
+ return this.$currentValue;
2199
+ };
1504
2200
 
1505
- const BOOLEAN_ATTRIBUTES = new Set([
1506
- 'checked',
1507
- 'selected',
1508
- 'disabled',
1509
- 'readonly',
1510
- 'required',
1511
- 'autofocus',
1512
- 'multiple',
1513
- 'autocomplete',
1514
- 'hidden',
1515
- 'contenteditable',
1516
- 'spellcheck',
1517
- 'translate',
1518
- 'draggable',
1519
- 'async',
1520
- 'defer',
1521
- 'autoplay',
1522
- 'controls',
1523
- 'loop',
1524
- 'muted',
1525
- 'download',
1526
- 'reversed',
1527
- 'open',
1528
- 'default',
1529
- 'formnovalidate',
1530
- 'novalidate',
1531
- 'scoped',
1532
- 'itemscope',
1533
- 'allowfullscreen',
1534
- 'allowpaymentrequest',
1535
- 'playsinline'
1536
- ]);
2201
+
2202
+ /**
2203
+ * Creates a derived observable that formats the current value using Intl.
2204
+ * Automatically reacts to both value changes and locale changes (Store.__nd.locale).
2205
+ *
2206
+ * @param {string | Function} type - Format type or custom formatter function
2207
+ * @param {Object} [options={}] - Options passed to the formatter
2208
+ * @returns {ObservableItem<string>}
2209
+ *
2210
+ * @example
2211
+ * // Currency
2212
+ * price.format('currency') // "15 000 FCFA"
2213
+ * price.format('currency', { currency: 'EUR' }) // "15 000,00 €"
2214
+ * price.format('currency', { notation: 'compact' }) // "15 K FCFA"
2215
+ *
2216
+ * // Number
2217
+ * count.format('number') // "15 000"
2218
+ *
2219
+ * // Percent
2220
+ * rate.format('percent') // "15,0 %"
2221
+ * rate.format('percent', { decimals: 2 }) // "15,00 %"
2222
+ *
2223
+ * // Date
2224
+ * date.format('date') // "3 mars 2026"
2225
+ * date.format('date', { dateStyle: 'full' }) // "mardi 3 mars 2026"
2226
+ * date.format('date', { format: 'DD/MM/YYYY' }) // "03/03/2026"
2227
+ * date.format('date', { format: 'DD MMM YYYY' }) // "03 mar 2026"
2228
+ * date.format('date', { format: 'DD MMMM YYYY' }) // "03 mars 2026"
2229
+ *
2230
+ * // Time
2231
+ * date.format('time') // "20:30"
2232
+ * date.format('time', { second: '2-digit' }) // "20:30:00"
2233
+ * date.format('time', { format: 'HH:mm:ss' }) // "20:30:00"
2234
+ *
2235
+ * // Datetime
2236
+ * date.format('datetime') // "3 mars 2026, 20:30"
2237
+ * date.format('datetime', { dateStyle: 'full' }) // "mardi 3 mars 2026, 20:30"
2238
+ * date.format('datetime', { format: 'DD/MM/YYYY HH:mm' }) // "03/03/2026 20:30"
2239
+ *
2240
+ * // Relative
2241
+ * date.format('relative') // "dans 11 jours"
2242
+ * date.format('relative', { unit: 'month' }) // "dans 1 mois"
2243
+ *
2244
+ * // Plural
2245
+ * count.format('plural', { singular: 'billet', plural: 'billets' }) // "3 billets"
2246
+ *
2247
+ * // Custom formatter
2248
+ * price.format(value => `${value.toLocaleString()} FCFA`)
2249
+ *
2250
+ * // Reacts to locale changes automatically
2251
+ * Store.setLocale('en-US');
2252
+ */
2253
+ ObservableItem.prototype.format = function(type, options = {}) {
2254
+ const self = this;
2255
+
2256
+ if (typeof type === 'function') {
2257
+ return new ObservableChecker(self, type);
2258
+ }
2259
+
2260
+ {
2261
+ if (!Formatters[type]) {
2262
+ throw new NativeDocumentError(
2263
+ `Observable.format : unknown type '${type}'. Available : ${Object.keys(Formatters).join(', ')}.`
2264
+ );
2265
+ }
2266
+ }
2267
+
2268
+ const formatter = Formatters[type];
2269
+ const localeObservable = Store.follow('locale');
2270
+
2271
+ return Observable$1.computed(() => formatter(self.val(), localeObservable.val(), options),
2272
+ [self, localeObservable]
2273
+ );
2274
+ };
2275
+
2276
+ ObservableItem.prototype.persist = function(key, options = {}) {
2277
+ let value = $getFromStorage$1(key, this.$currentValue);
2278
+ if(options.get) {
2279
+ value = options.get(value);
2280
+ }
2281
+ this.set(value);
2282
+ const saver = $saveToStorage$1(this.$currentValue);
2283
+ this.subscribe((newValue) => {
2284
+ saver(key, options.set ? options.set(newValue) : newValue);
2285
+ });
2286
+ return this;
2287
+ };
1537
2288
 
1538
2289
  /**
1539
2290
  *
@@ -1542,18 +2293,18 @@ var NativeDocument = (function (exports) {
1542
2293
  * @returns {ObservableItem}
1543
2294
  * @constructor
1544
2295
  */
1545
- function Observable(value, configs = null) {
2296
+ function Observable$1(value, configs = null) {
1546
2297
  return new ObservableItem(value, configs);
1547
2298
  }
1548
2299
 
1549
- const $ = Observable;
1550
- const obs = Observable;
2300
+ const $ = Observable$1;
2301
+ const obs = Observable$1;
1551
2302
 
1552
2303
  /**
1553
2304
  *
1554
2305
  * @param {string} propertyName
1555
2306
  */
1556
- Observable.useValueProperty = function(propertyName = 'value') {
2307
+ Observable$1.useValueProperty = function(propertyName = 'value') {
1557
2308
  Object.defineProperty(ObservableItem.prototype, propertyName, {
1558
2309
  get() {
1559
2310
  return this.$currentValue;
@@ -1571,7 +2322,7 @@ var NativeDocument = (function (exports) {
1571
2322
  * @param id
1572
2323
  * @returns {ObservableItem|null}
1573
2324
  */
1574
- Observable.getById = function(id) {
2325
+ Observable$1.getById = function(id) {
1575
2326
  const item = MemoryManager.getObservableById(parseInt(id));
1576
2327
  if(!item) {
1577
2328
  throw new NativeDocumentError('Observable.getById : No observable found with id ' + id);
@@ -1583,7 +2334,7 @@ var NativeDocument = (function (exports) {
1583
2334
  *
1584
2335
  * @param {ObservableItem} observable
1585
2336
  */
1586
- Observable.cleanup = function(observable) {
2337
+ Observable$1.cleanup = function(observable) {
1587
2338
  observable.cleanup();
1588
2339
  };
1589
2340
 
@@ -1592,7 +2343,7 @@ var NativeDocument = (function (exports) {
1592
2343
  * @param {Boolean} enable
1593
2344
  * @param {{interval:Boolean, threshold:number}} options
1594
2345
  */
1595
- Observable.autoCleanup = function(enable = false, options = {}) {
2346
+ Observable$1.autoCleanup = function(enable = false, options = {}) {
1596
2347
  if(!enable) {
1597
2348
  return;
1598
2349
  }
@@ -1737,6 +2488,12 @@ var NativeDocument = (function (exports) {
1737
2488
  return element;
1738
2489
  }
1739
2490
 
2491
+ function TemplateBinding(hydrate) {
2492
+ this.$hydrate = hydrate;
2493
+ }
2494
+
2495
+ TemplateBinding.prototype.__$isTemplateBinding = true;
2496
+
1740
2497
  String.prototype.toNdElement = function () {
1741
2498
  const formattedChild = this.resolveObservableTemplate ? this.resolveObservableTemplate() : this;
1742
2499
  if(Validator.isString(formattedChild)) {
@@ -1784,7 +2541,7 @@ var NativeDocument = (function (exports) {
1784
2541
  Function.prototype.toNdElement = function () {
1785
2542
  const child = this;
1786
2543
  {
1787
- PluginsManager$1.emit('BeforeProcessComponent', child);
2544
+ PluginsManager.emit('BeforeProcessComponent', child);
1788
2545
  }
1789
2546
  return ElementCreator.getChild(child());
1790
2547
  };
@@ -1793,6 +2550,88 @@ var NativeDocument = (function (exports) {
1793
2550
  return ElementCreator.createHydratableNode(null, this);
1794
2551
  };
1795
2552
 
2553
+ /**
2554
+ * @param {HTMLElement} el
2555
+ * @param {number} timeout
2556
+ */
2557
+ const waitForVisualEnd = (el, timeout = 1000) => {
2558
+ return new Promise((resolve) => {
2559
+ let isResolved = false;
2560
+
2561
+ const cleanupAndResolve = (e) => {
2562
+ if (e && e.target !== el) return;
2563
+ if (isResolved) return;
2564
+
2565
+ isResolved = true;
2566
+ el.removeEventListener('transitionend', cleanupAndResolve);
2567
+ el.removeEventListener('animationend', cleanupAndResolve);
2568
+ clearTimeout(timer);
2569
+ resolve();
2570
+ };
2571
+
2572
+ el.addEventListener('transitionend', cleanupAndResolve);
2573
+ el.addEventListener('animationend', cleanupAndResolve);
2574
+
2575
+ const timer = setTimeout(cleanupAndResolve, timeout);
2576
+
2577
+ const style = window.getComputedStyle(el);
2578
+ const hasTransition = style.transitionDuration !== '0s';
2579
+ const hasAnimation = style.animationDuration !== '0s';
2580
+
2581
+ if (!hasTransition && !hasAnimation) {
2582
+ cleanupAndResolve();
2583
+ }
2584
+ });
2585
+ };
2586
+
2587
+ NDElement.prototype.transitionOut = function(transitionName) {
2588
+ const exitClass = transitionName + '-exit';
2589
+ this.beforeUnmount('transition-exit', async function() {
2590
+ this.$element.classes.add(exitClass);
2591
+ await waitForVisualEnd(this.$element);
2592
+ this.$element.classes.remove(exitClass);
2593
+ });
2594
+ return this;
2595
+ };
2596
+
2597
+ NDElement.prototype.transitionIn = function(transitionName) {
2598
+ const startClass = transitionName + '-enter-from';
2599
+ const endClass = transitionName + '-enter-to';
2600
+
2601
+ this.$element.classes.add(startClass);
2602
+
2603
+ this.mounted(() => {
2604
+ requestAnimationFrame(() => {
2605
+ requestAnimationFrame(() => {
2606
+ this.$element.classes.remove(startClass);
2607
+ this.$element.classes.add(endClass);
2608
+
2609
+ waitForVisualEnd(this.$element).then(() => {
2610
+ this.$element.classes.remove(endClass);
2611
+ });
2612
+ });
2613
+ });
2614
+ });
2615
+ return this;
2616
+ };
2617
+
2618
+
2619
+ NDElement.prototype.transition = function (transitionName) {
2620
+ this.transitionIn(transitionName);
2621
+ this.transitionOut(transitionName);
2622
+ return this;
2623
+ };
2624
+
2625
+ NDElement.prototype.animate = function(animationName) {
2626
+ this.$element.classes.add(animationName);
2627
+
2628
+ waitForVisualEnd(this.$element).then(() => {
2629
+ this.$element.classes.remove(animationName);
2630
+ });
2631
+
2632
+ return this;
2633
+ };
2634
+
1796
2635
  String.prototype.handleNdAttribute = function(element, attributeName) {
1797
2636
  element.setAttribute(attributeName, this);
1798
2637
  };
@@ -1890,16 +2729,20 @@ var NativeDocument = (function (exports) {
1890
2729
  processChildren(children, parent) {
1891
2730
  if(children === null) return;
1892
2731
  {
1893
- PluginsManager$1.emit('BeforeProcessChildren', parent);
2732
+ PluginsManager.emit('BeforeProcessChildren', parent);
1894
2733
  }
1895
2734
  let child = this.getChild(children);
1896
2735
  if(child) {
1897
2736
  parent.appendChild(child);
1898
2737
  }
1899
2738
  {
1900
- PluginsManager$1.emit('AfterProcessChildren', parent);
2739
+ PluginsManager.emit('AfterProcessChildren', parent);
1901
2740
  }
1902
2741
  },
2742
+ async safeRemove(element) {
2743
+ await element.remove();
2744
+
2745
+ },
1903
2746
  getChild(child) {
1904
2747
  if(child == null) {
1905
2748
  return null;
@@ -2361,10 +3204,10 @@ var NativeDocument = (function (exports) {
2361
3204
  /**
2362
3205
  *
2363
3206
  * @param {string} name
2364
- * @param {?Function} customWrapper
3207
+ * @param {?Function=} customWrapper
2365
3208
  * @returns {Function}
2366
3209
  */
2367
- function HtmlElementWrapper(name, customWrapper) {
3210
+ function HtmlElementWrapper(name, customWrapper = null) {
2368
3211
  return createHtmlElement.bind(null, name, customWrapper);
2369
3212
  }
2370
3213
 
@@ -2418,7 +3261,7 @@ var NativeDocument = (function (exports) {
2418
3261
  hydrationState[targetType][property] = hydrateFunction;
2419
3262
  };
2420
3263
 
2421
- const bindAttachMethods = function(node, bindDingData, data) {
3264
+ const bindAttachMethods = (node, bindDingData, data) => {
2422
3265
  if(!bindDingData.attach) {
2423
3266
  return null;
2424
3267
  }
@@ -2654,7 +3497,7 @@ var NativeDocument = (function (exports) {
2654
3497
  String.prototype.use = function(args) {
2655
3498
  const value = this;
2656
3499
 
2657
- return Observable.computed(() => {
3500
+ return Observable$1.computed(() => {
2658
3501
  return value.replace(/\$\{(.*?)}/g, (match, key) => {
2659
3502
  const data = args[key];
2660
3503
  if(Validator.isObservable(data)) {
@@ -2674,7 +3517,7 @@ var NativeDocument = (function (exports) {
2674
3517
  return value;
2675
3518
  }
2676
3519
  const [_, id] = value.match(/\{\{#ObItem::\(([0-9]+)\)\}\}/);
2677
- return Observable.getById(id);
3520
+ return Observable$1.getById(id);
2678
3521
  });
2679
3522
  };
2680
3523
 
@@ -2723,7 +3566,7 @@ var NativeDocument = (function (exports) {
2723
3566
  const once$1 = (fn) => {
2724
3567
  let result = null;
2725
3568
  return (...args) => {
2726
- if(result) {
3569
+ if(result != null) {
2727
3570
  return result;
2728
3571
  }
2729
3572
  result = fn(...args);
@@ -3149,7 +3992,7 @@ var NativeDocument = (function (exports) {
3149
3992
 
3150
3993
  ObservableItem.call(this, target, configs);
3151
3994
  {
3152
- PluginsManager$1.emit('CreateObservableArray', this);
3995
+ PluginsManager.emit('CreateObservableArray', this);
3153
3996
  }
3154
3997
  };
3155
3998
 
@@ -3367,7 +4210,7 @@ var NativeDocument = (function (exports) {
3367
4210
  }
3368
4211
  }
3369
4212
 
3370
- const viewArray = Observable.array();
4213
+ const viewArray = Observable$1.array();
3371
4214
 
3372
4215
  const filters = Object.entries(filterCallbacks);
3373
4216
  const updateView = () => {
@@ -3453,7 +4296,7 @@ var NativeDocument = (function (exports) {
3453
4296
  * items.push(4); // Triggers update
3454
4297
  * items.subscribe((arr) => console.log(arr));
3455
4298
  */
3456
- Observable.array = function(target = [], configs = null) {
4299
+ Observable$1.array = function(target = [], configs = null) {
3457
4300
  return new ObservableArray(target, configs);
3458
4301
  };
3459
4302
 
@@ -3462,8 +4305,8 @@ var NativeDocument = (function (exports) {
3462
4305
  * @param {Function} callback
3463
4306
  * @returns {Function}
3464
4307
  */
3465
- Observable.batch = function(callback) {
3466
- const $observer = Observable(0);
4308
+ Observable$1.batch = function(callback) {
4309
+ const $observer = Observable$1(0);
3467
4310
  const batch = function() {
3468
4311
  if(Validator.isAsyncFunction(callback)) {
3469
4312
  return (callback(...arguments)).then(() => {
@@ -3477,10 +4320,71 @@ var NativeDocument = (function (exports) {
3477
4320
  return batch;
3478
4321
  };
3479
4322
 
3480
- const ObservableObjectValue = function(data) {
4323
+ const ObservableObject = function(target, configs) {
4324
+ ObservableItem.call(this, target);
4325
+ this.$observables = {};
4326
+ this.configs = configs;
4327
+
4328
+ this.$load(target);
4329
+
4330
+ for(const name in target) {
4331
+ if(!Object.hasOwn(this, name)) {
4332
+ Object.defineProperty(this, name, {
4333
+ get: () => this.$observables[name],
4334
+ set: (value) => this.$observables[name].set(value)
4335
+ });
4336
+ }
4337
+ }
4338
+
4339
+ };
4340
+
4341
+ ObservableObject.prototype = Object.create(ObservableItem.prototype);
4342
+
4343
+ Object.defineProperty(ObservableObject, '$value', {
4344
+ get() {
4345
+ return this.val();
4346
+ },
4347
+ set(value) {
4348
+ this.set(value);
4349
+ }
4350
+ });
4351
+
4352
+ ObservableObject.prototype.__$isObservableObject = true;
4353
+ ObservableObject.prototype.__isProxy__ = true;
4354
+
4355
+ ObservableObject.prototype.$load = function(initialValue) {
4356
+ const configs = this.configs;
4357
+ for(const key in initialValue) {
4358
+ const itemValue = initialValue[key];
4359
+ if(Array.isArray(itemValue)) {
4360
+ if(configs?.deep !== false) {
4361
+ const mappedItemValue = itemValue.map(item => {
4362
+ if(Validator.isJson(item)) {
4363
+ return Observable$1.json(item, configs);
4364
+ }
4365
+ if(Validator.isArray(item)) {
4366
+ return Observable$1.array(item, configs);
4367
+ }
4368
+ return Observable$1(item, configs);
4369
+ });
4370
+ this.$observables[key] = Observable$1.array(mappedItemValue, configs);
4371
+ continue;
4372
+ }
4373
+ this.$observables[key] = Observable$1.array(itemValue, configs);
4374
+ continue;
4375
+ }
4376
+ if(Validator.isObservable(itemValue) || Validator.isProxy(itemValue)) {
4377
+ this.$observables[key] = itemValue;
4378
+ continue;
4379
+ }
4380
+ this.$observables[key] = Observable$1(itemValue, configs);
4381
+ }
4382
+ };
4383
+
4384
+ ObservableObject.prototype.val = function() {
3481
4385
  const result = {};
3482
- for(const key in data) {
3483
- const dataItem = data[key];
4386
+ for(const key in this.$observables) {
4387
+ const dataItem = this.$observables[key];
3484
4388
  if(Validator.isObservable(dataItem)) {
3485
4389
  let value = dataItem.val();
3486
4390
  if(Array.isArray(value)) {
@@ -3503,9 +4407,10 @@ var NativeDocument = (function (exports) {
3503
4407
  }
3504
4408
  return result;
3505
4409
  };
4410
+ ObservableObject.prototype.$val = ObservableObject.prototype.val;
3506
4411
 
3507
- const ObservableGet = function(target, property) {
3508
- const item = target[property];
4412
+ ObservableObject.prototype.get = function(property) {
4413
+ const item = this.$observables[property];
3509
4414
  if(Validator.isObservable(item)) {
3510
4415
  return item.val();
3511
4416
  }
@@ -3514,100 +4419,87 @@ var NativeDocument = (function (exports) {
3514
4419
  }
3515
4420
  return item;
3516
4421
  };
4422
+ ObservableObject.prototype.$get = ObservableObject.prototype.get;
3517
4423
 
3518
- /**
3519
- * Creates an observable proxy for an object where each property becomes an observable.
3520
- * Properties can be accessed directly or via getter methods.
3521
- *
3522
- * @param {Object} initialValue - Initial object value
3523
- * @param {Object|null} [configs=null] - Configuration options
3524
- * // @param {boolean} [configs.propagation=true] - Whether changes propagate to parent
3525
- * @param {boolean} [configs.deep=false] - Whether to make nested objects observable
3526
- * @param {boolean} [configs.reset=false] - Whether to enable reset() method
3527
- * @returns {ObservableProxy} A proxy where each property is an observable
3528
- * @example
3529
- * const user = Observable.init({
3530
- * name: 'John',
3531
- * age: 25,
3532
- * address: { city: 'NYC' }
3533
- * }, { deep: true });
3534
- *
3535
- * user.name.val(); // 'John'
3536
- * user.name.set('Jane');
3537
- * user.name = 'Jane X'
3538
- * user.age.subscribe(val => console.log('Age:', val));
3539
- */
3540
- Observable.init = function(initialValue, configs = null) {
3541
- const data = {};
3542
- for(const key in initialValue) {
3543
- const itemValue = initialValue[key];
3544
- if(Array.isArray(itemValue)) {
3545
- if(configs?.deep !== false) {
3546
- const mappedItemValue = itemValue.map(item => {
3547
- if(Validator.isJson(item)) {
3548
- return Observable.json(item, configs);
3549
- }
3550
- if(Validator.isArray(item)) {
3551
- return Observable.array(item, configs);
4424
+ ObservableObject.prototype.set = function(newData) {
4425
+ const data = Validator.isProxy(newData) ? newData.$value : newData;
4426
+ const configs = this.configs;
4427
+
4428
+ for(const key in data) {
4429
+ const targetItem = this.$observables[key];
4430
+ const newValueOrigin = newData[key];
4431
+ const newValue = data[key];
4432
+
4433
+ if(Validator.isObservable(targetItem)) {
4434
+ if(!Validator.isArray(newValue)) {
4435
+ targetItem.set(newValue);
4436
+ continue;
4437
+ }
4438
+ const firstElementFromOriginalValue = newValueOrigin.at(0);
4439
+ if(Validator.isObservable(firstElementFromOriginalValue) || Validator.isProxy(firstElementFromOriginalValue)) {
4440
+ const newValues = newValue.map(item => {
4441
+ if(Validator.isProxy(firstElementFromOriginalValue)) {
4442
+ return Observable$1.init(item, configs);
3552
4443
  }
3553
- return Observable(item, configs);
4444
+ return Observable$1(item, configs);
3554
4445
  });
3555
- data[key] = Observable.array(mappedItemValue, configs);
4446
+ targetItem.set(newValues);
3556
4447
  continue;
3557
4448
  }
3558
- data[key] = Observable.array(itemValue, configs);
4449
+ targetItem.set([...newValue]);
3559
4450
  continue;
3560
4451
  }
3561
- if(Validator.isObservable(itemValue) || Validator.isProxy(itemValue)) {
3562
- data[key] = itemValue;
4452
+ if(Validator.isProxy(targetItem)) {
4453
+ targetItem.update(newValue);
3563
4454
  continue;
3564
4455
  }
3565
- data[key] = Observable(itemValue, configs);
4456
+ this[key] = newValue;
3566
4457
  }
4458
+ };
4459
+ ObservableObject.prototype.$set = ObservableObject.prototype.set;
4460
+ ObservableObject.prototype.$updateWith = ObservableObject.prototype.set;
3567
4461
 
3568
- const $reset = () => {
3569
- for(const key in data) {
3570
- const item = data[key];
3571
- item.reset();
3572
- }
3573
- };
3574
-
3575
- const $val = () => ObservableObjectValue(data);
4462
+ ObservableObject.prototype.observables = function() {
4463
+ return Object.values(this.$observables);
4464
+ };
4465
+ ObservableObject.prototype.$observables = ObservableObject.prototype.observables;
3576
4466
 
3577
- const $clone = () => Observable.init($val(), configs);
4467
+ ObservableObject.prototype.keys = function() {
4468
+ return Object.keys(this.$observables);
4469
+ };
4470
+ ObservableObject.prototype.$keys = ObservableObject.prototype.keys;
4471
+ ObservableObject.prototype.clone = function() {
4472
+ return Observable$1.init(this.val(), this.configs);
4473
+ };
4474
+ ObservableObject.prototype.$clone = ObservableObject.prototype.clone;
4475
+ ObservableObject.prototype.reset = function() {
4476
+ for(const key in this.$observables) {
4477
+ this.$observables[key].reset();
4478
+ }
4479
+ };
4480
+ ObservableObject.prototype.originalSubscribe = ObservableObject.prototype.subscribe;
4481
+ ObservableObject.prototype.subscribe = function(callback) {
4482
+ const observables = this.observables();
4483
+ const updatedValue = nextTick(() => {
4484
+ this.$currentValue = this.val();
4485
+ this.trigger();
4486
+ });
3578
4487
 
3579
- const $updateWith = (values) => {
3580
- Observable.update(proxy, values);
3581
- };
4488
+ this.originalSubscribe(callback);
3582
4489
 
3583
- const $get = (key) => ObservableGet(data, key);
4490
+ for(let i = 0, length = observables.length; i < length; i++) {
4491
+ const observable = observables[i];
4492
+ observable.subscribe(updatedValue);
4493
+ }
4494
+ };
4495
+ ObservableObject.prototype.configs = function() {
4496
+ return this.configs;
4497
+ };
3584
4498
 
3585
- const proxy = new Proxy(data, {
3586
- get(target, property) {
3587
- if(property === '__isProxy__') { return true; }
3588
- if(property === '$value') { return $val() }
3589
- if(property === 'get' || property === '$get') { return $get; }
3590
- if(property === 'val' || property === '$val') { return $val; }
3591
- if(property === 'set' || property === '$set' || property === '$updateWith') { return $updateWith; }
3592
- if(property === 'observables' || property === '$observables') { return Object.values(target); }
3593
- if(property === 'keys'|| property === '$keys') { return Object.keys(initialValue); }
3594
- if(property === 'clone' || property === '$clone') { return $clone; }
3595
- if(property === 'reset') { return $reset; }
3596
- if(property === 'configs') { return configs; }
3597
- return target[property];
3598
- },
3599
- set(target, prop, newValue) {
3600
- if(target[prop] !== undefined) {
3601
- Validator.isObservable(newValue)
3602
- ? target[prop].set(newValue.val())
3603
- : target[prop].set(newValue);
3604
- return true;
3605
- }
3606
- return true;
3607
- }
3608
- });
4499
+ ObservableObject.prototype.update = ObservableObject.prototype.set;
3609
4500
 
3610
- return proxy;
4501
+ Observable$1.init = function(initialValue, configs = null) {
4502
+ return new ObservableObject(initialValue, configs)
3611
4503
  };
3612
4504
 
3613
4505
  /**
@@ -3615,8 +4507,8 @@ var NativeDocument = (function (exports) {
3615
4507
  * @param {any[]} data
3616
4508
  * @return Proxy[]
3617
4509
  */
3618
- Observable.arrayOfObject = function(data) {
3619
- return data.map(item => Observable.object(item));
4510
+ Observable$1.arrayOfObject = function(data) {
4511
+ return data.map(item => Observable$1.object(item));
3620
4512
  };
3621
4513
 
3622
4514
  /**
@@ -3624,7 +4516,7 @@ var NativeDocument = (function (exports) {
3624
4516
  * @param {ObservableItem|Object<ObservableItem>} data
3625
4517
  * @returns {{}|*|null}
3626
4518
  */
3627
- Observable.value = function(data) {
4519
+ Observable$1.value = function(data) {
3628
4520
  if(Validator.isObservable(data)) {
3629
4521
  return data.val();
3630
4522
  }
@@ -3635,52 +4527,15 @@ var NativeDocument = (function (exports) {
3635
4527
  const result = [];
3636
4528
  for(let i = 0, length = data.length; i < length; i++) {
3637
4529
  const item = data[i];
3638
- result.push(Observable.value(item));
4530
+ result.push(Observable$1.value(item));
3639
4531
  }
3640
4532
  return result;
3641
4533
  }
3642
4534
  return data;
3643
4535
  };
3644
4536
 
3645
-
3646
- Observable.update = function($target, newData) {
3647
- const data = Validator.isProxy(newData) ? newData.$value : newData;
3648
- const configs = $target.configs;
3649
-
3650
- for(const key in data) {
3651
- const targetItem = $target[key];
3652
- const newValueOrigin = newData[key];
3653
- const newValue = data[key];
3654
-
3655
- if(Validator.isObservable(targetItem)) {
3656
- if(Validator.isArray(newValue)) {
3657
- const firstElementFromOriginalValue = newValueOrigin.at(0);
3658
- if(Validator.isObservable(firstElementFromOriginalValue) || Validator.isProxy(firstElementFromOriginalValue)) {
3659
- const newValues = newValue.map(item => {
3660
- if(Validator.isProxy(firstElementFromOriginalValue)) {
3661
- return Observable.init(item, configs);
3662
- }
3663
- return Observable(item, configs);
3664
- });
3665
- targetItem.set(newValues);
3666
- continue;
3667
- }
3668
- targetItem.set([...newValue]);
3669
- continue;
3670
- }
3671
- targetItem.set(newValue);
3672
- continue;
3673
- }
3674
- if(Validator.isProxy(targetItem)) {
3675
- Observable.update(targetItem, newValue);
3676
- continue;
3677
- }
3678
- $target[key] = newValue;
3679
- }
3680
- };
3681
-
3682
- Observable.object = Observable.init;
3683
- Observable.json = Observable.init;
4537
+ Observable$1.object = Observable$1.init;
4538
+ Observable$1.json = Observable$1.init;
3684
4539
 
3685
4540
  /**
3686
4541
  * Creates a computed observable that automatically updates when its dependencies change.
@@ -3701,12 +4556,12 @@ var NativeDocument = (function (exports) {
3701
4556
  * const batch = Observable.batch(() => { ... });
3702
4557
  * const computed = Observable.computed(() => { ... }, batch);
3703
4558
  */
3704
- Observable.computed = function(callback, dependencies = []) {
4559
+ Observable$1.computed = function(callback, dependencies = []) {
3705
4560
  const initialValue = callback();
3706
4561
  const observable = new ObservableItem(initialValue);
3707
4562
  const updatedValue = nextTick(() => observable.set(callback()));
3708
4563
  {
3709
- PluginsManager$1.emit('CreateObservableComputed', observable, dependencies);
4564
+ PluginsManager.emit('CreateObservableComputed', observable, dependencies);
3710
4565
  }
3711
4566
 
3712
4567
  if(Validator.isFunction(dependencies)) {
@@ -3730,85 +4585,12 @@ var NativeDocument = (function (exports) {
3730
4585
  return observable;
3731
4586
  };
3732
4587
 
3733
- const Store = (function() {
3734
-
3735
- const $stores = new Map();
3736
-
3737
- return {
3738
- /**
3739
- * Create a new state follower and return it.
3740
- * @param {string} name
3741
- * @returns {ObservableItem}
3742
- */
3743
- use(name) {
3744
- const {observer: originalObserver, subscribers } = $stores.get(name);
3745
- const observerFollower = Observable(originalObserver.val());
3746
- const unSubscriber = originalObserver.subscribe(value => observerFollower.set(value));
3747
- const updaterUnsubscriber = observerFollower.subscribe(value => originalObserver.set(value));
3748
- observerFollower.destroy = () => {
3749
- unSubscriber();
3750
- updaterUnsubscriber();
3751
- observerFollower.cleanup();
3752
- };
3753
- subscribers.add(observerFollower);
3754
-
3755
- return observerFollower;
3756
- },
3757
- /**
3758
- * @param {string} name
3759
- * @returns {ObservableItem}
3760
- */
3761
- follow(name) {
3762
- return this.use(name);
3763
- },
3764
- /**
3765
- * Create a new state and return the observer.
3766
- * @param {string} name
3767
- * @param {*} value
3768
- * @returns {ObservableItem}
3769
- */
3770
- create(name, value) {
3771
- const observer = Observable(value);
3772
- $stores.set(name, { observer, subscribers: new Set()});
3773
- return observer;
3774
- },
3775
- /**
3776
- * Get the observer for a state.
3777
- * @param {string} name
3778
- * @returns {null|ObservableItem}
3779
- */
3780
- get(name) {
3781
- const item = $stores.get(name);
3782
- return item ? item.observer : null;
3783
- },
3784
- /**
3785
- *
3786
- * @param {string} name
3787
- * @returns {{observer: ObservableItem, subscribers: Set}}
3788
- */
3789
- getWithSubscribers(name) {
3790
- return $stores.get(name);
3791
- },
3792
- /**
3793
- * Delete a state.
3794
- * @param {string} name
3795
- */
3796
- delete(name) {
3797
- const item = $stores.get(name);
3798
- if(!item) return;
3799
- item.observer.cleanup();
3800
- item.subscribers.forEach(follower => follower.destroy());
3801
- item.observer.clear();
3802
- }
3803
- };
3804
- }());
3805
-
3806
4588
  /**
3807
4589
  * Renders a list of items from an observable array or object, automatically updating when data changes.
3808
4590
  * Efficiently manages DOM updates by tracking items with keys.
3809
4591
  *
3810
4592
  * @param {ObservableItem<Array|Object>} data - Observable containing array or object to iterate over
3811
- * @param {Function} callback - Function that renders each item (item, index) => ValidChild
4593
+ * @param {(item: *, index: null|ObservableItem) => NdChild} callback - Function that renders each item (item, index) => ValidChild
3812
4594
  * @param {string|Function} [key] - Property name or function to generate unique keys for items
3813
4595
  * @param {Object} [options={}] - Configuration options
3814
4596
  * @param {boolean} [options.shouldKeepItemsInCache=false] - Whether to cache rendered items
@@ -3847,7 +4629,7 @@ var NativeDocument = (function (exports) {
3847
4629
  }
3848
4630
  const child = cacheItem.child?.deref();
3849
4631
  if(parent && child) {
3850
- parent.removeChild(child);
4632
+ child.remove();
3851
4633
  }
3852
4634
  cacheItem.indexObserver?.cleanup();
3853
4635
  cacheItem.child = null;
@@ -3871,14 +4653,14 @@ var NativeDocument = (function (exports) {
3871
4653
  }
3872
4654
 
3873
4655
  try {
3874
- const indexObserver = callback.length >= 2 ? Observable(indexKey) : null;
4656
+ const indexObserver = callback.length >= 2 ? Observable$1(indexKey) : null;
3875
4657
  let child = ElementCreator.getChild(callback(item, indexObserver));
3876
4658
  if(!child) {
3877
4659
  throw new NativeDocumentError("ForEach child can't be null or undefined!");
3878
4660
  }
3879
4661
  cache.set(keyId, { keyId, isNew: true, child: new WeakRef(child), indexObserver});
3880
4662
  } catch (e) {
3881
- DebugManager$1.error('ForEach', `Error creating element for key ${keyId}` , e);
4663
+ DebugManager.error('ForEach', `Error creating element for key ${keyId}` , e);
3882
4664
  throw e;
3883
4665
  }
3884
4666
  return keyId;
@@ -3965,7 +4747,7 @@ var NativeDocument = (function (exports) {
3965
4747
  * Provides index observables and handles array mutations efficiently.
3966
4748
  *
3967
4749
  * @param {ObservableArray} data - ObservableArray to iterate over
3968
- * @param {Function} callback - Function that renders each item (item, indexObservable) => ValidChild
4750
+ * @param {(item: *, index: null|ObservableItem) => NdChild} callback - Function that renders each item (item, indexObservable) => ValidChild
3969
4751
  * @param {Object} [configs={}] - Configuration options
3970
4752
  * @param {boolean} [configs.shouldKeepItemsInCache] - Whether to cache rendered items
3971
4753
  * @param {boolean} [configs.isParentUniqueChild] - When it's the only child of the parent
@@ -4053,7 +4835,7 @@ var NativeDocument = (function (exports) {
4053
4835
  cache.delete(item);
4054
4836
  }
4055
4837
 
4056
- const indexObserver = isIndexRequired ? Observable(indexKey) : null;
4838
+ const indexObserver = isIndexRequired ? Observable$1(indexKey) : null;
4057
4839
  let child = ElementCreator.getChild(callback(item, indexObserver));
4058
4840
  if(child) {
4059
4841
  cache.set(item, {
@@ -4237,7 +5019,7 @@ var NativeDocument = (function (exports) {
4237
5019
  * The element is mounted/unmounted from the DOM as the condition changes.
4238
5020
  *
4239
5021
  * @param {ObservableItem<boolean>|ObservableChecker<boolean>|ObservableWhen} condition - Observable condition to watch
4240
- * @param {ValidChild} child - Element or content to show/hide
5022
+ * @param {NdChild|(() => NdChild)} child - Element or content to show/hide
4241
5023
  * @param {Object} [options={}] - Configuration options
4242
5024
  * @param {string|null} [options.comment=null] - Comment for debugging
4243
5025
  * @param {boolean} [options.shouldKeepInCache=true] - Whether to cache the element when hidden
@@ -4248,7 +5030,7 @@ var NativeDocument = (function (exports) {
4248
5030
  */
4249
5031
  const ShowIf = function(condition, child, { comment = null, shouldKeepInCache = true} = {}) {
4250
5032
  if(!(Validator.isObservable(condition)) && !Validator.isObservableWhenResult(condition)) {
4251
- return DebugManager$1.warn('ShowIf', "ShowIf : condition must be an Observable / "+comment, condition);
5033
+ return DebugManager.warn('ShowIf', "ShowIf : condition must be an Observable / "+comment, condition);
4252
5034
  }
4253
5035
  const element = Anchor('Show if : '+(comment || ''));
4254
5036
 
@@ -4285,7 +5067,7 @@ var NativeDocument = (function (exports) {
4285
5067
  * Inverse of ShowIf - element is shown when condition is false.
4286
5068
  *
4287
5069
  * @param {ObservableItem<boolean>|ObservableChecker<boolean>} condition - Observable condition to watch
4288
- * @param {ValidChild} child - Element or content to show/hide
5070
+ * @param {NdChild|(() => NdChild)} child - Element or content to show/hide
4289
5071
  * @param {Object} [configs] - Configuration options
4290
5072
  * @param {string|null} [configs.comment] - Comment for debugging
4291
5073
  * @param {boolean} [configs.shouldKeepInCache] - Whether to cache element when hidden
@@ -4295,7 +5077,7 @@ var NativeDocument = (function (exports) {
4295
5077
  * HideIf(hasError, Div({}, 'Content'));
4296
5078
  */
4297
5079
  const HideIf = function(condition, child, configs) {
4298
- const hideCondition = Observable(!condition.val());
5080
+ const hideCondition = Observable$1(!condition.val());
4299
5081
  condition.subscribe(value => hideCondition.set(!value));
4300
5082
 
4301
5083
  return ShowIf(hideCondition, child, configs);
@@ -4306,7 +5088,7 @@ var NativeDocument = (function (exports) {
4306
5088
  * Same as ShowIf - element is shown when condition is true.
4307
5089
  *
4308
5090
  * @param {ObservableItem<boolean>|ObservableChecker<boolean>|ObservableWhen} condition - Observable condition to watch
4309
- * @param {ValidChild} child - Element or content to show/hide
5091
+ * @param {NdChild|(() => NdChild)} child - Element or content to show/hide
4310
5092
  * @param {Object} [configs] - Configuration options
4311
5093
  * @param {string|null} [configs.comment] - Comment for debugging
4312
5094
  * @param {boolean} [configs.shouldKeepInCache] - Whether to cache element when hidden
@@ -4322,13 +5104,13 @@ var NativeDocument = (function (exports) {
4322
5104
  *
4323
5105
  * @overload
4324
5106
  * @param {ObservableWhen} observerWhenResult - Result from observable.when(value)
4325
- * @param {ValidChild} view - Content to show when condition matches
5107
+ * @param {NdChild|(() => NdChild)} view - Content to show when condition matches
4326
5108
  * @returns {AnchorDocumentFragment}
4327
5109
  *
4328
5110
  * @overload
4329
5111
  * @param {ObservableItem} observer - Observable to watch
4330
5112
  * @param {*} target - Value to match
4331
- * @param {ValidChild} view - Content to show when observable equals target
5113
+ * @param {NdChild|(() => NdChild)} view - Content to show when observable equals target
4332
5114
  * @returns {AnchorDocumentFragment}
4333
5115
  *
4334
5116
  * @example
@@ -4372,7 +5154,7 @@ var NativeDocument = (function (exports) {
4372
5154
  * Like a switch statement for UI - shows the content corresponding to current value.
4373
5155
  *
4374
5156
  * @param {ObservableItem|ObservableChecker} $condition - Observable to watch
4375
- * @param {Object<string|number, ValidChild>} values - Map of values to their corresponding content
5157
+ * @param {Object<string|number, NdChild|(() => NdChild)>} values - Map of values to their corresponding content
4376
5158
  * @param {boolean} [shouldKeepInCache=true] - Whether to cache rendered views
4377
5159
  * @returns {AnchorDocumentFragment & {add: Function, remove: Function}} Fragment with dynamic methods
4378
5160
  * @example
@@ -4501,41 +5283,202 @@ var NativeDocument = (function (exports) {
4501
5283
  }
4502
5284
  };
4503
5285
 
5286
+ /**
5287
+ * Creates a `<div>` element.
5288
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLDivElement}
5289
+ */
4504
5290
  const Div = HtmlElementWrapper('div');
5291
+
5292
+ /**
5293
+ * Creates a `<span>` element.
5294
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLSpanElement}
5295
+ */
4505
5296
  const Span = HtmlElementWrapper('span');
5297
+
5298
+ /**
5299
+ * Creates a `<label>` element.
5300
+ * @type {function(LabelAttributes=, NdChild|NdChild[]=): HTMLLabelElement}
5301
+ */
4506
5302
  const Label = HtmlElementWrapper('label');
5303
+
5304
+ /**
5305
+ * Creates a `<p>` element.
5306
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLParagraphElement}
5307
+ */
4507
5308
  const P = HtmlElementWrapper('p');
5309
+
5310
+ /**
5311
+ * Alias for {@link P}.
5312
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLParagraphElement}
5313
+ */
4508
5314
  const Paragraph = P;
5315
+
5316
+ /**
5317
+ * Creates a `<strong>` element.
5318
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5319
+ */
4509
5320
  const Strong = HtmlElementWrapper('strong');
5321
+
5322
+ /**
5323
+ * Creates a `<h1>` element.
5324
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLHeadingElement}
5325
+ */
4510
5326
  const H1 = HtmlElementWrapper('h1');
5327
+
5328
+ /**
5329
+ * Creates a `<h2>` element.
5330
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLHeadingElement}
5331
+ */
4511
5332
  const H2 = HtmlElementWrapper('h2');
5333
+
5334
+ /**
5335
+ * Creates a `<h3>` element.
5336
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLHeadingElement}
5337
+ */
4512
5338
  const H3 = HtmlElementWrapper('h3');
5339
+
5340
+ /**
5341
+ * Creates a `<h4>` element.
5342
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLHeadingElement}
5343
+ */
4513
5344
  const H4 = HtmlElementWrapper('h4');
5345
+
5346
+ /**
5347
+ * Creates a `<h5>` element.
5348
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLHeadingElement}
5349
+ */
4514
5350
  const H5 = HtmlElementWrapper('h5');
5351
+
5352
+ /**
5353
+ * Creates a `<h6>` element.
5354
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLHeadingElement}
5355
+ */
4515
5356
  const H6 = HtmlElementWrapper('h6');
4516
5357
 
5358
+ /**
5359
+ * Creates a `<br>` element.
5360
+ * @type {function(GlobalAttributes=): HTMLBRElement}
5361
+ */
4517
5362
  const Br = HtmlElementWrapper('br');
4518
5363
 
5364
+ /**
5365
+ * Creates an `<a>` element.
5366
+ * @type {function(AnchorAttributes=, NdChild|NdChild[]=): HTMLAnchorElement}
5367
+ */
4519
5368
  const Link$1 = HtmlElementWrapper('a');
5369
+
5370
+ /**
5371
+ * Creates a `<pre>` element.
5372
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLPreElement}
5373
+ */
4520
5374
  const Pre = HtmlElementWrapper('pre');
5375
+
5376
+ /**
5377
+ * Creates a `<code>` element.
5378
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5379
+ */
4521
5380
  const Code = HtmlElementWrapper('code');
5381
+
5382
+ /**
5383
+ * Creates a `<blockquote>` element.
5384
+ * @type {function(GlobalAttributes & { cite?: string }=, NdChild|NdChild[]=): HTMLQuoteElement}
5385
+ */
4522
5386
  const Blockquote = HtmlElementWrapper('blockquote');
5387
+
5388
+ /**
5389
+ * Creates an `<hr>` element.
5390
+ * @type {function(GlobalAttributes=): HTMLHRElement}
5391
+ */
4523
5392
  const Hr = HtmlElementWrapper('hr');
5393
+
5394
+ /**
5395
+ * Creates an `<em>` element.
5396
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5397
+ */
4524
5398
  const Em = HtmlElementWrapper('em');
5399
+
5400
+ /**
5401
+ * Creates a `<small>` element.
5402
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5403
+ */
4525
5404
  const Small = HtmlElementWrapper('small');
5405
+
5406
+ /**
5407
+ * Creates a `<mark>` element.
5408
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5409
+ */
4526
5410
  const Mark = HtmlElementWrapper('mark');
5411
+
5412
+ /**
5413
+ * Creates a `<del>` element.
5414
+ * @type {function(ModAttributes=, NdChild|NdChild[]=): HTMLModElement}
5415
+ */
4527
5416
  const Del = HtmlElementWrapper('del');
5417
+
5418
+ /**
5419
+ * Creates an `<ins>` element.
5420
+ * @type {function(ModAttributes=, NdChild|NdChild[]=): HTMLModElement}
5421
+ */
4528
5422
  const Ins = HtmlElementWrapper('ins');
5423
+
5424
+ /**
5425
+ * Creates a `<sub>` element.
5426
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5427
+ */
4529
5428
  const Sub = HtmlElementWrapper('sub');
5429
+
5430
+ /**
5431
+ * Creates a `<sup>` element.
5432
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5433
+ */
4530
5434
  const Sup = HtmlElementWrapper('sup');
5435
+
5436
+ /**
5437
+ * Creates an `<abbr>` element.
5438
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5439
+ */
4531
5440
  const Abbr = HtmlElementWrapper('abbr');
5441
+
5442
+ /**
5443
+ * Creates a `<cite>` element.
5444
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5445
+ */
4532
5446
  const Cite = HtmlElementWrapper('cite');
5447
+
5448
+ /**
5449
+ * Creates a `<q>` element.
5450
+ * @type {function(GlobalAttributes & { cite?: string }=, NdChild|NdChild[]=): HTMLQuoteElement}
5451
+ */
4533
5452
  const Quote = HtmlElementWrapper('q');
4534
5453
 
5454
+ /**
5455
+ * Creates a `<dl>` element.
5456
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLDListElement}
5457
+ */
4535
5458
  const Dl = HtmlElementWrapper('dl');
5459
+
5460
+ /**
5461
+ * Creates a `<dt>` element.
5462
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5463
+ */
4536
5464
  const Dt = HtmlElementWrapper('dt');
5465
+
5466
+ /**
5467
+ * Creates a `<dd>` element.
5468
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5469
+ */
4537
5470
  const Dd = HtmlElementWrapper('dd');
4538
5471
 
5472
+ /**
5473
+ * Creates a `<form>` element.
5474
+ * Extended with fluent methods: `.submit()`, `.post()`, `.get()`, `.multipartFormData()`.
5475
+ * @type {function(FormAttributes=, NdChild|NdChild[]=): HTMLFormElement & {
5476
+ * submit: (actionOrFn: string | ((e: SubmitEvent) => void)) => HTMLFormElement,
5477
+ * post: (action: string) => HTMLFormElement,
5478
+ * get: (action: string) => HTMLFormElement,
5479
+ * multipartFormData: () => HTMLFormElement,
5480
+ * }}
5481
+ */
4539
5482
  const Form = HtmlElementWrapper('form', function(el) {
4540
5483
 
4541
5484
  el.submit = function(action) {
@@ -4565,68 +5508,298 @@ var NativeDocument = (function (exports) {
4565
5508
  return el;
4566
5509
  });
4567
5510
 
5511
+ /**
5512
+ * Creates an `<input>` element.
5513
+ * @type {function(InputAttributes=): HTMLInputElement}
5514
+ */
4568
5515
  const Input = HtmlElementWrapper('input');
4569
5516
 
5517
+ /**
5518
+ * Creates a `<textarea>` element.
5519
+ * @type {function(TextAreaAttributes=, NdChild|NdChild[]=): HTMLTextAreaElement}
5520
+ */
4570
5521
  const TextArea = HtmlElementWrapper('textarea');
5522
+
5523
+ /**
5524
+ * Alias for {@link TextArea}.
5525
+ * @type {function(TextAreaAttributes=, NdChild|NdChild[]=): HTMLTextAreaElement}
5526
+ */
4571
5527
  const TextInput = TextArea;
4572
5528
 
5529
+ /**
5530
+ * Creates a `<select>` element.
5531
+ * @type {function(SelectAttributes=, NdChild|NdChild[]=): HTMLSelectElement}
5532
+ */
4573
5533
  const Select = HtmlElementWrapper('select');
4574
- const FieldSet = HtmlElementWrapper('fieldset', );
5534
+
5535
+ /**
5536
+ * Creates a `<fieldset>` element.
5537
+ * @type {function(GlobalAttributes & { disabled?: Observable<boolean>|boolean }=, NdChild|NdChild[]=): HTMLFieldSetElement}
5538
+ */
5539
+ const FieldSet = HtmlElementWrapper('fieldset');
5540
+
5541
+ /**
5542
+ * Creates an `<option>` element.
5543
+ * @type {function(OptionAttributes=, NdChild|NdChild[]=): HTMLOptionElement}
5544
+ */
4575
5545
  const Option = HtmlElementWrapper('option');
5546
+
5547
+ /**
5548
+ * Creates a `<legend>` element.
5549
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLLegendElement}
5550
+ */
4576
5551
  const Legend = HtmlElementWrapper('legend');
5552
+
5553
+ /**
5554
+ * Creates a `<datalist>` element.
5555
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLDataListElement}
5556
+ */
4577
5557
  const Datalist = HtmlElementWrapper('datalist');
5558
+
5559
+ /**
5560
+ * Creates an `<output>` element.
5561
+ * @type {function(OutputAttributes=, NdChild|NdChild[]=): HTMLOutputElement}
5562
+ */
4578
5563
  const Output = HtmlElementWrapper('output');
5564
+
5565
+ /**
5566
+ * Creates a `<progress>` element.
5567
+ * @type {function(ProgressAttributes=, NdChild|NdChild[]=): HTMLProgressElement}
5568
+ */
4579
5569
  const Progress = HtmlElementWrapper('progress');
5570
+
5571
+ /**
5572
+ * Creates a `<meter>` element.
5573
+ * @type {function(MeterAttributes=, NdChild|NdChild[]=): HTMLMeterElement}
5574
+ */
4580
5575
  const Meter = HtmlElementWrapper('meter');
4581
5576
 
5577
+ /**
5578
+ * Creates an `<input readonly>` element.
5579
+ * @param {Omit<InputAttributes, 'type'|'readonly'|'readOnly'>} [attributes]
5580
+ * @returns {HTMLInputElement}
5581
+ */
4582
5582
  const ReadonlyInput = (attributes) => Input({ readonly: true, ...attributes });
4583
- const HiddenInput = (attributes) => Input({type: 'hidden', ...attributes });
5583
+
5584
+ /**
5585
+ * Creates an `<input type="hidden">` element.
5586
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5587
+ * @returns {HTMLInputElement}
5588
+ */
5589
+ const HiddenInput = (attributes) => Input({ type: 'hidden', ...attributes });
5590
+
5591
+ /**
5592
+ * Creates an `<input type="file">` element.
5593
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5594
+ * @returns {HTMLInputElement}
5595
+ */
4584
5596
  const FileInput = (attributes) => Input({ type: 'file', ...attributes });
5597
+
5598
+ /**
5599
+ * Creates an `<input type="password">` element.
5600
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5601
+ * @returns {HTMLInputElement}
5602
+ */
4585
5603
  const PasswordInput = (attributes) => Input({ type: 'password', ...attributes });
5604
+
5605
+ /**
5606
+ * Creates an `<input type="checkbox">` element.
5607
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5608
+ * @returns {HTMLInputElement}
5609
+ */
4586
5610
  const Checkbox = (attributes) => Input({ type: 'checkbox', ...attributes });
5611
+
5612
+ /**
5613
+ * Creates an `<input type="radio">` element.
5614
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5615
+ * @returns {HTMLInputElement}
5616
+ */
4587
5617
  const Radio = (attributes) => Input({ type: 'radio', ...attributes });
4588
5618
 
5619
+ /**
5620
+ * Creates an `<input type="range">` element.
5621
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5622
+ * @returns {HTMLInputElement}
5623
+ */
4589
5624
  const RangeInput = (attributes) => Input({ type: 'range', ...attributes });
5625
+
5626
+ /**
5627
+ * Creates an `<input type="color">` element.
5628
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5629
+ * @returns {HTMLInputElement}
5630
+ */
4590
5631
  const ColorInput = (attributes) => Input({ type: 'color', ...attributes });
5632
+
5633
+ /**
5634
+ * Creates an `<input type="date">` element.
5635
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5636
+ * @returns {HTMLInputElement}
5637
+ */
4591
5638
  const DateInput = (attributes) => Input({ type: 'date', ...attributes });
5639
+
5640
+ /**
5641
+ * Creates an `<input type="time">` element.
5642
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5643
+ * @returns {HTMLInputElement}
5644
+ */
4592
5645
  const TimeInput = (attributes) => Input({ type: 'time', ...attributes });
5646
+
5647
+ /**
5648
+ * Creates an `<input type="datetime-local">` element.
5649
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5650
+ * @returns {HTMLInputElement}
5651
+ */
4593
5652
  const DateTimeInput = (attributes) => Input({ type: 'datetime-local', ...attributes });
5653
+
5654
+ /**
5655
+ * Creates an `<input type="week">` element.
5656
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5657
+ * @returns {HTMLInputElement}
5658
+ */
4594
5659
  const WeekInput = (attributes) => Input({ type: 'week', ...attributes });
5660
+
5661
+ /**
5662
+ * Creates an `<input type="month">` element.
5663
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5664
+ * @returns {HTMLInputElement}
5665
+ */
4595
5666
  const MonthInput = (attributes) => Input({ type: 'month', ...attributes });
5667
+
5668
+ /**
5669
+ * Creates an `<input type="search">` element.
5670
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5671
+ * @returns {HTMLInputElement}
5672
+ */
4596
5673
  const SearchInput = (attributes) => Input({ type: 'search', ...attributes });
5674
+
5675
+ /**
5676
+ * Creates an `<input type="tel">` element.
5677
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5678
+ * @returns {HTMLInputElement}
5679
+ */
4597
5680
  const TelInput = (attributes) => Input({ type: 'tel', ...attributes });
5681
+
5682
+ /**
5683
+ * Creates an `<input type="url">` element.
5684
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5685
+ * @returns {HTMLInputElement}
5686
+ */
4598
5687
  const UrlInput = (attributes) => Input({ type: 'url', ...attributes });
5688
+
5689
+ /**
5690
+ * Creates an `<input type="email">` element.
5691
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5692
+ * @returns {HTMLInputElement}
5693
+ */
4599
5694
  const EmailInput = (attributes) => Input({ type: 'email', ...attributes });
4600
- const NumberInput = (attributes) => Input({ type: 'number', ...attributes });
4601
5695
 
5696
+ /**
5697
+ * Creates an `<input type="number">` element.
5698
+ * @param {Omit<InputAttributes, 'type'>} [attributes]
5699
+ * @returns {HTMLInputElement}
5700
+ */
5701
+ const NumberInput = (attributes) => Input({ type: 'number', ...attributes });
4602
5702
 
5703
+ /**
5704
+ * Creates a `<button>` element.
5705
+ * @type {function(ButtonAttributes=, NdChild|NdChild[]=): HTMLButtonElement}
5706
+ */
4603
5707
  const Button = HtmlElementWrapper('button');
5708
+
5709
+ /**
5710
+ * Creates a `<button type="button">` element.
5711
+ * @param {NdChild|NdChild[]} [child]
5712
+ * @param {Omit<ButtonAttributes, 'type'>} [attributes]
5713
+ * @returns {HTMLButtonElement}
5714
+ */
4604
5715
  const SimpleButton = (child, attributes) => Button(child, { type: 'button', ...attributes });
5716
+
5717
+ /**
5718
+ * Creates a `<button type="submit">` element.
5719
+ * @param {NdChild|NdChild[]} [child]
5720
+ * @param {Omit<ButtonAttributes, 'type'>} [attributes]
5721
+ * @returns {HTMLButtonElement}
5722
+ */
4605
5723
  const SubmitButton = (child, attributes) => Button(child, { type: 'submit', ...attributes });
4606
5724
 
5725
+ /**
5726
+ * Creates a `<main>` element.
5727
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5728
+ */
4607
5729
  const Main = HtmlElementWrapper('main');
5730
+
5731
+ /**
5732
+ * Creates a `<section>` element.
5733
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5734
+ */
4608
5735
  const Section = HtmlElementWrapper('section');
5736
+
5737
+ /**
5738
+ * Creates an `<article>` element.
5739
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5740
+ */
4609
5741
  const Article = HtmlElementWrapper('article');
5742
+
5743
+ /**
5744
+ * Creates an `<aside>` element.
5745
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5746
+ */
4610
5747
  const Aside = HtmlElementWrapper('aside');
5748
+
5749
+ /**
5750
+ * Creates a `<nav>` element.
5751
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5752
+ */
4611
5753
  const Nav = HtmlElementWrapper('nav');
5754
+
5755
+ /**
5756
+ * Creates a `<figure>` element.
5757
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5758
+ */
4612
5759
  const Figure = HtmlElementWrapper('figure');
5760
+
5761
+ /**
5762
+ * Creates a `<figcaption>` element.
5763
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5764
+ */
4613
5765
  const FigCaption = HtmlElementWrapper('figcaption');
4614
5766
 
5767
+ /**
5768
+ * Creates a `<header>` element.
5769
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5770
+ */
4615
5771
  const Header = HtmlElementWrapper('header');
5772
+
5773
+ /**
5774
+ * Creates a `<footer>` element.
5775
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5776
+ */
4616
5777
  const Footer = HtmlElementWrapper('footer');
4617
5778
 
5779
+ /**
5780
+ * Creates an `<img>` element.
5781
+ * @type {function(ImgAttributes=): HTMLImageElement}
5782
+ */
4618
5783
  const BaseImage = HtmlElementWrapper('img');
5784
+
5785
+ /**
5786
+ * Creates an `<img>` element.
5787
+ * @param {Observable<string>|string} src
5788
+ * @param {Omit<ImgAttributes, 'src'>} [attributes]
5789
+ * @returns {HTMLImageElement}
5790
+ */
4619
5791
  const Img = function(src, attributes) {
4620
5792
  return BaseImage({ src, ...attributes });
4621
5793
  };
4622
5794
 
4623
5795
  /**
4624
- *
4625
- * @param {string} src
4626
- * @param {string|null} defaultImage
4627
- * @param {Object} attributes
4628
- * @param {?Function} callback
4629
- * @returns {Image}
5796
+ * Creates an `<img>` that loads asynchronously, showing a placeholder until the image is ready.
5797
+ * Supports reactive `src` — automatically updates when the observable changes.
5798
+ * @param {Observable<string>|string} src - Final image URL
5799
+ * @param {string|null} defaultImage - Placeholder shown while loading
5800
+ * @param {Omit<ImgAttributes, 'src'>} attributes
5801
+ * @param {(error: NativeDocumentError|null, img: HTMLImageElement) => void} [callback]
5802
+ * @returns {HTMLImageElement}
4630
5803
  */
4631
5804
  const AsyncImg = function(src, defaultImage, attributes, callback) {
4632
5805
  const defaultSrc = Validator.isObservable(src) ? src.val() : src;
@@ -4650,56 +5823,230 @@ var NativeDocument = (function (exports) {
4650
5823
  };
4651
5824
 
4652
5825
  /**
4653
- *
4654
- * @param {string} src
4655
- * @param {Object} attributes
4656
- * @returns {Image}
5826
+ * Creates an `<img loading="lazy">` element.
5827
+ * @param {Observable<string>|string} src
5828
+ * @param {Omit<ImgAttributes, 'src'|'loading'>} [attributes]
5829
+ * @returns {HTMLImageElement}
4657
5830
  */
4658
5831
  const LazyImg = function(src, attributes) {
4659
5832
  return Img(src, { ...attributes, loading: 'lazy' });
4660
5833
  };
4661
5834
 
5835
+ /**
5836
+ * Creates a `<details>` element.
5837
+ * @type {function(DetailsAttributes=, NdChild|NdChild[]=): HTMLDetailsElement}
5838
+ */
4662
5839
  const Details = HtmlElementWrapper('details');
5840
+
5841
+ /**
5842
+ * Creates a `<summary>` element.
5843
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5844
+ */
4663
5845
  const Summary = HtmlElementWrapper('summary');
5846
+
5847
+ /**
5848
+ * Creates a `<dialog>` element.
5849
+ * @type {function(DialogAttributes=, NdChild|NdChild[]=): HTMLDialogElement}
5850
+ */
4664
5851
  const Dialog = HtmlElementWrapper('dialog');
5852
+
5853
+ /**
5854
+ * Creates a `<menu>` element.
5855
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLMenuElement}
5856
+ */
4665
5857
  const Menu = HtmlElementWrapper('menu');
4666
5858
 
5859
+ /**
5860
+ * Creates an `<ol>` element.
5861
+ * @type {function(OlAttributes=, NdChild|NdChild[]=): HTMLOListElement}
5862
+ */
4667
5863
  const OrderedList = HtmlElementWrapper('ol');
5864
+
5865
+ /**
5866
+ * Creates a `<ul>` element.
5867
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLUListElement}
5868
+ */
4668
5869
  const UnorderedList = HtmlElementWrapper('ul');
5870
+
5871
+ /**
5872
+ * Creates a `<li>` element.
5873
+ * @type {function(GlobalAttributes & { value?: number }=, NdChild|NdChild[]=): HTMLLIElement}
5874
+ */
4669
5875
  const ListItem = HtmlElementWrapper('li');
4670
5876
 
5877
+ /**
5878
+ * Alias for {@link ListItem}.
5879
+ * @type {typeof ListItem}
5880
+ */
4671
5881
  const Li = ListItem;
5882
+
5883
+ /**
5884
+ * Alias for {@link OrderedList}.
5885
+ * @type {typeof OrderedList}
5886
+ */
4672
5887
  const Ol = OrderedList;
5888
+
5889
+ /**
5890
+ * Alias for {@link UnorderedList}.
5891
+ * @type {typeof UnorderedList}
5892
+ */
4673
5893
  const Ul = UnorderedList;
4674
5894
 
5895
+ /**
5896
+ * Creates an `<audio>` element.
5897
+ * @type {function(AudioAttributes=, NdChild|NdChild[]=): HTMLAudioElement}
5898
+ */
4675
5899
  const Audio = HtmlElementWrapper('audio');
5900
+
5901
+ /**
5902
+ * Creates a `<video>` element.
5903
+ * @type {function(VideoAttributes=, NdChild|NdChild[]=): HTMLVideoElement}
5904
+ */
4676
5905
  const Video = HtmlElementWrapper('video');
5906
+
5907
+ /**
5908
+ * Creates a `<source>` element.
5909
+ * @type {function(SourceAttributes=): HTMLSourceElement}
5910
+ */
4677
5911
  const Source = HtmlElementWrapper('source');
5912
+
5913
+ /**
5914
+ * Creates a `<track>` element.
5915
+ * @type {function(TrackAttributes=): HTMLTrackElement}
5916
+ */
4678
5917
  const Track = HtmlElementWrapper('track');
5918
+
5919
+ /**
5920
+ * Creates a `<canvas>` element.
5921
+ * @type {function(CanvasAttributes=, NdChild|NdChild[]=): HTMLCanvasElement}
5922
+ */
4679
5923
  const Canvas = HtmlElementWrapper('canvas');
5924
+
5925
+ /**
5926
+ * Creates an `<svg>` element.
5927
+ * @type {function(SvgAttributes=, NdChild|NdChild[]=): SVGSVGElement}
5928
+ */
4680
5929
  const Svg = HtmlElementWrapper('svg');
4681
5930
 
5931
+ /**
5932
+ * Creates a `<time>` element.
5933
+ * @type {function(TimeAttributes=, NdChild|NdChild[]=): HTMLTimeElement}
5934
+ */
4682
5935
  const Time = HtmlElementWrapper('time');
5936
+
5937
+ /**
5938
+ * Creates a `<data>` element.
5939
+ * @type {function(GlobalAttributes & { value?: Observable<string>|string }=, NdChild|NdChild[]=): HTMLDataElement}
5940
+ */
4683
5941
  const Data = HtmlElementWrapper('data');
5942
+
5943
+ /**
5944
+ * Creates an `<address>` element.
5945
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5946
+ */
4684
5947
  const Address = HtmlElementWrapper('address');
5948
+
5949
+ /**
5950
+ * Creates a `<kbd>` element.
5951
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5952
+ */
4685
5953
  const Kbd = HtmlElementWrapper('kbd');
5954
+
5955
+ /**
5956
+ * Creates a `<samp>` element.
5957
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5958
+ */
4686
5959
  const Samp = HtmlElementWrapper('samp');
5960
+
5961
+ /**
5962
+ * Creates a `<var>` element.
5963
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
5964
+ */
4687
5965
  const Var = HtmlElementWrapper('var');
5966
+
5967
+ /**
5968
+ * Creates a `<wbr>` element.
5969
+ * @type {function(GlobalAttributes=): HTMLElement}
5970
+ */
4688
5971
  const Wbr = HtmlElementWrapper('wbr');
4689
5972
 
5973
+ /**
5974
+ * Creates a `<caption>` element.
5975
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLTableCaptionElement}
5976
+ */
4690
5977
  const Caption = HtmlElementWrapper('caption');
5978
+
5979
+ /**
5980
+ * Creates a `<table>` element.
5981
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLTableElement}
5982
+ */
4691
5983
  const Table = HtmlElementWrapper('table');
5984
+
5985
+ /**
5986
+ * Creates a `<thead>` element.
5987
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLTableSectionElement}
5988
+ */
4692
5989
  const THead = HtmlElementWrapper('thead');
5990
+
5991
+ /**
5992
+ * Creates a `<tfoot>` element.
5993
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLTableSectionElement}
5994
+ */
4693
5995
  const TFoot = HtmlElementWrapper('tfoot');
5996
+
5997
+ /**
5998
+ * Creates a `<tbody>` element.
5999
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLTableSectionElement}
6000
+ */
4694
6001
  const TBody = HtmlElementWrapper('tbody');
6002
+
6003
+ /**
6004
+ * Creates a `<tr>` element.
6005
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLTableRowElement}
6006
+ */
4695
6007
  const Tr = HtmlElementWrapper('tr');
6008
+
6009
+ /**
6010
+ * Alias for {@link Tr}.
6011
+ * @type {typeof Tr}
6012
+ */
4696
6013
  const TRow = Tr;
6014
+
6015
+ /**
6016
+ * Creates a `<th>` element.
6017
+ * @type {function(ThAttributes=, NdChild|NdChild[]=): HTMLTableCellElement}
6018
+ */
4697
6019
  const Th = HtmlElementWrapper('th');
6020
+
6021
+ /**
6022
+ * Alias for {@link Th}.
6023
+ * @type {typeof Th}
6024
+ */
4698
6025
  const THeadCell = Th;
6026
+
6027
+ /**
6028
+ * Alias for {@link Th}.
6029
+ * @type {typeof Th}
6030
+ */
4699
6031
  const TFootCell = Th;
6032
+
6033
+ /**
6034
+ * Creates a `<td>` element.
6035
+ * @type {function(TdAttributes=, NdChild|NdChild[]=): HTMLTableCellElement}
6036
+ */
4700
6037
  const Td = HtmlElementWrapper('td');
6038
+
6039
+ /**
6040
+ * Alias for {@link Td}.
6041
+ * @type {typeof Td}
6042
+ */
4701
6043
  const TBodyCell = Td;
4702
6044
 
6045
+ /**
6046
+ * Creates an empty `DocumentFragment` wrapper.
6047
+ * Useful for grouping elements without adding a DOM node.
6048
+ * @type {function(GlobalAttributes=, NdChild|NdChild[]=): DocumentFragment}
6049
+ */
4703
6050
  const Fragment = HtmlElementWrapper('');
4704
6051
 
4705
6052
  var elements = /*#__PURE__*/Object.freeze({
@@ -5102,7 +6449,7 @@ var NativeDocument = (function (exports) {
5102
6449
  window.history.pushState({ name: route.name(), params, path}, route.name() || path , path);
5103
6450
  this.handleRouteChange(route, params, query, path);
5104
6451
  } catch (e) {
5105
- DebugManager$1.error('HistoryRouter', 'Error in pushState', e);
6452
+ DebugManager.error('HistoryRouter', 'Error in pushState', e);
5106
6453
  }
5107
6454
  };
5108
6455
  /**
@@ -5115,7 +6462,7 @@ var NativeDocument = (function (exports) {
5115
6462
  window.history.replaceState({ name: route.name(), params, path}, route.name() || path , path);
5116
6463
  this.handleRouteChange(route, params, {}, path);
5117
6464
  } catch(e) {
5118
- DebugManager$1.error('HistoryRouter', 'Error in replaceState', e);
6465
+ DebugManager.error('HistoryRouter', 'Error in replaceState', e);
5119
6466
  }
5120
6467
  };
5121
6468
  this.forward = function() {
@@ -5142,7 +6489,7 @@ var NativeDocument = (function (exports) {
5142
6489
  }
5143
6490
  this.handleRouteChange(route, params, query, path);
5144
6491
  } catch(e) {
5145
- DebugManager$1.error('HistoryRouter', 'Error in popstate event', e);
6492
+ DebugManager.error('HistoryRouter', 'Error in popstate event', e);
5146
6493
  }
5147
6494
  });
5148
6495
  const { route, params, query, path } = this.resolve(defaultPath || (window.location.pathname+window.location.search));
@@ -5367,7 +6714,7 @@ var NativeDocument = (function (exports) {
5367
6714
  listener(request);
5368
6715
  next && next(request);
5369
6716
  } catch (e) {
5370
- DebugManager$1.warn('Route Listener', 'Error in listener:', e);
6717
+ DebugManager.warn('Route Listener', 'Error in listener:', e);
5371
6718
  }
5372
6719
  }
5373
6720
  };
@@ -5545,7 +6892,7 @@ var NativeDocument = (function (exports) {
5545
6892
  */
5546
6893
  Router.create = function(options, callback) {
5547
6894
  if(!Validator.isFunction(callback)) {
5548
- DebugManager$1.error('Router', 'Callback must be a function');
6895
+ DebugManager.error('Router', 'Callback must be a function');
5549
6896
  throw new RouterError('Callback must be a function');
5550
6897
  }
5551
6898
  const router = new Router(options);
@@ -5663,11 +7010,14 @@ var NativeDocument = (function (exports) {
5663
7010
  configs.body = params;
5664
7011
  }
5665
7012
  else {
5666
- configs.headers['Content-Type'] = 'application/json';
5667
7013
  if(method !== 'GET') {
7014
+ configs.headers['Content-Type'] = 'application/json';
5668
7015
  configs.body = JSON.stringify(params);
5669
7016
  } else {
5670
- configs.params = params;
7017
+ const queryString = new URLSearchParams(params).toString();
7018
+ if (queryString) {
7019
+ endpoint = endpoint + (endpoint.includes('?') ? '&' : '?') + queryString;
7020
+ }
5671
7021
  }
5672
7022
  }
5673
7023
  }
@@ -5734,10 +7084,11 @@ var NativeDocument = (function (exports) {
5734
7084
  exports.ElementCreator = ElementCreator;
5735
7085
  exports.HtmlElementWrapper = HtmlElementWrapper;
5736
7086
  exports.NDElement = NDElement;
5737
- exports.Observable = Observable;
5738
- exports.PluginsManager = PluginsManager$1;
7087
+ exports.Observable = Observable$1;
7088
+ exports.PluginsManager = PluginsManager;
5739
7089
  exports.SingletonView = SingletonView;
5740
7090
  exports.Store = Store;
7091
+ exports.StoreFactory = StoreFactory;
5741
7092
  exports.TemplateCloner = TemplateCloner;
5742
7093
  exports.Validator = Validator;
5743
7094
  exports.autoMemoize = autoMemoize;