thaw 5.0.2 → 6.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/README.md CHANGED
@@ -2,9 +2,17 @@ The narrow belt for AOP 🎀
2
2
  ---
3
3
  This package provides **narrow-trench** methods for aspect-oriented programming (AOP).
4
4
 
5
+ ## Abstract
6
+
7
+ * Consistent advice pattern semantics 📐
8
+ * Deterministic behavior (tested with mocked timers) ⚖️
9
+ * Preserves `this` context 🔒
10
+ * Small, predictable behavior 🎯
11
+ * Zero dependencies 🗽
12
+
5
13
  ## Prerequisites
6
14
 
7
- * Node.js `>= 20.x`
15
+ * Node.js `>= 20.0.0`
8
16
 
9
17
  ## Installation
10
18
 
@@ -15,37 +23,97 @@ npm install thaw --save
15
23
  ### Usage
16
24
 
17
25
  ```javascript
18
- import * as thaw from 'thaw';
19
-
20
- const around = thaw.around((val) => val + 1, (val) => val + val);
21
- const after = thaw.after((val) => val + 1, (val) => val + val);
22
- const before = thaw.before((val) => val + 1, (val) => val + val);
23
- const compose = thaw.compose(around, after, before);
24
-
25
- console.assert(around(1) === 6, 'around');
26
- console.assert(after(1) === 4, 'after');
27
- console.assert(before(1) === 3, 'before');
28
- console.assert(compose(1) === 29, 'compose');
29
-
30
- let bip = 0;
31
- const debounce = thaw.debounce(() => bip++, 100);
32
-
33
- setTimeout(debounce, 0);
34
- setTimeout(debounce, 100);
35
- setTimeout(debounce, 250); // will fire
36
- setTimeout(debounce, 300);
37
- setTimeout(debounce, 350); // will fire
38
- setTimeout(debounce, 400);
39
- setTimeout(() => console.assert(bip === 2, 'debounce'), 1e3);
40
-
41
- let pib = 0;
42
- const throttle = thaw.throttle(() => pib++, 100);
43
-
44
- setTimeout(throttle, 0); // will fire
45
- setTimeout(throttle, 100);
46
- setTimeout(throttle, 250); // will fire
47
- setTimeout(throttle, 300);
48
- setTimeout(throttle, 350); // will fire
49
- setTimeout(throttle, 400);
50
- setTimeout(() => console.assert(pib === 3, 'throttle'), 1e3);
26
+ import {
27
+ after,
28
+ afterReturning,
29
+ afterThrowing,
30
+ around,
31
+ before,
32
+ debounce,
33
+ pipe,
34
+ throttle,
35
+ } from 'thaw';
36
+
37
+ {
38
+ const fn = after(
39
+ (v) => v + 1,
40
+ (...args) => console.log('args passed to fn', args), // [ 1 ]
41
+ );
42
+
43
+ console.log('after', fn(1)); // 2
44
+ }
45
+
46
+ {
47
+ const fn = afterReturning(
48
+ (a, b) => a + b,
49
+ (result, ...args) => `${ result }-${ args }`,
50
+ );
51
+
52
+ console.log('afterReturning', fn(2, 3)); // 5-2,3
53
+ }
54
+
55
+ {
56
+ const fn = afterThrowing(
57
+ () => { throw new Error('boom'); },
58
+ (err, ...args) => console.error('afterThrowing', `${ err.message }-${ args }`), // boom-2,3
59
+ );
60
+
61
+ try {
62
+ fn(2, 3);
63
+ } catch (err) {
64
+ console.error('afterThrowing', err.message); // boom
65
+ }
66
+ }
67
+
68
+ {
69
+ const fn = around(
70
+ (v) => v + 1,
71
+ (proceed, ...args) => proceed() + args.at(0),
72
+ );
73
+
74
+ console.log('around', fn(1)); // 3
75
+ }
76
+
77
+ {
78
+ const fn = before(
79
+ (v) => v + 1,
80
+ (...args) => console.log('args passed to fn', args), // [ 1 ]
81
+ );
82
+
83
+ console.log('before', fn(1)); // 2
84
+ }
85
+
86
+ {
87
+ const fn = debounce((v) => {
88
+ console.log('debounce', v); // 3; after -> 200ms
89
+ }, 200);
90
+
91
+ fn(1);
92
+ fn(2);
93
+ fn(3);
94
+ }
95
+
96
+ {
97
+ const fn = pipe(
98
+ (v) => v + 2,
99
+ (v) => v + 3,
100
+ (v) => v + 4,
101
+ );
102
+
103
+ console.log('pipe', fn(1)); // 10
104
+ }
105
+
106
+ {
107
+ const fn = throttle((v) => {
108
+ console.log('throttle', v); // 1; idle -> 200ms
109
+ }, 200);
110
+
111
+ fn(1);
112
+ fn(2);
113
+ fn(3);
114
+ }
51
115
  ```
