fib-pool 1.6.1 → 1.6.2

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/Readme.md CHANGED
@@ -103,6 +103,45 @@ var res = p("test", (conn) => {
103
103
 
104
104
  ```
105
105
 
106
+ ## Strict mode (default: enabled)
107
+
108
+ By default, the pool uses strict mode to prevent accessing pooled objects outside of the callback function. This helps prevent resource leaks and ensures proper resource management.
109
+
110
+ ```js
111
+ var Pool = require("fib-pool");
112
+
113
+ var p = Pool({
114
+ create: () => {
115
+ return { value: 1, getValue: function() { return this.value; } };
116
+ }
117
+ });
118
+
119
+ var leaked;
120
+ p((obj) => {
121
+ leaked = obj;
122
+ // This works inside the pool callback
123
+ console.log(obj.getValue()); // OK
124
+ });
125
+
126
+ // This will throw an error: "access object outside of pool scope"
127
+ leaked.getValue(); // Error!
128
+ ```
129
+
130
+ When strict mode is enabled, it also prevents:
131
+ - Setting properties on pooled objects
132
+ - Calling `close()`, `destroy()`, or `dispose()` methods directly
133
+
134
+ To disable strict mode:
135
+
136
+ ```js
137
+ var p = Pool({
138
+ create: () => {
139
+ return { value: 1 };
140
+ },
141
+ strict: false
142
+ });
143
+ ```
144
+
106
145
  ## Clear a pool
107
146
 
108
147
  Simple example.
package/lib/index.js CHANGED
@@ -88,7 +88,6 @@ module.exports = function GetPool(_opt, maxsize, timeout) {
88
88
  break;
89
89
  }
90
90
  catch (e) {
91
- console.log(e);
92
91
  if (++cn >= retry) {
93
92
  err = e;
94
93
  break;
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "fib-pool",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "description": "Generic resource pooling for fibjs",
5
- "main": "./lib",
5
+ "main": "./src",
6
6
  "types": "typings/index.d.ts",
7
+ "type": "module",
7
8
  "repository": "git://github.com/fibjs/fib-pool.git",
8
9
  "author": "Liu Hu <lion@9465.net>",
9
10
  "contributors": [
@@ -20,15 +21,15 @@
20
21
  "fibjs"
21
22
  ],
22
23
  "scripts": {
23
- "build": "ftsc ./src/* --outDir lib",
24
24
  "test": "fibjs test",
25
- "ci": "npm run build && npm run test",
26
- "prepublishOnly": "npm run build"
25
+ "typecheck": "tsc --noEmit",
26
+ "ci": "npm run typecheck && npm run test"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@fibjs/ci": "^2.3.0",
30
30
  "@fibjs/types": "^0.27.1",
31
- "fib-typify": "^0.8.4"
31
+ "fib-typify": "^0.8.4",
32
+ "typescript": "^5.9.3"
32
33
  },
33
34
  "ci": {
34
35
  "type": "travis, appveyor",
package/src/index.ts ADDED
@@ -0,0 +1,287 @@
1
+ import * as coroutine from 'coroutine';
2
+ import * as util from 'util';
3
+
4
+ declare namespace FibPoolNS {
5
+ type FirstParameter<T> = T extends (arg: infer T, ...args: any[]) => any ? T : never;
6
+
7
+ interface DippedItemStruct {
8
+ close?: FibPoolOptionResult['destroy'];
9
+ destroy?: FibPoolOptionResult['destroy'];
10
+ dispose?: FibPoolOptionResult['destroy'];
11
+ }
12
+
13
+ interface FibPoolOptionArgs<T extends DippedItemStruct = {}> {
14
+ create: (name: string) => T
15
+ destroy?: (item?: T) => any;
16
+ maxsize?: number
17
+ timeout?: number
18
+ retry?: number | boolean
19
+ strict?: boolean
20
+ }
21
+ interface FibPoolOptionResult<T extends DippedItemStruct = {}> {
22
+ create: FibPoolOptionArgs<T>['create']
23
+ destroy: FibPoolOptionArgs<T>['destroy']
24
+ maxsize: FibPoolOptionArgs<T>['maxsize']
25
+ timeout: FibPoolOptionArgs<T>['timeout']
26
+ retry: FibPoolOptionArgs<T>['retry']
27
+ strict: FibPoolOptionArgs<T>['strict']
28
+ }
29
+
30
+ interface FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> {
31
+ (o: T_CREATED_ITEM): T_POOL_FUNC_RETURN
32
+ }
33
+
34
+ type FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> = {
35
+ <T_RETURN = T_POOL_FUNC_RETURN>(name: string, func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN
36
+ <T_RETURN = T_POOL_FUNC_RETURN>(func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN
37
+ } & {
38
+ connections?(): number;
39
+ info?(): {
40
+ maxsize: number;
41
+ count: number;
42
+ running: number;
43
+ wait: number;
44
+ timeout: number;
45
+ };
46
+ clear?(): void;
47
+ }
48
+ }
49
+
50
+ function ensureError (e: string | Error) {
51
+ if (typeof e === 'string')
52
+ e = new Error(e)
53
+
54
+ return e
55
+ }
56
+
57
+ export default function GetPool<
58
+ T_CREATED_ITEM extends FibPoolNS.DippedItemStruct,
59
+ T_POOL_FUNC_RETURN
60
+ > (
61
+ _opt: FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM> | FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM>['create'],
62
+ maxsize?: number,
63
+ timeout?: number
64
+ ): FibPoolNS.FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN> {
65
+ /* clean input arguments :start */
66
+ let opt = <FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>>_opt;
67
+ if (util.isFunction(_opt)) {
68
+ opt = <FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>>{
69
+ create: _opt,
70
+ maxsize: maxsize,
71
+ timeout: timeout
72
+ };
73
+ }
74
+
75
+ const create = opt.create;
76
+ const destroy = opt.destroy || ((o: {
77
+ close?: FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>['destroy'];
78
+ destroy?: FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>['destroy'];
79
+ dispose?: FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>['destroy'];
80
+ }) => {
81
+ if (util.isFunction(o.close)) o.close();
82
+ if (util.isFunction(o.destroy)) o.destroy();
83
+ if (util.isFunction(o.dispose)) o.dispose();
84
+ });
85
+
86
+ maxsize = opt.maxsize || 10;
87
+ timeout = opt.timeout || 60000;
88
+ let tm = timeout / 10;
89
+ if (tm < 10)
90
+ tm = 10;
91
+
92
+ const retry = opt.retry || 1;
93
+ const _strict = opt.strict === undefined ? true : opt.strict;
94
+ /* clean input arguments :end */
95
+
96
+ let pools = <{
97
+ o: T_CREATED_ITEM;
98
+ name: string;
99
+ time: Date;
100
+ }[]>[];
101
+
102
+ const jobs = <{
103
+ name: string;
104
+ ev: Class_Event;
105
+ o?: T_CREATED_ITEM;
106
+ e?: Error;
107
+ }[]>[];
108
+
109
+ let count = 0;
110
+ let running = 0;
111
+
112
+ const sem = new coroutine.Semaphore(maxsize);
113
+ let clearTimer: Class_Timer;
114
+
115
+ function clearPool() {
116
+ let c: typeof pools[any];
117
+ const d = new Date().getTime();
118
+
119
+ while (count) {
120
+ c = pools[0];
121
+
122
+ if (d - c.time.getTime() > timeout) {
123
+ pools = pools.slice(1);
124
+ count--;
125
+
126
+ if (c.o !== undefined)
127
+ coroutine.start(destroy, c.o);
128
+ } else
129
+ break;
130
+ }
131
+
132
+ if (!count) {
133
+ if (clearTimer) {
134
+ clearTimer.clear();
135
+ clearTimer = null;
136
+ }
137
+ } else if (!clearTimer) {
138
+ clearTimer = setInterval(clearPool, tm);
139
+ if (clearTimer && typeof clearTimer.unref === 'function')
140
+ clearTimer.unref();
141
+ }
142
+ }
143
+
144
+ function putback(
145
+ name: typeof jobs[any]['name'],
146
+ o: T_CREATED_ITEM,
147
+ e?: Error
148
+ ) {
149
+ e = ensureError(e);
150
+ for (let i = 0; i < jobs.length; i++) {
151
+ const job = jobs[i];
152
+ if (job.name === name) {
153
+ jobs.splice(i, 1);
154
+ job.o = o;
155
+ job.e = e;
156
+ job.ev.set();
157
+ return;
158
+ }
159
+ }
160
+
161
+ if (e === undefined)
162
+ pools[count++] = {
163
+ o: o,
164
+ name: name,
165
+ time: new Date()
166
+ };
167
+ }
168
+
169
+ function connect(name: string) {
170
+ let o: T_CREATED_ITEM;
171
+ let cn = 0;
172
+ let err: Error;
173
+
174
+ while (true) {
175
+ try {
176
+ o = create(name);
177
+ break;
178
+ } catch (e) {
179
+ if (++cn >= (retry as number)) {
180
+ err = e;
181
+ break;
182
+ }
183
+ }
184
+ }
185
+
186
+ putback(name, o, err);
187
+ }
188
+
189
+ const pool: FibPoolNS.FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN> = (
190
+ name: string | FibPoolNS.FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN>,
191
+ func?: FibPoolNS.FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN>
192
+ ) => {
193
+ if (typeof name === 'function') {
194
+ func = name as any;
195
+ name = "";
196
+ }
197
+
198
+ let r: T_POOL_FUNC_RETURN;
199
+ let o: typeof jobs[any]['o'];
200
+ let p = false;
201
+
202
+ clearPool();
203
+ sem.acquire();
204
+
205
+ if (count) {
206
+ for (let i = count - 1; i >= 0; i--)
207
+ if (pools[i].name === name) {
208
+ p = true
209
+ o = pools[i].o;
210
+ pools.splice(i, 1);
211
+ count--;
212
+ break;
213
+ }
214
+ }
215
+
216
+ if (!p) {
217
+ coroutine.start(connect, name);
218
+
219
+ const job = <typeof jobs[any]>{
220
+ name: name,
221
+ ev: new coroutine.Event()
222
+ };
223
+ jobs.push(job);
224
+
225
+ job.ev.wait();
226
+ if (job.e) {
227
+ sem.post();
228
+ throw ensureError(job.e);
229
+ }
230
+ o = job.o;
231
+ }
232
+
233
+ running++;
234
+ try {
235
+ const po = (_strict && typeof o === 'object') ? new Proxy({}, {
236
+ get: function (target, prop) {
237
+ if (prop === 'close' || prop === 'destroy' || prop === 'dispose')
238
+ throw new Error("Cannot close/destroy/dispose");
239
+ if (o === undefined)
240
+ throw new Error("access object outside of pool scope");
241
+ const value = o[prop];
242
+ if (typeof value === 'function') {
243
+ return function () {
244
+ return value.apply(o, arguments);
245
+ }
246
+ }
247
+ return value;
248
+ },
249
+ set: function (target, prop, value) {
250
+ throw new Error("Cannot set property");
251
+ }
252
+ }) as any : o;
253
+
254
+ r = func(po as T_CREATED_ITEM);
255
+ putback(name, o);
256
+ o = undefined;
257
+ } catch (e) {
258
+ if (o !== undefined)
259
+ coroutine.start(destroy, o);
260
+ o = undefined;
261
+ throw ensureError(e);
262
+ } finally {
263
+ running--;
264
+ sem.post();
265
+ clearPool();
266
+ }
267
+
268
+ return r;
269
+ };
270
+
271
+ pool.connections = () => count
272
+
273
+ pool.info = () => ({
274
+ maxsize: maxsize,
275
+ count: count,
276
+ running: running,
277
+ wait: sem.count(),
278
+ timeout: timeout
279
+ })
280
+
281
+ pool.clear = () => {
282
+ timeout = -1;
283
+ clearPool();
284
+ }
285
+
286
+ return pool;
287
+ }
package/tsconfig.json CHANGED
@@ -4,10 +4,14 @@
4
4
  "module": "commonjs",
5
5
  "alwaysStrict": false,
6
6
  "removeComments": true,
7
- "declaration": true,
8
- "declarationDir": "typings",
7
+ "noEmit": true,
9
8
  "types": [
10
9
  "@fibjs/types"
11
10
  ]
12
- }
11
+ },
12
+ "exclude": [
13
+ "test",
14
+ "typings",
15
+ "node_modules"
16
+ ]
13
17
  }
@@ -11,6 +11,7 @@ declare namespace FibPoolNS {
11
11
  maxsize?: number;
12
12
  timeout?: number;
13
13
  retry?: number | boolean;
14
+ strict?: boolean;
14
15
  }
15
16
  interface FibPoolOptionResult<T extends DippedItemStruct = {}> {
16
17
  create: FibPoolOptionArgs<T>['create'];
@@ -18,6 +19,7 @@ declare namespace FibPoolNS {
18
19
  maxsize: FibPoolOptionArgs<T>['maxsize'];
19
20
  timeout: FibPoolOptionArgs<T>['timeout'];
20
21
  retry: FibPoolOptionArgs<T>['retry'];
22
+ strict: FibPoolOptionArgs<T>['strict'];
21
23
  }
22
24
  interface FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> {
23
25
  (o: T_CREATED_ITEM): T_POOL_FUNC_RETURN;