w-ipproxy 1.0.2 → 1.0.4

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.
@@ -74,6 +74,8 @@ import provideServer from './provideServer.mjs'
74
74
  * @param {String} [opt.tar='https://httpbin.org/ip'] 輸入檢測目標網址字串,預設'https://httpbin.org/ip'
75
75
  * @param {Integer} [opt.timeGetProxies=30*1000] 輸入輪循抓取代理伺服器時間間隔整數,單位ms,預設30*1000
76
76
  * @param {Integer} [opt.timeTestProxies=60*1000] 輸入輪循測試代理伺服器時間間隔整數,單位ms,預設60*1000
77
+ * @param {Boolean} [opt.anonymityCheck=true] 輸入是否對代理做匿名性檢測布林值,開啟時會先打https://httpbin.org/ip驗證代理不會將本機真實IP透過X-Forwarded-For等方式洩漏給目標端,預設true
78
+ * @param {Integer} [opt.anonymityCheckRepeats=2] 輸入匿名性檢測重複次數整數,用以偵測負載平衡型代理的機率性洩漏,任一次洩漏即判定失敗,預設2
77
79
  * @param {Boolean} [opt.withServer=false] 輸入是否創建供數據供給伺服器布林值,預設false
78
80
  * @param {Integer} [opt.serverPort=8080] 輸入創建伺服器所用port整數,預設8080
79
81
  * @param {String} [opt.serverApiName='getProxies'] 輸入供給數據API名稱字串,預設'getProxies'
@@ -118,7 +120,7 @@ function WIpProxy(opt = {}) {
118
120
  `&timeout=5000` +
119
121
  // `&country=${join(['tw', 'cn', 'hk', 'jp', 'kr', 'sg', 'my', 'in', 'id', 'th', 'vn'])}` +
120
122
  // `&ssl=yes` +
121
- `&anonymity=all`
123
+ `&anonymity=elite` //elite匿名代理, all會使用X-Forwarded-For儲存原本ip導致被識別
122
124
  }
123
125
 
124
126
  //tar
@@ -141,6 +143,19 @@ function WIpProxy(opt = {}) {
141
143
  }
142
144
  timeTestProxies = cint(timeTestProxies)
143
145
 
146
+ //anonymityCheck
147
+ let anonymityCheck = get(opt, 'anonymityCheck')
148
+ if (!isbol(anonymityCheck)) {
149
+ anonymityCheck = true
150
+ }
151
+
152
+ //anonymityCheckRepeats
153
+ let anonymityCheckRepeats = get(opt, 'anonymityCheckRepeats')
154
+ if (!ispint(anonymityCheckRepeats)) {
155
+ anonymityCheckRepeats = 2
156
+ }
157
+ anonymityCheckRepeats = cint(anonymityCheckRepeats)
158
+
144
159
  //withServer
145
160
  let withServer = get(opt, 'withServer')
