@ktjs/core 0.27.2 → 0.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -13,6 +13,14 @@ declare class KTComputed<T> {
13
13
  */
14
14
  get value(): T;
15
15
  set value(_newValue: T);
16
+ /**
17
+ * Force listeners to run once with the latest computed result.
18
+ */
19
+ notify(): void;
20
+ /**
21
+ * Computed values are derived from dependencies and should not be mutated manually.
22
+ */
23
+ mutate<R = void>(_mutator?: (value: T) => R): void;
16
24
  /**
17
25
  * Register a callback when the value changes
18
26
  * @param callback (newValue, oldValue) => xxx
@@ -45,8 +53,11 @@ interface KTEffectOptions {
45
53
  */
46
54
  declare function effect(effectFn: () => void, reactives: Array<KTReactive<any>>, options?: Partial<KTEffectOptions>): () => void;
47
55
 
48
- type KTReactive<T> = KTRef<T> | KTComputed<T>;
49
56
  declare const toReactive: <T>(value: T | KTReactive<T>, onChange?: ReactiveChangeHandler<T>) => KTReactive<T>;
57
+ /**
58
+ * Extracts the value from a KTReactive, or returns the value directly if it's not reactive.
59
+ */
60
+ declare function dereactive<T = JSX.Element>(value: T | KTReactive<T>): T;
50
61
  type KTReactify<T> = T extends boolean ? KTReactive<boolean> : T extends any ? KTReactive<T> : never;
51
62
  type KTReactifyObject<T extends object> = {
52
63
  [K in keyof T]: KTReactify<T[K]>;
@@ -62,8 +73,45 @@ declare const enum KTReactiveType {
62
73
  declare const isKT: <T = any>(obj: any) => obj is KTReactive<T>;
63
74
  declare const isRef: <T = any>(obj: any) => obj is KTRef<T>;
64
75
  declare const isComputed: <T = any>(obj: any) => obj is KTComputed<T>;
76
+
65
77
  type ReactiveChangeHandler<T> = (newValue: T, oldValue: T) => void;
66
78
 
79
+ declare class KTReactive<T> {
80
+ /**
81
+ * Indicates that this is a KTRef instance
82
+ */
83
+ isKT: boolean;
84
+
85
+ ktType: KTReactiveType;
86
+
87
+ /**
88
+ * If new value and old value are both nodes, the old one will be replaced in the DOM
89
+ */
90
+ get value();
91
+ set value(newValue: T);
92
+
93
+ /**
94
+ * Force all listeners to run even when reference identity has not changed.
95
+ * Useful for in-place array/object mutations.
96
+ */
97
+ notify(): void;
98
+
99
+ /**
100
+ * Mutate current value in-place and notify listeners once.
101
+ *
102
+ * @example
103
+ * const items = ref<number[]>([1, 2]);
104
+ * items.mutate((list) => list.push(3));
105
+ */
106
+ mutate<R = void>(mutator: (currentValue: T) => R): R;
107
+ /**
108
+ * Register a callback when the value changes
109
+ * @param callback (newValue, oldValue) => xxx
110
+ */
111
+ addOnChange(callback: ReactiveChangeHandler<T>): void;
112
+ removeOnChange(callback: ReactiveChangeHandler<T>): void;
113
+ }
114
+
67
115
  declare class KTRef<T> {
68
116
  /**
69
117
  * Indicates that this is a KTRef instance
@@ -76,6 +124,19 @@ declare class KTRef<T> {
76
124
  */
77
125
  get value(): T;
78
126
  set value(newValue: T);
127
+ /**
128
+ * Force all listeners to run even when reference identity has not changed.
129
+ * Useful for in-place array/object mutations.
130
+ */
131
+ notify(): void;
132
+ /**
133
+ * Mutate current value in-place and notify listeners once.
134
+ *
135
+ * @example
136
+ * const items = ref<number[]>([1, 2]);
137
+ * items.mutate((list) => list.push(3));
138
+ */
139
+ mutate<R = void>(mutator: (currentValue: T) => R): R;
79
140
  /**
80
141
  * Register a callback when the value changes
81
142
  * @param callback (newValue, oldValue) => xxx
@@ -99,7 +160,6 @@ declare function ref<T = JSX.Element>(value?: T, onChange?: ReactiveChangeHandle
99
160
  * @param o value to convert
100
161
  */
101
162
  declare const toRef: <T = any>(o: any) => KTRef<T>;
102
- declare function deref<T = JSX.Element>(value: T | KTReactive<T>): T;
103
163
  type KTSurfaceRef<T extends Object> = {
104
164
  [K in keyof T]: KTRef<T[K]>;
105
165
  } & {
@@ -243,7 +303,7 @@ type KTComponent = (
243
303
  * ## About
244
304
  * @package @ktjs/core
245
305
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
246
- * @version 0.27.2 (Last Update: 2026.02.10 07:27:25.966)
306
+ * @version 0.28.1 (Last Update: 2026.02.10 11:23:01.128)
247
307
  * @license MIT
248
308
  * @link https://github.com/baendlorel/kt.js
249
309
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -1404,5 +1464,5 @@ interface KTForProps<T> {
1404
1464
  */
1405
1465
  declare function KTFor<T>(props: KTForProps<T>): KTForElement;
1406
1466
 
1407
- export { $modelOrRef, $setRef, Fragment, KTAsync, KTComputed, KTFor, KTReactiveType, KTRef, computed, h as createElement, createRedrawable, deref, effect, h, isComputed, isKT, isRef, jsx, jsxDEV, jsxs, ref, surfaceRef, toReactive, toRef };
1408
- export type { EventHandler, KTAttribute, KTForElement, KTForProps, KTPrefixedEventAttribute, KTRawAttr, KTRawContent, KTRawContents, KTReactify, KTReactifyObject, KTReactifyProps, KTReactive, KTSurfaceRef, ReactiveChangeHandler };
1467
+ export { $modelOrRef, $setRef, Fragment, KTAsync, KTComputed, KTFor, KTReactive, KTReactiveType, KTRef, computed, h as createElement, createRedrawable, dereactive, effect, h, isComputed, isKT, isRef, jsx, jsxDEV, jsxs, ref, surfaceRef, toReactive, toRef };
1468
+ export type { EventHandler, KTAttribute, KTForElement, KTForProps, KTPrefixedEventAttribute, KTRawAttr, KTRawContent, KTRawContents, KTReactify, KTReactifyObject, KTReactifyProps, KTSurfaceRef, ReactiveChangeHandler };
@@ -14,29 +14,6 @@ var __ktjs_core__ = (function (exports) {
14
14
  };
15
15
  }
16
16
 
17
- // String manipulation utilities
18
- /**
19
- * Default empty function
20
- */
21
- const $emptyFn = (() => true);
22
- const $emptyArray = [];
23
- /**
24
- * Safe and quick forEach implementation that works with array-like objects and handles sparse arrays.
25
- */
26
- const $forEach = (array, callback) => {
27
- const len = array.length;
28
- for (let i = 0; i < len; i++) {
29
- callback(array[i], i, array);
30
- }
31
- };
32
-
33
- const $emptyChildrenRef = { value: $emptyArray };
34
- // each instance shares the same empty array, but it will be replaced when used
35
- Comment.prototype.kisFragmentAnchor = false;
36
- Comment.prototype.kFragmentList = $emptyArray;
37
- Comment.prototype.kredraw = $emptyFn;
38
- Comment.prototype.kchildrenRef = $emptyChildrenRef;
39
-
40
17
  // Shared constants
41
18
  // Empty for now - can be extended with framework-wide constants
42
19
  /**
@@ -107,8 +84,23 @@ var __ktjs_core__ = (function (exports) {
107
84
  element.addEventListener(eventName, () => (valueRef.value = element[propName]));
108
85
  };
109
86
 
87
+ // String manipulation utilities
88
+ /**
89
+ * Default empty function
90
+ */
91
+ const $emptyFn = (() => true);
92
+ /**
93
+ * Safe and quick forEach implementation that works with array-like objects and handles sparse arrays.
94
+ */
95
+ const $forEach = (array, callback) => {
96
+ const len = array.length;
97
+ for (let i = 0; i < len; i++) {
98
+ callback(array[i], i, array);
99
+ }
100
+ };
101
+
110
102
  // incase that symbol is not supported
111
- Object.defineProperty(window, '__ktjs__', { value: '0.23.10' });
103
+ Object.defineProperty(window, '__ktjs__', { value: '0.23.11' });
112
104
 
113
105
  const isKT = (obj) => obj?.isKT;
114
106
  const isRef = (obj) => obj?.ktType === 1 /* KTReactiveType.REF */;
@@ -319,6 +311,14 @@ var __ktjs_core__ = (function (exports) {
319
311
  * @internal
320
312
  */
321
313
  _onChanges;
314
+ /**
315
+ * @internal
316
+ */
317
+ _emit(newValue, oldValue) {
318
+ for (let i = 0; i < this._onChanges.length; i++) {
319
+ this._onChanges[i](newValue, oldValue);
320
+ }
321
+ }
322
322
  constructor(_value, _onChanges) {
323
323
  this._value = _value;
324
324
  this._onChanges = _onChanges;
@@ -336,9 +336,30 @@ var __ktjs_core__ = (function (exports) {
336
336
  const oldValue = this._value;
337
337
  $replaceNode(oldValue, newValue);
338
338
  this._value = newValue;
339
- for (let i = 0; i < this._onChanges.length; i++) {
340
- this._onChanges[i](newValue, oldValue);
339
+ this._emit(newValue, oldValue);
340
+ }
341
+ /**
342
+ * Force all listeners to run even when reference identity has not changed.
343
+ * Useful for in-place array/object mutations.
344
+ */
345
+ notify() {
346
+ this._emit(this._value, this._value);
347
+ }
348
+ /**
349
+ * Mutate current value in-place and notify listeners once.
350
+ *
351
+ * @example
352
+ * const items = ref<number[]>([1, 2]);
353
+ * items.mutate((list) => list.push(3));
354
+ */
355
+ mutate(mutator) {
356
+ if (typeof mutator !== 'function') {
357
+ throw new Error('[kt.js error] KTRef.mutate: mutator must be a function');
341
358
  }
359
+ const oldValue = this._value;
360
+ const result = mutator(this._value);
361
+ this._emit(this._value, oldValue);
362
+ return result;
342
363
  }
343
364
  /**
344
365
  * Register a callback when the value changes
@@ -388,9 +409,6 @@ var __ktjs_core__ = (function (exports) {
388
409
  return ref(o);
389
410
  }
390
411
  };
391
- function deref(value) {
392
- return isKT(value) ? value.value : value;
393
- }
394
412
  function kcollect() {
395
413
  const newObj = {};
396
414
  const entries = $entries(this);
@@ -462,23 +480,37 @@ var __ktjs_core__ = (function (exports) {
462
480
  * @internal
463
481
  */
464
482
  _onChanges = [];
483
+ /**
484
+ * @internal
485
+ */
486
+ _emit(newValue, oldValue) {
487
+ for (let i = 0; i < this._onChanges.length; i++) {
488
+ this._onChanges[i](newValue, oldValue);
489
+ }
490
+ }
491
+ /**
492
+ * @internal
493
+ */
494
+ _recalculate(forceEmit = false) {
495
+ const oldValue = this._value;
496
+ const newValue = this._calculator();
497
+ if (oldValue === newValue) {
498
+ if (forceEmit) {
499
+ this._emit(newValue, oldValue);
500
+ }
501
+ return;
502
+ }
503
+ this._value = newValue;
504
+ $replaceNode(oldValue, newValue);
505
+ this._emit(newValue, oldValue);
506
+ }
465
507
  /**
466
508
  * @internal
467
509
  */
468
510
  _subscribe(reactives) {
469
511
  for (let i = 0; i < reactives.length; i++) {
470
512
  const reactive = reactives[i];
471
- reactive.addOnChange(() => {
472
- const oldValue = this._value;
473
- this._value = this._calculator();
474
- if (oldValue === this._value) {
475
- return;
476
- }
477
- $replaceNode(oldValue, this._value);
478
- for (let i = 0; i < this._onChanges.length; i++) {
479
- this._onChanges[i](this._value, oldValue);
480
- }
481
- });
513
+ reactive.addOnChange(() => this._recalculate());
482
514
  }
483
515
  }
484
516
  constructor(_calculator, reactives) {
@@ -495,6 +527,18 @@ var __ktjs_core__ = (function (exports) {
495
527
  set value(_newValue) {
496
528
  throw new Error('[kt.js error] KTComputed: cannot set value of a computed value');
497
529
  }
530
+ /**
531
+ * Force listeners to run once with the latest computed result.
532
+ */
533
+ notify() {
534
+ this._recalculate(true);
535
+ }
536
+ /**
537
+ * Computed values are derived from dependencies and should not be mutated manually.
538
+ */
539
+ mutate(_mutator) {
540
+ console.warn('[kt.js warn]','KTComputed.mutate: computed is derived automatically; manual mutate is ignored. Use notify() instead');
541
+ }
498
542
  /**
499
543
  * Register a callback when the value changes
500
544
  * @param callback (newValue, oldValue) => xxx
@@ -587,6 +631,12 @@ var __ktjs_core__ = (function (exports) {
587
631
  return ref(value, onChange);
588
632
  }
589
633
  };
634
+ /**
635
+ * Extracts the value from a KTReactive, or returns the value directly if it's not reactive.
636
+ */
637
+ function dereactive(value) {
638
+ return isKT(value) ? value.value : value;
639
+ }
590
640
 
591
641
  function applyKModel(element, valueRef) {
592
642
  if (!isKT(valueRef)) {
@@ -626,7 +676,7 @@ var __ktjs_core__ = (function (exports) {
626
676
  * ## About
627
677
  * @package @ktjs/core
628
678
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
629
- * @version 0.27.2 (Last Update: 2026.02.10 07:27:25.966)
679
+ * @version 0.28.1 (Last Update: 2026.02.10 11:23:01.128)
630
680
  * @license MIT
631
681
  * @link https://github.com/baendlorel/kt.js
632
682
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -661,34 +711,6 @@ var __ktjs_core__ = (function (exports) {
661
711
  return element;
662
712
  };
663
713
 
664
- const kredraw = function () {
665
- const newElements = this.kchildrenRef.value;
666
- const parent = this.parentNode;
667
- if (!parent) {
668
- // If anchor is not in DOM, only update internal state
669
- this.kFragmentList.length = 0;
670
- for (let i = 0; i < newElements.length; i++) {
671
- this.kFragmentList.push(newElements[i]);
672
- }
673
- return;
674
- }
675
- // Simple replacement algorithm: remove all old elements, insert all new elements
676
- // todo Future enhancement: key-based optimization
677
- // 1. Remove all old elements
678
- for (let i = 0; i < this.kFragmentList.length; i++) {
679
- this.kFragmentList[i].remove();
680
- }
681
- // 2. Insert all new elements
682
- const fragment = document.createDocumentFragment();
683
- this.kFragmentList.length = 0;
684
- for (let i = 0; i < newElements.length; i++) {
685
- const element = newElements[i];
686
- this.kFragmentList.push(element);
687
- fragment.appendChild(element);
688
- }
689
- // Insert after anchor
690
- parent.insertBefore(fragment, this.nextSibling);
691
- };
692
714
  /**
693
715
  * Fragment - Container component for managing arrays of child elements
694
716
  *
@@ -710,17 +732,44 @@ var __ktjs_core__ = (function (exports) {
710
732
  */
711
733
  function Fragment$1(props) {
712
734
  // key parameter reserved for future enhancement, currently unused
713
- const { key: _key } = props;
714
- const childrenRef = toReactive(props.children, () => anchor.kredraw());
735
+ // const { key: _key } = props;
736
+ const redraw = () => {
737
+ const newElements = childrenRef.value;
738
+ const parent = anchor.parentNode;
739
+ if (!parent) {
740
+ // If anchor is not in DOM, only update internal state
741
+ elements.length = 0;
742
+ for (let i = 0; i < newElements.length; i++) {
743
+ elements.push(newElements[i]);
744
+ }
745
+ return;
746
+ }
747
+ // Simple replacement algorithm: remove all old elements, insert all new elements
748
+ // todo Future enhancement: key-based optimization
749
+ // 1. Remove all old elements
750
+ for (let i = 0; i < elements.length; i++) {
751
+ elements[i].remove();
752
+ }
753
+ // 2. Insert all new elements
754
+ const fragment = document.createDocumentFragment();
755
+ elements.length = 0;
756
+ for (let i = 0; i < newElements.length; i++) {
757
+ const element = newElements[i];
758
+ elements.push(element);
759
+ fragment.appendChild(element);
760
+ }
761
+ // Insert after anchor
762
+ parent.insertBefore(fragment, anchor.nextSibling);
763
+ };
764
+ let initialized = false;
765
+ const childrenRef = toReactive(props.children, redraw);
766
+ const elements = [];
715
767
  const anchor = document.createComment('kt-fragment');
716
- anchor.kredraw = kredraw;
717
- anchor.kchildrenRef = childrenRef;
718
- anchor.kFragmentList = [];
719
- anchor.kisFragmentAnchor = true;
720
768
  // Observe DOM insertion
721
769
  const observer = new MutationObserver(() => {
722
- if (anchor.isConnected) {
723
- anchor.kredraw();
770
+ if (anchor.isConnected && !initialized) {
771
+ initialized = true;
772
+ redraw();
724
773
  observer.disconnect();
725
774
  }
726
775
  });
@@ -1075,7 +1124,7 @@ var __ktjs_core__ = (function (exports) {
1075
1124
  exports.computed = computed;
1076
1125
  exports.createElement = h;
1077
1126
  exports.createRedrawable = createRedrawable;
1078
- exports.deref = deref;
1127
+ exports.dereactive = dereactive;
1079
1128
  exports.effect = effect;
1080
1129
  exports.h = h;
1081
1130
  exports.isComputed = isComputed;