116
+
117
+ ---
118
+
119
+ For more details, please check tests in the repository.
package/dist/index.cjs ADDED
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.throttle = exports.pipe = exports.debounce = exports.before = exports.around = exports.afterThrowing = exports.afterReturning = exports.after = void 0;
7
+ const after = (fn, advice) => {
8
+ return function (...args) {
9
+ const result = fn.apply(this, args);
10
+ advice.apply(this, args);
11
+ return result;
12
+ };
13
+ };
14
+ exports.after = after;
15
+ const afterReturning = (fn, advice) => {
16
+ return function (...args) {
17
+ return advice.call(this, fn.apply(this, args), ...args);
18
+ };
19
+ };
20
+ exports.afterReturning = afterReturning;
21
+ const afterThrowing = (fn, advice) => {
22
+ return function (...args) {
23
+ try {
24
+ return fn.apply(this, args);
25
+ } catch (err) {
26
+ advice.call(this, err, ...args);
27
+ throw err;
28
+ }
29
+ };
30
+ };
31
+ exports.afterThrowing = afterThrowing;
32
+ const around = (fn, advice) => {
33
+ return function (...args) {
34
+ const proceed = () => fn.apply(this, args);
35
+ return advice.call(this, proceed, ...args);
36
+ };
37
+ };
38
+ exports.around = around;
39
+ const before = (fn, advice) => {
40
+ return function (...args) {
41
+ advice.apply(this, args);
42
+ return fn.apply(this, args);
43
+ };
44
+ };
45
+ exports.before = before;
46
+ const debounce = (fn, wait = 0) => {
47
+ let timer;
48
+ return function (...args) {
49
+ clearTimeout(timer);
50
+ timer = setTimeout(() => {
51
+ fn.apply(this, args);
52
+ }, wait);
53
+ };
54
+ };
55
+ exports.debounce = debounce;
56
+ const pipe = (...fns) => {
57
+ if (fns.length === 0) {
58
+ return (...args) => args.length <= 1 ? args.at(0) : args;
59
+ }
60
+ return function (...args) {
61
+ return fns.reduce((acc, fn, idx) => idx ? fn.call(this, acc) : fn.apply(this, acc), args);
62
+ };
63
+ };
64
+ exports.pipe = pipe;
65
+ const throttle = (fn, wait = 0) => {
66
+ let timer;
67
+ return function (...args) {
68
+ timer ||= (fn.apply(this, args), setTimeout(() => {
69
+ timer = null;
70
+ }, wait));
71
+ };
72
+ };
73
+ exports.throttle = throttle;
package/package.json CHANGED
@@ -1,29 +1,28 @@
1
1
  {
2
2
  "author": {
3
- "name": "Yehor Sergeenko",
4
3
  "email": "yehor.sergeenko@gmail.com",
4
+ "name": "Yehor Sergeenko",
5
5
  "url": "https://github.com/bricss"
6
6
  },
7
7
  "bugs": {
8
8
  "url": "https://github.com/bricss/thaw/issues"
9
9
  },
10
+ "description": "The narrow belt for AOP 🎀",
10
11
  "devDependencies": {
11
- "@babel/cli": "^7.28.3",
12
- "@babel/core": "^7.28.5",
13
- "@babel/eslint-parser": "^7.28.5",
14
- "@babel/preset-env": "^7.28.5",
12
+ "@babel/cli": "^7.28.6",
13
+ "@babel/core": "^7.29.0",
14
+ "@babel/preset-env": "^7.29.0",
15
15
  "c8": "^10.1.3",
16
- "eslint": "^9.39.2",
17
- "eslint-config-ultra-refined": "^3.8.4",
16
+ "eslint": "^10.0.0",
17
+ "eslint-config-ultra-refined": "^4.0.1",
18
18
  "mocha": "^11.7.5"
19
19
  },
20
- "description": "The narrow belt for AOP 🎀",
21
20
  "engines": {
22
- "node": ">=20.x"
21
+ "node": ">=20.0.0"
23
22
  },
24
23
  "exports": {
25
- "import": "./src/index.mjs",
26
- "require": "./dist/index.js"
24
+ "import": "./src/index.js",
25
+ "require": "./dist/index.cjs"
27
26
  },
28
27
  "files": [
29
28
  "dist",
@@ -42,12 +41,14 @@
42
41
  "url": "git+https://github.com/bricss/thaw.git"
43
42
  },
44
43
  "scripts": {
45
- "build": "rm -rf dist && npx babel src -d dist",
46
- "lint": "eslint",
47
- "prepack": "npm run build",
44
+ "build": "rm -rf dist && npx babel src --out-dir dist --out-file-extension .cjs",
45
+ "lint": "eslint --concurrency=auto",
46
+ "prepack": "npm run build && sh misc.sh && npm run lint",
48
47
  "pretest": "rm -rf coverage",
49
- "test": "mocha --exit --recursive",
48
+ "test": "mocha",
49
+ "test:bail": "mocha --bail",
50
50
  "test:cover": "c8 --include=src --reporter=lcov --reporter=text npm test"
51
51
  },
52
- "version": "5.0.2"
52
+ "type": "module",
53
+ "version": "6.0.0"
53
54
  }
