ctx-core 2.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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'
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'
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 {}
@@ -1,2 +1,5 @@
1
1
  import type { nullish } from '../nullish/index.js'
2
+ import type { primitive_T } from '../primitive/index.js'
2
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
@@ -1,8 +1,6 @@
1
- /**
2
- * @param {Object}val
3
- * @returns {Object}
4
- * @private
5
- */
6
- export function prototype_(val) {
7
- return Object.getPrototypeOf(val)
1
+ export const { getPrototypeOf, setPrototypeOf, defineProperty, defineProperties } = Object
2
+ export {
3
+ getPrototypeOf as prototype_,
4
+ setPrototypeOf as prototype__set,
8
5
  }
6
+ export const fn__proto = getPrototypeOf(getPrototypeOf)
@@ -0,0 +1,18 @@
1
+ export declare function rmemo_<val_T>(
2
+ def:rmemo_def_T<val_T>,
3
+ ...subscriber_a:rmemo_subscriber_T<val_T>[]
4
+ ):rmemo_T<val_T>
5
+ export declare function rsig_<val_T>(
6
+ init_val:val_T,
7
+ ...subscriber_a:rmemo_subscriber_T<val_T>[]
8
+ ):rmemo_T<val_T>
9
+ export type rmemo_T<val_T> = ((val?:val_T)=>val_T)&rmemo_o_T<val_T>
10
+ export type rmemo_def_T<val_T> = (rmemo:rmemo_T<val_T>)=>val_T
11
+ export type rmemo_subscriber_T<val_T> = (rmemo:rmemo_T<val_T>)=>unknown
12
+ export type rmemo_o_T<val_T> = {
13
+ _:val_T
14
+ readonly val:val_T
15
+ go():unknown
16
+ onset(val:val_T):unknown
17
+ refresh():val_T
18
+ }
@@ -0,0 +1,88 @@
1
+ /** @typedef {import('./index.d.ts').rmemo_T} */
2
+ /** @typedef {import('./index.d.ts').rmemo_subscriber_T} */
3
+ /** @type {(()=>unknown)[]} */
4
+ let queue = []
5
+ /** @type {WeakRef<rmemo_T>} */
6
+ let cur_ref
7
+ /**
8
+ * @param {(rmemo:rmemo_T<unknown>)=>unknown}_f
9
+ * @param {rmemo_subscriber_T<unknown>[]}subscriber_a
10
+ * @returns {rmemo_T}
11
+ * @private
12
+ */
13
+ export function rmemo_(_f, ...subscriber_a) {
14
+ let rmemo$ = (...arg_a)=>arg_a.length ? rmemo$._ = arg_a[0] : rmemo$._
15
+ let _a = []
16
+ let _r = new WeakRef(()=>rmemo$.refresh(_f(rmemo$)))
17
+ _r.l = 0
18
+ rmemo$._a = _a
19
+ rmemo$._f = _f
20
+ rmemo$._r = _r
21
+ rmemo$._rS = new Set
22
+ rmemo$.go = ()=>(rmemo$(), rmemo$)
23
+ rmemo$.onset = ()=>0
24
+ Object.defineProperty(rmemo$, '_', {
25
+ get() {
26
+ if (!_a.length) {
27
+ let prev_ref = cur_ref
28
+ cur_ref = _r
29
+ try {
30
+ _a[0] = _f(rmemo$)
31
+ } finally {
32
+ cur_ref = prev_ref
33
+ }
34
+ }
35
+ // allow self-referencing
36
+ if (cur_ref && cur_ref !== _r) {
37
+ // Math.max: bitwise is much faster on chrome
38
+ // https://measurethat.net/Benchmarks/Show/28483/0/mathmax-vs-bitwise
39
+ cur_ref.l = cur_ref.l ^ ((cur_ref.l ^ _r.l + 1) & -(cur_ref.l < _r.l + 1))
40
+ rmemo$._rS.add(cur_ref)
41
+ }
42
+ return _a[0]
43
+ },
44
+ set(val) {
45
+ if (!_a.length || val !== _a[0]) {
46
+ rmemo$.refresh(val)
47
+ }
48
+ return val
49
+ }
50
+ })
51
+ rmemo$.refresh = val=>{
52
+ let length = _a.length
53
+ _a[0] = val
54
+ rmemo$.onset(val)
55
+ if (length) {
56
+ let run_queue = !queue[0]
57
+ for (let ref of rmemo$._rS) {
58
+ if (!~queue.indexOf(ref)) queue.push(ref)
59
+ }
60
+ if (run_queue) {
61
+ for (let ref; ref = queue.shift();) {
62
+ if (queue.some(_ref=>ref.l > _ref.l)) {
63
+ queue.push(ref)
64
+ } else {
65
+ (ref.deref() || rmemo$._rS.delete)(ref)
66
+ }
67
+ }
68
+ }
69
+ }
70
+ return rmemo$
71
+ }
72
+ rmemo$._sa = subscriber_a.map(subscriber=>
73
+ rmemo_(()=>subscriber(rmemo$)).go())
74
+ return rmemo$
75
+ }
76
+ /**
77
+ * @param {unknown}init_val
78
+ * @param {rmemo_subscriber_T[]}subscriber_a
79
+ * @returns {rmemo_T}
80
+ * @private
81
+ */
82
+ export function rsig_(init_val, ...subscriber_a) {
83
+ let signal$ =
84
+ rmemo_(signal$=>signal$._v, ...subscriber_a)
85
+ signal$.onset = val=>signal$._v = val
86
+ signal$._v = init_val
87
+ return signal$
88
+ }
@@ -0,0 +1,250 @@
1
+ import { deepStrictEqual } from 'node:assert'
2
+ import { test } from 'uvu'
3
+ import { equal } from 'uvu/assert'
4
+ import { sleep } from '../sleep/index.js'
5
+ import { rmemo_, rmemo_T, rsig_ } from './index.js'
6
+ test('rmemo_|static value', ()=>{
7
+ let count = 0
8
+ let rmemo$ = rmemo_(rmemo=>{
9
+ count++
10
+ return 'rmemo-value'
11
+ })
12
+ equal(count, 0)
13
+ equal(rmemo$(), 'rmemo-value')
14
+ equal(count, 1)
15
+ equal(rmemo$(), 'rmemo-value')
16
+ equal(count, 1)
17
+ })
18
+ test('rsig_', ()=>{
19
+ let rsig$ = rsig_('val0')
20
+ equal(rsig$(), 'val0')
21
+ rsig$('val1')
22
+ equal(rsig$(), 'val1')
23
+ })
24
+ test('rmemo_|def function|rmemo$ argument', ()=>{
25
+ let rsig$ = rsig_('val0')
26
+ let rmemo$:rmemo_T<string>&{custom?:string} = rmemo_<string>((_rmemo$:rmemo_T<string>&{custom?:string})=>
27
+ `${_rmemo$.custom}-${rsig$()}`)
28
+ rmemo$.custom = 'custom_val0'
29
+ equal(rmemo$(), 'custom_val0-val0')
30
+ rmemo$.custom = 'custom_val1'
31
+ equal(rmemo$(), 'custom_val0-val0')
32
+ rsig$('val1')
33
+ equal(rmemo$(), 'custom_val1-val1')
34
+ })
35
+ test('rsig_|async subsubscriber|case 1', async ()=>{
36
+ let resolve:(user:{ id:string })=>void
37
+ let user0 = { id: 'id-0' }
38
+ let user1 = { id: 'id-1' }
39
+ let id$ = rsig_('id-0')
40
+ let user$ = rsig_<{ id:string }|null>(
41
+ null,
42
+ async (_user$)=>{
43
+ id$()
44
+ let user:{ id:string } = await new Promise(_resolve=>resolve = _resolve)
45
+ _user$(user)
46
+ })
47
+ equal(user$(), null)
48
+ resolve!(user0)
49
+ await sleep(0)
50
+ equal(user$(), user0)
51
+ id$('id-1')
52
+ equal(user$(), user0)
53
+ resolve!(user1)
54
+ await sleep(0)
55
+ })
56
+ test('rsig_|async subsubscriber|case 2', async ()=>{
57
+ let a$ = rsig_(1)
58
+ let b$ = rsig_(2)
59
+ let sleepCycles = 5
60
+ let taskArgumentsCalls:number[][] = []
61
+ let sum$ = rsig_<null|number>(null,
62
+ async sum$=>{
63
+ taskArgumentsCalls.push([a$(), b$()])
64
+ for (let i = 0; i < sleepCycles; i++) {
65
+ await Promise.resolve()
66
+ }
67
+ sum$(a$() + b$())
68
+ })
69
+ equal(sum$(), null)
70
+ deepStrictEqual(taskArgumentsCalls, [[1, 2]])
71
+ a$(10)
72
+ b$(20)
73
+ for (let i = 0; i < sleepCycles; i++) {
74
+ equal(sum$(), null)
75
+ await Promise.resolve()
76
+ deepStrictEqual(taskArgumentsCalls, [
77
+ [1, 2],
78
+ [10, 2],
79
+ [10, 20]
80
+ ])
81
+ }
82
+ equal(sum$(), 30)
83
+ deepStrictEqual(taskArgumentsCalls, [
84
+ [1, 2],
85
+ [10, 2],
86
+ [10, 20]
87
+ ])
88
+ })
89
+ test('rmemo_+rsig_|simple graph', ()=>{
90
+ let base$ = rsig_('base0')
91
+ let dep0$ = rmemo_(()=>base$() + '-dep0')
92
+ let dep1$ = rmemo_(()=>dep0$() + '-dep1')
93
+ let dep2$ = rmemo_(()=>dep1$() + '-dep2')
94
+ let dep3$ = rmemo_(()=>dep2$() + '-dep3')
95
+ let dep4$ = rmemo_(()=>dep3$() + '-dep4')
96
+ equal(dep4$(), 'base0-dep0-dep1-dep2-dep3-dep4')
97
+ equal(dep3$(), 'base0-dep0-dep1-dep2-dep3')
98
+ equal(dep2$(), 'base0-dep0-dep1-dep2')
99
+ equal(dep1$(), 'base0-dep0-dep1')
100
+ equal(dep0$(), 'base0-dep0')
101
+ equal(base$(), 'base0')
102
+ base$('base1')
103
+ equal(base$(), 'base1')
104
+ equal(dep0$(), 'base1-dep0')
105
+ equal(dep1$(), 'base1-dep0-dep1')
106
+ equal(dep2$(), 'base1-dep0-dep1-dep2')
107
+ equal(dep3$(), 'base1-dep0-dep1-dep2-dep3')
108
+ equal(dep4$(), 'base1-dep0-dep1-dep2-dep3-dep4')
109
+ })
110
+ test('prevents diamond dependency problem 1', ()=>{
111
+ let store$ = rsig_(0)
112
+ let values:string[] = []
113
+ let a$ = rmemo_(()=>`a${store$()}`)
114
+ let b$ = rmemo_(()=>a$().replace('a', 'b'))
115
+ let c$ = rmemo_(()=>a$().replace('a', 'c'))
116
+ let d$ = rmemo_(()=>a$().replace('a', 'd'))
117
+ let combined$ = rmemo_(()=>`${b$()}${c$()}${d$()}`,
118
+ combined$=>
119
+ values.push(combined$()))
120
+ deepStrictEqual(values, ['b0c0d0'])
121
+ store$(1)
122
+ store$(2)
123
+ deepStrictEqual(values, ['b0c0d0', 'b1c1d1', 'b2c2d2'])
124
+ })
125
+ test('prevents diamond dependency problem 2', ()=>{
126
+ let store$ = rsig_(0)
127
+ let values:string[] = []
128
+ let a$ = rmemo_(()=>`a${store$()}`)
129
+ let b$ = rmemo_(()=>a$().replace('a', 'b'))
130
+ let c$ = rmemo_(()=>b$().replace('b', 'c'))
131
+ let d$ = rmemo_(()=>c$().replace('c', 'd'))
132
+ let e$ = rmemo_(()=>d$().replace('d', 'e'))
133
+ let combined$ = rmemo_<string>(
134
+ ()=>[a$(), e$()].join(''),
135
+ combined$=>values.push(combined$()))
136
+ deepStrictEqual(values, ['a0e0'])
137
+ store$(1)
138
+ deepStrictEqual(values, ['a0e0', 'a1e1'])
139
+ })
140
+ test('prevents diamond dependency problem 3', ()=>{
141
+ let store$ = rsig_(0)
142
+ let values:string[] = []
143
+ let a$ = rmemo_(()=>`a${store$()}`)
144
+ let b$ = rmemo_(()=>a$().replace('a', 'b'))
145
+ let c$ = rmemo_(()=>b$().replace('b', 'c'))
146
+ let d$ = rmemo_(()=>c$().replace('c', 'd'))
147
+ rmemo_<string>(
148
+ ()=>`${a$()}${b$()}${c$()}${d$()}`,
149
+ combined$=>values.push(combined$()))
150
+ deepStrictEqual(values, ['a0b0c0d0'])
151
+ store$(1)
152
+ deepStrictEqual(values, ['a0b0c0d0', 'a1b1c1d1'])
153
+ })
154
+ test('autosubscribe: prevents diamond dependency problem 4 (complex)', ()=>{
155
+ let store1$ = rsig_(0)
156
+ let store2$ = rsig_(0)
157
+ let values:string[] = []
158
+ let fn =
159
+ (name:string)=>
160
+ (...v:(number|string)[])=>
161
+ `${name}${v.join('')}`
162
+ let a$ = rmemo_(()=>`a${store1$()}`)
163
+ let b$ = rmemo_(()=>`b${store2$()}`)
164
+ let c$ = rmemo_(()=>`c${a$()}${b$()}`)
165
+ let d$ = rmemo_(()=>`d${a$()}`)
166
+ let e$ = rmemo_(()=>`e${c$()}${d$()}`)
167
+ let f$ = rmemo_(()=>`f${e$()}`)
168
+ let g$ = rmemo_(()=>`g${f$()}`)
169
+ rmemo_(
170
+ ()=>e$(),
171
+ combined1$=>values.push(combined1$()))
172
+ rmemo_(
173
+ ()=>[e$(), g$()].join(''),
174
+ combined2$=>values.push(combined2$()))
175
+ deepStrictEqual(values, ['eca0b0da0', 'eca0b0da0gfeca0b0da0'])
176
+ store1$(1)
177
+ store2$(2)
178
+ deepStrictEqual(values, [
179
+ 'eca0b0da0',
180
+ 'eca0b0da0gfeca0b0da0',
181
+ 'eca1b0da1',
182
+ 'eca1b0da1gfeca1b0da1',
183
+ 'eca1b2da1',
184
+ 'eca1b2da1gfeca1b2da1'
185
+ ])
186
+ })
187
+ test('prevents diamond dependency problem 5', ()=>{
188
+ let events = ''
189
+ let firstName$ = rsig_('John')
190
+ let lastName$ = rsig_('Doe')
191
+ let fullName$ = rmemo_(()=>{
192
+ events += 'full '
193
+ return `${firstName$()} ${lastName$()}`
194
+ })
195
+ let isFirstShort$ = rmemo_(()=>{
196
+ events += 'short '
197
+ return firstName$().length < 10
198
+ })
199
+ let displayName$ = rmemo_(
200
+ ()=>{
201
+ events += 'display '
202
+ return isFirstShort$() ? fullName$() : firstName$()
203
+ }
204
+ )
205
+ equal(events, '')
206
+ equal(displayName$(), 'John Doe')
207
+ equal(events, 'display short full ')
208
+ firstName$('Benedict')
209
+ equal(displayName$(), 'Benedict Doe')
210
+ equal(events, 'display short full short full display ')
211
+ firstName$('Montgomery')
212
+ equal(displayName$(), 'Montgomery')
213
+ equal(events, 'display short full short full display short full display ')
214
+ })
215
+ test('prevents diamond dependency problem 6', ()=>{
216
+ let store1$ = rsig_(0)
217
+ let store2$ = rsig_(0)
218
+ let values:string[] = []
219
+ let a$ = rmemo_(()=>`a${store1$()}`)
220
+ let b$ = rmemo_(()=>`b${store2$()}`)
221
+ let c$ = rmemo_(()=>b$().replace('b', 'c'))
222
+ rmemo_(
223
+ ()=>`${a$()}${c$()}`,
224
+ combined$=>values.push(combined$()))
225
+ deepStrictEqual(values, ['a0c0'])
226
+ store1$(1)
227
+ deepStrictEqual(values, ['a0c0', 'a1c0'])
228
+ })
229
+ test('prevents dependency listeners from being out of order', ()=>{
230
+ let base$ = rsig_(0)
231
+ let a$ = rmemo_(()=>{
232
+ return `${base$()}a`
233
+ })
234
+ let values:string[] = []
235
+ let b$ = rmemo_(()=>{
236
+ return `${a$()}b`
237
+ }, b$=>values.push(b$()))
238
+ equal(b$(), '0ab')
239
+ deepStrictEqual(values, ['0ab'])
240
+ equal(a$(), '0a')
241
+ base$(1)
242
+ deepStrictEqual(values, ['0ab', '1ab'])
243
+ })
244
+ test('computes initial value when argument is undefined', ()=>{
245
+ let one$ = rsig_<string|undefined>(undefined)
246
+ let two$ = rmemo_(()=>!!one$())
247
+ equal(one$(), undefined)
248
+ equal(two$(), false)
249
+ })
250
+ test.run()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ctx-core",
3
- "version": "2.3.0",
3
+ "version": "3.0.0",
4
4
  "description": "ctx-core core library",
5
5
  "keywords": [
6
6
  "ctx-core",
@@ -53,7 +53,7 @@
53
53
  "queue",
54
54
  "random",
55
55
  "regex",
56
- "rememo",
56
+ "rmemo",
57
57
  "set",
58
58
  "sleep",
59
59
  "string",
@@ -94,6 +94,7 @@
94
94
  "./queue": "./queue/index.js",
95
95
  "./random": "./random/index.js",
96
96
  "./regex": "./regex/index.js",
97
+ "./rmemo": "./rmemo/index.js",
97
98
  "./set": "./set/index.js",
98
99
  "./sleep": "./sleep/index.js",
99
100
  "./string": "./string/index.js",
@@ -105,11 +106,13 @@
105
106
  },
106
107
  "devDependencies": {
107
108
  "@arethetypeswrong/cli": "^0.13.1",
108
- "@types/node": "^20.9.0",
109
+ "@size-limit/preset-small-lib": "^11.0.0",
110
+ "@types/node": "^20.9.1",
109
111
  "@types/sinon": "^17.0.1",
110
112
  "c8": "^8.0.1",
111
113
  "check-dts": "^0.7.2",
112
114
  "sinon": "^17.0.1",
115
+ "size-limit": "^11.0.0",
113
116
  "ts-node": "^10.9.1",
114
117
  "tsx": "^4.1.2",
115
118
  "typescript": "next",
@@ -120,12 +123,37 @@
120
123
  "cache": "~/.npm"
121
124
  },
122
125
  "sideEffects": false,
126
+ "size-limit": [
127
+ {
128
+ "name": "be",
129
+ "import": {
130
+ "./be": "{ be_ }"
131
+ },
132
+ "limit": "600 B"
133
+ },
134
+ {
135
+ "name": "rmemo",
136
+ "import": {
137
+ "./rmemo": "{ rmemo_ }"
138
+ },
139
+ "limit": "351 B"
140
+ },
141
+ {
142
+ "name": "rmemo signal",
143
+ "import": {
144
+ "./rmemo": "{ rsig_ }"
145
+ },
146
+ "limit": "377 B"
147
+ }
148
+ ],
123
149
  "scripts": {
124
150
  "build": ":",
125
151
  "clean": ":",
126
152
  "exec": "$@",
127
- "test": "pnpm test-unit && check-dts",
128
- "test-unit": "tsx node_modules/uvu/bin.js . '\\.test\\.(ts|js)$'",
129
- "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"
130
158
  }
131
159
  }
@@ -0,0 +1 @@
1
+ export * from '../all/rmemo/index.js'
package/rmemo/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from '../all/rmemo/index.js'
@@ -1,8 +0,0 @@
1
- export declare function rememo_<val_T>(def:()=>val_T):rememo_T<val_T>
2
- export type rememo_T<val_T> =
3
- ((rememo:rememo_T<val>)=>val_T)
4
- &rememo_o_T<val_T>
5
- export type rememo_o_T<val_T> = {
6
- val:val_T
7
- listeners:Set<WeakRef<rememo_T<val_T>>>
8
- }
@@ -1,9 +0,0 @@
1
- /** @typedef {import('./index.d.ts').rememo_o_T} */
2
- /**
3
- * @param {(rememo:rememo_T<unknown>)=>unknown}fn
4
- * @returns {rememo_T}
5
- * @private
6
- */
7
- export function rememo_(fn) {
8
- // let
9
- }
package/rememo/index.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from '../all/rememo/index.js'
package/rememo/index.js DELETED
@@ -1 +0,0 @@
1
- export * from '../all/rememo/index.js'