├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── .release-it.js
├── CHANGELOG.md
├── LICENSE
├── README.md
├── package.json
├── rollup.config.mjs
├── src
├── adapt
│ ├── index.js
│ └── rs
│ │ ├── index.js
│ │ └── parseTaskTree.js
├── globalVarible.js
├── handle
│ ├── batteryHandle.js
│ ├── connectionHandle.js
│ ├── cookieHandle.js
│ ├── dateAndRandomHandle.js
│ ├── evalHandle.js
│ ├── eventHandle.js
│ ├── funcHandle.js
│ ├── index.js
│ ├── intervalHandle.js
│ ├── ovserver.js
│ └── timeoutHandle.js
├── index.js
├── tools
│ ├── addConstant.js
│ ├── cacher.js
│ ├── compressText.js
│ ├── exit.js
│ ├── getNativeProto.js
│ ├── gvOper.js
│ ├── index.js
│ ├── monitor.js
│ ├── runtime.js
│ ├── setFunc.js
│ ├── setObj.js
│ └── timeout.js
└── utils
│ ├── index.js
│ └── loopRun.js
└── test
├── sdenv.test.js
├── tools.test.js
└── utils.test.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_size = 2
8 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "parserOptions": {
3 | "ecmaVersion": 2020
4 | },
5 | "root": true,
6 | "extends": "airbnb-base",
7 | "plugins": [
8 | "jest"
9 | ],
10 | "env": {
11 | "jest/globals": true
12 | },
13 | "globals": {
14 | "window": true
15 | },
16 | "rules": {
17 | "semi": 0,
18 | "comma-dangle": 0,
19 | "no-param-reassign": 0,
20 | "func-names": 0,
21 | "no-underscore-dangle": 0,
22 | "object-curly-newline": 0,
23 | "no-console": 0,
24 | "no-plusplus": 0,
25 | "no-multi-assign": 0,
26 | "import/prefer-default-export": 0,
27 | "space-before-function-paren": 0,
28 | "new-cap": 0,
29 | "arrow-body-style": 0,
30 | "camelcase": 0,
31 | "consistent-return": 0,
32 | "no-restricted-syntax": 0,
33 | "guard-for-in": 0,
34 | "no-eval": 0,
35 | "no-extend-native": 0
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | output/
4 | npm-debug.log
5 | yarn-error.log
6 | .idea
7 | .vscode
8 | package-lock.json
9 | yarn.lock
10 | .history
11 | *.swp
12 | build/
13 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /*
2 | !src/
3 | !build/
4 |
--------------------------------------------------------------------------------
/.release-it.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | github: {
3 | release: true
4 | },
5 | git: {
6 | commitMessage: "release: v${version}"
7 | },
8 | npm: {
9 | publish: false
10 | },
11 | hooks: {
12 | "after:bump": "echo 更新版本成功"
13 | },
14 | plugins: {
15 | '@release-it/conventional-changelog': {
16 | preset: 'conventionalcommits',
17 | infile: 'CHANGELOG.md',
18 | sameFile: true,
19 | releaseRules: [
20 | { type: 'feat', release: 'minor' },
21 | { type: 'fix', release: 'patch' },
22 | { type: 'docs', release: 'patch' },
23 | { type: 'style', release: 'patch' },
24 | { type: 'refactor', release: 'patch' },
25 | { type: 'perf', release: 'patch' },
26 | { type: 'test', release: 'patch' },
27 | ],
28 | },
29 | },
30 | };
31 |
32 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## [1.3.0](https://github.com/pysunday/sdenv-extend/compare/1.2.0...1.3.0) (2024-04-15)
4 |
5 |
6 | ### Features
7 |
8 | * 1. 去除单例创建实例方式;2. 删除无用代码;3. 其它代码优化 ([f70ac4f](https://github.com/pysunday/sdenv-extend/commit/f70ac4f567276937f3af973b650a0eb6144b8e6c))
9 |
10 |
11 | ### Bug Fixes
12 |
13 | * cjs文件版本去除 ([6f51887](https://github.com/pysunday/sdenv-extend/commit/6f51887a7ebcdea0a7693aaef5a6608a67f33dd0))
14 | * 任务树日志默认关闭 ([f450553](https://github.com/pysunday/sdenv-extend/commit/f450553c1b2126ce5bfb5eb5d62412bc08564390))
15 | * 命名一致 ([87cccf9](https://github.com/pysunday/sdenv-extend/commit/87cccf9c68d62bb0df5ecd1ae76ab920c9087f4c))
16 |
17 | ## [1.2.0](https://github.com/pysunday/sdenv-extend/compare/1.1.0...1.2.0) (2024-03-11)
18 |
19 |
20 | ### Features
21 |
22 | * README更新、handle代码优化、打包逻辑优化 ([c4ad635](https://github.com/pysunday/sdenv-extend/commit/c4ad63546d3bc2410182205e61af3c1d35cbb685))
23 |
24 | ## [1.1.0](https://github.com/pysunday/sdenv-extend/compare/1.0.0...1.1.0) (2024-03-07)
25 |
26 |
27 | ### Features
28 |
29 | * 适配chrome的修改,如定时器、dom监控、循环监控等 ([c34e4c3](https://github.com/pysunday/sdenv-extend/commit/c34e4c3b0ef4637d31bbed65a58c21da3ebd3ccc))
30 |
31 | ## 1.0.0 (2024-02-26)
32 |
33 |
34 | ### Bug Fixes
35 |
36 | * load事件延后与定时器逻辑优化 ([759c442](https://github.com/pysunday/sdenv-extend/commit/759c4422e554ef7134ed9a4e50adddfe751737dc))
37 |
38 | ## 1.0.1 (2024-01-12)
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2024, pysunday
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | 1. Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 |
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | 3. Neither the name of the copyright holder nor the names of its
16 | contributors may be used to endorse or promote products derived from
17 | this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](http://badge.fury.io/js/sdenv-extend)
2 |
3 | 用于sdenv补环境框架与真实浏览器加载网页的公共方法与共有处理.
4 |
5 | ## node端
6 |
7 | 1. 安装包:`npm install sdenv-extend`
8 | 2. 导入:`const SdenvExtend = require('sdenv-extend')`
9 | 3. 初始化(需要传入window对象):`window.sdenv = new SdenvExtend({ }, window)`
10 |
11 | ## 浏览器端
12 |
13 | 打包文件下载:`https://github.com/pysunday/sdenv-extend/releases`,文件名为sdenv-extend-iife的js文件即为浏览器端使用文件。
14 |
15 | 1. head内引入:``
16 | 2. 初始化:`window.sdenv = new SdenvExtend(cfg)`
17 | 3. html页面中使用:
18 | ```javascript
19 | if (!window.sdenv) window.sdenv = new SdenvExtend();
20 | ```
21 | 4. 使用extend handle拓展方法,应该在网页第一处javascript执行前后添加,即执行html中javascript代码前的最后一处node执行处或者就在该javascript代码内,建议是在html中的第一处javascript代码内使用,如:
22 | ```javascript
23 | sdenv
24 | .getHandle('battery')('charging_success')
25 | .getHandle('eval')()
26 | ...
27 | .getHandle('func')();
28 | ```
29 |
30 | ## API
31 |
32 | ### extend handle拓展方法
33 |
34 | 在SdenvExtend实例化对象后通过链式方法getHandle添加,见上方浏览器端第四条
35 |
36 | #### `.getHandle('battery')(string | object)`
37 |
38 | 作用: 定义机器电量及充电状态,[跳转MDN文档](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getBattery)
39 |
40 | params:
41 | * string(预存配置):charging_success(正在充电,电量100%,该项默认)、discharging_success(未充电,电量100%)、charging_ing(正在充电)、discharging_ing(未充电)
42 | * object(自定义配置):如预存配置不满足使用需求则可传入自定义对象,参考浏览器端`navigator.getBattery().then(data => console.log(data))`打印结果
43 |
44 | 默认值:
45 |
46 | ```json
47 | {
48 | onchargingchange: null,
49 | onchargingtimechange: null,
50 | ondischargingtimechange: null,
51 | onlevelchange: null,
52 | charging: true,
53 | chargingTime: 0,
54 | dischargingTime: Infinity,
55 | level: 1,
56 | }
57 | ```
58 |
59 | #### `.getHandle('connection')(object)`
60 |
61 | 作用:定义网络环境,[跳转MDN文档](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/connection)
62 |
63 | params:
64 | * object:参考浏览器端`navigator.connection`返回结果
65 |
66 | 默认值:
67 |
68 | ```json
69 | {
70 | downlink: 6.66,
71 | effectiveType: "4g",
72 | onchange: null,
73 | rtt: 0,
74 | saveData: false,
75 | }
76 | ```
77 |
78 | #### `.getHandle('cookie')(object)`
79 |
80 | 作用:cookie值监控封装,[跳转MDN文档](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie)
81 |
82 | params:
83 | * object: 包含日志打印、回调、数据处理的钩子对象,可用属性(参考附录一):getLog、setLog、log、getCb、setCb、cb、parse
84 |
85 | #### `.getHandle('eval')(object)`
86 |
87 | 作用:eval方法调用监控封装,[跳转MDN文档](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval)
88 |
89 | 该拓展应用后会将代码字符串中的debugger去除,如果我们自己想要断点可以使用sdDebugger代替
90 |
91 | params:
92 | * object: 包含日志打印、回调钩子对象,可用属性(参考附录一):log、cb
93 |
94 | eval执行固定值映射:
95 | * `!new function(){eval("this.a=1")}().a` -> false
96 |
97 | #### `.getHandle('func')(object)`
98 |
99 | 作用:Function方法调用监控封装,[跳转MDN文档](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function)
100 |
101 | 该拓展应用后会将代码字符串中的debugger去除,如果我们自己想要断点可以使用sdDebugger代替
102 |
103 | params:
104 | * object: 包含日志打印、回调钩子对象,可用属性(参考附录一):log、cb
105 |
106 | #### `.getHandle('event')(object)`
107 |
108 | 作用:`addEventListener`方法的调用监控封装,[跳转MDN文档](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
109 |
110 | params:
111 | * object: 包含日志打印、回调、事件过滤的钩子对象,可用属性(参考附录一):addLog、runLog、log、addCb、runCb、cb、filter
112 |
113 | #### `.getHandle('ovserver')(object)`
114 |
115 | 作用:`MutationObserver`方法的调用监控封装,[跳转MDN文档](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
116 |
117 | params:
118 | * object: 包含日志打印、回调、事件过滤的钩子对象,可用属性(参考附录一):newLog、addLog、runLog、log、newCb、addCb、runCb、cb、filter
119 |
120 | #### `.getHandle('timeout')(object)`
121 |
122 | 作用:`setTimeout`方法的调用监控封装,[跳转MDN文档](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout)
123 |
124 | params:
125 | * object: 包含日志打印、回调钩子对象,可用属性(参考附录一):log、cb、filter,除了附录一中记录的这三个属性还有一个特殊的time属性,当time属性不传则使用sdenv-extend内部封装的定时器,当time值为数字则使用自带定时器,且time的优先级大于调用时传入的time值
126 |
127 | #### `.getHandle('interval')(object)`
128 |
129 | 作用:`setInterval`方法的调用监控封装,[跳转MDN文档](https://developer.mozilla.org/en-US/docs/Web/API/setInterval)
130 |
131 | params:
132 | * object: 包含日志打印、回调钩子对象,可用属性(参考附录一):log、cb、filter,除了附录一中记录的这三个属性还有一个特殊的time属性,当time属性不传则使用sdenv-extend内部封装的定时器,当time值为数字则使用自带定时器,且time的优先级大于调用时传入的time值
133 |
134 | #### `.getHandle('dateAndRandom')(object)`
135 |
136 | 作用:用于获取运行时时间值记录及固定时间值与随机数
137 |
138 | params:
139 | * object -> datas(object):指定上下文获取的运行时日期数据
140 | * object -> randomReturn(number):Math.random方法返回的数值
141 |
142 | 其中datas的值为指定上下文运行时的产物,可以在浏览器端运行时在断点的位置执行`sdenv.utils.getDateData(copy)`获取
143 |
144 | ### tools工具方法
145 |
146 | 待补充...
147 |
148 | ## 附录1: extend handle入参对象配置列表
149 |
150 | 属性名 | 类型 | 作用 | 默认值
151 | ------ | ---- | ---- | ------
152 | getLog | boolean | 开启get日志 | -
153 | setLog | boolean | 开启set日志 | -
154 | addLog | boolean | 开启方法调用日志 | -
155 | runLog | boolean | 开启回调运行日志 | -
156 | newLog | boolean | 开启实例化过程日志 | -
157 | log | boolean | 同时开启get和set日志 | -
158 | getCb | function | get的回调 | -
159 | setCb | function | set的回调 | -
160 | addCb | function | 方法调用的回调 | -
161 | runCb | function | 回调运行的回调 | -
162 | newCb | function | 实例化过程的回调 | -
163 | cb | function | 同时设置get和set回调,回调里设置断点更友好 | -
164 | parse | function | set最终值时执行 | `(val) => val`
165 | filter | function | 返回false则抛弃该项代码运行 | `(...params) => true`
166 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sdenv-extend",
3 | "version": "1.3.1",
4 | "description": "sdenv补环境框架与浏览器网页环境的共有函数库",
5 | "main": "build/sdenv-extend-cjs.js",
6 | "module": "src/index.js",
7 | "scripts": {
8 | "test": "jest ./test/",
9 | "test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand ./test/",
10 | "build": "rollup -c && gzip-size build/sdenv-extend-iife.js",
11 | "lint": "./node_modules/.bin/eslint src/* test/*",
12 | "release": "release-it"
13 | },
14 | "pre-commit": [
15 | "lint"
16 | ],
17 | "keywords": [
18 | "sdenv",
19 | "sdenv-extend"
20 | ],
21 | "files": [
22 | "package.json",
23 | "README.md",
24 | "src",
25 | "build",
26 | "CHANGELOG.md"
27 | ],
28 | "publishConfig": {
29 | "registry": "https://registry.npmjs.org/"
30 | },
31 | "repository": {
32 | "type": "git",
33 | "url": "https://github.com/pysunday/sdenv-extend"
34 | },
35 | "homepage": "https://github.com/pysunday/sdenv-extend#readme",
36 | "bugs": "https://github.com/pysunday/sdenv-extend/issues",
37 | "author": "pysunday",
38 | "license": "ISC",
39 | "devDependencies": {
40 | "@optimize-lodash/rollup-plugin": "^4.0.4",
41 | "@release-it/conventional-changelog": "^8.0.1",
42 | "@rollup/plugin-commonjs": "^25.0.7",
43 | "@rollup/plugin-node-resolve": "^15.2.3",
44 | "@rollup/plugin-terser": "^0.4.4",
45 | "babel-eslint": "^10.1.0",
46 | "eslint": "^8.56.0",
47 | "eslint-config-airbnb-base": "^15.0.0",
48 | "eslint-plugin-import": "^2.29.1",
49 | "eslint-plugin-jest": "^27.6.1",
50 | "gzip-size-cli": "^5.1.0",
51 | "jest": "^29.7.0",
52 | "release-it": "^17.0.1",
53 | "lodash-es": "^4.17.21",
54 | "rollup": "^4.9.4"
55 | },
56 | "jest": {
57 | "moduleNameMapper": {
58 | "@/(.*)": "/$1",
59 | "@src/(.*)": "/src/$1"
60 | }
61 | },
62 | "engines": {
63 | "node": ">=18"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import glob from 'glob';
2 | import path from 'path';
3 | import fs from 'fs';
4 | import resolve from '@rollup/plugin-node-resolve';
5 | import commonjs from '@rollup/plugin-commonjs';
6 | import terser from '@rollup/plugin-terser';
7 |
8 | const watcher = (globs) => ({
9 | buildStart () {
10 | for (const item of globs) {
11 | glob.sync(path.resolve(item)).forEach((filename) => { this.addWatchFile(filename) })
12 | }
13 | }
14 | })
15 |
16 | const updateVersion = () => ({
17 | renderStart (outputOptions, inputOptions) {
18 | outputOptions.footer = () => `SdenvExtend.version = 'V${JSON.parse(fs.readFileSync('package.json', 'utf8')).version}'`
19 | }
20 | })
21 |
22 | export default () => {
23 | const fileName = 'sdenv-extend';
24 | return {
25 | input: 'src/index.js',
26 | output: [
27 | {
28 | file: `build/${fileName}-cjs.js`,
29 | strict: false,
30 | format: 'cjs',
31 | },
32 | {
33 | file: `build/${fileName}-iife.js`,
34 | format: 'iife',
35 | name: 'SdenvExtend',
36 | strict: false,
37 | plugins: [updateVersion()]
38 | },
39 | {
40 | file: `build/${fileName}-iife.min.js`,
41 | format: 'iife',
42 | name: 'SdenvExtend',
43 | strict: false,
44 | plugins: [terser(), updateVersion()]
45 | }
46 | ],
47 | plugins: [
48 | resolve({ moduleDirectories: ['node_modules'] }),
49 | commonjs(),
50 | watcher(['package.json']),
51 | ]
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/adapt/index.js:
--------------------------------------------------------------------------------
1 | export * as rs from './rs/index';
2 |
--------------------------------------------------------------------------------
/src/adapt/rs/index.js:
--------------------------------------------------------------------------------
1 | export * from './parseTaskTree';
2 |
--------------------------------------------------------------------------------
/src/adapt/rs/parseTaskTree.js:
--------------------------------------------------------------------------------
1 | // 修改任务树增加任务名称等信息
2 | function parse(val, keyMap = {}, deep = 0, deeps = [0], parent = null) {
3 | // 任务列表、主要子树、次要子树、监控值变化的配置
4 | if (keyMap._count === undefined) keyMap._count = 0;
5 | const { taskKey, oneKey, twoKey, monitor = {} } = keyMap;
6 | const str = val[taskKey];
7 | val.parent = parent;
8 | val.str = str;
9 | val.val = {};
10 | if (!str) {
11 | val.arr = [];
12 | val[taskKey] = [];
13 | } else {
14 | val.arr = str.split('').map((it) => it.charCodeAt())
15 | if (keyMap._count > 0) val[taskKey] = [...val.arr]; // 最外层让瑞数自己处理
16 | }
17 | val.idx = `${deeps.join('>')}-${keyMap._count++}`;
18 | val[oneKey] = val[oneKey].map((it, idx) => {
19 | if (it) {
20 | parse.call(this, it, keyMap, deep + 1, [...deeps, 'one', idx], val);
21 | return this.getTools('monitor')(it, it.idx, { setLog: false, ...monitor });
22 | }
23 | return it;
24 | });
25 | val[twoKey] = val[twoKey].map((it, idx) => {
26 | if (it) {
27 | parse.call(this, it, keyMap, deep + 1, [...deeps, 'two', idx], val);
28 | return this.getTools('monitor')(it, it.idx, { setLog: false, ...monitor });
29 | }
30 | return it;
31 | })
32 | return val;
33 | }
34 |
35 | export const parseTaskTree = function(...params) {
36 | this.cache.runsObj = parse.call(this, ...params); // 任务树
37 | this.cache.runsArr = []; // 任务运行时队列
38 | }
39 |
--------------------------------------------------------------------------------
/src/globalVarible.js:
--------------------------------------------------------------------------------
1 | export const gv = {
2 | adapt: {}, // 适配专用网站方法
3 | tools: {}, // 内部工具
4 | utils: {}, // 外部方法
5 | config: {
6 | proxyOpen: false, // 是否允许开启代理
7 | randomReturn: undefined, // 随机数值固定
8 | randomFixed: false, // 固定日期与随机数,文件:@/config/dateAndRandom.json
9 | // 强制setInterval的时间设置,如设置的足够大让执行函数不运行,其中:undefined表示通过框架拦截并计算运行时间,0表示不拦截且使用原时间
10 | timeInterval: undefined,
11 | timeTimeout: undefined, // 与timeInterval功能类似,区别是控制setTimeout
12 | isNode: typeof window === 'undefined', // node环境标识
13 | },
14 | memory: {
15 | runinfo: { // 程序运行时间节点
16 | start: new Date().getTime(), // 代码开始运行
17 | isDied: false, // 窗口生命是否已经结束
18 | },
19 | timeout: null, // 存放处理setTimeout/setInterval的类
20 | sdEval: eval, // node内置eval
21 | sdWindow: typeof window !== 'undefined' ? window : global,
22 | sdFunction: Function,
23 | sdDate: Date,
24 | sdMath: Math,
25 | SdenvExtend: null, // node环境缓存sdenv-extend插件,用于浏览器环境使用
26 | },
27 | cache: {
28 | dynamicCode: [], // 缓存动态生成代码
29 | runloop: {}, // 缓存循环代码运行时
30 | /*
31 | 缓存循环执行顺序
32 | sdenv.cache.runsArr
33 | .map((it, num) => ({idx: it.idx, num, ...it.val}))
34 | .reduce((ans, it) => (
35 | ans[it.idx] === undefined ? ans[it.idx] = [it] : ans[it.idx].push(it), ans
36 | ), {})
37 | */
38 | runsArr: [],
39 | runsObj: [], // 缓存循环递归树
40 | runtime: {}, // 存放运行时数据的代理
41 | monitor: {}, // 监控变量对象
42 | }, // 存储缓存数据
43 | datas: {
44 | dateAndRandom: {}, // 随机数相关数据
45 | },
46 | }
47 |
--------------------------------------------------------------------------------
/src/handle/batteryHandle.js:
--------------------------------------------------------------------------------
1 | import _round from 'lodash-es/round'
2 |
3 | /*
4 | charging: 是否正在充电, true表示正在充电
5 | chargingTime: 充满需要多久时间, 0表示已经充满,未充电总是为Infinity
6 | dischargingTime: 可以用多久,正在充电时总为Infinity
7 | level: 电量,1表示100%电量
8 | */
9 |
10 | const level = _round(Math.random(), 2); // 电量
11 | const chargingTime = (1 - level) * 100 * 300; // 充满时间
12 | const dischargingTime = level * 100 * 443; // 可用时间
13 |
14 | const charging = {
15 | charging_success: { // 正在充电,电量100%
16 | charging: true,
17 | chargingTime: 0,
18 | dischargingTime: Infinity,
19 | level: 1,
20 | },
21 | discharging_success: { // 未充电,电量100%
22 | charging: false,
23 | chargingTime: Infinity,
24 | dischargingTime: Infinity,
25 | level: 1,
26 | },
27 | charging_ing: { // 正在充电
28 | charging: true,
29 | chargingTime,
30 | dischargingTime: Infinity,
31 | level,
32 | },
33 | discharging_ing: { // 未充电
34 | charging: false,
35 | chargingTime: Infinity,
36 | dischargingTime: dischargingTime,
37 | level,
38 | }
39 | }
40 |
41 | export function batteryHandle(config) {
42 | if (typeof config === 'string') {
43 | config = charging[config] || {};
44 | } else if (typeof config !== 'object') {
45 | config = {};
46 | }
47 | const win = this.memory.sdWindow;
48 | win.navigator.getBattery = this.getTools('setNativeFuncName')(() => {
49 | return new Promise((resolve, reject) => {
50 | resolve({
51 | onchargingchange: null,
52 | onchargingtimechange: null,
53 | ondischargingtimechange: null,
54 | onlevelchange: null,
55 | ...charging.charging_success,
56 | ...config,
57 | })
58 | });
59 | }, 'getBattery');
60 | }
61 |
62 | export default batteryHandle;
63 |
--------------------------------------------------------------------------------
/src/handle/connectionHandle.js:
--------------------------------------------------------------------------------
1 | import _get from 'lodash-es/get'
2 |
3 | /*
4 | downlink:设备下行速速,单位兆
5 | effectiveType:网络类型,如:"slow-2g"、"2g"、"3g"、"4g"
6 | rrt:设备的往返延时,单位毫秒
7 | saveData:是否为节流模式
8 | type:网络连接类型,有些浏览器有有些没有,如:"bluetooth"(蓝牙1)、"cellular"(蜂窝网络2)、"ethernet"(以太网3)、"none"(无网络连接)、"wifi"(wifi连接4)、"wimax5"
9 | */
10 |
11 | export function connectionHandle(config) {
12 | const win = this.memory.sdWindow;
13 | if (typeof config !== 'object') {
14 | config = {};
15 | }
16 | this.getTools('addConstants')(win.navigator.connection, {
17 | downlink: 6.66,
18 | effectiveType: "4g",
19 | onchange: null,
20 | rtt: 0,
21 | saveData: false,
22 | ...config,
23 | });
24 | }
25 |
26 | export default connectionHandle
27 |
--------------------------------------------------------------------------------
/src/handle/cookieHandle.js:
--------------------------------------------------------------------------------
1 | export function cookieHandle(config) {
2 | if (typeof config !== 'object') config = {};
3 | const win = this.memory.sdWindow;
4 | const cookieGet = Object.getOwnPropertyDescriptor(win.Document.prototype, 'cookie').get;
5 | const cookieSet = Object.getOwnPropertyDescriptor(win.Document.prototype, 'cookie').set;
6 | const {
7 | getLog, // 开启get日志
8 | setLog, // 开启set日志
9 | log,
10 | getCb, // get的回调,设置的debugger更友好
11 | setCb, // set的回调,设置的debugger更友好
12 | cb, // 回调,设置的debugger更友好
13 | parse = (val) => val,
14 | } = config;
15 | Object.defineProperty(win.document, 'cookie', {
16 | configurable: false,
17 | enumerable: false,
18 | get() {
19 | const cookie = cookieGet.call(win.document);
20 | if (getLog || log) win.console.log(`【GET COOKIE】长:${cookie.length} 值:${cookie}`);
21 | (getCb || cb)?.(cookie);
22 | return cookie;
23 | },
24 | set(val) {
25 | if (setLog || log) win.console.log(`【SET COOKIE】长:${val.length} 值:${val}`);
26 | (setCb || cb)?.(val);
27 | cookieSet.call(win.document, parse(val));
28 | }
29 | })
30 | }
31 |
32 | export default cookieHandle;
33 |
--------------------------------------------------------------------------------
/src/handle/dateAndRandomHandle.js:
--------------------------------------------------------------------------------
1 | function DateAndRandom(sdenv, { datas }) {
2 | const win = sdenv.memory.sdWindow;
3 | this.sdenv = sdenv;
4 | if (datas && Array.isArray(datas._newdate) && Array.isArray(datas._newdate[0])) {
5 | datas._newdate = datas._newdate.reduce((ans, [val, num]) => ([...ans, ...new Array(num).fill(val)]), []);
6 | }
7 | this.data = datas || {};
8 | if (this.data?.firstMap && Object.values(this.data.firstMap).some((it) => !it)) {
9 | throw new Error('日期首位配置错误请检查');
10 | }
11 | this.runs = [];
12 | Object.assign(this, {
13 | _now: win.Date.now,
14 | _parse: win.Date.parse,
15 | _valueOf: win.Date.prototype.valueOf,
16 | _getTime: win.Date.prototype.getTime,
17 | _toString: win.Date.prototype.toString,
18 | _random: win.Math.random,
19 | });
20 | }
21 | DateAndRandom.prototype.shift = function (name) {
22 | const { firstMap } = this.data;
23 | let val = this.data[name].shift();
24 | if (typeof firstMap[name] === 'number') {
25 | val += firstMap[name];
26 | }
27 | this.runs.push(val);
28 | return val;
29 | }
30 | DateAndRandom.prototype.wrapFun = function (funcName, def) {
31 | const name = `_${funcName}`;
32 | if (!this[name]) return undefined;
33 | if (def === undefined && !Array.isArray(this.data[name])) this.data[name] = [];
34 | const self = this;
35 | return this.sdenv.getTools('setNativeFuncName')(function() {
36 | if (def !== undefined) return def;
37 | if (self.data.firstMap?.[name]) {
38 | if (!self.data[name].length) {
39 | throw new Error(`DateAndRandom的${name}数据不够`);
40 | }
41 | return self.shift(name);
42 | }
43 | const val = self[name].call(this);
44 | self.data[name].push(val);
45 | return val
46 | }, funcName);
47 | }
48 | DateAndRandom.prototype.wrapClass = function (className, cla) {
49 | const win = this.sdenv.memory.sdWindow;
50 | const name = `_${className}`;
51 | if (!Array.isArray(this.data[name])) this.data[name] = [];
52 | const self = this;
53 | return this.sdenv.getTools('setNativeFuncName')(new Proxy(cla, {
54 | construct(target, argumentsList, newTarget) {
55 | if (self.data.firstMap?.[name]) {
56 | if (!self.data[name].length) {
57 | throw new Error(`DateAndRandom的${name}数据不够`);
58 | }
59 | return new cla(self.shift(name));
60 | }
61 | const val = Reflect.construct(target, argumentsList, newTarget);
62 | self.data[name].push(win.Date.prototype.valueOf.call(val));
63 | return val;
64 | },
65 | apply(target, argumentsList, newTarget) {
66 | const val = Reflect.apply(target, argumentsList, newTarget);
67 | return val;
68 | }
69 | }), cla.name || className);
70 | }
71 | DateAndRandom.prototype.getData = function (copy) {
72 | const ret = Object.entries(this.data).reduce((ans, [key, val]) => {
73 | if (key === 'firstMap' || !val.length) return ans;
74 | if (key === '_random') {
75 | ans[key] = val;
76 | ans.firstMap[key] = true;
77 | return ans;
78 | }
79 | const first = val[0];
80 | ans.firstMap[key] = first;
81 | if (key === '_newdate') {
82 | ans[key] = val.map((it) => it - first).reduce((arr, each) => {
83 | if (arr.length === 0 || arr[arr.length - 1][0] !== each) {
84 | arr.push([each, 1]);
85 | } else {
86 | arr[arr.length - 1][1] += 1;
87 | }
88 | return arr;
89 | }, []);
90 | } else {
91 | ans[key] = val.map((it) => it - first);
92 | }
93 | return ans;
94 | }, { firstMap: {} });
95 | if (copy) {
96 | copy(JSON.stringify(ret));
97 | console.log('日期与随机数数据复制成功');
98 | }
99 | return ret;
100 | }
101 |
102 | export function dateAndRandomHandle(config) {
103 | if (typeof config !== 'object') config = {};
104 | const win = this.memory.sdWindow;
105 | const { randomReturn = this.config.randomReturn } = config;
106 | const dateAndRandom = new DateAndRandom(this, config);
107 | this.getTools('addUtil')(dateAndRandom.getData.bind(dateAndRandom), 'getDateData');
108 | win.Date = dateAndRandom.wrapClass('newdate', win.Date);
109 | win.Date.now = dateAndRandom.wrapFun('now');
110 | win.Date.parse = dateAndRandom.wrapFun('parse');
111 | win.Math.random = dateAndRandom.wrapFun('random', randomReturn);
112 | }
113 |
114 | export default dateAndRandomHandle;
115 |
--------------------------------------------------------------------------------
/src/handle/evalHandle.js:
--------------------------------------------------------------------------------
1 | const evalmap = {
2 | '!new function(){eval("this.a=1")}().a': 'false',
3 | }
4 |
5 | export function evalHandle(config) {
6 | if (typeof config !== 'object') config = {};
7 | const win = this.memory.sdWindow;
8 | const { cb, log } = config;
9 | const self = this;
10 | win.eval = this.getTools('setNativeFuncName')(new Proxy(win.eval, {
11 | apply(target, thisArg, params) {
12 | if (log) win.console.log(`【EVAL APPLY】参数:${params.map(it => self.getTools('compressText')(JSON.stringify(it)))}`);
13 | const new_params = params.map((param) => {
14 | if (typeof evalmap[param] === 'string') return evalmap[param];
15 | if (param.includes('debugger')) {
16 | param = param.replace(/debugger/g, '')
17 | }
18 | if (param.includes('sdDebugger')) {
19 | param = param.replace(/sdDebugger/g, 'debugger');
20 | }
21 | return param
22 | });
23 | const dynamicCode = {
24 | type: 'eval',
25 | params,
26 | tar_params: new_params,
27 | }
28 | cb?.(dynamicCode);
29 | self.cache.dynamicCode.push({ ...dynamicCode });
30 | return Reflect.apply(target, thisArg, new_params);
31 | },
32 | }), 'eval');
33 | }
34 |
35 | export default evalHandle;
36 |
--------------------------------------------------------------------------------
/src/handle/eventHandle.js:
--------------------------------------------------------------------------------
1 | export function eventHandle(config) {
2 | const self = this;
3 | const win = this.memory.sdWindow;
4 | const delay = (ms) => new Promise(resolve => win.setTimeout(resolve, ms))
5 | if (typeof config !== 'object') config = {};
6 | const { addLog, runLog, log, addCb, runCb, cb, filter = () => true } = config;
7 | win.addEventListener = this.getTools('setNativeFuncName')(new Proxy(win.addEventListener, {
8 | apply: function (target, thisArg, params) {
9 | if (!filter || !filter(...params)) return;
10 | const [type, callback] = params;
11 | const funcStr = callback.param ? JSON.stringify(callback.param) : self.getTools('compressText')(callback.toString());
12 | if (addLog || log) win.console.log(`【ADD EVENT】事件名:${type}, 方法:${funcStr}`);
13 | (addCb || cb)?.(...params);
14 | return Reflect.apply(target, thisArg, [
15 | type,
16 | async () => {
17 | // load事件需要等下一次事件循环时执行: 延时为0的定时任务先执行
18 | if (type === 'load') await delay(0);
19 | (runCb || cb)?.(...params);
20 | if (runLog || log) win.console.log(`【RUN EVENT】事件名:${type}, 方法:${funcStr}`);
21 | callback();
22 | },
23 | ]);
24 | },
25 | }), 'addEventListener');
26 | }
27 |
28 | export default eventHandle;
29 |
--------------------------------------------------------------------------------
/src/handle/funcHandle.js:
--------------------------------------------------------------------------------
1 | export function funcHandle(config) {
2 | const self = this;
3 | if (typeof config !== 'object') config = {};
4 | const win = this.memory.sdWindow;
5 | const { log, cb } = config;
6 | win.Function = this.getTools('setNativeFuncName')(new Proxy(win.Function, {
7 | apply(target, thisArg, params) {
8 | const new_params = params.map((param) => {
9 | if (param.includes('debugger')) {
10 | param = param.replace(/debugger/g, '')
11 | }
12 | if (param.includes('sdDebugger')) {
13 | param = param.replace(/sdDebugger/g, 'debugger');
14 | }
15 | return param
16 | });
17 | if (log) win.console.log(`【FUNCTION APPLY】参数:${params.map(it => self.getTools('compressText')(JSON.stringify(it)))}`);
18 | const dynamicCode = {
19 | type: 'Function',
20 | params,
21 | tar_params: new_params,
22 | }
23 | cb?.(dynamicCode);
24 | self.cache.dynamicCode.push({ ...dynamicCode });
25 | return Reflect.apply(target, thisArg, new_params);
26 | }
27 | }), 'Function');
28 | win.Function.prototype.constructor = win.Function;
29 | this.getTools('setNativeFuncName')(win.Function.prototype, '');
30 | }
31 |
32 | export default funcHandle;
33 |
--------------------------------------------------------------------------------
/src/handle/index.js:
--------------------------------------------------------------------------------
1 | export * from './evalHandle';
2 | export * from './funcHandle';
3 | export * from './eventHandle';
4 | export * from './cookieHandle';
5 | export * from './batteryHandle';
6 | export * from './connectionHandle';
7 | export * from './intervalHandle';
8 | export * from './timeoutHandle';
9 | export * from './dateAndRandomHandle';
10 | export * from './ovserver';
11 |
--------------------------------------------------------------------------------
/src/handle/intervalHandle.js:
--------------------------------------------------------------------------------
1 | export function intervalHandle(config) {
2 | const self = this;
3 | if (typeof config !== 'object') config = {};
4 | const win = this.memory.sdWindow;
5 | const { log, cb, time, filter = () => true } = config;
6 | win.setInterval = this.getTools('setNativeFuncName')(new Proxy(win.setInterval, {
7 | apply: function (target, thisArg, params) {
8 | if (!filter || !filter(...params)) return;
9 | const [func, timeout] = params;
10 | const funcStr = func.param ? JSON.stringify(func.param) : self.getTools('compressText')(func.toString());
11 | if (log) win.console.log(`【INTERVAL APPLY】增加setInterval事件,时间:${timeout}, 方法:${funcStr}`);
12 | if (time !== undefined) {
13 | if (typeof time !== 'number') throw new Error(`time配置如果存在值则必须是数字`);
14 | return Reflect.apply(target, thisArg, [
15 | () => {
16 | if (log) win.console.log(`【INTERVAL RUN】setInterval执行,时间:${timeout},方法:${funcStr}`);
17 | func()
18 | if (log) win.console.log(`【INTERVAL RUNED】setInterval执行,时间:${timeout},方法:${funcStr}`);
19 | },
20 | time || timeout,
21 | ]);
22 | }
23 | return self.getTools('addInterval')(func, timeout);
24 | },
25 | }), 'setInterval');
26 | }
27 |
28 | export default intervalHandle;
29 |
--------------------------------------------------------------------------------
/src/handle/ovserver.js:
--------------------------------------------------------------------------------
1 | let cache = undefined;
2 |
3 | export function ovserverHandle(config) {
4 | const self = this;
5 | if (typeof config !== 'object') config = {};
6 | const win = this.memory.sdWindow;
7 | const delay = (ms) => new Promise(resolve => win.setTimeout(resolve, ms))
8 | if (!cache) {
9 | cache = win.MutationObserver;
10 | }
11 | const { newLog, addLog, runLog, log, addCb, runCb, newCb, cb, filter = () => true } = config;
12 | win.MutationObserver = this.getTools('setNativeFuncName')(new Proxy(cache, {
13 | construct: function (target, argArray, newTarget) {
14 | const [func] = argArray;
15 | const funcStr = func.param ? JSON.stringify(func.param) : self.getTools('compressText')(func.toString());
16 | if (newLog || log) win.console.log(`【NEW OVSERVER】方法:${funcStr}`);
17 | (newCb || cb)?.(...argArray);
18 | const result = Reflect.construct(target, [async (...params) => {
19 | if (!filter || !filter(...argArray)) return;
20 | if (runLog || log) win.console.log(`【RUN OVSERVER】方法:${funcStr}`);
21 | (runCb || cb)?.(...params[0]);
22 | await delay(0);
23 | func(...params);
24 | }], newTarget);
25 | result.observe = new Proxy(result.observe, {
26 | apply: function (target, thisArg, params) {
27 | if (addLog || log) win.console.log(`【ADD OVSERVER】方法:${funcStr}`);
28 | (addCb || cb)?.(...params);
29 | return Reflect.apply(target, thisArg, params);
30 | }
31 | })
32 | return result;
33 | },
34 | }), 'MutationObserver');
35 | }
36 |
37 | export default ovserverHandle;
38 |
--------------------------------------------------------------------------------
/src/handle/timeoutHandle.js:
--------------------------------------------------------------------------------
1 | export function timeoutHandle(config) {
2 | const self = this;
3 | if (typeof config !== 'object') config = {};
4 | const win = this.memory.sdWindow;
5 | const { log, cb, time, filter = () => true } = config;
6 | win.setTimeout = this.getTools('setNativeFuncName')(new Proxy(win.setTimeout, {
7 | apply: function (target, thisArg, params) {
8 | if (!filter || !filter(...params)) return;
9 | const [func, timeout] = params;
10 | const funcStr = func.param ? JSON.stringify(func.param) : self.getTools('compressText')(func.toString());
11 | if (log) win.console.log(`【TIMEOUT APPLY】增加setTimeout事件,时间:${timeout}, 方法: ${funcStr}`);
12 | if (time !== undefined) {
13 | if (typeof time !== 'number') throw new Error(`time配置如果存在值则必须是数字`);
14 | return Reflect.apply(target, thisArg, [
15 | () => {
16 | if (log) win.console.log(`【TIMEOUT RUN】setTimeout执行,时间:${timeout},方法:${funcStr}`);
17 | func()
18 | if (log) win.console.log(`【TIMEOUT RUNED】setTimeout执行,时间:${timeout},方法:${funcStr}`);
19 | },
20 | time || timeout,
21 | ]);
22 | }
23 | return self.getTools('addTimeout')(func, timeout);
24 | },
25 | }), 'setTimeout');
26 | win.document.addEventListener('readystatechange', function() {
27 | if (log) win.console.log(`【READY STATE】${win.document.readyState}`);
28 | });
29 | }
30 |
31 | export default timeoutHandle;
32 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import _merge from 'lodash-es/merge';
2 | import { gv } from './globalVarible';
3 | import { loopRunInit } from './utils/index';
4 | import * as tools from './tools/index';
5 | import * as adapt from './adapt/index'
6 | import * as handles from './handle/index';
7 |
8 |
9 | export default class {
10 | constructor(config, win = undefined) {
11 | const self = this;
12 | Object.assign(this, _merge(
13 | {},
14 | gv,
15 | config || {},
16 | {
17 | tools,
18 | adapt,
19 | }
20 | ));
21 | this.setWindow(win);
22 | loopRunInit.call(this);
23 | this.bindThis();
24 | }
25 |
26 | bindThis() {
27 | const that = this;
28 | function addBind(obj) {
29 | Object.entries(obj).forEach(([key, fun]) => {
30 | if (typeof fun !== 'function') {
31 | addBind(fun)
32 | } else {
33 | obj[key] = fun.bind(that);
34 | }
35 | })
36 | }
37 | ['tools', 'adapt'].forEach(it => {
38 | addBind(this[it]);
39 | });
40 | }
41 |
42 | setWindow(win) {
43 | if (!win) return;
44 | Object.assign(this.memory, {
45 | sdWindow: win,
46 | sdEval: win.eval,
47 | sdFunction: win.Function,
48 | sdDate: win.Date,
49 | sdMath: win.Math,
50 | });
51 | if (this.config.isNode) {
52 | // 修改setFunc工具中的Function指向到window.Function
53 | // tools._setFuncInit();
54 | Object.setPrototypeOf(win.window, win.Window.prototype);
55 | }
56 | }
57 |
58 | getHandle(name) {
59 | const handleName = `${name}Handle`;
60 | if (!handles[handleName]) return;
61 | return (...params) => {
62 | handles[handleName].call(this, ...params);
63 | return this;
64 | }
65 | }
66 |
67 | getTools(name) {
68 | if (!tools[name]) return;
69 | return (...params) => {
70 | return tools[name].call(this, ...params);
71 | }
72 | }
73 |
74 | getUtils(name) {
75 | if (!utils[name]) return;
76 | return (...params) => {
77 | return utils[name].call(this, ...params);
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/tools/addConstant.js:
--------------------------------------------------------------------------------
1 | export function addConstant(object, property, value) {
2 | Object.defineProperty(object, property, {
3 | configurable: false,
4 | enumerable: true,
5 | writable: false,
6 | value
7 | });
8 | }
9 |
10 | export function addConstants(Constructor, propertyMap) {
11 | for (const property in propertyMap) {
12 | const value = propertyMap[property];
13 | addConstant(Constructor, property, value);
14 | }
15 | }
16 |
17 | export function mixin(target, source, allowKeys = []) {
18 | const keys = Reflect.ownKeys(source);
19 | for (let i = 0; i < keys.length; ++i) {
20 | if (keys[i] in target && !allowKeys.includes(keys[i])) {
21 | continue;
22 | }
23 | Object.defineProperty(target, keys[i], Object.getOwnPropertyDescriptor(source, keys[i]));
24 | }
25 | };
26 |
27 |
--------------------------------------------------------------------------------
/src/tools/cacher.js:
--------------------------------------------------------------------------------
1 | import _get from 'lodash-es/get';
2 |
3 | export function addRuntimeData(name, key, val) {
4 | // 添加运行时数据
5 | if (!this.cache.runtime[name]) this.cache.runtime[name] = {};
6 | const runtimeData = this.cache.runtime[name];
7 | if (!runtimeData[key]) runtimeData[key] = [];
8 | runtimeData[key].push(val);
9 | return val;
10 | }
11 |
12 | export function getRuntimeData(name, key) {
13 | return _get(this, ['cache.runtime', name, key]
14 | .filter((it) => it !== undefined)
15 | .join('.'));
16 | }
17 |
--------------------------------------------------------------------------------
/src/tools/compressText.js:
--------------------------------------------------------------------------------
1 | export function compressText(text, max = 100) {
2 | if (typeof text !== 'string') text = `${text}`;
3 | if (text.length <= max) {
4 | return text;
5 | }
6 | return text.replace(/[\n\s\r]+/g, ' ').slice(0, max) + '......';
7 | }
8 |
--------------------------------------------------------------------------------
/src/tools/exit.js:
--------------------------------------------------------------------------------
1 | export function exit(params) {
2 | const win = this.memory.sdWindow;
3 | this.getTools('setDied')()
4 | if (params.url) win.onbeforeunload?.(params.url);
5 | }
6 |
--------------------------------------------------------------------------------
/src/tools/getNativeProto.js:
--------------------------------------------------------------------------------
1 | import { setNativeFuncName } from './setFunc';
2 | import { setNativeObjName } from './setObj';
3 |
4 | export function getNativeProto(funcname, objname, attrs = {}) {
5 | // 自动生成原型函数并返回基于原始数据与该原型函数的实例化对象
6 | const func = setNativeFuncName(() => {
7 | throw new TypeError('Illegal constructor');
8 | }, funcname);
9 | const obj = setNativeObjName({
10 | __proto__: func,
11 | ...attrs,
12 | }, funcname);
13 | return [func, obj];
14 | }
15 |
--------------------------------------------------------------------------------
/src/tools/gvOper.js:
--------------------------------------------------------------------------------
1 | export function addUtil(func, name) {
2 | if (!func) debugger;
3 | this.utils[name || func.name] = func.bind(this);
4 | return func;
5 | }
6 |
7 | export function getUtil(name) {
8 | return this.utils[name];
9 | }
10 |
--------------------------------------------------------------------------------
/src/tools/index.js:
--------------------------------------------------------------------------------
1 | export * from './addConstant';
2 | export * from './cacher';
3 | export * from './compressText';
4 | export * from './exit';
5 | export * from './getNativeProto';
6 | export * from './gvOper';
7 | export * from './monitor';
8 | export * from './runtime';
9 | export * from './setFunc';
10 | export * from './setObj';
11 | export * from './timeout';
12 |
--------------------------------------------------------------------------------
/src/tools/monitor.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-debugger */
2 |
3 | export function monitor(tar, name, config = {}) {
4 | const win = this.memory.sdWindow;
5 | const tools = this.tools;
6 | const {
7 | getLog, // 开启get日志
8 | setLog, // 开启set日志
9 | getKeys = [], // 触发get的debugger的键集合
10 | setKeys = [], // 触发set的debugger的键集合
11 | keys = [], // 触发debugger的键集合
12 | getCb, // get的回调,设置的debugger更友好
13 | setCb, // set的回调,设置的debugger更友好
14 | cb, // 回调,设置的debugger更友好
15 | parse = (key, val) => val,
16 | } = config;
17 | if (!this.cache.monitor[name]) this.cache.monitor[name] = [];
18 | tar.sdMonitorName = name;
19 | const newTar = new win.Proxy(tar, {
20 | get(target, property, receiver) {
21 | if (getLog) win.console.log(`${name} Getting ${property}`);
22 | if (getKeys.includes(property) || keys.includes(property)) debugger;
23 | (getCb || cb)?.(property, name);
24 | return Reflect.get(target, property, receiver);
25 | },
26 | set(target, property, value, receiver) {
27 | const show = [
28 | win,
29 | win.Math,
30 | win.navigation
31 | ].includes(value) && value.toString ? value.toString() : value
32 | if (setLog && !(Array.isArray(target) && property === 'length')) {
33 | win.console.log(`${name} Setting ${property} to ${tools.compressText(show)}`);
34 | }
35 | if (setKeys.includes(property) || keys.includes(property)) debugger;
36 | (setCb || cb)?.(property, value, name);
37 | return Reflect.set(target, property, parse(property, value), receiver);
38 | }
39 | });
40 | this.cache.monitor[name].push(newTar);
41 | return newTar;
42 | }
43 |
44 | export function monitorFunction(tar, name, config = {}) {
45 | const win = this.memory.sdWindow;
46 | const {
47 | log, // 开启日志
48 | isDebugger, // 是否在调用时触发debugger
49 | cb, // 回调,设置的debugger更友好
50 | } = config;
51 | if (!this.cache.monitor[name]) this.cache.monitor[name] = [];
52 | const newTar = new win.Proxy(tar, {
53 | apply(target, thisArg, argArray) {
54 | const result = Reflect.apply(target, thisArg, argArray);
55 | if (log) win.console.log(`${name} Apply ${argArray}`);
56 | if (isDebugger) debugger;
57 | cb?.(argArray, result, name);
58 | return result;
59 | }
60 | });
61 | this.cache.monitor[name].push(newTar);
62 | return newTar;
63 | }
64 |
65 | export function getMonitor(name) {
66 | if (name) return this.cache.monitor[name];
67 | return this.cache.monitor
68 | }
69 |
--------------------------------------------------------------------------------
/src/tools/runtime.js:
--------------------------------------------------------------------------------
1 | export function isAlive() {
2 | // 窗口是否还活跃
3 | return !this.memory.runinfo.isDied;
4 | }
5 |
6 | export function isDied() {
7 | // 窗口是否还活跃
8 | return this.memory.runinfo.isDied;
9 | }
10 |
11 | export function setDied() {
12 | // 设置窗口为死亡状态
13 | this.memory.runinfo.isDied = true;
14 | }
15 |
--------------------------------------------------------------------------------
/src/tools/setFunc.js:
--------------------------------------------------------------------------------
1 | let originToString = Function.toString;
2 | const canToStrigArr = [];
3 | const toString = function() {
4 | if (canToStrigArr.includes(this.name)) {
5 | return `function ${this.name || ''}() { [native code] }`;
6 | }
7 | return originToString.call(this);
8 | }
9 |
10 | export const setFuncNative = function(func, name) {
11 | // 修改函数的toString方法返回native code标识
12 | if (!func) return undefined;
13 | func.name = name || func.name || '';
14 | canToStrigArr.push(func.name);
15 | Object.defineProperty(func, 'toString', {
16 | enumerable: false,
17 | configurable: true,
18 | writable: true,
19 | value: toString,
20 | });
21 | return func;
22 | };
23 |
24 | export const setFuncName = function(func, name) {
25 | // 修改方法的name属性值
26 | Object.defineProperty(func, 'name', {
27 | configurable: true,
28 | enumerable: false,
29 | writable: false,
30 | value: name
31 | });
32 | return func;
33 | }
34 |
35 | export const setNativeFuncName = function(func, name) {
36 | setFuncName(func, name);
37 | setFuncNative(func, name);
38 | return func;
39 | }
40 | setNativeFuncName(toString, 'toString');
41 |
42 | export const _setFuncInit = function() {
43 | // 修改Function指向与toString原型链指向
44 | const win = this.memory.sdWindow;
45 | originToString = win.Function.toString;
46 | toString.__proto__ = win.Function.prototype;
47 | }
48 |
--------------------------------------------------------------------------------
/src/tools/setObj.js:
--------------------------------------------------------------------------------
1 | export const setObjNative = function(obj, name) {
2 | // 修改函数的toString方法返回native code标识
3 | Object.defineProperty(obj, 'toString', {
4 | enumerable: false,
5 | configurable: true,
6 | writable: true,
7 | value() {
8 | return `[object ${name}]`;
9 | }
10 | });
11 | return obj;
12 | };
13 |
14 | export const setObjName = function(obj, name) {
15 | /*
16 | 修改toString的显示,如:
17 | window.navigator.webkitPersistentStorage.toString() === '[object DeprecatedStorageQuota]'
18 | */
19 | Object.defineProperty(obj, Symbol.toStringTag, {
20 | configurable: true,
21 | enumerable: false,
22 | writable: false,
23 | value: name
24 | });
25 | return obj;
26 | };
27 |
28 | export const setNativeObjName = function(obj, name) {
29 | setObjName(obj, name);
30 | setObjNative(obj, name);
31 | return obj;
32 | }
33 |
--------------------------------------------------------------------------------
/src/tools/timeout.js:
--------------------------------------------------------------------------------
1 | import { isDied, isAlive } from './runtime';
2 |
3 | const currentTask = function () {
4 | this.cache = null;
5 | this.add = (item) => {
6 | if (!this.cache) return false;
7 | if (this.cache.task.includes(item)) return
8 | this.cache.task.push(item);
9 | }
10 | this.set = (task) => this.cache = task;
11 | this.get = () => this.cache;
12 | this.getTime = () => {
13 | if (!this.cache) return false;
14 | return this.cache.time;
15 | }
16 | this.getTask = (idx) => {
17 | if (!this.cache) return false;
18 | if (typeof idx === 'number') return this.cache.task[idx]
19 | return this.cache.task;
20 | }
21 | this.clear = () => this.cache = null;
22 | }
23 |
24 | class Timeout {
25 | constructor(sdenv) {
26 | this.sdenv = sdenv;
27 | this.index = [];
28 | this.timeouts = {};
29 | this.lastOp = sdenv.memory.runinfo.start;
30 | this.isLock = false;
31 | this.id = 0;
32 | this.currentTask = new currentTask();
33 | }
34 |
35 | addTimeout(func, time, type = 'timeout', idx = this.index.length) {
36 | const { sdenv } = this;
37 | let timekey = new sdenv.memory.sdDate().getTime() - sdenv.memory.runinfo.start + time;
38 | const obj = {
39 | func, // 方法
40 | type, // 类型: timeout interval
41 | flag: 0, // 执行状态: -1(取消执行)0(未执行)1(执行中)2(已执行)
42 | time, // 延时时间
43 | expect_time: timekey, // 预期执行时间
44 | real_time: null, // 实际执行时间
45 | index: idx, // 编号
46 | id: this.id++,
47 | }
48 | if (this.index[idx]) {
49 | this.index[idx] = obj;
50 | } else {
51 | this.index.push(obj);
52 | }
53 | if (time === 0 && this.currentTask.get()) {
54 | timekey = this.currentTask.getTime();
55 | this.timeouts[timekey].push(obj);
56 | this.currentTask.add(obj);
57 | } else {
58 | if (this.timeouts[timekey] === undefined) this.timeouts[timekey] = [];
59 | this.timeouts[timekey].push(obj);
60 | if (isAlive()) this.exec();
61 | }
62 | // const win = sdenv.memory.sdWindow;
63 | // win.console.log(`程序时间${timekey}处${type}回调添加成功`);
64 | return idx;
65 | }
66 |
67 | addInterval(func, time, idx = undefined) {
68 | const self = this;
69 | const newFunc = function() {
70 | try {
71 | func();
72 | } catch (err) {
73 | console.error(err);
74 | } finally {
75 | if (self.index[idx].flag !== -1) self.addInterval(func, time, idx);
76 | }
77 | }
78 | idx = this.addTimeout(newFunc, time, 'interval', idx);
79 | return idx;
80 | }
81 |
82 | remove(idx) {
83 | if (typeof idx !== 'number' || !this.index[idx]) return false;
84 | if (this.index[idx].flag === 0) {
85 | this.index[idx].flag = -1;
86 | } else if ([-1, 2].includes(this.index[idx].flag)) {
87 | return undefined;
88 | } else if (this.index[idx].flag === 1) {
89 | return false;
90 | }
91 | return undefined
92 | }
93 |
94 | exec() {
95 | if (this.isLock) return;
96 | this.isLock = true;
97 | this.sdenv.memory.sdWindow.setTimeout(() => {
98 | this.isLock = false;
99 | this.run();
100 | }, 0);
101 | }
102 |
103 | run() {
104 | const { sdenv } = this;
105 | const win = sdenv.memory.sdWindow;
106 | const times = Object.keys(this.timeouts).filter((key) => {
107 | if (Number(key) + sdenv.memory.runinfo.start <= this.lastOp) return false;
108 | return true;
109 | });
110 | if (times.length === 0) return false;
111 | this.lastOp = Number(times[0]) + sdenv.memory.runinfo.start;
112 | this.currentTask.set({
113 | task: this.timeouts[times[0]],
114 | time: times[0],
115 | });
116 | for(let i = 0; this.currentTask.getTask(i); i++) {
117 | const cfg = this.currentTask.getTask(i);
118 | if (isDied() || cfg.flag === -1) return;
119 | const funcStr = cfg.func.param ? JSON.stringify(cfg.func.param) : sdenv.tools.compressText(cfg.func.toString());
120 | win.console.log(`【TIMEOUT RUN】执行程序时间${times[0]}处${cfg.type}回调,延时:${cfg.time},编号:${cfg.id},方法:${funcStr}`);
121 | cfg.flag = 1;
122 | cfg.real_time = new sdenv.memory.sdDate().getTime() - sdenv.memory.runinfo.start;
123 | try {
124 | cfg.func();
125 | win.console.log(`【TIMEOUT RUNED】执行程序时间${times[0]}处${cfg.type}回调,延时:${cfg.time},编号:${cfg.id},方法:${funcStr}`);
126 | } catch (e) {
127 | console.error(e);
128 | }
129 | cfg.flag = 2
130 | }
131 | this.currentTask.clear();
132 | if (isDied()) return undefined;
133 | if (times.length > 1) {
134 | this.run();
135 | } else {
136 | this.exec();
137 | }
138 | return undefined;
139 | }
140 | }
141 |
142 | function check() {
143 | if (!this.memory.timeout) {
144 | this.memory.timeout = new Timeout(this);
145 | }
146 | }
147 |
148 | export const addTimeout = function(func, time) {
149 | check.call(this);
150 | return this.memory.timeout.addTimeout(func, Number(time || 0), 'timeout')
151 | }
152 |
153 | export const addInterval = function(func, time) {
154 | check.call(this);
155 | return this.memory.timeout.addInterval(func, time);
156 | }
157 |
158 | export const removeTimeout = function(idx) {
159 | check.call(this);
160 | return this.memory.timeout.remove(idx);
161 | }
162 |
163 | export const removeInterval = function(idx) {
164 | check.call(this);
165 | return this.memory.timeout.remove(idx);
166 | }
167 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | export * from './loopRun';
2 |
--------------------------------------------------------------------------------
/src/utils/loopRun.js:
--------------------------------------------------------------------------------
1 | // 循环体执行监控
2 | import _orderBy from 'lodash-es/orderBy';
3 |
4 | export function loopRunInit() {
5 | const sdenv = this;
6 | const addUtil = sdenv.getTools('addUtil');
7 | const preLoop = {};
8 | const win = sdenv.memory.sdWindow;
9 | const runloop = sdenv.cache.runloop = { current: 1 };
10 |
11 | let log = false;
12 |
13 | addUtil((key, idx, name, runlist = '', lens = 0) => {
14 | /*
15 | * 初始化一个循环
16 | * key: 循环体的case的变量名
17 | * idx: 初始化下标
18 | * name: 循环方法名称
19 | * runlist: 循环任务名或者循环任务队列
20 | * lens: 任务队列长度
21 | * */
22 | const arr = []
23 | const current = runloop.current++;
24 | if (!runloop[key]) {
25 | runloop[key] = [];
26 | }
27 | const loopobj = {
28 | idx, // 启动的下标
29 | name, // 函数名
30 | data: arr, // 实际运行队列
31 | current, // 启动编号
32 | idxs: [], // 实际运行下标队列
33 | list: Array.isArray(runlist) ? runlist.join() : runlist, // 预期运行队列
34 | lens: lens || (Array.isArray(runlist) ? runlist.length : -1), // 预期运行队列长度
35 | pre: { // 上一次循环信息
36 | current: preLoop.cur,
37 | curloop: preLoop.num,
38 | },
39 | param: {}, // 需要记录的关键数据
40 | }
41 | if (preLoop.loop) {
42 | preLoop.loop.param[preLoop.loop.data.length - 1] = {
43 | next: current // 档次编号记录到父循环中
44 | }
45 | }
46 | runloop[key].push(loopobj);
47 | if (log) win.console.log(`【RUN TASK】current:${current}`);
48 | return {
49 | addLoop: (id, it, _key) => {
50 | loopobj.data.push(it);
51 | loopobj.idxs.push(id);
52 | Object.assign(preLoop, {
53 | key: _key,
54 | cur: current,
55 | num: arr.length,
56 | loop: loopobj,
57 | });
58 | return (...args) => {
59 | if (log) {
60 | console.log([`(${current},${arr.length})`, ...args.map(it => sdenv.tools.compressText(it, 200))].join(' ==== '));
61 | }
62 | }
63 | },
64 | curLoop: () => arr.length,
65 | current,
66 | loopobj,
67 | openLog: () => (log = true),
68 | closeLog: () => (log = false),
69 | isLog: () => log,
70 | }
71 | }, 'initLoop');
72 |
73 | // 上一个循环的信息
74 | addUtil(() => ({ ...preLoop }), 'getPreLoop');
75 |
76 | addUtil(() => log, 'getLogLoop');
77 |
78 | addUtil(() => log = false, 'closeLogLoop');
79 |
80 | addUtil(() => log = true, 'openLogLoop');
81 |
82 | addUtil((copy) => {
83 | // 返回循环体运行数据,copy为复制方法,非浏览器环境需要手动传入
84 | const data = JSON.parse(JSON.stringify(runloop));
85 | const ascii = {};
86 | Object.entries(data).forEach(([key, item]) => {
87 | if (!Array.isArray(item)) return;
88 | ascii[key] = {}
89 | item.forEach((each) => {
90 | each.data.forEach((it) => {
91 | if (ascii[key][it] === undefined) ascii[key][it] = 0;
92 | ascii[key][it]++;
93 | })
94 | each.data = each.data.join();
95 | each.idxs = each.idxs.join();
96 | })
97 | ascii[key] = _orderBy(Object.entries(ascii[key]).map(([code, num]) => ({ code, num })), 'num', 'desc')
98 | });
99 | if (copy) {
100 | copy(JSON.stringify(data));
101 | console.log('运行时数据复制成功');
102 | }
103 | return { ascii, ...data };
104 | }, 'getLoopData');
105 | }
106 |
--------------------------------------------------------------------------------
/test/sdenv.test.js:
--------------------------------------------------------------------------------
1 | const sdenvExtend = require('../');
2 |
3 | test('sdenv', () => {
4 | const sdenv = new sdenvExtend();
5 | expect(sdenv === sdenvExtend()).toBe(true);
6 | expect(eval('!new function(){eval("this.a=1")}().a')).toBe(false)
7 | });
8 |
--------------------------------------------------------------------------------
/test/tools.test.js:
--------------------------------------------------------------------------------
1 | const sdenv = require('../sdenv-extend')();
2 |
3 | test('has tools', () => {
4 | expect(Object.keys(sdenv.tools).length > 0).toBe(true);
5 | });
6 |
--------------------------------------------------------------------------------
/test/utils.test.js:
--------------------------------------------------------------------------------
1 | const sdenv = require('../sdenv-extend')({
2 | config: {
3 | randomReturn: 0.123,
4 | randomFixed: true
5 | },
6 | datas: {
7 | dateAndRandom: {
8 | firstMap: { _getTime: 1704778312614, _newdate: 1704778312614 },
9 | _getTime: [0, 100],
10 | _newdate: [0, 100]
11 | }
12 | }
13 | });
14 |
15 | test('looprun test', () => {
16 | const { runloop } = sdenv.cache;
17 | const {
18 | addLoop,
19 | curLoop,
20 | current,
21 | loopobj
22 | } = sdenv.utils.initLoop('casekey', 0, 'funcname', [1, 2, 3, 4, 5]);
23 | expect(curLoop()).toBe(0);
24 | addLoop(0, 10, 'casekey');
25 | expect(curLoop()).toBe(1);
26 | addLoop(1, 8, 'casekey');
27 | expect(curLoop()).toBe(2);
28 | expect(runloop.current).toBe(2);
29 | expect(current).toBe(1);
30 | expect(loopobj.data).toEqual([10, 8]);
31 | expect(loopobj.idxs).toEqual([0, 1]);
32 | });
33 |
34 | test('dateAndRandom test', () => {
35 | expect(Math.random()).toBe(0.123);
36 | expect(new Date().getTime()).toBe(1704778312614);
37 | expect(new Date().getTime()).toBe(1704778312714);
38 | });
39 |
--------------------------------------------------------------------------------