just-bash 2.12.8 → 2.13.1
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.
- package/README.md +332 -260
- package/dist/AGENTS.md +1 -1
- package/dist/Bash.d.ts +17 -0
- package/dist/bin/chunks/{awk2-J2PNSA7C.js → awk2-VTJMI54B.js} +1 -1
- package/dist/bin/chunks/{chunk-O5B6WWQQ.js → chunk-A3HQTYHR.js} +1 -1
- package/dist/bin/chunks/chunk-AAW7UMPO.js +9 -0
- package/dist/bin/chunks/{chunk-2V53PP6G.js → chunk-B3RU2PUI.js} +27 -27
- package/dist/bin/chunks/chunk-CQG2HEAL.js +5 -0
- package/dist/bin/chunks/chunk-DOXYBGNA.js +12 -0
- package/dist/bin/chunks/chunk-FBJJY4ZV.js +14 -0
- package/dist/bin/{shell/chunks/chunk-YAEF6X2N.js → chunks/chunk-GR23MPTT.js} +1 -1
- package/dist/bin/{shell/chunks/chunk-35JD7YEM.js → chunks/chunk-HJEHIH4P.js} +13 -13
- package/dist/bin/chunks/{chunk-UG4GMDQL.js → chunk-LGF54XJQ.js} +1 -1
- package/dist/bin/chunks/chunk-LIYVQA3X.js +2 -0
- package/dist/bin/chunks/{chunk-2BC3N3L2.js → chunk-ORUYSLP4.js} +4 -4
- package/dist/bin/chunks/{chunk-REGSV3X5.js → chunk-SDLWFYVT.js} +1 -1
- package/dist/bin/chunks/{chunk-STOAUD75.js → chunk-THNL3XFF.js} +8 -8
- package/dist/bin/chunks/chunk-TKTKRRAL.js +11 -0
- package/dist/bin/chunks/{chunk-ZJGIBTWD.js → chunk-V7ZOPVQS.js} +1 -1
- package/dist/bin/chunks/{chunk-ZYQQ6B7B.js → chunk-ZO5PSLKR.js} +2 -2
- package/dist/bin/chunks/curl-SRTMGOVV.js +26 -0
- package/dist/bin/chunks/env-NTPN5QYM.js +2 -0
- package/dist/bin/{shell/chunks/expansion-JBCP2CHQ.js → chunks/expansion-2RO5M3QC.js} +1 -1
- package/dist/bin/{shell/chunks/find-MTLF23HX.js → chunks/find-GAYRV4IF.js} +1 -1
- package/dist/bin/{shell/chunks/flag-coverage-CFWN3JJN.js → chunks/flag-coverage-A4G6STMS.js} +1 -1
- package/dist/bin/chunks/{help-4H52JYYC.js → help-DVG4AAGE.js} +1 -1
- package/dist/bin/{shell/chunks/jq-JFXEKNLN.js → chunks/jq-V7FYGIKO.js} +1 -1
- package/dist/bin/chunks/js-exec-JORCTVUT.js +97 -0
- package/dist/bin/chunks/js-exec-worker.js +4369 -0
- package/dist/bin/chunks/python3-PF7AHNQM.js +12 -0
- package/dist/bin/chunks/rg-C6KMBFNG.js +2 -0
- package/dist/bin/chunks/time-DDS6JJ23.js +2 -0
- package/dist/bin/chunks/{timeout-JJWIFL7W.js → timeout-Z24MNWOP.js} +1 -1
- package/dist/bin/chunks/worker.js +88 -33
- package/dist/bin/chunks/{xan-M6MLWZCU.js → xan-MOZFJGMY.js} +1 -1
- package/dist/bin/chunks/xargs-SCYIFXOW.js +2 -0
- package/dist/bin/chunks/{yq-YWUQUXJJ.js → yq-JJLSDDST.js} +1 -1
- package/dist/bin/just-bash.js +212 -211
- package/dist/bin/shell/chunks/{awk2-J2PNSA7C.js → awk2-VTJMI54B.js} +1 -1
- package/dist/bin/shell/chunks/{chunk-O5B6WWQQ.js → chunk-A3HQTYHR.js} +1 -1
- package/dist/bin/shell/chunks/chunk-AAW7UMPO.js +9 -0
- package/dist/bin/shell/chunks/{chunk-2V53PP6G.js → chunk-B3RU2PUI.js} +27 -27
- package/dist/bin/shell/chunks/chunk-CQG2HEAL.js +5 -0
- package/dist/bin/shell/chunks/chunk-DOXYBGNA.js +12 -0
- package/dist/bin/shell/chunks/chunk-FBJJY4ZV.js +14 -0
- package/dist/bin/{chunks/chunk-YAEF6X2N.js → shell/chunks/chunk-GR23MPTT.js} +1 -1
- package/dist/bin/{chunks/chunk-35JD7YEM.js → shell/chunks/chunk-HJEHIH4P.js} +13 -13
- package/dist/bin/shell/chunks/{chunk-UG4GMDQL.js → chunk-LGF54XJQ.js} +1 -1
- package/dist/bin/shell/chunks/chunk-LIYVQA3X.js +2 -0
- package/dist/bin/shell/chunks/{chunk-2BC3N3L2.js → chunk-ORUYSLP4.js} +4 -4
- package/dist/bin/shell/chunks/{chunk-REGSV3X5.js → chunk-SDLWFYVT.js} +1 -1
- package/dist/bin/shell/chunks/{chunk-STOAUD75.js → chunk-THNL3XFF.js} +8 -8
- package/dist/bin/shell/chunks/chunk-TKTKRRAL.js +11 -0
- package/dist/bin/shell/chunks/{chunk-ZJGIBTWD.js → chunk-V7ZOPVQS.js} +1 -1
- package/dist/bin/shell/chunks/{chunk-ZYQQ6B7B.js → chunk-ZO5PSLKR.js} +2 -2
- package/dist/bin/shell/chunks/curl-SRTMGOVV.js +26 -0
- package/dist/bin/shell/chunks/env-NTPN5QYM.js +2 -0
- package/dist/bin/{chunks/expansion-JBCP2CHQ.js → shell/chunks/expansion-2RO5M3QC.js} +1 -1
- package/dist/bin/{chunks/find-MTLF23HX.js → shell/chunks/find-GAYRV4IF.js} +1 -1
- package/dist/bin/{chunks/flag-coverage-CFWN3JJN.js → shell/chunks/flag-coverage-A4G6STMS.js} +1 -1
- package/dist/bin/shell/chunks/{help-4H52JYYC.js → help-DVG4AAGE.js} +1 -1
- package/dist/bin/{chunks/jq-JFXEKNLN.js → shell/chunks/jq-V7FYGIKO.js} +1 -1
- package/dist/bin/shell/chunks/js-exec-B6QNYHUL.js +97 -0
- package/dist/bin/shell/chunks/python3-J7DP4JBQ.js +12 -0
- package/dist/bin/shell/chunks/rg-C6KMBFNG.js +2 -0
- package/dist/bin/shell/chunks/time-DDS6JJ23.js +2 -0
- package/dist/bin/shell/chunks/{timeout-JJWIFL7W.js → timeout-Z24MNWOP.js} +1 -1
- package/dist/bin/shell/chunks/{xan-M6MLWZCU.js → xan-MOZFJGMY.js} +1 -1
- package/dist/bin/shell/chunks/xargs-SCYIFXOW.js +2 -0
- package/dist/bin/shell/chunks/{yq-YWUQUXJJ.js → yq-JJLSDDST.js} +1 -1
- package/dist/bin/shell/shell.js +212 -212
- package/dist/bundle/browser.js +600 -600
- package/dist/bundle/chunks/{awk2-FUVZGMX2.js → awk2-POPGKRAI.js} +1 -1
- package/dist/bundle/chunks/{chunk-DZZS6SJP.js → chunk-7TSDKFEO.js} +1 -1
- package/dist/bundle/chunks/{chunk-D4QDMGEB.js → chunk-BBXLRYSX.js} +2 -2
- package/dist/bundle/chunks/chunk-CWQS3NFK.js +1 -0
- package/dist/bundle/chunks/chunk-F5CMUULS.js +13 -0
- package/dist/bundle/chunks/{chunk-TRD56HID.js → chunk-FEIOJCZD.js} +1 -1
- package/dist/bundle/chunks/chunk-H2WXJGD7.js +10 -0
- package/dist/bundle/chunks/{chunk-D2FZX7A2.js → chunk-LPQPILI2.js} +8 -8
- package/dist/bundle/chunks/{chunk-2GOYXRRP.js → chunk-MLXIYONF.js} +4 -4
- package/dist/bundle/chunks/chunk-NAERJDUW.js +8 -0
- package/dist/bundle/chunks/{chunk-OKEHYWBE.js → chunk-NYQYO467.js} +13 -13
- package/dist/bundle/chunks/{chunk-JKLUDNMU.js → chunk-O2DBFL6Z.js} +1 -1
- package/dist/bundle/chunks/{chunk-JDMQDJYE.js → chunk-OARHFVLG.js} +1 -1
- package/dist/bundle/chunks/{chunk-AKVMAONP.js → chunk-RUF7WQ7U.js} +1 -1
- package/dist/bundle/chunks/chunk-TOMNU26N.js +4 -0
- package/dist/bundle/chunks/{chunk-ZLJ5TCLC.js → chunk-UNYNJIFU.js} +27 -27
- package/dist/bundle/chunks/chunk-YTNYSM6T.js +11 -0
- package/dist/bundle/chunks/curl-COE4TZE6.js +25 -0
- package/dist/bundle/chunks/env-5EPCWSXR.js +1 -0
- package/dist/bundle/chunks/{expansion-XG7G47TX.js → expansion-ENLSRCXJ.js} +1 -1
- package/dist/bundle/chunks/{find-DOIVMX6X.js → find-TPUOAIUQ.js} +1 -1
- package/dist/bundle/chunks/{flag-coverage-VML3BMJT.js → flag-coverage-G2R7PMYU.js} +1 -1
- package/dist/bundle/chunks/{help-IA5CMGR4.js → help-VVWX7SA5.js} +1 -1
- package/dist/bundle/chunks/{jq-SSCW4AAA.js → jq-DIRZBOTX.js} +1 -1
- package/dist/bundle/chunks/js-exec-YJSMH5AG.js +96 -0
- package/dist/bundle/chunks/js-exec-worker.js +4369 -0
- package/dist/bundle/chunks/python3-6HF56IYI.js +11 -0
- package/dist/bundle/chunks/rg-FOQSCCX3.js +1 -0
- package/dist/bundle/chunks/{time-XL42Z4U5.js → time-YG5BMRIQ.js} +1 -1
- package/dist/bundle/chunks/{timeout-QCU4INQT.js → timeout-VRKMCG72.js} +1 -1
- package/dist/bundle/chunks/worker.js +88 -33
- package/dist/bundle/chunks/{xan-K7XYDHFV.js → xan-BXDXYEIB.js} +1 -1
- package/dist/bundle/chunks/xargs-I6EZUCYF.js +1 -0
- package/dist/bundle/chunks/{yq-WTK3HUOR.js → yq-XMVSIL6Z.js} +1 -1
- package/dist/bundle/index.cjs +969 -870
- package/dist/bundle/index.js +151 -151
- package/dist/commands/curl/types.d.ts +1 -1
- package/dist/commands/js-exec/fetch-polyfill.d.ts +6 -0
- package/dist/commands/js-exec/js-exec.d.ts +11 -0
- package/dist/commands/js-exec/module-shims.d.ts +29 -0
- package/dist/commands/js-exec/path-polyfill.d.ts +6 -0
- package/dist/commands/js-exec/worker.d.ts +30 -0
- package/dist/commands/python3/worker.d.ts +1 -0
- package/dist/commands/query-engine/safe-object.d.ts +11 -0
- package/dist/commands/registry.d.ts +13 -2
- package/dist/commands/{python3/fs-bridge-handler.d.ts → worker-bridge/bridge-handler.d.ts} +25 -8
- package/dist/commands/{python3 → worker-bridge}/protocol.d.ts +6 -3
- package/dist/commands/{python3/sync-fs-backend.d.ts → worker-bridge/sync-backend.d.ts} +25 -4
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/interpreter/interpreter.d.ts +3 -0
- package/dist/interpreter/types.d.ts +8 -0
- package/dist/limits.d.ts +3 -1
- package/dist/network/allow-list.d.ts +5 -3
- package/dist/network/fetch.d.ts +1 -1
- package/dist/network/index.d.ts +1 -1
- package/dist/network/types.d.ts +32 -1
- package/dist/types.d.ts +12 -0
- package/package.json +13 -12
- package/dist/bin/chunks/chunk-47HZU3SY.js +0 -5
- package/dist/bin/chunks/chunk-CZFRRDQC.js +0 -12
- package/dist/bin/chunks/chunk-N4EU64Y4.js +0 -9
- package/dist/bin/chunks/chunk-QZNF3Y3J.js +0 -2
- package/dist/bin/chunks/chunk-VIYJJTN2.js +0 -14
- package/dist/bin/chunks/curl-3GMIPMCI.js +0 -26
- package/dist/bin/chunks/env-HOVBNLUR.js +0 -2
- package/dist/bin/chunks/python3-KI2FQWSN.js +0 -17
- package/dist/bin/chunks/rg-34GE6REQ.js +0 -2
- package/dist/bin/chunks/time-GZSHCM77.js +0 -2
- package/dist/bin/chunks/xargs-GBL6PZ2K.js +0 -2
- package/dist/bin/shell/chunks/chunk-47HZU3SY.js +0 -5
- package/dist/bin/shell/chunks/chunk-CZFRRDQC.js +0 -12
- package/dist/bin/shell/chunks/chunk-N4EU64Y4.js +0 -9
- package/dist/bin/shell/chunks/chunk-QZNF3Y3J.js +0 -2
- package/dist/bin/shell/chunks/chunk-VIYJJTN2.js +0 -14
- package/dist/bin/shell/chunks/curl-3GMIPMCI.js +0 -26
- package/dist/bin/shell/chunks/env-HOVBNLUR.js +0 -2
- package/dist/bin/shell/chunks/python3-E5X6WBBU.js +0 -17
- package/dist/bin/shell/chunks/rg-34GE6REQ.js +0 -2
- package/dist/bin/shell/chunks/time-GZSHCM77.js +0 -2
- package/dist/bin/shell/chunks/xargs-GBL6PZ2K.js +0 -2
- package/dist/bundle/chunks/chunk-3RA5L262.js +0 -8
- package/dist/bundle/chunks/chunk-EX62JIX3.js +0 -13
- package/dist/bundle/chunks/chunk-RH6GWZAJ.js +0 -1
- package/dist/bundle/chunks/chunk-WECLUBEQ.js +0 -11
- package/dist/bundle/chunks/chunk-ZSJYNBAF.js +0 -4
- package/dist/bundle/chunks/curl-KM2ZAUR6.js +0 -25
- package/dist/bundle/chunks/env-XZY4LKEO.js +0 -1
- package/dist/bundle/chunks/python3-2OHR6PZU.js +0 -16
- package/dist/bundle/chunks/rg-RAICUFGG.js +0 -1
- 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
|
+
}
|
|
@@ -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
|
-
/**
|
|
9
|
-
export type
|
|
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
|
|
2
|
+
* Main thread bridge handler
|
|
3
3
|
*
|
|
4
|
-
* Runs on the main thread and processes filesystem
|
|
5
|
-
*
|
|
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
|
-
|
|
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
|
|
16
|
+
* Handles requests from a worker thread.
|
|
16
17
|
*/
|
|
17
|
-
export declare class
|
|
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
|
-
|
|
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<
|
|
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
|
|
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
|
|
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
|
|
8
|
+
* Synchronous backend for worker threads.
|
|
9
9
|
*/
|
|
10
|
-
export declare class
|
|
10
|
+
export declare class SyncBackend {
|
|
11
11
|
private protocol;
|
|
12
|
-
|
|
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";
|
|
@@ -10,7 +10,7 @@ export type { BufferEncoding, CpOptions, DirectoryEntry, FileContent, FileEntry,
|
|
|
10
10
|
export { MountableFs, type MountableFsOptions, type MountConfig, } from "./fs/mountable-fs/index.js";
|
|
11
11
|
export { OverlayFs, type OverlayFsOptions } from "./fs/overlay-fs/index.js";
|
|
12
12
|
export { ReadWriteFs, type ReadWriteFsOptions, } from "./fs/read-write-fs/index.js";
|
|
13
|
-
export type { NetworkConfig, SecureFetch } from "./network/index.js";
|
|
13
|
+
export type { AllowedUrl, AllowedUrlEntry, NetworkConfig, RequestTransform, SecureFetch, } from "./network/index.js";
|
|
14
14
|
export { NetworkAccessDeniedError, RedirectNotAllowedError, TooManyRedirectsError, } from "./network/index.js";
|
|
15
15
|
export { parse } from "./parser/parser.js";
|
|
16
16
|
export type { CommandFinished as SandboxCommandFinished, OutputMessage, SandboxOptions, WriteFilesInput, } from "./sandbox/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";
|
|
@@ -10,7 +10,7 @@ export type { BufferEncoding, CpOptions, DirectoryEntry, FileContent, FileEntry,
|
|
|
10
10
|
export { MountableFs, type MountableFsOptions, type MountConfig, } from "./fs/mountable-fs/index.js";
|
|
11
11
|
export { OverlayFs, type OverlayFsOptions } from "./fs/overlay-fs/index.js";
|
|
12
12
|
export { ReadWriteFs, type ReadWriteFsOptions, } from "./fs/read-write-fs/index.js";
|
|
13
|
-
export type { NetworkConfig, SecureFetch } from "./network/index.js";
|
|
13
|
+
export type { AllowedUrl, AllowedUrlEntry, NetworkConfig, RequestTransform, SecureFetch, } from "./network/index.js";
|
|
14
14
|
export { NetworkAccessDeniedError, RedirectNotAllowedError, TooManyRedirectsError, } from "./network/index.js";
|
|
15
15
|
export { parse } from "./parser/parser.js";
|
|
16
16
|
export type { CommandFinished as SandboxCommandFinished, OutputMessage, SandboxOptions, WriteFilesInput, } from "./sandbox/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:
|
|
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) */
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* This module provides URL allow-list matching that is enforced at the fetch layer,
|
|
5
5
|
* independent of any parsing or user input manipulation.
|
|
6
6
|
*/
|
|
7
|
+
import type { AllowedUrlEntry } from "./types.js";
|
|
7
8
|
/**
|
|
8
9
|
* Parses a URL string into its components.
|
|
9
10
|
* Returns null if the URL is invalid.
|
|
@@ -39,10 +40,10 @@ export declare function matchesAllowListEntry(url: string, allowedEntry: string)
|
|
|
39
40
|
* Checks if a URL is allowed by any entry in the allow-list.
|
|
40
41
|
*
|
|
41
42
|
* @param url The URL to check
|
|
42
|
-
* @param allowedUrlPrefixes The list of allowed URL prefixes
|
|
43
|
+
* @param allowedUrlPrefixes The list of allowed URL prefixes (strings or objects)
|
|
43
44
|
* @returns true if the URL is allowed
|
|
44
45
|
*/
|
|
45
|
-
export declare function isUrlAllowed(url: string, allowedUrlPrefixes:
|
|
46
|
+
export declare function isUrlAllowed(url: string, allowedUrlPrefixes: AllowedUrlEntry[]): boolean;
|
|
46
47
|
/**
|
|
47
48
|
* Check if a hostname is a private/loopback IP address.
|
|
48
49
|
* Only checks the string format — does not perform DNS resolution.
|
|
@@ -51,6 +52,7 @@ export declare function isPrivateIp(hostname: string): boolean;
|
|
|
51
52
|
/**
|
|
52
53
|
* Validates an allow-list configuration.
|
|
53
54
|
* Each entry must be a full origin (scheme + host), optionally followed by a path prefix.
|
|
55
|
+
* Accepts both plain strings and AllowedUrl objects.
|
|
54
56
|
* Returns an array of error messages for invalid entries.
|
|
55
57
|
*/
|
|
56
|
-
export declare function validateAllowList(allowedUrlPrefixes:
|
|
58
|
+
export declare function validateAllowList(allowedUrlPrefixes: AllowedUrlEntry[]): string[];
|
package/dist/network/fetch.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { type FetchResult, type NetworkConfig } from "./types.js";
|
|
10
10
|
export interface SecureFetchOptions {
|
|
11
11
|
method?: string;
|
|
12
|
-
headers?: Record<string, string>;
|
|
12
|
+
headers?: Headers | Record<string, string>;
|
|
13
13
|
body?: string;
|
|
14
14
|
followRedirects?: boolean;
|
|
15
15
|
/** Override timeout for this request (capped at global timeout) */
|
package/dist/network/index.d.ts
CHANGED
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
* Provides secure network access with URL allow-list enforcement.
|
|
5
5
|
*/
|
|
6
6
|
export { createSecureFetch, type SecureFetch, type SecureFetchOptions, } from "./fetch.js";
|
|
7
|
-
export { type FetchResult, type HttpMethod, NetworkAccessDeniedError, type NetworkConfig, RedirectNotAllowedError, TooManyRedirectsError, } from "./types.js";
|
|
7
|
+
export { type AllowedUrl, type AllowedUrlEntry, type FetchResult, type HttpMethod, NetworkAccessDeniedError, type NetworkConfig, RedirectNotAllowedError, type RequestTransform, TooManyRedirectsError, } from "./types.js";
|
package/dist/network/types.d.ts
CHANGED
|
@@ -15,6 +15,26 @@ export interface DnsLookupResult {
|
|
|
15
15
|
* HTTP methods that can be allowed
|
|
16
16
|
*/
|
|
17
17
|
export type HttpMethod = "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS";
|
|
18
|
+
/**
|
|
19
|
+
* Header transform applied at the fetch boundary.
|
|
20
|
+
* Headers specified here override any user-supplied headers with the same name.
|
|
21
|
+
*/
|
|
22
|
+
export interface RequestTransform {
|
|
23
|
+
headers: Record<string, string>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* An allowed URL entry with optional header transforms.
|
|
27
|
+
* Transforms are applied at the fetch boundary so secrets never enter the sandbox.
|
|
28
|
+
*/
|
|
29
|
+
export interface AllowedUrl {
|
|
30
|
+
url: string;
|
|
31
|
+
transform?: RequestTransform[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* An entry in the allowedUrlPrefixes list: either a plain URL string or
|
|
35
|
+
* an object with a URL and optional transforms.
|
|
36
|
+
*/
|
|
37
|
+
export type AllowedUrlEntry = string | AllowedUrl;
|
|
18
38
|
/**
|
|
19
39
|
* Configuration for network access
|
|
20
40
|
*/
|
|
@@ -25,6 +45,17 @@ export interface NetworkConfig {
|
|
|
25
45
|
* - An origin: "https://api.example.com" - allows all paths on this origin
|
|
26
46
|
* - An origin + path prefix: "https://api.example.com/v1/" - allows only paths starting with /v1/
|
|
27
47
|
*
|
|
48
|
+
* Entries can be plain strings or objects with transforms for credentials brokering:
|
|
49
|
+
* ```
|
|
50
|
+
* allowedUrlPrefixes: [
|
|
51
|
+
* "https://other-api.com",
|
|
52
|
+
* {
|
|
53
|
+
* url: "https://ai-gateway.vercel.sh",
|
|
54
|
+
* transform: [{ headers: { "Authorization": "Bearer secret" } }],
|
|
55
|
+
* },
|
|
56
|
+
* ]
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
28
59
|
* The check is performed on the full URL, so "https://api.example.com/v1" will allow:
|
|
29
60
|
* - https://api.example.com/v1
|
|
30
61
|
* - https://api.example.com/v1/users
|
|
@@ -36,7 +67,7 @@ export interface NetworkConfig {
|
|
|
36
67
|
*
|
|
37
68
|
* Invalid entries (missing scheme, missing host, relative paths) will throw an error.
|
|
38
69
|
*/
|
|
39
|
-
allowedUrlPrefixes?:
|
|
70
|
+
allowedUrlPrefixes?: AllowedUrlEntry[];
|
|
40
71
|
/**
|
|
41
72
|
* List of allowed HTTP methods. Defaults to ["GET", "HEAD"] for safety.
|
|
42
73
|
* dangerouslyAllowFullInternetAccess to enables all methods.
|
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;
|