lib:strict

none

目前为 2024-04-10 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name lib:strict
  3. // @version 0.2
  4. // @description none
  5. // @run-at document-start
  6. // @author You
  7. // @license GPLv3
  8. // @match *://*/*
  9. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAMAAABiM0N1AAAAAXNSR0IB2cksfwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAHJQTFRFAAAAEIijAo2yAI60BYyuF4WaFIifAY6zBI2wB4usGIaZEYigIoiZCIyrE4igG4iYD4mjEomhFoedCoqpDIqnDomlBYyvE4efEYmiDYqlA42xBoytD4mkCYqqGYSUFYidC4qoC4upAo6yCoupDYqmCYur4zowOQAAACZ0Uk5TAO////9vr////1+/D/+/L+/Pf/////+f3///////H4////////+5G91rAAACgUlEQVR4nM2Y22KjIBCGidg1264liZqDadK03X3/V2wNKHMC7MpF/xthHD5mgERAqZhWhfYqH6K+Qf2qNNf625hCoFj9/gblMUi5q5jLkXLCKudgyiRm0FMK82cWJp1fLbV5VmvJbCIc0GCYaFqqlDJgADdBjncqAXYobm1xh72aFMflbysteFfdy2Yi1XGOm5HGBzQ1dq7TzEoxjeNTjQZb7VA3e1c7+ImgasAgQ9+xusNVNZIo5xmOMgihIS2PbCQIiHEUdTvhxCcS/kPomfFI2zHy2PkWmA6aNatIJpKFJyekyy02xh5Y3DI9T4aOT6VhIUrsNTFp1pf79Z4SIIVDegl6IJO6cHiL/GimIZDhgTu/BlYWCQzHMl0zBWT/T3KAhtxOuUB9FtBrpsz0RV4xsjHmW+UCaffcSy/5viMGer0/6HdFNMZBq/vjJL38H9Dqx4Fuy0Em12DbZy+9pGtiDijbglwAehyj11n0tRD3WUBm+lwulE/8h4BuA+iWAQQnteg2Xm63WQLTpnMnpjdge0Mgu/GRPsV4xdjQ94Lfi624fabhDkfUqIKNrM64Q837v8yL0prasepCgrtvw1sJpoqanGEX7b5mQboNW8eawXaWXTMfMGxub472hzWzHSn6Sg2G9+6TAyRruE71s+zAzjWaknoyJCQzwxrghH2k5FDT4eqWunuNxyN9QCGcxVod5oADbYnIUkDTGZEf1xDJnSFteQ3KdsT8zYDMQXcHxsevcLH1TrsABzkNPyA/L7b0jg704viMMlpQI96WsHknCt/3YH0kOEo9zcGkwrFK39ck72rmoehmKqo2RKlilzSy/nJKEV45CT38myJp456fezktHjN5aeMAAAAASUVORK5CYII=
  10. // @grant none
  11. // @namespace https://greasyfork.org/users/1184528
  12. // ==/UserScript==
  13.  
  14. ;(() => {
  15. const a = loadlib("allfuncs")
  16. function testformat(
  17. obj,
  18. format,
  19. options = {
  20. allowextras: false,
  21. extrastype: undefined,
  22. allowconversions: false,
  23. logerror: true,
  24. allowunset: false,
  25. throwerror: true,
  26. insideextras: undefined,
  27. },
  28. insideextras
  29. ) {
  30. if (options.extrastype) {
  31. if (a(options.extrastype).gettype("string").val) {
  32. options.extrastype = options.extrastype.split("|")
  33. }
  34. }
  35. if (options.allowunset) options.extrastype.push("none")
  36. var isvalid = true
  37. var unsets = []
  38. if (insideextras) {
  39. for (var i = 0; i < Object.keys(obj).length; i++) {
  40. format.push(insideextras)
  41. }
  42. }
  43. format.find(
  44. (
  45. {
  46. name,
  47. prop = name,
  48. type,
  49. inside,
  50. insideoptions = {},
  51. value = [],
  52. insideextras,
  53. },
  54. i
  55. ) => {
  56. prop ??= String(i)
  57. if (insideextras) inside ??= []
  58. insideoptions = { ...options, ...insideoptions }
  59. var realvalue = obj[prop]
  60. if (a(type).gettype("string").val) {
  61. type = type.split("|")
  62. }
  63. if (options.allowunset) type.push("none")
  64. if (!a(realvalue).gettype(type).val) {
  65. if (options.allowconversions) {
  66. if (
  67. type.find((type) => {
  68. if (settype.test(realvalue, type)) {
  69. obj[prop] = settype(realvalue, type)
  70. return true
  71. }
  72. })
  73. ) {
  74. return false
  75. }
  76. if (type.includes("any")) return false
  77. }
  78.  
  79. if (!type.includes(obj[prop]))
  80. if (options.logerror) {
  81. if (!(prop in obj))
  82. error(
  83. `the object is missing the property "${prop}". the property "${prop}" should have a type of "${type.join(
  84. "|"
  85. )}"`
  86. )
  87. else
  88. error(
  89. `the value "${prop}" is of type "${a(
  90. obj[prop]
  91. ).gettype()}" when it should be a type of "${type.join("|")}"`
  92. )
  93. error("missing or invalid peram type", { prop, obj, type })
  94. }
  95. if (options.throwerror)
  96. throw new Error("missing or invalid peram type")
  97. isvalid = false
  98. return true
  99. }
  100. if (!(prop in obj)) unsets.push(prop)
  101. if (inside) {
  102. if (!testformat(realvalue, inside, insideoptions, insideextras)) {
  103. if (options.logerror) error("invalid inside", { obj })
  104. if (options.throwerror) throw new Error("invalid inside")
  105. isvalid = false
  106. return true
  107. }
  108. }
  109. }
  110. )
  111. var objnames = [...Object.keys(obj), ...unsets]
  112. var formatnames = Object.values(format).map((e, i) => e.name ?? String(i))
  113. var extras = objnames.filter((prop) => !formatnames.includes(prop))
  114. if (!options.allowextras) {
  115. if (extras.length) {
  116. if (options.logerror) {
  117. error(
  118. `the object has the following extra properties: ${JSON.stringify(
  119. extras
  120. )}`
  121. )
  122. error("extras found when allowextras is false", {
  123. obj,
  124. objnames,
  125. formatnames,
  126. })
  127. }
  128. if (options.throwerror)
  129. throw new Error("extras found when allowextras is false")
  130.  
  131. isvalid = false
  132. }
  133. }
  134. if (extras?.[0]?.name == "__extras") {
  135. var extras = extras[0]
  136. extras.shift()
  137. if (!testformat(obj.inside, obj.__extras, options)) {
  138. isvalid = false
  139. return true
  140. }
  141. }
  142.  
  143. if (options.extrastype) {
  144. extras.find((prop) => {
  145. if (!a(obj[prop]).gettype(options.extrastype).val) {
  146. if (options.allowconversions) {
  147. if (
  148. options.extrastype.find((type) => {
  149. if (settype.test(obj[prop], type)) {
  150. obj[prop] = settype(obj[prop], type)
  151. return true
  152. }
  153. })
  154. ) {
  155. return false
  156. }
  157. if (options.extrastype.includes("any")) return false
  158. }
  159. isvalid = false
  160. return true
  161. }
  162. })
  163. }
  164. return isvalid
  165. }
  166. function strictfunction(func, types, options = {}) {
  167. if (!!options === options) options = { allowconversions: options }
  168. return function (...args) {
  169. if (
  170. !testformat((args = [...args]), types, {
  171. ...options,
  172. throwerror: true,
  173. logerror: true,
  174. })
  175. )
  176. throw new Error("error when calling function")
  177. return func.call(this, ...args)
  178. }
  179. }
  180.  
  181. function setformat(obj, options = {}) {
  182. if (obj[Symbol.for("setformat")]) obj = { ...obj }
  183. return new Proxy(obj, {
  184. get(_obj, prop) {
  185. if (prop == Symbol.for("setformat")) return true
  186. if (prop == Symbol.for("options")) return options
  187. return Reflect.get(_obj, prop)
  188. },
  189. })
  190. }
  191. var entireobject
  192. function newtestformat(
  193. obj,
  194. format,
  195. tempoptions = {
  196. allowextras: false,
  197. extrasformat: {},
  198. // allowconversions: false,
  199. }
  200. ) {
  201. entireobject ??= obj
  202. var extrakeys = []
  203. var options = { ...tempoptions, ...format[Symbol.for("options")] }
  204. for (var objkey of Object.keys(obj)) {
  205. if (!(objkey in format))
  206. if (options.allowextras) extrakeys.push(objkey)
  207. else
  208. throw new Error(
  209. error("object has extra key", {
  210. extrakey: objkey,
  211. })
  212. )
  213. }
  214. for (var [formatkey, formatval] of Object.entries(format)) {
  215. checkformat(formatkey, formatval)
  216. }
  217. function checkformat(formatkey, formatval) {
  218. let currentcomparevalue = obj[formatkey]
  219. if (formatval[Symbol.for("condfunc")])
  220. formatval = formatval(obj, entireobject)
  221. if (!a(obj).gettype(["object", "array"]).val)
  222. throw new Error(
  223. error(`obj is not an object`, {
  224. objectisinstead: obj,
  225. tryingtoreadproperty: formatkey,
  226. })
  227. )
  228. if (!(formatkey in obj))
  229. if (
  230. (formatval && formatval[Symbol.for("optional")]) ||
  231. (formatval.type && formatval.type.includes("none")) ||
  232. (formatval.type && formatval.type.includes("undefined")) ||
  233. (formatval.value && formatval.value.includes(undefined))
  234. )
  235. return
  236. else {
  237. throw new Error(
  238. error(`obj is missing property`, {
  239. formatkey,
  240. obj,
  241. format: { ...format[formatkey] },
  242. })
  243. )
  244. }
  245. if (!formatval[Symbol.for("setformat")]) {
  246. newtestformat(currentcomparevalue, formatval, tempoptions)
  247. } else {
  248. for (var [typekey, currentcheck] of Object.entries(formatval)) {
  249. // log({ obj, currentcomparevalue, typekey, currentcheck, formatval })
  250. switch (typekey) {
  251. case "type":
  252. if (
  253. a(currentcomparevalue).gettype(currentcheck).val ||
  254. currentcheck == "any" ||
  255. currentcheck?.includes?.("any")
  256. )
  257. break
  258. else
  259. throw new Error(
  260. error("type missmatch", {
  261. property: formatkey,
  262. obj,
  263. currentvalue: currentcomparevalue,
  264. typeofcurrentvalue: a(currentcomparevalue).gettype().val,
  265. typeshouldbe: currentcheck,
  266. })
  267. )
  268. case "value":
  269. if (currentcheck.includes(currentcomparevalue)) break
  270. else
  271. throw new Error(
  272. error("value missmatch", {
  273. formatkey,
  274. currentcomparevalue,
  275. typeofcurrentcomparevalue:
  276. a(currentcomparevalue).gettype().val,
  277. currentcheck,
  278. })
  279. )
  280. default:
  281. throw new Error(
  282. "invalid key in format",
  283. error({
  284. typekey,
  285. formatval,
  286. })
  287. )
  288. }
  289. }
  290. }
  291. }
  292. for (var key of extrakeys) {
  293. if (!options.extrasformat)
  294. throw new Error(
  295. `options has allowextras options must also have extrasformat`
  296. )
  297. // log(options.extrasformat, options)
  298. checkformat(key, setformat(options.extrasformat), 1)
  299. }
  300. entireobject = undefined
  301. return true
  302. }
  303. loadlib("libloader").savelib("strict", {
  304. strictfunction,
  305. oldtestformat: testformat,
  306. setformat,
  307. optional: function (obj) {
  308. return new Proxy(obj, {
  309. get(_obj, prop) {
  310. if (prop == Symbol.for("optional")) return true
  311. return Reflect.get(_obj, prop)
  312. },
  313. })
  314. },
  315. condfunc: function (func) {
  316. return new Proxy(func, {
  317. get(_obj, prop) {
  318. if (prop == Symbol.for("condfunc")) return true
  319. return Reflect.get(_obj, prop)
  320. },
  321. })
  322. },
  323. testformat: newtestformat,
  324. })
  325. })()