just-bash 2.12.8 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +89 -3
  2. package/dist/AGENTS.md +1 -1
  3. package/dist/Bash.d.ts +17 -0
  4. package/dist/bin/chunks/{awk2-J2PNSA7C.js → awk2-VTJMI54B.js} +1 -1
  5. package/dist/bin/chunks/chunk-3YMCKZTY.js +14 -0
  6. package/dist/bin/chunks/{chunk-O5B6WWQQ.js → chunk-A3HQTYHR.js} +1 -1
  7. package/dist/bin/chunks/chunk-AAW7UMPO.js +9 -0
  8. package/dist/bin/chunks/{chunk-2V53PP6G.js → chunk-B3RU2PUI.js} +27 -27
  9. package/dist/bin/chunks/chunk-CQG2HEAL.js +5 -0
  10. package/dist/bin/chunks/chunk-DOXYBGNA.js +12 -0
  11. package/dist/bin/{shell/chunks/chunk-YAEF6X2N.js → chunks/chunk-GR23MPTT.js} +1 -1
  12. package/dist/bin/{shell/chunks/chunk-35JD7YEM.js → chunks/chunk-HJEHIH4P.js} +13 -13
  13. package/dist/bin/chunks/{chunk-UG4GMDQL.js → chunk-LGF54XJQ.js} +1 -1
  14. package/dist/bin/chunks/chunk-N6KA6G3Q.js +11 -0
  15. package/dist/bin/chunks/{chunk-2BC3N3L2.js → chunk-ORUYSLP4.js} +4 -4
  16. package/dist/bin/chunks/{chunk-REGSV3X5.js → chunk-SDLWFYVT.js} +1 -1
  17. package/dist/bin/chunks/{chunk-STOAUD75.js → chunk-THNL3XFF.js} +8 -8
  18. package/dist/bin/chunks/{chunk-ZJGIBTWD.js → chunk-V7ZOPVQS.js} +1 -1
  19. package/dist/bin/chunks/{chunk-ZYQQ6B7B.js → chunk-ZO5PSLKR.js} +2 -2
  20. package/dist/bin/{shell/chunks/curl-3GMIPMCI.js → chunks/curl-B64SIJOD.js} +1 -1
  21. package/dist/bin/chunks/env-NTPN5QYM.js +2 -0
  22. package/dist/bin/{shell/chunks/expansion-JBCP2CHQ.js → chunks/expansion-2RO5M3QC.js} +1 -1
  23. package/dist/bin/{shell/chunks/find-MTLF23HX.js → chunks/find-GAYRV4IF.js} +1 -1
  24. package/dist/bin/{shell/chunks/flag-coverage-CFWN3JJN.js → chunks/flag-coverage-IRM4GISL.js} +1 -1
  25. package/dist/bin/{shell/chunks/help-4H52JYYC.js → chunks/help-DVG4AAGE.js} +1 -1
  26. package/dist/bin/{shell/chunks/jq-JFXEKNLN.js → chunks/jq-V7FYGIKO.js} +1 -1
  27. package/dist/bin/chunks/js-exec-YGYYZEEQ.js +97 -0
  28. package/dist/bin/chunks/js-exec-worker.js +4368 -0
  29. package/dist/bin/chunks/python3-EIXZW3LO.js +12 -0
  30. package/dist/bin/chunks/rg-C6KMBFNG.js +2 -0
  31. package/dist/bin/{shell/chunks/time-GZSHCM77.js → chunks/time-VSKBXRQH.js} +1 -1
  32. package/dist/bin/chunks/{timeout-JJWIFL7W.js → timeout-Z24MNWOP.js} +1 -1
  33. package/dist/bin/chunks/worker.js +87 -33
  34. package/dist/bin/chunks/{xan-M6MLWZCU.js → xan-MOZFJGMY.js} +1 -1
  35. package/dist/bin/chunks/xargs-SCYIFXOW.js +2 -0
  36. package/dist/bin/chunks/{yq-YWUQUXJJ.js → yq-JJLSDDST.js} +1 -1
  37. package/dist/bin/just-bash.js +158 -157
  38. package/dist/bin/shell/chunks/{awk2-J2PNSA7C.js → awk2-VTJMI54B.js} +1 -1
  39. package/dist/bin/shell/chunks/chunk-3YMCKZTY.js +14 -0
  40. package/dist/bin/shell/chunks/{chunk-O5B6WWQQ.js → chunk-A3HQTYHR.js} +1 -1
  41. package/dist/bin/shell/chunks/chunk-AAW7UMPO.js +9 -0
  42. package/dist/bin/shell/chunks/{chunk-2V53PP6G.js → chunk-B3RU2PUI.js} +27 -27
  43. package/dist/bin/shell/chunks/chunk-CQG2HEAL.js +5 -0
  44. package/dist/bin/shell/chunks/chunk-DOXYBGNA.js +12 -0
  45. package/dist/bin/{chunks/chunk-YAEF6X2N.js → shell/chunks/chunk-GR23MPTT.js} +1 -1
  46. package/dist/bin/{chunks/chunk-35JD7YEM.js → shell/chunks/chunk-HJEHIH4P.js} +13 -13
  47. package/dist/bin/shell/chunks/{chunk-UG4GMDQL.js → chunk-LGF54XJQ.js} +1 -1
  48. package/dist/bin/shell/chunks/chunk-N6KA6G3Q.js +11 -0
  49. package/dist/bin/shell/chunks/{chunk-2BC3N3L2.js → chunk-ORUYSLP4.js} +4 -4
  50. package/dist/bin/shell/chunks/{chunk-REGSV3X5.js → chunk-SDLWFYVT.js} +1 -1
  51. package/dist/bin/shell/chunks/{chunk-STOAUD75.js → chunk-THNL3XFF.js} +8 -8
  52. package/dist/bin/shell/chunks/{chunk-ZJGIBTWD.js → chunk-V7ZOPVQS.js} +1 -1
  53. package/dist/bin/shell/chunks/{chunk-ZYQQ6B7B.js → chunk-ZO5PSLKR.js} +2 -2
  54. package/dist/bin/{chunks/curl-3GMIPMCI.js → shell/chunks/curl-B64SIJOD.js} +1 -1
  55. package/dist/bin/shell/chunks/env-NTPN5QYM.js +2 -0
  56. package/dist/bin/{chunks/expansion-JBCP2CHQ.js → shell/chunks/expansion-2RO5M3QC.js} +1 -1
  57. package/dist/bin/{chunks/find-MTLF23HX.js → shell/chunks/find-GAYRV4IF.js} +1 -1
  58. package/dist/bin/{chunks/flag-coverage-CFWN3JJN.js → shell/chunks/flag-coverage-IRM4GISL.js} +1 -1
  59. package/dist/bin/{chunks/help-4H52JYYC.js → shell/chunks/help-DVG4AAGE.js} +1 -1
  60. package/dist/bin/{chunks/jq-JFXEKNLN.js → shell/chunks/jq-V7FYGIKO.js} +1 -1
  61. package/dist/bin/shell/chunks/js-exec-VYG74FQ3.js +97 -0
  62. package/dist/bin/shell/chunks/python3-WO3WFGMS.js +12 -0
  63. package/dist/bin/shell/chunks/rg-C6KMBFNG.js +2 -0
  64. package/dist/bin/{chunks/time-GZSHCM77.js → shell/chunks/time-VSKBXRQH.js} +1 -1
  65. package/dist/bin/shell/chunks/{timeout-JJWIFL7W.js → timeout-Z24MNWOP.js} +1 -1
  66. package/dist/bin/shell/chunks/{xan-M6MLWZCU.js → xan-MOZFJGMY.js} +1 -1
  67. package/dist/bin/shell/chunks/xargs-SCYIFXOW.js +2 -0
  68. package/dist/bin/shell/chunks/{yq-YWUQUXJJ.js → yq-JJLSDDST.js} +1 -1
  69. package/dist/bin/shell/shell.js +164 -164
  70. package/dist/bundle/browser.js +534 -534
  71. package/dist/bundle/chunks/{awk2-FUVZGMX2.js → awk2-POPGKRAI.js} +1 -1
  72. package/dist/bundle/chunks/{chunk-DZZS6SJP.js → chunk-7TSDKFEO.js} +1 -1
  73. package/dist/bundle/chunks/{chunk-D4QDMGEB.js → chunk-BBXLRYSX.js} +2 -2
  74. package/dist/bundle/chunks/{chunk-TRD56HID.js → chunk-FEIOJCZD.js} +1 -1
  75. package/dist/bundle/chunks/chunk-JCMONG3T.js +13 -0
  76. package/dist/bundle/chunks/{chunk-D2FZX7A2.js → chunk-LPQPILI2.js} +8 -8
  77. package/dist/bundle/chunks/chunk-MJWMXCEJ.js +10 -0
  78. package/dist/bundle/chunks/{chunk-2GOYXRRP.js → chunk-MLXIYONF.js} +4 -4
  79. package/dist/bundle/chunks/chunk-NAERJDUW.js +8 -0
  80. package/dist/bundle/chunks/{chunk-OKEHYWBE.js → chunk-NYQYO467.js} +13 -13
  81. package/dist/bundle/chunks/{chunk-JKLUDNMU.js → chunk-O2DBFL6Z.js} +1 -1
  82. package/dist/bundle/chunks/{chunk-JDMQDJYE.js → chunk-OARHFVLG.js} +1 -1
  83. package/dist/bundle/chunks/{chunk-AKVMAONP.js → chunk-RUF7WQ7U.js} +1 -1
  84. package/dist/bundle/chunks/chunk-TOMNU26N.js +4 -0
  85. package/dist/bundle/chunks/{chunk-ZLJ5TCLC.js → chunk-UNYNJIFU.js} +27 -27
  86. package/dist/bundle/chunks/chunk-YTNYSM6T.js +11 -0
  87. package/dist/bundle/chunks/{curl-KM2ZAUR6.js → curl-FCIO57JJ.js} +1 -1
  88. package/dist/bundle/chunks/env-5EPCWSXR.js +1 -0
  89. package/dist/bundle/chunks/{expansion-XG7G47TX.js → expansion-ENLSRCXJ.js} +1 -1
  90. package/dist/bundle/chunks/{find-DOIVMX6X.js → find-TPUOAIUQ.js} +1 -1
  91. package/dist/bundle/chunks/{flag-coverage-VML3BMJT.js → flag-coverage-SPT2DN2I.js} +1 -1
  92. package/dist/bundle/chunks/{help-IA5CMGR4.js → help-VVWX7SA5.js} +1 -1
  93. package/dist/bundle/chunks/{jq-SSCW4AAA.js → jq-DIRZBOTX.js} +1 -1
  94. package/dist/bundle/chunks/js-exec-R2LSP7M4.js +96 -0
  95. package/dist/bundle/chunks/js-exec-worker.js +4368 -0
  96. package/dist/bundle/chunks/python3-DH2SBOI3.js +11 -0
  97. package/dist/bundle/chunks/rg-FOQSCCX3.js +1 -0
  98. package/dist/bundle/chunks/{time-XL42Z4U5.js → time-FABCOJJU.js} +1 -1
  99. package/dist/bundle/chunks/{timeout-QCU4INQT.js → timeout-VRKMCG72.js} +1 -1
  100. package/dist/bundle/chunks/worker.js +87 -33
  101. package/dist/bundle/chunks/{xan-K7XYDHFV.js → xan-BXDXYEIB.js} +1 -1
  102. package/dist/bundle/chunks/xargs-I6EZUCYF.js +1 -0
  103. package/dist/bundle/chunks/{yq-WTK3HUOR.js → yq-XMVSIL6Z.js} +1 -1
  104. package/dist/bundle/index.cjs +956 -857
  105. package/dist/bundle/index.js +145 -145
  106. package/dist/commands/js-exec/fetch-polyfill.d.ts +6 -0
  107. package/dist/commands/js-exec/js-exec.d.ts +11 -0
  108. package/dist/commands/js-exec/module-shims.d.ts +29 -0
  109. package/dist/commands/js-exec/path-polyfill.d.ts +6 -0
  110. package/dist/commands/js-exec/worker.d.ts +30 -0
  111. package/dist/commands/python3/worker.d.ts +1 -0
  112. package/dist/commands/query-engine/safe-object.d.ts +11 -0
  113. package/dist/commands/registry.d.ts +13 -2
  114. package/dist/commands/{python3/fs-bridge-handler.d.ts → worker-bridge/bridge-handler.d.ts} +25 -8
  115. package/dist/commands/{python3 → worker-bridge}/protocol.d.ts +6 -3
  116. package/dist/commands/{python3/sync-fs-backend.d.ts → worker-bridge/sync-backend.d.ts} +25 -4
  117. package/dist/index.d.cts +3 -3
  118. package/dist/index.d.ts +3 -3
  119. package/dist/interpreter/interpreter.d.ts +3 -0
  120. package/dist/interpreter/types.d.ts +8 -0
  121. package/dist/limits.d.ts +3 -1
  122. package/dist/types.d.ts +12 -0
  123. package/package.json +12 -11
  124. package/dist/bin/chunks/chunk-47HZU3SY.js +0 -5
  125. package/dist/bin/chunks/chunk-CZFRRDQC.js +0 -12
  126. package/dist/bin/chunks/chunk-N4EU64Y4.js +0 -9
  127. package/dist/bin/chunks/chunk-VIYJJTN2.js +0 -14
  128. package/dist/bin/chunks/env-HOVBNLUR.js +0 -2
  129. package/dist/bin/chunks/python3-KI2FQWSN.js +0 -17
  130. package/dist/bin/chunks/rg-34GE6REQ.js +0 -2
  131. package/dist/bin/chunks/xargs-GBL6PZ2K.js +0 -2
  132. package/dist/bin/shell/chunks/chunk-47HZU3SY.js +0 -5
  133. package/dist/bin/shell/chunks/chunk-CZFRRDQC.js +0 -12
  134. package/dist/bin/shell/chunks/chunk-N4EU64Y4.js +0 -9
  135. package/dist/bin/shell/chunks/chunk-VIYJJTN2.js +0 -14
  136. package/dist/bin/shell/chunks/env-HOVBNLUR.js +0 -2
  137. package/dist/bin/shell/chunks/python3-E5X6WBBU.js +0 -17
  138. package/dist/bin/shell/chunks/rg-34GE6REQ.js +0 -2
  139. package/dist/bin/shell/chunks/xargs-GBL6PZ2K.js +0 -2
  140. package/dist/bundle/chunks/chunk-3RA5L262.js +0 -8
  141. package/dist/bundle/chunks/chunk-EX62JIX3.js +0 -13
  142. package/dist/bundle/chunks/chunk-WECLUBEQ.js +0 -11
  143. package/dist/bundle/chunks/chunk-ZSJYNBAF.js +0 -4
  144. package/dist/bundle/chunks/env-XZY4LKEO.js +0 -1
  145. package/dist/bundle/chunks/python3-2OHR6PZU.js +0 -16
  146. package/dist/bundle/chunks/rg-RAICUFGG.js +0 -1
  147. package/dist/bundle/chunks/xargs-2BBAQDTC.js +0 -1
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Web Fetch API polyfill source for QuickJS.
3
+ * Exported as a string constant to be evaluated inside the sandbox.
4
+ * Provides URLSearchParams, URL, Headers, Response, Request, and fetch().
5
+ */
6
+ export declare const FETCH_POLYFILL_SOURCE = "\n(function() {\n // --- URLSearchParams ---\n function URLSearchParams(init) {\n this._entries = [];\n if (!init) return;\n if (typeof init === 'string') {\n var s = init;\n if (s.charAt(0) === '?') s = s.slice(1);\n var pairs = s.split('&');\n for (var i = 0; i < pairs.length; i++) {\n var pair = pairs[i];\n if (pair === '') continue;\n var eq = pair.indexOf('=');\n if (eq === -1) {\n this._entries.push([decodeURIComponent(pair), '']);\n } else {\n this._entries.push([\n decodeURIComponent(pair.slice(0, eq)),\n decodeURIComponent(pair.slice(eq + 1))\n ]);\n }\n }\n } else if (typeof init === 'object' && init !== null) {\n if (init instanceof URLSearchParams) {\n this._entries = init._entries.slice();\n } else {\n var keys = Object.keys(init);\n for (var i = 0; i < keys.length; i++) {\n this._entries.push([keys[i], String(init[keys[i]])]);\n }\n }\n }\n }\n\n URLSearchParams.prototype.append = function(name, value) {\n this._entries.push([String(name), String(value)]);\n };\n\n URLSearchParams.prototype.delete = function(name) {\n var n = String(name);\n this._entries = this._entries.filter(function(e) { return e[0] !== n; });\n };\n\n URLSearchParams.prototype.get = function(name) {\n var n = String(name);\n for (var i = 0; i < this._entries.length; i++) {\n if (this._entries[i][0] === n) return this._entries[i][1];\n }\n return null;\n };\n\n URLSearchParams.prototype.getAll = function(name) {\n var n = String(name);\n var result = [];\n for (var i = 0; i < this._entries.length; i++) {\n if (this._entries[i][0] === n) result.push(this._entries[i][1]);\n }\n return result;\n };\n\n URLSearchParams.prototype.has = function(name) {\n var n = String(name);\n for (var i = 0; i < this._entries.length; i++) {\n if (this._entries[i][0] === n) return true;\n }\n return false;\n };\n\n URLSearchParams.prototype.set = function(name, value) {\n var n = String(name);\n var v = String(value);\n var found = false;\n var newEntries = [];\n for (var i = 0; i < this._entries.length; i++) {\n if (this._entries[i][0] === n) {\n if (!found) {\n newEntries.push([n, v]);\n found = true;\n }\n } else {\n newEntries.push(this._entries[i]);\n }\n }\n if (!found) newEntries.push([n, v]);\n this._entries = newEntries;\n };\n\n URLSearchParams.prototype.sort = function() {\n this._entries.sort(function(a, b) {\n if (a[0] < b[0]) return -1;\n if (a[0] > b[0]) return 1;\n return 0;\n });\n };\n\n URLSearchParams.prototype.toString = function() {\n return this._entries.map(function(e) {\n return encodeURIComponent(e[0]) + '=' + encodeURIComponent(e[1]);\n }).join('&');\n };\n\n URLSearchParams.prototype.forEach = function(callback, thisArg) {\n for (var i = 0; i < this._entries.length; i++) {\n callback.call(thisArg, this._entries[i][1], this._entries[i][0], this);\n }\n };\n\n URLSearchParams.prototype.entries = function() {\n var idx = 0;\n var entries = this._entries;\n return {\n next: function() {\n if (idx >= entries.length) return { done: true, value: undefined };\n return { done: false, value: entries[idx++].slice() };\n },\n [Symbol.iterator]: function() { return this; }\n };\n };\n\n URLSearchParams.prototype.keys = function() {\n var idx = 0;\n var entries = this._entries;\n return {\n next: function() {\n if (idx >= entries.length) return { done: true, value: undefined };\n return { done: false, value: entries[idx++][0] };\n },\n [Symbol.iterator]: function() { return this; }\n };\n };\n\n URLSearchParams.prototype.values = function() {\n var idx = 0;\n var entries = this._entries;\n return {\n next: function() {\n if (idx >= entries.length) return { done: true, value: undefined };\n return { done: false, value: entries[idx++][1] };\n },\n [Symbol.iterator]: function() { return this; }\n };\n };\n\n URLSearchParams.prototype[Symbol.iterator] = URLSearchParams.prototype.entries;\n\n Object.defineProperty(URLSearchParams.prototype, 'size', {\n get: function() { return this._entries.length; }\n });\n\n // --- URL ---\n var urlRegex = /^([a-zA-Z][a-zA-Z0-9+.-]*):(?:\\/\\/(?:([^:@/?#]*)(?::([^@/?#]*))?@)?([^:/?#]*)(?::([0-9]+))?)?(\\/[^?#]*)?(?:\\?([^#]*))?(?:#(.*))?$/;\n\n function URL(url, base) {\n var input = String(url);\n\n if (base !== undefined) {\n var baseUrl = (base instanceof URL) ? base : new URL(String(base));\n // Resolve relative URL against base\n if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(input)) {\n // Absolute URL - parse as-is\n } else if (input.charAt(0) === '/' && input.charAt(1) === '/') {\n // Protocol-relative\n input = baseUrl.protocol + input;\n } else if (input.charAt(0) === '/') {\n // Absolute path\n input = baseUrl.origin + input;\n } else if (input.charAt(0) === '?' || input.charAt(0) === '#') {\n // Query or hash only\n var basePath = baseUrl.protocol + '//' + baseUrl.host + baseUrl.pathname;\n if (input.charAt(0) === '#') {\n input = basePath + baseUrl.search + input;\n } else {\n input = basePath + input;\n }\n } else {\n // Relative path\n var basePath = baseUrl.protocol + '//' + baseUrl.host;\n var dirPath = baseUrl.pathname;\n var lastSlash = dirPath.lastIndexOf('/');\n if (lastSlash >= 0) dirPath = dirPath.slice(0, lastSlash + 1);\n else dirPath = '/';\n input = basePath + dirPath + input;\n }\n }\n\n var m = urlRegex.exec(input);\n if (!m) throw new TypeError(\"Invalid URL: \" + String(url));\n\n this.protocol = m[1].toLowerCase() + ':';\n this.username = m[2] ? decodeURIComponent(m[2]) : '';\n this.password = m[3] ? decodeURIComponent(m[3]) : '';\n this.hostname = m[4] || '';\n this.port = m[5] || '';\n this.pathname = m[6] || '/';\n this.hash = m[8] ? '#' + m[8] : '';\n\n // Normalize pathname (resolve . and ..)\n var parts = this.pathname.split('/');\n var resolved = [];\n for (var i = 0; i < parts.length; i++) {\n if (parts[i] === '..') { if (resolved.length > 1) resolved.pop(); }\n else if (parts[i] !== '.') resolved.push(parts[i]);\n }\n this.pathname = resolved.join('/') || '/';\n\n // searchParams is live\n this._searchParamsStr = m[7] || '';\n this.searchParams = new URLSearchParams(this._searchParamsStr);\n }\n\n Object.defineProperty(URL.prototype, 'search', {\n get: function() {\n var s = this.searchParams.toString();\n return s ? '?' + s : '';\n },\n set: function(v) {\n this.searchParams = new URLSearchParams(String(v));\n }\n });\n\n Object.defineProperty(URL.prototype, 'host', {\n get: function() {\n return this.port ? this.hostname + ':' + this.port : this.hostname;\n }\n });\n\n Object.defineProperty(URL.prototype, 'origin', {\n get: function() {\n return this.protocol + '//' + this.host;\n }\n });\n\n Object.defineProperty(URL.prototype, 'href', {\n get: function() {\n var auth = '';\n if (this.username) {\n auth = this.username;\n if (this.password) auth += ':' + this.password;\n auth += '@';\n }\n return this.protocol + '//' + auth + this.host + this.pathname + this.search + this.hash;\n },\n set: function(v) {\n var parsed = new URL(String(v));\n this.protocol = parsed.protocol;\n this.username = parsed.username;\n this.password = parsed.password;\n this.hostname = parsed.hostname;\n this.port = parsed.port;\n this.pathname = parsed.pathname;\n this.searchParams = parsed.searchParams;\n this.hash = parsed.hash;\n }\n });\n\n URL.prototype.toString = function() { return this.href; };\n URL.prototype.toJSON = function() { return this.href; };\n\n // --- Headers ---\n function Headers(init) {\n this._map = {};\n if (!init) return;\n if (init instanceof Headers) {\n var keys = Object.keys(init._map);\n for (var i = 0; i < keys.length; i++) {\n this._map[keys[i]] = init._map[keys[i]].slice();\n }\n } else if (typeof init === 'object') {\n var keys = Object.keys(init);\n for (var i = 0; i < keys.length; i++) {\n this._map[keys[i].toLowerCase()] = [String(init[keys[i]])];\n }\n }\n }\n\n Headers.prototype.append = function(name, value) {\n var key = String(name).toLowerCase();\n if (!this._map[key]) this._map[key] = [];\n this._map[key].push(String(value));\n };\n\n Headers.prototype.delete = function(name) {\n delete this._map[String(name).toLowerCase()];\n };\n\n Headers.prototype.get = function(name) {\n var vals = this._map[String(name).toLowerCase()];\n return vals ? vals.join(', ') : null;\n };\n\n Headers.prototype.has = function(name) {\n return String(name).toLowerCase() in this._map;\n };\n\n Headers.prototype.set = function(name, value) {\n this._map[String(name).toLowerCase()] = [String(value)];\n };\n\n Headers.prototype.forEach = function(callback, thisArg) {\n var keys = Object.keys(this._map).sort();\n for (var i = 0; i < keys.length; i++) {\n callback.call(thisArg, this._map[keys[i]].join(', '), keys[i], this);\n }\n };\n\n Headers.prototype.entries = function() {\n var keys = Object.keys(this._map).sort();\n var map = this._map;\n var idx = 0;\n return {\n next: function() {\n if (idx >= keys.length) return { done: true, value: undefined };\n var k = keys[idx++];\n return { done: false, value: [k, map[k].join(', ')] };\n },\n [Symbol.iterator]: function() { return this; }\n };\n };\n\n Headers.prototype.keys = function() {\n var keys = Object.keys(this._map).sort();\n var idx = 0;\n return {\n next: function() {\n if (idx >= keys.length) return { done: true, value: undefined };\n return { done: false, value: keys[idx++] };\n },\n [Symbol.iterator]: function() { return this; }\n };\n };\n\n Headers.prototype.values = function() {\n var keys = Object.keys(this._map).sort();\n var map = this._map;\n var idx = 0;\n return {\n next: function() {\n if (idx >= keys.length) return { done: true, value: undefined };\n return { done: false, value: map[keys[idx++]].join(', ') };\n },\n [Symbol.iterator]: function() { return this; }\n };\n };\n\n Headers.prototype[Symbol.iterator] = Headers.prototype.entries;\n\n // --- Response ---\n function Response(body, init) {\n if (init === undefined) init = {};\n this.status = init.status !== undefined ? init.status : 200;\n this.statusText = init.statusText !== undefined ? init.statusText : '';\n this.headers = init.headers instanceof Headers ? init.headers : new Headers(init.headers);\n this.body = body !== undefined && body !== null ? String(body) : '';\n this.ok = this.status >= 200 && this.status <= 299;\n this.url = '';\n this.redirected = false;\n this.type = 'basic';\n this.bodyUsed = false;\n }\n\n Response.prototype.text = function() {\n this.bodyUsed = true;\n return Promise.resolve(this.body);\n };\n\n Response.prototype.json = function() {\n this.bodyUsed = true;\n try {\n return Promise.resolve(JSON.parse(this.body));\n } catch (e) {\n return Promise.reject(e);\n }\n };\n\n Response.prototype.clone = function() {\n var r = new Response(this.body, {\n status: this.status,\n statusText: this.statusText,\n headers: new Headers(this.headers)\n });\n r.url = this.url;\n r.redirected = this.redirected;\n r.type = this.type;\n return r;\n };\n\n Response.json = function(data, init) {\n if (init === undefined) init = {};\n var headers = init.headers instanceof Headers ? init.headers : new Headers(init.headers);\n if (!headers.has('content-type')) {\n headers.set('content-type', 'application/json');\n }\n return new Response(JSON.stringify(data), {\n status: init.status !== undefined ? init.status : 200,\n statusText: init.statusText || '',\n headers: headers\n });\n };\n\n Response.error = function() {\n var r = new Response(null, { status: 0, statusText: '' });\n r.type = 'error';\n r.ok = false;\n return r;\n };\n\n Response.redirect = function(url, status) {\n if (status === undefined) status = 302;\n var r = new Response(null, {\n status: status,\n statusText: '',\n headers: new Headers({ location: String(url) })\n });\n r.redirected = true;\n return r;\n };\n\n // --- Request ---\n function Request(input, init) {\n if (init === undefined) init = {};\n if (input instanceof Request) {\n this.url = input.url;\n this.method = input.method;\n this.headers = new Headers(input.headers);\n this.body = input.body;\n } else {\n this.url = String(input);\n this.method = 'GET';\n this.headers = new Headers();\n this.body = null;\n }\n if (init.method !== undefined) this.method = String(init.method).toUpperCase();\n if (init.headers !== undefined) this.headers = init.headers instanceof Headers ? init.headers : new Headers(init.headers);\n if (init.body !== undefined) this.body = init.body !== null ? String(init.body) : null;\n }\n\n Request.prototype.clone = function() {\n return new Request(this);\n };\n\n // --- Assign to globalThis ---\n globalThis.URLSearchParams = URLSearchParams;\n globalThis.URL = URL;\n globalThis.Headers = Headers;\n globalThis.Response = Response;\n globalThis.Request = Request;\n\n // --- Wrap native fetch ---\n var _nativeFetch = globalThis[Symbol.for('jb:fetch')];\n globalThis.fetch = function fetch(input, init) {\n try {\n var url, method, headers, body;\n\n if (input instanceof Request) {\n url = input.url;\n method = input.method;\n headers = {};\n input.headers.forEach(function(v, k) { headers[k] = v; });\n body = input.body;\n } else {\n url = String(input);\n method = undefined;\n headers = undefined;\n body = undefined;\n }\n\n if (init) {\n if (init.method !== undefined) method = String(init.method).toUpperCase();\n if (init.headers !== undefined) {\n var h = init.headers instanceof Headers ? init.headers : new Headers(init.headers);\n headers = {};\n h.forEach(function(v, k) { headers[k] = v; });\n }\n if (init.body !== undefined) body = init.body !== null ? String(init.body) : undefined;\n }\n\n var opts = Object.create(null);\n if (method) opts.method = method;\n if (headers) opts.headers = headers;\n if (body) opts.body = body;\n\n var raw = _nativeFetch(url, opts);\n\n var respHeaders = new Headers(raw.headers || {});\n var response = new Response(raw.body, {\n status: raw.status,\n statusText: raw.statusText || '',\n headers: respHeaders\n });\n response.url = raw.url || url;\n\n return Promise.resolve(response);\n } catch (e) {\n return Promise.reject(new TypeError(e.message || 'fetch failed'));\n }\n };\n})();\n";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * js-exec - Execute JavaScript code via QuickJS (WASM)
3
+ *
4
+ * Runs JavaScript code in an isolated worker thread with access to the
5
+ * virtual filesystem, HTTP, and sub-shell execution via SharedArrayBuffer bridge.
6
+ *
7
+ * This command is Node.js only (uses worker_threads).
8
+ */
9
+ import type { Command } from "../../types.js";
10
+ export declare const jsExecCommand: Command;
11
+ export declare const nodeStubCommand: Command;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Node.js module shims for the QuickJS sandbox.
3
+ * Each is a JS source string evaluated inside the sandbox IIFE.
4
+ * They set up globals (e.g. globalThis[Symbol.for('jb:os')]) referenced by both
5
+ * require() and ESM VIRTUAL_MODULES.
6
+ */
7
+ /** EventEmitter — pure JS, no dependencies. Must run before stream. */
8
+ export declare const EVENTS_MODULE_SOURCE = "\nvar EventEmitter = (function() {\n function EE() {\n this._events = {};\n this._maxListeners = 10;\n }\n EE.prototype.on = function(event, listener) {\n if (!this._events[event]) this._events[event] = [];\n this._events[event].push(listener);\n return this;\n };\n EE.prototype.addListener = EE.prototype.on;\n EE.prototype.once = function(event, listener) {\n var self = this;\n function wrapper() {\n self.removeListener(event, wrapper);\n listener.apply(this, arguments);\n }\n wrapper._original = listener;\n return this.on(event, wrapper);\n };\n EE.prototype.off = function(event, listener) {\n return this.removeListener(event, listener);\n };\n EE.prototype.removeListener = function(event, listener) {\n var list = this._events[event];\n if (list) {\n this._events[event] = list.filter(function(fn) {\n return fn !== listener && fn._original !== listener;\n });\n }\n return this;\n };\n EE.prototype.removeAllListeners = function(event) {\n if (event) delete this._events[event];\n else this._events = {};\n return this;\n };\n EE.prototype.emit = function(event) {\n var list = this._events[event];\n if (!list || list.length === 0) return false;\n var args = Array.prototype.slice.call(arguments, 1);\n var fns = list.slice();\n for (var i = 0; i < fns.length; i++) fns[i].apply(this, args);\n return true;\n };\n EE.prototype.listeners = function(event) {\n return (this._events[event] || []).slice();\n };\n EE.prototype.listenerCount = function(event) {\n return (this._events[event] || []).length;\n };\n EE.prototype.setMaxListeners = function(n) {\n this._maxListeners = n;\n return this;\n };\n EE.prototype.eventNames = function() {\n return Object.keys(this._events);\n };\n EE.prototype.prependListener = function(event, listener) {\n if (!this._events[event]) this._events[event] = [];\n this._events[event].unshift(listener);\n return this;\n };\n return EE;\n})();\nglobalThis[Symbol.for('jb:events')] = { EventEmitter: EventEmitter };\n";
9
+ /** OS module — hardcoded sandbox values */
10
+ export declare const OS_MODULE_SOURCE = "\nvar _os = {\n platform: function() { return globalThis.process.platform; },\n arch: function() { return globalThis.process.arch; },\n homedir: function() { return '/home/user'; },\n tmpdir: function() { return '/tmp'; },\n type: function() { return 'Linux'; },\n hostname: function() { return 'sandbox'; },\n EOL: '\\n',\n cpus: function() { return []; },\n totalmem: function() { return 0; },\n freemem: function() { return 0; },\n endianness: function() { return 'LE'; }\n};\nglobalThis[Symbol.for('jb:os')] = _os;\n";
11
+ /** URL module — wraps globalThis.URL/URLSearchParams from fetch polyfill */
12
+ export declare const URL_MODULE_SOURCE = "\nvar _urlMod = {\n URL: globalThis.URL,\n URLSearchParams: globalThis.URLSearchParams,\n parse: function(urlStr) {\n try {\n var u = new URL(urlStr);\n return {\n protocol: u.protocol, host: u.host, hostname: u.hostname,\n port: u.port, pathname: u.pathname, search: u.search,\n hash: u.hash, href: u.href, path: u.pathname + u.search\n };\n } catch(e) {\n return {\n protocol: null, host: null, hostname: null, port: null,\n pathname: urlStr, search: '', hash: '', href: urlStr, path: urlStr\n };\n }\n },\n format: function(obj) {\n if (typeof obj === 'string') return obj;\n if (obj instanceof URL) return obj.href;\n var auth = obj.auth ? obj.auth + '@' : '';\n var host = obj.host || ((obj.hostname || '') + (obj.port ? ':' + obj.port : ''));\n return (obj.protocol ? obj.protocol + '//' : '') + auth + host +\n (obj.pathname || '/') + (obj.search || '') + (obj.hash || '');\n }\n};\nglobalThis[Symbol.for('jb:url')] = _urlMod;\n";
13
+ /** Assert module — pure JS */
14
+ export declare const ASSERT_MODULE_SOURCE = "\nvar _deepEqual = function(a, b) {\n if (a === b) return true;\n if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false;\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n var ka = Object.keys(a), kb = Object.keys(b);\n if (ka.length !== kb.length) return false;\n for (var i = 0; i < ka.length; i++) {\n if (!_deepEqual(a[ka[i]], b[ka[i]])) return false;\n }\n return true;\n};\nvar _assert = function(val, msg) {\n if (!val) throw new Error(msg || 'AssertionError: expected truthy value');\n};\n_assert.ok = _assert;\n_assert.equal = function(a, b, msg) {\n if (a != b) throw new Error(msg || 'AssertionError: ' + a + ' != ' + b);\n};\n_assert.notEqual = function(a, b, msg) {\n if (a == b) throw new Error(msg || 'AssertionError: ' + a + ' == ' + b);\n};\n_assert.strictEqual = function(a, b, msg) {\n if (a !== b) throw new Error(msg || 'AssertionError: ' + a + ' !== ' + b);\n};\n_assert.notStrictEqual = function(a, b, msg) {\n if (a === b) throw new Error(msg || 'AssertionError: ' + a + ' === ' + b);\n};\n_assert.deepEqual = function(a, b, msg) {\n if (!_deepEqual(a, b)) throw new Error(msg || 'AssertionError: objects not deep equal');\n};\n_assert.deepStrictEqual = _assert.deepEqual;\n_assert.notDeepEqual = function(a, b, msg) {\n if (_deepEqual(a, b)) throw new Error(msg || 'AssertionError: objects are deep equal');\n};\n_assert.throws = function(fn, expected, msg) {\n var threw = false;\n try { fn(); } catch(e) {\n threw = true;\n if (expected instanceof RegExp && !expected.test(e.message))\n throw new Error(msg || 'AssertionError: error message did not match');\n }\n if (!threw) throw new Error(msg || 'AssertionError: function did not throw');\n};\n_assert.doesNotThrow = function(fn, msg) {\n // @banned-pattern-ignore: sandbox-internal assertion helper; e.message is from user code inside QuickJS, not host details\n try { fn(); } catch(e) {\n throw new Error(msg || 'AssertionError: function threw: ' + e.message);\n }\n};\n_assert.fail = function(msg) {\n throw new Error(msg || 'AssertionError: assert.fail()');\n};\nglobalThis[Symbol.for('jb:assert')] = _assert;\n";
15
+ /** Util module — format, inspect, promisify, types */
16
+ export declare const UTIL_MODULE_SOURCE = "\nvar _util = {\n format: function() {\n var args = Array.prototype.slice.call(arguments);\n if (args.length === 0) return '';\n var fmt = args[0];\n if (typeof fmt !== 'string') {\n return args.map(function(a) {\n return typeof a === 'string' ? a : JSON.stringify(a);\n }).join(' ');\n }\n var i = 1;\n var str = fmt.replace(/%[sdjifoO%]/g, function(m) {\n if (m === '%%') return '%';\n if (i >= args.length) return m;\n var v = args[i++];\n if (m === '%s') return String(v);\n if (m === '%d') return Number(v).toString();\n if (m === '%i') { var n = Number(v); return (isNaN(n) ? 'NaN' : Math.trunc(n)).toString(); }\n if (m === '%j') return JSON.stringify(v);\n if (m === '%f') return parseFloat(v).toString();\n if (m === '%o' || m === '%O') return JSON.stringify(v);\n return m;\n });\n while (i < args.length) {\n str += ' ' + (typeof args[i] === 'string' ? args[i] : JSON.stringify(args[i]));\n i++;\n }\n return str;\n },\n inspect: function(obj, opts) {\n if (obj === null) return 'null';\n if (obj === undefined) return 'undefined';\n if (typeof obj === 'string') return \"'\" + obj + \"'\";\n if (typeof obj === 'function') return '[Function: ' + (obj.name || 'anonymous') + ']';\n var seen = [];\n try {\n return JSON.stringify(obj, function(key, val) {\n if (typeof val === 'object' && val !== null) {\n if (seen.indexOf(val) !== -1) return '[Circular]';\n seen.push(val);\n }\n return val;\n });\n } catch(e) { return String(obj); }\n },\n promisify: function(fn) {\n return function() {\n var args = Array.prototype.slice.call(arguments);\n return new Promise(function(resolve, reject) {\n args.push(function(err, val) { if (err) reject(err); else resolve(val); });\n fn.apply(null, args);\n });\n };\n },\n types: {\n isDate: function(v) { return v instanceof Date; },\n isRegExp: function(v) { return v instanceof RegExp; },\n isArray: function(v) { return Array.isArray(v); },\n isMap: function(v) { return typeof Map !== 'undefined' && v instanceof Map; },\n isSet: function(v) { return typeof Set !== 'undefined' && v instanceof Set; }\n },\n inherits: function(ctor, superCtor) {\n ctor.prototype = Object.create(superCtor.prototype);\n ctor.prototype.constructor = ctor;\n }\n};\nglobalThis[Symbol.for('jb:util')] = _util;\n";
17
+ /** Buffer module — wraps Uint8Array, pure-JS UTF-8 (no TextEncoder/TextDecoder) */
18
+ export declare const BUFFER_MODULE_SOURCE = "\nfunction _utf8Encode(str) {\n var bytes = [];\n for (var i = 0; i < str.length; i++) {\n var c = str.charCodeAt(i);\n if (c < 0x80) {\n bytes.push(c);\n } else if (c < 0x800) {\n bytes.push(0xC0 | (c >> 6), 0x80 | (c & 0x3F));\n } else if (c >= 0xD800 && c <= 0xDBFF && i + 1 < str.length) {\n var lo = str.charCodeAt(++i);\n var cp = ((c - 0xD800) * 0x400) + (lo - 0xDC00) + 0x10000;\n bytes.push(0xF0 | (cp >> 18), 0x80 | ((cp >> 12) & 0x3F), 0x80 | ((cp >> 6) & 0x3F), 0x80 | (cp & 0x3F));\n } else {\n bytes.push(0xE0 | (c >> 12), 0x80 | ((c >> 6) & 0x3F), 0x80 | (c & 0x3F));\n }\n }\n return bytes;\n}\nfunction _utf8Decode(bytes) {\n var str = '';\n var i = 0;\n while (i < bytes.length) {\n var b = bytes[i];\n if (b < 0x80) { str += String.fromCharCode(b); i++; }\n else if ((b & 0xE0) === 0xC0) { str += String.fromCharCode(((b & 0x1F) << 6) | (bytes[i+1] & 0x3F)); i += 2; }\n else if ((b & 0xF0) === 0xE0) { str += String.fromCharCode(((b & 0x0F) << 12) | ((bytes[i+1] & 0x3F) << 6) | (bytes[i+2] & 0x3F)); i += 3; }\n else if ((b & 0xF8) === 0xF0) { var cp = ((b & 0x07) << 18) | ((bytes[i+1] & 0x3F) << 12) | ((bytes[i+2] & 0x3F) << 6) | (bytes[i+3] & 0x3F); cp -= 0x10000; str += String.fromCharCode((cp >> 10) + 0xD800, (cp & 0x3FF) + 0xDC00); i += 4; }\n else { i++; }\n }\n return str;\n}\n// _utf8Encode/_utf8Decode are IIFE-local vars, available to all module shims\n\nfunction Buffer(arg) {\n if (typeof arg === 'number') {\n this._data = new Uint8Array(arg);\n } else if (arg instanceof ArrayBuffer) {\n this._data = new Uint8Array(arg);\n } else if (arg instanceof Uint8Array) {\n this._data = new Uint8Array(arg);\n } else if (Array.isArray(arg)) {\n this._data = new Uint8Array(arg);\n } else {\n this._data = new Uint8Array(0);\n }\n this.length = this._data.length;\n}\nBuffer.from = function(data, encoding) {\n if (typeof data === 'string') {\n return new Buffer(_utf8Encode(data));\n }\n if (data instanceof ArrayBuffer) return new Buffer(data);\n if (data instanceof Uint8Array) return new Buffer(data);\n if (Array.isArray(data)) return new Buffer(data);\n if (data && data._data) return new Buffer(data._data.slice());\n return new Buffer(0);\n};\nBuffer.alloc = function(size, fill) {\n var buf = new Buffer(size);\n if (fill !== undefined) {\n var fillByte = typeof fill === 'number' ? fill : 0;\n buf._data.fill(fillByte);\n }\n return buf;\n};\nBuffer.allocUnsafe = Buffer.alloc;\nBuffer.isBuffer = function(obj) { return obj instanceof Buffer; };\nBuffer.concat = function(list, totalLength) {\n if (!totalLength) {\n totalLength = 0;\n for (var i = 0; i < list.length; i++) totalLength += list[i].length;\n }\n var result = new Uint8Array(totalLength);\n var offset = 0;\n for (var i = 0; i < list.length; i++) {\n result.set(list[i]._data, offset);\n offset += list[i].length;\n }\n return new Buffer(result);\n};\nBuffer.byteLength = function(str) {\n return _utf8Encode(str).length;\n};\nBuffer.prototype.toString = function(encoding) {\n return _utf8Decode(this._data);\n};\nBuffer.prototype.toJSON = function() {\n return { type: 'Buffer', data: Array.from(this._data) };\n};\nBuffer.prototype.slice = function(start, end) {\n return new Buffer(this._data.slice(start, end));\n};\nBuffer.prototype.copy = function(target, targetStart, sourceStart, sourceEnd) {\n targetStart = targetStart || 0;\n sourceStart = sourceStart || 0;\n sourceEnd = sourceEnd || this.length;\n var sub = this._data.subarray(sourceStart, sourceEnd);\n target._data.set(sub, targetStart);\n return sub.length;\n};\nBuffer.prototype.write = function(str, offset) {\n var bytes = _utf8Encode(str);\n offset = offset || 0;\n this._data.set(bytes, offset);\n return bytes.length;\n};\nBuffer.prototype.fill = function(val, offset, end) {\n this._data.fill(typeof val === 'number' ? val : 0, offset, end);\n return this;\n};\nBuffer.prototype.equals = function(other) {\n if (this.length !== other.length) return false;\n for (var i = 0; i < this.length; i++) {\n if (this._data[i] !== other._data[i]) return false;\n }\n return true;\n};\nBuffer.prototype.readUInt8 = function(offset) { return this._data[offset]; };\nBuffer.prototype.writeUInt8 = function(value, offset) { this._data[offset] = value; return offset + 1; };\nglobalThis[Symbol.for('jb:buffer')] = { Buffer: Buffer };\nglobalThis.Buffer = Buffer;\n";
19
+ /** Stream module — minimal stubs based on EventEmitter */
20
+ export declare const STREAM_MODULE_SOURCE = "\nvar _EE = globalThis[Symbol.for('jb:events')].EventEmitter;\n\nfunction Stream() { _EE.call(this); }\nStream.prototype = Object.create(_EE.prototype);\nStream.prototype.constructor = Stream;\nStream.prototype.pipe = function(dest) {\n this.on('data', function(chunk) { dest.write(chunk); });\n this.on('end', function() { if (dest.end) dest.end(); });\n return dest;\n};\n\nfunction Readable(opts) {\n Stream.call(this);\n this.readable = true;\n this._readableState = { ended: false, buffer: [] };\n}\nReadable.prototype = Object.create(Stream.prototype);\nReadable.prototype.constructor = Readable;\nReadable.prototype.read = function() { return null; };\nReadable.prototype.push = function(chunk) {\n if (chunk === null) { this._readableState.ended = true; this.emit('end'); return false; }\n this.emit('data', chunk);\n return true;\n};\nReadable.prototype.destroy = function() { this.emit('close'); return this; };\n\nfunction Writable(opts) {\n Stream.call(this);\n this.writable = true;\n this._writableState = { ended: false };\n}\nWritable.prototype = Object.create(Stream.prototype);\nWritable.prototype.constructor = Writable;\nWritable.prototype.write = function(chunk) { return true; };\nWritable.prototype.end = function(chunk) {\n if (chunk) this.write(chunk);\n this._writableState.ended = true;\n this.emit('finish');\n return this;\n};\nWritable.prototype.destroy = function() { this.emit('close'); return this; };\n\nfunction Duplex(opts) {\n Readable.call(this, opts);\n Writable.call(this, opts);\n}\nDuplex.prototype = Object.create(Readable.prototype);\nvar _wKeys = Object.keys(Writable.prototype);\nfor (var _wi = 0; _wi < _wKeys.length; _wi++) {\n if (!Duplex.prototype[_wKeys[_wi]]) Duplex.prototype[_wKeys[_wi]] = Writable.prototype[_wKeys[_wi]];\n}\nDuplex.prototype.constructor = Duplex;\n\nfunction Transform(opts) { Duplex.call(this, opts); }\nTransform.prototype = Object.create(Duplex.prototype);\nTransform.prototype.constructor = Transform;\nTransform.prototype._transform = function(chunk, encoding, cb) { if (cb) cb(null, chunk); };\n\nfunction PassThrough(opts) { Transform.call(this, opts); }\nPassThrough.prototype = Object.create(Transform.prototype);\nPassThrough.prototype.constructor = PassThrough;\n\nfunction pipeline() {\n var streams = Array.prototype.slice.call(arguments);\n var cb = typeof streams[streams.length - 1] === 'function' ? streams.pop() : null;\n for (var i = 0; i < streams.length - 1; i++) streams[i].pipe(streams[i + 1]);\n if (cb) {\n var last = streams[streams.length - 1];\n last.on('finish', function() { cb(null); });\n last.on('error', function(e) { cb(e); });\n }\n return streams[streams.length - 1];\n}\n\nglobalThis[Symbol.for('jb:stream')] = {\n Stream: Stream, Readable: Readable, Writable: Writable,\n Duplex: Duplex, Transform: Transform, PassThrough: PassThrough,\n pipeline: pipeline\n};\n";
21
+ /** StringDecoder module — uses _utf8Decode from Buffer shim */
22
+ export declare const STRING_DECODER_MODULE_SOURCE = "\nfunction StringDecoder(encoding) {\n this.encoding = (encoding || 'utf-8').toLowerCase();\n if (this.encoding === 'utf8') this.encoding = 'utf-8';\n}\nStringDecoder.prototype.write = function(buf) {\n if (typeof buf === 'string') return buf;\n var data = buf instanceof Uint8Array ? buf : (buf && buf._data ? buf._data : new Uint8Array(0));\n return _utf8Decode(data);\n};\nStringDecoder.prototype.end = function(buf) {\n if (buf) return this.write(buf);\n return '';\n};\nglobalThis[Symbol.for('jb:string_decoder')] = { StringDecoder: StringDecoder };\n";
23
+ /** Querystring module — pure JS */
24
+ export declare const QUERYSTRING_MODULE_SOURCE = "\nvar _qs = {\n parse: function(str, sep, eq) {\n sep = sep || '&'; eq = eq || '=';\n var result = Object.create(null);\n if (!str || typeof str !== 'string') return result;\n var pairs = str.split(sep);\n for (var i = 0; i < pairs.length; i++) {\n var idx = pairs[i].indexOf(eq);\n var key, val;\n if (idx >= 0) {\n key = decodeURIComponent(pairs[i].slice(0, idx).replace(/\\+/g, ' '));\n val = decodeURIComponent(pairs[i].slice(idx + 1).replace(/\\+/g, ' '));\n } else {\n key = decodeURIComponent(pairs[i].replace(/\\+/g, ' '));\n val = '';\n }\n if (result[key] !== undefined) {\n if (Array.isArray(result[key])) result[key].push(val);\n else result[key] = [result[key], val];\n } else {\n result[key] = val;\n }\n }\n return result;\n },\n stringify: function(obj, sep, eq) {\n sep = sep || '&'; eq = eq || '=';\n var pairs = [];\n var keys = Object.keys(obj);\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i];\n var val = obj[key];\n if (Array.isArray(val)) {\n for (var j = 0; j < val.length; j++) {\n pairs.push(encodeURIComponent(key) + eq + encodeURIComponent(val[j]));\n }\n } else {\n pairs.push(encodeURIComponent(key) + eq + encodeURIComponent(val));\n }\n }\n return pairs.join(sep);\n },\n escape: function(str) { return encodeURIComponent(str); },\n unescape: function(str) { return decodeURIComponent(str); }\n};\n_qs.decode = _qs.parse;\n_qs.encode = _qs.stringify;\nglobalThis[Symbol.for('jb:querystring')] = _qs;\n";
25
+ /**
26
+ * Unsupported Node.js modules — throw clear error at require/import time.
27
+ * Map of module name to hint message.
28
+ */
29
+ export declare const UNSUPPORTED_MODULES: Record<string, string>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Pure-JS POSIX path module source for QuickJS.
3
+ * Exported as a string constant to be evaluated inside the sandbox.
4
+ * Wrapped in an IIFE to avoid name conflicts with module-level exports.
5
+ */
6
+ export declare const PATH_MODULE_SOURCE = "\n(function() {\n var sep = '/';\n var delimiter = ':';\n\n function normalize(p) {\n if (p === '') return '.';\n var isAbs = p.charCodeAt(0) === 47;\n var trailingSlash = p.charCodeAt(p.length - 1) === 47;\n var parts = p.split('/');\n var out = [];\n for (var i = 0; i < parts.length; i++) {\n var seg = parts[i];\n if (seg === '' || seg === '.') continue;\n if (seg === '..') {\n if (out.length > 0 && out[out.length - 1] !== '..') out.pop();\n else if (!isAbs) out.push('..');\n } else {\n out.push(seg);\n }\n }\n var result = out.join('/');\n if (isAbs) result = '/' + result;\n if (trailingSlash && result[result.length - 1] !== '/') result += '/';\n return result || (isAbs ? '/' : '.');\n }\n\n function join() {\n var joined = '';\n for (var i = 0; i < arguments.length; i++) {\n var arg = arguments[i];\n if (typeof arg !== 'string') throw new TypeError('Path must be a string');\n if (arg.length > 0) {\n if (joined.length > 0) joined += '/' + arg;\n else joined = arg;\n }\n }\n if (joined.length === 0) return '.';\n return normalize(joined);\n }\n\n function resolve() {\n var resolved = '';\n var resolvedAbsolute = false;\n for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {\n var path = i >= 0 ? arguments[i] : globalThis.process.cwd();\n if (typeof path !== 'string') throw new TypeError('Path must be a string');\n if (path.length === 0) continue;\n if (resolved.length > 0) resolved = path + '/' + resolved;\n else resolved = path;\n resolvedAbsolute = path.charCodeAt(0) === 47;\n }\n resolved = normalize(resolved);\n if (resolvedAbsolute) return '/' + resolved.replace(/^\\/+/, '');\n return resolved.length > 0 ? resolved : '.';\n }\n\n function isAbsolute(p) {\n return typeof p === 'string' && p.length > 0 && p.charCodeAt(0) === 47;\n }\n\n function dirname(p) {\n if (p.length === 0) return '.';\n var hasRoot = p.charCodeAt(0) === 47;\n var end = -1;\n for (var i = p.length - 1; i >= 1; i--) {\n if (p.charCodeAt(i) === 47) { end = i; break; }\n }\n if (end === -1) return hasRoot ? '/' : '.';\n if (hasRoot && end === 0) return '/';\n return p.slice(0, end);\n }\n\n function basename(p, ext) {\n var start = 0;\n for (var i = p.length - 1; i >= 0; i--) {\n if (p.charCodeAt(i) === 47) { start = i + 1; break; }\n }\n var base = p.slice(start);\n if (ext && base.endsWith(ext)) {\n base = base.slice(0, base.length - ext.length);\n }\n return base;\n }\n\n function extname(p) {\n var startDot = -1;\n var startPart = 0;\n for (var i = p.length - 1; i >= 0; i--) {\n var code = p.charCodeAt(i);\n if (code === 47) { startPart = i + 1; break; }\n if (code === 46 && startDot === -1) startDot = i;\n }\n if (startDot === -1 || startDot === startPart ||\n (startDot === startPart + 1 && p.charCodeAt(startPart) === 46)) {\n return '';\n }\n return p.slice(startDot);\n }\n\n function relative(from, to) {\n if (from === to) return '';\n from = resolve(from);\n to = resolve(to);\n if (from === to) return '';\n var fromParts = from.split('/').filter(Boolean);\n var toParts = to.split('/').filter(Boolean);\n var common = 0;\n var length = Math.min(fromParts.length, toParts.length);\n for (var i = 0; i < length; i++) {\n if (fromParts[i] !== toParts[i]) break;\n common++;\n }\n var ups = [];\n for (var i = common; i < fromParts.length; i++) ups.push('..');\n return ups.concat(toParts.slice(common)).join('/') || '.';\n }\n\n function parse(p) {\n var root = p.charCodeAt(0) === 47 ? '/' : '';\n var dir = dirname(p);\n var base = basename(p);\n var ext = extname(p);\n var name = ext ? base.slice(0, base.length - ext.length) : base;\n return { root: root, dir: dir, base: base, ext: ext, name: name };\n }\n\n function format(obj) {\n var dir = obj.dir || obj.root || '';\n var base = obj.base || ((obj.name || '') + (obj.ext || ''));\n if (!dir) return base;\n if (dir === obj.root) return dir + base;\n return dir + '/' + base;\n }\n\n var posix = { sep: sep, delimiter: delimiter, join: join, resolve: resolve, normalize: normalize, isAbsolute: isAbsolute, dirname: dirname, basename: basename, extname: extname, relative: relative, parse: parse, format: format };\n posix.posix = posix;\n\n globalThis[Symbol.for('jb:path')] = posix;\n})();\n";
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Worker thread for JavaScript execution via QuickJS.
3
+ * Keeps QuickJS loaded and handles multiple execution requests.
4
+ *
5
+ * Defense-in-depth activates AFTER QuickJS loads (WASM init needs unrestricted JS).
6
+ * User JavaScript code runs inside the QuickJS sandbox with no access to Node.js globals.
7
+ *
8
+ * Build: Bundled to worker.js via esbuild (see package.json "build:worker").
9
+ * Run: npx esbuild src/commands/js-exec/worker.ts --bundle --platform=node --format=esm --outfile=src/commands/js-exec/worker.js --external:quickjs-emscripten
10
+ */
11
+ import { type WorkerDefenseStats } from "../../security/index.js";
12
+ export interface JsExecWorkerInput {
13
+ protocolToken: string;
14
+ sharedBuffer: SharedArrayBuffer;
15
+ jsCode: string;
16
+ cwd: string;
17
+ env: Record<string, string>;
18
+ args: string[];
19
+ scriptPath?: string;
20
+ bootstrapCode?: string;
21
+ isModule?: boolean;
22
+ stripTypes?: boolean;
23
+ timeoutMs?: number;
24
+ }
25
+ export interface JsExecWorkerOutput {
26
+ protocolToken?: string;
27
+ success: boolean;
28
+ error?: string;
29
+ defenseStats?: WorkerDefenseStats;
30
+ }
@@ -18,6 +18,7 @@ export interface WorkerInput {
18
18
  env: Record<string, string>;
19
19
  args: string[];
20
20
  scriptPath?: string;
21
+ timeoutMs?: number;
21
22
  }
