lib:strict

none

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