lib:strict

none

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

  1. // ==UserScript==
  2. // @name lib:strict
  3. // @version 0.1
  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. ;(() => {
  16. const a = loadlib('allfuncs')
  17. function testformat(
  18. obj,
  19. format,
  20. options = {
  21. allowextras: false,
  22. extrastype: undefined,
  23. allowconversions: false,
  24. logerror: true,
  25. allowunset: false,
  26. throwerror: true,
  27. insideextras: undefined,
  28. },
  29. insideextras
  30. ) {
  31. if (options.extrastype) {
  32. if (a(options.extrastype).gettype("string").val) {
  33. options.extrastype = options.extrastype.split("|")
  34. }
  35. }
  36. if (options.allowunset) options.extrastype.push("none")
  37. var isvalid = true
  38. var unsets = []
  39. if (insideextras) {
  40. for (var i = 0; i < Object.keys(obj).length; i++) {
  41. format.push(insideextras)
  42. }
  43. }
  44. format.find(
  45. (
  46. {
  47. name,
  48. prop = name,
  49. type,
  50. inside,
  51. insideoptions = {},
  52. value = [],
  53. insideextras,
  54. },
  55. i
  56. ) => {
  57. prop ??= String(i)
  58. if (insideextras) inside ??= []
  59. insideoptions = { ...options, ...insideoptions }
  60. var realvalue = obj[prop]
  61. if (a(type).gettype("string").val) {
  62. type = type.split("|")
  63. }
  64. if (options.allowunset) type.push("none")
  65. if (!a(realvalue).gettype(type).val) {
  66. if (options.allowconversions) {
  67. if (
  68. type.find((type) => {
  69. if (settype.test(realvalue, type)) {
  70. obj[prop] = settype(realvalue, type)
  71. return true
  72. }
  73. })
  74. ) {
  75. return false
  76. }
  77. if (type.includes("any")) return false
  78. }
  79.  
  80. if (!type.includes(obj[prop]))
  81. if (options.logerror) {
  82. if (!(prop in obj))
  83. error(
  84. `the object is missing the property "${prop}". the property "${prop}" should have a type of "${type.join(
  85. "|"
  86. )}"`
  87. )
  88. else
  89. error(
  90. `the value "${prop}" is of type "${a(
  91. obj[prop]
  92. ).gettype()}" when it should be a type of "${type.join("|")}"`
  93. )
  94. error("missing or invalid peram type", { prop, obj, type })
  95. }
  96. if (options.throwerror)
  97. throw new Error("missing or invalid peram type")
  98. isvalid = false
  99. return true
  100. }
  101. if (!(prop in obj)) unsets.push(prop)
  102. if (inside) {
  103. if (!testformat(realvalue, inside, insideoptions, insideextras)) {
  104. if (options.logerror) error("invalid inside", { obj })
  105. if (options.throwerror) throw new Error("invalid inside")
  106. isvalid = false
  107. return true
  108. }
  109. }
  110. }
  111. )
  112. var objnames = [...Object.keys(obj), ...unsets]
  113. var formatnames = Object.values(format).map((e, i) => e.name ?? String(i))
  114. var extras = objnames.filter((prop) => !formatnames.includes(prop))
  115. if (!options.allowextras) {
  116. if (extras.length) {
  117. if (options.logerror) {
  118. error(
  119. `the object has the following extra properties: ${JSON.stringify(
  120. extras
  121. )}`
  122. )
  123. error("extras found when allowextras is false", {
  124. obj,
  125. objnames,
  126. formatnames,
  127. })
  128. }
  129. if (options.throwerror)
  130. throw new Error("extras found when allowextras is false")
  131.  
  132. isvalid = false
  133. }
  134. }
  135. if (extras?.[0]?.name == "__extras") {
  136. var extras = extras[0]
  137. extras.shift()
  138. if (!testformat(obj.inside, obj.__extras, options)) {
  139. isvalid = false
  140. return true
  141. }
  142. }
  143.  
  144. if (options.extrastype) {
  145. extras.find((prop) => {
  146. if (!a(obj[prop]).gettype(options.extrastype).val) {
  147. if (options.allowconversions) {
  148. if (
  149. options.extrastype.find((type) => {
  150. if (settype.test(obj[prop], type)) {
  151. obj[prop] = settype(obj[prop], type)
  152. return true
  153. }
  154. })
  155. ) {
  156. return false
  157. }
  158. if (options.extrastype.includes("any")) return false
  159. }
  160. isvalid = false
  161. return true
  162. }
  163. })
  164. }
  165. return isvalid
  166. }
  167. function strictfunction(func, types, options = {}) {
  168. if (!!options === options) options = { allowconversions: options }
  169. return function (...args) {
  170. if (
  171. !testformat((args = [...args]), types, {
  172. ...options,
  173. throwerror: true,
  174. logerror: true,
  175. })
  176. )
  177. throw new Error("error when calling function")
  178. return func.call(this, ...args)
  179. }
  180. }
  181.  
  182. function setformat(obj, options = {}) {
  183. if (obj[Symbol.for("setformat")]) obj = { ...obj }
  184. return new Proxy(obj, {
  185. get(_obj, prop) {
  186. if (prop == Symbol.for("setformat")) return true
  187. if (prop == Symbol.for("options")) return options
  188. return Reflect.get(_obj, prop)
  189. },
  190. })
  191. }
  192. var entireobject
  193. function newtestformat(
  194. obj,
  195. format,
  196. tempoptions = {
  197. allowextras: false,
  198. extrasformat: {},
  199. // allowconversions: false,
  200. }
  201. ) {
  202. entireobject ??= obj
  203. var extrakeys = []
  204. var options = { ...tempoptions, ...format[Symbol.for("options")] }
  205. for (var objkey of Object.keys(obj)) {
  206. if (!(objkey in format))
  207. if (options.allowextras) extrakeys.push(objkey)
  208. else
  209. throw new Error(
  210. error("object has extra key", {
  211. extrakey: objkey,
  212. })
  213. )
  214. }
  215. for (var [formatkey, formatval] of Object.entries(format)) {
  216. checkformat(formatkey, formatval)
  217. }
  218. function checkformat(formatkey, formatval) {
  219. let currentcomparevalue = obj[formatkey]
  220. if (formatval[Symbol.for("condfunc")])
  221. formatval = formatval(obj, entireobject)
  222. if (!a(obj).gettype(["object", "array"]).val)
  223. throw new Error(
  224. error(`obj is not an object`, {
  225. objectisinstead: obj,
  226. tryingtoreadproperty: formatkey,
  227. })
  228. )
  229. if (!(formatkey in obj))
  230. if (
  231. (formatval && formatval[Symbol.for("optional")]) ||
  232. (formatval.type && formatval.type.includes("none")) ||
  233. (formatval.type && formatval.type.includes("undefined")) ||
  234. (formatval.value && formatval.value.includes(undefined))
  235. )
  236. return
  237. else {
  238. throw new Error(
  239. error(`obj is missing property`, {
  240. formatkey,
  241. obj,
  242. format: { ...format[formatkey] },
  243. })
  244. )
  245. }
  246. if (!formatval[Symbol.for("setformat")]) {
  247. newtestformat(currentcomparevalue, formatval, tempoptions)
  248. } else {
  249. for (var [typekey, currentcheck] of Object.entries(formatval)) {
  250. // log({ obj, currentcomparevalue, typekey, currentcheck, formatval })
  251. switch (typekey) {
  252. case "type":
  253. if (
  254. a(currentcomparevalue).gettype(currentcheck).val ||
  255. currentcheck == "any" ||
  256. currentcheck?.includes?.("any")
  257. )
  258. break
  259. else
  260. throw new Error(
  261. error("type missmatch", {
  262. property: formatkey,
  263. obj,
  264. currentvalue: currentcomparevalue,
  265. typeofcurrentvalue: a(currentcomparevalue).gettype().val,
  266. typeshouldbe: currentcheck,
  267. })
  268. )
  269. case "value":
  270. if (currentcheck.includes(currentcomparevalue)) break
  271. else
  272. throw new Error(
  273. error("value missmatch", {
  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. })()