ctx-core 2.2.2 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { isPromise } from '../isPromise/index.js'
1
+ import { isPromiseLike } from '../isPromiseLike/index.js'
2
2
  /**
3
3
  * Returns an async function, that, as long as it continues to be invoked, will not
4
4
  * be triggered. The function will be called after it stops being called for
@@ -35,7 +35,7 @@ export function debounce(func, wait, immediate) {
35
35
  async function invoke() {
36
36
  try {
37
37
  const in_rv = func.apply(apply_this, arg_a)
38
- resolve(isPromise(in_rv) ? await in_rv : in_rv)
38
+ resolve(isPromiseLike(in_rv) ? await in_rv : in_rv)
39
39
  } catch (e) {
40
40
  reject(e)
41
41
  }
package/all/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from './Map__object/index.js'
5
5
  export * from './NumericRange/index.js'
6
6
  export * from './NumericUnit/index.js'
7
7
  export * from './PHI/index.js'
8
+ export * from './primitive/index.js'
8
9
  export * from './Request/index.js'
9
10
  export * from './Response/index.js'
10
11
  export * from './Timeout/index.js'
@@ -188,7 +189,8 @@ export * from './isNumber/index.js'
188
189
  export * from './isNumber_andor/index.js'
189
190
  export * from './isNumber_or/index.js'
190
191
  export * from './isObject/index.js'
191
- export * from './isPromise/index.js'
192
+ export * from './isPrimitive/index.js'
193
+ export * from './isPromiseLike/index.js'
192
194
  export * from './item_r_idx/index.js'
193
195
  export * from './key_compare/index.js'
194
196
  export * from './key_r/index.js'
@@ -222,6 +224,7 @@ export * from './matrix__validate_length/index.js'
222
224
  export * from './maybe/index.js'
223
225
  export * from './mean/index.js'
224
226
  export * from './median/index.js'
227
+ export * from './memo/index.js'
225
228
  export * from './merge/index.js'
226
229
  export * from './minute/index.js'
227
230
  export * from './minute/index.js'
@@ -291,6 +294,7 @@ export * from './prev_idx/index.js'
291
294
  export * from './promise/index.js'
292
295
  export * from './promise_o/index.js'
293
296
  export * from './promise_timeout/index.js'
297
+ export * from './prototype/index.js'
294
298
  export * from './prototype_mixin/index.js'
295
299
  export * from './push/index.js'
296
300
  export * from './quantile/index.js'
package/all/index.js CHANGED
@@ -5,6 +5,7 @@ export * from './Map__object/index.js'
5
5
  export * from './NumericRange/index.js'
6
6
  export * from './NumericUnit/index.js'
7
7
  export * from './PHI/index.js'
8
+ export * from './primitive/index.js'
8
9
  export * from './Request/index.js'
9
10
  export * from './Response/index.js'
10
11
  export * from './Timeout/index.js'
@@ -188,7 +189,8 @@ export * from './isNumber/index.js'
188
189
  export * from './isNumber_andor/index.js'
189
190
  export * from './isNumber_or/index.js'
190
191
  export * from './isObject/index.js'
191
- export * from './isPromise/index.js'
192
+ export * from './isPrimitive/index.js'
193
+ export * from './isPromiseLike/index.js'
192
194
  export * from './item_r_idx/index.js'
193
195
  export * from './key_compare/index.js'
194
196
  export * from './key_r/index.js'
@@ -222,6 +224,7 @@ export * from './matrix__validate_length/index.js'
222
224
  export * from './maybe/index.js'
223
225
  export * from './mean/index.js'
224
226
  export * from './median/index.js'
227
+ export * from './memo/index.js'
225
228
  export * from './merge/index.js'
226
229
  export * from './minute/index.js'
227
230
  export * from './minute/index.js'
@@ -291,6 +294,7 @@ export * from './prev_idx/index.js'
291
294
  export * from './promise/index.js'
292
295
  export * from './promise_o/index.js'
293
296
  export * from './promise_timeout/index.js'
297
+ export * from './prototype/index.js'
294
298
  export * from './prototype_mixin/index.js'
295
299
  export * from './push/index.js'
296
300
  export * from './quantile/index.js'
@@ -13,7 +13,7 @@ export function intersection_set_(member_a_nowrap_a_nowrap) {
13
13
  const rest_a_nowrap = rest_a_nowrap_a[i]
14
14
  const rest_a = wrap_a_(rest_a_nowrap)
15
15
  const current_set = new Set(rest_a)
16
- member_set.forEach((member)=>{
16
+ member_set.forEach(member=>{
17
17
  if (!current_set.has(member)) member_set.delete(member)
18
18
  })
19
19
  }
@@ -0,0 +1 @@
1
+ export declare function isPrimitive(v:unknown):boolean
@@ -0,0 +1,6 @@
1
+ export function isPrimitive(val) {
2
+ return (
3
+ val === null
4
+ || (typeof val !== 'object' && typeof val !== 'function')
5
+ )
6
+ }
@@ -0,0 +1,17 @@
1
+ import { test } from 'uvu'
2
+ import { equal } from 'uvu/assert'
3
+ import { isPrimitive } from './index.js'
4
+ test('isPrimitive', ()=>{
5
+ equal(isPrimitive(1), true)
6
+ equal(isPrimitive('foo'), true)
7
+ equal(isPrimitive(null), true)
8
+ equal(isPrimitive(undefined), true)
9
+ equal(isPrimitive(true), true)
10
+ equal(isPrimitive(false), true)
11
+ equal(isPrimitive(Symbol('foo')), true)
12
+ equal(isPrimitive({}), false)
13
+ equal(isPrimitive([]), false)
14
+ equal(isPrimitive(()=>1), false)
15
+ equal(isPrimitive(new Date), false)
16
+ })
17
+ test.run()
@@ -0,0 +1,4 @@
1
+ export declare function isPromiseLike(val:any):boolean
2
+ export {
3
+ isPromiseLike as isPromise,
4
+ }
@@ -2,6 +2,9 @@
2
2
  * @param {unknown}val
3
3
  * @returns {boolean}
4
4
  */