22
23
  export interface WorkerOutput {
23
24
  success: boolean;
@@ -60,6 +60,17 @@ export declare function sanitizeParsedData(value: unknown): unknown;
60
60
  * Returns null if the value is not a non-array object.
61
61
  */
62
62
  export declare function asQueryRecord(value: unknown): Record<string, unknown> | null;
63
+ /**
64
+ * Create a null-prototype object from a static lookup table literal.
65
+ * Use this to define Record/dictionary constants that are safe from
66
+ * prototype pollution (e.g., `__proto__` lookups return `undefined`).
67
+ *
68
+ * ```ts
69
+ * const COLORS = nullPrototype({ red: "#f00", blue: "#00f" });
70
+ * COLORS["__proto__"] // undefined (no prototype chain)
71
+ * ```
72
+ */
73
+ export declare function nullPrototype<T extends Record<string, unknown>>(obj: T): T;
63
74
  /**
64
75
  * Create a null-prototype shallow copy of an object.
65
76
  * This prevents prototype chain lookups without filtering any keys.
@@ -5,8 +5,10 @@ export type CommandName = "echo" | "cat" | "printf" | "ls" | "mkdir" | "rmdir" |
5
5
  export type NetworkCommandName = "curl";
6
6
  /** Python command names (only available when python is explicitly enabled) */
7
7
  export type PythonCommandName = "python3" | "python";
8
- /** All command names including network and python commands */
9
- export type AllCommandName = CommandName | NetworkCommandName | PythonCommandName;
8
+ /** JavaScript command names (only available when javascript is explicitly enabled) */
9
+ export type JavaScriptCommandName = "js-exec" | "node";
10
+ /** All command names including network, python, and javascript commands */
11
+ export type AllCommandName = CommandName | NetworkCommandName | PythonCommandName | JavaScriptCommandName;
10
12
  /**
11
13
  * Gets all available command names (excludes network commands)
12
14
  */
@@ -35,6 +37,15 @@ export declare function getPythonCommandNames(): string[];
35
37
  * Note: Python introduces additional security surface (arbitrary code execution).
36
38
  */
37
39
  export declare function createPythonCommands(): Command[];
40
+ /**
41
+ * Gets all javascript command names
42
+ */
43
+ export declare function getJavaScriptCommandNames(): string[];
44
+ /**
45
+ * Creates javascript commands for registration (js-exec).
46
+ * These are only registered when javascript is explicitly enabled.
47
+ */
48
+ export declare function createJavaScriptCommands(): Command[];
38
49
  /**
39
50
  * Clears the command cache (for testing)
40
51
  */
@@ -1,33 +1,47 @@
1
1
  /**
2
- * Main thread filesystem bridge handler
2
+ * Main thread bridge handler
3
3
  *
4
- * Runs on the main thread and processes filesystem requests from the worker thread.
5
- * Uses SharedArrayBuffer + Atomics for synchronization.
4
+ * Runs on the main thread and processes filesystem, I/O, HTTP, and exec
5
+ * requests from a worker thread via SharedArrayBuffer + Atomics.
6
6
  */
7
7
  import type { IFileSystem } from "../../fs/interface.js";
8
8
  import type { SecureFetch } from "../../network/fetch.js";
9
- export interface FsBridgeOutput {
9
+ import type { CommandExecOptions, ExecResult } from "../../types.js";
10
+ export interface BridgeOutput {
10
11
  stdout: string;
11
12
  stderr: string;
12
13
  exitCode: number;
13
14
  }
14
15
  /**
15
- * Handles filesystem requests from the worker thread.
16
+ * Handles requests from a worker thread.
16
17
  */
17
- export declare class FsBridgeHandler {
18
+ export declare class BridgeHandler {
18
19
  private fs;
19
20
  private cwd;
21
+ private commandName;
20
22
  private secureFetch;
21
23
  private maxOutputSize;
24
+ private exec;
22
25
  private protocol;
23
26
  private running;
24
27
  private output;
25
28
  private outputLimitExceeded;
26
- constructor(sharedBuffer: SharedArrayBuffer, fs: IFileSystem, cwd: string, secureFetch?: SecureFetch | undefined, maxOutputSize?: number);
29
+ private startTime;
30
+ private timeoutMs;
31
+ constructor(sharedBuffer: SharedArrayBuffer, fs: IFileSystem, cwd: string, commandName: string, secureFetch?: SecureFetch | undefined, maxOutputSize?: number, exec?: ((command: string, options: CommandExecOptions) => Promise<ExecResult>) | undefined);
32
+ /**
33
+ * Returns remaining milliseconds before the overall execution deadline.
34
+ */
35
+ private remainingMs;
36
+ /**
37
+ * Races a promise against the remaining execution deadline.
38
+ * If the deadline expires first, sets `this.running = false` and rejects.
39
+ */
40
+ private raceDeadline;
27
41
  /**
28
42
  * Run the handler loop until EXIT operation or timeout.
29
43
  */
30
- run(timeoutMs: number): Promise<FsBridgeOutput>;
44
+ run(timeoutMs: number): Promise<BridgeOutput>;
31
45
  stop(): void;
32
46
  private handleOperation;
33
47
  private resolvePath;
@@ -44,11 +58,14 @@ export declare class FsBridgeHandler {
44
58
  private handleReadlink;
45
59
  private handleChmod;
46
60
  private handleRealpath;
61
+ private handleRename;
62
+ private handleCopyFile;
47
63
  private handleWriteStdout;
48
64
  private handleWriteStderr;
49
65
  private handleExit;
50
66
  private tryAppendOutput;
51
67
  private appendOutputLimitError;
52
68
  private handleHttpRequest;
69
+ private handleExecCommand;
53
70
  private setErrorFromException;
54
71
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
- * SharedArrayBuffer protocol for synchronous filesystem bridge
2
+ * SharedArrayBuffer protocol for synchronous worker bridge
3
3
  *
4
- * This protocol enables synchronous filesystem access from a worker thread
5
- * (where CPython/Python runs) to the main thread (which has async IFileSystem).
4
+ * This protocol enables synchronous filesystem and I/O access from a worker thread
5
+ * (where CPython/Python or QuickJS runs) to the main thread (which has async IFileSystem).
6
6
  */
7
7
  declare global {
8
8
  interface Atomics {
@@ -31,10 +31,13 @@ export declare const OpCode: {
31
31
  readonly LSTAT: 11;
32
32
  readonly CHMOD: 12;
33
33
  readonly REALPATH: 13;
34
+ readonly RENAME: 14;
35
+ readonly COPY_FILE: 15;
34
36
  readonly WRITE_STDOUT: 100;
35
37
  readonly WRITE_STDERR: 101;
36
38
  readonly EXIT: 102;
37
39
  readonly HTTP_REQUEST: 200;
40
+ readonly EXEC_COMMAND: 300;
38
41
  };
39
42
  export type OpCodeType = (typeof OpCode)[keyof typeof OpCode];
40
43
  /** Status codes for synchronization */
@@ -1,15 +1,16 @@
1
1
  /**
2
- * Worker-side synchronous filesystem backend
2
+ * Worker-side synchronous backend
3
3
  *
4
4
  * Runs in the worker thread and makes synchronous calls to the main thread
5
5
  * via SharedArrayBuffer + Atomics.
6
6
  */
7
7
  /**
8
- * Synchronous filesystem backend for Pyodide worker.
8
+ * Synchronous backend for worker threads.
9
9
  */
10
- export declare class SyncFsBackend {
10
+ export declare class SyncBackend {
11
11
  private protocol;
12
- constructor(sharedBuffer: SharedArrayBuffer);
12
+ private operationTimeoutMs;
13
+ constructor(sharedBuffer: SharedArrayBuffer, operationTimeoutMs?: number);
13
14
  private execSync;
14
15
  readFile(path: string): Uint8Array;
15
16
  writeFile(path: string, data: Uint8Array): void;
@@ -38,6 +39,8 @@ export declare class SyncFsBackend {
38
39
  readlink(path: string): string;
39
40
  chmod(path: string, mode: number): void;
40
41
  realpath(path: string): string;
42
+ rename(oldPath: string, newPath: string): void;
43
+ copyFile(src: string, dest: string): void;
41
44
  writeStdout(data: string): void;
42
45
  writeStderr(data: string): void;
43
46
  exit(code: number): void;
@@ -56,4 +59,22 @@ export declare class SyncFsBackend {
56
59
  body: string;
57
60
  url: string;
58
61
  };
62
+ /**
63
+ * Execute a shell command through the main thread's exec function.
64
+ * Returns the result as { stdout, stderr, exitCode }.
65
+ */
66
+ execCommand(command: string, stdin?: string): {
67
+ stdout: string;
68
+ stderr: string;
69
+ exitCode: number;
70
+ };
71
+ /**
72
+ * Execute a shell command with structured args (shell-escaped on the main thread).
73
+ * Prevents command injection from unsanitized args.
74
+ */
75
+ execCommandArgs(command: string, args: string[]): {
76
+ stdout: string;
77
+ stderr: string;
78
+ exitCode: number;
79
+ };
59
80
  }
package/dist/index.d.cts CHANGED
@@ -1,8 +1,8 @@
1
1
  export type { CommandNode, PipelineNode, ScriptNode, SimpleCommandNode, StatementNode, WordNode, } from "./ast/types.js";
2
- export type { BashLogger, BashOptions, ExecOptions } from "./Bash.js";
2
+ export type { BashLogger, BashOptions, ExecOptions, JavaScriptConfig, } from "./Bash.js";
3
3
  export { Bash } from "./Bash.js";
4
- export type { AllCommandName, CommandName, NetworkCommandName, PythonCommandName, } from "./commands/registry.js";
5
- export { getCommandNames, getNetworkCommandNames, getPythonCommandNames, } from "./commands/registry.js";
4
+ export type { AllCommandName, CommandName, JavaScriptCommandName, NetworkCommandName, PythonCommandName, } from "./commands/registry.js";
5
+ export { getCommandNames, getJavaScriptCommandNames, getNetworkCommandNames, getPythonCommandNames, } from "./commands/registry.js";
6
6
  export type { CustomCommand, LazyCommand } from "./custom-commands.js";
7
7
  export { defineCommand } from "./custom-commands.js";
8
8
  export { InMemoryFs } from "./fs/in-memory-fs/index.js";
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export type { CommandNode, PipelineNode, ScriptNode, SimpleCommandNode, StatementNode, WordNode, } from "./ast/types.js";
2
- export type { BashLogger, BashOptions, ExecOptions } from "./Bash.js";
2
+ export type { BashLogger, BashOptions, ExecOptions, JavaScriptConfig, } from "./Bash.js";
3
3
  export { Bash } from "./Bash.js";
4
- export type { AllCommandName, CommandName, NetworkCommandName, PythonCommandName, } from "./commands/registry.js";
5
- export { getCommandNames, getNetworkCommandNames, getPythonCommandNames, } from "./commands/registry.js";
4
+ export type { AllCommandName, CommandName, JavaScriptCommandName, NetworkCommandName, PythonCommandName, } from "./commands/registry.js";
5
+ export { getCommandNames, getJavaScriptCommandNames, getNetworkCommandNames, getPythonCommandNames, } from "./commands/registry.js";
6
6
  export type { CustomCommand, LazyCommand } from "./custom-commands.js";
7
7
  export { defineCommand } from "./custom-commands.js";
8
8
  export { InMemoryFs } from "./fs/in-memory-fs/index.js";
@@ -25,6 +25,7 @@ export interface InterpreterOptions {
25
25
  cwd?: string;
26
26
  replaceEnv?: boolean;
27
27
  signal?: AbortSignal;
28
+ args?: string[];
28
29
  }) => Promise<ExecResult>;
29
30
  /** Optional secure fetch function for network-enabled commands */
30
31
  fetch?: SecureFetch;
@@ -38,6 +39,8 @@ export interface InterpreterOptions {
38
39
  * When true, fail closed if execution occurs outside defense async context.
39
40
  */
40
41
  requireDefenseContext?: boolean;
42
+ /** Bootstrap JavaScript code for js-exec */
43
+ jsBootstrapCode?: string;
41
44
  }
42
45
  export declare class Interpreter {
43
46
  private ctx;
@@ -302,6 +302,8 @@ export interface InterpreterState extends VariableAttributeState, LocalScopingSt
302
302
  * Checked at statement boundaries; when aborted, execution stops.
303
303
  */
304
304
  signal?: AbortSignal;
305
+ /** Extra arguments injected via exec({ args }), appended to first command's args */
306
+ extraArgs?: string[];
305
307
  }
306
308
  export interface InterpreterContext {
307
309
  state: InterpreterState;
@@ -314,6 +316,7 @@ export interface InterpreterContext {
314
316
  cwd?: string;
315
317
  replaceEnv?: boolean;
316
318
  signal?: AbortSignal;
319
+ args?: string[];
317
320
  }) => Promise<ExecResult>;
318
321
  executeScript: (node: ScriptNode) => Promise<ExecResult>;
319
322
  executeStatement: (node: StatementNode) => Promise<ExecResult>;
@@ -333,4 +336,9 @@ export interface InterpreterContext {
333
336
  * sandbox async context. Used to fail closed on context-loss bugs.
334
337
  */
335
338
  requireDefenseContext?: boolean;
339
+ /**
340
+ * Bootstrap JavaScript code for js-exec.
341
+ * Threaded through the context chain instead of shell env.
342
+ */
343
+ jsBootstrapCode?: string;
336
344
  }
package/dist/limits.d.ts CHANGED
@@ -23,8 +23,10 @@ export interface ExecutionLimits {
23
23
  maxJqIterations?: number;
24
24
  /** Maximum sqlite3 query execution time in milliseconds (default: 5000) */
25
25
  maxSqliteTimeoutMs?: number;
26
- /** Maximum Python execution time in milliseconds (default: 30000) */
26
+ /** Maximum Python execution time in milliseconds (default: 10000, or 60000 with network) */
27
27
  maxPythonTimeoutMs?: number;
28
+ /** Maximum JavaScript (js-exec) execution time in milliseconds (default: 10000, or 60000 with network) */
29
+ maxJsTimeoutMs?: number;
28
30
  /** Maximum glob filesystem operations (default: 100000) */
29
31
  maxGlobOperations?: number;
30
32
  /** Maximum string length in bytes (default: 10MB = 10485760) */
package/dist/types.d.ts CHANGED
@@ -53,6 +53,12 @@ export interface CommandExecOptions {
53
53
  * Used by `timeout` to ensure timed-out commands don't continue running.
54
54
  */
55
55
  signal?: AbortSignal;
56
+ /**
57
+ * Additional argv entries appended to the first executed command.
58
+ * Values bypass shell parsing entirely — no escaping, splitting, or globbing.
59
+ * Like child_process.spawnSync(cmd, args).
60
+ */
61
+ args?: string[];
56
62
  }
57
63
  /**
58
64
  * Context provided to commands during execution.
@@ -170,6 +176,12 @@ export interface CommandContext {
170
176
  * before and after awaited operations.
171
177
  */
172
178
  requireDefenseContext?: boolean;
179
+ /**
180
+ * Bootstrap JavaScript code for js-exec.
181
+ * Threaded through the context chain instead of shell env to prevent
182
+ * user access/injection via environment variables.
183
+ */
184
+ jsBootstrapCode?: string;
173
185
  }
174
186
  export interface Command {
175
187
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "just-bash",
3
- "version": "2.12.8",
3
+ "version": "2.13.0",
4
4
  "description": "A simulated bash environment with virtual filesystem",
5
5
  "repository": {
6
6
  "type": "git",
@@ -59,7 +59,6 @@
59
59
  "license": "Apache-2.0",
60
60
  "devDependencies": {
61
61
  "@biomejs/biome": "^2.3.10",
62
- "fast-check": "^3.23.2",
63
62
  "@types/ini": "^4.1.1",
64
63
  "@types/node": "^25.0.3",
65
64
  "@types/papaparse": "^5.5.2",
@@ -68,6 +67,7 @@
68
67
  "@types/turndown": "^5.0.6",
69
68
  "@vitest/coverage-v8": "^4.0.18",
70
69
  "esbuild": "^0.27.2",
70
+ "fast-check": "^3.23.2",
71
71
  "knip": "^5.41.1",
72
72
  "typescript": "^5.9.3",
73
73
  "vitest": "^4.0.16"
@@ -81,6 +81,7 @@
81
81
  "minimatch": "^10.1.1",
82
82
  "modern-tar": "^0.7.3",
83
83
  "papaparse": "^5.5.3",
84
+ "quickjs-emscripten": "^0.32.0",
84
85
  "re2js": "^1.2.1",
85
86
  "smol-toml": "^1.6.0",
86
87
  "sprintf-js": "^1.1.3",
@@ -96,25 +97,25 @@
96
97
  "scripts": {
97
98
  "build": "rm -rf dist && tsc && pnpm build:lib && pnpm build:lib:cjs && pnpm build:browser && pnpm build:cli && pnpm build:shell && pnpm build:worker && pnpm build:clean && cp dist/index.d.ts dist/index.d.cts && sed '1,/^-->/d' AGENTS.npm.md > dist/AGENTS.md",
98
99
  "build:clean": "find dist -name '*.test.js' -delete && find dist -name '*.test.d.ts' -delete",
99
- "build:worker": "esbuild src/commands/python3/worker.ts --bundle --platform=node --format=esm --outfile=src/commands/python3/worker.js --external:../../../vendor/cpython-emscripten/* && cp src/commands/python3/worker.js dist/commands/python3/worker.js && mkdir -p dist/bin/chunks && cp src/commands/python3/worker.js dist/bin/chunks/worker.js && mkdir -p dist/bundle/chunks && cp src/commands/python3/worker.js dist/bundle/chunks/worker.js",
100
- "build:lib": "esbuild dist/index.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bundle --chunk-names=chunks/[name]-[hash] --external:diff --external:minimatch --external:sprintf-js --external:turndown --external:sql.js --external:@mongodb-js/zstd --external:node-liblzma --external:compressjs",
101
- "build:lib:cjs": "esbuild dist/index.js --bundle --platform=node --format=cjs --minify --outfile=dist/bundle/index.cjs --external:diff --external:minimatch --external:sprintf-js --external:turndown --external:sql.js --external:@mongodb-js/zstd --external:node-liblzma --external:compressjs",
100
+ "build:worker": "esbuild src/commands/python3/worker.ts --bundle --platform=node --format=esm --outfile=src/commands/python3/worker.js --external:../../../vendor/cpython-emscripten/* && cp src/commands/python3/worker.js dist/commands/python3/worker.js && mkdir -p dist/bin/chunks && cp src/commands/python3/worker.js dist/bin/chunks/worker.js && mkdir -p dist/bundle/chunks && cp src/commands/python3/worker.js dist/bundle/chunks/worker.js && esbuild src/commands/js-exec/worker.ts --bundle --platform=node --format=esm --outfile=src/commands/js-exec/worker.js --external:quickjs-emscripten && cp src/commands/js-exec/worker.js dist/commands/js-exec/worker.js && cp src/commands/js-exec/worker.js dist/bin/chunks/js-exec-worker.js && cp src/commands/js-exec/worker.js dist/bundle/chunks/js-exec-worker.js",
101
+ "build:lib": "esbuild dist/index.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bundle --chunk-names=chunks/[name]-[hash] --external:diff --external:minimatch --external:sprintf-js --external:turndown --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:compressjs",
102
+ "build:lib:cjs": "esbuild dist/index.js --bundle --platform=node --format=cjs --minify --outfile=dist/bundle/index.cjs --external:diff --external:minimatch --external:sprintf-js --external:turndown --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:compressjs",
102
103
  "build:browser": "esbuild dist/browser.js --bundle --platform=browser --format=esm --minify --outfile=dist/bundle/browser.js --external:diff --external:minimatch --external:sprintf-js --external:turndown --external:node:* --external:@mongodb-js/zstd --external:node-liblzma --external:compressjs --define:__BROWSER__=true",
103
- "build:cli": "esbuild dist/cli/just-bash.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bin --entry-names=[name] --chunk-names=chunks/[name]-[hash] --banner:js='#!/usr/bin/env node' --external:sql.js --external:@mongodb-js/zstd --external:node-liblzma --external:compressjs",
104
- "build:shell": "esbuild dist/cli/shell.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bin/shell --entry-names=[name] --chunk-names=chunks/[name]-[hash] --banner:js='#!/usr/bin/env node' --external:sql.js --external:@mongodb-js/zstd --external:node-liblzma --external:compressjs",
105
- "validate": "pnpm lint && pnpm knip && pnpm typecheck && pnpm build && pnpm check:python-worker-sync && pnpm test:run && pnpm test:python && pnpm test:dist && pnpm test:examples",
104
+ "build:cli": "esbuild dist/cli/just-bash.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bin --entry-names=[name] --chunk-names=chunks/[name]-[hash] --banner:js='#!/usr/bin/env node' --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:compressjs",
105
+ "build:shell": "esbuild dist/cli/shell.js --bundle --splitting --platform=node --format=esm --minify --outdir=dist/bin/shell --entry-names=[name] --chunk-names=chunks/[name]-[hash] --banner:js='#!/usr/bin/env node' --external:sql.js --external:quickjs-emscripten --external:@mongodb-js/zstd --external:node-liblzma --external:compressjs",
106
+ "validate": "pnpm lint && pnpm knip && pnpm typecheck && pnpm build && pnpm check:worker-sync && pnpm test:run && pnpm test:wasm && pnpm test:dist && pnpm test:examples",
106
107
  "test:examples": "cd examples/cjs-consumer && pnpm install --no-frozen-lockfile && npx tsc --noEmit",
107
108
  "typecheck": "tsc --noEmit",
108
109
  "lint": "biome check . && pnpm lint:banned",
109
- "check:python-worker-sync": "node scripts/check-python-worker-sync.js",
110
+ "check:worker-sync": "node scripts/check-worker-sync.js",
110
111
  "lint:banned": "node scripts/check-banned-patterns.js",
111
112
  "lint:fix": "biome check --write .",
112
113
  "knip": "knip",
113
114
  "test": "vitest",
114
- "test:run": "vitest run --exclude src/security/fuzzing/ --exclude src/commands/python3/ --exclude src/agent-examples/python-scripting.test.ts",
115
+ "test:run": "vitest run --exclude src/security/fuzzing/ --exclude src/commands/python3/ --exclude src/commands/sqlite3/ --exclude src/commands/js-exec/ --exclude src/agent-examples/python-scripting.test.ts",
115
116
  "test:dist": "vitest run src/cli/just-bash.bundle.test.ts",
116
117
  "test:unit": "vitest run --config vitest.unit.config.ts",
117
- "test:python": "vitest run --config vitest.python.config.ts",
118
+ "test:wasm": "vitest run --config vitest.wasm.config.ts",
118
119
  "test:comparison": "vitest run --config vitest.comparison.config.ts",
119
120
  "test:comparison:record": "RECORD_FIXTURES=1 vitest run --config vitest.comparison.config.ts",
120
121
  "test:coverage": "vitest run --coverage",
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import{a as A,b as I,c as b}from"./chunk-GTNBSMZR.js";var $={name:"xargs",summary:"build and execute command lines from standard input",usage:"xargs [OPTION]... [COMMAND [INITIAL-ARGS]]",options:["-I REPLACE replace occurrences of REPLACE with input","-d DELIM use DELIM as input delimiter (e.g., -d '\\n' for newline)","-n NUM use at most NUM arguments per command line","-P NUM run at most NUM processes at a time","-0, --null items are separated by null, not whitespace","-t, --verbose print commands before executing","-r, --no-run-if-empty do not run command if input is empty"," --help display this help and exit"]},N={name:"xargs",async execute(l,i){if(I(l))return A($);let m=null,g=null,c=null,o=null,h=!1,x=!1,y=!1,s=0;for(let e=0;e<l.length;e++){let t=l[e];if(t==="-I"&&e+1<l.length)m=l[++e],s=e+1;else if(t==="-d"&&e+1<l.length)g=l[++e].replace(/\\n/g,`
3
- `).replace(/\\t/g," ").replace(/\\r/g,"\r").replace(/\\0/g,"\0").replace(/\\\\/g,"\\"),s=e+1;else if(t==="-n"&&e+1<l.length)c=parseInt(l[++e],10),s=e+1;else if(t==="-P"&&e+1<l.length)o=parseInt(l[++e],10),s=e+1;else if(t==="-0"||t==="--null")h=!0,s=e+1;else if(t==="-t"||t==="--verbose")x=!0,s=e+1;else if(t==="-r"||t==="--no-run-if-empty")y=!0,s=e+1;else{if(t.startsWith("--"))return b("xargs",t);if(t.startsWith("-")&&t.length>1){for(let n of t.slice(1))if(!"0tr".includes(n))return b("xargs",`-${n}`);t.includes("0")&&(h=!0),t.includes("t")&&(x=!0),t.includes("r")&&(y=!0),s=e+1}else if(!t.startsWith("-")){s=e;break}}}let a=l.slice(s);a.length===0&&a.push("echo");let r;if(h?r=i.stdin.split("\0").filter(e=>e.length>0):g!==null?r=i.stdin.replace(/\n$/,"").split(g).filter(t=>t.length>0):r=i.stdin.split(/\s+/).map(e=>e.trim()).filter(e=>e.length>0),r.length===0)return y?{stdout:"",stderr:"",exitCode:0}:{stdout:"",stderr:"",exitCode:0};let d="",u="",f=0,v=e=>/[\s"'\\$`!*?[\]{}();&|<>#]/.test(e)?`"${e.replace(/([\\"`$])/g,"\\$1")}"`:e,C=async e=>{let t=e.map(v).join(" ");return x&&(u+=`${t}
4
- `),i.exec?i.exec(t,{cwd:i.cwd,signal:i.signal}):{stdout:`${t}
5
- `,stderr:"",exitCode:0}},w=async e=>{if(o!==null&&o>1)for(let t=0;t<e.length;t+=o){let n=e.slice(t,t+o),M=await Promise.all(n.map(C));for(let p of M)d+=p.stdout,u+=p.stderr,p.exitCode!==0&&(f=p.exitCode)}else for(let t of e){let n=await C(t);d+=n.stdout,u+=n.stderr,n.exitCode!==0&&(f=n.exitCode)}};if(m!==null){let e=r.map(t=>a.map(n=>n.replaceAll(m,t)));await w(e)}else if(c!==null){let e=[];for(let t=0;t<r.length;t+=c){let n=r.slice(t,t+c);e.push([...a,...n])}await w(e)}else{let e=[...a,...r],t=await C(e);d+=t.stdout,u+=t.stderr,f=t.exitCode}return{stdout:d,stderr:u,exitCode:f}}},P={name:"xargs",flags:[{flag:"-I",type:"value",valueHint:"string"},{flag:"-d",type:"value",valueHint:"delimiter"},{flag:"-n",type:"value",valueHint:"number"},{flag:"-0",type:"boolean"},{flag:"-t",type:"boolean"},{flag:"-r",type:"boolean"}],stdinType:"text"};export{N as a,P as b};
@@ -1,12 +0,0 @@
1
- #!/usr/bin/env node
2
- import{a as x}from"./chunk-YTIURC67.js";import{a as O}from"./chunk-4OALHZXB.js";import{a as c,b as p}from"./chunk-OOJCYVYF.js";import{a as h,b as A,c as l}from"./chunk-GTNBSMZR.js";var y={name:"timeout",summary:"run a command with a time limit",usage:"timeout [OPTION] DURATION COMMAND [ARG]...",description:`Start COMMAND, and kill it if still running after DURATION.
3
-
4
- DURATION is a number with optional suffix:
5
- s - seconds (default)
6
- m - minutes
7
- h - hours
8
- d - days`,options:["-k, --kill-after=DURATION send KILL signal after DURATION if still running","-s, --signal=SIGNAL specify signal to send (default: TERM)"," --preserve-status exit with same status as COMMAND, even on timeout"," --foreground run command in foreground"," --help display this help and exit"]},D={name:"timeout",async execute(s,n){if(A(s))return h(y);let i=0;for(let e=0;e<s.length;e++){let t=s[e];if(t==="--preserve-status")i=e+1;else if(t==="--foreground")i=e+1;else if(t==="-k"||t==="--kill-after")e++,i=e+1;else if(t.startsWith("--kill-after="))i=e+1;else if(t==="-s"||t==="--signal")e++,i=e+1;else if(t.startsWith("--signal="))i=e+1;else{if(t.startsWith("--")&&t!=="--")return l("timeout",t);if(t.startsWith("-")&&t.length>1&&t!=="--")if(t.startsWith("-k"))i=e+1;else if(t.startsWith("-s"))i=e+1;else return l("timeout",t);else{i=e;break}}}let r=s.slice(i);if(r.length===0)return{stdout:"",stderr:`timeout: missing operand
9
- `,exitCode:1};let u=r[0],m=x(u);if(m===null)return{stdout:"",stderr:`timeout: invalid time interval '${u}'
10
- `,exitCode:1};let d=r.slice(1);if(d.length===0)return{stdout:"",stderr:`timeout: missing operand
11
- `,exitCode:1};if(!n.exec)return{stdout:"",stderr:`timeout: exec not available
12
- `,exitCode:1};let v=O(d),f=new AbortController,o;try{let e=new Promise(a=>{o=c(()=>{f.abort(),a({timedOut:!0})},m)}),t=n.exec(v,{cwd:n.cwd,signal:f.signal,stdin:n.stdin}).then(a=>({timedOut:!1,result:a})),g=await Promise.race([e,t]);return g.timedOut?{stdout:"",stderr:"",exitCode:124}:g.result}finally{o!==void 0&&p(o)}}},I={name:"timeout",flags:[{flag:"-k",type:"value",valueHint:"string"},{flag:"-s",type:"value",valueHint:"string"},{flag:"--preserve-status",type:"boolean"},{flag:"--foreground",type:"boolean"}],needsArgs:!0,minArgs:2};export{D as a,I as b};
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env node
2
- import{a as v}from"./chunk-4OALHZXB.js";import{a as d}from"./chunk-4PRVMER6.js";import{a as c,b as m,c as f}from"./chunk-GTNBSMZR.js";var y={name:"env",summary:"run a program in a modified environment",usage:"env [OPTION]... [NAME=VALUE]... [COMMAND [ARG]...]",options:["-i, --ignore-environment start with an empty environment","-u NAME, --unset=NAME remove NAME from the environment"," --help display this help and exit"]},N={name:"env",async execute(o,i){if(m(o))return c(y);let a=!1,r=[],u=new Map,t=-1;for(let n=0;n<o.length;n++){let e=o[n];if(e==="-i"||e==="--ignore-environment")a=!0;else if(e==="-u"&&n+1<o.length)r.push(o[++n]);else if(e.startsWith("-u"))r.push(e.slice(2));else if(e.startsWith("--unset="))r.push(e.slice(8));else{if(e.startsWith("--")&&e!=="--")return f("env",e);if(e.startsWith("-")&&e!=="-"){for(let l of e.slice(1))if(l!=="i"&&l!=="u")return f("env",`-${l}`);e.includes("i")&&(a=!0)}else if(e.includes("=")&&t===-1){let l=e.indexOf("="),g=e.slice(0,l),x=e.slice(l+1);u.set(g,x)}else{t=n;break}}}let s;if(a)s=new Map(u);else{s=new Map(i.env);for(let n of r)s.delete(n);for(let[n,e]of u)s.set(n,e)}if(t===-1){let n=[];for(let[e,l]of s)n.push(`${e}=${l}`);return{stdout:n.join(`
3
- `)+(n.length>0?`
4
- `:""),stderr:"",exitCode:0}}if(!i.exec)return{stdout:"",stderr:`env: command execution not supported in this context
5
- `,exitCode:1};let p=o.slice(t),h=v(["command",...p]);return i.exec(h,{cwd:i.cwd,env:d(s),replaceEnv:!0,stdin:i.stdin,signal:i.signal})}},A={name:"printenv",summary:"print all or part of environment",usage:"printenv [OPTION]... [VARIABLE]...",options:[" --help display this help and exit"]},C={name:"printenv",async execute(o,i){if(m(o))return c(A);let a=o.filter(t=>!t.startsWith("-"));if(a.length===0){let t=[];for(let[s,p]of i.env)t.push(`${s}=${p}`);return{stdout:t.join(`
6
- `)+(t.length>0?`
7
- `:""),stderr:"",exitCode:0}}let r=[],u=0;for(let t of a){let s=i.env.get(t);s!==void 0?r.push(s):u=1}return{stdout:r.join(`
8
- `)+(r.length>0?`
9
- `:""),stderr:"",exitCode:u}}},O={name:"env",flags:[{flag:"-i",type:"boolean"},{flag:"-u",type:"value",valueHint:"string"}]},F={name:"printenv",flags:[]};export{N as a,C as b,O as c,F as d};