ctx-core 4.3.1 → 4.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.
package/all/index.d.ts CHANGED
@@ -309,6 +309,7 @@ export * from './rank/index.js'
309
309
  export * from './rate_limit/index.js'
310
310
  export * from './reduce/index.js'
311
311
  export * from './reject/index.js'
312
+ export * from './rmemo/index.js'
312
313
  export * from './remove/index.js'
313
314
  export * from './remove_idx/index.js'
314
315
  export * from './resolver_curry/index.js'
package/all/index.js CHANGED
@@ -309,6 +309,7 @@ export * from './rank/index.js'
309
309
  export * from './rate_limit/index.js'
310
310
  export * from './reduce/index.js'
311
311
  export * from './reject/index.js'
312
+ export * from './rmemo/index.js'
312
313
  export * from './remove/index.js'
313
314
  export * from './remove_idx/index.js'
314
315
  export * from './resolver_curry/index.js'
@@ -1,2 +1,2 @@
1
1
  import type { nullish } from '../nullish/index.js'
2
- export type primitive_T = boolean|number|string|nullish
2
+ export type primitive_T = boolean|number|bigint|string|nullish
@@ -1,23 +1,28 @@
1
- export declare function r_rmemo_<val_T>(
1
+ export declare function r_rmemo_<val_T, ret_T extends rmemo_T<val_T> = r_rmemo_T<val_T>>(
2
2
  def:rmemo_def_T<val_T>,
3
3
  ...subscriber_a:rmemo_subscriber_T<val_T>[]
4
- ):r_rmemo_T<val_T>
4
+ ):ret_T
5
5
  export declare function rw_rmemo_<val_T>(
6
6
  init_val:val_T,
7
7
  ...subscriber_a:rmemo_subscriber_T<val_T>[]
8
8
  ):rw_rmemo_T<val_T>
9
+ export declare function rwr_rmemo_<val_T>(
10
+ def:rmemo_def_T<val_T>,
11
+ ...subscriber_a:rmemo_subscriber_T<val_T>[]
12
+ ):rw_rmemo_T<val_T>
13
+ export declare function rw_rmemo__set_<val_T>(rmemo:rw_rmemo_T<val_T>):val_T
14
+ export type rmemo_T<val_T> = r_rmemo_T<val_T>|rw_rmemo_T<val_T>
9
15
  export type r_rmemo_T<val_T> = {
10
16
  readonly _:val_T
11
17
  val:val_T
12
- get():val_T
13
- go():r_rmemo_T<val_T>
18
+ rmr:WeakRef<()=>val_T>
19
+ rmrs?:Set<WeakRef<()=>val_T>>
14
20
  }
15
21
  export type rw_rmemo_T<val_T> = {
16
22
  _:val_T
17
23
  val:val_T
18
- get():val_T
19
- set(val:val_T):void
20
- go():r_rmemo_T<val_T>
24
+ rmr:WeakRef<()=>val_T>
25
+ rmrs:Set<WeakRef<()=>val_T>>
21
26
  }