package/src/index.js ADDED
@@ -0,0 +1,73 @@
1
+ export const after = (fn, advice) => {
2
+ return function (...args) {
3
+ const result = fn.apply(this, args);
4
+
5
+ advice.apply(this, args);
6
+
7
+ return result;
8
+ };
9
+ };
10
+
11
+ export const afterReturning = (fn, advice) => {
12
+ return function (...args) {
13
+ return advice.call(this, fn.apply(this, args), ...args);
14
+ };
15
+ };
16
+
17
+ export const afterThrowing = (fn, advice) => {
18
+ return function (...args) {
19
+ try {
20
+ return fn.apply(this, args);
21
+ } catch (err) {
22
+ advice.call(this, err, ...args);
23
+ throw err;
24
+ }
25
+ };
26
+ };
27
+
28
+ export const around = (fn, advice) => {
29
+ return function (...args) {
30
+ const proceed = () => fn.apply(this, args);
31
+
32
+ return advice.call(this, proceed, ...args);
33
+ };
34
+ };
35
+
36
+ export const before = (fn, advice) => {
37
+ return function (...args) {
38
+ advice.apply(this, args);
39
+
40
+ return fn.apply(this, args);
41
+ };
42
+ };
43
+
44
+ export const debounce = (fn, wait = 0) => {
45
+ let timer;
46
+
47
+ return function (...args) {
48
+ clearTimeout(timer);
49
+ timer = setTimeout(() => {
50
+ fn.apply(this, args);
51
+ }, wait);
52
+ };
53
+ };
54
+
55
+ export const pipe = (...fns) => {
56
+ if (fns.length === 0) {
57
+ return (...args) => args.length <= 1 ? args.at(0) : args;
58
+ }
59
+
60
+ return function (...args) {
61
+ return fns.reduce((acc, fn, idx) => idx ? fn.call(this, acc) : fn.apply(this, acc), args);
62
+ };
63
+ };
64
+
65
+ export const throttle = (fn, wait = 0) => {
66
+ let timer;
67
+
68
+ return function (...args) {
69
+ timer ||= (fn.apply(this, args), setTimeout(() => {
70
+ timer = null;
71
+ }, wait));
72
+ };
73
+ };
package/dist/index.js DELETED
@@ -1,54 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.throttle = exports.debounce = exports.compose = exports.before = exports.around = exports.after = void 0;
7
- const around = (fn, cb) => {
8
- return (...args) => {
9
- return cb(fn(cb(...args)));
10
- };
11
- };
12
- exports.around = around;
13
- const after = (fn, cb) => {
14
- return (...args) => {
15
- return cb(fn(...args));
16
- };
17
- };
18
- exports.after = after;
19
- const before = (fn, cb) => {
20
- return (...args) => {
21
- return fn(cb(...args));
22
- };
23
- };
24
- exports.before = before;
25
- const compose = (...fns) => {
26
- return (...args) => {
27
- let result = null;
28
- for (let i = 0; i < fns.length; i++) {
29
- result = fns[i](...(i ? [result] : args));
30
- }
31
- return result;
32
- };
33
- };
34
- exports.compose = compose;
35
- const debounce = (fn, wait = 0) => {
36
- let tick;
37
- return (...args) => {
38
- clearTimeout(tick);
39
- tick = setTimeout(() => {
40
- fn(...args);
41
- }, wait);
42
- };
43
- };
44
- exports.debounce = debounce;
45
- const throttle = (fn, wait = 0) => {
46
- let tick;
47
- return (...args) => {
48
- tick = !tick && setTimeout(() => {
49
- tick = clearTimeout(tick);
50
- fn(...args);
51
- }, wait);
52
- };
53
- };
54
- exports.throttle = throttle;
package/src/index.mjs DELETED
@@ -1,51 +0,0 @@
1
- export const around = (fn, cb) => {
2
- return (...args) => {
3
- return cb(fn(cb(...args)));
4
- };
5
- };
6
-
7
- export const after = (fn, cb) => {
8
- return (...args) => {
9
- return cb(fn(...args));
10
- };
11
- };
12
-
13
- export const before = (fn, cb) => {
14
- return (...args) => {
15
- return fn(cb(...args));
16
- };
17
- };
18
-
19
- export const compose = (...fns) => {
20
- return (...args) => {
21
- let result = null;
22
-
23
- for (let i = 0; i < fns.length; i++) {
24
- result = fns[i](...(i ? [result] : args));
25
- }
26
-
27
- return result;
28
- };
29
- };
30
-
31
- export const debounce = (fn, wait = 0) => {
32
- let tick;
33
-
34
- return (...args) => {
35
- clearTimeout(tick);
36
- tick = setTimeout(() => {
37
- fn(...args);
38
- }, wait);
39
- };
40
- };
41
-
42
- export const throttle = (fn, wait = 0) => {
43
- let tick;
44
-
45
- return (...args) => {
46
- tick = !tick && setTimeout(() => {
47
- tick = clearTimeout(tick);
48
- fn(...args);
49
- }, wait);
50
- };
51
- };