5
- export function isPromise(val) {
5
+ export function isPromiseLike(val) {
6
6
  return val ? typeof val.then === 'function' : false
7
7
  }
8
+ export {
9
+ isPromiseLike as isPromise,
10
+ }
@@ -0,0 +1,6 @@
1
+ export declare function memo_<
2
+ ret_T,
3
+ arg_a_T extends Array<unknown>,
4
+ >(fn:(...arg_a:arg_a_T)=>ret_T):((...arg_a:arg_a_T)=>ret_T)&{
5
+ clear():void
6
+ }
@@ -0,0 +1,61 @@
1
+ import { isPrimitive } from '../isPrimitive/index.js'
2
+ import { prototype_ } from '../prototype/index.js'
3
+ let empty_sym, WeakRef_proto
4
+ /**
5
+ * @param {(...arg_a:any[])=>any}fn
6
+ * @returns A memo caching arguments & return values. Arguments that are Objects are cached with a WeakRef.
7
+ * @private
8
+ */
9
+ export function memo_(fn) {
10
+ let m = new Map
11
+ let wm = new WeakMap
12
+ if (!empty_sym) {
13
+ empty_sym = Symbol('E')
14
+ WeakRef_proto = WeakRef.prototype
15
+ }
16
+ let memo = (...arg_a)=>{
17
+ if (!arg_a.length) {
18
+ if (!m.has(empty_sym)) {
19
+ m.set(empty_sym, fn())
20
+ }
21
+ return m.get(empty_sym)
22
+ }
23
+ let arg0 = arg_a[0]
24
+ let map = isPrimitive(arg0) ? m : wm
25
+ /** @type {[unknown[], unknown][]} */
26
+ let cache__arg_a_T_ret__a = map.get(arg0)
27
+ if (!cache__arg_a_T_ret__a) {
28
+ cache__arg_a_T_ret__a = []
29
+ map.set(arg0, cache__arg_a_T_ret__a)
30
+ }
31
+ cache__arg_a_T_ret__a:for (let cache__arg_a_T_ret__a__i = cache__arg_a_T_ret__a.length; cache__arg_a_T_ret__a__i--;) {
32
+ let cache__arg_a_T_ret = cache__arg_a_T_ret__a[cache__arg_a_T_ret__a__i]
33
+ let cache__arg_a = cache__arg_a_T_ret[0]
34
+ let { length } = arg_a
35
+ if (cache__arg_a.length !== length) continue
36
+ for (let arg_a__i = length; arg_a__i--;) {
37
+ let cache__arg_val = cache__arg_a[arg_a__i]
38
+ if (prototype_(cache__arg_val) === WeakRef_proto) {
39
+ cache__arg_val = cache__arg_val.deref()
40
+ if (!cache__arg_val) { // cleanup cache__arg_a when arg is GC
41
+ cache__arg_a_T_ret__a.splice(cache__arg_a_T_ret__a__i, 1)
42
+ continue cache__arg_a_T_ret__a
43
+ }
44
+ }
45
+ let arg_val = arg_a[arg_a__i]
46
+ if (arg_val !== cache__arg_val) break
47
+ if (!arg_a__i) { // arguments match
48
+ return cache__arg_a_T_ret[1]
49
+ }
50
+ }
51
+ }
52
+ let ret = fn(...arg_a)
53
+ cache__arg_a_T_ret__a.push([arg_a.map(arg=>isPrimitive(arg) ? arg : new WeakRef(arg)), ret])
54
+ return ret
55
+ }
56
+ memo.clear = ()=>{
57
+ m.clear()
58
+ wm = new WeakMap
59
+ }
60
+ return memo
61
+ }
@@ -0,0 +1,75 @@
1
+ import { test } from 'uvu'
2
+ import { equal } from 'uvu/assert'
3
+ import { memo_ } from './index.js'
4
+ test('memo_|empty', ()=>{
5
+ let call_count = 0
6
+ let fn = ()=>{
7
+ call_count++
8
+ return 'memo-return'
9
+ }
10
+ let memo = memo_(fn)
11
+ equal(call_count, 0)
12
+ equal(memo(), 'memo-return')
13
+ equal(call_count, 1)
14
+ equal(memo(), 'memo-return')
15
+ equal(call_count, 1)
16
+ })
17
+ test('memo_|primitive arguments', ()=>{
18
+ let call_count = 0
19
+ let fn = (arg0:number, arg1:number)=>{
20
+ call_count++
21
+ return arg0 + arg1
22
+ }
23
+ let memo = memo_(fn)
24
+ equal(call_count, 0)
25
+ equal(memo(1, 2), 3)
26
+ equal(call_count, 1)
27
+ equal(memo(1, 2), 3)
28
+ equal(call_count, 1)
29
+ equal(memo(1, 3), 4)
30
+ equal(call_count, 2)
31
+ memo.clear()
32
+ equal(memo(1, 2), 3)
33
+ equal(call_count, 3)
34
+ })
35
+ test('memo_|primitive|with objects', ()=>{
36
+ let call_count = 0
37
+ let fn = (arg0:number, arg1:object)=>{
38
+ call_count++
39
+ return { ...arg1, arg0 }
40
+ }
41
+ let memo = memo_(fn)
42
+ let arg1 = { arg1: true }
43
+ let arg1_2 = { arg1_2: true }
44
+ equal(call_count, 0)
45
+ equal(memo(1, arg1), { arg1: true, arg0: 1 })
46
+ equal(call_count, 1)
47
+ equal(memo(1, arg1), { arg1: true, arg0: 1 })
48
+ equal(call_count, 1)
49
+ equal(memo(1, arg1_2), { arg1_2: true, arg0: 1 })
50
+ equal(call_count, 2)
51
+ memo.clear()
52
+ equal(memo(1, arg1), { arg1: true, arg0: 1 })
53
+ equal(call_count, 3)
54
+ })
55
+ test('memo_|objects|with primitives', ()=>{
56
+ let call_count = 0
57
+ let fn = (arg0:object, arg1:number)=>{
58
+ call_count++
59
+ return { ...arg0, arg1 }
60
+ }
61
+ let memo = memo_(fn)
62
+ let arg0 = { arg0: true }
63
+ let arg0_2 = { arg0_2: true }
64
+ equal(call_count, 0)
65
+ equal(memo(arg0, 1), { arg0: true, arg1: 1 })
66
+ equal(call_count, 1)
67
+ equal(memo(arg0, 1), { arg0: true, arg1: 1 })
68
+ equal(call_count, 1)
69
+ equal(memo(arg0_2, 1), { arg0_2: true, arg1: 1 })
70
+ equal(call_count, 2)
71
+ memo.clear()
72
+ equal(memo(arg0, 1), { arg0: true, arg1: 1 })
73
+ equal(call_count, 3)
74
+ })
75
+ test.run()
package/all/noop/index.js CHANGED
@@ -2,5 +2,5 @@
2
2
  * Returns function that does nothing
3
3
  * @param {unknown}_
4
4
  */
5
- export function noop(..._) {
5
+ export function noop() {
6
6
  }
@@ -0,0 +1,2 @@
1
+ import type { nullish } from '../nullish/index.js'
2
+ export type primitive_T = boolean|number|string|nullish
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,5 @@
1
+ import type { nullish } from '../nullish/index.js'
2
+ import type { primitive_T } from '../primitive/index.js'
3
+ export declare function prototype_(val:Exclude<any, nullish>):Object
4
+ export declare function prototype__set(obj:Exclude<any, primitive_T>, val:Object):Object
5
+ export declare const fn__proto:Object
@@ -0,0 +1,6 @@
1
+ export const { getPrototypeOf, setPrototypeOf, defineProperty, defineProperties } = Object
2
+ export {
3
+ getPrototypeOf as prototype_,
4
+ setPrototypeOf as prototype__set,
5
+ }
6
+ export const fn__proto = getPrototypeOf(getPrototypeOf)
@@ -0,0 +1,14 @@
1
+ import { test } from 'uvu'
2
+ import { is, not } from 'uvu/assert'
3
+ import { prototype_ } from './index.js'
4
+ test('prototype_', ()=>{
5
+ is(prototype_({}), Object.prototype)
6
+ is(prototype_(new WeakRef({})), WeakRef.prototype)
7
+ is(prototype_(1), Number.prototype)
8
+ is(prototype_(true), Boolean.prototype)
9
+ is(prototype_(false), Boolean.prototype)
10
+ not.equal(prototype_(new Date), Boolean.prototype)
11
+ not.equal(prototype_({}), Boolean.prototype)
12
+ not.equal(prototype_(1), Boolean.prototype)
13
+ })
14
+ test.run()
@@ -0,0 +1,20 @@
1
+ export declare function rememo_<val_T>(
2
+ def:(rememo:rememo__T<val_T>)=>val_T,
3
+ ...subscriber_a:rememo_subscriber_T<val_T>[]
4
+ ):rememo_T<val_T>
5
+ export declare function signal_<val_T>(
6
+ init_val:val_T,
7
+ ...subscriber_a:rememo_subscriber_T<val_T>[]
8
+ ):rememo_T<val_T>
9
+ export type rememo__T<val_T> =
10
+ (def:rememo_def_T<val_T>, ...subscriber_a:rememo_subscriber_T<val_T>[])=>val_T
11
+ export type rememo_T<val_T> = ((val?:val_T)=>val_T)&rememo_o_T<val_T>
12
+ export type rememo_def_T<val_T> = (rememo:rememo__T<val>)=>val_T
13
+ export type rememo_subscriber_T<val_T> = (rememo:rememo_T<val_T>)=>unknown
14
+ export type rememo_o_T<val_T> = {
15
+ _:val_T
16
+ readonly val:val_T
17
+ init():unknown
18
+ onset(val:val_T):unknown
19
+ refresh():val_T
20
+ }
@@ -0,0 +1,82 @@
1
+ /** @typedef {import('./index.d.ts').rememo_T} */
2
+ /** @typedef {import('./index.d.ts').rememo_subscriber_T} */
3
+ /** @type {(()=>unknown)[]} */
4
+ let queue = []
5
+ /** @type {WeakRef<rememo_T>} */
6
+ let cur_ref
7
+ /**
8
+ * @param {(rememo:rememo_T<unknown>)=>unknown}_f
9
+ * @param {rememo_subscriber_T<unknown>[]}subscriber_a
10
+ * @returns {rememo_T}
11
+ * @private
12
+ */
13
+ export function rememo_(_f, ...subscriber_a) {
14
+ let _a = []
15
+ let _rS = new Set
16
+ let rememo$ = (...arg_a)=>arg_a.length ? rememo$._ = arg_a[0] : rememo$._
17
+ rememo$._a = _a
18
+ rememo$._f = _f
19
+ rememo$._rS = _rS
20
+ rememo$._r = new WeakRef(()=>rememo$.refresh(_f(rememo$)))
21
+ rememo$.onset = ()=>0
22
+ rememo$._r.l = 0
23
+ rememo$.init = ()=>(rememo$(), rememo$)
24
+ Object.defineProperty(rememo$, '_', {
25
+ get() {
26
+ // allow self-referencing
27
+ if (!_a.length) {
28
+ let prev_ref = cur_ref
29
+ cur_ref = rememo$._r
30
+ try {
31
+ _a[0] = _f(rememo$)
32
+ } finally {
33
+ cur_ref = prev_ref
34
+ }
35
+ }
36
+ if (cur_ref && cur_ref !== rememo$._r) {
37
+ cur_ref.l = Math.max(rememo$._r.l + 1, cur_ref.l)
38
+ _rS.add(cur_ref)
39
+ }
40
+ return _a[0]
41
+ },
42
+ set(val) {
43
+ if (!_a.length || val !== _a[0]) {
44
+ rememo$.refresh(val)
45
+ }
46
+ return val
47
+ }
48
+ })
49
+ rememo$.refresh = val=>{
50
+ let { length } = _a
51
+ _a[0] = val
52
+ rememo$.onset(val)
53
+ if (length) {
54
+ let run_queue = !queue[0]
55
+ for (let ref of _rS) {
56
+ if (!~queue.indexOf(ref)) queue.push(ref)
57
+ }
58
+ if (run_queue) {
59
+ for (let ref; ref = queue.shift();) {
60
+ queue.some(_ref=>ref.l > _ref.l) ? queue.push(ref) : ref.deref()?.() ?? _rS.delete(ref)
61
+ }
62
+ }
63
+ }
64
+ return rememo$
65
+ }
66
+ rememo$._sa = subscriber_a.map(subscriber=>
67
+ rememo_(()=>subscriber(rememo$)).init())
68
+ return rememo$
69
+ }
70
+ /**
71
+ * @param {unknown}init_val
72
+ * @param {rememo_subscriber_T[]}subscriber_a
73
+ * @returns {rememo_T}
74
+ * @private
75
+ */
76
+ export function signal_(init_val, ...subscriber_a) {
77
+ let signal$ =
78
+ rememo_(signal$=>signal$._v, ...subscriber_a)
79
+ signal$.onset = val=>signal$._v = val
80
+ signal$._v = init_val
81
+ return signal$
82
+ }
@@ -0,0 +1,241 @@
1
+ import { deepStrictEqual } from 'node:assert'
2
+ import { clock } from 'sinon'
3
+ import { test } from 'uvu'
4
+ import { equal } from 'uvu/assert'
5
+ import { sleep } from '../sleep/index.js'
6
+ import { rememo_, signal_ } from './index.js'
7
+ test('rememo_|static value', ()=>{
8
+ let count = 0
9
+ let rememo$ = rememo_(rememo=>{
10
+ count++
11
+ return 'rememo-value'
12
+ })
13
+ equal(count, 0)
14
+ equal(rememo$(), 'rememo-value')
15
+ equal(count, 1)
16
+ equal(rememo$(), 'rememo-value')
17
+ equal(count, 1)
18
+ })
19
+ test('signal_', ()=>{
20
+ let signal$ = signal_('val0')
21
+ equal(signal$(), 'val0')
22
+ signal$('val1')
23
+ equal(signal$(), 'val1')
24
+ })
25
+ test('rememo_|async subsubscriber', async ()=>{
26
+ let resolve:(user:{ id:string })=>void
27
+ let user0 = { id: 'id-0' }
28
+ let user1 = { id: 'id-1' }
29
+ let id$ = signal_('id-0')
30
+ let user$ = signal_<{ id:string }|null>(
31
+ null,
32
+ async (_user$)=>{
33
+ id$()
34
+ let user:{ id:string } = await new Promise(_resolve=>resolve = _resolve)
35
+ _user$(user)
36
+ })
37
+ equal(user$(), null)
38
+ resolve!(user0)
39
+ await sleep(0)
40
+ equal(user$(), user0)
41
+ id$('id-1')
42
+ equal(user$(), user0)
43
+ resolve!(user1)
44
+ await sleep(0)
45
+ })
46
+ test('rememo_+signal_|simple graph', ()=>{
47
+ let base$ = signal_('base0')
48
+ let dep0$ = rememo_(()=>base$() + '-dep0')
49
+ let dep1$ = rememo_(()=>dep0$() + '-dep1')
50
+ let dep2$ = rememo_(()=>dep1$() + '-dep2')
51
+ let dep3$ = rememo_(()=>dep2$() + '-dep3')
52
+ let dep4$ = rememo_(()=>dep3$() + '-dep4')
53
+ equal(dep4$(), 'base0-dep0-dep1-dep2-dep3-dep4')
54
+ equal(dep3$(), 'base0-dep0-dep1-dep2-dep3')
55
+ equal(dep2$(), 'base0-dep0-dep1-dep2')
56
+ equal(dep1$(), 'base0-dep0-dep1')
57
+ equal(dep0$(), 'base0-dep0')
58
+ equal(base$(), 'base0')
59
+ base$('base1')
60
+ equal(base$(), 'base1')
61
+ equal(dep0$(), 'base1-dep0')
62
+ equal(dep1$(), 'base1-dep0-dep1')
63
+ equal(dep2$(), 'base1-dep0-dep1-dep2')
64
+ equal(dep3$(), 'base1-dep0-dep1-dep2-dep3')
65
+ equal(dep4$(), 'base1-dep0-dep1-dep2-dep3-dep4')
66
+ })
67
+ test('prevents diamond dependency problem 1', ()=>{
68
+ let store$ = signal_(0)
69
+ let values:string[] = []
70
+ let a$ = rememo_(()=>`a${store$()}`)
71
+ let b$ = rememo_(()=>a$().replace('a', 'b'))
72
+ let c$ = rememo_(()=>a$().replace('a', 'c'))
73
+ let d$ = rememo_(()=>a$().replace('a', 'd'))
74
+ let combined$ = rememo_(()=>`${b$()}${c$()}${d$()}`,
75
+ combined$=>
76
+ values.push(combined$()))
77
+ deepStrictEqual(values, ['b0c0d0'])
78
+ store$(1)
79
+ store$(2)
80
+ deepStrictEqual(values, ['b0c0d0', 'b1c1d1', 'b2c2d2'])
81
+ })
82
+ test('prevents diamond dependency problem 2', ()=>{
83
+ let store$ = signal_(0)
84
+ let values:string[] = []
85
+ let a$ = rememo_(()=>`a${store$()}`)
86
+ let b$ = rememo_(()=>a$().replace('a', 'b'))
87
+ let c$ = rememo_(()=>b$().replace('b', 'c'))
88
+ let d$ = rememo_(()=>c$().replace('c', 'd'))
89
+ let e$ = rememo_(()=>d$().replace('d', 'e'))
90
+ let combined$ = rememo_<string>(
91
+ ()=>[a$(), e$()].join(''),
92
+ combined$=>values.push(combined$()))
93
+ deepStrictEqual(values, ['a0e0'])
94
+ store$(1)
95
+ deepStrictEqual(values, ['a0e0', 'a1e1'])
96
+ })
97
+ test('prevents diamond dependency problem 3', ()=>{
98
+ let store$ = signal_(0)
99
+ let values:string[] = []
100
+ let a$ = rememo_(()=>`a${store$()}`)
101
+ let b$ = rememo_(()=>a$().replace('a', 'b'))
102
+ let c$ = rememo_(()=>b$().replace('b', 'c'))
103
+ let d$ = rememo_(()=>c$().replace('c', 'd'))
104
+ rememo_<string>(
105
+ ()=>`${a$()}${b$()}${c$()}${d$()}`,
106
+ combined$=>values.push(combined$()))
107
+ deepStrictEqual(values, ['a0b0c0d0'])
108
+ store$(1)
109
+ deepStrictEqual(values, ['a0b0c0d0', 'a1b1c1d1'])
110
+ })
111
+ test('autosubscribe: prevents diamond dependency problem 4 (complex)', ()=>{
112
+ let store1$ = signal_(0)
113
+ let store2$ = signal_(0)
114
+ let values:string[] = []
115
+ let fn =
116
+ (name:string)=>
117
+ (...v:(number|string)[])=>
118
+ `${name}${v.join('')}`
119
+ let a$ = rememo_(()=>`a${store1$()}`)
120
+ let b$ = rememo_(()=>`b${store2$()}`)
121
+ let c$ = rememo_(()=>`c${a$()}${b$()}`)
122
+ let d$ = rememo_(()=>`d${a$()}`)
123
+ let e$ = rememo_(()=>`e${c$()}${d$()}`)
124
+ let f$ = rememo_(()=>`f${e$()}`)
125
+ let g$ = rememo_(()=>`g${f$()}`)
126
+ rememo_(
127
+ ()=>e$(),
128
+ combined1$=>values.push(combined1$()))
129
+ rememo_(
130
+ ()=>[e$(), g$()].join(''),
131
+ combined2$=>values.push(combined2$()))
132
+ deepStrictEqual(values, ['eca0b0da0', 'eca0b0da0gfeca0b0da0'])
133
+ store1$(1)
134
+ store2$(2)
135
+ deepStrictEqual(values, [
136
+ 'eca0b0da0',
137
+ 'eca0b0da0gfeca0b0da0',
138
+ 'eca1b0da1',
139
+ 'eca1b0da1gfeca1b0da1',
140
+ 'eca1b2da1',
141
+ 'eca1b2da1gfeca1b2da1'
142
+ ])
143
+ })
144
+ test('prevents diamond dependency problem 5', ()=>{
145
+ let events = ''
146
+ let firstName$ = signal_('John')
147
+ let lastName$ = signal_('Doe')
148
+ let fullName$ = rememo_(()=>{
149
+ events += 'full '
150
+ return `${firstName$()} ${lastName$()}`
151
+ })
152
+ let isFirstShort$ = rememo_(()=>{
153
+ events += 'short '
154
+ return firstName$().length < 10
155
+ })
156
+ let displayName$ = rememo_(
157
+ ()=>{
158
+ events += 'display '
159
+ return isFirstShort$() ? fullName$() : firstName$()
160
+ }
161
+ )
162
+ equal(events, '')
163
+ equal(displayName$(), 'John Doe')
164
+ equal(events, 'display short full ')
165
+ firstName$('Benedict')
166
+ equal(displayName$(), 'Benedict Doe')
167
+ equal(events, 'display short full short full display ')
168
+ firstName$('Montgomery')
169
+ equal(displayName$(), 'Montgomery')
170
+ equal(events, 'display short full short full display short full display ')
171
+ })
172
+ test('prevents diamond dependency problem 6', ()=>{
173
+ let store1$ = signal_(0)
174
+ let store2$ = signal_(0)
175
+ let values:string[] = []
176
+ let a$ = rememo_(()=>`a${store1$()}`)
177
+ let b$ = rememo_(()=>`b${store2$()}`)
178
+ let c$ = rememo_(()=>b$().replace('b', 'c'))
179
+ rememo_(
180
+ ()=>`${a$()}${c$()}`,
181
+ combined$=>values.push(combined$()))
182
+ deepStrictEqual(values, ['a0c0'])
183
+ store1$(1)
184
+ deepStrictEqual(values, ['a0c0', 'a1c0'])
185
+ })
186
+ test('prevents dependency listeners from being out of order', ()=>{
187
+ let base$ = signal_(0)
188
+ let a$ = rememo_(()=>{
189
+ return `${base$()}a`
190
+ })
191
+ let values:string[] = []
192
+ let b$ = rememo_(()=>{
193
+ return `${a$()}b`
194
+ }, b$=>values.push(b$()))
195
+ equal(b$(), '0ab')
196
+ deepStrictEqual(values, ['0ab'])
197
+ equal(a$(), '0a')
198
+ base$(1)
199
+ deepStrictEqual(values, ['0ab', '1ab'])
200
+ })
201
+ test('computes initial value when argument is undefined', ()=>{
202
+ let one$ = signal_<string|undefined>(undefined)
203
+ let two$ = rememo_(()=>!!one$())
204
+ equal(one$(), undefined)
205
+ equal(two$(), false)
206
+ })
207
+ test('async computed using task', async ()=>{
208
+ let a$ = signal_(1)
209
+ let b$ = signal_(2)
210
+ let sleepCycles = 5
211
+ let taskArgumentsCalls:number[][] = []
212
+ let sum$ = signal_<null|number>(null,
213
+ async sum$=>{
214
+ taskArgumentsCalls.push([a$(), b$()])
215
+ for (let i = 0; i < sleepCycles; i++) {
216
+ await Promise.resolve()
217
+ }
218
+ sum$(a$() + b$())
219
+ })
220
+ equal(sum$(), null)
221
+ deepStrictEqual(taskArgumentsCalls, [[1, 2]])
222
+ // sleepCycles = 0
223
+ a$(10)
224
+ b$(20)
225
+ for (let i = 0; i < sleepCycles; i++) {
226
+ equal(sum$(), null)
227
+ await Promise.resolve()
228
+ deepStrictEqual(taskArgumentsCalls, [
229
+ [1, 2],
230
+ [10, 2],
231
+ [10, 20]
232
+ ])
233
+ }
234
+ equal(sum$(), 30)
235
+ deepStrictEqual(taskArgumentsCalls, [
236
+ [1, 2],
237
+ [10, 2],
238
+ [10, 20]
239
+ ])
240
+ })
241
+ test.run()
@@ -36,7 +36,7 @@ export * from '../all/ifelse/index.js'
36
36
  export * from '../all/iife/index.js'
37
37
  export * from '../all/invert/index.js'
38
38
  export * from '../all/isArray/index.js'
39
- export * from '../all/isPromise/index.js'
39
+ export * from '../all/isPromiseLike/index.js'
40
40
  export * from '../all/left_and/index.js'
41
41
  export * from '../all/left_or/index.js'
42
42
  export * from '../all/many_andand/index.js'
@@ -45,6 +45,7 @@ export * from '../all/map_apply/index.js'
45
45
  export * from '../all/map_call/index.js'
46
46
  export * from '../all/map_fn/index.js'
47
47
  export * from '../all/maybe/index.js'
48
+ export * from '../all/memo/index.js'
48
49
  export * from '../all/neq/index.js'
49
50
  export * from '../all/neql/index.js'
50
51
  export * from '../all/nf/index.js'
package/function/index.js CHANGED
@@ -36,7 +36,7 @@ export * from '../all/ifelse/index.js'
36
36
  export * from '../all/iife/index.js'
37
37
  export * from '../all/invert/index.js'
38
38
  export * from '../all/isArray/index.js'
39
- export * from '../all/isPromise/index.js'
39
+ export * from '../all/isPromiseLike/index.js'
40
40
  export * from '../all/left_and/index.js'
41
41
  export * from '../all/left_or/index.js'
42
42
  export * from '../all/many_andand/index.js'
@@ -45,6 +45,7 @@ export * from '../all/map_apply/index.js'
45
45
  export * from '../all/map_call/index.js'
46
46
  export * from '../all/map_fn/index.js'
47
47
  export * from '../all/maybe/index.js'
48
+ export * from '../all/memo/index.js'
48
49
  export * from '../all/neq/index.js'
49
50
  export * from '../all/neql/index.js'
50
51
  export * from '../all/nf/index.js'
package/object/index.d.ts CHANGED
@@ -40,6 +40,7 @@ export * from '../all/or_null/index.js'
40
40
  export * from '../all/pick/index.js'
41
41
  export * from '../all/pick_keys/index.js'
42
42
  export * from '../all/pick_val_a/index.js'
43
+ export * from '../all/prototype/index.js'
43
44
  export * from '../all/rc_be/index.js'
44
45
  export * from '../all/rc_be_/index.js'
45
46
  export * from '../all/toString/index.js'
package/object/index.js CHANGED
@@ -39,6 +39,7 @@ export * from '../all/or_null/index.js'
39
39
  export * from '../all/pick/index.js'
40
40
  export * from '../all/pick_keys/index.js'
41
41
  export * from '../all/pick_val_a/index.js'
42
+ export * from '../all/prototype/index.js'
42
43
  export * from '../all/rc_be/index.js'
43
44
  export * from '../all/rc_be_/index.js'
44
45
  export * from '../all/toString/index.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ctx-core",
3
- "version": "2.2.2",
3
+ "version": "2.4.0",
4
4
  "description": "ctx-core core library",
5
5
  "keywords": [
6
6
  "ctx-core",
@@ -53,6 +53,7 @@
53
53
  "queue",
54
54
  "random",
55
55
  "regex",
56
+ "rememo",
56
57
  "set",
57
58
  "sleep",
58
59
  "string",
@@ -93,6 +94,7 @@
93
94
  "./queue": "./queue/index.js",
94
95
  "./random": "./random/index.js",
95
96
  "./regex": "./regex/index.js",
97
+ "./rememo": "./rememo/index.js",
96
98
  "./set": "./set/index.js",
97
99
  "./sleep": "./sleep/index.js",
98
100
  "./string": "./string/index.js",
@@ -104,10 +106,13 @@
104
106
  },
105
107
  "devDependencies": {
106
108
  "@arethetypeswrong/cli": "^0.13.1",
109
+ "@size-limit/preset-small-lib": "^11.0.0",
110
+ "@types/node": "^20.9.1",
107
111
  "@types/sinon": "^17.0.1",
108
112
  "c8": "^8.0.1",
109
113
  "check-dts": "^0.7.2",
110
114
  "sinon": "^17.0.1",
115
+ "size-limit": "^11.0.0",
111
116
  "ts-node": "^10.9.1",
112
117
  "tsx": "^4.1.2",
113
118
  "typescript": "next",
@@ -118,12 +123,37 @@
118
123
  "cache": "~/.npm"
119
124
  },
120
125
  "sideEffects": false,
126
+ "size-limit": [
127
+ {
128
+ "name": "be",
129
+ "import": {
130
+ "./be": "{ be_ }"
131
+ },
132
+ "limit": "600 B"
133
+ },
134
+ {
135
+ "name": "rememo",
136
+ "import": {
137
+ "./rememo": "{ rememo_ }"
138
+ },
139
+ "limit": "358 B"
140
+ },
141
+ {
142
+ "name": "rememo signal",
143
+ "import": {
144
+ "./rememo": "{ signal_ }"
145
+ },
146
+ "limit": "385 B"
147
+ }
148
+ ],
121
149
  "scripts": {
122
150
  "build": ":",
123
151
  "clean": ":",
124
152
  "exec": "$@",
125
- "test": "pnpm test-unit && check-dts",
126
- "test-unit": "tsx node_modules/uvu/bin.js . '\\.test\\.(ts|js)$'",
127
- "test-unit-coverage": "c8 pnpm test-unit"
153
+ "test": "pnpm run /^test:/",
154
+ "test:size": "size-limit",
155
+ "test:type": "check-dts",
156
+ "test:unit": "tsx node_modules/uvu/bin.js . '\\.test\\.(ts|js)$'",
157
+ "disable:test:coverage": "c8 pnpm test-unit"
128
158
  }
129
159
  }
@@ -0,0 +1 @@
1
+ export * from '../all/rememo/index.js'
@@ -0,0 +1 @@
1
+ export * from '../all/rememo/index.js'
package/tsconfig.json CHANGED
@@ -5,7 +5,10 @@
5
5
  "moduleResolution": "nodenext",
6
6
  "target": "ESNext",
7
7
  "strict": true,
8
- "skipLibCheck": true
9
- },
10
- "exclude": ["node_modules"]
8
+ "skipLibCheck": true,
9
+ "lib": ["ESNext"],
10
+ "types": [
11
+ "node"
12
+ ]
13
+ }
11
14
  }
@@ -1 +0,0 @@
1
- export declare function isPromise(obj:any):boolean