22
27
  export type rmemo_val_T<rw_rmemo_T> = rw_rmemo_T extends {
23
28
  _:infer val_T
@@ -2,9 +2,9 @@
2
2
  /** @typedef {import('./index.d.ts').r_rmemo_T} */
3
3
  /** @typedef {import('./index.d.ts').rmemo_subscriber_T} */
4
4
  /** @type {WeakRef<r_rmemo_T>} */
5
- let cur_r
6
- /** @type {(()=>unknown)[]} */
7
- let queue = []
5
+ let cur_rmr
6
+ /** @type {Set<()=>unknown>} */
7
+ let queue = new Set
8
8
  /**
9
9
  * @param {rmemo_def_T}rmemo_def
10
10
  * @param {rmemo_subscriber_T<unknown>[]}subscriber_a
@@ -12,60 +12,80 @@ let queue = []
12
12
  * @private
13
13
  */
14
14
  export function r_rmemo_(rmemo_def, ...subscriber_a) {
15
- let init
15
+ let refresh
16
+ let rmrs
16
17
  let r_rmemo = {
17
18
  get _() {
18
19
  if (!('val' in r_rmemo)) {
19
- let prev_r = cur_r
20
- cur_r = r_rmemo._r
21
- try {
22
- init()
23
- } finally {
24
- cur_r = prev_r
25
- }
20
+ refresh()
26
21
  }
27
- if (cur_r) {
28
- cur_r.l = cur_r.l < r_rmemo._r.l + 1 ? r_rmemo._r.l + 1 : cur_r.l
29
- r_rmemo._rs.add(cur_r)
22
+ if (cur_rmr) {
23
+ let cur_rmr_refresh = cur_rmr.deref()
24
+ cur_rmr_refresh.l =
25
+ cur_rmr_refresh.l < refresh.l + 1
26
+ ? refresh.l + 1
27
+ : cur_rmr_refresh.l
28
+ rmrs.add(cur_rmr)
29
+ cur_rmr_refresh.s.add(r_rmemo) // conditional in rmr calls this r_memo
30
+ cur_rmr_refresh.S.add(r_rmemo) // prevent this rmemo from GC while cur_rmr is still active
30
31
  }
31
32
  return r_rmemo.val
32
33
  },
33
34
  set _(val) {
34
35
  if (val !== r_rmemo.val) {
35
- r_rmemo.val = val
36
- // hook for rw_rmemo_
37
- r_rmemo._s?.(val)
38
- let run_queue = !queue[0]
39
- for (let r of r_rmemo._rs) {
40
- if (!~queue.indexOf(r)) queue.push(r)
41
- }
42
- if (!r_rmemo._sa) {
43
- // add reference to subscribers to prevent GC
44
- r_rmemo._sa = subscriber_a.map(subscriber=>
45
- r_rmemo_(()=>subscriber(r_rmemo)).go())
36
+ r_rmemo.val = val // val is available for other purposes
37
+ let run_queue = !queue.size
38
+ for (let rmr of rmrs) {
39
+ val = rmr.deref() // val is no longer used...saving bytes
40
+ if (!val) {
41
+ rmrs.delete(rmr)
42
+ } else if (val.s.has(r_rmemo)) { // if conditional rmr refresh calls this r_memo, add to queue
43
+ queue.add(val)
44
+ }
46
45
  }
46
+ // add reference to subscribers to prevent GC
47
+ r_rmemo._s ||=
48
+ subscriber_a.map(subscriber=>
49
+ r_rmemo_(subscriber$=>(
50
+ subscriber(r_rmemo),
51
+ subscriber$
52
+ ))._)
47
53
  if (run_queue) {
48
- // eslint-disable-next-line no-cond-assign
49
- for (let r; r = queue.shift();) {
50
- if (queue.some(queue_r=>r.l > queue_r.l)) {
51
- queue.push(r)
52
- } else {
53
- (r.deref() || r_rmemo._rs.delete)(r)
54
+ cur_refresh_loop:for (let cur_refresh of queue) {
55
+ queue.delete(cur_refresh)
56
+ for (let queue_refresh of queue) {
57
+ if (cur_refresh.l > queue_refresh.l) {
58
+ queue.add(cur_refresh)
59
+ continue cur_refresh_loop
60
+ }
54
61
  }
62
+ cur_refresh()
55
63
  }
56
64
  }
57
65
  }
58
66
  },
59
- go: ()=>(r_rmemo._, r_rmemo),
60
- get: ()=>r_rmemo._,
61
- set: val=>r_rmemo._ = val,
62
- _rs: new Set,
63
67
  }
64
- init = ()=>r_rmemo._ = rmemo_def(r_rmemo)
65
- r_rmemo._r = new WeakRef(init)
66
- r_rmemo._r.l = 0
68
+ refresh = ()=>{
69
+ let prev_rmr = cur_rmr
70
+ cur_rmr = r_rmemo.rmr
71
+ refresh.s.clear()
72
+ try {
73
+ r_rmemo._ = rmemo_def(r_rmemo)
74
+ } catch (err) {
75
+ console.error(err)
76
+ }
77
+ cur_rmr = prev_rmr // finally is not necessary due since catch does not throw
78
+ }
79
+ refresh.l = 0
80
+ // rmrs = new Set
81
+ // r_rmemo.rmrs is kept for GC testing/debugging purposes...small size increase
82
+ r_rmemo.rmrs = rmrs = new Set
83
+ r_rmemo.rmr = new WeakRef(refresh)
84
+ refresh.s = new Set
85
+ refresh.S = new Set
67
86
  return r_rmemo
68
87
  }
88
+ export { r_rmemo_ as rwr_rmemo_ }
69
89
  /**
70
90
  * @param {unknown}init_val
71
91
  * @param {rmemo_subscriber_T[]}subscriber_a
@@ -73,8 +93,17 @@ export function r_rmemo_(rmemo_def, ...subscriber_a) {
73
93
  * @private
74
94
  */
75
95
  export function rw_rmemo_(init_val, ...subscriber_a) {
76
- let rw_rmemo = r_rmemo_(_rw_rmemo=>_rw_rmemo._v, ...subscriber_a)
77
- rw_rmemo._s = val=>rw_rmemo._v = val
78
- rw_rmemo._v = init_val
79
- return rw_rmemo
96
+ return r_rmemo_(rw_rmemo=>
97
+ 'val' in rw_rmemo
98
+ ? rw_rmemo.val
99
+ : init_val,
100
+ ...subscriber_a)
101
+ }
102
+ /**
103
+ * @param {rw_rmemo_T}rw_rmemo
104
+ * @returns {(val:unknown)=>unknown}
105
+ * @private
106
+ */
107
+ export function rw_rmemo__set_(rw_rmemo) {
108
+ return val=>rw_rmemo._ = val
80
109
  }
@@ -1,8 +1,10 @@
1
+ // Reference: https://github.com/nanostores/nanostores/blob/main/computed/index.test.ts
2
+ // Reference: https://github.com/vanjs-org/van/blob/main/test/van.test.ts
1
3
  import { deepStrictEqual } from 'node:assert'
2
4
  import { test } from 'uvu'
3
5
  import { equal } from 'uvu/assert'
4
6
  import { sleep } from '../sleep/index.js'
5
- import { r_rmemo_, type r_rmemo_T, rw_rmemo_ } from './index.js'
7
+ import { r_rmemo_, type r_rmemo_T, rw_rmemo_, rwr_rmemo_ } from './index.js'
6
8
  test('r_rmemo_|static value', ()=>{
7
9
  let count = 0
8
10
  const r_rmemo = r_rmemo_(()=>{
@@ -15,15 +17,9 @@ test('r_rmemo_|static value', ()=>{
15
17
  equal(r_rmemo._, 'rmemo-value')
16
18
  equal(count, 1)
17
19
  })
18
- test('rw_rmemo_', ()=>{
19
- const rw_rmemo = rw_rmemo_('val0')
20
- equal(rw_rmemo._, 'val0')
21
- rw_rmemo._ = 'val1'
22
- equal(rw_rmemo._, 'val1')
23
- })
24
- test('r_rmemo_|def function|rmemo$ argument', ()=>{
20
+ test('r_rmemo_|def function|rmemo argument', ()=>{
25
21
  const rw_rmemo = rw_rmemo_('val0')
26
- const r_rmemo:r_rmemo_T<string>&{custom?:string} = r_rmemo_<string>((_rmemo$:r_rmemo_T<string>&{custom?:string})=>
22
+ const r_rmemo:r_rmemo_T<string>&{ custom?:string } = r_rmemo_<string>((_rmemo$:r_rmemo_T<string>&{ custom?:string })=>
27
23
  `${_rmemo$.custom}-${rw_rmemo._}`)
28
24
  r_rmemo.custom = 'custom_val0'
29
25
  equal(r_rmemo._, 'custom_val0-val0')
@@ -32,6 +28,113 @@ test('r_rmemo_|def function|rmemo$ argument', ()=>{
32
28
  rw_rmemo._ = 'val1'
33
29
  equal(r_rmemo._, 'custom_val1-val1')
34
30
  })
31
+ test('r_memo_|side effect', ()=>{
32
+ const history:string[] = []
33
+ const s = rw_rmemo_('This')
34
+ r_rmemo_(()=>history.push(s._))._
35
+ s._ = 'is'
36
+ s._ = 'a'
37
+ s._ = 'test'
38
+ s._ = 'test'
39
+ equal(history, ['This', 'is', 'a', 'test'])
40
+ })
41
+ test('r_rmemo_|conditional', ()=>{
42
+ const cond$ = rw_rmemo_(true)
43
+ const a$ = rw_rmemo_(1)
44
+ const b$ = rw_rmemo_(2)
45
+ const c$ = rw_rmemo_(3)
46
+ const d$ = rw_rmemo_(4)
47
+ let trigger_count = 0
48
+ const sum$ = r_rmemo_(()=>(++trigger_count, cond$._ ? a$._ + b$._ : c$._ + d$._))
49
+ equal(sum$._, 3)
50
+ equal(trigger_count, 1)
51
+ a$._ = 11
52
+ equal(sum$._, 13)
53
+ equal(trigger_count, 2)
54
+ b$._ = 12
55
+ equal(sum$._, 23)
56
+ equal(trigger_count, 3)
57
+ // Changing c$ or d$ won't triggered the effect as they're not its current dependencies
58
+ c$._ = 13
59
+ equal(sum$._, 23)
60
+ equal(trigger_count, 3)
61
+ d$._ = 14
62
+ equal(sum$._, 23)
63
+ equal(trigger_count, 3)
64
+ cond$._ = false
65
+ equal(sum$._, 27)
66
+ equal(trigger_count, 4)
67
+ c$._ = 23
68
+ equal(sum$._, 37)
69
+ equal(trigger_count, 5)
70
+ d$._ = 24
71
+ equal(sum$._, 47)
72
+ equal(trigger_count, 6)
73
+ // Changing a$ or b$ won't triggered the effect as they're not its current dependencies
74
+ a$._ = 21
75
+ equal(sum$._, 47)
76
+ equal(trigger_count, 6)
77
+ b$._ = 22
78
+ equal(sum$._, 47)
79
+ equal(trigger_count, 6)
80
+ })
81
+ test('rwr_rmemo_', ()=>{
82
+ const num_items$ = rw_rmemo_(0)
83
+ const items$ = r_rmemo_(()=>[...Array(num_items$._).keys()].map(i=>`Item ${i + 1}`))
84
+ const selected_index$ = rwr_rmemo_(()=>(items$._, 0))
85
+ const selected_item$ = r_rmemo_(()=>items$._[selected_index$._])
86
+ num_items$._ = 3
87
+ equal(num_items$._, 3)
88
+ equal(items$._.join(','), 'Item 1,Item 2,Item 3')
89
+ equal(selected_index$._, 0)
90
+ equal(selected_item$._, 'Item 1')
91
+ selected_index$._ = 2
92
+ equal(selected_index$._, 2)
93
+ equal(selected_item$._, 'Item 3')
94
+ num_items$._ = 5
95
+ equal(num_items$._, 5)
96
+ equal(items$._.join(','), 'Item 1,Item 2,Item 3,Item 4,Item 5')
97
+ equal(selected_index$._, 0)
98
+ equal(selected_item$._, 'Item 1')
99
+ selected_index$._ = 3
100
+ equal(selected_index$._, 3)
101
+ equal(selected_item$._, 'Item 4')
102
+ })
103
+ test('r_rmemo_|error|case 1', ()=>{
104
+ const r$ = r_rmemo_(()=>{
105
+ throw new Error('error case')
106
+ })
107
+ equal(r$._, undefined)
108
+ })
109
+ test('r_rmemo_|error|case 2', ()=>{
110
+ const rw0 = rw_rmemo_(1)
111
+ const r1 = r_rmemo_(()=>rw0._ * 2)
112
+ const r2 = r_rmemo_(()=>{
113
+ if (rw0._ > 1) throw new Error()
114
+ return rw0._
115
+ })
116
+ const r3 = r_rmemo_(()=>rw0._ * rw0._)
117
+ equal(r1._, 2)
118
+ equal(r2._, 1)
119
+ equal(r3._, 1)
120
+ rw0._ = 3
121
+ equal(r1._, 6)
122
+ // r2._ keeps it's old value of 1 due to error
123
+ equal(r2._, 1)
124
+ equal(r3._, 9)
125
+ })
126
+ test('rw_rmemo_', ()=>{
127
+ const rw_rmemo = rw_rmemo_('val0')
128
+ equal(rw_rmemo._, 'val0')
129
+ rw_rmemo._ = 'val1'
130
+ equal(rw_rmemo._, 'val1')
131
+ })
132
+ test('rw_rmemo_|undefined', ()=>{
133
+ const rw_rmemo = rw_rmemo_(undefined)
134
+ const r_rmemo = r_rmemo_(()=>rw_rmemo._)
135
+ equal(rw_rmemo._, undefined)
136
+ equal(r_rmemo._, undefined)
137
+ })
35
138
  test('rw_rmemo_|async subsubscriber|case 1', async ()=>{
36
139
  let resolve:(user:{ id:string })=>void
37
140
  const user0 = { id: 'id-0' }
@@ -125,7 +228,7 @@ test('prevents diamond dependency problem 1', ()=>{
125
228
  r_rmemo_(()=>`${b$._}${c$._}${d$._}`,
126
229
  combined$=>
127
230
  values.push(combined$._)
128
- ).go()
231
+ )._
129
232
  deepStrictEqual(values, ['b0c0d0'])
130
233
  store$._ = 1
131
234
  store$._ = 2
@@ -142,7 +245,7 @@ test('prevents diamond dependency problem 2', ()=>{
142
245
  r_rmemo_<string>(
143
246
  ()=>[a$._, e$._].join(''),
144
247
  $=>values.push($._)
145
- ).go()
248
+ )._
146
249
  deepStrictEqual(values, ['a0e0'])
147
250
  store$._ = 1
148
251
  deepStrictEqual(values, ['a0e0', 'a1e1'])
@@ -157,7 +260,7 @@ test('prevents diamond dependency problem 3', ()=>{
157
260
  r_rmemo_<string>(
158
261
  ()=>`${a$._}${b$._}${c$._}${d$._}`,
159
262
  combined$=>values.push(combined$._)
160
- ).go()
263
+ )._
161
264
  deepStrictEqual(values, ['a0b0c0d0'])
162
265
  store$._ = 1
163
266
  deepStrictEqual(values, ['a0b0c0d0', 'a1b1c1d1'])
@@ -176,11 +279,11 @@ test('autosubscribe: prevents diamond dependency problem 4 (complex)', ()=>{
176
279
  r_rmemo_(
177
280
  ()=>e$._,
178
281
  combined1$=>values.push(combined1$._)
179
- ).go()
282
+ )._
180
283
  r_rmemo_(
181
284
  ()=>[e$._, g$._].join(''),
182
285
  combined2$=>values.push(combined2$._)
183
- ).go()
286
+ )._
184
287
  deepStrictEqual(values, ['eca0b0da0', 'eca0b0da0gfeca0b0da0'])
185
288
  store1$._ = 1
186
289
  store2$._ = 2
@@ -231,7 +334,7 @@ test('prevents diamond dependency problem 6', ()=>{
231
334
  r_rmemo_(
232
335
  ()=>`${a$._}${c$._}`,
233
336
  combined$=>values.push(combined$._)
234
- ).go()
337
+ )._
235
338
  deepStrictEqual(values, ['a0c0'])
236
339
  store1$._ = 1
237
340
  deepStrictEqual(values, ['a0c0', 'a1c0'])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ctx-core",
3
- "version": "4.3.1",
3
+ "version": "4.4.0",
4
4
  "description": "ctx-core core library",
5
5
  "keywords": [
6
6
  "ctx-core",
@@ -107,14 +107,14 @@
107
107
  "devDependencies": {
108
108
  "@arethetypeswrong/cli": "^0.13.1",
109
109
  "@size-limit/preset-small-lib": "^11.0.0",
110
- "@types/node": "^20.9.3",
110
+ "@types/node": "^20.9.4",
111
111
  "@types/sinon": "^17.0.2",
112
112
  "c8": "^8.0.1",
113
113
  "check-dts": "^0.7.2",
114
114
  "sinon": "^17.0.1",
115
115
  "size-limit": "^11.0.0",
116
116
  "ts-node": "^10.9.1",
117
- "tsx": "^4.2.0",
117
+ "tsx": "^4.3.0",
118
118
  "typescript": "next",
119
119
  "uvu": "^0.5.6"
120
120
  },
@@ -150,28 +150,28 @@
150
150
  "import": {
151
151
  "./rmemo": "{ r_rmemo_ }"
152
152
  },
153
- "limit": "289 B"
153
+ "limit": "298 B"
154
154
  },
155
155
  {
156
- "name": "r_rmemo_ signal_",
156
+ "name": "r_rmemo_ rw_rmemo_",
157
157
  "import": {
158
158
  "./rmemo": "{ rw_rmemo_, r_rmemo_ }"
159
159
  },
160
160
  "limit": "318 B"
161
161
  },
162
162
  {
163
- "name": "r_rmemo_ signal_ be_ ctx_",
163
+ "name": "r_rmemo_ rw_rmemo_ be_ ctx_",
164
164
  "import": {
165
165
  "./rmemo": "{ rw_rmemo_, r_rmemo_, be_, ctx_ }"
166
166
  },
167
- "limit": "475 B"
167
+ "limit": "478 B"
168
168
  },
169
169
  {
170
- "name": "r_rmemo_ signal_ be_ ctx_ be_r_rmemo_pair_ be_rw_rmemo_triple_",
170
+ "name": "r_rmemo_ rw_rmemo_ be_ ctx_ be_r_rmemo_pair_ be_rw_rmemo_triple_",
171
171
  "import": {
172
172
  "./rmemo": "{ rw_rmemo_, r_rmemo_, be_, ctx_, be_r_rmemo_pair_, be_rw_rmemo_triple_ }"
173
173
  },
174
- "limit": "557 B"
174
+ "limit": "571 B"
175
175
  }
176
176
  ],
177
177
  "scripts": {
package/rmemo/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { rw_rmemo__set_ as set_ } from '../all/rmemo/index.js'
1
2
  export * from '../all/be_/index.js'
2
3
  export * from '../all/be_r_rmemo_pair/index.js'
3
4
  export * from '../all/be_rw_rmemo_triple/index.js'