146
161
  if (!isbol(withServer)) {
@@ -170,6 +185,71 @@ function WIpProxy(opt = {}) {
170
185
  let prxsRaw = []
171
186
  let kpPrx = {}
172
187
 
188
+ //myRealIp: 本機真實公網IP, 用於匿名性檢測時比對代理egress是否洩漏
189
+ let myRealIp = ''
190
+ let myRealIpReady = false
191
+ let myRealIpPm = null
192
+ async function _fetchMyRealIp() {
193
+ if (myRealIpReady) {
194
+ return
195
+ }
196
+ if (!myRealIpPm) {
197
+ myRealIpPm = (async () => {
198
+ try {
199
+ let res = await axios.get('https://httpbin.org/ip', { timeout: 10000 })
200
+ let origin = get(res, 'data.origin', '')
201
+ if (isestr(origin)) {
202
+ //origin可能為"1.2.3.4"或"1.2.3.4:port", 取首個逗號前且去port
203
+ myRealIp = origin.split(',')[0].trim().split(':')[0]
204
+ }
205
+ }
206
+ catch (err) {
207
+ //取不到就算了, 匿名性檢查會自動跳過
208
+ }
209
+ myRealIpReady = true
210
+ })()
211
+ }
212
+ await myRealIpPm
213
+ }
214
+
215
+ async function _anonymityCheckCore(host, port) {
216
+ if (!anonymityCheck) {
217
+ return { ok: true }
218
+ }
219
+ if (!isestr(myRealIp)) {
220
+ return { ok: true } //沒抓到自己IP就無從比對, 放行
221
+ }
222
+ let agent = new https.Agent({ rejectUnauthorized: false })
223
+ for (let i = 0; i < anonymityCheckRepeats; i++) {
224
+ try {
225
+ let res = await axios.get('https://httpbin.org/ip', {
226
+ proxy: { host, port, protocol: 'http' },
227
+ httpsAgent: agent,
228
+ timeout: 5000,
229
+ headers: {
230
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/122.0.0.0 Safari/537.36',
231
+ 'Accept': 'application/json',
232
+ },
233
+ validateStatus: () => true,
234
+ })
235
+ if (res.status !== 200) {
236
+ return { ok: false, reason: `anonymity status[${res.status}]` }
237
+ }
238
+ let origin = get(res, 'data.origin', '')
239
+ if (!isestr(origin)) {
240
+ return { ok: false, reason: 'anonymity non-json' }
241
+ }
242
+ if (origin.indexOf(myRealIp) >= 0) {
243
+ return { ok: false, reason: `leaks real IP` }
244
+ }
245
+ }
246
+ catch (err) {
247
+ return { ok: false, reason: trim(err.message || '') }
248
+ }
249
+ }
250
+ return { ok: true }
251
+ }
252
+
173
253
  async function _getProxiesCore(src) {
174
254
  let ps = []
175
255
 
@@ -249,6 +329,12 @@ function WIpProxy(opt = {}) {
249
329
  }
250
330
  }
251
331
 
332
+ //匿名性預檢: 先確認代理不會透過X-Forwarded-For等洩漏本機真實IP
333
+ let ac = await _anonymityCheckCore(host, port)
334
+ if (!ac.ok) {
335
+ return ret('error', ac.reason)
336
+ }
337
+
252
338
  //r
253
339
  let r = {}
254
340
  try {
@@ -345,6 +431,9 @@ function WIpProxy(opt = {}) {
345
431
 
346
432
  let core = async() => {
347
433
 
434
+ //確保本機真實IP已取得, 供匿名性檢查比對
435
+ await _fetchMyRealIp()
436
+
348
437
  //get
349
438
  let r = await _testProxiesCore(prxsRaw)
350
439
 
@@ -452,7 +541,7 @@ export default WIpProxy
452
541
  <br class="clear">
453
542
 
454
543
  <footer>
455
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.2</a> on Tue Mar 17 2026 21:42:27 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
544
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Fri Apr 17 2026 13:33:04 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
456
545
  </footer>
457
546
 
458
547
  <script>prettyPrint();</script>
package/docs/index.html CHANGED
@@ -71,7 +71,7 @@
71
71
  <br class="clear">
72
72
 
73
73
  <footer>
74
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.2</a> on Tue Mar 17 2026 21:42:27 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
74
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Fri Apr 17 2026 13:33:04 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
75
75
  </footer>
76
76
 
77
77
  <script>prettyPrint();</script>
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "w-ipproxy",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "main": "dist/w-ipproxy.umd.js",
5
5
  "dependencies": {
6
- "@hapi/hapi": "^21.4.7",
6
+ "@hapi/hapi": "^21.4.8",
7
7
  "axios": "^1.13.6",
8
8
  "cheerio": "^1.2.0",
9
- "lodash-es": "^4.17.23",
10
- "wsemi": "^1.8.43"
9
+ "lodash-es": "^4.18.1",
10
+ "wsemi": "^1.8.47"
11
11
  },
12
12
  "devDependencies": {
13
- "w-package-tools": "^1.1.3"
13
+ "w-package-tools": "^1.1.5"
14
14
  },
15
15
  "scripts": {
16
16
  "test": "mocha --parallel --timeout 60000",
package/src/WIpProxy.mjs CHANGED
@@ -27,6 +27,8 @@ import provideServer from './provideServer.mjs'
27
27
  * @param {String} [opt.tar='https://httpbin.org/ip'] 輸入檢測目標網址字串,預設'https://httpbin.org/ip'
28
28
  * @param {Integer} [opt.timeGetProxies=30*1000] 輸入輪循抓取代理伺服器時間間隔整數,單位ms,預設30*1000
29
29
  * @param {Integer} [opt.timeTestProxies=60*1000] 輸入輪循測試代理伺服器時間間隔整數,單位ms,預設60*1000
30
+ * @param {Boolean} [opt.anonymityCheck=true] 輸入是否對代理做匿名性檢測布林值,開啟時會先打https://httpbin.org/ip驗證代理不會將本機真實IP透過X-Forwarded-For等方式洩漏給目標端,預設true
31
+ * @param {Integer} [opt.anonymityCheckRepeats=2] 輸入匿名性檢測重複次數整數,用以偵測負載平衡型代理的機率性洩漏,任一次洩漏即判定失敗,預設2
30
32
  * @param {Boolean} [opt.withServer=false] 輸入是否創建供數據供給伺服器布林值,預設false
31
33
  * @param {Integer} [opt.serverPort=8080] 輸入創建伺服器所用port整數,預設8080
32
34
  * @param {String} [opt.serverApiName='getProxies'] 輸入供給數據API名稱字串,預設'getProxies'
@@ -71,7 +73,7 @@ function WIpProxy(opt = {}) {
71
73
  `&timeout=5000` +
72
74
  // `&country=${join(['tw', 'cn', 'hk', 'jp', 'kr', 'sg', 'my', 'in', 'id', 'th', 'vn'])}` +
73
75
  // `&ssl=yes` +
74
- `&anonymity=all`
76
+ `&anonymity=elite` //elite匿名代理, all會使用X-Forwarded-For儲存原本ip導致被識別
75
77
  }
76
78
 
77
79
  //tar
@@ -94,6 +96,19 @@ function WIpProxy(opt = {}) {
94
96
  }
95
97
  timeTestProxies = cint(timeTestProxies)
96
98
 
99
+ //anonymityCheck
100
+ let anonymityCheck = get(opt, 'anonymityCheck')
101
+ if (!isbol(anonymityCheck)) {
102
+ anonymityCheck = true
103
+ }
104
+
105
+ //anonymityCheckRepeats
106
+ let anonymityCheckRepeats = get(opt, 'anonymityCheckRepeats')
107
+ if (!ispint(anonymityCheckRepeats)) {
108
+ anonymityCheckRepeats = 2
109
+ }
110
+ anonymityCheckRepeats = cint(anonymityCheckRepeats)
111
+
97
112
  //withServer
98
113
  let withServer = get(opt, 'withServer')
99
114
  if (!isbol(withServer)) {
@@ -123,6 +138,71 @@ function WIpProxy(opt = {}) {
123
138
  let prxsRaw = []
124
139
  let kpPrx = {}
125
140
 
141
+ //myRealIp: 本機真實公網IP, 用於匿名性檢測時比對代理egress是否洩漏
142
+ let myRealIp = ''
143
+ let myRealIpReady = false
144
+ let myRealIpPm = null
145
+ async function _fetchMyRealIp() {
146
+ if (myRealIpReady) {
147
+ return
148
+ }
149
+ if (!myRealIpPm) {
150
+ myRealIpPm = (async () => {
151
+ try {
152
+ let res = await axios.get('https://httpbin.org/ip', { timeout: 10000 })
153
+ let origin = get(res, 'data.origin', '')
154
+ if (isestr(origin)) {
155
+ //origin可能為"1.2.3.4"或"1.2.3.4:port", 取首個逗號前且去port
156
+ myRealIp = origin.split(',')[0].trim().split(':')[0]
157
+ }
158
+ }
159
+ catch (err) {
160
+ //取不到就算了, 匿名性檢查會自動跳過
161
+ }
162
+ myRealIpReady = true
163
+ })()
164
+ }
165
+ await myRealIpPm
166
+ }
167
+
168
+ async function _anonymityCheckCore(host, port) {
169
+ if (!anonymityCheck) {
170
+ return { ok: true }
171
+ }
172
+ if (!isestr(myRealIp)) {
173
+ return { ok: true } //沒抓到自己IP就無從比對, 放行
174
+ }
175
+ let agent = new https.Agent({ rejectUnauthorized: false })
176
+ for (let i = 0; i < anonymityCheckRepeats; i++) {
177
+ try {
178
+ let res = await axios.get('https://httpbin.org/ip', {
179
+ proxy: { host, port, protocol: 'http' },
180
+ httpsAgent: agent,
181
+ timeout: 5000,
182
+ headers: {
183
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/122.0.0.0 Safari/537.36',
184
+ 'Accept': 'application/json',
185
+ },
186
+ validateStatus: () => true,
187
+ })
188
+ if (res.status !== 200) {
189
+ return { ok: false, reason: `anonymity status[${res.status}]` }
190
+ }
191
+ let origin = get(res, 'data.origin', '')
192
+ if (!isestr(origin)) {
193
+ return { ok: false, reason: 'anonymity non-json' }
194
+ }
195
+ if (origin.indexOf(myRealIp) >= 0) {
196
+ return { ok: false, reason: `leaks real IP` }
197
+ }
198
+ }
199
+ catch (err) {
200
+ return { ok: false, reason: trim(err.message || '') }
201
+ }
202
+ }
203
+ return { ok: true }
204
+ }
205
+
126
206
  async function _getProxiesCore(src) {
127
207
  let ps = []
128
208
 
@@ -202,6 +282,12 @@ function WIpProxy(opt = {}) {
202
282
  }
203
283
  }
204
284
 
285
+ //匿名性預檢: 先確認代理不會透過X-Forwarded-For等洩漏本機真實IP
286
+ let ac = await _anonymityCheckCore(host, port)
287
+ if (!ac.ok) {
288
+ return ret('error', ac.reason)
289
+ }
290
+
205
291
  //r
206
292
  let r = {}
207
293
  try {
@@ -298,6 +384,9 @@ function WIpProxy(opt = {}) {
298
384
 
299
385
  let core = async() => {
300
386
 
387
+ //確保本機真實IP已取得, 供匿名性檢查比對
388
+ await _fetchMyRealIp()
389
+
301
390
  //get
302
391
  let r = await _testProxiesCore(prxsRaw)
303
392
 
@@ -0,0 +1,140 @@
1
+ import assert from 'assert'
2
+ import axios from 'axios'
3
+ import https from 'https'
4
+ import wip from '../src/WIpProxy.mjs'
5
+
6
+
7
+ describe('anonymity (elite + anonymityCheck)', function() {
8
+ this.timeout(240 * 1000) //4min, 因為getProxiesOnce加上獨立leak驗證本身就耗時
9
+
10
+ let myIp = ''
11
+ let ps = []
12
+
13
+ before(async function() {
14
+
15
+ //取本機真實公網IP
16
+ try {
17
+ let r = await axios.get('https://httpbin.org/ip', { timeout: 10000 })
18
+ myIp = r.data?.origin || ''
19
+ }
20
+ catch (err) {
21
+ this.skip()
22
+ }
23
+ if (!myIp) {
24
+ this.skip()
25
+ }
26
+
27
+ //預設設定已是anonymity=elite且啟用anonymityCheck, 走完整過濾路徑
28
+ let wo = wip({})
29
+ ps = await wo.getProxiesOnce()
30
+
31
+ })
32
+
33
+ it('no returned proxy should leak real IP via httpbin origin', async function() {
34
+ if (ps.length === 0) {
35
+ this.skip()
36
+ }
37
+
38
+ let agent = new https.Agent({ rejectUnauthorized: false })
39
+ let sample = ps.slice(0, 30)
40
+ let pms = sample.map(async (p) => {
41
+ try {
42
+ let res = await axios.get('https://httpbin.org/ip', {
43
+ proxy: { host: p.host, port: p.port, protocol: 'http' },
44
+ httpsAgent: agent,
45
+ timeout: 8000,
46
+ headers: {
47
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/122.0.0.0 Safari/537.36',
48
+ 'Accept': 'application/json',
49
+ },
50
+ validateStatus: () => true,
51
+ })
52
+ if (res.status !== 200) {
53
+ return { proxy: p.proxy, ok: true, reason: 'non-200, ignored' }
54
+ }
55
+ let origin = res.data?.origin
56
+ if (typeof origin !== 'string') {
57
+ return { proxy: p.proxy, ok: true, reason: 'non-json, ignored' }
58
+ }
59
+ //回傳origin含本機真實IP即代表該代理洩漏
60
+ let leaks = origin.indexOf(myIp) >= 0
61
+ return { proxy: p.proxy, ok: !leaks, origin }
62
+ }
63
+ catch (err) {
64
+ //網路錯誤不算洩漏, 直接放行
65
+ return { proxy: p.proxy, ok: true, reason: err.message }
66
+ }
67
+ })
68
+ let rrs = await Promise.all(pms)
69
+ let leaked = rrs.filter((r) => !r.ok)
70
+
71
+ assert.strictEqual(
72
+ leaked.length, 0,
73
+ `expected 0 proxies to leak real IP, got ${leaked.length}: ` +
74
+ leaked.slice(0, 5).map((r) => `${r.proxy} (origin=${r.origin})`).join('; ')
75
+ )
76
+ })
77
+
78
+ it('no returned proxy should carry X-Forwarded-For / Via when viewed from target', async function() {
79
+ if (ps.length === 0) {
80
+ this.skip()
81
+ }
82
+
83
+ let LEAK_IP_HEADERS = [
84
+ 'x-forwarded-for', 'x-real-ip', 'x-client-ip', 'client-ip',
85
+ 'true-client-ip', 'cf-connecting-ip', 'forwarded',
86
+ 'x-originating-ip', 'x-cluster-client-ip',
87
+ ]
88
+ let LEAK_PROXY_HEADERS = [
89
+ 'via', 'proxy-connection', 'x-proxy-id',
90
+ ]
91
+
92
+ let agent = new https.Agent({ rejectUnauthorized: false })
93
+ let sample = ps.slice(0, 30)
94
+ let pms = sample.map(async (p) => {
95
+ try {
96
+ let res = await axios.get('https://httpbin.org/headers', {
97
+ proxy: { host: p.host, port: p.port, protocol: 'http' },
98
+ httpsAgent: agent,
99
+ timeout: 8000,
100
+ headers: {
101
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/122.0.0.0 Safari/537.36',
102
+ 'Accept': 'application/json',
103
+ },
104
+ validateStatus: () => true,
105
+ })
106
+ if (res.status !== 200 || typeof res.data?.headers !== 'object') {
107
+ return { proxy: p.proxy, ok: true, reason: 'ignored' }
108
+ }
109
+ let hs = {}
110
+ for (let k in res.data.headers) {
111
+ hs[k.toLowerCase()] = String(res.data.headers[k])
112
+ }
113
+ let bad = []
114
+ for (let k of LEAK_IP_HEADERS) {
115
+ if (k in hs && hs[k].indexOf(myIp) >= 0) {
116
+ bad.push(`${k}=${hs[k]}`)
117
+ }
118
+ }
119
+ for (let k of LEAK_PROXY_HEADERS) {
120
+ if (k in hs) {
121
+ bad.push(`${k}=${hs[k]}`)
122
+ }
123
+ }
124
+ return { proxy: p.proxy, ok: bad.length === 0, bad }
125
+ }
126
+ catch (err) {
127
+ return { proxy: p.proxy, ok: true, reason: err.message }
128
+ }
129
+ })
130
+ let rrs = await Promise.all(pms)
131
+ let leaked = rrs.filter((r) => !r.ok)
132
+
133
+ assert.strictEqual(
134
+ leaked.length, 0,
135
+ `expected 0 proxies with leaky headers, got ${leaked.length}: ` +
136
+ leaked.slice(0, 5).map((r) => `${r.proxy} (${r.bad.join(',')})`).join('; ')
137
+ )
138
+ })
139
+
140
+ })
@@ -0,0 +1,89 @@
1
+ import assert from 'assert'
2
+ import axios from 'axios'
3
+ import https from 'https'
4
+ import wip from '../src/WIpProxy.mjs'
5
+
6
+
7
+ describe('getProxiesOnce', function() {
8
+ this.timeout(180 * 1000) //3min, 因為需要抓清單+檢測+獨立驗證
9
+
10
+ let myIp = ''
11
+ let ps = []
12
+
13
+ before(async function() {
14
+
15
+ //取本機真實公網IP (不走代理), 作為後續洩漏比對基準
16
+ try {
17
+ let r = await axios.get('https://httpbin.org/ip', { timeout: 10000 })
18
+ myIp = r.data?.origin || ''
19
+ }
20
+ catch (err) {
21
+ //網路異常則整組test跳過
22
+ this.skip()
23
+ }
24
+ if (!myIp) {
25
+ this.skip()
26
+ }
27
+
28
+ //取代理清單 (預設anonymity=elite + 匿名性檢查)
29
+ let wo = wip({})
30
+ ps = await wo.getProxiesOnce()
31
+
32
+ })
33
+
34
+ it('should return a non-empty array of proxy objects', function() {
35
+ //若上游proxyscrape當下無代理供應, 放行以免CI誤報 (非本專案bug)
36
+ if (ps.length === 0) {
37
+ this.skip()
38
+ }
39
+ assert.ok(Array.isArray(ps))
40
+ for (let p of ps) {
41
+ assert.strictEqual(typeof p.host, 'string')
42
+ assert.ok(p.host.length > 0)
43
+ assert.strictEqual(typeof p.port, 'number')
44
+ assert.ok(p.port > 0)
45
+ assert.strictEqual(p.proxy, `${p.host}:${p.port}`)
46
+ }
47
+ })
48
+
49
+ it('majority of returned proxies should be independently usable against httpbin', async function() {
50
+ if (ps.length === 0) {
51
+ this.skip()
52
+ }
53
+
54
+ //獨立再打一次httpbin.org/ip, 不倚賴內建的檢測結果
55
+ let agent = new https.Agent({ rejectUnauthorized: false })
56
+ let sample = ps.slice(0, 30) //取前30個避免長時間阻塞CI
57
+ let pms = sample.map(async (p) => {
58
+ try {
59
+ let res = await axios.get('https://httpbin.org/ip', {
60
+ proxy: { host: p.host, port: p.port, protocol: 'http' },
61
+ httpsAgent: agent,
62
+ timeout: 8000,
63
+ headers: {
64
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/122.0.0.0 Safari/537.36',
65
+ 'Accept': 'application/json',
66
+ },
67
+ validateStatus: () => true,
68
+ })
69
+ return res.status === 200
70
+ && typeof res.data === 'object'
71
+ && res.data !== null
72
+ && typeof res.data.origin === 'string'
73
+ }
74
+ catch (err) {
75
+ return false
76
+ }
77
+ })
78
+ let rrs = await Promise.all(pms)
79
+ let okCount = rrs.filter((v) => v).length
80
+ let ratio = okCount / rrs.length
81
+
82
+ //要求至少50%仍可用, 免疫於個別代理剛好在測試間掛掉的flakiness
83
+ assert.ok(
84
+ ratio >= 0.5,
85
+ `independent reachability ratio too low: ${okCount}/${rrs.length} (${(ratio * 100).toFixed(1)}%)`
86
+ )
87
+ })
88
+
89
+ })
@@ -1,20 +1,17 @@
1
1
  import rollupFiles from 'w-package-tools/src/rollupFiles.mjs'
2
2
  import getFiles from 'w-package-tools/src/getFiles.mjs'
3
- import w from 'wsemi'
4
3
 
5
4
 
6
5
  let fdSrc = './src'
7
6
  let fdTar = './dist'
8
- let fns = getFiles(fdSrc)
9
- fns = fns.filter((v) => {
10
- return w.strleft(v, 1) === 'W'
11
- })
7
+
12
8
 
13
9
  rollupFiles({
14
- fns,
10
+ fns: 'WIpProxy.mjs',
15
11
  fdSrc,
16
12
  fdTar,
17
- nameDistType: 'kebabCase',
13
+ hookNameDist: () => 'w-ipproxy',
14
+ // nameDistType: 'kebabCase', //直接由hookNameDist給予
18
15
  globals: {
19
16
  '@hapi/hapi': '@hapi/hapi',
20
17
  '@hapi/inert': '@hapi/inert',