added networking to the CLI
This commit is contained in:
parent
9bee0aed5b
commit
f61ac35580
23
examples/extract_lynglang_version.lyng
Normal file
23
examples/extract_lynglang_version.lyng
Normal file
@ -0,0 +1,23 @@
|
||||
#!/env/bin lyng
|
||||
|
||||
import lyng.io.http
|
||||
|
||||
// Step 1: download the main lynglang.com page.
|
||||
val home = Http.get("https://lynglang.com").text()
|
||||
|
||||
// Step 2: find the version-script reference in the page HTML.
|
||||
val jsRef = "src=\"([^\"]*lyng-version\\.js)\"".re.find(home)
|
||||
require(jsRef != null, "lyng-version.js reference not found on the homepage")
|
||||
|
||||
// Step 3: extract the referenced script path from the first regex capture.
|
||||
val versionJsPath = jsRef[1]
|
||||
|
||||
// Step 4: download the script that exposes `window.LYNG_VERSION`.
|
||||
val versionJs = Http.get("https://lynglang.com/" + versionJsPath).text()
|
||||
|
||||
// Step 5: pull the actual version string from the JavaScript source.
|
||||
val versionMatch = "LYNG_VERSION\\s*=\\s*\"([^\"]+)\"".re.find(versionJs)
|
||||
require(versionMatch != null, "LYNG_VERSION assignment not found")
|
||||
|
||||
// Step 6: print the discovered version for the user.
|
||||
println("Lynglang.com version: " + ((versionMatch as RegexMatch)[1]))
|
||||
@ -14,6 +14,7 @@ firebaseCrashlyticsBuildtools = "3.0.3"
|
||||
okioVersion = "3.10.2"
|
||||
compiler = "3.2.0-alpha11"
|
||||
ktor = "3.3.1"
|
||||
slf4j = "2.0.17"
|
||||
|
||||
[libraries]
|
||||
clikt = { module = "com.github.ajalt.clikt:clikt", version.ref = "clikt" }
|
||||
@ -40,6 +41,7 @@ ktor-client-js = { module = "io.ktor:ktor-client-js", version.ref = "ktor" }
|
||||
ktor-client-winhttp = { module = "io.ktor:ktor-client-winhttp", version.ref = "ktor" }
|
||||
ktor-client-websockets = { module = "io.ktor:ktor-client-websockets", version.ref = "ktor" }
|
||||
ktor-network = { module = "io.ktor:ktor-network", version.ref = "ktor" }
|
||||
slf4j-nop = { module = "org.slf4j:slf4j-nop", version.ref = "slf4j" }
|
||||
|
||||
[plugins]
|
||||
androidLibrary = { id = "com.android.library", version.ref = "agp" }
|
||||
|
||||
@ -80,6 +80,11 @@ kotlin {
|
||||
val nativeMain by creating {
|
||||
dependsOn(commonMain)
|
||||
}
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation(libs.slf4j.nop)
|
||||
}
|
||||
}
|
||||
val jvmTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test"))
|
||||
|
||||
@ -34,9 +34,15 @@ import net.sergeych.lyng.ScriptError
|
||||
import net.sergeych.lyng.Source
|
||||
import net.sergeych.lyng.io.console.createConsoleModule
|
||||
import net.sergeych.lyng.io.fs.createFs
|
||||
import net.sergeych.lyng.io.http.createHttpModule
|
||||
import net.sergeych.lyng.io.net.createNetModule
|
||||
import net.sergeych.lyng.io.ws.createWsModule
|
||||
import net.sergeych.lyng.obj.*
|
||||
import net.sergeych.lyngio.console.security.PermitAllConsoleAccessPolicy
|
||||
import net.sergeych.lyngio.fs.security.PermitAllAccessPolicy
|
||||
import net.sergeych.lyngio.http.security.PermitAllHttpAccessPolicy
|
||||
import net.sergeych.lyngio.net.security.PermitAllNetAccessPolicy
|
||||
import net.sergeych.lyngio.ws.security.PermitAllWsAccessPolicy
|
||||
import net.sergeych.mp_tools.globalDefer
|
||||
import okio.FileSystem
|
||||
import okio.Path.Companion.toPath
|
||||
@ -74,6 +80,11 @@ val baseScopeDefer = globalDefer {
|
||||
// Install console access by default for interactive CLI scripts.
|
||||
// Scripts still need to `import lyng.io.console` to use it.
|
||||
createConsoleModule(PermitAllConsoleAccessPolicy, this)
|
||||
// Install network-oriented lyngio modules for CLI scripts.
|
||||
// Scripts still need to import the modules they use explicitly.
|
||||
createHttpModule(PermitAllHttpAccessPolicy, this)
|
||||
createWsModule(PermitAllWsAccessPolicy, this)
|
||||
createNetModule(PermitAllNetAccessPolicy, this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package net.sergeych.lyng_cli
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange
|
||||
import com.sun.net.httpserver.HttpServer
|
||||
import net.sergeych.jvmExitImpl
|
||||
import net.sergeych.runMain
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.PrintStream
|
||||
import java.net.InetSocketAddress
|
||||
|
||||
class CliNetworkJvmTest {
|
||||
private val originalOut: PrintStream = System.out
|
||||
private val originalErr: PrintStream = System.err
|
||||
|
||||
private class TestExit(val code: Int) : RuntimeException()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
jvmExitImpl = { code -> throw TestExit(code) }
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
System.setOut(originalOut)
|
||||
System.setErr(originalErr)
|
||||
jvmExitImpl = { code -> kotlin.system.exitProcess(code) }
|
||||
}
|
||||
|
||||
private data class CliResult(val out: String, val err: String, val exitCode: Int?)
|
||||
|
||||
private fun runCli(vararg args: String): CliResult {
|
||||
val outBuf = ByteArrayOutputStream()
|
||||
val errBuf = ByteArrayOutputStream()
|
||||
System.setOut(PrintStream(outBuf, true, Charsets.UTF_8))
|
||||
System.setErr(PrintStream(errBuf, true, Charsets.UTF_8))
|
||||
|
||||
var exitCode: Int? = null
|
||||
try {
|
||||
runMain(arrayOf(*args))
|
||||
} catch (e: TestExit) {
|
||||
exitCode = e.code
|
||||
} finally {
|
||||
System.out.flush()
|
||||
System.err.flush()
|
||||
}
|
||||
return CliResult(outBuf.toString("UTF-8"), errBuf.toString("UTF-8"), exitCode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cliHasAllNetworkingModulesInstalled() {
|
||||
val server = newServer()
|
||||
try {
|
||||
val script = """
|
||||
import lyng.io.http
|
||||
import lyng.io.ws
|
||||
import lyng.io.net
|
||||
|
||||
assert(Http.isSupported())
|
||||
println("ws=" + Ws.isSupported())
|
||||
println("net=" + Net.isSupported())
|
||||
|
||||
val home = Http.get("http://127.0.0.1:${server.address.port}/").text()
|
||||
val jsRef = "src=\"([^\"]*lyng-version\\.js)\"".re.find(home)
|
||||
require(jsRef != null, "lyng-version.js reference not found")
|
||||
|
||||
val versionJsPath = (jsRef as RegexMatch)[1]
|
||||
val versionJs = Http.get("http://127.0.0.1:${server.address.port}/" + versionJsPath).text()
|
||||
val versionMatch = "LYNG_VERSION\\s*=\\s*\"([^\"]+)\"".re.find(versionJs)
|
||||
require(versionMatch != null, "LYNG_VERSION assignment not found")
|
||||
|
||||
println("version=" + ((versionMatch as RegexMatch)[1]))
|
||||
""".trimIndent()
|
||||
|
||||
val result = runCli("-x", script)
|
||||
assertNull(result.exitCode)
|
||||
assertTrue(result.err, result.err.isBlank())
|
||||
assertTrue(result.out, result.out.contains("ws="))
|
||||
assertTrue(result.out, result.out.contains("net="))
|
||||
assertTrue(result.out, result.out.contains("version=9.9.9-test"))
|
||||
} finally {
|
||||
server.stop(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun newServer(): HttpServer {
|
||||
val server = HttpServer.create(InetSocketAddress("127.0.0.1", 0), 0)
|
||||
server.createContext("/") { exchange ->
|
||||
when (exchange.requestURI.path) {
|
||||
"/" -> writeResponse(
|
||||
exchange,
|
||||
200,
|
||||
"""
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="lyng-version.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<span id="lyng-version-ribbon"></span>
|
||||
</body>
|
||||
</html>
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
"/lyng-version.js" -> writeResponse(exchange, 200, """window.LYNG_VERSION = "9.9.9-test";""")
|
||||
else -> writeResponse(exchange, 404, "missing")
|
||||
}
|
||||
}
|
||||
server.start()
|
||||
return server
|
||||
}
|
||||
|
||||
private fun writeResponse(exchange: HttpExchange, status: Int, body: String) {
|
||||
val bytes = body.toByteArray()
|
||||
exchange.sendResponseHeaders(status, bytes.size.toLong())
|
||||
exchange.responseBody.use { out ->
|
||||
out.write(bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user