lib:strict

none

目前為 2024-04-10 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         lib:strict
// @version      0.1
// @description  none
// @run-at       document-start
// @author       You
// @license      GPLv3
// @match        *://*/*
// @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=
// @grant        none
// @namespace https://greasyfork.org/users/1184528
// ==/UserScript==


;(() => {
  const a = loadlib('allfuncs')
  function testformat(
    obj,
    format,
    options = {
      allowextras: false,
      extrastype: undefined,
      allowconversions: false,
      logerror: true,
      allowunset: false,
      throwerror: true,
      insideextras: undefined,
    },
    insideextras
  ) {
    if (options.extrastype) {
      if (a(options.extrastype).gettype("string").val) {
        options.extrastype = options.extrastype.split("|")
      }
    }
    if (options.allowunset) options.extrastype.push("none")
    var isvalid = true
    var unsets = []
    if (insideextras) {
      for (var i = 0; i < Object.keys(obj).length; i++) {
        format.push(insideextras)
      }
    }
    format.find(
      (
        {
          name,
          prop = name,
          type,
          inside,
          insideoptions = {},
          value = [],
          insideextras,
        },
        i
      ) => {
        prop ??= String(i)
        if (insideextras) inside ??= []
        insideoptions = { ...options, ...insideoptions }
        var realvalue = obj[prop]
        if (a(type).gettype("string").val) {
          type = type.split("|")
        }
        if (options.allowunset) type.push("none")
        if (!a(realvalue).gettype(type).val) {
          if (options.allowconversions) {
            if (
              type.find((type) => {
                if (settype.test(realvalue, type)) {
                  obj[prop] = settype(realvalue, type)
                  return true
                }
              })
            ) {
              return false
            }
            if (type.includes("any")) return false
          }

          if (!type.includes(obj[prop]))
            if (options.logerror) {
              if (!(prop in obj))
                error(
                  `the object is missing the property "${prop}". the property "${prop}" should have a type of "${type.join(
                    "|"
                  )}"`
                )
              else
                error(
                  `the value "${prop}" is of type "${a(
                    obj[prop]
                  ).gettype()}" when it should be a type of "${type.join("|")}"`
                )
              error("missing or invalid peram type", { prop, obj, type })
            }
          if (options.throwerror)
            throw new Error("missing or invalid peram type")
          isvalid = false
          return true
        }
        if (!(prop in obj)) unsets.push(prop)
        if (inside) {
          if (!testformat(realvalue, inside, insideoptions, insideextras)) {
            if (options.logerror) error("invalid inside", { obj })
            if (options.throwerror) throw new Error("invalid inside")
            isvalid = false
            return true
          }
        }
      }
    )
    var objnames = [...Object.keys(obj), ...unsets]
    var formatnames = Object.values(format).map((e, i) => e.name ?? String(i))
    var extras = objnames.filter((prop) => !formatnames.includes(prop))
    if (!options.allowextras) {
      if (extras.length) {
        if (options.logerror) {
          error(
            `the object has the following extra properties: ${JSON.stringify(
              extras
            )}`
          )
          error("extras found when allowextras is false", {
            obj,
            objnames,
            formatnames,
          })
        }
        if (options.throwerror)
          throw new Error("extras found when allowextras is false")

        isvalid = false
      }
    }
    if (extras?.[0]?.name == "__extras") {
      var extras = extras[0]
      extras.shift()
      if (!testformat(obj.inside, obj.__extras, options)) {
        isvalid = false
        return true
      }
    }

    if (options.extrastype) {
      extras.find((prop) => {
        if (!a(obj[prop]).gettype(options.extrastype).val) {
          if (options.allowconversions) {
            if (
              options.extrastype.find((type) => {
                if (settype.test(obj[prop], type)) {
                  obj[prop] = settype(obj[prop], type)
                  return true
                }
              })
            ) {
              return false
            }
            if (options.extrastype.includes("any")) return false
          }
          isvalid = false
          return true
        }
      })
    }
    return isvalid
  }
  function strictfunction(func, types, options = {}) {
    if (!!options === options) options = { allowconversions: options }
    return function (...args) {
      if (
        !testformat((args = [...args]), types, {
          ...options,
          throwerror: true,
          logerror: true,
        })
      )
        throw new Error("error when calling function")
      return func.call(this, ...args)
    }
  }

  function setformat(obj, options = {}) {
    if (obj[Symbol.for("setformat")]) obj = { ...obj }
    return new Proxy(obj, {
      get(_obj, prop) {
        if (prop == Symbol.for("setformat")) return true
        if (prop == Symbol.for("options")) return options
        return Reflect.get(_obj, prop)
      },
    })
  }
  var entireobject
  function newtestformat(
    obj,
    format,
    tempoptions = {
      allowextras: false,
      extrasformat: {},
      // allowconversions: false,
    }
  ) {
    entireobject ??= obj
    var extrakeys = []
    var options = { ...tempoptions, ...format[Symbol.for("options")] }
    for (var objkey of Object.keys(obj)) {
      if (!(objkey in format))
        if (options.allowextras) extrakeys.push(objkey)
        else
          throw new Error(
            error("object has extra key", {
              extrakey: objkey,
            })
          )
    }
    for (var [formatkey, formatval] of Object.entries(format)) {
      checkformat(formatkey, formatval)
    }
    function checkformat(formatkey, formatval) {
      let currentcomparevalue = obj[formatkey]
      if (formatval[Symbol.for("condfunc")])
        formatval = formatval(obj, entireobject)
      if (!a(obj).gettype(["object", "array"]).val)
        throw new Error(
          error(`obj is not an object`, {
            objectisinstead: obj,
            tryingtoreadproperty: formatkey,
          })
        )
      if (!(formatkey in obj))
        if (
          (formatval && formatval[Symbol.for("optional")]) ||
          (formatval.type && formatval.type.includes("none")) ||
          (formatval.type && formatval.type.includes("undefined")) ||
          (formatval.value && formatval.value.includes(undefined))
        )
          return
        else {
          throw new Error(
            error(`obj is missing property`, {
              formatkey,
              obj,
              format: { ...format[formatkey] },
            })
          )
        }
      if (!formatval[Symbol.for("setformat")]) {
        newtestformat(currentcomparevalue, formatval, tempoptions)
      } else {
        for (var [typekey, currentcheck] of Object.entries(formatval)) {
          // log({ obj, currentcomparevalue, typekey, currentcheck, formatval })
          switch (typekey) {
            case "type":
              if (
                a(currentcomparevalue).gettype(currentcheck).val ||
                currentcheck == "any" ||
                currentcheck?.includes?.("any")
              )
                break
              else
                throw new Error(
                  error("type missmatch", {
                    property: formatkey,
                    obj,
                    currentvalue: currentcomparevalue,
                    typeofcurrentvalue: a(currentcomparevalue).gettype().val,
                    typeshouldbe: currentcheck,
                  })
                )
            case "value":
              if (currentcheck.includes(currentcomparevalue)) break
              else
                throw new Error(
                  error("value missmatch", {
                    currentcomparevalue,
                    typeofcurrentcomparevalue:
                      a(currentcomparevalue).gettype().val,
                    currentcheck,
                  })
                )
            default:
              throw new Error(
                "invalid key in format",
                error({
                  typekey,
                  formatval,
                })
              )
          }
        }
      }
    }
    for (var key of extrakeys) {
      if (!options.extrasformat)
        throw new Error(
          `options has allowextras options must also have extrasformat`
        )
      // log(options.extrasformat, options)
      checkformat(key, setformat(options.extrasformat), 1)
    }
    entireobject = undefined
    return true
  }
  loadlib("libloader").savelib("strict", {
    strictfunction,
    oldtestformat: testformat,
    setformat,
    optional: function (obj) {
      return new Proxy(obj, {
        get(_obj, prop) {
          if (prop == Symbol.for("optional")) return true
          return Reflect.get(_obj, prop)
        },
      })
    },
    condfunc: function (func) {
      return new Proxy(func, {
        get(_obj, prop) {
          if (prop == Symbol.for("condfunc")) return true
          return Reflect.get(_obj, prop)
        },
      })
    },
    testformat: newtestformat,
  })
})()