From b62742903651b2e621fb34407b5f8ca38558a84a Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Thu, 27 Apr 2023 11:59:40 +0800
Subject: [PATCH 01/14] Generate type definitions with tsc

---
 package.json  |  7 +++++--
 tsconfig.json | 10 ++++++++++
 yarn.lock     |  5 +++++
 3 files changed, 20 insertions(+), 2 deletions(-)
 create mode 100644 tsconfig.json

diff --git a/package.json b/package.json
index 2ea7e30..3feaade 100644
--- a/package.json
+++ b/package.json
@@ -3,13 +3,15 @@
   "version": "0.1.4",
   "description": "A Lua VM written in JS ES6 targeting the browser",
   "main": "src/fengari.js",
+  "types": "dist/fengari.d.ts",
   "directories": {
     "lib": "src",
     "test": "test"
   },
   "scripts": {
+    "emit": "tsc",
     "lint": "eslint src/ test/",
-    "prepublishOnly": "git diff-index --quiet --cached HEAD -- && npm run lint && npm run test",
+    "prepublishOnly": "git diff-index --quiet --cached HEAD -- && npm run lint && npm run emit && npm run test",
     "test": "jest"
   },
   "repository": {
@@ -31,7 +33,8 @@
   "homepage": "https://github.com/fengari-lua/fengari#readme",
   "devDependencies": {
     "eslint": "^5.15.1",
-    "jest": "^24.5.0"
+    "jest": "^24.5.0",
+    "typescript": "^5.0.4"
   },
   "dependencies": {
     "readline-sync": "^1.4.9",
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..6465c60
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,10 @@
+{
+  "include": ["src/**/*"],
+  "compilerOptions": {
+    "allowJs": true,
+    "declaration": true,
+    "emitDeclarationOnly": true,
+    "outDir": "dist",
+    "declarationMap": true
+  }
+}
diff --git a/yarn.lock b/yarn.lock
index b4a1d75..0f5de9c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3488,6 +3488,11 @@ type-check@~0.3.2:
   dependencies:
     prelude-ls "~1.1.2"
 
+typescript@^5.0.4:
+  version "5.0.4"
+  resolved "https://registry.npmmirror.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b"
+  integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
+
 uglify-js@^3.1.4:
   version "3.4.9"
   resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"

From 40bfd91d2733b84e127e5d7667fd70d4bf098514 Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Thu, 27 Apr 2023 12:01:03 +0800
Subject: [PATCH 02/14] src/defs.js: add jsdoc with typing info

---
 src/defs.js | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 72 insertions(+), 4 deletions(-)

diff --git a/src/defs.js b/src/defs.js
index daa5f70..5b9470c 100644
--- a/src/defs.js
+++ b/src/defs.js
@@ -4,6 +4,11 @@
  * Fengari specific string conversion functions
  */
 
+/**
+ * Converts a JavaScript string into a Uint8Array (used a Lua string).
+ *
+ * @type {function(string):Uint8Array}
+ */
 let luastring_from;
 if (typeof Uint8Array.from === "function") {
     luastring_from = Uint8Array.from.bind(Uint8Array);
@@ -17,6 +22,11 @@ if (typeof Uint8Array.from === "function") {
     };
 }
 
+/**
+ * Returns the index of the first occurrence of the character in the Lua string.
+ *
+ * @type {function(Uint8Array, number, number?):number}
+ */
 let luastring_indexOf;
 if (typeof (new Uint8Array().indexOf) === "function") {
     luastring_indexOf = function(s, v, i) {
@@ -31,6 +41,11 @@ if (typeof (new Uint8Array().indexOf) === "function") {
     };
 }
 
+/**
+ * Constructs a Lua string from characters.
+ *
+ * @type {function(...number):Uint8Array}
+ */
 let luastring_of;
 if (typeof Uint8Array.of === "function") {
     luastring_of = Uint8Array.of.bind(Uint8Array);
@@ -40,11 +55,22 @@ if (typeof Uint8Array.of === "function") {
     };
 }
 
+/**
+ * Returns `true` if the object can be used as a Lua string.
+ *
+ * @param {any} s the object
+ */
 const is_luastring = function(s) {
     return s instanceof Uint8Array;
 };
 
-/* test two lua strings for equality */
+/**
+ * Tests two Lua strings for equality.
+ *
+ * @param {Uint8Array} a str 1
+ * @param {Uint8Array} b str 2
+ * @returns {boolean}
+ */
 const luastring_eq = function(a, b) {
     if (a !== b) {
         let len = a.length;
@@ -57,6 +83,16 @@ const luastring_eq = function(a, b) {
 };
 
 const unicode_error_message = "cannot convert invalid utf8 to javascript string";
+
+/**
+ * Converts a Lua string (in UTF-8) to a normal JavaScript string.
+ *
+ * @param {Uint8Array} value the Lua string
+ * @param {number?} from the staring index
+ * @param {number?} to the ending index
+ * @param {boolean?} replacement_char whether to replace invalid utf8 chars
+ * @returns {string}
+ */
 const to_jsstring = function(value, from, to, replacement_char) {
     if (!is_luastring(value)) throw new TypeError("to_jsstring expects a Uint8Array");
 
@@ -159,7 +195,12 @@ const uri_allowed = (";,/?:@&=+$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV
     return uri_allowed;
 }, {});
 
-/* utility function to convert a lua string to a js string with uri escaping */
+/**
+ * Utility function to convert a Lua string to a JavaScript string with URI escaping.
+ *
+ * @param {Uint8Array} a the string
+ * @returns {string}
+ */
 const to_uristring = function(a) {
     if (!is_luastring(a)) throw new TypeError("to_uristring expects a Uint8Array");
     let s = "";
@@ -176,6 +217,11 @@ const to_uristring = function(a) {
 
 const to_luastring_cache = {};
 
+/**
+ * @param {string} str
+ * @param {object?} cache
+ * @returns {Uint8Array}
+ */
 const to_luastring = function(str, cache) {
     if (typeof str !== "string") throw new TypeError("to_luastring expects a javascript string");
 
@@ -224,6 +270,15 @@ const to_luastring = function(str, cache) {
     return outU8Array;
 };
 
+/**
+ * Returns a Lua string.
+ *
+ * If `str` is already a Lua string, it returns it as is.
+ * Otherwise, it tries to convert it.
+ *
+ * @param {string|Uint8Array} str
+ * @returns
+ */
 const from_userstring = function(str) {
     if (!is_luastring(str)) {
         if (typeof str === "string") {
@@ -269,7 +324,6 @@ module.exports.LUA_RELEASE         = LUA_RELEASE;
 module.exports.LUA_COPYRIGHT       = LUA_COPYRIGHT;
 module.exports.LUA_AUTHORS         = LUA_AUTHORS;
 
-
 const thread_status = {
     LUA_OK:        0,
     LUA_YIELD:     1,
@@ -291,7 +345,15 @@ const constant_types = {
     LUA_TFUNCTION:      6,
     LUA_TUSERDATA:      7,
     LUA_TTHREAD:        8,
-    LUA_NUMTAGS:        9
+    LUA_NUMTAGS:        9,
+
+    LUA_TSHRSTR:        0,
+    LUA_TLNGSTR:        0,
+    LUA_TNUMFLT:        0,
+    LUA_TNUMINT:        0,
+    LUA_TLCL:           0,
+    LUA_TLCF:           0,
+    LUA_TCCL:           0,
 };
 
 constant_types.LUA_TSHRSTR = constant_types.LUA_TSTRING | (0 << 4);  /* short strings */
@@ -332,6 +394,12 @@ const LUA_MINSTACK = 20;
 const { LUAI_MAXSTACK } = require('./luaconf.js');
 const LUA_REGISTRYINDEX = -LUAI_MAXSTACK - 1000;
 
+/**
+ * Returns a pseudo-index for the i-th upvalue.
+ *
+ * @param {number} i
+ * @returns {number}
+ */
 const lua_upvalueindex = function(i) {
     return LUA_REGISTRYINDEX - i;
 };

From b400812c2308c49da226727afb0afda9d63b4b07 Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Thu, 27 Apr 2023 12:11:50 +0800
Subject: [PATCH 03/14] src/luaconf.js: add typing info

---
 src/luaconf.js | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/src/luaconf.js b/src/luaconf.js
index 5fb91cd..66757da 100644
--- a/src/luaconf.js
+++ b/src/luaconf.js
@@ -126,6 +126,7 @@ if (typeof process === "undefined") {
 ** This macro is not on by default even in compatibility mode,
 ** because this is not really an incompatibility.
 */
+/** @type {boolean} */
 const LUA_COMPAT_FLOATSTRING = conf.LUA_COMPAT_FLOATSTRING || false;
 
 const LUA_MAXINTEGER = 2147483647;
@@ -137,6 +138,7 @@ const LUA_MININTEGER = -2147483648;
 ** its only purpose is to stop Lua from consuming unlimited stack
 ** space (and to reserve some numbers for pseudo-indices).
 */
+/** @type {number} */
 const LUAI_MAXSTACK = conf.LUAI_MAXSTACK || 1000000;
 
 /*
@@ -144,16 +146,29 @@ const LUAI_MAXSTACK = conf.LUAI_MAXSTACK || 1000000;
 @@ of a function in debug information.
 ** CHANGE it if you want a different size.
 */
+/** @type {number} */
 const LUA_IDSIZE = conf.LUA_IDSIZE || (60-1); /* fengari uses 1 less than lua as we don't embed the null byte */
 
+/**
+ * @param {number} n
+ * @returns {string}
+ */
 const lua_integer2str = function(n) {
     return String(n); /* should match behaviour of LUA_INTEGER_FMT */
 };
 
+/**
+ * @param {number} n
+ * @returns {string}
+ */
 const lua_number2str = function(n) {
     return String(Number(n.toPrecision(14))); /* should match behaviour of LUA_NUMBER_FMT */
 };
 
+/**
+ * @param {number} n
+ * @returns {number|false}
+ */
 const lua_numbertointeger = function(n) {
     return n >= LUA_MININTEGER && n < -LUA_MININTEGER ? n : false;
 };
@@ -175,9 +190,15 @@ const lua_getlocaledecpoint = function() {
 /*
 @@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
 */
+/** @type {number} */
 const LUAL_BUFFERSIZE = conf.LUAL_BUFFERSIZE || 8192;
 
-// See: http://croquetweak.blogspot.fr/2014/08/deconstructing-floats-frexp-and-ldexp.html
+/**
+ * See: http://croquetweak.blogspot.fr/2014/08/deconstructing-floats-frexp-and-ldexp.html
+ *
+ * @param {number} value
+ * @returns {[number, number]}
+ */
 const frexp = function(value) {
     if (value === 0) return [value, 0];
     var data = new DataView(new ArrayBuffer(8));
@@ -192,6 +213,13 @@ const frexp = function(value) {
     return [mantissa, exponent];
 };
 
+/**
+ * See: http://croquetweak.blogspot.fr/2014/08/deconstructing-floats-frexp-and-ldexp.html
+ *
+ * @param {number} mantissa
+ * @param {number} exponent
+ * @returns {number}
+ */
 const ldexp = function(mantissa, exponent) {
     var steps = Math.min(3, Math.ceil(Math.abs(exponent) / 1023));
     var result = mantissa;

From d7bc2fecb411f87fc969e54ed03a218c6d32839d Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Thu, 27 Apr 2023 14:12:14 +0800
Subject: [PATCH 04/14] src/lapi.js: add type info

---
 src/defs.js    |   2 +-
 src/lapi.js    | 455 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/lstring.js |  18 ++
 3 files changed, 474 insertions(+), 1 deletion(-)

diff --git a/src/defs.js b/src/defs.js
index 5b9470c..7b41a1c 100644
--- a/src/defs.js
+++ b/src/defs.js
@@ -277,7 +277,7 @@ const to_luastring = function(str, cache) {
  * Otherwise, it tries to convert it.
  *
  * @param {string|Uint8Array} str
- * @returns
+ * @returns {Uint8Array}
  */
 const from_userstring = function(str) {
     if (!is_luastring(str)) {
diff --git a/src/lapi.js b/src/lapi.js
index 7443fb7..ed88612 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -1,5 +1,9 @@
 "use strict";
 
+/**
+ * @typedef {import('./lstate').lua_State} lua_State
+ */
+
 const {
     LUA_MULTRET,
     LUA_OPBNOT,
@@ -73,17 +77,31 @@ const isvalid = function(o) {
     return o !== lobject.luaO_nilobject;
 };
 
+/**
+ * @param {lua_State} L
+ * @returns {number}
+ */
 const lua_version = function(L) {
     if (L === null) return LUA_VERSION_NUM;
     else return L.l_G.version;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {function?} panicf
+ * @returns {function?}
+ */
 const lua_atpanic = function(L, panicf) {
     let old = L.l_G.panic;
     L.l_G.panic = panicf;
     return old;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {function?} errorf
+ * @returns {function?}
+ */
 const lua_atnativeerror = function(L, errorf) {
     let old = L.l_G.atnativeerror;
     L.l_G.atnativeerror = errorf;
@@ -130,6 +148,11 @@ const index2addr_ = function(L, idx) {
     }
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} n
+ * @returns {boolean}
+ */
 const lua_checkstack = function(L, n) {
     let res;
     let ci = L.ci;
@@ -152,6 +175,11 @@ const lua_checkstack = function(L, n) {
     return res;
 };
 
+/**
+ * @param {lua_State} from
+ * @param {lua_State} to
+ * @param {number} n
+ */
 const lua_xmove = function(from, to, n) {
     if (from === to) return;
     api_checknelems(from, n);
@@ -173,21 +201,38 @@ const lua_xmove = function(from, to, n) {
 /*
 ** convert an acceptable stack index into an absolute index
 */
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_absindex = function(L, idx) {
     return (idx > 0 || idx <= LUA_REGISTRYINDEX)
         ? idx
         : (L.top - L.ci.funcOff) + idx;
 };
 
+/**
+ * @param {lua_State} L
+ * @returns {number}
+ */
 const lua_gettop = function(L) {
     return L.top - (L.ci.funcOff + 1);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ */
 const lua_pushvalue = function(L, idx) {
     lobject.pushobj2s(L, index2addr(L, idx));
     api_check(L, L.top <= L.ci.top, "stack overflow");
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ */
 const lua_settop = function(L, idx) {
     let func = L.ci.funcOff;
     let newtop;
@@ -201,6 +246,10 @@ const lua_settop = function(L, idx) {
     ldo.adjust_top(L, newtop);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} n
+ */
 const lua_pop = function(L, n) {
     lua_settop(L, -n - 1);
 };
@@ -218,6 +267,11 @@ const reverse = function(L, from, to) {
 ** Let x = AB, where A is a prefix of length 'n'. Then,
 ** rotate x n === BA. But BA === (A^r . B^r)^r.
 */
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @param {number} n
+ */
 const lua_rotate = function(L, idx, n) {
     let t = L.top - 1;
     let pIdx = index2addr_(L, idx);
@@ -230,20 +284,37 @@ const lua_rotate = function(L, idx, n) {
     reverse(L, pIdx, L.top - 1);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} fromidx
+ * @param {number} toidx
+ */
 const lua_copy = function(L, fromidx, toidx) {
     let from = index2addr(L, fromidx);
     index2addr(L, toidx).setfrom(from);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ */
 const lua_remove = function(L, idx) {
     lua_rotate(L, idx, -1);
     lua_pop(L, 1);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ */
 const lua_insert = function(L, idx) {
     lua_rotate(L, idx, 1);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ */
 const lua_replace = function(L, idx) {
     lua_copy(L, -1, idx);
     lua_pop(L, 1);
@@ -253,23 +324,40 @@ const lua_replace = function(L, idx) {
 ** push functions (JS -> stack)
 */
 
+/**
+ * @param {lua_State} L
+ */
 const lua_pushnil = function(L) {
     L.stack[L.top] = new TValue(LUA_TNIL, null);
     api_incr_top(L);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} n
+ */
 const lua_pushnumber = function(L, n) {
     fengari_argcheck(typeof n === "number");
     L.stack[L.top] = new TValue(LUA_TNUMFLT, n);
     api_incr_top(L);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} n
+ */
 const lua_pushinteger = function(L, n) {
     fengari_argcheckinteger(n);
     L.stack[L.top] = new TValue(LUA_TNUMINT, n);
     api_incr_top(L);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {string|Uint8Array} s
+ * @param {number} len
+ * @returns {any}
+ */
 const lua_pushlstring = function(L, s, len) {
     fengari_argcheckinteger(len);
     let ts;
@@ -286,6 +374,11 @@ const lua_pushlstring = function(L, s, len) {
     return ts.value;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {string|Uint8Array} s
+ * @returns {any}
+ */
 const lua_pushstring = function (L, s) {
     if (s === undefined || s === null) {
         L.stack[L.top] = new TValue(LUA_TNIL, null);
@@ -299,17 +392,34 @@ const lua_pushstring = function (L, s) {
     return s;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {string|Uint8Array} fmt
+ * @param {any[]} argp
+ * @returns {any}
+ */
 const lua_pushvfstring = function (L, fmt, argp) {
     fmt = from_userstring(fmt);
     return lobject.luaO_pushvfstring(L, fmt, argp);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {string|Uint8Array} fmt
+ * @param {any} argp
+ * @returns {any}
+ */
 const lua_pushfstring = function (L, fmt, ...argp) {
     fmt = from_userstring(fmt);
     return lobject.luaO_pushvfstring(L, fmt, argp);
 };
 
 /* Similar to lua_pushstring, but takes a JS string */
+/**
+ * @param {lua_State} L
+ * @param {string} s
+ * @returns {any}
+ */
 const lua_pushliteral = function (L, s) {
     if (s === undefined || s === null) {
         L.stack[L.top] = new TValue(LUA_TNIL, null);
@@ -325,6 +435,11 @@ const lua_pushliteral = function (L, s) {
     return s;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {function} fn
+ * @param {number} n
+ */
 const lua_pushcclosure = function(L, fn, n) {
     fengari_argcheck(typeof fn === "function");
     fengari_argcheckinteger(n);
@@ -347,28 +462,47 @@ const lua_pushcclosure = function(L, fn, n) {
 
 const lua_pushjsclosure = lua_pushcclosure;
 
+/**
+ * @param {lua_State} L
+ * @param {function} fn
+ */
 const lua_pushcfunction = function(L, fn) {
     lua_pushcclosure(L, fn, 0);
 };
 
 const lua_pushjsfunction = lua_pushcfunction;
 
+/**
+ * @param {lua_State} L
+ * @param {number} b
+ */
 const lua_pushboolean = function(L, b) {
     L.stack[L.top] = new TValue(LUA_TBOOLEAN, !!b);
     api_incr_top(L);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {any} p
+ */
 const lua_pushlightuserdata = function(L, p) {
     L.stack[L.top] = new TValue(LUA_TLIGHTUSERDATA, p);
     api_incr_top(L);
 };
 
+/**
+ * @param {lua_State} L
+ * @returns {boolean}
+ */
 const lua_pushthread = function(L) {
     L.stack[L.top] = new TValue(LUA_TTHREAD, L);
     api_incr_top(L);
     return L.l_G.mainthread === L;
 };
 
+/**
+ * @param {lua_State} L
+ */
 const lua_pushglobaltable = function(L) {
     lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
 };
@@ -391,10 +525,18 @@ const auxsetstr = function(L, t, k) {
     delete L.stack[--L.top];
 };
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} name
+ */
 const lua_setglobal = function(L, name) {
     auxsetstr(L, ltable.luaH_getint(L.l_G.l_registry.value, LUA_RIDX_GLOBALS), name);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} objindex
+ */
 const lua_setmetatable = function(L, objindex) {
     api_checknelems(L, 1);
     let mt;
@@ -422,6 +564,10 @@ const lua_setmetatable = function(L, objindex) {
     return true;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ */
 const lua_settable = function(L, idx) {
     api_checknelems(L, 2);
     let t = index2addr(L, idx);
@@ -430,10 +576,20 @@ const lua_settable = function(L, idx) {
     delete L.stack[--L.top];
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @param {Uint8Array} k
+ */
 const lua_setfield = function(L, idx, k) {
     auxsetstr(L, index2addr(L, idx), k);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @param {number} n
+ */
 const lua_seti = function(L, idx, n) {
     fengari_argcheckinteger(n);
     api_checknelems(L, 1);
@@ -446,6 +602,10 @@ const lua_seti = function(L, idx, n) {
     delete L.stack[--L.top];
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ */
 const lua_rawset = function(L, idx) {
     api_checknelems(L, 2);
     let o = index2addr(L, idx);
@@ -458,6 +618,11 @@ const lua_rawset = function(L, idx) {
     delete L.stack[--L.top];
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @param {number} n
+ */
 const lua_rawseti = function(L, idx, n) {
     fengari_argcheckinteger(n);
     api_checknelems(L, 1);
@@ -467,6 +632,11 @@ const lua_rawseti = function(L, idx, n) {
     delete L.stack[--L.top];
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @param {any} p
+ */
 const lua_rawsetp = function(L, idx, p) {
     api_checknelems(L, 1);
     let o = index2addr(L, idx);
@@ -489,6 +659,12 @@ const auxgetstr = function(L, t, k) {
     return L.stack[L.top - 1].ttnov();
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @param {number} n
+ * @returns {number}
+ */
 const lua_rawgeti = function(L, idx, n) {
     let t = index2addr(L, idx);
     fengari_argcheckinteger(n);
@@ -498,6 +674,12 @@ const lua_rawgeti = function(L, idx, n) {
     return L.stack[L.top - 1].ttnov();
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @param {any} p
+ * @returns {number}
+ */
 const lua_rawgetp = function(L, idx, p) {
     let t = index2addr(L, idx);
     api_check(L, t.ttistable(), "table expected");
@@ -507,6 +689,11 @@ const lua_rawgetp = function(L, idx, p) {
     return L.stack[L.top - 1].ttnov();
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_rawget = function(L, idx) {
     let t = index2addr(L, idx);
     api_check(L, t.ttistable(t), "table expected");
@@ -515,6 +702,11 @@ const lua_rawget = function(L, idx) {
 };
 
 // narray and nrec are mostly useless for this implementation
+/**
+ * @param {lua_State} L
+ * @param {number} narray
+ * @param {number} nrec
+ */
 const lua_createtable = function(L, narray, nrec) {
     let t = new lobject.TValue(LUA_TTABLE, ltable.luaH_new(L));
     L.stack[L.top] = t;
@@ -525,6 +717,11 @@ const luaS_newudata = function(L, size) {
     return new lobject.Udata(L, size);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} size
+ * @returns {any}
+ */
 const lua_newuserdata = function(L, size) {
     let u = luaS_newudata(L, size);
     L.stack[L.top] = new lobject.TValue(LUA_TUSERDATA, u);
@@ -557,6 +754,12 @@ const aux_upvalue = function(L, fi, n) {
     }
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} funcindex
+ * @param {number} n
+ * @returns {any}
+ */
 const lua_getupvalue = function(L, funcindex, n) {
     let up = aux_upvalue(L, index2addr(L, funcindex), n);
     if (up) {
@@ -569,6 +772,12 @@ const lua_getupvalue = function(L, funcindex, n) {
     return null;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} funcindex
+ * @param {number} n
+ * @returns {any}
+ */
 const lua_setupvalue = function(L, funcindex, n) {
     let fi = index2addr(L, funcindex);
     api_checknelems(L, 1);
@@ -583,15 +792,28 @@ const lua_setupvalue = function(L, funcindex, n) {
     return null;
 };
 
+/**
+ * @param {lua_State} L
+ */
 const lua_newtable = function(L) {
     lua_createtable(L, 0, 0);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} n
+ * @param {function} f
+ */
 const lua_register = function(L, n, f) {
     lua_pushcfunction(L, f);
     lua_setglobal(L, n);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} objindex
+ * @returns {number}
+ */
 const lua_getmetatable = function(L, objindex) {
     let obj = index2addr(L, objindex);
     let mt;
@@ -615,6 +837,11 @@ const lua_getmetatable = function(L, objindex) {
     return res;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_getuservalue = function(L, idx) {
     let o = index2addr(L, idx);
     api_check(L, o.ttisfulluserdata(), "full userdata expected");
@@ -624,16 +851,33 @@ const lua_getuservalue = function(L, idx) {
     return L.stack[L.top - 1].ttnov();
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_gettable = function(L, idx) {
     let t = index2addr(L, idx);
     lvm.luaV_gettable(L, t, L.stack[L.top - 1], L.top - 1);
     return L.stack[L.top - 1].ttnov();
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @param {Uint8Array} k
+ * @returns {number}
+ */
 const lua_getfield = function(L, idx, k) {
     return auxgetstr(L, index2addr(L, idx), k);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @param {number} n
+ * @returns {number}
+ */
 const lua_geti = function(L, idx, n) {
     let t = index2addr(L, idx);
     fengari_argcheckinteger(n);
@@ -643,6 +887,11 @@ const lua_geti = function(L, idx, n) {
     return L.stack[L.top - 1].ttnov();
 };
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} name
+ * @returns {number}
+ */
 const lua_getglobal = function(L, name) {
     return auxgetstr(L, ltable.luaH_getint(L.l_G.l_registry.value, LUA_RIDX_GLOBALS), name);
 };
@@ -651,11 +900,21 @@ const lua_getglobal = function(L, name) {
 ** access functions (stack -> JS)
 */
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_toboolean = function(L, idx) {
     let o = index2addr(L, idx);
     return !o.l_isfalse();
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {Uint8Array}
+ */
 const lua_tolstring = function(L, idx) {
     let o = index2addr(L, idx);
 
@@ -670,6 +929,11 @@ const lua_tolstring = function(L, idx) {
 
 const lua_tostring =  lua_tolstring;
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {string}
+ */
 const lua_tojsstring = function(L, idx) {
     let o = index2addr(L, idx);
 
@@ -682,11 +946,21 @@ const lua_tojsstring = function(L, idx) {
     return o.jsstring();
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {DataView}
+ */
 const lua_todataview = function(L, idx) {
     let u8 = lua_tolstring(L, idx);
     return new DataView(u8.buffer, u8.byteOffset, u8.byteLength);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_rawlen = function(L, idx) {
     let o = index2addr(L, idx);
     switch (o.ttype()) {
@@ -702,30 +976,60 @@ const lua_rawlen = function(L, idx) {
     }
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {function?}
+ */
 const lua_tocfunction = function(L, idx) {
     let o = index2addr(L, idx);
     if (o.ttislcf() || o.ttisCclosure()) return o.value;
     else return null;  /* not a C function */
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_tointeger = function(L, idx) {
     let n = lua_tointegerx(L, idx);
     return n === false ? 0 : n;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_tointegerx = function(L, idx) {
     return lvm.tointeger(index2addr(L, idx));
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_tonumber = function(L, idx) {
     let n = lua_tonumberx(L, idx);
     return n === false ? 0 : n;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_tonumberx = function(L, idx) {
     return lvm.tonumber(index2addr(L, idx));
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {any}
+ */
 const lua_touserdata = function(L, idx) {
     let o = index2addr(L, idx);
     switch (o.ttnov()) {
@@ -737,11 +1041,21 @@ const lua_touserdata = function(L, idx) {
     }
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {lua_State}
+ */
 const lua_tothread = function(L, idx) {
     let o = index2addr(L, idx);
     return o.ttisthread() ? o.value : null;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {any}
+ */
 const lua_topointer = function(L, idx) {
     let o = index2addr(L, idx);
     switch (o.ttype()) {
@@ -790,6 +1104,13 @@ const lua_toproxy = function(L, idx) {
 };
 
 
+/**
+ * @param {lua_State} L
+ * @param {number} index1
+ * @param {number} index2
+ * @param {number} op
+ * @returns {number}
+ */
 const lua_compare = function(L, index1, index2, op) {
     let o1 = index2addr(L, index1);
     let o2 = index2addr(L, index2);
@@ -808,6 +1129,11 @@ const lua_compare = function(L, index1, index2, op) {
     return i;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} s
+ * @returns {number}
+ */
 const lua_stringtonumber = function(L, s) {
     let tv = new TValue();
     let sz = lobject.luaO_str2num(s, tv);
@@ -822,77 +1148,162 @@ const f_call = function(L, ud) {
     ldo.luaD_callnoyield(L, ud.funcOff, ud.nresults);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_type = function(L, idx) {
     let o = index2addr(L, idx);
     return isvalid(o) ?  o.ttnov() : LUA_TNONE;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} t
+ * @returns {Uint8Array}
+ */
 const lua_typename = function(L, t) {
     api_check(L, LUA_TNONE <= t && t < LUA_NUMTAGS, "invalid tag");
     return ltm.ttypename(t);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_iscfunction = function(L, idx) {
     let o = index2addr(L, idx);
     return o.ttislcf(o) || o.ttisCclosure();
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} n
+ * @returns {number}
+ */
 const lua_isnil = function(L, n) {
     return lua_type(L, n) === LUA_TNIL;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} n
+ * @returns {number}
+ */
 const lua_isboolean = function(L, n) {
     return lua_type(L, n) === LUA_TBOOLEAN;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} n
+ * @returns {number}
+ */
 const lua_isnone = function(L, n) {
     return lua_type(L, n) === LUA_TNONE;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} n
+ * @returns {number}
+ */
 const lua_isnoneornil = function(L, n) {
     return lua_type(L, n) <= 0;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_istable = function(L, idx) {
     return index2addr(L, idx).ttistable();
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_isinteger = function(L, idx) {
     return index2addr(L, idx).ttisinteger();
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_isnumber = function(L, idx) {
     return lvm.tonumber(index2addr(L, idx)) !== false;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_isstring = function(L, idx) {
     let o = index2addr(L, idx);
     return o.ttisstring() || lvm.cvt2str(o);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_isuserdata = function(L, idx) {
     let o = index2addr(L, idx);
     return o.ttisfulluserdata(o) || o.ttislightuserdata();
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_isthread = function(L, idx) {
     return lua_type(L, idx) === LUA_TTHREAD;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_isfunction = function(L, idx) {
     return lua_type(L, idx) === LUA_TFUNCTION;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_islightuserdata = function(L, idx) {
     return lua_type(L, idx) === LUA_TLIGHTUSERDATA;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} index1
+ * @param {number} index2
+ * @returns {number}
+ */
 const lua_rawequal = function(L, index1, index2) {
     let o1 = index2addr(L, index1);
     let o2 = index2addr(L, index2);
     return isvalid(o1) && isvalid(o2) ? lvm.luaV_equalobj(null, o1, o2) : 0;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} op
+ */
 const lua_arith = function(L, op) {
     if (op !== LUA_OPUNM && op !== LUA_OPBNOT)
         api_checknelems(L, 2);  /* all other operations expect two operands */
@@ -937,10 +1348,18 @@ const lua_dump = function(L, writer, data, strip) {
     return 1;
 };
 
+/**
+ * @param {lua_State} L
+ * @returns {number}
+ */
 const lua_status = function(L) {
     return L.status;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ */
 const lua_setuservalue = function(L, idx) {
     api_checknelems(L, 1);
     let o = index2addr(L, idx);
@@ -972,6 +1391,11 @@ const lua_callk = function(L, nargs, nresults, ctx, k) {
         L.ci.top = L.top;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} n
+ * @param {number} r
+ */
 const lua_call = function(L, n, r) {
     lua_callk(L, n, r, 0, null);
 };
@@ -1017,6 +1441,13 @@ const lua_pcallk = function(L, nargs, nresults, errfunc, ctx, k) {
     return status;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} n
+ * @param {number} r
+ * @param {any} f
+ * @returns {number}
+ */
 const lua_pcall = function(L, n, r, f) {
     return lua_pcallk(L, n, r, f, 0, null);
 };
@@ -1025,11 +1456,20 @@ const lua_pcall = function(L, n, r, f) {
 ** miscellaneous functions
 */
 
+/**
+ * @param {lua_State} L
+ * @returns {number}
+ */
 const lua_error = function(L) {
     api_checknelems(L, 1);
     ldebug.luaG_errormsg(L);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const lua_next = function(L, idx) {
     let t = index2addr(L, idx);
     api_check(L, t.ttistable(), "table expected");
@@ -1045,6 +1485,10 @@ const lua_next = function(L, idx) {
     }
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} n
+ */
 const lua_concat = function(L, n) {
     api_checknelems(L, n);
     if (n >= 2)
@@ -1055,6 +1499,10 @@ const lua_concat = function(L, n) {
     }
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ */
 const lua_len = function(L, idx) {
     let t = index2addr(L, idx);
     let tv = new TValue();
@@ -1094,6 +1542,13 @@ const lua_upvalueid = function(L, fidx, n) {
     }
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} fidx1
+ * @param {number} n1
+ * @param {number} fidx2
+ * @param {number} n2
+ */
 const lua_upvaluejoin = function(L, fidx1, n1, fidx2, n2) {
     let ref1 = getupvalref(L, fidx1, n1);
     let ref2 = getupvalref(L, fidx2, n2);
diff --git a/src/lstring.js b/src/lstring.js
index 4769866..eeb883f 100644
--- a/src/lstring.js
+++ b/src/lstring.js
@@ -8,8 +8,16 @@ const {
 } = require('./defs.js');
 const { lua_assert } = require("./llimits.js");
 
+/**
+ * @typedef {import('./lstate').lua_State} lua_State
+ */
+
 class TString {
 
+    /**
+     * @param {lua_State} L
+     * @param {Uint8Array} str
+     */
     constructor(L, str) {
         this.hash = null;
         this.realstring = str;
@@ -57,11 +65,21 @@ const luaS_bless = function(L, str) {
 };
 
 /* makes a copy */
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} str
+ * @returns {TString}
+ */
 const luaS_new = function(L, str) {
     return luaS_bless(L, luastring_from(str));
 };
 
 /* takes a js string */
+/**
+ * @param {lua_State} L
+ * @param {string} str
+ * @returns {TString}
+ */
 const luaS_newliteral = function(L, str) {
     return luaS_bless(L, to_luastring(str));
 };

From 71d42ecb639c437ca96395f87cf88f236cf1d071 Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Thu, 27 Apr 2023 14:17:13 +0800
Subject: [PATCH 05/14] src/lualib.js: add type info

---
 src/lualib.js | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/lualib.js b/src/lualib.js
index fa30268..ca5abb6 100644
--- a/src/lualib.js
+++ b/src/lualib.js
@@ -5,37 +5,48 @@ const {
     LUA_VERSION_MINOR
 } = require("./lua.js");
 
+/**
+ * @typedef {import('./lstate').lua_State} lua_State
+ */
+
 const LUA_VERSUFFIX = "_" + LUA_VERSION_MAJOR + "_" + LUA_VERSION_MINOR;
 module.exports.LUA_VERSUFFIX = LUA_VERSUFFIX;
 
 module.exports.lua_assert = function(c) {};
 
+/** @type {(L: lua_State) => number} */
 module.exports.luaopen_base = require("./lbaselib.js").luaopen_base;
 
 const LUA_COLIBNAME = "coroutine";
 module.exports.LUA_COLIBNAME = LUA_COLIBNAME;
+/** @type {(L: lua_State) => number} */
 module.exports.luaopen_coroutine = require("./lcorolib.js").luaopen_coroutine;
 
 const LUA_TABLIBNAME = "table";
 module.exports.LUA_TABLIBNAME = LUA_TABLIBNAME;
+/** @type {(L: lua_State) => number} */
 module.exports.luaopen_table = require("./ltablib.js").luaopen_table;
 
 if (typeof process !== "undefined") {
     const LUA_IOLIBNAME = "io";
     module.exports.LUA_IOLIBNAME = LUA_IOLIBNAME;
+    /** @type {(L: lua_State) => number} */
     module.exports.luaopen_io = require("./liolib.js").luaopen_io;
 }
 
 const LUA_OSLIBNAME = "os";
 module.exports.LUA_OSLIBNAME = LUA_OSLIBNAME;
+/** @type {(L: lua_State) => number} */
 module.exports.luaopen_os = require("./loslib.js").luaopen_os;
 
 const LUA_STRLIBNAME = "string";
 module.exports.LUA_STRLIBNAME = LUA_STRLIBNAME;
+/** @type {(L: lua_State) => number} */
 module.exports.luaopen_string = require("./lstrlib.js").luaopen_string;
 
 const LUA_UTF8LIBNAME = "utf8";
 module.exports.LUA_UTF8LIBNAME = LUA_UTF8LIBNAME;
+/** @type {(L: lua_State) => number} */
 module.exports.luaopen_utf8 = require("./lutf8lib.js").luaopen_utf8;
 
 const LUA_BITLIBNAME = "bit32";
@@ -44,19 +55,24 @@ module.exports.LUA_BITLIBNAME = LUA_BITLIBNAME;
 
 const LUA_MATHLIBNAME = "math";
 module.exports.LUA_MATHLIBNAME = LUA_MATHLIBNAME;
+/** @type {(L: lua_State) => number} */
 module.exports.luaopen_math = require("./lmathlib.js").luaopen_math;
 
 const LUA_DBLIBNAME = "debug";
 module.exports.LUA_DBLIBNAME = LUA_DBLIBNAME;
+/** @type {(L: lua_State) => number} */
 module.exports.luaopen_debug = require("./ldblib.js").luaopen_debug;
 
 const LUA_LOADLIBNAME = "package";
 module.exports.LUA_LOADLIBNAME = LUA_LOADLIBNAME;
+/** @type {(L: lua_State) => number} */
 module.exports.luaopen_package = require("./loadlib.js").luaopen_package;
 
 const LUA_FENGARILIBNAME = "fengari";
 module.exports.LUA_FENGARILIBNAME = LUA_FENGARILIBNAME;
+/** @type {(L: lua_State) => number} */
 module.exports.luaopen_fengari = require("./fengarilib.js").luaopen_fengari;
 
 const linit = require('./linit.js');
+/** @type {(L: lua_State) => void} */
 module.exports.luaL_openlibs = linit.luaL_openlibs;

From 16a4a59bd8b1c9b786f6c70aa9e7aa93b233f43f Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Thu, 27 Apr 2023 14:26:51 +0800
Subject: [PATCH 06/14] src/lauxlib.js: add type info

---
 src/lauxlib.js | 190 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 190 insertions(+)

diff --git a/src/lauxlib.js b/src/lauxlib.js
index 9d414b2..b62bf27 100644
--- a/src/lauxlib.js
+++ b/src/lauxlib.js
@@ -1,5 +1,9 @@
 "use strict";
 
+/**
+ * @typedef {import('./lstate').lua_State} lua_State
+ */
+
 const {
     LUAL_BUFFERSIZE
 } = require('./luaconf.js');
@@ -198,6 +202,12 @@ const lastlevel = function(L) {
     return le - 1;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {lua_State} L1
+ * @param {Uint8Array} msg
+ * @param {number} level
+ */
 const luaL_traceback = function(L, L1, msg, level) {
     let ar = new lua_Debug();
     let top = lua_gettop(L);
@@ -231,6 +241,12 @@ const panic = function(L) {
     throw new Error(msg);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} arg
+ * @param {Uint8Array} extramsg
+ * @returns {number}
+ */
 const luaL_argerror = function(L, arg, extramsg) {
     let ar = new lua_Debug();
 
@@ -264,6 +280,10 @@ const typeerror = function(L, arg, tname) {
     return luaL_argerror(L, arg, msg);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} level
+ */
 const luaL_where = function(L, level) {
     let ar = new lua_Debug();
     if (lua_getstack(L, level, ar)) {
@@ -284,6 +304,12 @@ const luaL_error = function(L, fmt, ...argp) {
 };
 
 /* Unlike normal lua, we pass in an error object */
+/**
+ * @param {lua_State} L
+ * @param {number} stat
+ * @param {Uint8Array} fname
+ * @returns {number}
+ */
 const luaL_fileresult = function(L, stat, fname, e) {
     if (stat) {
         lua_pushboolean(L, 1);
@@ -308,6 +334,11 @@ const luaL_fileresult = function(L, stat, fname, e) {
 };
 
 /* Unlike normal lua, we pass in an error object */
+/**
+ * @param {lua_State} L
+ * @param {number} e
+ * @returns {number}
+ */
 const luaL_execresult = function(L, e) {
     let what, stat;
     if (e === null) {
@@ -331,10 +362,20 @@ const luaL_execresult = function(L, e) {
     return 3;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} n
+ * @returns {number}
+ */
 const luaL_getmetatable = function(L, n) {
     return lua_getfield(L, LUA_REGISTRYINDEX, n);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} tname
+ * @returns {number}
+ */
 const luaL_newmetatable = function(L, tname) {
     if (luaL_getmetatable(L, tname) !== LUA_TNIL)  /* name already in use? */
         return 0;  /* leave previous value on top, but return 0 */
@@ -348,6 +389,10 @@ const luaL_newmetatable = function(L, tname) {
 
 };
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} tname
+ */
 const luaL_setmetatable = function(L, tname) {
     luaL_getmetatable(L, tname);
     lua_setmetatable(L, -2);
@@ -385,6 +430,10 @@ const tag_error = function(L, arg, tag) {
     typeerror(L, arg, lua_typename(L, tag));
 };
 
+/**
+ * @param {number}
+ * @returns {lua_State}
+ */
 const luaL_newstate = function() {
     let L = lua_newstate();
     if (L) lua_atpanic(L, panic);
@@ -392,24 +441,49 @@ const luaL_newstate = function() {
 };
 
 
+/**
+ * @param {lua_State} L
+ * @param {number} i
+ * @returns {Uint8Array}
+ */
 const luaL_typename = function(L, i) {
     return lua_typename(L, lua_type(L, i));
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} cond
+ * @param {number} arg
+ * @param {Uint8Array} extramsg
+ */
 const luaL_argcheck = function(L, cond, arg, extramsg) {
     if (!cond) luaL_argerror(L, arg, extramsg);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} arg
+ */
 const luaL_checkany = function(L, arg) {
     if (lua_type(L, arg) === LUA_TNONE)
         luaL_argerror(L, arg, to_luastring("value expected", true));
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} arg
+ * @param {number} t
+ */
 const luaL_checktype = function(L, arg, t) {
     if (lua_type(L, arg) !== t)
         tag_error(L, arg, t);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} arg
+ * @returns {Uint8Array}
+ */
 const luaL_checklstring = function(L, arg) {
     let s = lua_tolstring(L, arg);
     if (s === null || s === undefined) tag_error(L, arg, LUA_TSTRING);
@@ -418,6 +492,12 @@ const luaL_checklstring = function(L, arg) {
 
 const luaL_checkstring = luaL_checklstring;
 
+/**
+ * @param {lua_State} L
+ * @param {number} arg
+ * @param {Uint8Array} def
+ * @returns {Uint8Array}
+ */
 const luaL_optlstring = function(L, arg, def) {
     if (lua_type(L, arg) <= 0) {
         return def === null ? null : from_userstring(def);
@@ -433,6 +513,11 @@ const interror = function(L, arg) {
         tag_error(L, arg, LUA_TNUMBER);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} arg
+ * @returns {number}
+ */
 const luaL_checknumber = function(L, arg) {
     let d = lua_tonumberx(L, arg);
     if (d === false)
@@ -440,10 +525,21 @@ const luaL_checknumber = function(L, arg) {
     return d;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} arg
+ * @param {number} def
+ * @returns {number}
+ */
 const luaL_optnumber = function(L, arg, def) {
     return luaL_opt(L, luaL_checknumber, arg, def);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} arg
+ * @returns {number}
+ */
 const luaL_checkinteger = function(L, arg) {
     let d = lua_tointegerx(L, arg);
     if (d === false)
@@ -451,6 +547,12 @@ const luaL_checkinteger = function(L, arg) {
     return d;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} arg
+ * @param {number} def
+ * @returns {number}
+ */
 const luaL_optinteger = function(L, arg, def) {
     return luaL_opt(L, luaL_checkinteger, arg, def);
 };
@@ -532,22 +634,53 @@ const getS = function(L, ud) {
     return s;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} buff
+ * @param {number} size
+ * @param {Uint8Array} name
+ * @param {Uint8Array} mode
+ * @returns {number}
+ */
 const luaL_loadbufferx = function(L, buff, size, name, mode) {
     return lua_load(L, getS, {string: buff}, name, mode);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} s
+ * @param {number} sz
+ * @param {Uint8Array} n
+ * @returns {number}
+ */
 const luaL_loadbuffer = function(L, s, sz, n) {
     return luaL_loadbufferx(L, s, sz, n, null);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} s
+ * @returns {number}
+ */
 const luaL_loadstring = function(L, s) {
     return luaL_loadbuffer(L, s, s.length, s);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} s
+ * @returns {number}
+ */
 const luaL_dostring = function(L, s) {
     return (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0));
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} obj
+ * @param {Uint8Array} event
+ * @returns {number}
+ */
 const luaL_getmetafield = function(L, obj, event) {
     if (!lua_getmetatable(L, obj))  /* no metatable? */
         return LUA_TNIL;
@@ -562,6 +695,12 @@ const luaL_getmetafield = function(L, obj, event) {
     }
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} obj
+ * @param {Uint8Array} event
+ * @returns {number}
+ */
 const luaL_callmeta = function(L, obj, event) {
     obj = lua_absindex(L, obj);
     if (luaL_getmetafield(L, obj, event) === LUA_TNIL)
@@ -573,6 +712,11 @@ const luaL_callmeta = function(L, obj, event) {
     return true;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {number}
+ */
 const luaL_len = function(L, idx) {
     lua_len(L, idx);
     let l = lua_tointegerx(L, -1);
@@ -584,6 +728,11 @@ const luaL_len = function(L, idx) {
 
 const p_I = to_luastring("%I");
 const p_f = to_luastring("%f");
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @returns {Uint8Array}
+ */
 const luaL_tolstring = function(L, idx) {
     if (luaL_callmeta(L, idx, __tostring)) {
         if (!lua_isstring(L, -1))
@@ -659,6 +808,13 @@ const find_subarray = function(arr, subarr, from_index) {
     return -1;
 };
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} s
+ * @param {Uint8Array} p
+ * @param {Uint8Array} r
+ * @returns {Uint8Array}
+ */
 const luaL_gsub = function(L, s, p, r) {
     let wild;
     let b = new luaL_Buffer();
@@ -677,6 +833,12 @@ const luaL_gsub = function(L, s, p, r) {
 ** ensure that stack[idx][fname] has a table and push that table
 ** into the stack
 */
+/**
+ * @param {lua_State} L
+ * @param {number} idx
+ * @param {Uint8Array} fname
+ * @returns {number}
+ */
 const luaL_getsubtable = function(L, idx, fname) {
     if (lua_getfield(L, idx, fname) === LUA_TTABLE)
         return true;  /* table already there */
@@ -713,6 +875,11 @@ const luaL_setfuncs = function(L, l, nup) {
 ** this extra space, Lua will generate the same 'stack overflow' error,
 ** but without 'msg'.)
 */
+/**
+ * @param {lua_State} L
+ * @param {number} space
+ * @param {Uint8Array} msg
+ */
 const luaL_checkstack = function(L, space, msg) {
     if (!lua_checkstack(L, space)) {
         if (msg)
@@ -735,6 +902,11 @@ const luaL_newlib = function(L, l) {
 const LUA_NOREF  = -2;
 const LUA_REFNIL = -1;
 
+/**
+ * @param {lua_State} L
+ * @param {number} t
+ * @returns {number}
+ */
 const luaL_ref = function(L, t) {
     let ref;
     if (lua_isnil(L, -1)) {
@@ -756,6 +928,11 @@ const luaL_ref = function(L, t) {
 };
 
 
+/**
+ * @param {lua_State} L
+ * @param {number} t
+ * @param {number} ref
+ */
 const luaL_unref = function(L, t, ref) {
     if (ref >= 0) {
         t = lua_absindex(L, t);
@@ -978,10 +1155,20 @@ if (typeof process === "undefined") {
     };
 }
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} filename
+ * @returns {number}
+ */
 const luaL_loadfile = function(L, filename) {
     return luaL_loadfilex(L, filename, null);
 };
 
+/**
+ * @param {lua_State} L
+ * @param {Uint8Array} filename
+ * @returns {number}
+ */
 const luaL_dofile = function(L, filename) {
     return (luaL_loadfile(L, filename) || lua_pcall(L, 0, LUA_MULTRET, 0));
 };
@@ -1015,6 +1202,9 @@ const luaL_checkversion_ = function(L, ver, sz) {
 };
 
 /* There is no point in providing this function... */
+/**
+ * @param {lua_State} L
+ */
 const luaL_checkversion = function(L) {
     luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES);
 };

From 64aaa89b1b49bc372dbe5f7a0da72831055efb4d Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Thu, 27 Apr 2023 16:02:32 +0800
Subject: [PATCH 07/14] Fix incorrect signatures with tsc checkJs

---
 package.json   |  1 +
 src/defs.js    | 15 ++++++++-------
 src/lapi.js    | 44 ++++++++++++++++++++++----------------------
 src/lauxlib.js | 34 ++++++++++++++++++++--------------
 src/ldebug.js  |  3 +++
 src/ldo.js     |  3 +++
 src/llimits.js |  8 ++++++--
 7 files changed, 63 insertions(+), 45 deletions(-)

diff --git a/package.json b/package.json
index 3feaade..92e2f74 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
   },
   "scripts": {
     "emit": "tsc",
+    "type-test": "tsc --allowJs --checkJs",
     "lint": "eslint src/ test/",
     "prepublishOnly": "git diff-index --quiet --cached HEAD -- && npm run lint && npm run emit && npm run test",
     "test": "jest"
diff --git a/src/defs.js b/src/defs.js
index 7b41a1c..e9ae44c 100644
--- a/src/defs.js
+++ b/src/defs.js
@@ -7,7 +7,7 @@
 /**
  * Converts a JavaScript string into a Uint8Array (used a Lua string).
  *
- * @type {function(string):Uint8Array}
+ * @type {function(ArrayLike<number>):Uint8Array}
  */
 let luastring_from;
 if (typeof Uint8Array.from === "function") {
@@ -25,7 +25,7 @@ if (typeof Uint8Array.from === "function") {
 /**
  * Returns the index of the first occurrence of the character in the Lua string.
  *
- * @type {function(Uint8Array, number, number?):number}
+ * @type {function(Uint8Array|any[], number, number=):number}
  */
 let luastring_indexOf;
 if (typeof (new Uint8Array().indexOf) === "function") {
@@ -34,7 +34,7 @@ if (typeof (new Uint8Array().indexOf) === "function") {
     };
 } else {
     /* Browsers that don't support Uint8Array.indexOf seem to allow using Array.indexOf on Uint8Array objects e.g. IE11 */
-    let array_indexOf = [].indexOf;
+    let array_indexOf = [0].indexOf;
     if (array_indexOf.call(new Uint8Array(1), 0) !== 0) throw Error("missing .indexOf");
     luastring_indexOf = function(s, v, i) {
         return array_indexOf.call(s, v, i);
@@ -88,9 +88,9 @@ const unicode_error_message = "cannot convert invalid utf8 to javascript string"
  * Converts a Lua string (in UTF-8) to a normal JavaScript string.
  *
  * @param {Uint8Array} value the Lua string
- * @param {number?} from the staring index
- * @param {number?} to the ending index
- * @param {boolean?} replacement_char whether to replace invalid utf8 chars
+ * @param {number} [from] the staring index
+ * @param {number} [to] the ending index
+ * @param {boolean} [replacement_char] whether to replace invalid utf8 chars
  * @returns {string}
  */
 const to_jsstring = function(value, from, to, replacement_char) {
@@ -219,7 +219,7 @@ const to_luastring_cache = {};
 
 /**
  * @param {string} str
- * @param {object?} cache
+ * @param {boolean} [cache]
  * @returns {Uint8Array}
  */
 const to_luastring = function(str, cache) {
@@ -231,6 +231,7 @@ const to_luastring = function(str, cache) {
     }
 
     let len = str.length;
+    /** @type {Array<number> | Uint8Array} */
     let outU8Array = Array(len); /* array is at *least* going to be length of string */
     let outIdx = 0;
     for (let i = 0; i < len; ++i) {
diff --git a/src/lapi.js b/src/lapi.js
index ed88612..835984e 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -78,7 +78,7 @@ const isvalid = function(o) {
 };
 
 /**
- * @param {lua_State} L
+ * @param {lua_State?} L
  * @returns {number}
  */
 const lua_version = function(L) {
@@ -417,7 +417,7 @@ const lua_pushfstring = function (L, fmt, ...argp) {
 /* Similar to lua_pushstring, but takes a JS string */
 /**
  * @param {lua_State} L
- * @param {string} s
+ * @param {string?} [s]
  * @returns {any}
  */
 const lua_pushliteral = function (L, s) {
@@ -474,7 +474,7 @@ const lua_pushjsfunction = lua_pushcfunction;
 
 /**
  * @param {lua_State} L
- * @param {number} b
+ * @param {any} b
  */
 const lua_pushboolean = function(L, b) {
     L.stack[L.top] = new TValue(LUA_TBOOLEAN, !!b);
@@ -704,8 +704,8 @@ const lua_rawget = function(L, idx) {
 // narray and nrec are mostly useless for this implementation
 /**
  * @param {lua_State} L
- * @param {number} narray
- * @param {number} nrec
+ * @param {number} [narray]
+ * @param {number} [nrec]
  */
 const lua_createtable = function(L, narray, nrec) {
     let t = new lobject.TValue(LUA_TTABLE, ltable.luaH_new(L));
@@ -719,7 +719,7 @@ const luaS_newudata = function(L, size) {
 
 /**
  * @param {lua_State} L
- * @param {number} size
+ * @param {number} [size]
  * @returns {any}
  */
 const lua_newuserdata = function(L, size) {
@@ -812,7 +812,7 @@ const lua_register = function(L, n, f) {
 /**
  * @param {lua_State} L
  * @param {number} objindex
- * @returns {number}
+ * @returns {boolean}
  */
 const lua_getmetatable = function(L, objindex) {
     let obj = index2addr(L, objindex);
@@ -903,7 +903,7 @@ const lua_getglobal = function(L, name) {
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @returns {number}
+ * @returns {boolean}
  */
 const lua_toboolean = function(L, idx) {
     let o = index2addr(L, idx);
@@ -913,7 +913,7 @@ const lua_toboolean = function(L, idx) {
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @returns {Uint8Array}
+ * @returns {Uint8Array?}
  */
 const lua_tolstring = function(L, idx) {
     let o = index2addr(L, idx);
@@ -932,7 +932,7 @@ const lua_tostring =  lua_tolstring;
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @returns {string}
+ * @returns {string?}
  */
 const lua_tojsstring = function(L, idx) {
     let o = index2addr(L, idx);
@@ -1000,7 +1000,7 @@ const lua_tointeger = function(L, idx) {
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @returns {number}
+ * @returns {number|false}
  */
 const lua_tointegerx = function(L, idx) {
     return lvm.tointeger(index2addr(L, idx));
@@ -1019,7 +1019,7 @@ const lua_tonumber = function(L, idx) {
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @returns {number}
+ * @returns {number|false}
  */
 const lua_tonumberx = function(L, idx) {
     return lvm.tonumber(index2addr(L, idx));
@@ -1181,7 +1181,7 @@ const lua_iscfunction = function(L, idx) {
 /**
  * @param {lua_State} L
  * @param {number} n
- * @returns {number}
+ * @returns {boolean}
  */
 const lua_isnil = function(L, n) {
     return lua_type(L, n) === LUA_TNIL;
@@ -1190,7 +1190,7 @@ const lua_isnil = function(L, n) {
 /**
  * @param {lua_State} L
  * @param {number} n
- * @returns {number}
+ * @returns {boolean}
  */
 const lua_isboolean = function(L, n) {
     return lua_type(L, n) === LUA_TBOOLEAN;
@@ -1199,7 +1199,7 @@ const lua_isboolean = function(L, n) {
 /**
  * @param {lua_State} L
  * @param {number} n
- * @returns {number}
+ * @returns {boolean}
  */
 const lua_isnone = function(L, n) {
     return lua_type(L, n) === LUA_TNONE;
@@ -1208,7 +1208,7 @@ const lua_isnone = function(L, n) {
 /**
  * @param {lua_State} L
  * @param {number} n
- * @returns {number}
+ * @returns {boolean}
  */
 const lua_isnoneornil = function(L, n) {
     return lua_type(L, n) <= 0;
@@ -1217,7 +1217,7 @@ const lua_isnoneornil = function(L, n) {
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @returns {number}
+ * @returns {boolean}
  */
 const lua_istable = function(L, idx) {
     return index2addr(L, idx).ttistable();
@@ -1235,7 +1235,7 @@ const lua_isinteger = function(L, idx) {
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @returns {number}
+ * @returns {boolean}
  */
 const lua_isnumber = function(L, idx) {
     return lvm.tonumber(index2addr(L, idx)) !== false;
@@ -1264,7 +1264,7 @@ const lua_isuserdata = function(L, idx) {
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @returns {number}
+ * @returns {boolean}
  */
 const lua_isthread = function(L, idx) {
     return lua_type(L, idx) === LUA_TTHREAD;
@@ -1273,7 +1273,7 @@ const lua_isthread = function(L, idx) {
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @returns {number}
+ * @returns {boolean}
  */
 const lua_isfunction = function(L, idx) {
     return lua_type(L, idx) === LUA_TFUNCTION;
@@ -1282,7 +1282,7 @@ const lua_isfunction = function(L, idx) {
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @returns {number}
+ * @returns {boolean}
  */
 const lua_islightuserdata = function(L, idx) {
     return lua_type(L, idx) === LUA_TLIGHTUSERDATA;
@@ -1458,7 +1458,7 @@ const lua_pcall = function(L, n, r, f) {
 
 /**
  * @param {lua_State} L
- * @returns {number}
+ * @returns {never}
  */
 const lua_error = function(L) {
     api_checknelems(L, 1);
diff --git a/src/lauxlib.js b/src/lauxlib.js
index b62bf27..9d7e677 100644
--- a/src/lauxlib.js
+++ b/src/lauxlib.js
@@ -244,8 +244,8 @@ const panic = function(L) {
 /**
  * @param {lua_State} L
  * @param {number} arg
- * @param {Uint8Array} extramsg
- * @returns {number}
+ * @param {any} extramsg
+ * @returns {never}
  */
 const luaL_argerror = function(L, arg, extramsg) {
     let ar = new lua_Debug();
@@ -267,6 +267,9 @@ const luaL_argerror = function(L, arg, extramsg) {
     return luaL_error(L, to_luastring("bad argument #%d to '%s' (%s)"), arg, ar.name, extramsg);
 };
 
+/**
+ * @returns {never}
+ */
 const typeerror = function(L, arg, tname) {
     let typearg;
     if (luaL_getmetafield(L, arg, __name) === LUA_TSTRING)
@@ -306,8 +309,9 @@ const luaL_error = function(L, fmt, ...argp) {
 /* Unlike normal lua, we pass in an error object */
 /**
  * @param {lua_State} L
- * @param {number} stat
- * @param {Uint8Array} fname
+ * @param {any} [stat]
+ * @param {string|Uint8Array|false} [fname]
+ * @param {any} [e]
  * @returns {number}
  */
 const luaL_fileresult = function(L, stat, fname, e) {
@@ -336,7 +340,7 @@ const luaL_fileresult = function(L, stat, fname, e) {
 /* Unlike normal lua, we pass in an error object */
 /**
  * @param {lua_State} L
- * @param {number} e
+ * @param {any} e
  * @returns {number}
  */
 const luaL_execresult = function(L, e) {
@@ -426,12 +430,14 @@ const luaL_checkoption = function(L, arg, def, lst) {
     return luaL_argerror(L, arg, lua_pushfstring(L, to_luastring("invalid option '%s'"), name));
 };
 
+/**
+ * @returns {never}
+ */
 const tag_error = function(L, arg, tag) {
     typeerror(L, arg, lua_typename(L, tag));
 };
 
 /**
- * @param {number}
  * @returns {lua_State}
  */
 const luaL_newstate = function() {
@@ -452,9 +458,9 @@ const luaL_typename = function(L, i) {
 
 /**
  * @param {lua_State} L
- * @param {number} cond
+ * @param {any} cond
  * @param {number} arg
- * @param {Uint8Array} extramsg
+ * @param {any} extramsg
  */
 const luaL_argcheck = function(L, cond, arg, extramsg) {
     if (!cond) luaL_argerror(L, arg, extramsg);
@@ -495,7 +501,7 @@ const luaL_checkstring = luaL_checklstring;
 /**
  * @param {lua_State} L
  * @param {number} arg
- * @param {Uint8Array} def
+ * @param {string|Uint8Array} [def]
  * @returns {Uint8Array}
  */
 const luaL_optlstring = function(L, arg, def) {
@@ -543,7 +549,7 @@ const luaL_optnumber = function(L, arg, def) {
 const luaL_checkinteger = function(L, arg) {
     let d = lua_tointegerx(L, arg);
     if (d === false)
-        interror(L, arg);
+        return interror(L, arg);
     return d;
 };
 
@@ -699,7 +705,7 @@ const luaL_getmetafield = function(L, obj, event) {
  * @param {lua_State} L
  * @param {number} obj
  * @param {Uint8Array} event
- * @returns {number}
+ * @returns {boolean}
  */
 const luaL_callmeta = function(L, obj, event) {
     obj = lua_absindex(L, obj);
@@ -721,7 +727,7 @@ const luaL_len = function(L, idx) {
     lua_len(L, idx);
     let l = lua_tointegerx(L, -1);
     if (l === false)
-        luaL_error(L, to_luastring("object length is not an integer", true));
+        return luaL_error(L, to_luastring("object length is not an integer", true));
     lua_pop(L, 1);  /* remove object */
     return l;
 };
@@ -837,7 +843,7 @@ const luaL_gsub = function(L, s, p, r) {
  * @param {lua_State} L
  * @param {number} idx
  * @param {Uint8Array} fname
- * @returns {number}
+ * @returns {boolean}
  */
 const luaL_getsubtable = function(L, idx, fname) {
     if (lua_getfield(L, idx, fname) === LUA_TTABLE)
@@ -878,7 +884,7 @@ const luaL_setfuncs = function(L, l, nup) {
 /**
  * @param {lua_State} L
  * @param {number} space
- * @param {Uint8Array} msg
+ * @param {any} msg
  */
 const luaL_checkstack = function(L, space, msg) {
     if (!lua_checkstack(L, space)) {
diff --git a/src/ldebug.js b/src/ldebug.js
index a471c75..a22cad0 100644
--- a/src/ldebug.js
+++ b/src/ldebug.js
@@ -608,6 +608,9 @@ const luaG_runerror = function(L, fmt, ...argp) {
     luaG_errormsg(L);
 };
 
+/**
+ * @returns {never}
+ */
 const luaG_errormsg = function(L) {
     if (L.errfunc !== 0) {  /* is there an error handling function? */
         let errfunc = L.errfunc;
diff --git a/src/ldo.js b/src/ldo.js
index 5f849ca..b4b1336 100644
--- a/src/ldo.js
+++ b/src/ldo.js
@@ -368,6 +368,9 @@ const luaD_call = function(L, off, nResults) {
     L.nCcalls--;
 };
 
+/**
+ * @returns {never}
+ */
 const luaD_throw = function(L, errcode) {
     if (L.errorJmp) {  /* thread has an error handler? */
         L.errorJmp.status = errcode;  /* set status */
diff --git a/src/llimits.js b/src/llimits.js
index 4f2b3f3..a573585 100644
--- a/src/llimits.js
+++ b/src/llimits.js
@@ -1,7 +1,11 @@
 "use strict";
 
-const lua_assert = function(c) {
-    if (!c) throw Error("assertion failed");
+/**
+ * @param {any} c
+ * @param {string} [msg]
+ */
+const lua_assert = function(c, msg) {
+    if (!c) throw Error(msg || "assertion failed");
 };
 module.exports.lua_assert = lua_assert;
 

From 477f93fc9e16d24d305d181a56138d06029fdedb Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Thu, 27 Apr 2023 16:33:33 +0800
Subject: [PATCH 08/14] Workaround possible tsc caveat when re-exporting

Possibly due to [TypeScript#51819], the exported declarations are quite
incomplete when re-exporting using `module.exports.key = value` syntax.

[TypeScript#51819]:
https://github.com/microsoft/TypeScript/issues/51819
---
 src/fengari.js |  43 +++---
 src/lua.js     | 370 +++++++++++++++++++++++++------------------------
 2 files changed, 208 insertions(+), 205 deletions(-)

diff --git a/src/fengari.js b/src/fengari.js
index 147756f..267ed00 100644
--- a/src/fengari.js
+++ b/src/fengari.js
@@ -10,28 +10,27 @@ Copyright © 1994–2017 Lua.org, PUC-Rio.
 
 const core = require("./fengaricore.js");
 
-module.exports.FENGARI_AUTHORS         = core.FENGARI_AUTHORS;
-module.exports.FENGARI_COPYRIGHT       = core.FENGARI_COPYRIGHT;
-module.exports.FENGARI_RELEASE         = core.FENGARI_RELEASE;
-module.exports.FENGARI_VERSION         = core.FENGARI_VERSION;
-module.exports.FENGARI_VERSION_MAJOR   = core.FENGARI_VERSION_MAJOR;
-module.exports.FENGARI_VERSION_MINOR   = core.FENGARI_VERSION_MINOR;
-module.exports.FENGARI_VERSION_NUM     = core.FENGARI_VERSION_NUM;
-module.exports.FENGARI_VERSION_RELEASE = core.FENGARI_VERSION_RELEASE;
+const exported = {
+    FENGARI_AUTHORS:          core.FENGARI_AUTHORS,
+    FENGARI_COPYRIGHT:        core.FENGARI_COPYRIGHT,
+    FENGARI_RELEASE:          core.FENGARI_RELEASE,
+    FENGARI_VERSION:          core.FENGARI_VERSION,
+    FENGARI_VERSION_MAJOR:    core.FENGARI_VERSION_MAJOR,
+    FENGARI_VERSION_MINOR:    core.FENGARI_VERSION_MINOR,
+    FENGARI_VERSION_NUM:      core.FENGARI_VERSION_NUM,
+    FENGARI_VERSION_RELEASE:  core.FENGARI_VERSION_RELEASE,
 
-module.exports.luastring_eq      = core.luastring_eq;
-module.exports.luastring_indexOf = core.luastring_indexOf;
-module.exports.luastring_of      = core.luastring_of;
-module.exports.to_jsstring       = core.to_jsstring;
-module.exports.to_luastring      = core.to_luastring;
-module.exports.to_uristring      = core.to_uristring;
+    luastring_eq:             core.luastring_eq,
+    luastring_indexOf:        core.luastring_indexOf,
+    luastring_of:             core.luastring_of,
+    to_jsstring:              core.to_jsstring,
+    to_luastring:             core.to_luastring,
+    to_uristring:             core.to_uristring,
 
-const luaconf = require('./luaconf.js');
-const lua     = require('./lua.js');
-const lauxlib = require('./lauxlib.js');
-const lualib  = require('./lualib.js');
+    luaconf:                  require('./luaconf.js'),
+    lua:                      require('./lua.js'),
+    lauxlib:                  require('./lauxlib.js'),
+    lualib:                   require('./lualib.js'),
+};
 
-module.exports.luaconf = luaconf;
-module.exports.lua     = lua;
-module.exports.lauxlib = lauxlib;
-module.exports.lualib  = lualib;
+module.exports = exported;
diff --git a/src/lua.js b/src/lua.js
index 154cfa8..635d6d9 100644
--- a/src/lua.js
+++ b/src/lua.js
@@ -6,186 +6,190 @@ const ldebug = require("./ldebug.js");
 const ldo    = require("./ldo.js");
 const lstate = require("./lstate.js");
 
-module.exports.LUA_AUTHORS             = defs.LUA_AUTHORS;
-module.exports.LUA_COPYRIGHT           = defs.LUA_COPYRIGHT;
-module.exports.LUA_ERRERR              = defs.thread_status.LUA_ERRERR;
-module.exports.LUA_ERRGCMM             = defs.thread_status.LUA_ERRGCMM;
-module.exports.LUA_ERRMEM              = defs.thread_status.LUA_ERRMEM;
-module.exports.LUA_ERRRUN              = defs.thread_status.LUA_ERRRUN;
-module.exports.LUA_ERRSYNTAX           = defs.thread_status.LUA_ERRSYNTAX;
-module.exports.LUA_HOOKCALL            = defs.LUA_HOOKCALL;
-module.exports.LUA_HOOKCOUNT           = defs.LUA_HOOKCOUNT;
-module.exports.LUA_HOOKLINE            = defs.LUA_HOOKLINE;
-module.exports.LUA_HOOKRET             = defs.LUA_HOOKRET;
-module.exports.LUA_HOOKTAILCALL        = defs.LUA_HOOKTAILCALL;
-module.exports.LUA_MASKCALL            = defs.LUA_MASKCALL;
-module.exports.LUA_MASKCOUNT           = defs.LUA_MASKCOUNT;
-module.exports.LUA_MASKLINE            = defs.LUA_MASKLINE;
-module.exports.LUA_MASKRET             = defs.LUA_MASKRET;
-module.exports.LUA_MINSTACK            = defs.LUA_MINSTACK;
-module.exports.LUA_MULTRET             = defs.LUA_MULTRET;
-module.exports.LUA_NUMTAGS             = defs.constant_types.LUA_NUMTAGS;
-module.exports.LUA_OK                  = defs.thread_status.LUA_OK;
-module.exports.LUA_OPADD               = defs.LUA_OPADD;
-module.exports.LUA_OPBAND              = defs.LUA_OPBAND;
-module.exports.LUA_OPBNOT              = defs.LUA_OPBNOT;
-module.exports.LUA_OPBOR               = defs.LUA_OPBOR;
-module.exports.LUA_OPBXOR              = defs.LUA_OPBXOR;
-module.exports.LUA_OPDIV               = defs.LUA_OPDIV;
-module.exports.LUA_OPEQ                = defs.LUA_OPEQ;
-module.exports.LUA_OPIDIV              = defs.LUA_OPIDIV;
-module.exports.LUA_OPLE                = defs.LUA_OPLE;
-module.exports.LUA_OPLT                = defs.LUA_OPLT;
-module.exports.LUA_OPMOD               = defs.LUA_OPMOD;
-module.exports.LUA_OPMUL               = defs.LUA_OPMUL;
-module.exports.LUA_OPPOW               = defs.LUA_OPPOW;
-module.exports.LUA_OPSHL               = defs.LUA_OPSHL;
-module.exports.LUA_OPSHR               = defs.LUA_OPSHR;
-module.exports.LUA_OPSUB               = defs.LUA_OPSUB;
-module.exports.LUA_OPUNM               = defs.LUA_OPUNM;
-module.exports.LUA_REGISTRYINDEX       = defs.LUA_REGISTRYINDEX;
-module.exports.LUA_RELEASE             = defs.LUA_RELEASE;
-module.exports.LUA_RIDX_GLOBALS        = defs.LUA_RIDX_GLOBALS;
-module.exports.LUA_RIDX_LAST           = defs.LUA_RIDX_LAST;
-module.exports.LUA_RIDX_MAINTHREAD     = defs.LUA_RIDX_MAINTHREAD;
-module.exports.LUA_SIGNATURE           = defs.LUA_SIGNATURE;
-module.exports.LUA_TNONE               = defs.constant_types.LUA_TNONE;
-module.exports.LUA_TNIL                = defs.constant_types.LUA_TNIL;
-module.exports.LUA_TBOOLEAN            = defs.constant_types.LUA_TBOOLEAN;
-module.exports.LUA_TLIGHTUSERDATA      = defs.constant_types.LUA_TLIGHTUSERDATA;
-module.exports.LUA_TNUMBER             = defs.constant_types.LUA_TNUMBER;
-module.exports.LUA_TSTRING             = defs.constant_types.LUA_TSTRING;
-module.exports.LUA_TTABLE              = defs.constant_types.LUA_TTABLE;
-module.exports.LUA_TFUNCTION           = defs.constant_types.LUA_TFUNCTION;
-module.exports.LUA_TUSERDATA           = defs.constant_types.LUA_TUSERDATA;
-module.exports.LUA_TTHREAD             = defs.constant_types.LUA_TTHREAD;
-module.exports.LUA_VERSION             = defs.LUA_VERSION;
-module.exports.LUA_VERSION_MAJOR       = defs.LUA_VERSION_MAJOR;
-module.exports.LUA_VERSION_MINOR       = defs.LUA_VERSION_MINOR;
-module.exports.LUA_VERSION_NUM         = defs.LUA_VERSION_NUM;
-module.exports.LUA_VERSION_RELEASE     = defs.LUA_VERSION_RELEASE;
-module.exports.LUA_YIELD               = defs.thread_status.LUA_YIELD;
-module.exports.lua_Debug               = defs.lua_Debug;
-module.exports.lua_upvalueindex        = defs.lua_upvalueindex;
-module.exports.lua_absindex            = lapi.lua_absindex;
-module.exports.lua_arith               = lapi.lua_arith;
-module.exports.lua_atpanic             = lapi.lua_atpanic;
-module.exports.lua_atnativeerror       = lapi.lua_atnativeerror;
-module.exports.lua_call                = lapi.lua_call;
-module.exports.lua_callk               = lapi.lua_callk;
-module.exports.lua_checkstack          = lapi.lua_checkstack;
-module.exports.lua_close               = lstate.lua_close;
-module.exports.lua_compare             = lapi.lua_compare;
-module.exports.lua_concat              = lapi.lua_concat;
-module.exports.lua_copy                = lapi.lua_copy;
-module.exports.lua_createtable         = lapi.lua_createtable;
-module.exports.lua_dump                = lapi.lua_dump;
-module.exports.lua_error               = lapi.lua_error;
-module.exports.lua_gc                  = lapi.lua_gc;
-module.exports.lua_getallocf           = lapi.lua_getallocf;
-module.exports.lua_getextraspace       = lapi.lua_getextraspace;
-module.exports.lua_getfield            = lapi.lua_getfield;
-module.exports.lua_getglobal           = lapi.lua_getglobal;
-module.exports.lua_gethook             = ldebug.lua_gethook;
-module.exports.lua_gethookcount        = ldebug.lua_gethookcount;
-module.exports.lua_gethookmask         = ldebug.lua_gethookmask;
-module.exports.lua_geti                = lapi.lua_geti;
-module.exports.lua_getinfo             = ldebug.lua_getinfo;
-module.exports.lua_getlocal            = ldebug.lua_getlocal;
-module.exports.lua_getmetatable        = lapi.lua_getmetatable;
-module.exports.lua_getstack            = ldebug.lua_getstack;
-module.exports.lua_gettable            = lapi.lua_gettable;
-module.exports.lua_gettop              = lapi.lua_gettop;
-module.exports.lua_getupvalue          = lapi.lua_getupvalue;
-module.exports.lua_getuservalue        = lapi.lua_getuservalue;
-module.exports.lua_insert              = lapi.lua_insert;
-module.exports.lua_isboolean           = lapi.lua_isboolean;
-module.exports.lua_iscfunction         = lapi.lua_iscfunction;
-module.exports.lua_isfunction          = lapi.lua_isfunction;
-module.exports.lua_isinteger           = lapi.lua_isinteger;
-module.exports.lua_islightuserdata     = lapi.lua_islightuserdata;
-module.exports.lua_isnil               = lapi.lua_isnil;
-module.exports.lua_isnone              = lapi.lua_isnone;
-module.exports.lua_isnoneornil         = lapi.lua_isnoneornil;
-module.exports.lua_isnumber            = lapi.lua_isnumber;
-module.exports.lua_isproxy             = lapi.lua_isproxy;
-module.exports.lua_isstring            = lapi.lua_isstring;
-module.exports.lua_istable             = lapi.lua_istable;
-module.exports.lua_isthread            = lapi.lua_isthread;
-module.exports.lua_isuserdata          = lapi.lua_isuserdata;
-module.exports.lua_isyieldable         = ldo.lua_isyieldable;
-module.exports.lua_len                 = lapi.lua_len;
-module.exports.lua_load                = lapi.lua_load;
-module.exports.lua_newstate            = lstate.lua_newstate;
-module.exports.lua_newtable            = lapi.lua_newtable;
-module.exports.lua_newthread           = lstate.lua_newthread;
-module.exports.lua_newuserdata         = lapi.lua_newuserdata;
-module.exports.lua_next                = lapi.lua_next;
-module.exports.lua_pcall               = lapi.lua_pcall;
-module.exports.lua_pcallk              = lapi.lua_pcallk;
-module.exports.lua_pop                 = lapi.lua_pop;
-module.exports.lua_pushboolean         = lapi.lua_pushboolean;
-module.exports.lua_pushcclosure        = lapi.lua_pushcclosure;
-module.exports.lua_pushcfunction       = lapi.lua_pushcfunction;
-module.exports.lua_pushfstring         = lapi.lua_pushfstring;
-module.exports.lua_pushglobaltable     = lapi.lua_pushglobaltable;
-module.exports.lua_pushinteger         = lapi.lua_pushinteger;
-module.exports.lua_pushjsclosure       = lapi.lua_pushjsclosure;
-module.exports.lua_pushjsfunction      = lapi.lua_pushjsfunction;
-module.exports.lua_pushlightuserdata   = lapi.lua_pushlightuserdata;
-module.exports.lua_pushliteral         = lapi.lua_pushliteral;
-module.exports.lua_pushlstring         = lapi.lua_pushlstring;
-module.exports.lua_pushnil             = lapi.lua_pushnil;
-module.exports.lua_pushnumber          = lapi.lua_pushnumber;
-module.exports.lua_pushstring          = lapi.lua_pushstring;
-module.exports.lua_pushthread          = lapi.lua_pushthread;
-module.exports.lua_pushvalue           = lapi.lua_pushvalue;
-module.exports.lua_pushvfstring        = lapi.lua_pushvfstring;
-module.exports.lua_rawequal            = lapi.lua_rawequal;
-module.exports.lua_rawget              = lapi.lua_rawget;
-module.exports.lua_rawgeti             = lapi.lua_rawgeti;
-module.exports.lua_rawgetp             = lapi.lua_rawgetp;
-module.exports.lua_rawlen              = lapi.lua_rawlen;
-module.exports.lua_rawset              = lapi.lua_rawset;
-module.exports.lua_rawseti             = lapi.lua_rawseti;
-module.exports.lua_rawsetp             = lapi.lua_rawsetp;
-module.exports.lua_register            = lapi.lua_register;
-module.exports.lua_remove              = lapi.lua_remove;
-module.exports.lua_replace             = lapi.lua_replace;
-module.exports.lua_resume              = ldo.lua_resume;
-module.exports.lua_rotate              = lapi.lua_rotate;
-module.exports.lua_setallocf           = lapi.lua_setallocf;
-module.exports.lua_setfield            = lapi.lua_setfield;
-module.exports.lua_setglobal           = lapi.lua_setglobal;
-module.exports.lua_sethook             = ldebug.lua_sethook;
-module.exports.lua_seti                = lapi.lua_seti;
-module.exports.lua_setlocal            = ldebug.lua_setlocal;
-module.exports.lua_setmetatable        = lapi.lua_setmetatable;
-module.exports.lua_settable            = lapi.lua_settable;
-module.exports.lua_settop              = lapi.lua_settop;
-module.exports.lua_setupvalue          = lapi.lua_setupvalue;
-module.exports.lua_setuservalue        = lapi.lua_setuservalue;
-module.exports.lua_status              = lapi.lua_status;
-module.exports.lua_stringtonumber      = lapi.lua_stringtonumber;
-module.exports.lua_toboolean           = lapi.lua_toboolean;
-module.exports.lua_todataview          = lapi.lua_todataview;
-module.exports.lua_tointeger           = lapi.lua_tointeger;
-module.exports.lua_tointegerx          = lapi.lua_tointegerx;
-module.exports.lua_tojsstring          = lapi.lua_tojsstring;
-module.exports.lua_tolstring           = lapi.lua_tolstring;
-module.exports.lua_tonumber            = lapi.lua_tonumber;
-module.exports.lua_tonumberx           = lapi.lua_tonumberx;
-module.exports.lua_topointer           = lapi.lua_topointer;
-module.exports.lua_toproxy             = lapi.lua_toproxy;
-module.exports.lua_tostring            = lapi.lua_tostring;
-module.exports.lua_tothread            = lapi.lua_tothread;
-module.exports.lua_touserdata          = lapi.lua_touserdata;
-module.exports.lua_type                = lapi.lua_type;
-module.exports.lua_typename            = lapi.lua_typename;
-module.exports.lua_upvalueid           = lapi.lua_upvalueid;
-module.exports.lua_upvaluejoin         = lapi.lua_upvaluejoin;
-module.exports.lua_version             = lapi.lua_version;
-module.exports.lua_xmove               = lapi.lua_xmove;
-module.exports.lua_yield               = ldo.lua_yield;
-module.exports.lua_yieldk              = ldo.lua_yieldk;
-module.exports.lua_tocfunction         = lapi.lua_tocfunction;
+const exported = {
+    LUA_AUTHORS:              defs.LUA_AUTHORS,
+    LUA_COPYRIGHT:            defs.LUA_COPYRIGHT,
+    LUA_ERRERR:               defs.thread_status.LUA_ERRERR,
+    LUA_ERRGCMM:              defs.thread_status.LUA_ERRGCMM,
+    LUA_ERRMEM:               defs.thread_status.LUA_ERRMEM,
+    LUA_ERRRUN:               defs.thread_status.LUA_ERRRUN,
+    LUA_ERRSYNTAX:            defs.thread_status.LUA_ERRSYNTAX,
+    LUA_HOOKCALL:             defs.LUA_HOOKCALL,
+    LUA_HOOKCOUNT:            defs.LUA_HOOKCOUNT,
+    LUA_HOOKLINE:             defs.LUA_HOOKLINE,
+    LUA_HOOKRET:              defs.LUA_HOOKRET,
+    LUA_HOOKTAILCALL:         defs.LUA_HOOKTAILCALL,
+    LUA_MASKCALL:             defs.LUA_MASKCALL,
+    LUA_MASKCOUNT:            defs.LUA_MASKCOUNT,
+    LUA_MASKLINE:             defs.LUA_MASKLINE,
+    LUA_MASKRET:              defs.LUA_MASKRET,
+    LUA_MINSTACK:             defs.LUA_MINSTACK,
+    LUA_MULTRET:              defs.LUA_MULTRET,
+    LUA_NUMTAGS:              defs.constant_types.LUA_NUMTAGS,
+    LUA_OK:                   defs.thread_status.LUA_OK,
+    LUA_OPADD:                defs.LUA_OPADD,
+    LUA_OPBAND:               defs.LUA_OPBAND,
+    LUA_OPBNOT:               defs.LUA_OPBNOT,
+    LUA_OPBOR:                defs.LUA_OPBOR,
+    LUA_OPBXOR:               defs.LUA_OPBXOR,
+    LUA_OPDIV:                defs.LUA_OPDIV,
+    LUA_OPEQ:                 defs.LUA_OPEQ,
+    LUA_OPIDIV:               defs.LUA_OPIDIV,
+    LUA_OPLE:                 defs.LUA_OPLE,
+    LUA_OPLT:                 defs.LUA_OPLT,
+    LUA_OPMOD:                defs.LUA_OPMOD,
+    LUA_OPMUL:                defs.LUA_OPMUL,
+    LUA_OPPOW:                defs.LUA_OPPOW,
+    LUA_OPSHL:                defs.LUA_OPSHL,
+    LUA_OPSHR:                defs.LUA_OPSHR,
+    LUA_OPSUB:                defs.LUA_OPSUB,
+    LUA_OPUNM:                defs.LUA_OPUNM,
+    LUA_REGISTRYINDEX:        defs.LUA_REGISTRYINDEX,
+    LUA_RELEASE:              defs.LUA_RELEASE,
+    LUA_RIDX_GLOBALS:         defs.LUA_RIDX_GLOBALS,
+    LUA_RIDX_LAST:            defs.LUA_RIDX_LAST,
+    LUA_RIDX_MAINTHREAD:      defs.LUA_RIDX_MAINTHREAD,
+    LUA_SIGNATURE:            defs.LUA_SIGNATURE,
+    LUA_TNONE:                defs.constant_types.LUA_TNONE,
+    LUA_TNIL:                 defs.constant_types.LUA_TNIL,
+    LUA_TBOOLEAN:             defs.constant_types.LUA_TBOOLEAN,
+    LUA_TLIGHTUSERDATA:       defs.constant_types.LUA_TLIGHTUSERDATA,
+    LUA_TNUMBER:              defs.constant_types.LUA_TNUMBER,
+    LUA_TSTRING:              defs.constant_types.LUA_TSTRING,
+    LUA_TTABLE:               defs.constant_types.LUA_TTABLE,
+    LUA_TFUNCTION:            defs.constant_types.LUA_TFUNCTION,
+    LUA_TUSERDATA:            defs.constant_types.LUA_TUSERDATA,
+    LUA_TTHREAD:              defs.constant_types.LUA_TTHREAD,
+    LUA_VERSION:              defs.LUA_VERSION,
+    LUA_VERSION_MAJOR:        defs.LUA_VERSION_MAJOR,
+    LUA_VERSION_MINOR:        defs.LUA_VERSION_MINOR,
+    LUA_VERSION_NUM:          defs.LUA_VERSION_NUM,
+    LUA_VERSION_RELEASE:      defs.LUA_VERSION_RELEASE,
+    LUA_YIELD:                defs.thread_status.LUA_YIELD,
+    lua_Debug:                defs.lua_Debug,
+    lua_upvalueindex:         defs.lua_upvalueindex,
+    lua_absindex:             lapi.lua_absindex,
+    lua_arith:                lapi.lua_arith,
+    lua_atpanic:              lapi.lua_atpanic,
+    lua_atnativeerror:        lapi.lua_atnativeerror,
+    lua_call:                 lapi.lua_call,
+    lua_callk:                lapi.lua_callk,
+    lua_checkstack:           lapi.lua_checkstack,
+    lua_close:                lstate.lua_close,
+    lua_compare:              lapi.lua_compare,
+    lua_concat:               lapi.lua_concat,
+    lua_copy:                 lapi.lua_copy,
+    lua_createtable:          lapi.lua_createtable,
+    lua_dump:                 lapi.lua_dump,
+    lua_error:                lapi.lua_error,
+    lua_gc:                   lapi.lua_gc,
+    lua_getallocf:            lapi.lua_getallocf,
+    lua_getextraspace:        lapi.lua_getextraspace,
+    lua_getfield:             lapi.lua_getfield,
+    lua_getglobal:            lapi.lua_getglobal,
+    lua_gethook:              ldebug.lua_gethook,
+    lua_gethookcount:         ldebug.lua_gethookcount,
+    lua_gethookmask:          ldebug.lua_gethookmask,
+    lua_geti:                 lapi.lua_geti,
+    lua_getinfo:              ldebug.lua_getinfo,
+    lua_getlocal:             ldebug.lua_getlocal,
+    lua_getmetatable:         lapi.lua_getmetatable,
+    lua_getstack:             ldebug.lua_getstack,
+    lua_gettable:             lapi.lua_gettable,
+    lua_gettop:               lapi.lua_gettop,
+    lua_getupvalue:           lapi.lua_getupvalue,
+    lua_getuservalue:         lapi.lua_getuservalue,
+    lua_insert:               lapi.lua_insert,
+    lua_isboolean:            lapi.lua_isboolean,
+    lua_iscfunction:          lapi.lua_iscfunction,
+    lua_isfunction:           lapi.lua_isfunction,
+    lua_isinteger:            lapi.lua_isinteger,
+    lua_islightuserdata:      lapi.lua_islightuserdata,
+    lua_isnil:                lapi.lua_isnil,
+    lua_isnone:               lapi.lua_isnone,
+    lua_isnoneornil:          lapi.lua_isnoneornil,
+    lua_isnumber:             lapi.lua_isnumber,
+    lua_isproxy:              lapi.lua_isproxy,
+    lua_isstring:             lapi.lua_isstring,
+    lua_istable:              lapi.lua_istable,
+    lua_isthread:             lapi.lua_isthread,
+    lua_isuserdata:           lapi.lua_isuserdata,
+    lua_isyieldable:          ldo.lua_isyieldable,
+    lua_len:                  lapi.lua_len,
+    lua_load:                 lapi.lua_load,
+    lua_newstate:             lstate.lua_newstate,
+    lua_newtable:             lapi.lua_newtable,
+    lua_newthread:            lstate.lua_newthread,
+    lua_newuserdata:          lapi.lua_newuserdata,
+    lua_next:                 lapi.lua_next,
+    lua_pcall:                lapi.lua_pcall,
+    lua_pcallk:               lapi.lua_pcallk,
+    lua_pop:                  lapi.lua_pop,
+    lua_pushboolean:          lapi.lua_pushboolean,
+    lua_pushcclosure:         lapi.lua_pushcclosure,
+    lua_pushcfunction:        lapi.lua_pushcfunction,
+    lua_pushfstring:          lapi.lua_pushfstring,
+    lua_pushglobaltable:      lapi.lua_pushglobaltable,
+    lua_pushinteger:          lapi.lua_pushinteger,
+    lua_pushjsclosure:        lapi.lua_pushjsclosure,
+    lua_pushjsfunction:       lapi.lua_pushjsfunction,
+    lua_pushlightuserdata:    lapi.lua_pushlightuserdata,
+    lua_pushliteral:          lapi.lua_pushliteral,
+    lua_pushlstring:          lapi.lua_pushlstring,
+    lua_pushnil:              lapi.lua_pushnil,
+    lua_pushnumber:           lapi.lua_pushnumber,
+    lua_pushstring:           lapi.lua_pushstring,
+    lua_pushthread:           lapi.lua_pushthread,
+    lua_pushvalue:            lapi.lua_pushvalue,
+    lua_pushvfstring:         lapi.lua_pushvfstring,
+    lua_rawequal:             lapi.lua_rawequal,
+    lua_rawget:               lapi.lua_rawget,
+    lua_rawgeti:              lapi.lua_rawgeti,
+    lua_rawgetp:              lapi.lua_rawgetp,
+    lua_rawlen:               lapi.lua_rawlen,
+    lua_rawset:               lapi.lua_rawset,
+    lua_rawseti:              lapi.lua_rawseti,
+    lua_rawsetp:              lapi.lua_rawsetp,
+    lua_register:             lapi.lua_register,
+    lua_remove:               lapi.lua_remove,
+    lua_replace:              lapi.lua_replace,
+    lua_resume:               ldo.lua_resume,
+    lua_rotate:               lapi.lua_rotate,
+    lua_setallocf:            lapi.lua_setallocf,
+    lua_setfield:             lapi.lua_setfield,
+    lua_setglobal:            lapi.lua_setglobal,
+    lua_sethook:              ldebug.lua_sethook,
+    lua_seti:                 lapi.lua_seti,
+    lua_setlocal:             ldebug.lua_setlocal,
+    lua_setmetatable:         lapi.lua_setmetatable,
+    lua_settable:             lapi.lua_settable,
+    lua_settop:               lapi.lua_settop,
+    lua_setupvalue:           lapi.lua_setupvalue,
+    lua_setuservalue:         lapi.lua_setuservalue,
+    lua_status:               lapi.lua_status,
+    lua_stringtonumber:       lapi.lua_stringtonumber,
+    lua_toboolean:            lapi.lua_toboolean,
+    lua_todataview:           lapi.lua_todataview,
+    lua_tointeger:            lapi.lua_tointeger,
+    lua_tointegerx:           lapi.lua_tointegerx,
+    lua_tojsstring:           lapi.lua_tojsstring,
+    lua_tolstring:            lapi.lua_tolstring,
+    lua_tonumber:             lapi.lua_tonumber,
+    lua_tonumberx:            lapi.lua_tonumberx,
+    lua_topointer:            lapi.lua_topointer,
+    lua_toproxy:              lapi.lua_toproxy,
+    lua_tostring:             lapi.lua_tostring,
+    lua_tothread:             lapi.lua_tothread,
+    lua_touserdata:           lapi.lua_touserdata,
+    lua_type:                 lapi.lua_type,
+    lua_typename:             lapi.lua_typename,
+    lua_upvalueid:            lapi.lua_upvalueid,
+    lua_upvaluejoin:          lapi.lua_upvaluejoin,
+    lua_version:              lapi.lua_version,
+    lua_xmove:                lapi.lua_xmove,
+    lua_yield:                ldo.lua_yield,
+    lua_yieldk:               ldo.lua_yieldk,
+    lua_tocfunction:          lapi.lua_tocfunction,
+};
+
+module.exports = exported;

From 2c73341969fa0e638ce577173c6578246bb28f99 Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Thu, 27 Apr 2023 18:29:21 +0800
Subject: [PATCH 09/14] Bug fixes and typing tests with checkJs

Attempts to make tsc happy.

Possible bugs:
- lapi.js: there is no ts.value but ts.realstring
- liolib.js: "19.x.x" > 6 is always false
- loadlib.js: not lua_pushstring but lua_pushfstring
- loslib.js: Uint8Arrays instead of strings passed to nodejs functions
- lstrlib.js: wrong args to luaL_argcheck
- ltm.js: extraneous new
---
 package.json         |  6 +++---
 src/defs.js          |  1 +
 src/lapi.js          | 11 +++++-----
 src/lauxlib.js       |  4 ++--
 src/ldump.js         |  2 +-
 src/liolib.js        |  2 +-
 src/loadlib.js       |  3 ++-
 src/lobject.js       |  2 +-
 src/loslib.js        | 29 ++++++++++++++------------
 src/lstrlib.js       |  9 +++++----
 src/ltable.js        |  6 +++---
 src/ltm.js           | 48 ++++++++++++++++++++++----------------------
 src/lvm.js           |  1 +
 test/lapi.test.js    |  3 ++-
 test/lauxlib.test.js | 17 +++++++++++++++-
 tsconfig.json        |  3 ++-
 yarn.lock            |  5 +++++
 17 files changed, 91 insertions(+), 61 deletions(-)

diff --git a/package.json b/package.json
index 92e2f74..ea5fd96 100644
--- a/package.json
+++ b/package.json
@@ -10,10 +10,9 @@
   },
   "scripts": {
     "emit": "tsc",
-    "type-test": "tsc --allowJs --checkJs",
     "lint": "eslint src/ test/",
-    "prepublishOnly": "git diff-index --quiet --cached HEAD -- && npm run lint && npm run emit && npm run test",
-    "test": "jest"
+    "prepublishOnly": "git diff-index --quiet --cached HEAD -- && npm run lint && npm run test",
+    "test": "npm run emit && jest"
   },
   "repository": {
     "type": "git",
@@ -33,6 +32,7 @@
   },
   "homepage": "https://github.com/fengari-lua/fengari#readme",
   "devDependencies": {
+    "@types/node": "^18.16.1",
     "eslint": "^5.15.1",
     "jest": "^24.5.0",
     "typescript": "^5.0.4"
diff --git a/src/defs.js b/src/defs.js
index e9ae44c..454558b 100644
--- a/src/defs.js
+++ b/src/defs.js
@@ -288,6 +288,7 @@ const from_userstring = function(str) {
             throw new TypeError("expects an array of bytes or javascript string");
         }
     }
+    // @ts-ignore
     return str;
 };
 
diff --git a/src/lapi.js b/src/lapi.js
index 835984e..4e3b46a 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -356,7 +356,7 @@ const lua_pushinteger = function(L, n) {
  * @param {lua_State} L
  * @param {string|Uint8Array} s
  * @param {number} len
- * @returns {any}
+ * @returns {Uint8Array}
  */
 const lua_pushlstring = function(L, s, len) {
     fengari_argcheckinteger(len);
@@ -371,7 +371,7 @@ const lua_pushlstring = function(L, s, len) {
     }
     lobject.pushsvalue2s(L, ts);
     api_check(L, L.top <= L.ci.top, "stack overflow");
-    return ts.value;
+    return ts.realstring;
 };
 
 /**
@@ -418,9 +418,10 @@ const lua_pushfstring = function (L, fmt, ...argp) {
 /**
  * @param {lua_State} L
  * @param {string?} [s]
- * @returns {any}
+ * @returns {Uint8Array?}
  */
 const lua_pushliteral = function (L, s) {
+    let arr = null;
     if (s === undefined || s === null) {
         L.stack[L.top] = new TValue(LUA_TNIL, null);
         L.top++;
@@ -428,11 +429,11 @@ const lua_pushliteral = function (L, s) {
         fengari_argcheck(typeof s === "string");
         let ts = luaS_newliteral(L, s);
         lobject.pushsvalue2s(L, ts);
-        s = ts.getstr(); /* internal copy */
+        arr = ts.getstr(); /* internal copy */
     }
     api_check(L, L.top <= L.ci.top, "stack overflow");
 
-    return s;
+    return arr;
 };
 
 /**
diff --git a/src/lauxlib.js b/src/lauxlib.js
index 9d7e677..35ba417 100644
--- a/src/lauxlib.js
+++ b/src/lauxlib.js
@@ -1010,7 +1010,7 @@ if (typeof process === "undefined") {
             this.f = null;  /* file being read */
             this.buff = new Uint8Array(1024);  /* area for reading file */
             this.pos = 0;  /* current position in file */
-            this.err = void 0;
+            this.err = null;
         }
     }
 
@@ -1089,7 +1089,7 @@ if (typeof process === "undefined") {
             this.f = null;  /* file being read */
             this.buff = Buffer.alloc(1024);  /* area for reading file */
             this.pos = 0;  /* current position in file */
-            this.err = void 0;
+            this.err = null;
         }
     }
 
diff --git a/src/ldump.js b/src/ldump.js
index 596a319..6a30f0b 100644
--- a/src/ldump.js
+++ b/src/ldump.js
@@ -24,7 +24,7 @@ const LUAC_FORMAT  = 0;   /* this is the official format */
 class DumpState {
     constructor() {
         this.L = null;
-        this.write = null;
+        this.writer = null;
         this.data = null;
         this.strip = NaN;
         this.status = NaN;
diff --git a/src/liolib.js b/src/liolib.js
index 92dda58..9372dca 100644
--- a/src/liolib.js
+++ b/src/liolib.js
@@ -131,7 +131,7 @@ const io_output = function(L) {
 };
 
 /* node <= 6 doesn't support passing a Uint8Array to fs.writeSync */
-const prepare_string_for_write = process.versions.node > 6 ?
+const prepare_string_for_write = parseInt(process.versions.node.split('.')[0]) > 6 ?
     (s) => s : // identity function
     (s) => Buffer.from(s.buffer, s.byteOffset, s.byteLength);
 
diff --git a/src/loadlib.js b/src/loadlib.js
index afcfd3c..56848f7 100644
--- a/src/loadlib.js
+++ b/src/loadlib.js
@@ -79,6 +79,7 @@ const global_env = (function() {
     } else if (typeof window !== "undefined") {
         /* browser window */
         return window;
+        // @ts-ignore
     } else if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
         /* web worker */
         return self;
@@ -437,7 +438,7 @@ const searcher_Croot = function(L) {
         if (stat != ERRFUNC)
             return checkload(L, 0, filename);  /* real error */
         else {  /* open function not found */
-            lua_pushstring(L, to_luastring("\n\tno module '%s' in file '%s'"), name, filename);
+            lua_pushfstring(L, to_luastring("\n\tno module '%s' in file '%s'"), name, filename);
             return 1;
         }
     }
diff --git a/src/lobject.js b/src/lobject.js
index c83439c..4db914e 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -552,7 +552,7 @@ const l_str2int = function(s) {
     } else {  /* decimal */
         for (; i < s.length && lisdigit(s[i]); i++) {
             let d = s[i] - 48 /* ('0').charCodeAt(0) */;
-            if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg))  /* overflow? */
+            if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + (neg ? 1 : 0)))  /* overflow? */
                 return null;  /* do not accept it (as integer) */
             a = (a * 10 + d)|0;
             empty = false;
diff --git a/src/loslib.js b/src/loslib.js
index 2e7b90e..9e67e36 100644
--- a/src/loslib.js
+++ b/src/loslib.js
@@ -85,7 +85,8 @@ const setallfields = function(L, time, utc) {
     setfield(L, "month", (utc ? time.getUTCMonth()   : time.getMonth()) + 1);
     setfield(L, "year",  utc ? time.getUTCFullYear() : time.getFullYear());
     setfield(L, "wday",  (utc ? time.getUTCDay()     : time.getDay()) + 1);
-    setfield(L, "yday", Math.floor((time - (new Date(time.getFullYear(), 0, 0 /* shortcut to correct day by one */))) / 86400000));
+    setfield(L, "yday", Math.floor(
+        (time.valueOf() - (new Date(time.getFullYear(), 0, 0 /* shortcut to correct day by one */)).valueOf()) / 86400000));
     // setboolfield(L, "isdst", time.get);
 };
 
@@ -142,7 +143,7 @@ const week_number = function(date, start_of_week) {
         else
             weekday--;
     }
-    let yday = (date - new Date(date.getFullYear(), 0, 1)) / 86400000;
+    let yday = (date.valueOf() - new Date(date.getFullYear(), 0, 1).valueOf()) / 86400000;
     return Math.floor((yday + 7 - weekday) / 7);
 };
 
@@ -283,7 +284,7 @@ const strftime = function(L, b, s, date) {
 
                 // '000'
                 case 106 /* j */: {
-                    let yday = Math.floor((date - new Date(date.getFullYear(), 0, 1)) / 86400000);
+                    let yday = Math.floor((date.valueOf() - new Date(date.getFullYear(), 0, 1).valueOf()) / 86400000);
                     if (yday < 100) {
                         if (yday < 10)
                             luaL_addchar(b, 48 /* 0 */);
@@ -425,17 +426,17 @@ const os_time = function(L) {
         luaL_checktype(L, 1, LUA_TTABLE);
         lua_settop(L, 1);  /* make sure table is at the top */
         t = new Date(
-            getfield(L, "year", -1, 0),
-            getfield(L, "month", -1, 1),
-            getfield(L, "day", -1, 0),
-            getfield(L, "hour", 12, 0),
-            getfield(L, "min", 0, 0),
-            getfield(L, "sec", 0, 0)
+            Number(getfield(L, "year", -1, 0)),
+            Number(getfield(L, "month", -1, 1)),
+            Number(getfield(L, "day", -1, 0)),
+            Number(getfield(L, "hour", 12, 0)),
+            Number(getfield(L, "min", 0, 0)),
+            Number(getfield(L, "sec", 0, 0))
         );
         setallfields(L, t);
     }
 
-    lua_pushinteger(L, Math.floor(t / 1000));
+    lua_pushinteger(L, Math.floor(t.valueOf() / 1000));
     return 1;
 };
 
@@ -501,6 +502,7 @@ if (typeof process === "undefined") {
     };
 
     syslib.getenv = function(L) {
+        /** @type {string|Uint8Array} */
         let key = luaL_checkstring(L, 1);
         key = to_jsstring(key); /* https://github.com/nodejs/node/issues/16961 */
         if (Object.prototype.hasOwnProperty.call(process.env, key)) {
@@ -521,7 +523,7 @@ if (typeof process === "undefined") {
     };
 
     syslib.remove = function(L) {
-        let filename = luaL_checkstring(L, 1);
+        let filename = to_jsstring(luaL_checkstring(L, 1));
         try {
             fs.unlinkSync(filename);
         } catch (e) {
@@ -539,8 +541,8 @@ if (typeof process === "undefined") {
     };
 
     syslib.rename = function(L) {
-        let fromname = luaL_checkstring(L, 1);
-        let toname = luaL_checkstring(L, 2);
+        let fromname = to_jsstring(luaL_checkstring(L, 1));
+        let toname = to_jsstring(luaL_checkstring(L, 2));
         try {
             fs.renameSync(fromname, toname);
         } catch (e) {
@@ -558,6 +560,7 @@ if (typeof process === "undefined") {
     };
 
     syslib.execute = function(L) {
+        /** @type {string|Uint8Array} */
         let cmd = luaL_optstring(L, 1, null);
         if (cmd !== null) {
             cmd = to_jsstring(cmd);
diff --git a/src/lstrlib.js b/src/lstrlib.js
index 21609d4..fe59a20 100644
--- a/src/lstrlib.js
+++ b/src/lstrlib.js
@@ -131,7 +131,7 @@ const str_char = function(L) {
     let p = luaL_buffinitsize(L, b, n);
     for (let i = 1; i <= n; i++) {
         let c = luaL_checkinteger(L, i);
-        luaL_argcheck(L, c >= 0 && c <= 255, "value out of range"); // Strings are 8-bit clean
+        luaL_argcheck(L, c >= 0 && c <= 255, i, "value out of range"); // Strings are 8-bit clean
         p[i-1] = c;
     }
     luaL_pushresultsize(b, n);
@@ -540,8 +540,7 @@ const getdetails = function(h, totalsize, fmt) {
         else {
             let o = getoption(h, fmt);
             align = o.size;
-            o = o.opt;
-            if (o === Kchar || align === 0)
+            if (o.opt === Kchar || align === 0)
                 luaL_argerror(h.L, 1, to_luastring("invalid next option for option 'X'"));
         }
     }
@@ -809,7 +808,7 @@ const unpacknum = function(L, b, islittle, size) {
 
     let dv = new DataView(new ArrayBuffer(size));
     for (let i = 0; i < size; i++)
-        dv.setUint8(i, b[i], islittle);
+        dv.setUint8(i, b[i]);
 
     if (size == 4) return dv.getFloat32(0, islittle);
     else return dv.getFloat64(0, islittle);
@@ -1390,8 +1389,10 @@ const add_value = function(ms, b, s, e, tr) {
 };
 
 const str_gsub = function(L) {
+    /** @type {any|Uint8Array} */
     let src = luaL_checkstring(L, 1);  /* subject */
     let srcl = src.length;
+    /** @type {number|Uint8Array} */
     let p = luaL_checkstring(L, 2);  /* pattern */
     let lp = p.length;
     let lastmatch = null;  /* end of last match */
diff --git a/src/ltable.js b/src/ltable.js
index fdc34bf..4e62d0b 100644
--- a/src/ltable.js
+++ b/src/ltable.js
@@ -106,9 +106,9 @@ class Table {
         this.id = L.l_G.id_counter++;
         this.strong = new Map();
         this.dead_strong = new Map();
-        this.dead_weak = void 0; /* initialised when needed */
-        this.f = void 0; /* first entry */
-        this.l = void 0; /* last entry */
+        this.dead_weak = null; /* initialised when needed */
+        this.f = null; /* first entry */
+        this.l = null; /* last entry */
         this.metatable = null;
         this.flags = ~0;
     }
diff --git a/src/ltm.js b/src/ltm.js
index a1aa8e3..3949ff5 100644
--- a/src/ltm.js
+++ b/src/ltm.js
@@ -71,30 +71,30 @@ const TMS = {
 };
 
 const luaT_init = function(L) {
-    L.l_G.tmname[TMS.TM_INDEX]    = new luaS_new(L, to_luastring("__index", true));
-    L.l_G.tmname[TMS.TM_NEWINDEX] = new luaS_new(L, to_luastring("__newindex", true));
-    L.l_G.tmname[TMS.TM_GC]       = new luaS_new(L, to_luastring("__gc", true));
-    L.l_G.tmname[TMS.TM_MODE]     = new luaS_new(L, to_luastring("__mode", true));
-    L.l_G.tmname[TMS.TM_LEN]      = new luaS_new(L, to_luastring("__len", true));
-    L.l_G.tmname[TMS.TM_EQ]       = new luaS_new(L, to_luastring("__eq", true));
-    L.l_G.tmname[TMS.TM_ADD]      = new luaS_new(L, to_luastring("__add", true));
-    L.l_G.tmname[TMS.TM_SUB]      = new luaS_new(L, to_luastring("__sub", true));
-    L.l_G.tmname[TMS.TM_MUL]      = new luaS_new(L, to_luastring("__mul", true));
-    L.l_G.tmname[TMS.TM_MOD]      = new luaS_new(L, to_luastring("__mod", true));
-    L.l_G.tmname[TMS.TM_POW]      = new luaS_new(L, to_luastring("__pow", true));
-    L.l_G.tmname[TMS.TM_DIV]      = new luaS_new(L, to_luastring("__div", true));
-    L.l_G.tmname[TMS.TM_IDIV]     = new luaS_new(L, to_luastring("__idiv", true));
-    L.l_G.tmname[TMS.TM_BAND]     = new luaS_new(L, to_luastring("__band", true));
-    L.l_G.tmname[TMS.TM_BOR]      = new luaS_new(L, to_luastring("__bor", true));
-    L.l_G.tmname[TMS.TM_BXOR]     = new luaS_new(L, to_luastring("__bxor", true));
-    L.l_G.tmname[TMS.TM_SHL]      = new luaS_new(L, to_luastring("__shl", true));
-    L.l_G.tmname[TMS.TM_SHR]      = new luaS_new(L, to_luastring("__shr", true));
-    L.l_G.tmname[TMS.TM_UNM]      = new luaS_new(L, to_luastring("__unm", true));
-    L.l_G.tmname[TMS.TM_BNOT]     = new luaS_new(L, to_luastring("__bnot", true));
-    L.l_G.tmname[TMS.TM_LT]       = new luaS_new(L, to_luastring("__lt", true));
-    L.l_G.tmname[TMS.TM_LE]       = new luaS_new(L, to_luastring("__le", true));
-    L.l_G.tmname[TMS.TM_CONCAT]   = new luaS_new(L, to_luastring("__concat", true));
-    L.l_G.tmname[TMS.TM_CALL]     = new luaS_new(L, to_luastring("__call", true));
+    L.l_G.tmname[TMS.TM_INDEX]    = luaS_new(L, to_luastring("__index", true));
+    L.l_G.tmname[TMS.TM_NEWINDEX] = luaS_new(L, to_luastring("__newindex", true));
+    L.l_G.tmname[TMS.TM_GC]       = luaS_new(L, to_luastring("__gc", true));
+    L.l_G.tmname[TMS.TM_MODE]     = luaS_new(L, to_luastring("__mode", true));
+    L.l_G.tmname[TMS.TM_LEN]      = luaS_new(L, to_luastring("__len", true));
+    L.l_G.tmname[TMS.TM_EQ]       = luaS_new(L, to_luastring("__eq", true));
+    L.l_G.tmname[TMS.TM_ADD]      = luaS_new(L, to_luastring("__add", true));
+    L.l_G.tmname[TMS.TM_SUB]      = luaS_new(L, to_luastring("__sub", true));
+    L.l_G.tmname[TMS.TM_MUL]      = luaS_new(L, to_luastring("__mul", true));
+    L.l_G.tmname[TMS.TM_MOD]      = luaS_new(L, to_luastring("__mod", true));
+    L.l_G.tmname[TMS.TM_POW]      = luaS_new(L, to_luastring("__pow", true));
+    L.l_G.tmname[TMS.TM_DIV]      = luaS_new(L, to_luastring("__div", true));
+    L.l_G.tmname[TMS.TM_IDIV]     = luaS_new(L, to_luastring("__idiv", true));
+    L.l_G.tmname[TMS.TM_BAND]     = luaS_new(L, to_luastring("__band", true));
+    L.l_G.tmname[TMS.TM_BOR]      = luaS_new(L, to_luastring("__bor", true));
+    L.l_G.tmname[TMS.TM_BXOR]     = luaS_new(L, to_luastring("__bxor", true));
+    L.l_G.tmname[TMS.TM_SHL]      = luaS_new(L, to_luastring("__shl", true));
+    L.l_G.tmname[TMS.TM_SHR]      = luaS_new(L, to_luastring("__shr", true));
+    L.l_G.tmname[TMS.TM_UNM]      = luaS_new(L, to_luastring("__unm", true));
+    L.l_G.tmname[TMS.TM_BNOT]     = luaS_new(L, to_luastring("__bnot", true));
+    L.l_G.tmname[TMS.TM_LT]       = luaS_new(L, to_luastring("__lt", true));
+    L.l_G.tmname[TMS.TM_LE]       = luaS_new(L, to_luastring("__le", true));
+    L.l_G.tmname[TMS.TM_CONCAT]   = luaS_new(L, to_luastring("__concat", true));
+    L.l_G.tmname[TMS.TM_CALL]     = luaS_new(L, to_luastring("__call", true));
 };
 
 /*
diff --git a/src/lvm.js b/src/lvm.js
index 423d2a7..78f5048 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -595,6 +595,7 @@ const luaV_execute = function(L) {
                 let init = L.stack[ra];
                 let plimit = L.stack[ra + 1];
                 let pstep = L.stack[ra + 2];
+                /** @type {any} */
                 let forlim;
 
                 if (init.ttisinteger() && pstep.ttisinteger() && (forlim = forlimit(plimit, pstep.value))) {
diff --git a/test/lapi.test.js b/test/lapi.test.js
index 399155a..70570d1 100644
--- a/test/lapi.test.js
+++ b/test/lapi.test.js
@@ -57,7 +57,8 @@ test('lua_pushliteral', () => {
     if (!L) throw Error("failed to create lua state");
 
     {
-        lua.lua_pushliteral(L, "hello");
+        expect(lua.lua_pushliteral(L, "hello"))
+            .toBeInstanceOf(Uint8Array);
     }
 
     expect(lauxlib.luaL_typename(L, -1))
diff --git a/test/lauxlib.test.js b/test/lauxlib.test.js
index 62198ac..df9d816 100644
--- a/test/lauxlib.test.js
+++ b/test/lauxlib.test.js
@@ -2,7 +2,7 @@
 
 const lua = require('../src/lua.js');
 const lauxlib = require("../src/lauxlib.js");
-const {to_luastring} = require("../src/fengaricore.js");
+const {to_jsstring, to_luastring} = require("../src/fengaricore.js");
 
 
 test('luaL_ref, lua_rawgeti, luaL_unref, LUA_REGISTRYINDEX', () => {
@@ -20,3 +20,18 @@ test('luaL_ref, lua_rawgeti, luaL_unref, LUA_REGISTRYINDEX', () => {
     expect(lua.lua_tojsstring(L, -1))
         .toBe("hello references!");
 });
+
+
+test('luaL_checkstring', () => {
+    let L = lauxlib.luaL_newstate();
+    if (!L) throw Error("failed to create lua state");
+
+    {
+        lua.lua_pushstring(L, to_luastring("hello!"));
+    }
+
+    expect(lauxlib.luaL_checkstring(L, -1))
+        .toBeInstanceOf(Uint8Array);
+    expect(to_jsstring(lauxlib.luaL_checkstring(L, -1)))
+        .toBe("hello!");
+});
diff --git a/tsconfig.json b/tsconfig.json
index 6465c60..afac56e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -5,6 +5,7 @@
     "declaration": true,
     "emitDeclarationOnly": true,
     "outDir": "dist",
-    "declarationMap": true
+    "declarationMap": true,
+    "checkJs": true
   }
 }
diff --git a/yarn.lock b/yarn.lock
index 0f5de9c..cc8ff9b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -319,6 +319,11 @@
   resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.3.tgz#7c6b0f8eaf16ae530795de2ad1b85d34bf2f5c58"
   integrity sha512-wp6IOGu1lxsfnrD+5mX6qwSwWuqsdkKKxTN4aQc4wByHAKZJf9/D4KXPQ1POUjEbnCP5LMggB0OEFNY9OTsMqg==
 
+"@types/node@^18.16.1":
+  version "18.16.1"
+  resolved "https://registry.npmmirror.com/@types/node/-/node-18.16.1.tgz#5db121e9c5352925bb1f1b892c4ae620e3526799"
+  integrity sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA==
+
 "@types/stack-utils@^1.0.1":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"

From 7523f530f7c39961f90b96800c7c74ad90578905 Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Fri, 28 Apr 2023 01:34:04 +0800
Subject: [PATCH 10/14] Reverse several unnecessary changes

- Define new variables when reusing older ones confuse tsc
- Pass buffers to nodejs functions to avoid decoding errors
- Refine type info and rid @ts-ignore
---
 src/defs.js    | 84 ++++++++++++++++++++++++++++----------------------
 src/lapi.js    |  2 +-
 src/loadlib.js |  1 -
 src/lobject.js |  3 +-
 src/loslib.js  | 31 +++++++++++--------
 src/lvm.js     |  3 +-
 tsconfig.json  |  4 ++-
 7 files changed, 73 insertions(+), 55 deletions(-)

diff --git a/src/defs.js b/src/defs.js
index 454558b..583b601 100644
--- a/src/defs.js
+++ b/src/defs.js
@@ -5,7 +5,7 @@
  */
 
 /**
- * Converts a JavaScript string into a Uint8Array (used a Lua string).
+ * Converts a char array into a Uint8Array.
  *
  * @type {function(ArrayLike<number>):Uint8Array}
  */
@@ -34,7 +34,7 @@ if (typeof (new Uint8Array().indexOf) === "function") {
     };
 } else {
     /* Browsers that don't support Uint8Array.indexOf seem to allow using Array.indexOf on Uint8Array objects e.g. IE11 */
-    let array_indexOf = [0].indexOf;
+    let array_indexOf = Array.prototype.indexOf;
     if (array_indexOf.call(new Uint8Array(1), 0) !== 0) throw Error("missing .indexOf");
     luastring_indexOf = function(s, v, i) {
         return array_indexOf.call(s, v, i);
@@ -231,8 +231,7 @@ const to_luastring = function(str, cache) {
     }
 
     let len = str.length;
-    /** @type {Array<number> | Uint8Array} */
-    let outU8Array = Array(len); /* array is at *least* going to be length of string */
+    const outU8Array = Array(len); /* array is at *least* going to be length of string */
     let outIdx = 0;
     for (let i = 0; i < len; ++i) {
         let u = str.charCodeAt(i);
@@ -264,11 +263,11 @@ const to_luastring = function(str, cache) {
             }
         }
     }
-    outU8Array = luastring_from(outU8Array);
+    const converted = luastring_from(outU8Array);
 
-    if (cache) to_luastring_cache[str] = outU8Array;
+    if (cache) to_luastring_cache[str] = converted;
 
-    return outU8Array;
+    return converted;
 };
 
 /**
@@ -281,14 +280,13 @@ const to_luastring = function(str, cache) {
  * @returns {Uint8Array}
  */
 const from_userstring = function(str) {
-    if (!is_luastring(str)) {
+    if (!(str instanceof Uint8Array)) {
         if (typeof str === "string") {
             str = to_luastring(str);
         } else {
             throw new TypeError("expects an array of bytes or javascript string");
         }
     }
-    // @ts-ignore
     return str;
 };
 
@@ -336,37 +334,49 @@ const thread_status = {
     LUA_ERRERR:    6
 };
 
-const constant_types = {
-    LUA_TNONE:          -1,
-    LUA_TNIL:           0,
-    LUA_TBOOLEAN:       1,
-    LUA_TLIGHTUSERDATA: 2,
-    LUA_TNUMBER:        3,
-    LUA_TSTRING:        4,
-    LUA_TTABLE:         5,
-    LUA_TFUNCTION:      6,
-    LUA_TUSERDATA:      7,
-    LUA_TTHREAD:        8,
-    LUA_NUMTAGS:        9,
-
-    LUA_TSHRSTR:        0,
-    LUA_TLNGSTR:        0,
-    LUA_TNUMFLT:        0,
-    LUA_TNUMINT:        0,
-    LUA_TLCL:           0,
-    LUA_TLCF:           0,
-    LUA_TCCL:           0,
-};
+const LUA_TNONE          = -1;
+const LUA_TNIL           = 0;
+const LUA_TBOOLEAN       = 1;
+const LUA_TLIGHTUSERDATA = 2;
+const LUA_TNUMBER        = 3;
+const LUA_TSTRING        = 4;
+const LUA_TTABLE         = 5;
+const LUA_TFUNCTION      = 6;
+const LUA_TUSERDATA      = 7;
+const LUA_TTHREAD        = 8;
+const LUA_NUMTAGS        = 9;
+
+const LUA_TSHRSTR = LUA_TSTRING | (0 << 4);  /* short strings */
+const LUA_TLNGSTR = LUA_TSTRING | (1 << 4);  /* long strings */
 
-constant_types.LUA_TSHRSTR = constant_types.LUA_TSTRING | (0 << 4);  /* short strings */
-constant_types.LUA_TLNGSTR = constant_types.LUA_TSTRING | (1 << 4);  /* long strings */
+const LUA_TNUMFLT = LUA_TNUMBER | (0 << 4);  /* float numbers */
+const LUA_TNUMINT = LUA_TNUMBER | (1 << 4);  /* integer numbers */
 
-constant_types.LUA_TNUMFLT = constant_types.LUA_TNUMBER | (0 << 4);  /* float numbers */
-constant_types.LUA_TNUMINT = constant_types.LUA_TNUMBER | (1 << 4);  /* integer numbers */
+const LUA_TLCL = LUA_TFUNCTION | (0 << 4);  /* Lua closure */
+const LUA_TLCF = LUA_TFUNCTION | (1 << 4);  /* light C function */
+const LUA_TCCL = LUA_TFUNCTION | (2 << 4);  /* C closure */
 
-constant_types.LUA_TLCL = constant_types.LUA_TFUNCTION | (0 << 4);  /* Lua closure */
-constant_types.LUA_TLCF = constant_types.LUA_TFUNCTION | (1 << 4);  /* light C function */
-constant_types.LUA_TCCL = constant_types.LUA_TFUNCTION | (2 << 4);  /* C closure */
+const constant_types = {
+    LUA_TNONE:          LUA_TNONE,
+    LUA_TNIL:           LUA_TNIL,
+    LUA_TBOOLEAN:       LUA_TBOOLEAN,
+    LUA_TLIGHTUSERDATA: LUA_TLIGHTUSERDATA,
+    LUA_TNUMBER:        LUA_TNUMBER,
+    LUA_TSTRING:        LUA_TSTRING,
+    LUA_TTABLE:         LUA_TTABLE,
+    LUA_TFUNCTION:      LUA_TFUNCTION,
+    LUA_TUSERDATA:      LUA_TUSERDATA,
+    LUA_TTHREAD:        LUA_TTHREAD,
+    LUA_NUMTAGS:        LUA_NUMTAGS,
+
+    LUA_TSHRSTR:        LUA_TSHRSTR,
+    LUA_TLNGSTR:        LUA_TLNGSTR,
+    LUA_TNUMFLT:        LUA_TNUMFLT,
+    LUA_TNUMINT:        LUA_TNUMINT,
+    LUA_TLCL:           LUA_TLCL,
+    LUA_TLCF:           LUA_TLCF,
+    LUA_TCCL:           LUA_TCCL,
+};
 
 /*
 ** Comparison and arithmetic functions
diff --git a/src/lapi.js b/src/lapi.js
index 4e3b46a..e50080f 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -406,7 +406,7 @@ const lua_pushvfstring = function (L, fmt, argp) {
 /**
  * @param {lua_State} L
  * @param {string|Uint8Array} fmt
- * @param {any} argp
+ * @param {any[]} argp
  * @returns {any}
  */
 const lua_pushfstring = function (L, fmt, ...argp) {
diff --git a/src/loadlib.js b/src/loadlib.js
index 56848f7..92bf677 100644
--- a/src/loadlib.js
+++ b/src/loadlib.js
@@ -79,7 +79,6 @@ const global_env = (function() {
     } else if (typeof window !== "undefined") {
         /* browser window */
         return window;
-        // @ts-ignore
     } else if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
         /* web worker */
         return self;
diff --git a/src/lobject.js b/src/lobject.js
index 4db914e..c6be413 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -538,6 +538,7 @@ const l_str2int = function(s) {
     let i = 0;
     let a = 0;
     let empty = true;
+    /** @type {boolean|any} */
     let neg;
 
     while (lisspace(s[i])) i++;  /* skip initial spaces */
@@ -552,7 +553,7 @@ const l_str2int = function(s) {
     } else {  /* decimal */
         for (; i < s.length && lisdigit(s[i]); i++) {
             let d = s[i] - 48 /* ('0').charCodeAt(0) */;
-            if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + (neg ? 1 : 0)))  /* overflow? */
+            if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg))  /* overflow? */
                 return null;  /* do not accept it (as integer) */
             a = (a * 10 + d)|0;
             empty = false;
diff --git a/src/loslib.js b/src/loslib.js
index 9e67e36..9dbaf1d 100644
--- a/src/loslib.js
+++ b/src/loslib.js
@@ -92,6 +92,13 @@ const setallfields = function(L, time, utc) {
 
 const L_MAXDATEFIELD = (Number.MAX_SAFE_INTEGER / 2);
 
+/**
+ * @param {import('./lstate').lua_State} L
+ * @param {string} key
+ * @param {number} d
+ * @param {number} delta
+ * @returns
+ */
 const getfield = function(L, key, d, delta) {
     let t = lua_getfield(L, -1, to_luastring(key, true));  /* get field and its type */
     let res = lua_tointegerx(L, -1);
@@ -426,12 +433,12 @@ const os_time = function(L) {
         luaL_checktype(L, 1, LUA_TTABLE);
         lua_settop(L, 1);  /* make sure table is at the top */
         t = new Date(
-            Number(getfield(L, "year", -1, 0)),
-            Number(getfield(L, "month", -1, 1)),
-            Number(getfield(L, "day", -1, 0)),
-            Number(getfield(L, "hour", 12, 0)),
-            Number(getfield(L, "min", 0, 0)),
-            Number(getfield(L, "sec", 0, 0))
+            getfield(L, "year", -1, 0),
+            getfield(L, "month", -1, 1),
+            getfield(L, "day", -1, 0),
+            getfield(L, "hour", 12, 0),
+            getfield(L, "min", 0, 0),
+            getfield(L, "sec", 0, 0)
         );
         setallfields(L, t);
     }
@@ -523,13 +530,13 @@ if (typeof process === "undefined") {
     };
 
     syslib.remove = function(L) {
-        let filename = to_jsstring(luaL_checkstring(L, 1));
+        let filename = luaL_checkstring(L, 1);
         try {
-            fs.unlinkSync(filename);
+            fs.unlinkSync(Buffer.from(filename));
         } catch (e) {
             if (e.code === 'EISDIR') {
                 try {
-                    fs.rmdirSync(filename);
+                    fs.rmdirSync(Buffer.from(filename));
                 } catch (e) {
                     return luaL_fileresult(L, false, filename, e);
                 }
@@ -541,10 +548,10 @@ if (typeof process === "undefined") {
     };
 
     syslib.rename = function(L) {
-        let fromname = to_jsstring(luaL_checkstring(L, 1));
-        let toname = to_jsstring(luaL_checkstring(L, 2));
+        let fromname = luaL_checkstring(L, 1);
+        let toname = luaL_checkstring(L, 2);
         try {
-            fs.renameSync(fromname, toname);
+            fs.renameSync(Buffer.from(fromname), Buffer.from(toname));
         } catch (e) {
             return luaL_fileresult(L, false, false, e);
         }
diff --git a/src/lvm.js b/src/lvm.js
index 78f5048..c02335e 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -595,7 +595,6 @@ const luaV_execute = function(L) {
                 let init = L.stack[ra];
                 let plimit = L.stack[ra + 1];
                 let pstep = L.stack[ra + 2];
-                /** @type {any} */
                 let forlim;
 
                 if (init.ttisinteger() && pstep.ttisinteger() && (forlim = forlimit(plimit, pstep.value))) {
@@ -800,7 +799,7 @@ const forlimit = function(obj, step) {
     if (ilimit === false) {
         let n = tonumber(obj);
         if (n === false)
-            return false;
+            return null;
 
         if (0 < n) {
             ilimit = LUA_MAXINTEGER;
diff --git a/tsconfig.json b/tsconfig.json
index afac56e..f7d9569 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,6 +6,8 @@
     "emitDeclarationOnly": true,
     "outDir": "dist",
     "declarationMap": true,
-    "checkJs": true
+    "checkJs": true,
+    "lib": ["DOM", "WebWorker", "ES2015"],
+    "skipLibCheck": true
   }
 }

From 81472fba051dcce2b2be7ce4c4d2e70c64164502 Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Fri, 28 Apr 2023 10:46:45 +0800
Subject: [PATCH 11/14] Test type info with test files

---
 package.json              |   3 +-
 test/ldblib.test.js       |   3 +
 test/lstrlib.test.js      |   4 +-
 test/test-suite/ltests.js |  10 +-
 tsconfig.test.json        |   8 ++
 yarn.lock                 | 288 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 307 insertions(+), 9 deletions(-)
 create mode 100644 tsconfig.test.json

diff --git a/package.json b/package.json
index ea5fd96..f2658bc 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
     "emit": "tsc",
     "lint": "eslint src/ test/",
     "prepublishOnly": "git diff-index --quiet --cached HEAD -- && npm run lint && npm run test",
-    "test": "npm run emit && jest"
+    "test": "tsc -p ./tsconfig.test.json && jest"
   },
   "repository": {
     "type": "git",
@@ -32,6 +32,7 @@
   },
   "homepage": "https://github.com/fengari-lua/fengari#readme",
   "devDependencies": {
+    "@types/jest": "^29.5.1",
     "@types/node": "^18.16.1",
     "eslint": "^5.15.1",
     "jest": "^24.5.0",
diff --git a/test/ldblib.test.js b/test/ldblib.test.js
index ba68853..d33abef 100644
--- a/test/ldblib.test.js
+++ b/test/ldblib.test.js
@@ -194,6 +194,7 @@ test('debug.traceback (with a global)', () => {
     let L = lauxlib.luaL_newstate();
     if (!L) throw Error("failed to create lua state");
 
+    /** @type {string|Uint8Array} */
     let luaCode = `
         local trace
 
@@ -238,6 +239,7 @@ test('debug.traceback (with a upvalue)', () => {
     let L = lauxlib.luaL_newstate();
     if (!L) throw Error("failed to create lua state");
 
+    /** @type {string|Uint8Array} */
     let luaCode = `
         local trace
         local rec
@@ -282,6 +284,7 @@ test('debug.getinfo', () => {
     let L = lauxlib.luaL_newstate();
     if (!L) throw Error("failed to create lua state");
 
+    /** @type {string|Uint8Array} */
     let luaCode = `
         local alocal = function(p1, p2) end
         global = function() return alocal end
diff --git a/test/lstrlib.test.js b/test/lstrlib.test.js
index 3ae2ec3..e41495f 100644
--- a/test/lstrlib.test.js
+++ b/test/lstrlib.test.js
@@ -159,9 +159,7 @@ test('string.format', () => {
         lua.lua_call(L, 0, -1);
     }
 
-    expect(lua.lua_tojsstring(L, -1)).toBe('"a string with \\"quotes\\" and \\\n new line"',
-        "Correct element(s) on the stack"
-    );
+    expect(lua.lua_tojsstring(L, -1)).toBe('"a string with \\"quotes\\" and \\\n new line"');
 });
 
 
diff --git a/test/test-suite/ltests.js b/test/test-suite/ltests.js
index 6fc086c..02f1453 100644
--- a/test/test-suite/ltests.js
+++ b/test/test-suite/ltests.js
@@ -171,6 +171,7 @@ const runJS = function(L, L1, pc) {
                 break;
             }
             case "func2num": {
+                /** @type {any} */
                 let func = lua.lua_tocfunction(L1, getindex(L, L1, pc));
                 if (func === null) func = 0;
                 else if (func.id) func = func.id;
@@ -187,7 +188,7 @@ const runJS = function(L, L1, pc) {
                 break;
             }
             case "getmetatable": {
-                if (lua.lua_getmetatable(L1, getindex(L, L1, pc)) === 0)
+                if (lua.lua_getmetatable(L1, getindex(L, L1, pc)) === false)
                     lua.lua_pushnil(L1);
                 break;
             }
@@ -307,7 +308,7 @@ const runJS = function(L, L1, pc) {
             case "print": {
                 let n = getnum(L, L1, pc);
                 if (n !== 0) {
-                    console.log(`${lauxlib.luaL_tojsstring(L1, n, null)}\n`);
+                    console.log(`${lua.lua_tojsstring(L1, n)}\n`);
                     lua.lua_pop(L1, 1);
                 }
                 else printstack(L1);
@@ -607,11 +608,10 @@ const closestate = function(L) {
 
 const doremote = function(L) {
     let L1 = getstate(L);
-    let lcode;
-    let code = lauxlib.luaL_checklstring(L, 2, lcode);
+    let code = lauxlib.luaL_checklstring(L, 2);
     let status;
     lua.lua_settop(L1, 0);
-    status = lauxlib.luaL_loadbuffer(L1, code, lcode, code);
+    status = lauxlib.luaL_loadbuffer(L1, code, code.length, code);
     if (status === lua.LUA_OK)
         status = lua.lua_pcall(L1, 0, lua.LUA_MULTRET, 0);
     if (status !== lua.LUA_OK) {
diff --git a/tsconfig.test.json b/tsconfig.test.json
new file mode 100644
index 0000000..2560a3d
--- /dev/null
+++ b/tsconfig.test.json
@@ -0,0 +1,8 @@
+{
+  "include": ["src/**/*", "test/**/*"],
+  "compilerOptions": {
+    "noEmit": true,
+    "emitDeclarationOnly": false,
+  },
+  "extends": "./tsconfig.json",
+}
diff --git a/yarn.lock b/yarn.lock
index cc8ff9b..a5dec95 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9,6 +9,13 @@
   dependencies:
     "@babel/highlight" "^7.0.0"
 
+"@babel/code-frame@^7.12.13":
+  version "7.21.4"
+  resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39"
+  integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==
+  dependencies:
+    "@babel/highlight" "^7.18.6"
+
 "@babel/core@^7.1.0":
   version "7.3.4"
   resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.3.4.tgz#921a5a13746c21e32445bf0798680e9d11a6530b"
@@ -68,6 +75,11 @@
   dependencies:
     "@babel/types" "^7.0.0"
 
+"@babel/helper-validator-identifier@^7.18.6":
+  version "7.19.1"
+  resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
+  integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
+
 "@babel/helpers@^7.2.0":
   version "7.3.1"
   resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.3.1.tgz#949eec9ea4b45d3210feb7dc1c22db664c9e44b9"
@@ -86,6 +98,15 @@
     esutils "^2.0.2"
     js-tokens "^4.0.0"
 
+"@babel/highlight@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
+  integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.18.6"
+    chalk "^2.0.0"
+    js-tokens "^4.0.0"
+
 "@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.2.2", "@babel/parser@^7.3.4":
   version "7.3.4"
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.4.tgz#a43357e4bbf4b92a437fb9e465c192848287f27c"
@@ -193,6 +214,13 @@
     "@types/node" "*"
     jest-mock "^24.5.0"
 
+"@jest/expect-utils@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.npmmirror.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036"
+  integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==
+  dependencies:
+    jest-get-type "^29.4.3"
+
 "@jest/fake-timers@^24.5.0":
   version "24.5.0"
   resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.5.0.tgz#4a29678b91fd0876144a58f8d46e6c62de0266f0"
@@ -229,6 +257,13 @@
     source-map "^0.6.0"
     string-length "^2.0.0"
 
+"@jest/schemas@^29.4.3":
+  version "29.4.3"
+  resolved "https://registry.npmmirror.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788"
+  integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==
+  dependencies:
+    "@sinclair/typebox" "^0.25.16"
+
 "@jest/source-map@^24.3.0":
   version "24.3.0"
   resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.3.0.tgz#563be3aa4d224caf65ff77edc95cd1ca4da67f28"
@@ -276,6 +311,23 @@
     "@types/istanbul-lib-coverage" "^1.1.0"
     "@types/yargs" "^12.0.9"
 
+"@jest/types@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.npmmirror.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593"
+  integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==
+  dependencies:
+    "@jest/schemas" "^29.4.3"
+    "@types/istanbul-lib-coverage" "^2.0.0"
+    "@types/istanbul-reports" "^3.0.0"
+    "@types/node" "*"
+    "@types/yargs" "^17.0.8"
+    chalk "^4.0.0"
+
+"@sinclair/typebox@^0.25.16":
+  version "0.25.24"
+  resolved "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718"
+  integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==
+
 "@types/babel__core@^7.1.0":
   version "7.1.0"
   resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.0.tgz#710f2487dda4dcfd010ca6abb2b4dc7394365c51"
@@ -309,11 +361,38 @@
   dependencies:
     "@babel/types" "^7.3.0"
 
+"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
+  version "2.0.4"
+  resolved "https://registry.npmmirror.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44"
+  integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==
+
 "@types/istanbul-lib-coverage@^1.1.0":
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.0.tgz#2cc2ca41051498382b43157c8227fea60363f94a"
   integrity sha512-ohkhb9LehJy+PA40rDtGAji61NCgdtKLAlFoYp4cnuuQEswwdK3vz9SOIkkyc3wrk8dzjphQApNs56yyXLStaQ==
 
+"@types/istanbul-lib-report@*":
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686"
+  integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==
+  dependencies:
+    "@types/istanbul-lib-coverage" "*"
+
+"@types/istanbul-reports@^3.0.0":
+  version "3.0.1"
+  resolved "https://registry.npmmirror.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff"
+  integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==
+  dependencies:
+    "@types/istanbul-lib-report" "*"
+
+"@types/jest@^29.5.1":
+  version "29.5.1"
+  resolved "https://registry.npmmirror.com/@types/jest/-/jest-29.5.1.tgz#83c818aa9a87da27d6da85d3378e5a34d2f31a47"
+  integrity sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==
+  dependencies:
+    expect "^29.0.0"
+    pretty-format "^29.0.0"
+
 "@types/node@*":
   version "11.11.3"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.3.tgz#7c6b0f8eaf16ae530795de2ad1b85d34bf2f5c58"
@@ -329,11 +408,28 @@
   resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
   integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
 
+"@types/stack-utils@^2.0.0":
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
+  integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
+
+"@types/yargs-parser@*":
+  version "21.0.0"
+  resolved "https://registry.npmmirror.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b"
+  integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==
+
 "@types/yargs@^12.0.2", "@types/yargs@^12.0.9":
   version "12.0.9"
   resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.9.tgz#693e76a52f61a2f1e7fb48c0eef167b95ea4ffd0"
   integrity sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA==
 
+"@types/yargs@^17.0.8":
+  version "17.0.24"
+  resolved "https://registry.npmmirror.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902"
+  integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==
+  dependencies:
+    "@types/yargs-parser" "*"
+
 abab@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f"
@@ -404,6 +500,18 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
   dependencies:
     color-convert "^1.9.0"
 
+ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
+ansi-styles@^5.0.0:
+  version "5.2.0"
+  resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
+  integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
+
 anymatch@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@@ -596,6 +704,13 @@ braces@^2.3.1:
     split-string "^3.0.2"
     to-regex "^3.0.1"
 
+braces@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+  integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+  dependencies:
+    fill-range "^7.0.1"
+
 browser-process-hrtime@^0.1.2:
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4"
@@ -666,6 +781,14 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.2:
     escape-string-regexp "^1.0.5"
     supports-color "^5.3.0"
 
+chalk@^4.0.0:
+  version "4.1.2"
+  resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
 chardet@^0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
@@ -676,6 +799,11 @@ ci-info@^2.0.0:
   resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
   integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
 
+ci-info@^3.2.0:
+  version "3.8.0"
+  resolved "https://registry.npmmirror.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91"
+  integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==
+
 class-utils@^0.3.5:
   version "0.3.6"
   resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
@@ -732,11 +860,23 @@ color-convert@^1.9.0:
   dependencies:
     color-name "1.1.3"
 
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
 color-name@1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
   integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
 
+color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
 combined-stream@^1.0.6, combined-stream@~1.0.6:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828"
@@ -900,6 +1040,11 @@ diff-sequences@^24.3.0:
   resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.3.0.tgz#0f20e8a1df1abddaf4d9c226680952e64118b975"
   integrity sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==
 
+diff-sequences@^29.4.3:
+  version "29.4.3"
+  resolved "https://registry.npmmirror.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2"
+  integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==
+
 doctrine@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
@@ -967,6 +1112,11 @@ escape-string-regexp@^1.0.5:
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
   integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
 
+escape-string-regexp@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
+  integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
+
 escodegen@^1.9.1:
   version "1.11.1"
   resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.1.tgz#c485ff8d6b4cdb89e27f4a856e91f118401ca510"
@@ -1130,6 +1280,17 @@ expect@^24.5.0:
     jest-message-util "^24.5.0"
     jest-regex-util "^24.3.0"
 
+expect@^29.0.0:
+  version "29.5.0"
+  resolved "https://registry.npmmirror.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7"
+  integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==
+  dependencies:
+    "@jest/expect-utils" "^29.5.0"
+    jest-get-type "^29.4.3"
+    jest-matcher-utils "^29.5.0"
+    jest-message-util "^29.5.0"
+    jest-util "^29.5.0"
+
 extend-shallow@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
@@ -1237,6 +1398,13 @@ fill-range@^4.0.0:
     repeat-string "^1.6.1"
     to-regex-range "^2.1.0"
 
+fill-range@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+  integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+  dependencies:
+    to-regex-range "^5.0.1"
+
 find-up@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
@@ -1345,6 +1513,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2:
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
   integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
 
+graceful-fs@^4.2.9:
+  version "4.2.11"
+  resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+  integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
 growly@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
@@ -1379,6 +1552,11 @@ has-flag@^3.0.0:
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
   integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
 
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
 has-symbols@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
@@ -1629,6 +1807,11 @@ is-number@^3.0.0:
   dependencies:
     kind-of "^3.0.2"
 
+is-number@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
 is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
@@ -1833,6 +2016,16 @@ jest-diff@^24.5.0:
     jest-get-type "^24.3.0"
     pretty-format "^24.5.0"
 
+jest-diff@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.npmmirror.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63"
+  integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==
+  dependencies:
+    chalk "^4.0.0"
+    diff-sequences "^29.4.3"
+    jest-get-type "^29.4.3"
+    pretty-format "^29.5.0"
+
 jest-docblock@^24.3.0:
   version "24.3.0"
   resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.3.0.tgz#b9c32dac70f72e4464520d2ba4aec02ab14db5dd"
@@ -1879,6 +2072,11 @@ jest-get-type@^24.3.0:
   resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.3.0.tgz#582cfd1a4f91b5cdad1d43d2932f816d543c65da"
   integrity sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==
 
+jest-get-type@^29.4.3:
+  version "29.4.3"
+  resolved "https://registry.npmmirror.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5"
+  integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==
+
 jest-haste-map@^24.5.0:
   version "24.5.0"
   resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.5.0.tgz#3f17d0c548b99c0c96ed2893f9c0ccecb2eb9066"
@@ -1933,6 +2131,16 @@ jest-matcher-utils@^24.5.0:
     jest-get-type "^24.3.0"
     pretty-format "^24.5.0"
 
+jest-matcher-utils@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.npmmirror.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5"
+  integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==
+  dependencies:
+    chalk "^4.0.0"
+    jest-diff "^29.5.0"
+    jest-get-type "^29.4.3"
+    pretty-format "^29.5.0"
+
 jest-message-util@^24.5.0:
   version "24.5.0"
   resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.5.0.tgz#181420a65a7ef2e8b5c2f8e14882c453c6d41d07"
@@ -1947,6 +2155,21 @@ jest-message-util@^24.5.0:
     slash "^2.0.0"
     stack-utils "^1.0.1"
 
+jest-message-util@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.npmmirror.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e"
+  integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==
+  dependencies:
+    "@babel/code-frame" "^7.12.13"
+    "@jest/types" "^29.5.0"
+    "@types/stack-utils" "^2.0.0"
+    chalk "^4.0.0"
+    graceful-fs "^4.2.9"
+    micromatch "^4.0.4"
+    pretty-format "^29.5.0"
+    slash "^3.0.0"
+    stack-utils "^2.0.3"
+
 jest-mock@^24.5.0:
   version "24.5.0"
   resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.5.0.tgz#976912c99a93f2a1c67497a9414aa4d9da4c7b76"
@@ -2080,6 +2303,18 @@ jest-util@^24.5.0:
     slash "^2.0.0"
     source-map "^0.6.0"
 
+jest-util@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.npmmirror.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f"
+  integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==
+  dependencies:
+    "@jest/types" "^29.5.0"
+    "@types/node" "*"
+    chalk "^4.0.0"
+    ci-info "^3.2.0"
+    graceful-fs "^4.2.9"
+    picomatch "^2.2.3"
+
 jest-validate@^24.5.0:
   version "24.5.0"
   resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.5.0.tgz#62fd93d81214c070bb2d7a55f329a79d8057c7de"
@@ -2377,6 +2612,14 @@ micromatch@^3.1.10, micromatch@^3.1.4:
     snapdragon "^0.8.1"
     to-regex "^3.0.2"
 
+micromatch@^4.0.4:
+  version "4.0.5"
+  resolved "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+  integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+  dependencies:
+    braces "^3.0.2"
+    picomatch "^2.3.1"
+
 mime-db@~1.38.0:
   version "1.38.0"
   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad"
@@ -2730,6 +2973,11 @@ performance-now@^2.1.0:
   resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
   integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
 
+picomatch@^2.2.3, picomatch@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
 pify@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
@@ -2774,6 +3022,15 @@ pretty-format@^24.5.0:
     ansi-styles "^3.2.0"
     react-is "^16.8.4"
 
+pretty-format@^29.0.0, pretty-format@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.npmmirror.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a"
+  integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==
+  dependencies:
+    "@jest/schemas" "^29.4.3"
+    ansi-styles "^5.0.0"
+    react-is "^18.0.0"
+
 process-nextick-args@~2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
@@ -2825,6 +3082,11 @@ react-is@^16.8.4:
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.4.tgz#90f336a68c3a29a096a3d648ab80e87ec61482a2"
   integrity sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA==
 
+react-is@^18.0.0:
+  version "18.2.0"
+  resolved "https://registry.npmmirror.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
+  integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
+
 read-pkg-up@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978"
@@ -3119,6 +3381,11 @@ slash@^2.0.0:
   resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
   integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
 
+slash@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
+  integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+
 slice-ansi@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636"
@@ -3255,6 +3522,13 @@ stack-utils@^1.0.1:
   resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8"
   integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==
 
+stack-utils@^2.0.3:
+  version "2.0.6"
+  resolved "https://registry.npmmirror.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f"
+  integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==
+  dependencies:
+    escape-string-regexp "^2.0.0"
+
 static-extend@^0.1.1:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
@@ -3359,6 +3633,13 @@ supports-color@^6.0.0, supports-color@^6.1.0:
   dependencies:
     has-flag "^3.0.0"
 
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
 symbol-tree@^3.2.2:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
@@ -3431,6 +3712,13 @@ to-regex-range@^2.1.0:
     is-number "^3.0.0"
     repeat-string "^1.6.1"
 
+to-regex-range@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  dependencies:
+    is-number "^7.0.0"
+
 to-regex@^3.0.1, to-regex@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"

From 8eec9d522fb383e9b568de1e939f4e254d72e119 Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Fri, 28 Apr 2023 11:17:50 +0800
Subject: [PATCH 12/14] Add a os.rename/remove test

---
 test/loslib.test.js | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/test/loslib.test.js b/test/loslib.test.js
index af7de92..cafdb7f 100644
--- a/test/loslib.test.js
+++ b/test/loslib.test.js
@@ -160,3 +160,31 @@ test('os.getenv', () => {
 
     expect(lua.lua_isstring(L, -1)).toBe(true);
 });
+
+describe('filesystem tests', () => {
+    (process ? it : it.skip)('os.rename, os.remove', () => {
+        expect(typeof lualib.luaopen_io).toBe("function");
+        let L = lauxlib.luaL_newstate();
+        if (!L) throw Error("failed to create lua state");
+
+        {
+            const fs = require("fs");
+            const tmp = require("tmp");
+            const file = tmp.fileSync();
+            lua.lua_pushstring(L, file.name);
+            lua.lua_setglobal(L, "filename");
+            lualib.luaL_openlibs(L);
+
+            expect(lauxlib.luaL_loadstring(L, to_luastring(`assert(os.rename(filename, filename .. '.renamed'))`)))
+                .toBe(lua.LUA_OK);
+            lua.lua_call(L, 0, -1);
+            expect(fs.existsSync(file.name)).toBe(false);
+            expect(fs.existsSync(`${file.name}.renamed`)).toBe(true);
+
+            expect(lauxlib.luaL_loadstring(L, to_luastring(`assert(os.remove(filename .. '.renamed'))`)))
+                .toBe(lua.LUA_OK);
+            lua.lua_call(L, 0, -1);
+            expect(fs.existsSync(`${file.name}.renamed`)).toBe(false);
+        }
+    });
+});

From 4e3dc6a0260d750706ee1cb9ae698ec53a892c77 Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Fri, 28 Apr 2023 11:33:10 +0800
Subject: [PATCH 13/14] Use LuaString as a type alias for Uint8Array

---
 package.json   |  2 +-
 src/defs.js    | 26 ++++++++++++++-----------
 src/lapi.js    | 29 ++++++++++++++-------------
 src/lauxlib.js | 53 +++++++++++++++++++++++++-------------------------
 src/loadlib.js |  3 +--
 src/loslib.js  |  8 ++++++--
 src/lstring.js |  3 ++-
 src/lstrlib.js |  8 ++++++--
 8 files changed, 73 insertions(+), 59 deletions(-)

diff --git a/package.json b/package.json
index f2658bc..aaa2a21 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
   "scripts": {
     "emit": "tsc",
     "lint": "eslint src/ test/",
-    "prepublishOnly": "git diff-index --quiet --cached HEAD -- && npm run lint && npm run test",
+    "prepublishOnly": "git diff-index --quiet --cached HEAD -- && npm run lint && npm run emit && npm run test",
     "test": "tsc -p ./tsconfig.test.json && jest"
   },
   "repository": {
diff --git a/src/defs.js b/src/defs.js
index 583b601..af391ba 100644
--- a/src/defs.js
+++ b/src/defs.js
@@ -5,9 +5,13 @@
  */
 
 /**
- * Converts a char array into a Uint8Array.
+ * @typedef {Uint8Array} LuaString
+ */
+
+/**
+ * Converts a char array into a Lua string.
  *
- * @type {function(ArrayLike<number>):Uint8Array}
+ * @type {function(ArrayLike<number>):LuaString}
  */
 let luastring_from;
 if (typeof Uint8Array.from === "function") {
@@ -25,7 +29,7 @@ if (typeof Uint8Array.from === "function") {
 /**
  * Returns the index of the first occurrence of the character in the Lua string.
  *
- * @type {function(Uint8Array|any[], number, number=):number}
+ * @type {function(LuaString|number[], number, number=):number}
  */
 let luastring_indexOf;
 if (typeof (new Uint8Array().indexOf) === "function") {
@@ -44,7 +48,7 @@ if (typeof (new Uint8Array().indexOf) === "function") {
 /**
  * Constructs a Lua string from characters.
  *
- * @type {function(...number):Uint8Array}
+ * @type {function(...number):LuaString}
  */
 let luastring_of;
 if (typeof Uint8Array.of === "function") {
@@ -67,8 +71,8 @@ const is_luastring = function(s) {
 /**
  * Tests two Lua strings for equality.
  *
- * @param {Uint8Array} a str 1
- * @param {Uint8Array} b str 2
+ * @param {LuaString} a str 1
+ * @param {LuaString} b str 2
  * @returns {boolean}
  */
 const luastring_eq = function(a, b) {
@@ -87,7 +91,7 @@ const unicode_error_message = "cannot convert invalid utf8 to javascript string"
 /**
  * Converts a Lua string (in UTF-8) to a normal JavaScript string.
  *
- * @param {Uint8Array} value the Lua string
+ * @param {LuaString} value the Lua string
  * @param {number} [from] the staring index
  * @param {number} [to] the ending index
  * @param {boolean} [replacement_char] whether to replace invalid utf8 chars
@@ -198,7 +202,7 @@ const uri_allowed = (";,/?:@&=+$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV
 /**
  * Utility function to convert a Lua string to a JavaScript string with URI escaping.
  *
- * @param {Uint8Array} a the string
+ * @param {LuaString} a the string
  * @returns {string}
  */
 const to_uristring = function(a) {
@@ -220,7 +224,7 @@ const to_luastring_cache = {};
 /**
  * @param {string} str
  * @param {boolean} [cache]
- * @returns {Uint8Array}
+ * @returns {LuaString}
  */
 const to_luastring = function(str, cache) {
     if (typeof str !== "string") throw new TypeError("to_luastring expects a javascript string");
@@ -276,8 +280,8 @@ const to_luastring = function(str, cache) {
  * If `str` is already a Lua string, it returns it as is.
  * Otherwise, it tries to convert it.
  *
- * @param {string|Uint8Array} str
- * @returns {Uint8Array}
+ * @param {string|LuaString} str
+ * @returns {LuaString}
  */
 const from_userstring = function(str) {
     if (!(str instanceof Uint8Array)) {
diff --git a/src/lapi.js b/src/lapi.js
index e50080f..ef6cf21 100644
--- a/src/lapi.js
+++ b/src/lapi.js
@@ -2,6 +2,7 @@
 
 /**
  * @typedef {import('./lstate').lua_State} lua_State
+ * @typedef {import('./defs').LuaString} LuaString
  */
 
 const {
@@ -354,9 +355,9 @@ const lua_pushinteger = function(L, n) {
 
 /**
  * @param {lua_State} L
- * @param {string|Uint8Array} s
+ * @param {string|LuaString} s
  * @param {number} len
- * @returns {Uint8Array}
+ * @returns {LuaString}
  */
 const lua_pushlstring = function(L, s, len) {
     fengari_argcheckinteger(len);
@@ -376,7 +377,7 @@ const lua_pushlstring = function(L, s, len) {
 
 /**
  * @param {lua_State} L
- * @param {string|Uint8Array} s
+ * @param {string|LuaString} s
  * @returns {any}
  */
 const lua_pushstring = function (L, s) {
@@ -394,7 +395,7 @@ const lua_pushstring = function (L, s) {
 
 /**
  * @param {lua_State} L
- * @param {string|Uint8Array} fmt
+ * @param {string|LuaString} fmt
  * @param {any[]} argp
  * @returns {any}
  */
@@ -405,7 +406,7 @@ const lua_pushvfstring = function (L, fmt, argp) {
 
 /**
  * @param {lua_State} L
- * @param {string|Uint8Array} fmt
+ * @param {string|LuaString} fmt
  * @param {any[]} argp
  * @returns {any}
  */
@@ -418,7 +419,7 @@ const lua_pushfstring = function (L, fmt, ...argp) {
 /**
  * @param {lua_State} L
  * @param {string?} [s]
- * @returns {Uint8Array?}
+ * @returns {LuaString?}
  */
 const lua_pushliteral = function (L, s) {
     let arr = null;
@@ -528,7 +529,7 @@ const auxsetstr = function(L, t, k) {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} name
+ * @param {string|LuaString} name
  */
 const lua_setglobal = function(L, name) {
     auxsetstr(L, ltable.luaH_getint(L.l_G.l_registry.value, LUA_RIDX_GLOBALS), name);
@@ -580,7 +581,7 @@ const lua_settable = function(L, idx) {
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @param {Uint8Array} k
+ * @param {LuaString} k
  */
 const lua_setfield = function(L, idx, k) {
     auxsetstr(L, index2addr(L, idx), k);
@@ -802,7 +803,7 @@ const lua_newtable = function(L) {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} n
+ * @param {LuaString} n
  * @param {function} f
  */
 const lua_register = function(L, n, f) {
@@ -866,7 +867,7 @@ const lua_gettable = function(L, idx) {
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @param {Uint8Array} k
+ * @param {string|LuaString} k
  * @returns {number}
  */
 const lua_getfield = function(L, idx, k) {
@@ -890,7 +891,7 @@ const lua_geti = function(L, idx, n) {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} name
+ * @param {LuaString} name
  * @returns {number}
  */
 const lua_getglobal = function(L, name) {
@@ -914,7 +915,7 @@ const lua_toboolean = function(L, idx) {
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @returns {Uint8Array?}
+ * @returns {LuaString?}
  */
 const lua_tolstring = function(L, idx) {
     let o = index2addr(L, idx);
@@ -1132,7 +1133,7 @@ const lua_compare = function(L, index1, index2, op) {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} s
+ * @param {LuaString} s
  * @returns {number}
  */
 const lua_stringtonumber = function(L, s) {
@@ -1162,7 +1163,7 @@ const lua_type = function(L, idx) {
 /**
  * @param {lua_State} L
  * @param {number} t
- * @returns {Uint8Array}
+ * @returns {LuaString}
  */
 const lua_typename = function(L, t) {
     api_check(L, LUA_TNONE <= t && t < LUA_NUMTAGS, "invalid tag");
diff --git a/src/lauxlib.js b/src/lauxlib.js
index 35ba417..eb81551 100644
--- a/src/lauxlib.js
+++ b/src/lauxlib.js
@@ -2,6 +2,7 @@
 
 /**
  * @typedef {import('./lstate').lua_State} lua_State
+ * @typedef {import('./defs').LuaString} LuaString
  */
 
 const {
@@ -205,7 +206,7 @@ const lastlevel = function(L) {
 /**
  * @param {lua_State} L
  * @param {lua_State} L1
- * @param {Uint8Array} msg
+ * @param {LuaString} msg
  * @param {number} level
  */
 const luaL_traceback = function(L, L1, msg, level) {
@@ -310,7 +311,7 @@ const luaL_error = function(L, fmt, ...argp) {
 /**
  * @param {lua_State} L
  * @param {any} [stat]
- * @param {string|Uint8Array|false} [fname]
+ * @param {string|LuaString|false} [fname]
  * @param {any} [e]
  * @returns {number}
  */
@@ -368,7 +369,7 @@ const luaL_execresult = function(L, e) {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} n
+ * @param {LuaString} n
  * @returns {number}
  */
 const luaL_getmetatable = function(L, n) {
@@ -377,7 +378,7 @@ const luaL_getmetatable = function(L, n) {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} tname
+ * @param {LuaString} tname
  * @returns {number}
  */
 const luaL_newmetatable = function(L, tname) {
@@ -395,7 +396,7 @@ const luaL_newmetatable = function(L, tname) {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} tname
+ * @param {LuaString} tname
  */
 const luaL_setmetatable = function(L, tname) {
     luaL_getmetatable(L, tname);
@@ -450,7 +451,7 @@ const luaL_newstate = function() {
 /**
  * @param {lua_State} L
  * @param {number} i
- * @returns {Uint8Array}
+ * @returns {LuaString}
  */
 const luaL_typename = function(L, i) {
     return lua_typename(L, lua_type(L, i));
@@ -488,7 +489,7 @@ const luaL_checktype = function(L, arg, t) {
 /**
  * @param {lua_State} L
  * @param {number} arg
- * @returns {Uint8Array}
+ * @returns {LuaString}
  */
 const luaL_checklstring = function(L, arg) {
     let s = lua_tolstring(L, arg);
@@ -501,8 +502,8 @@ const luaL_checkstring = luaL_checklstring;
 /**
  * @param {lua_State} L
  * @param {number} arg
- * @param {string|Uint8Array} [def]
- * @returns {Uint8Array}
+ * @param {string|LuaString} [def]
+ * @returns {LuaString}
  */
 const luaL_optlstring = function(L, arg, def) {
     if (lua_type(L, arg) <= 0) {
@@ -642,10 +643,10 @@ const getS = function(L, ud) {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} buff
+ * @param {LuaString} buff
  * @param {number} size
- * @param {Uint8Array} name
- * @param {Uint8Array} mode
+ * @param {LuaString} name
+ * @param {LuaString} mode
  * @returns {number}
  */
 const luaL_loadbufferx = function(L, buff, size, name, mode) {
@@ -654,9 +655,9 @@ const luaL_loadbufferx = function(L, buff, size, name, mode) {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} s
+ * @param {LuaString} s
  * @param {number} sz
- * @param {Uint8Array} n
+ * @param {LuaString} n
  * @returns {number}
  */
 const luaL_loadbuffer = function(L, s, sz, n) {
@@ -665,7 +666,7 @@ const luaL_loadbuffer = function(L, s, sz, n) {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} s
+ * @param {LuaString} s
  * @returns {number}
  */
 const luaL_loadstring = function(L, s) {
@@ -674,7 +675,7 @@ const luaL_loadstring = function(L, s) {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} s
+ * @param {LuaString} s
  * @returns {number}
  */
 const luaL_dostring = function(L, s) {
@@ -684,7 +685,7 @@ const luaL_dostring = function(L, s) {
 /**
  * @param {lua_State} L
  * @param {number} obj
- * @param {Uint8Array} event
+ * @param {LuaString} event
  * @returns {number}
  */
 const luaL_getmetafield = function(L, obj, event) {
@@ -704,7 +705,7 @@ const luaL_getmetafield = function(L, obj, event) {
 /**
  * @param {lua_State} L
  * @param {number} obj
- * @param {Uint8Array} event
+ * @param {LuaString} event
  * @returns {boolean}
  */
 const luaL_callmeta = function(L, obj, event) {
@@ -737,7 +738,7 @@ const p_f = to_luastring("%f");
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @returns {Uint8Array}
+ * @returns {LuaString}
  */
 const luaL_tolstring = function(L, idx) {
     if (luaL_callmeta(L, idx, __tostring)) {
@@ -816,10 +817,10 @@ const find_subarray = function(arr, subarr, from_index) {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} s
- * @param {Uint8Array} p
- * @param {Uint8Array} r
- * @returns {Uint8Array}
+ * @param {LuaString} s
+ * @param {LuaString} p
+ * @param {LuaString} r
+ * @returns {LuaString}
  */
 const luaL_gsub = function(L, s, p, r) {
     let wild;
@@ -842,7 +843,7 @@ const luaL_gsub = function(L, s, p, r) {
 /**
  * @param {lua_State} L
  * @param {number} idx
- * @param {Uint8Array} fname
+ * @param {LuaString} fname
  * @returns {boolean}
  */
 const luaL_getsubtable = function(L, idx, fname) {
@@ -1163,7 +1164,7 @@ if (typeof process === "undefined") {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} filename
+ * @param {LuaString} filename
  * @returns {number}
  */
 const luaL_loadfile = function(L, filename) {
@@ -1172,7 +1173,7 @@ const luaL_loadfile = function(L, filename) {
 
 /**
  * @param {lua_State} L
- * @param {Uint8Array} filename
+ * @param {LuaString} filename
  * @returns {number}
  */
 const luaL_dofile = function(L, filename) {
diff --git a/src/loadlib.js b/src/loadlib.js
index 92bf677..58ea283 100644
--- a/src/loadlib.js
+++ b/src/loadlib.js
@@ -70,7 +70,6 @@ const {
     to_luastring,
     to_uristring
 } = require("./fengaricore.js");
-const fengari  = require('./fengari.js');
 
 const global_env = (function() {
     if (typeof process !== "undefined") {
@@ -143,7 +142,7 @@ if (typeof process === "undefined") {
             lua_pushstring(L, to_luastring(`${e.name}: ${e.message}`));
             return null;
         }
-        let res = func(fengari);
+        let res = func(require('./fengari.js'));
         if (typeof res === "function" || (typeof res === "object" && res !== null)) {
             return res;
         } else if (res === void 0) { /* assume library added symbols to global environment */
diff --git a/src/loslib.js b/src/loslib.js
index 9dbaf1d..da05bcd 100644
--- a/src/loslib.js
+++ b/src/loslib.js
@@ -1,5 +1,9 @@
 "use strict";
 
+/**
+ * @typedef {import('./defs').LuaString} LuaString
+ */
+
 const {
     LUA_TNIL,
     LUA_TTABLE,
@@ -509,7 +513,7 @@ if (typeof process === "undefined") {
     };
 
     syslib.getenv = function(L) {
-        /** @type {string|Uint8Array} */
+        /** @type {string|LuaString} */
         let key = luaL_checkstring(L, 1);
         key = to_jsstring(key); /* https://github.com/nodejs/node/issues/16961 */
         if (Object.prototype.hasOwnProperty.call(process.env, key)) {
@@ -567,7 +571,7 @@ if (typeof process === "undefined") {
     };
 
     syslib.execute = function(L) {
-        /** @type {string|Uint8Array} */
+        /** @type {string|LuaString} */
         let cmd = luaL_optstring(L, 1, null);
         if (cmd !== null) {
             cmd = to_jsstring(cmd);
diff --git a/src/lstring.js b/src/lstring.js
index eeb883f..750f660 100644
--- a/src/lstring.js
+++ b/src/lstring.js
@@ -10,6 +10,7 @@ const { lua_assert } = require("./llimits.js");
 
 /**
  * @typedef {import('./lstate').lua_State} lua_State
+ * @typedef {import('./defs').LuaString} LuaString
  */
 
 class TString {
@@ -67,7 +68,7 @@ const luaS_bless = function(L, str) {
 /* makes a copy */
 /**
  * @param {lua_State} L
- * @param {Uint8Array} str
+ * @param {LuaString} str
  * @returns {TString}
  */
 const luaS_new = function(L, str) {
diff --git a/src/lstrlib.js b/src/lstrlib.js
index fe59a20..5e056bd 100644
--- a/src/lstrlib.js
+++ b/src/lstrlib.js
@@ -2,6 +2,10 @@
 
 const { sprintf } = require('sprintf-js');
 
+/**
+ * @typedef {import('./defs').LuaString} LuaString
+ */
+
 const {
     LUA_INTEGER_FMT,
     LUA_INTEGER_FRMLEN,
@@ -1389,10 +1393,10 @@ const add_value = function(ms, b, s, e, tr) {
 };
 
 const str_gsub = function(L) {
-    /** @type {any|Uint8Array} */
+    /** @type {any|LuaString} */
     let src = luaL_checkstring(L, 1);  /* subject */
     let srcl = src.length;
-    /** @type {number|Uint8Array} */
+    /** @type {number|LuaString} */
     let p = luaL_checkstring(L, 2);  /* pattern */
     let lp = p.length;
     let lastmatch = null;  /* end of last match */

From a6476ff677efc478c83b2d2cc9ab835bae18583f Mon Sep 17 00:00:00 2001
From: gudzpoz <gudzpoz@live.com>
Date: Fri, 28 Apr 2023 12:00:30 +0800
Subject: [PATCH 14/14] Ask tsc to merge all d.ts files into one

---
 src/fengari.js     |  4 +---
 src/fengaricore.js | 36 +++++++++++++++++++-----------------
 src/lua.js         |  4 +---
 src/lualib.js      |  3 +--
 tsconfig.json      |  2 +-
 5 files changed, 23 insertions(+), 26 deletions(-)

diff --git a/src/fengari.js b/src/fengari.js
index 267ed00..32a8a63 100644
--- a/src/fengari.js
+++ b/src/fengari.js
@@ -10,7 +10,7 @@ Copyright © 1994–2017 Lua.org, PUC-Rio.
 
 const core = require("./fengaricore.js");
 
-const exported = {
+module.exports = {
     FENGARI_AUTHORS:          core.FENGARI_AUTHORS,
     FENGARI_COPYRIGHT:        core.FENGARI_COPYRIGHT,
     FENGARI_RELEASE:          core.FENGARI_RELEASE,
@@ -32,5 +32,3 @@ const exported = {
     lauxlib:                  require('./lauxlib.js'),
     lualib:                   require('./lualib.js'),
 };
-
-module.exports = exported;
diff --git a/src/fengaricore.js b/src/fengaricore.js
index b253e9a..854ece0 100644
--- a/src/fengaricore.js
+++ b/src/fengaricore.js
@@ -17,20 +17,22 @@ const FENGARI_RELEASE         = FENGARI_VERSION + "." + FENGARI_VERSION_RELEASE;
 const FENGARI_AUTHORS         = "B. Giannangeli, Daurnimator";
 const FENGARI_COPYRIGHT       = FENGARI_RELEASE + "  Copyright (C) 2017-2019 " + FENGARI_AUTHORS + "\nBased on: " + defs.LUA_COPYRIGHT;
 
-module.exports.FENGARI_AUTHORS         = FENGARI_AUTHORS;
-module.exports.FENGARI_COPYRIGHT       = FENGARI_COPYRIGHT;
-module.exports.FENGARI_RELEASE         = FENGARI_RELEASE;
-module.exports.FENGARI_VERSION         = FENGARI_VERSION;
-module.exports.FENGARI_VERSION_MAJOR   = FENGARI_VERSION_MAJOR;
-module.exports.FENGARI_VERSION_MINOR   = FENGARI_VERSION_MINOR;
-module.exports.FENGARI_VERSION_NUM     = FENGARI_VERSION_NUM;
-module.exports.FENGARI_VERSION_RELEASE = FENGARI_VERSION_RELEASE;
-module.exports.is_luastring            = defs.is_luastring;
-module.exports.luastring_eq            = defs.luastring_eq;
-module.exports.luastring_from          = defs.luastring_from;
-module.exports.luastring_indexOf       = defs.luastring_indexOf;
-module.exports.luastring_of            = defs.luastring_of;
-module.exports.to_jsstring             = defs.to_jsstring;
-module.exports.to_luastring            = defs.to_luastring;
-module.exports.to_uristring            = defs.to_uristring;
-module.exports.from_userstring         = defs.from_userstring;
+module.exports = {
+    FENGARI_AUTHORS:          FENGARI_AUTHORS,
+    FENGARI_COPYRIGHT:        FENGARI_COPYRIGHT,
+    FENGARI_RELEASE:          FENGARI_RELEASE,
+    FENGARI_VERSION:          FENGARI_VERSION,
+    FENGARI_VERSION_MAJOR:    FENGARI_VERSION_MAJOR,
+    FENGARI_VERSION_MINOR:    FENGARI_VERSION_MINOR,
+    FENGARI_VERSION_NUM:      FENGARI_VERSION_NUM,
+    FENGARI_VERSION_RELEASE:  FENGARI_VERSION_RELEASE,
+    is_luastring:             defs.is_luastring,
+    luastring_eq:             defs.luastring_eq,
+    luastring_from:           defs.luastring_from,
+    luastring_indexOf:        defs.luastring_indexOf,
+    luastring_of:             defs.luastring_of,
+    to_jsstring:              defs.to_jsstring,
+    to_luastring:             defs.to_luastring,
+    to_uristring:             defs.to_uristring,
+    from_userstring:          defs.from_userstring
+};
diff --git a/src/lua.js b/src/lua.js
index 635d6d9..aff83d3 100644
--- a/src/lua.js
+++ b/src/lua.js
@@ -6,7 +6,7 @@ const ldebug = require("./ldebug.js");
 const ldo    = require("./ldo.js");
 const lstate = require("./lstate.js");
 
-const exported = {
+module.exports = {
     LUA_AUTHORS:              defs.LUA_AUTHORS,
     LUA_COPYRIGHT:            defs.LUA_COPYRIGHT,
     LUA_ERRERR:               defs.thread_status.LUA_ERRERR,
@@ -191,5 +191,3 @@ const exported = {
     lua_yieldk:               ldo.lua_yieldk,
     lua_tocfunction:          lapi.lua_tocfunction,
 };
-
-module.exports = exported;
diff --git a/src/lualib.js b/src/lualib.js
index ca5abb6..47170aa 100644
--- a/src/lualib.js
+++ b/src/lualib.js
@@ -73,6 +73,5 @@ module.exports.LUA_FENGARILIBNAME = LUA_FENGARILIBNAME;
 /** @type {(L: lua_State) => number} */
 module.exports.luaopen_fengari = require("./fengarilib.js").luaopen_fengari;
 
-const linit = require('./linit.js');
 /** @type {(L: lua_State) => void} */
-module.exports.luaL_openlibs = linit.luaL_openlibs;
+module.exports.luaL_openlibs = require('./linit.js').luaL_openlibs;
diff --git a/tsconfig.json b/tsconfig.json
index f7d9569..3eb2ead 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -4,7 +4,7 @@
     "allowJs": true,
     "declaration": true,
     "emitDeclarationOnly": true,
-    "outDir": "dist",
+    "outFile": "dist/fengari.js",
     "declarationMap": true,
     "checkJs": true,
     "lib": ["DOM", "WebWorker", "ES2015"],