diff --git a/apps/ppp/astro.config.mjs b/apps/ppp/astro.config.mjs index 64c6cdc..338ca57 100644 --- a/apps/ppp/astro.config.mjs +++ b/apps/ppp/astro.config.mjs @@ -2,7 +2,7 @@ import { defineConfig } from "astro/config"; import { fileURLToPath } from "node:url"; import { viteStaticCopy } from "vite-plugin-static-copy"; -import crossOriginIsolation from 'vite-plugin-cross-origin-isolation' +import crossOriginIsolation from "vite-plugin-cross-origin-isolation"; import mdx from "@astrojs/mdx"; import svelte from "@astrojs/svelte"; @@ -35,19 +35,19 @@ export default defineConfig({ { src: "node_modules/gleam-runtime/dist/precompiled", dest: "_astro", - rename: "gleam" + rename: "gleam", }, { src: "node_modules/dotnet-runtime/dist/compiler", - dest: "_astro/dotnet" + dest: "_astro/dotnet", }, { src: "node_modules/dotnet-runtime/dist/lib", - dest: "_astro/dotnet" + dest: "_astro/dotnet", }, ], }), - crossOriginIsolation() + crossOriginIsolation(), ], }, markdown: { @@ -63,4 +63,7 @@ export default defineConfig({ ru: "en", }, }, + experimental: { + contentLayer: true, + }, }); diff --git a/apps/ppp/package.json b/apps/ppp/package.json index e8d2dcf..e2baa92 100644 --- a/apps/ppp/package.json +++ b/apps/ppp/package.json @@ -12,20 +12,21 @@ }, "dependencies": { "@astrojs/check": "^0.9.3", - "@astrojs/mdx": "^3.1.3", + "@astrojs/mdx": "^3.1.4", "@astrojs/svelte": "^5.7.0", "@astrojs/tailwind": "^5.1.0", "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0", - "astro": "^4.14.2", + "astro": "^4.14.5", "astro-icon": "^1.1.1", "libs": "workspace:*", - "monaco-editor": "^0.50.0", + "monaco-editor": "^0.51.0", "monaco-editor-textmate": "^4.0.0", "monaco-textmate": "^3.0.1", "monaco-vim": "^0.4.1", "onigasm": "^2.2.5", "tailwindcss": "^3.4.10", + "compiler": "workspace:*", "testing": "workspace:*", "dotnet-runtime": "workspace:*", "gleam-runtime": "workspace:*", @@ -38,13 +39,13 @@ "java-runtime": "workspace:*" }, "devDependencies": { - "@iconify-json/lucide": "^1.1.207", + "@iconify-json/lucide": "^1.1.208", "@iconify/svelte": "^4.0.2", "@tailwindcss/typography": "^0.5.14", "@types/color": "^3.0.6", "color": "^4.2.3", "daisyui": "^4.12.10", - "svelte": "5.0.0-next.225", + "svelte": "5.0.0-next.237", "vite-plugin-cross-origin-isolation": "^0.1.6", "vite-plugin-static-copy": "^1.0.6" } diff --git a/apps/ppp/src/adapters/runtime/dotnet/compiler-factory.ts b/apps/ppp/src/adapters/runtime/dotnet/compiler-factory.ts new file mode 100644 index 0000000..3ac629e --- /dev/null +++ b/apps/ppp/src/adapters/runtime/dotnet/compiler-factory.ts @@ -0,0 +1,245 @@ +import { + type CompilerModuleExports, + type CompilerModuleImports, + type DotnetModule, + DotnetProgram, + DotnetCompilerFactory, + DotnetRuntimeFactory, +} from "dotnet-runtime"; +import type { CompilerFactory } from "compiler"; +import { inContext } from "libs/context"; +import { createLogger, redirect } from "libs/logger"; +import { patch } from "libs/patcher"; + +const dotnetUrl = new URL( + import.meta.env.BASE_URL + "/_astro/dotnet/compiler/dotnet.js", + globalThis.location.origin +).toString(); + +const precompiledLibsIndexUrl = new URL( + import.meta.env.BASE_URL + "/_astro/dotnet/lib", + globalThis.location.origin +).toString(); + +export const LIBS = [ + "Humanizer.dll", + "Microsoft.Bcl.AsyncInterfaces.dll", + "Microsoft.CSharp.dll", + // "Microsoft.CodeAnalysis.CSharp.Workspaces.dll", + // "Microsoft.CodeAnalysis.CSharp.dll", + // "Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll", + // "Microsoft.CodeAnalysis.VisualBasic.dll", + // "Microsoft.CodeAnalysis.Workspaces.dll", + // "Microsoft.CodeAnalysis.dll", + // "Microsoft.JSInterop.WebAssembly.dll", + // "Microsoft.JSInterop.dll", + // "Microsoft.VisualBasic.Core.dll", + // "Microsoft.VisualBasic.dll", + // "Microsoft.Win32.Primitives.dll", + // "Microsoft.Win32.Registry.dll", + "System.AppContext.dll", + "System.Buffers.dll", + "System.Collections.Concurrent.dll", + "System.Collections.Immutable.dll", + "System.Collections.NonGeneric.dll", + "System.Collections.Specialized.dll", + "System.Collections.dll", + // "System.ComponentModel.Annotations.dll", + // "System.ComponentModel.DataAnnotations.dll", + // "System.ComponentModel.EventBasedAsync.dll", + // "System.ComponentModel.Primitives.dll", + // "System.ComponentModel.TypeConverter.dll", + // "System.ComponentModel.dll", + // "System.Composition.AttributedModel.dll", + // "System.Composition.Convention.dll", + // "System.Composition.Hosting.dll", + // "System.Composition.Runtime.dll", + // "System.Composition.TypedParts.dll", + // "System.Configuration.dll", + "System.Console.dll", + "System.Core.dll", + // "System.Data.Common.dll", + // "System.Data.DataSetExtensions.dll", + // "System.Data.dll", + // "System.Diagnostics.Contracts.dll", + "System.Diagnostics.Debug.dll", + // "System.Diagnostics.DiagnosticSource.dll", + // "System.Diagnostics.FileVersionInfo.dll", + // "System.Diagnostics.Process.dll", + // "System.Diagnostics.StackTrace.dll", + // "System.Diagnostics.TextWriterTraceListener.dll", + // "System.Diagnostics.Tools.dll", + // "System.Diagnostics.TraceSource.dll", + // "System.Diagnostics.Tracing.dll", + // "System.Drawing.Primitives.dll", + // "System.Drawing.dll", + // "System.Dynamic.Runtime.dll", + // "System.Formats.Asn1.dll", + // "System.Formats.Tar.dll", + "System.Globalization.Calendars.dll", + "System.Globalization.Extensions.dll", + "System.Globalization.dll", + // "System.IO.Compression.Brotli.dll", + // "System.IO.Compression.FileSystem.dll", + // "System.IO.Compression.ZipFile.dll", + // "System.IO.Compression.dll", + // "System.IO.FileSystem.AccessControl.dll", + // "System.IO.FileSystem.DriveInfo.dll", + // "System.IO.FileSystem.Primitives.dll", + // "System.IO.FileSystem.Watcher.dll", + "System.IO.FileSystem.dll", + // "System.IO.IsolatedStorage.dll", + // "System.IO.MemoryMappedFiles.dll", + // "System.IO.Pipelines.dll", + // "System.IO.Pipes.AccessControl.dll", + // "System.IO.Pipes.dll", + "System.IO.UnmanagedMemoryStream.dll", + "System.IO.dll", + "System.Linq.Expressions.dll", + "System.Linq.Parallel.dll", + "System.Linq.Queryable.dll", + "System.Linq.dll", + "System.Memory.dll", + "System.Net.Http.Json.dll", + "System.Net.Http.dll", + "System.Net.HttpListener.dll", + // "System.Net.Mail.dll", + "System.Net.NameResolution.dll", + "System.Net.NetworkInformation.dll", + // "System.Net.Ping.dll", + "System.Net.Primitives.dll", + // "System.Net.Quic.dll", + "System.Net.Requests.dll", + // "System.Net.Security.dll", + // "System.Net.ServicePoint.dll", + "System.Net.Sockets.dll", + "System.Net.WebClient.dll", + "System.Net.WebHeaderCollection.dll", + // "System.Net.WebProxy.dll", + // "System.Net.WebSockets.Client.dll", + // "System.Net.WebSockets.dll", + "System.Net.dll", + "System.Numerics.Vectors.dll", + "System.Numerics.dll", + "System.ObjectModel.dll", + "System.Private.CoreLib.dll", + // "System.Private.DataContractSerialization.dll", + "System.Private.Uri.dll", + // "System.Private.Xml.Linq.dll", + // "System.Private.Xml.dll", + // "System.Reflection.DispatchProxy.dll", + // "System.Reflection.Emit.ILGeneration.dll", + // "System.Reflection.Emit.Lightweight.dll", + // "System.Reflection.Emit.dll", + // "System.Reflection.Extensions.dll", + // "System.Reflection.Metadata.dll", + // "System.Reflection.Primitives.dll", + // "System.Reflection.TypeExtensions.dll", + "System.Reflection.dll", + "System.Resources.Reader.dll", + "System.Resources.ResourceManager.dll", + "System.Resources.Writer.dll", + // "System.Runtime.CompilerServices.Unsafe.dll", + // "System.Runtime.CompilerServices.VisualC.dll", + "System.Runtime.Extensions.dll", + "System.Runtime.Handles.dll", + // "System.Runtime.InteropServices.JavaScript.dll", + "System.Runtime.InteropServices.RuntimeInformation.dll", + "System.Runtime.InteropServices.dll", + // "System.Runtime.Intrinsics.dll", + // "System.Runtime.Loader.dll", + // "System.Runtime.Numerics.dll", + // "System.Runtime.Serialization.Formatters.dll", + "System.Runtime.Serialization.Json.dll", + // "System.Runtime.Serialization.Primitives.dll", + // "System.Runtime.Serialization.Xml.dll", + // "System.Runtime.Serialization.dll", + "System.Runtime.dll", + // "System.Security.AccessControl.dll", + // "System.Security.Claims.dll", + // "System.Security.Cryptography.Algorithms.dll", + // "System.Security.Cryptography.Cng.dll", + // "System.Security.Cryptography.Csp.dll", + // "System.Security.Cryptography.Encoding.dll", + // "System.Security.Cryptography.OpenSsl.dll", + // "System.Security.Cryptography.Primitives.dll", + // "System.Security.Cryptography.X509Certificates.dll", + // "System.Security.Cryptography.dll", + // "System.Security.Principal.Windows.dll", + "System.Security.Principal.dll", + "System.Security.SecureString.dll", + "System.Security.dll", + // "System.ServiceModel.Web.dll", + // "System.ServiceProcess.dll", + // "System.Text.Encoding.CodePages.dll", + "System.Text.Encoding.Extensions.dll", + "System.Text.Encoding.dll", + "System.Text.Encodings.Web.dll", + "System.Text.Json.dll", + "System.Text.RegularExpressions.dll", + // "System.Threading.Channels.dll", + // "System.Threading.Overlapped.dll", + // "System.Threading.Tasks.Dataflow.dll", + "System.Threading.Tasks.Extensions.dll", + "System.Threading.Tasks.Parallel.dll", + "System.Threading.Tasks.dll", + "System.Threading.Thread.dll", + "System.Threading.ThreadPool.dll", + "System.Threading.Timer.dll", + "System.Threading.dll", + // "System.Transactions.Local.dll", + "System.Transactions.dll", + "System.ValueTuple.dll", + "System.Web.HttpUtility.dll", + // "System.Web.dll", + // "System.Windows.dll", + // "System.Xml.Linq.dll", + // "System.Xml.ReaderWriter.dll", + // "System.Xml.Serialization.dll", + // "System.Xml.XDocument.dll", + // "System.Xml.XPath.XDocument.dll", + // "System.Xml.XPath.dll", + // "System.Xml.XmlDocument.dll", + // "System.Xml.XmlSerializer.dll", + // "System.Xml.dll", + "System.dll", + "WebAssembly.dll", + // "WindowsBase.dll", + // "compiler.dll", + "mscorlib.dll", + "netstandard.dll", +]; + +export const makeDotnetCompiler: CompilerFactory = async (ctx, out) => { + const log = createLogger(out); + const patchedConsole = redirect(globalThis.console, log); + + const { dotnet } = await inContext(ctx, import(/* @vite-ignore */ dotnetUrl)); + const consolePatch = patch(globalThis, "console", patchedConsole); + try { + const compilerModule: DotnetModule< + CompilerModuleImports, + CompilerModuleExports + > = await inContext(ctx, dotnet.create()); + const compiler = await inContext( + ctx, + new DotnetCompilerFactory(log, compilerModule).create( + precompiledLibsIndexUrl, + LIBS + ) + ); + const runtimeFactory = new DotnetRuntimeFactory(compiler); + return { + async compile(_, files) { + if (files.length !== 1) { + throw new Error("Compilation of multiple files is not implemented"); + } + const runtime = runtimeFactory.create(files[0].content); + return new DotnetProgram(runtime); + }, + [Symbol.dispose]() {}, + }; + } finally { + consolePatch[Symbol.dispose](); + } +}; diff --git a/apps/ppp/src/adapters/runtime/dotnet/description.svelte b/apps/ppp/src/adapters/runtime/dotnet/description.svelte index fd6f954..bf0f9c6 100644 --- a/apps/ppp/src/adapters/runtime/dotnet/description.svelte +++ b/apps/ppp/src/adapters/runtime/dotnet/description.svelte @@ -1,26 +1,9 @@

- .NET {version} -

- -

- Your code is compiled by the Roslyn compiler (compiled to WebAssembly with a - several .NET assemblies) and is executed in the context of the current page. + Class Program with a public static method Main is required.

-

Namespace test is reserved.

- -

Available libraries:

- -
- {#each LIBS as lib} -

- {lib} -

- {/each} -
+ diff --git a/apps/ppp/src/adapters/runtime/dotnet/info.svelte b/apps/ppp/src/adapters/runtime/dotnet/info.svelte new file mode 100644 index 0000000..9d9699e --- /dev/null +++ b/apps/ppp/src/adapters/runtime/dotnet/info.svelte @@ -0,0 +1,24 @@ + + +

+ .NET {version} +

+ +

+ Your code is compiled by the Roslyn compiler (compiled to WebAssembly with a + several .NET assemblies) and is executed in the context of the current page. +

+ +

Available libraries:

+ +
+ {#each LIBS as lib} +

+ {lib} +

+ {/each} +
diff --git a/apps/ppp/src/adapters/runtime/dotnet/test-description.svelte b/apps/ppp/src/adapters/runtime/dotnet/test-description.svelte new file mode 100644 index 0000000..ae87a4a --- /dev/null +++ b/apps/ppp/src/adapters/runtime/dotnet/test-description.svelte @@ -0,0 +1,7 @@ + + +

Namespace test is reserved.

+ + diff --git a/apps/ppp/src/adapters/runtime/dotnet/worker.ts b/apps/ppp/src/adapters/runtime/dotnet/worker.ts new file mode 100644 index 0000000..3cb34d4 --- /dev/null +++ b/apps/ppp/src/adapters/runtime/dotnet/worker.ts @@ -0,0 +1,5 @@ +import { startCompilerActor } from "compiler/actor"; + +import { makeDotnetCompiler } from "./compiler-factory"; + +startCompilerActor(makeDotnetCompiler); diff --git a/apps/ppp/src/adapters/runtime/gleam/compiler-factory.ts b/apps/ppp/src/adapters/runtime/gleam/compiler-factory.ts new file mode 100644 index 0000000..ba7cbdc --- /dev/null +++ b/apps/ppp/src/adapters/runtime/gleam/compiler-factory.ts @@ -0,0 +1,44 @@ +import { redirect, createLogger } from "libs/logger"; +import type { CompilerFactory } from "compiler"; +import { + GleamModuleCompiler, + type GleamModule, + GleamProgram, +} from "gleam-runtime"; + +// @ts-expect-error .wasm is an asset +import compilerWasmUrl from "gleam-runtime/compiler.wasm"; +import { compileJsModule } from "libs/js"; + +const precompiledGleamStdlibIndexUrl = new URL( + import.meta.env.BASE_URL + "/_astro/gleam", + globalThis.location.origin +).toString(); + +export const makeGleamCompiler: CompilerFactory = async (ctx, out) => { + const patchedConsole = redirect(globalThis.console, createLogger(out)); + const compiler = new GleamModuleCompiler( + out, + precompiledGleamStdlibIndexUrl, + await WebAssembly.compileStreaming( + fetch(compilerWasmUrl, { signal: ctx.signal }) + ) + ); + return { + async compile(_, files) { + if (files.length !== 1) { + throw new Error("Compilation of multiple files is not implemented"); + } + const jsCode = compiler.compile(files[0].content); + const jsModule = await compileJsModule(jsCode); + if (!jsModule || typeof jsModule !== "object") { + throw new Error("Compilation failed"); + } + if (!("main" in jsModule) || typeof jsModule.main !== "function") { + throw new Error("Main function is missing"); + } + return new GleamProgram(jsModule as GleamModule, patchedConsole); + }, + [Symbol.dispose]() {}, + }; +}; diff --git a/apps/ppp/src/adapters/runtime/gleam/worker.ts b/apps/ppp/src/adapters/runtime/gleam/worker.ts new file mode 100644 index 0000000..50e83e4 --- /dev/null +++ b/apps/ppp/src/adapters/runtime/gleam/worker.ts @@ -0,0 +1,5 @@ +import { startCompilerActor } from "compiler/actor"; + +import { makeGleamCompiler } from "./compiler-factory"; + +startCompilerActor(makeGleamCompiler); diff --git a/apps/ppp/src/adapters/runtime/go/compiler-factory.ts b/apps/ppp/src/adapters/runtime/go/compiler-factory.ts new file mode 100644 index 0000000..974e3c0 --- /dev/null +++ b/apps/ppp/src/adapters/runtime/go/compiler-factory.ts @@ -0,0 +1,27 @@ +import { + GoProgram, + makeCompilerFactory, + makeGoCompilerFactory, + makeGoExecutorFactory, +} from "go-runtime"; +import type { CompilerFactory } from "compiler"; +import { inContext } from "libs/context"; + +import wasmInit from "go-runtime/compiler.wasm?init"; + +export const makeGoCompiler: CompilerFactory = async (ctx, out) => { + const goExecutorFactory = makeGoExecutorFactory( + makeGoCompilerFactory( + await makeCompilerFactory((imports) => inContext(ctx, wasmInit(imports))) + ) + ); + return { + async compile(ctx, files) { + if (files.length !== 1) { + throw new Error("Compilation of multiple files is not implemented"); + } + return new GoProgram(await goExecutorFactory(ctx, out, files[0].content)); + }, + [Symbol.dispose]() {}, + }; +}; diff --git a/apps/ppp/src/adapters/runtime/go/test-compiler-factory.ts b/apps/ppp/src/adapters/runtime/go/test-compiler-factory.ts index 76ea014..80a653f 100644 --- a/apps/ppp/src/adapters/runtime/go/test-compiler-factory.ts +++ b/apps/ppp/src/adapters/runtime/go/test-compiler-factory.ts @@ -2,9 +2,10 @@ import { inContext, type Context } from "libs/context"; import type { Writer } from "libs/io"; import type { TestCompiler } from "testing"; import { - createCompilerFactory, + makeCompilerFactory, + makeGoCompilerFactory, + makeGoEvaluatorFactory, GoTestProgram, - makeGoRuntimeFactory, } from "go-runtime"; import wasmInit from "go-runtime/compiler.wasm?init"; @@ -23,9 +24,11 @@ export class GoTestCompilerFactory { return generateCaseExecutionCode(input); } } - const goRuntimeFactory = makeGoRuntimeFactory( - await createCompilerFactory((imports) => - inContext(ctx, wasmInit(imports)) + const goEvaluatorFactory = makeGoEvaluatorFactory( + makeGoCompilerFactory( + await makeCompilerFactory((imports) => + inContext(ctx, wasmInit(imports)) + ) ) ); return { @@ -34,7 +37,7 @@ export class GoTestCompilerFactory { throw new Error("Compilation of multiple files is not implemented"); } return new TestProgram( - await goRuntimeFactory(ctx, this.out, files[0].content) + await goEvaluatorFactory(ctx, this.out, files[0].content) ); }, [Symbol.dispose]() {}, diff --git a/apps/ppp/src/adapters/runtime/go/worker.ts b/apps/ppp/src/adapters/runtime/go/worker.ts new file mode 100644 index 0000000..c349b89 --- /dev/null +++ b/apps/ppp/src/adapters/runtime/go/worker.ts @@ -0,0 +1,5 @@ +import { startCompilerActor } from "compiler/actor"; + +import { makeGoCompiler } from "./compiler-factory"; + +startCompilerActor(makeGoCompiler); diff --git a/apps/ppp/src/adapters/runtime/java/compiler-factory.ts b/apps/ppp/src/adapters/runtime/java/compiler-factory.ts new file mode 100644 index 0000000..a056e3c --- /dev/null +++ b/apps/ppp/src/adapters/runtime/java/compiler-factory.ts @@ -0,0 +1,32 @@ +import { + initFs, + JavaCompiler, + JavaProgram, + makeJVMFactory, +} from "java-runtime"; +import type { CompilerFactory } from "compiler"; + +// @ts-expect-error vite url import +import libZipUrl from "java-runtime/doppio.zip"; + +const CLASSNAME = "Program"; + +export const makeJavaCompiler: CompilerFactory = async (ctx, out) => { + const jvmFactory = makeJVMFactory(out); + const libZipData = await fetch(libZipUrl, { + signal: ctx.signal, + cache: "force-cache", + }).then((response) => response.arrayBuffer()); + const fs = await initFs(libZipData); + const compiler = new JavaCompiler(jvmFactory, `/home/${CLASSNAME}.java`, fs); + return { + async compile(ctx, files) { + if (files.length !== 1) { + throw new Error("Compilation of multiple files is not implemented"); + } + await compiler.compile(ctx, files[0].content); + return new JavaProgram(CLASSNAME, jvmFactory); + }, + [Symbol.dispose]() {}, + }; +}; diff --git a/apps/ppp/src/adapters/runtime/java/description.svelte b/apps/ppp/src/adapters/runtime/java/description.svelte index 4454e53..a3dc1fa 100644 --- a/apps/ppp/src/adapters/runtime/java/description.svelte +++ b/apps/ppp/src/adapters/runtime/java/description.svelte @@ -1,18 +1,9 @@

- {version} + Class Program with a public static method main is required.

-

- Your code is compiled by Javac and executed in - DoppioJVM in a web worker environment. -

- -

- Public class Test is reserved. -

+ diff --git a/apps/ppp/src/adapters/runtime/java/info.svelte b/apps/ppp/src/adapters/runtime/java/info.svelte new file mode 100644 index 0000000..26c72ba --- /dev/null +++ b/apps/ppp/src/adapters/runtime/java/info.svelte @@ -0,0 +1,14 @@ + + +

+ {version} +

+ +

+ Your code is compiled by Javac and executed in + DoppioJVM in a web worker environment. +

diff --git a/apps/ppp/src/adapters/runtime/java/test-description.svelte b/apps/ppp/src/adapters/runtime/java/test-description.svelte new file mode 100644 index 0000000..787d3df --- /dev/null +++ b/apps/ppp/src/adapters/runtime/java/test-description.svelte @@ -0,0 +1,9 @@ + + +

+ Public class Test is reserved. +

+ + diff --git a/apps/ppp/src/adapters/runtime/java/worker.ts b/apps/ppp/src/adapters/runtime/java/worker.ts new file mode 100644 index 0000000..bd56a88 --- /dev/null +++ b/apps/ppp/src/adapters/runtime/java/worker.ts @@ -0,0 +1,5 @@ +import { startCompilerActor } from "compiler/actor"; + +import { makeJavaCompiler } from "./compiler-factory"; + +startCompilerActor(makeJavaCompiler); diff --git a/apps/ppp/src/adapters/runtime/js/compiler-factory.ts b/apps/ppp/src/adapters/runtime/js/compiler-factory.ts new file mode 100644 index 0000000..ee1ea30 --- /dev/null +++ b/apps/ppp/src/adapters/runtime/js/compiler-factory.ts @@ -0,0 +1,16 @@ +import { redirect, createLogger } from "libs/logger"; +import type { CompilerFactory } from "compiler"; +import { JsProgram } from "javascript-runtime"; + +export const makeJsCompiler: CompilerFactory = async (_, out) => { + const patchedConsole = redirect(globalThis.console, createLogger(out)); + return { + async compile(_, files) { + if (files.length !== 1) { + throw new Error("Compilation of multiple files is not implemented"); + } + return new JsProgram(files[0].content, patchedConsole); + }, + [Symbol.dispose]() {}, + }; +}; diff --git a/apps/ppp/src/adapters/runtime/js/worker.ts b/apps/ppp/src/adapters/runtime/js/worker.ts new file mode 100644 index 0000000..5440484 --- /dev/null +++ b/apps/ppp/src/adapters/runtime/js/worker.ts @@ -0,0 +1,5 @@ +import { startCompilerActor } from "compiler/actor"; + +import { makeJsCompiler } from "./compiler-factory"; + +startCompilerActor(makeJsCompiler); diff --git a/apps/ppp/src/adapters/runtime/php/compiler-factory.ts b/apps/ppp/src/adapters/runtime/php/compiler-factory.ts new file mode 100644 index 0000000..d2b498a --- /dev/null +++ b/apps/ppp/src/adapters/runtime/php/compiler-factory.ts @@ -0,0 +1,18 @@ +import { inContext } from "libs/context"; +import type { CompilerFactory } from "compiler"; +import { phpCompilerFactory, PHPProgram } from "php-runtime"; + +export const makePhpCompiler: CompilerFactory = async (ctx, out) => { + const php = await inContext(ctx, phpCompilerFactory()); + return { + async compile(_, files) { + if (files.length !== 1) { + throw new Error("Compilation of multiple files is not implemented"); + } + return new PHPProgram(files[0].content, php, out); + }, + [Symbol.dispose]() { + php[Symbol.dispose](); + }, + }; +}; diff --git a/apps/ppp/src/adapters/runtime/php/worker.ts b/apps/ppp/src/adapters/runtime/php/worker.ts new file mode 100644 index 0000000..128549a --- /dev/null +++ b/apps/ppp/src/adapters/runtime/php/worker.ts @@ -0,0 +1,5 @@ +import { startCompilerActor } from "compiler/actor"; + +import { makePhpCompiler } from "./compiler-factory"; + +startCompilerActor(makePhpCompiler); diff --git a/apps/ppp/src/adapters/runtime/python/compiler-factory.ts b/apps/ppp/src/adapters/runtime/python/compiler-factory.ts new file mode 100644 index 0000000..e5fb280 --- /dev/null +++ b/apps/ppp/src/adapters/runtime/python/compiler-factory.ts @@ -0,0 +1,31 @@ +import type { CompilerFactory } from "compiler"; +import { createLogger } from "libs/logger"; +import { PyProgram, pyRuntimeFactory } from "python-runtime"; + +// @ts-ignore +import wasmUrl from "python-runtime/pyodide.wasm"; +// @ts-ignore +import stdlibUrl from "python-runtime/python-stdlib.zip"; + +export const makePythonCompiler: CompilerFactory = async (ctx, out) => { + const log = createLogger(out); + const pyRuntime = await pyRuntimeFactory( + ctx, + log, + (ctx, imports) => + WebAssembly.instantiateStreaming( + fetch(wasmUrl, { signal: ctx.signal }), + imports + ), + stdlibUrl + ); + return { + async compile(_, files) { + if (files.length !== 1) { + throw new Error("Compilation of multiple files is not implemented"); + } + return new PyProgram(files[0].content, pyRuntime); + }, + [Symbol.dispose]() {}, + }; +}; diff --git a/apps/ppp/src/adapters/runtime/python/worker.ts b/apps/ppp/src/adapters/runtime/python/worker.ts new file mode 100644 index 0000000..0a0e618 --- /dev/null +++ b/apps/ppp/src/adapters/runtime/python/worker.ts @@ -0,0 +1,5 @@ +import { startCompilerActor } from "compiler/actor"; + +import { makePythonCompiler } from "./compiler-factory"; + +startCompilerActor(makePythonCompiler); diff --git a/apps/ppp/src/adapters/runtime/rust/compiler-factory.ts b/apps/ppp/src/adapters/runtime/rust/compiler-factory.ts new file mode 100644 index 0000000..584dbe6 --- /dev/null +++ b/apps/ppp/src/adapters/runtime/rust/compiler-factory.ts @@ -0,0 +1,66 @@ +import type { Context } from "libs/context"; +import { COLOR_ENCODED } from "libs/logger"; +import { isErr } from "libs/result"; +import type { CompilerFactory } from "compiler"; +import { RustProgram, wasiRuntimeFactory } from "rust-runtime"; + +// @ts-expect-error .wasm is an asset +import miriWasmUrl from "rust-runtime/miri.wasm"; + +const libsUrls = import.meta.glob("/node_modules/rust-runtime/dist/lib/*", { + eager: true, + import: "default", +}) as Record; + +// TODO: manual cache for large assets +function loadLibs(ctx: Context) { + return Promise.all( + Object.entries(libsUrls).map(async ([lib, url]) => { + const response = await fetch(url, { + signal: ctx.signal, + cache: "force-cache", + }); + const buffer = await response.arrayBuffer(); + return [lib.slice(36), buffer] as [string, ArrayBuffer]; + }) + ); +} + +export const makeRustCompiler: CompilerFactory = async (ctx, out) => { + const [miri, libs] = await Promise.all([ + await WebAssembly.compileStreaming( + fetch(miriWasmUrl, { signal: ctx.signal, cache: "force-cache" }) + ), + loadLibs(ctx), + ]); + const wasi = wasiRuntimeFactory( + out, + { + write: (text) => { + let r = out.write(COLOR_ENCODED.ERROR); + if (isErr(r)) { + return r; + } + const r2 = out.write(text); + if (isErr(r2)) { + return r2; + } + r = out.write(COLOR_ENCODED.RESET); + if (isErr(r)) { + return r; + } + return r2; + }, + }, + libs + ); + return { + async compile(_, files) { + if (files.length !== 1) { + throw new Error("Compilation of multiple files is not implemented"); + } + return new RustProgram(files[0].content, wasi, miri); + }, + [Symbol.dispose]() {}, + }; +}; diff --git a/apps/ppp/src/adapters/runtime/rust/test-compiler-factory.ts b/apps/ppp/src/adapters/runtime/rust/test-compiler-factory.ts index fca8cac..810e042 100644 --- a/apps/ppp/src/adapters/runtime/rust/test-compiler-factory.ts +++ b/apps/ppp/src/adapters/runtime/rust/test-compiler-factory.ts @@ -13,16 +13,6 @@ const libsUrls = import.meta.glob("/node_modules/rust-runtime/dist/lib/*", { import: "default", }) as Record; -export interface RustUniversalFactoryData { - RustTestProgram: typeof RustTestProgram; - wasiRuntimeFactory: typeof wasiRuntimeFactory; - makeTestProgramCompiler: ( - ctx: Context, - generateOutputContentCode: (input: I) => string, - transformResult: (result: string) => O - ) => Promise>; -} - // TODO: manual cache for large assets function loadLibs(ctx: Context) { return Promise.all( diff --git a/apps/ppp/src/adapters/runtime/rust/worker.ts b/apps/ppp/src/adapters/runtime/rust/worker.ts new file mode 100644 index 0000000..20ba139 --- /dev/null +++ b/apps/ppp/src/adapters/runtime/rust/worker.ts @@ -0,0 +1,5 @@ +import { startCompilerActor } from "compiler/actor"; + +import { makeRustCompiler } from "./compiler-factory"; + +startCompilerActor(makeRustCompiler); diff --git a/apps/ppp/src/adapters/runtime/test-descriptions.ts b/apps/ppp/src/adapters/runtime/test-descriptions.ts new file mode 100644 index 0000000..69e935e --- /dev/null +++ b/apps/ppp/src/adapters/runtime/test-descriptions.ts @@ -0,0 +1,25 @@ +import type { Component } from "svelte"; + +import { Language } from "@/shared/languages"; + +import JsDescription from "./js/description.svelte"; +import TsDescription from "./ts/description.svelte"; +import PhpDescription from "./php/description.svelte"; +import PyDescription from "./python/description.svelte"; +import GoDescription from "./go/description.svelte"; +import RustDescription from "./rust/description.svelte"; +import GleamDescription from "./gleam/description.svelte"; +import DotnetDescription from "./dotnet/test-description.svelte"; +import JavaDescription from "./java/test-description.svelte"; + +export const DESCRIPTIONS: Record = { + [Language.JavaScript]: JsDescription, + [Language.TypeScript]: TsDescription, + [Language.PHP]: PhpDescription, + [Language.Python]: PyDescription, + [Language.Go]: GoDescription, + [Language.Rust]: RustDescription, + [Language.Gleam]: GleamDescription, + [Language.CSharp]: DotnetDescription, + [Language.Java]: JavaDescription, +}; diff --git a/apps/ppp/src/adapters/runtime/ts/compiler-factory.ts b/apps/ppp/src/adapters/runtime/ts/compiler-factory.ts new file mode 100644 index 0000000..a8ddcb1 --- /dev/null +++ b/apps/ppp/src/adapters/runtime/ts/compiler-factory.ts @@ -0,0 +1,20 @@ +import { JsProgram } from "javascript-runtime"; +import type { CompilerFactory } from "compiler"; +import { redirect, createLogger } from "libs/logger"; +import { compileTsModule } from "typescript-runtime"; + +export const makeTsCompiler: CompilerFactory = async (_, out) => { + const patchedConsole = redirect(globalThis.console, createLogger(out)); + return { + async compile (_, files) { + if (files.length !== 1) { + throw new Error("Compilation of multiple files is not implemented"); + } + return new JsProgram( + compileTsModule(files[0].content), + patchedConsole + ); + }, + [Symbol.dispose]() {}, + }; +}; diff --git a/apps/ppp/src/adapters/runtime/ts/worker.ts b/apps/ppp/src/adapters/runtime/ts/worker.ts new file mode 100644 index 0000000..a5b1c61 --- /dev/null +++ b/apps/ppp/src/adapters/runtime/ts/worker.ts @@ -0,0 +1,5 @@ +import { startCompilerActor } from "compiler/actor"; + +import { makeTsCompiler } from "./compiler-factory"; + +startCompilerActor(makeTsCompiler); diff --git a/apps/ppp/src/adapters/storage.svelte.ts b/apps/ppp/src/adapters/storage.svelte.ts deleted file mode 100644 index 83090b1..0000000 --- a/apps/ppp/src/adapters/storage.svelte.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { SyncStorage } from "@/shared"; - -export interface StorageState { - value: T; -} - -export function reactive(storage: SyncStorage): StorageState { - let value = $state(storage.load()); - return { - get value() { - return value; - }, - set value(newValue) { - value = newValue; - storage.save(newValue); - }, - }; -} diff --git a/apps/ppp/src/adapters/storage.ts b/apps/ppp/src/adapters/storage.ts index 8d82a8f..42c7e07 100644 --- a/apps/ppp/src/adapters/storage.ts +++ b/apps/ppp/src/adapters/storage.ts @@ -1,4 +1,4 @@ -import type { SyncStorage } from "@/shared"; +import type { SyncStorage } from "@/lib/sync-storage.svelte"; export function createSyncStorage( storage: Storage, diff --git a/apps/ppp/src/components/editor/context.svelte.ts b/apps/ppp/src/components/editor/context.svelte.ts new file mode 100644 index 0000000..86f2866 --- /dev/null +++ b/apps/ppp/src/components/editor/context.svelte.ts @@ -0,0 +1,26 @@ +import { getContext, setContext } from "svelte"; +import type { editor } from "monaco-editor"; +import type { Terminal } from "@xterm/xterm"; +import type { FitAddon } from '@xterm/addon-fit'; + +import type { Lang } from "@/i18n"; + +export class EditorContext { + editor = $state(); + constructor( + public readonly lang: Lang, + public model: editor.ITextModel, + public terminal: Terminal, + public terminalFitAddon: FitAddon + ) {} +} + +const EDITOR_CONTEXT = Symbol("editor-context"); + +export function setEditorContext(ctx: EditorContext) { + setContext(EDITOR_CONTEXT, ctx); +} + +export function getEditorContext() { + return getContext(EDITOR_CONTEXT); +} diff --git a/apps/ppp/src/components/editor/controls/checkbox.svelte b/apps/ppp/src/components/editor/controls/checkbox.svelte new file mode 100644 index 0000000..91f2ba1 --- /dev/null +++ b/apps/ppp/src/components/editor/controls/checkbox.svelte @@ -0,0 +1,22 @@ + + +
+ +
diff --git a/apps/ppp/src/components/editor/controls/index.ts b/apps/ppp/src/components/editor/controls/index.ts new file mode 100644 index 0000000..01db85c --- /dev/null +++ b/apps/ppp/src/components/editor/controls/index.ts @@ -0,0 +1,2 @@ +export { default as CheckBox } from "./checkbox.svelte"; +export { default as Number } from "./number.svelte"; diff --git a/apps/ppp/src/components/editor/controls/number.svelte b/apps/ppp/src/components/editor/controls/number.svelte new file mode 100644 index 0000000..68f26e3 --- /dev/null +++ b/apps/ppp/src/components/editor/controls/number.svelte @@ -0,0 +1,26 @@ + + + diff --git a/apps/ppp/src/components/editor/editor.svelte b/apps/ppp/src/components/editor/editor.svelte index 1b60b3f..0bb198e 100644 --- a/apps/ppp/src/components/editor/editor.svelte +++ b/apps/ppp/src/components/editor/editor.svelte @@ -1,164 +1,51 @@ - - - - - {#snippet panel({ resizer, api })} - - {#snippet header()} - - - {#snippet preLabel(lang)} - - {/snippet} - {#snippet label(lang)} - {LANGUAGE_TITLE[lang]} - {/snippet} - {#snippet postLabel(lang)} - { - selectedLang = lang - e.stopPropagation() - dialogElement.showModal() - }} class="invisible group-hover:visible" icon="lucide:info" /> - {/snippet} - - {/snippet} - - {/snippet} - - - - - e.stopPropagation()}> - - - \ No newline at end of file +
diff --git a/apps/ppp/src/components/editor/index.ts b/apps/ppp/src/components/editor/index.ts new file mode 100644 index 0000000..ed24a7c --- /dev/null +++ b/apps/ppp/src/components/editor/index.ts @@ -0,0 +1,5 @@ +export * from "./context.svelte"; +export * from "./terminal"; +export { default as Editor } from "./editor.svelte"; +export { default as VimStatus } from "./vim-status.svelte"; +export { default as RunButton } from "./run-button.svelte"; diff --git a/apps/ppp/src/components/editor/model.ts b/apps/ppp/src/components/editor/model.ts deleted file mode 100644 index cbe800c..0000000 --- a/apps/ppp/src/components/editor/model.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { editor } from "monaco-editor"; - -import { Language } from "@/shared/languages"; -import { createSyncStorage } from "@/adapters/storage"; -import { reactive } from "@/adapters/storage.svelte"; - -export interface SurfaceApi { - editor: editor.IStandaloneCodeEditor | undefined; - width: number; - panelHeight: number; - isPanelCollapsed: boolean; - showPanel(height: number): boolean; - hidePanel(): boolean; - togglePanel(height: number): boolean; -} - -export const vimState = reactive( - createSyncStorage(localStorage, "editor-vim", false) -); - -export const testRunnerTimeout = reactive( - createSyncStorage(localStorage, "editor-test-runner-timeout", 60000) -); - -export const LANG_ICONS: Record = { - [Language.JavaScript]: "vscode-icons:file-type-js", - [Language.Python]: "vscode-icons:file-type-python", - [Language.TypeScript]: "vscode-icons:file-type-typescript", - [Language.Go]: "vscode-icons:file-type-go", - [Language.PHP]: "vscode-icons:file-type-php", - [Language.Rust]: "vscode-icons:file-type-rust", - [Language.Gleam]: "vscode-icons:file-type-gleam", - [Language.CSharp]: "vscode-icons:file-type-csharp", - [Language.Java]: "vscode-icons:file-type-java", -}; diff --git a/apps/ppp/src/components/editor/panel/context.svelte.ts b/apps/ppp/src/components/editor/panel/context.svelte.ts new file mode 100644 index 0000000..43b38da --- /dev/null +++ b/apps/ppp/src/components/editor/panel/context.svelte.ts @@ -0,0 +1,21 @@ +import { getContext, setContext } from "svelte"; + +import { EditorPanelTab } from "@/shared/editor-panel-tab"; + +export class EditorPanelContext { + selectedTab = $state(); + + constructor(selectedTab: EditorPanelTab) { + this.selectedTab = selectedTab; + } +} + +const EDITOR_PANEL_CONTEXT = Symbol("editor-panel-context"); + +export function setEditorPanelContext(ctx: EditorPanelContext) { + setContext(EDITOR_PANEL_CONTEXT, ctx); +} + +export function getEditorPanelContext() { + return getContext(EDITOR_PANEL_CONTEXT); +} diff --git a/apps/ppp/src/components/editor/panel/header.svelte b/apps/ppp/src/components/editor/panel/header.svelte deleted file mode 100644 index 2b0aee9..0000000 --- a/apps/ppp/src/components/editor/panel/header.svelte +++ /dev/null @@ -1,96 +0,0 @@ - - -
- -
- {#snippet tabButton({ tab, append }: TabButtonProps)} - - - - { - if (selectedTab === tab) { - selectedTab = null - } else { - selectedTab = tab - } - }} - > - {TAB_TITLES[tab]} - {#if append} - {@render append()} - {/if} - - {/snippet} - {#snippet testBadge()} -
= 0} - > - {lastTestId}/{testsCount} -
- {/snippet} - {@render tabButton({ tab: Tab.Tests, append: testBadge })} - {@render tabButton({ tab: Tab.Output })} - {@render tabButton({ tab: Tab.Settings })} -
-
- {@render append()} -
- - diff --git a/apps/ppp/src/components/editor/panel/index.ts b/apps/ppp/src/components/editor/panel/index.ts new file mode 100644 index 0000000..22cc38d --- /dev/null +++ b/apps/ppp/src/components/editor/panel/index.ts @@ -0,0 +1,7 @@ +export * from "./model"; +export { default as Panel } from "./panel.svelte"; +export { default as Tab } from "./tab.svelte"; +export { default as Tabs } from "./tabs.svelte"; +export { default as TerminalTab } from "./terminal-tab.svelte"; +export { default as TabContent } from "./tab-content.svelte"; +export { default as PanelToggle } from "./panel-toggle.svelte"; diff --git a/apps/ppp/src/components/editor/panel/model.ts b/apps/ppp/src/components/editor/panel/model.ts index ad0dac9..692698a 100644 --- a/apps/ppp/src/components/editor/panel/model.ts +++ b/apps/ppp/src/components/editor/panel/model.ts @@ -1,11 +1,4 @@ -export enum Tab { - Tests = "tests", - Output = "output", - Settings = "settings", -} - -export const TAB_TITLES: Record = { - [Tab.Tests]: "Tests", - [Tab.Output]: "Output", - [Tab.Settings]: "Settings", -}; +export const PANEL_BORDER_HEIGHT = 1; +export const PANEL_HEADER_VERTICAL_PADDING = 4 * 2; +export const MIN_PANEL_HEIGHT = + 32 + PANEL_HEADER_VERTICAL_PADDING + PANEL_BORDER_HEIGHT; diff --git a/apps/ppp/src/components/editor/panel/panel-toggle.svelte b/apps/ppp/src/components/editor/panel/panel-toggle.svelte new file mode 100644 index 0000000..7358a79 --- /dev/null +++ b/apps/ppp/src/components/editor/panel/panel-toggle.svelte @@ -0,0 +1,63 @@ + + + diff --git a/apps/ppp/src/components/editor/panel/panel.svelte b/apps/ppp/src/components/editor/panel/panel.svelte index a82b907..e53d46c 100644 --- a/apps/ppp/src/components/editor/panel/panel.svelte +++ b/apps/ppp/src/components/editor/panel/panel.svelte @@ -1,192 +1,36 @@ - -
- -
- {#if selectedTab === Tab.Tests} - - {:else if selectedTab === Tab.Settings} - - {:else if selectedTab === null} -
- Select a tab -
- {/if} - - - {@render children()} -
-
+ + {@render children()} + diff --git a/apps/ppp/src/components/editor/panel/settings.svelte b/apps/ppp/src/components/editor/panel/settings.svelte deleted file mode 100644 index c5859bc..0000000 --- a/apps/ppp/src/components/editor/panel/settings.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - -
-
- -
- -
diff --git a/apps/ppp/src/components/editor/panel/tab-content.svelte b/apps/ppp/src/components/editor/panel/tab-content.svelte new file mode 100644 index 0000000..fc08147 --- /dev/null +++ b/apps/ppp/src/components/editor/panel/tab-content.svelte @@ -0,0 +1,20 @@ + + +{#if ctx.selectedTab === tab} + {@render children()} +{/if} diff --git a/apps/ppp/src/components/editor/panel/tab.svelte b/apps/ppp/src/components/editor/panel/tab.svelte new file mode 100644 index 0000000..8f924f4 --- /dev/null +++ b/apps/ppp/src/components/editor/panel/tab.svelte @@ -0,0 +1,45 @@ + + + + + { + ctx.selectedTab = tab + }} +> + {t(getEditorPanelTabLabel(tab))} + {#if append} + {@render append()} + {/if} + + + diff --git a/apps/ppp/src/components/editor/panel/tabs.svelte b/apps/ppp/src/components/editor/panel/tabs.svelte new file mode 100644 index 0000000..b4f227d --- /dev/null +++ b/apps/ppp/src/components/editor/panel/tabs.svelte @@ -0,0 +1,22 @@ + + +
+ {@render children()} +
+ + diff --git a/apps/ppp/src/components/editor/panel/terminal-tab.svelte b/apps/ppp/src/components/editor/panel/terminal-tab.svelte new file mode 100644 index 0000000..eec44dd --- /dev/null +++ b/apps/ppp/src/components/editor/panel/terminal-tab.svelte @@ -0,0 +1,49 @@ + + +
diff --git a/apps/ppp/src/components/editor/panel/terminal.svelte b/apps/ppp/src/components/editor/panel/terminal.svelte deleted file mode 100644 index f6870f4..0000000 --- a/apps/ppp/src/components/editor/panel/terminal.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - -
diff --git a/apps/ppp/src/components/editor/panel/terminal.ts b/apps/ppp/src/components/editor/panel/terminal.ts deleted file mode 100644 index dd3995d..0000000 --- a/apps/ppp/src/components/editor/panel/terminal.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { ITheme } from "@xterm/xterm"; -import type { Theme } from "daisyui"; -import themes from "daisyui/src/theming/themes"; - -export function makeTheme(themeName: Theme): ITheme { - const theme = themes[themeName]; - return { - background: "oklch(23.1012% 0 0 / 1)", - }; -} diff --git a/apps/ppp/src/components/editor/panel/tests.svelte b/apps/ppp/src/components/editor/panel/tests.svelte index 40b0d8f..1e38d7f 100644 --- a/apps/ppp/src/components/editor/panel/tests.svelte +++ b/apps/ppp/src/components/editor/panel/tests.svelte @@ -1,18 +1,18 @@
- {#each testsData as testData, i} + {#each testCases as testCase, i}
{#if lastTestId === i} @@ -25,7 +25,7 @@ Case {i + 1}
{JSON.stringify(testData.input, null, 2)}{JSON.stringify(testCase.input, null, 2)}
{/each} diff --git a/apps/ppp/src/components/editor/run-button.svelte b/apps/ppp/src/components/editor/run-button.svelte new file mode 100644 index 0000000..3e3eacd --- /dev/null +++ b/apps/ppp/src/components/editor/run-button.svelte @@ -0,0 +1,26 @@ + + + diff --git a/apps/ppp/src/components/editor/surface.svelte b/apps/ppp/src/components/editor/surface.svelte deleted file mode 100644 index 784fea6..0000000 --- a/apps/ppp/src/components/editor/surface.svelte +++ /dev/null @@ -1,148 +0,0 @@ - - -
- { - start = { x: e.clientX, y: $state.snapshot(width) } - }} - onMove={(e) => { - width = normalizeWidth(start.x - e.clientX + start.y) - ed?.layout({ width, height }, true) - }} - onMoveEnd={() => { - widthStorage.save(width) - }} - /> -
- {#snippet resizer()} - { - start = { x: $state.snapshot(height), y: e.clientY } - }} - onMove={(e) => { - height = normalizeHeight(start.x - (start.y - e.clientY)) - ed?.layout({ width, height }, true) - }} - /> - {/snippet} - {@render panel({ - resizer, - api - })} -
diff --git a/apps/ppp/src/components/editor/terminal.ts b/apps/ppp/src/components/editor/terminal.ts new file mode 100644 index 0000000..09c1405 --- /dev/null +++ b/apps/ppp/src/components/editor/terminal.ts @@ -0,0 +1,34 @@ +import { FitAddon } from "@xterm/addon-fit"; +import { Terminal, type ITheme } from "@xterm/xterm"; +import type { Theme } from "daisyui"; +import themes from "daisyui/src/theming/themes"; +import type { Writer } from "libs/io"; +import { ok } from "libs/result"; + +function makeTerminalTheme(themeName: Theme): ITheme { + const theme = themes[themeName]; + return { + background: "oklch(23.1012% 0 0 / 1)", + }; +} + +export function createTerminal() { + const terminal = new Terminal({ + theme: makeTerminalTheme("business"), + fontFamily: "monospace", + convertEol: true, + rows: 1, + }); + const fitAddon = new FitAddon(); + terminal.loadAddon(fitAddon); + return { terminal, fitAddon }; +} + +export function createTerminalWriter(terminal: Terminal): Writer { + return { + write(data) { + terminal.write(data); + return ok(data.length); + }, + }; +} diff --git a/apps/ppp/src/components/editor/vim-mode.svelte b/apps/ppp/src/components/editor/vim-mode.svelte deleted file mode 100644 index 0dc8f24..0000000 --- a/apps/ppp/src/components/editor/vim-mode.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - -
diff --git a/apps/ppp/src/components/editor/vim-status.svelte b/apps/ppp/src/components/editor/vim-status.svelte new file mode 100644 index 0000000..665942a --- /dev/null +++ b/apps/ppp/src/components/editor/vim-status.svelte @@ -0,0 +1,28 @@ + + +
diff --git a/apps/ppp/src/components/logo.svelte b/apps/ppp/src/components/logo.svelte new file mode 100644 index 0000000..ef65115 --- /dev/null +++ b/apps/ppp/src/components/logo.svelte @@ -0,0 +1,18 @@ + + + +
+ 3P +
+
diff --git a/apps/ppp/src/components/resizable-panel.svelte b/apps/ppp/src/components/resizable-panel.svelte new file mode 100644 index 0000000..c14aa93 --- /dev/null +++ b/apps/ppp/src/components/resizable-panel.svelte @@ -0,0 +1,79 @@ + + + + +
+ { + start = { x: size, y: e[coord] }; + }} + onMove={(e) => { + size = normalizeSize(op(start.y, e[coord], start.x), size); + }} + /> + {@render children()} +
diff --git a/apps/ppp/src/components/resizer/index.ts b/apps/ppp/src/components/resizer/index.ts new file mode 100644 index 0000000..f12e0d3 --- /dev/null +++ b/apps/ppp/src/components/resizer/index.ts @@ -0,0 +1,2 @@ +export * from './model' +export { default as Resizer } from './resizer.svelte' diff --git a/apps/ppp/src/components/resizer/model.ts b/apps/ppp/src/components/resizer/model.ts new file mode 100644 index 0000000..1d2710d --- /dev/null +++ b/apps/ppp/src/components/resizer/model.ts @@ -0,0 +1,9 @@ +export enum Orientation { + Vertical = "vertical", + Horizontal = "horizontal", +} + +export enum Alignment { + Start = "start", + End = "end", +} diff --git a/apps/ppp/src/components/resizer.svelte b/apps/ppp/src/components/resizer/resizer.svelte similarity index 89% rename from apps/ppp/src/components/resizer.svelte rename to apps/ppp/src/components/resizer/resizer.svelte index 6b93621..ab1434f 100644 --- a/apps/ppp/src/components/resizer.svelte +++ b/apps/ppp/src/components/resizer/resizer.svelte @@ -1,16 +1,6 @@ - - + +
+
+
+ +
+ {@render children()} +
+
+
+ + + +
+ + + + + {#snippet append()} +
= 0} + > + {lastTestId}/{testCases.length} +
+ {/snippet} +
+ +
+
+ + + {#snippet preLabel(lang)} + + {/snippet} + {#snippet label(lang)} + {LANGUAGE_TITLE[lang]} + {/snippet} + {#snippet postLabel(lang)} + { + describedLanguage = lang + e.stopPropagation() + descriptionDialogElement.showModal() + }} class="invisible group-hover:visible" icon="lucide:info" /> + {/snippet} + + +
+
+ + +
+ {#each testCases as testCase, i} +
+
+ {#if lastTestId === i} + + {:else if i < lastTestId} + + {:else} + + {/if} + Case {i + 1} +
+
{JSON.stringify(testCase.input, null, 2)}
+
+ {/each} +
+
+ +
+ + +
+
+
+
+
+
+ + + + e.stopPropagation()}> + + + diff --git a/apps/ppp/src/content/config.ts b/apps/ppp/src/content/config.ts new file mode 100644 index 0000000..3a1adf3 --- /dev/null +++ b/apps/ppp/src/content/config.ts @@ -0,0 +1,27 @@ +import { defineCollection, z } from "astro:content"; +import { glob } from "astro/loaders"; +import type { GenerateIdOptions } from "node_modules/astro/dist/content/loaders/glob"; + +function generateId({ data, entry }: GenerateIdOptions) { + if (typeof data.slug === "string") { + return data.slug; + } + const slug = entry + .replaceAll(/\(.+?\)\//g, "") + .replaceAll(/\/index\.mdx?/g, ""); + return slug; +} + +const problems = defineCollection({ + type: "content_layer", + loader: glob({ + pattern: "**/*.mdx", + base: "./src/problems", + generateId, + }), + schema: z.object({ + title: z.string(), + }), +}); + +export const collections = { problems }; diff --git a/apps/ppp/src/i18n.ts b/apps/ppp/src/i18n.ts index 507144a..391e5d5 100644 --- a/apps/ppp/src/i18n.ts +++ b/apps/ppp/src/i18n.ts @@ -1,4 +1,6 @@ import { Page, TITLE } from "./shared"; +import { ProblemCategory } from "./shared/problems"; +import { EditorPanelTab } from "./shared/editor-panel-tab"; export enum Lang { EN = "en", @@ -16,17 +18,47 @@ export function getNextLang(lang: Lang): Lang { export enum Label { MainPage = "page:main", - ProblemPage = "page:problem", + ProblemsPage = "page:problems", + EditorPage = "page:editor", + ProblemsCategoryDesignPatterns = "problems:category:design-patterns", + EditorPanelTabTests = "editor:panel:tab:tests", + EditorPanelTabOutput = "editor:panel:tab:output", + EditorPanelTabSettings = "editor:panel:tab:settings", + EditorSettingsVimMode = "editor:settings:vim-mode", + EditorRunButton = "editor:button:run", + EditorStopButton = "editor:button:stop", + EditorSettingsExecutionTimeout = "editor:settings:execution-timeout", + EditorSettingsExecutionTimeoutAlt = "editor:settings:execution-timeout-alt", } const strings: Record> = { [Lang.EN]: { [Label.MainPage]: TITLE, - [Label.ProblemPage]: "Problem", + [Label.ProblemsPage]: "Problems", + [Label.EditorPage]: "Editor", + [Label.ProblemsCategoryDesignPatterns]: "Design Patterns", + [Label.EditorPanelTabTests]: "Tests", + [Label.EditorPanelTabOutput]: "Output", + [Label.EditorPanelTabSettings]: "Settings", + [Label.EditorSettingsVimMode]: "Vim mode", + [Label.EditorRunButton]: "Run", + [Label.EditorStopButton]: "Stop", + [Label.EditorSettingsExecutionTimeout]: "Execution timeout (ms)", + [Label.EditorSettingsExecutionTimeoutAlt]: "Use zero to disable", }, [Lang.RU]: { [Label.MainPage]: TITLE, - [Label.ProblemPage]: "Задача", + [Label.ProblemsPage]: "Проблемы", + [Label.EditorPage]: "Редактор", + [Label.ProblemsCategoryDesignPatterns]: "Паттерны проектирования", + [Label.EditorPanelTabTests]: "Тесты", + [Label.EditorPanelTabOutput]: "Вывод", + [Label.EditorPanelTabSettings]: "Настройки", + [Label.EditorSettingsVimMode]: "Режим Vim", + [Label.EditorRunButton]: "Запустить", + [Label.EditorStopButton]: "Остановить", + [Label.EditorSettingsExecutionTimeout]: "Таймаут выполнения (мс)", + [Label.EditorSettingsExecutionTimeoutAlt]: "Используйте ноль для отключения", }, }; @@ -36,9 +68,28 @@ export function useTranslations(lang: Lang) { const PAGE_TO_LABEL: Record = { [Page.Main]: Label.MainPage, - [Page.Problem]: Label.ProblemPage, + [Page.Problems]: Label.ProblemsPage, + [Page.Editor]: Label.EditorPage, }; export function getPageLabel(page: Page): Label { return PAGE_TO_LABEL[page]; } + +const PROBLEM_CATEGORY_TO_LABEL: Record = { + [ProblemCategory.DesignPatterns]: Label.ProblemsCategoryDesignPatterns, +}; + +export function getProblemCategoryLabel(category: ProblemCategory): Label { + return PROBLEM_CATEGORY_TO_LABEL[category]; +} + +const EDITOR_PANEL_TAB_TO_LABEL: Record = { + [EditorPanelTab.Tests]: Label.EditorPanelTabTests, + [EditorPanelTab.Output]: Label.EditorPanelTabOutput, + [EditorPanelTab.Settings]: Label.EditorPanelTabSettings, +}; + +export function getEditorPanelTabLabel(tab: EditorPanelTab): Label { + return EDITOR_PANEL_TAB_TO_LABEL[tab]; +} diff --git a/apps/ppp/src/layouts/landing.astro b/apps/ppp/src/layouts/landing.astro index 2dd0a3b..3c5b05d 100644 --- a/apps/ppp/src/layouts/landing.astro +++ b/apps/ppp/src/layouts/landing.astro @@ -1,5 +1,5 @@ --- -import RootLayout from "@/components/root-layout.astro"; +import Root from "./root.astro"; interface Props { title: string; @@ -8,13 +8,14 @@ interface Props { const { title } = Astro.props; --- - + +
-
+ diff --git a/apps/ppp/src/layouts/problem.astro b/apps/ppp/src/layouts/problem.astro deleted file mode 100644 index 0bb3ce3..0000000 --- a/apps/ppp/src/layouts/problem.astro +++ /dev/null @@ -1,59 +0,0 @@ ---- -import { Icon } from "astro-icon/components"; - -import { RESET_BUTTON_ID } from '@/shared'; -import RootLayout from "@/components/root-layout.astro"; -import EditorProvider from "@/containers/editor-provider.astro"; - -export interface Props { - title: string; - category: string; -} - -const { title, category } = Astro.props; ---- - - -
-
-
-
-
-
-
- 3P -
-
- -
- - - - -
-
-
- -
-
-
-
-
- - - -
-
-
diff --git a/apps/ppp/src/components/root-layout.astro b/apps/ppp/src/layouts/root.astro similarity index 100% rename from apps/ppp/src/components/root-layout.astro rename to apps/ppp/src/layouts/root.astro diff --git a/apps/ppp/src/lib/reactive-window.svelte.ts b/apps/ppp/src/lib/reactive-window.svelte.ts new file mode 100644 index 0000000..a445830 --- /dev/null +++ b/apps/ppp/src/lib/reactive-window.svelte.ts @@ -0,0 +1,19 @@ +class ReactiveWindow implements Disposable { + innerWidth = $state(window.innerWidth); + innerHeight = $state(window.innerHeight); + + constructor() { + window.addEventListener("resize", this.onResize); + } + + [Symbol.dispose]() { + window.removeEventListener("resize", this.onResize); + } + + private onResize = () => { + this.innerWidth = window.innerWidth; + this.innerHeight = window.innerHeight; + }; +} + +export const reactiveWindow = new ReactiveWindow() diff --git a/apps/ppp/src/lib/sync-storage.svelte.ts b/apps/ppp/src/lib/sync-storage.svelte.ts new file mode 100644 index 0000000..e8ec8ae --- /dev/null +++ b/apps/ppp/src/lib/sync-storage.svelte.ts @@ -0,0 +1,29 @@ +export interface SyncStorage { + load(): T + save(data: T): void + clear(): void +} + +export function immediateSave(storage: SyncStorage, value: () => T) { + $effect(() => { + storage.save(value()); + }); +} + +export function debouncedSave( + storage: SyncStorage, + value: () => T, + debounce: number +) { + let callbackId: NodeJS.Timeout; + $effect(() => { + clearTimeout(callbackId); + const newValue = value(); + callbackId = setTimeout(() => { + storage.save(newValue); + }, debounce); + return () => { + clearTimeout(callbackId); + }; + }); +} diff --git a/apps/ppp/src/lib/vec2.ts b/apps/ppp/src/lib/vec2.ts new file mode 100644 index 0000000..48ea69a --- /dev/null +++ b/apps/ppp/src/lib/vec2.ts @@ -0,0 +1,4 @@ +export interface Vec2 { + x: number; + y: number; +} diff --git a/apps/ppp/src/pages/editor/_editor.svelte b/apps/ppp/src/pages/editor/_editor.svelte new file mode 100644 index 0000000..fbeed31 --- /dev/null +++ b/apps/ppp/src/pages/editor/_editor.svelte @@ -0,0 +1,226 @@ + + +
+ + +
+ + + + + +
+ + + {#snippet preLabel(lang)} + + {/snippet} + {#snippet label(lang)} + {LANGUAGE_TITLE[lang]} + {/snippet} + {#snippet postLabel(lang)} + { + describedLanguage = lang + e.stopPropagation() + descriptionDialogElement.showModal() + }} class="invisible group-hover:visible" icon="lucide:info" /> + {/snippet} + + +
+
+ + +
+ + +
+
+
+
+
+ + + + e.stopPropagation()}> + + + diff --git a/apps/ppp/src/pages/editor/_program.cs b/apps/ppp/src/pages/editor/_program.cs new file mode 100644 index 0000000..a547cbc --- /dev/null +++ b/apps/ppp/src/pages/editor/_program.cs @@ -0,0 +1,9 @@ +using System; + +public class Program +{ + public static void Main() + { + Console.WriteLine("Hello, World!"); + } +} diff --git a/apps/ppp/src/pages/editor/_program.gleam b/apps/ppp/src/pages/editor/_program.gleam new file mode 100644 index 0000000..3d3f3e0 --- /dev/null +++ b/apps/ppp/src/pages/editor/_program.gleam @@ -0,0 +1,5 @@ +import gleam/io + +pub fn main() { + io.println("Hello, World!") +} diff --git a/apps/ppp/src/pages/editor/_program.go b/apps/ppp/src/pages/editor/_program.go new file mode 100644 index 0000000..16e2b22 --- /dev/null +++ b/apps/ppp/src/pages/editor/_program.go @@ -0,0 +1,5 @@ +package main + +func main() { + println("Hello, World!") +} diff --git a/apps/ppp/src/pages/editor/_program.java b/apps/ppp/src/pages/editor/_program.java new file mode 100644 index 0000000..a42f5de --- /dev/null +++ b/apps/ppp/src/pages/editor/_program.java @@ -0,0 +1,5 @@ +class Program { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } +} diff --git a/apps/ppp/src/pages/editor/_program.js b/apps/ppp/src/pages/editor/_program.js new file mode 100644 index 0000000..184dfcc --- /dev/null +++ b/apps/ppp/src/pages/editor/_program.js @@ -0,0 +1 @@ +console.log("Hello, World!"); diff --git a/apps/ppp/src/pages/editor/_program.php b/apps/ppp/src/pages/editor/_program.php new file mode 100644 index 0000000..38d1be6 --- /dev/null +++ b/apps/ppp/src/pages/editor/_program.php @@ -0,0 +1,3 @@ + = { + [Language.PHP]: { + initialValue: phpProgram, + compilerFactory: makeRemoteCompilerFactory(PhpWorker), + Description: DESCRIPTIONS[Language.PHP], + }, + [Language.TypeScript]: { + initialValue: tsProgram, + compilerFactory: makeRemoteCompilerFactory(TsWorker), + Description: DESCRIPTIONS[Language.TypeScript], + }, + [Language.Python]: { + initialValue: pythonProgram, + compilerFactory: makeRemoteCompilerFactory(PythonWorker), + Description: DESCRIPTIONS[Language.Python], + }, + [Language.JavaScript]: { + initialValue: jsProgram, + compilerFactory: makeRemoteCompilerFactory(JsWorker), + Description: DESCRIPTIONS[Language.JavaScript], + }, + [Language.Go]: { + initialValue: goProgram, + compilerFactory: makeRemoteCompilerFactory(GoWorker), + Description: DESCRIPTIONS[Language.Go], + }, + [Language.Rust]: { + initialValue: rustProgram, + compilerFactory: makeRemoteCompilerFactory(RustWorker), + Description: DESCRIPTIONS[Language.Rust], + }, + [Language.Gleam]: { + initialValue: gleamProgram, + compilerFactory: makeRemoteCompilerFactory(GleamWorker), + Description: DESCRIPTIONS[Language.Gleam], + }, + [Language.CSharp]: { + initialValue: csProgram, + compilerFactory: makeDotnetCompiler, + Description: DESCRIPTIONS[Language.CSharp], + }, + [Language.Java]: { + initialValue: javaProgram, + compilerFactory: makeRemoteCompilerFactory(JavaWorker), + Description: DESCRIPTIONS[Language.Java], + }, +}; diff --git a/apps/ppp/src/pages/editor/index.astro b/apps/ppp/src/pages/editor/index.astro new file mode 100644 index 0000000..c55f77d --- /dev/null +++ b/apps/ppp/src/pages/editor/index.astro @@ -0,0 +1,15 @@ +--- +import type { Lang } from '@/i18n' +import Root from '@/layouts/root.astro' +import EditorProvider from '@/containers/editor-provider.astro' + +import SvelteEditor from './_editor.svelte' + +const lang = Astro.currentLocale as Lang +--- + + + + + + diff --git a/apps/ppp/src/pages/index.astro b/apps/ppp/src/pages/index.astro index cd7b082..1f443f8 100644 --- a/apps/ppp/src/pages/index.astro +++ b/apps/ppp/src/pages/index.astro @@ -1,8 +1,14 @@ --- -import { TITLE } from "@/shared"; +import { Page } from "@/shared"; import Layout from "@/layouts/landing.astro"; +import Navigation from '@/containers/navigation.astro'; +import { getPageLabel, Lang, useTranslations } from '@/i18n'; + +const lang = Astro.currentLocale as Lang; +const t = useTranslations(lang); --- - + + Landing page diff --git a/apps/ppp/src/pages/problems/[...slug].astro b/apps/ppp/src/pages/problems/[...slug].astro new file mode 100644 index 0000000..ccc8803 --- /dev/null +++ b/apps/ppp/src/pages/problems/[...slug].astro @@ -0,0 +1,54 @@ +--- +import { getCollection, type CollectionEntry } from 'astro:content'; + +import { PROBLEM_CATEGORIES, type ProblemCategory } from '@/shared/problems'; + +import Problem from './_problem.astro'; +import Category from './_category.astro'; + +interface Path { + params: { + slug: string + } +} + +interface CategoryPath extends Path { + props: { + type: "category" + category: ProblemCategory + entries: CollectionEntry<'problems'>[] + } +} + +interface ProblemPath extends Path { + props: { + type: "problem" + entry: CollectionEntry<'problems'> + } +} + +export async function getStaticPaths() { + const problems = await getCollection('problems'); + const categoriesPaths: CategoryPath[] = PROBLEM_CATEGORIES.map(category => ({ + params: { + slug: category + }, + props: { + type: "category", + category, + entries: problems.filter(entry => entry.id.includes(category)), + } + })) + const problemPaths: ProblemPath[] = problems.map(entry => ({ + params: { slug: entry.id }, props: { type: "problem", entry }, + })); + return [...categoriesPaths, ...problemPaths]; +} + +const props = Astro.props +--- +{ + props.type === "category" + ? + : +} diff --git a/apps/ppp/src/pages/problems/_category.astro b/apps/ppp/src/pages/problems/_category.astro new file mode 100644 index 0000000..427a97b --- /dev/null +++ b/apps/ppp/src/pages/problems/_category.astro @@ -0,0 +1,26 @@ +--- +import { type CollectionEntry } from 'astro:content' + +import { Page } from '@/shared'; +import type { ProblemCategory } from '@/shared/problems'; +import Layout from '@/layouts/landing.astro' +import Navigation from '@/containers/navigation.astro' +import { type Lang, getProblemCategoryLabel, useTranslations } from '@/i18n'; + +import ProblemsList from './_problems-list.astro'; + +export interface Props { + category: ProblemCategory + problems: CollectionEntry<'problems'>[] +} + +const { category, problems } = Astro.props + +const lang = Astro.currentLocale as Lang; +const t = useTranslations(lang); + +--- + + + + diff --git a/apps/ppp/src/pages/problems/_problem.astro b/apps/ppp/src/pages/problems/_problem.astro new file mode 100644 index 0000000..f452157 --- /dev/null +++ b/apps/ppp/src/pages/problems/_problem.astro @@ -0,0 +1,19 @@ +--- +import { render, type CollectionEntry } from 'astro:content' + +import Root from '@/layouts/root.astro' +import EditorProvider from '@/containers/editor-provider.astro' + +export interface Props { + entry: CollectionEntry<'problems'> +} + +const { entry } = Astro.props +const { Content } = await render(entry) +--- + + + + + + diff --git a/apps/ppp/src/pages/problems/_problems-list.astro b/apps/ppp/src/pages/problems/_problems-list.astro new file mode 100644 index 0000000..1d48fc5 --- /dev/null +++ b/apps/ppp/src/pages/problems/_problems-list.astro @@ -0,0 +1,24 @@ +--- +import type { CollectionEntry } from 'astro:content' +import { getRelativeLocaleUrl } from 'astro:i18n' + +import { Page } from '@/shared' +import type { Lang } from '@/i18n' + +export interface Props { + problems: CollectionEntry<'problems'>[] +} + +const { problems } = Astro.props + +const lang = Astro.currentLocale as Lang +--- + diff --git a/apps/ppp/src/pages/problems/index.astro b/apps/ppp/src/pages/problems/index.astro new file mode 100644 index 0000000..dcf1db9 --- /dev/null +++ b/apps/ppp/src/pages/problems/index.astro @@ -0,0 +1,20 @@ +--- +import { getCollection } from 'astro:content' + +import { Page } from '@/shared'; +import Layout from '@/layouts/landing.astro' +import Navigation from '@/containers/navigation.astro' +import { type Lang, getPageLabel, useTranslations } from '@/i18n'; + +import ProblemsList from './_problems-list.astro'; + +const lang = Astro.currentLocale as Lang; +const t = useTranslations(lang); + +const problems = await getCollection("problems") + +--- + + + + diff --git a/apps/ppp/src/content/design-patterns/factory/csharp/code.cs b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/csharp/code.cs similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/csharp/code.cs rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/csharp/code.cs diff --git a/apps/ppp/src/content/design-patterns/factory/csharp/definitions.cs b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/csharp/definitions.cs similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/csharp/definitions.cs rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/csharp/definitions.cs diff --git a/apps/ppp/src/content/design-patterns/factory/csharp/execution-code.cs b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/csharp/execution-code.cs similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/csharp/execution-code.cs rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/csharp/execution-code.cs diff --git a/apps/ppp/src/content/design-patterns/factory/csharp/factory.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/csharp/factory.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/csharp/factory.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/csharp/factory.ts diff --git a/apps/ppp/src/content/design-patterns/factory/csharp/index.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/csharp/index.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/csharp/index.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/csharp/index.ts diff --git a/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/editor-wrapper.astro b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/editor-wrapper.astro new file mode 100644 index 0000000..4a489ba --- /dev/null +++ b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/editor-wrapper.astro @@ -0,0 +1,11 @@ +--- +import type { Lang } from '@/i18n' + +import Editor from './editor.svelte' + +const lang = Astro.currentLocale as Lang +--- + + + + diff --git a/apps/ppp/src/content/design-patterns/factory/editor.astro b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/editor.svelte similarity index 53% rename from apps/ppp/src/content/design-patterns/factory/editor.astro rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/editor.svelte index 46a745e..ab54e9b 100644 --- a/apps/ppp/src/content/design-patterns/factory/editor.astro +++ b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/editor.svelte @@ -1,16 +1,10 @@ ---- -export interface Props { - contentId: string; -} + + + + testCompilerFactory: javaFactory, + }, + }} +/> diff --git a/apps/ppp/src/content/design-patterns/factory/gleam/code.gleam b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/gleam/code.gleam similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/gleam/code.gleam rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/gleam/code.gleam diff --git a/apps/ppp/src/content/design-patterns/factory/gleam/factory.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/gleam/factory.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/gleam/factory.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/gleam/factory.ts diff --git a/apps/ppp/src/content/design-patterns/factory/gleam/index.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/gleam/index.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/gleam/index.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/gleam/index.ts diff --git a/apps/ppp/src/content/design-patterns/factory/go/code.go b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/go/code.go similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/go/code.go rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/go/code.go diff --git a/apps/ppp/src/content/design-patterns/factory/go/factory.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/go/factory.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/go/factory.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/go/factory.ts diff --git a/apps/ppp/src/content/design-patterns/factory/go/index.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/go/index.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/go/index.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/go/index.ts diff --git a/apps/ppp/src/pages/example.mdx b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/index.mdx similarity index 78% rename from apps/ppp/src/pages/example.mdx rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/index.mdx index cc7722e..5eff23d 100644 --- a/apps/ppp/src/pages/example.mdx +++ b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/index.mdx @@ -1,8 +1,11 @@ -import Layout from "@/layouts/problem.astro"; +--- +title: Платежные системы +--- import Hint from "@/components/hint.astro"; -import Editor from "@/content/design-patterns/factory/editor.astro"; - +import Editor from './editor-wrapper.astro' + + # Платежные системы Необходимо выполнить интеграцию с тремя платежными системами, реализовав операцию `payment`. @@ -18,5 +21,4 @@ import Editor from "@/content/design-patterns/factory/editor.astro"; > Для тестовых данных результат операций будет целочисленным положительным числом. Используй фабрику. - - + diff --git a/apps/ppp/src/content/design-patterns/factory/java/code.java b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/java/code.java similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/java/code.java rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/java/code.java diff --git a/apps/ppp/src/content/design-patterns/factory/java/factory.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/java/factory.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/java/factory.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/java/factory.ts diff --git a/apps/ppp/src/content/design-patterns/factory/java/index.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/java/index.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/java/index.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/java/index.ts diff --git a/apps/ppp/src/content/design-patterns/factory/js/code.js b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/js/code.js similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/js/code.js rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/js/code.js diff --git a/apps/ppp/src/content/design-patterns/factory/js/factory.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/js/factory.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/js/factory.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/js/factory.ts diff --git a/apps/ppp/src/content/design-patterns/factory/js/index.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/js/index.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/js/index.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/js/index.ts diff --git a/apps/ppp/src/content/design-patterns/factory/php/code.php b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/php/code.php similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/php/code.php rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/php/code.php diff --git a/apps/ppp/src/content/design-patterns/factory/php/factory.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/php/factory.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/php/factory.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/php/factory.ts diff --git a/apps/ppp/src/content/design-patterns/factory/php/index.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/php/index.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/php/index.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/php/index.ts diff --git a/apps/ppp/src/content/design-patterns/factory/python/code.py b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/python/code.py similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/python/code.py rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/python/code.py diff --git a/apps/ppp/src/content/design-patterns/factory/python/factory.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/python/factory.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/python/factory.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/python/factory.ts diff --git a/apps/ppp/src/content/design-patterns/factory/python/index.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/python/index.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/python/index.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/python/index.ts diff --git a/apps/ppp/src/content/design-patterns/factory/reference.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/reference.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/reference.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/reference.ts diff --git a/apps/ppp/src/content/design-patterns/factory/rust/code.rs b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/rust/code.rs similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/rust/code.rs rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/rust/code.rs diff --git a/apps/ppp/src/content/design-patterns/factory/rust/factory.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/rust/factory.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/rust/factory.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/rust/factory.ts diff --git a/apps/ppp/src/content/design-patterns/factory/rust/index.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/rust/index.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/rust/index.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/rust/index.ts diff --git a/apps/ppp/src/content/design-patterns/factory/tests-data.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/tests-data.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/tests-data.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/tests-data.ts diff --git a/apps/ppp/src/content/design-patterns/factory/ts/code.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/ts/code.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/ts/code.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/ts/code.ts diff --git a/apps/ppp/src/content/design-patterns/factory/ts/factory.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/ts/factory.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/ts/factory.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/ts/factory.ts diff --git a/apps/ppp/src/content/design-patterns/factory/ts/index.ts b/apps/ppp/src/problems/design-patterns/(factory)/payment-systems/ts/index.ts similarity index 100% rename from apps/ppp/src/content/design-patterns/factory/ts/index.ts rename to apps/ppp/src/problems/design-patterns/(factory)/payment-systems/ts/index.ts diff --git a/apps/ppp/src/shared/editor-panel-tab.ts b/apps/ppp/src/shared/editor-panel-tab.ts new file mode 100644 index 0000000..39ea34b --- /dev/null +++ b/apps/ppp/src/shared/editor-panel-tab.ts @@ -0,0 +1,5 @@ +export enum EditorPanelTab { + Tests = "tests", + Output = "output", + Settings = "settings", +} diff --git a/apps/ppp/src/shared/languages.ts b/apps/ppp/src/shared/languages.ts index dedd066..d8adcff 100644 --- a/apps/ppp/src/shared/languages.ts +++ b/apps/ppp/src/shared/languages.ts @@ -1,8 +1,8 @@ export enum Language { - PHP = "php", - Python = "python", - TypeScript = "typescript", JavaScript = "javascript", + TypeScript = "typescript", + Python = "python", + PHP = "php", Go = "go", Rust = "rust", Gleam = "gleam", @@ -10,6 +10,8 @@ export enum Language { Java = "java", } +export const LANGUAGES = Object.values(Language); + export const LANGUAGE_TITLE: Record = { [Language.PHP]: "PHP", [Language.TypeScript]: "TypeScript", @@ -21,3 +23,15 @@ export const LANGUAGE_TITLE: Record = { [Language.CSharp]: "CSharp", [Language.Java]: "Java", }; + +export const LANGUAGE_ICONS: Record = { + [Language.JavaScript]: "vscode-icons:file-type-js", + [Language.Python]: "vscode-icons:file-type-python", + [Language.TypeScript]: "vscode-icons:file-type-typescript", + [Language.Go]: "vscode-icons:file-type-go", + [Language.PHP]: "vscode-icons:file-type-php", + [Language.Rust]: "vscode-icons:file-type-rust", + [Language.Gleam]: "vscode-icons:file-type-gleam", + [Language.CSharp]: "vscode-icons:file-type-csharp", + [Language.Java]: "vscode-icons:file-type-java", +}; diff --git a/apps/ppp/src/shared/problems.ts b/apps/ppp/src/shared/problems.ts new file mode 100644 index 0000000..d1714f2 --- /dev/null +++ b/apps/ppp/src/shared/problems.ts @@ -0,0 +1,11 @@ +import { Page } from './shared'; + +export enum ProblemCategory { + DesignPatterns = "design-patterns", +} + +export const PROBLEM_CATEGORIES = Object.values(ProblemCategory) + +export function problemCategoryPage(category: ProblemCategory): string { + return `${Page.Problems}/${category}` +} diff --git a/apps/ppp/src/shared/shared.ts b/apps/ppp/src/shared/shared.ts index 83c72fc..b726a67 100644 --- a/apps/ppp/src/shared/shared.ts +++ b/apps/ppp/src/shared/shared.ts @@ -1,19 +1,7 @@ export enum Page { Main = "/", - Problem = "/task", + Problems = "/problems", + Editor = "/editor", } export const TITLE = "Programming Patterns Practice"; - -export interface Position { - x: number; - y: number; -} - -export interface SyncStorage { - load(): T - save(data: T): void - clear(): void -} - -export const RESET_BUTTON_ID = "editor-reset-button"; diff --git a/flake.nix b/flake.nix index 1d1d82c..5d84470 100644 --- a/flake.nix +++ b/flake.nix @@ -39,7 +39,7 @@ pkgs.nodejs pkgs.bun pkgs.pnpm - pkgs.go + pkgs.go_1_23 pkgs.python3 pkgs.gcc pkgs.curl diff --git a/mkfile b/mkfile index 37068fc..30d3f91 100644 --- a/mkfile +++ b/mkfile @@ -22,6 +22,12 @@ libs/: pnpm run build popd +compiler/: + pushd packages/compiler + b: + pnpm run build + popd + testing/: pushd packages/testing b: diff --git a/packages/compiler/package.json b/packages/compiler/package.json new file mode 100644 index 0000000..8b743e9 --- /dev/null +++ b/packages/compiler/package.json @@ -0,0 +1,17 @@ +{ + "name": "compiler", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "build": "tsc", + "dev": "tsc --watch" + }, + "dependencies": { + "libs": "workspace:*" + }, + "exports": { + ".": "./dist/index.js", + "./actor": "./dist/actor.js" + } +} diff --git a/packages/compiler/src/actor.ts b/packages/compiler/src/actor.ts new file mode 100644 index 0000000..0f1996e --- /dev/null +++ b/packages/compiler/src/actor.ts @@ -0,0 +1,177 @@ +import { + Actor, + MessageType, + startRemote, + WorkerConnection, + type Connection, + type EventMessage, + type IncomingMessage, + type OutgoingMessage, +} from "libs/actor"; + +import type { Compiler, CompilerFactory, File, Program } from "./compiler.js"; +import { createContext, inContext, type Context } from "libs/context"; +import type { Writer } from "libs/io"; +import { ok } from "libs/result"; +import { stringifyError } from "libs/error"; +import { createLogger } from "libs/logger"; + +interface Handlers { + [key: string]: any; + init(): Promise; + compile(files: File[]): Promise; + run(): Promise; + cancel(): void; + dispose(): void; +} + +type Incoming = IncomingMessage; + +interface WriteEventMessage extends EventMessage<"write", Uint8Array> {} + +type CompilerActorEvent = WriteEventMessage; + +type Outgoing = OutgoingMessage | CompilerActorEvent; + +class CompilerActor extends Actor { + private ctx: Context = createContext(); + private compiler: Compiler | null = null; + private program: Program | null = null; + + constructor( + connection: Connection, + compilerFactory: CompilerFactory + ) { + const handlers: Handlers = { + init: async () => { + const out: Writer = { + write(buffer) { + // TODO: synchronously wait for response + connection.send({ + type: MessageType.Event, + event: "write", + payload: buffer, + }); + return ok(buffer.length); + }, + }; + this.compiler = await compilerFactory(this.ctx, out); + }, + compile: async (files) => { + if (this.compiler === null) { + throw new Error("Compiler not initialized"); + } + this.program = await this.compiler.compile(this.ctx, files); + }, + run: async () => { + if (this.program === null) { + const err = new Error("Program not compiled"); + connection.send({ + type: MessageType.Event, + event: "error", + payload: err.message, + }); + throw err; + } + await this.program.run(this.ctx); + }, + cancel: () => { + this.ctx.cancel(); + this.ctx = createContext(); + }, + dispose: () => { + if (this.program !== null) { + this.program[Symbol.dispose](); + } + if (this.compiler !== null) { + this.compiler[Symbol.dispose](); + } + }, + }; + super(connection, handlers, stringifyError); + } +} + +export function startCompilerActor( + compilerFactory: CompilerFactory +) { + const connection = new WorkerConnection( + self as unknown as Worker + ); + const actor = new CompilerActor(connection, compilerFactory); + const stopConnection = connection.start(); + const stopActor = actor.start(); + return () => { + stopActor(); + stopConnection(); + }; +} + +interface WorkerConstructor { + new (): Worker; +} + +export function makeRemoteCompilerFactory(Worker: WorkerConstructor) { + return async (ctx: Context, out: Writer): Promise => { + const worker = new Worker(); + const connection = new WorkerConnection(worker); + const stopConnection = connection.start(); + const log = createLogger(out); + const remote = startRemote( + log, + connection, + { + error: (err) => { + log.error(err); + }, + write: (text) => { + out.write(text); + }, + } + ); + const dispose = () => { + remote[Symbol.dispose](); + stopConnection(); + worker.terminate(); + }; + const cancelSubscription = ctx.onCancel(() => { + remote.cancel(); + }); + try { + await inContext(ctx, remote.init()); + } catch (err) { + dispose(); + throw err; + } finally { + cancelSubscription[Symbol.dispose](); + } + return { + async compile(ctx, files) { + const cancelSubscription = ctx.onCancel(() => { + remote.cancel(); + }); + try { + await inContext(ctx, remote.compile(files)); + return { + async run(ctx) { + const cancelSubscription = ctx.onCancel(() => { + remote.cancel(); + }); + try { + await inContext(ctx, remote.run()); + } finally { + cancelSubscription[Symbol.dispose](); + } + }, + [Symbol.dispose]: () => { + void remote.dispose(); + }, + }; + } finally { + cancelSubscription[Symbol.dispose](); + } + }, + [Symbol.dispose]: dispose, + }; + }; +} diff --git a/packages/compiler/src/compiler.ts b/packages/compiler/src/compiler.ts new file mode 100644 index 0000000..3fac575 --- /dev/null +++ b/packages/compiler/src/compiler.ts @@ -0,0 +1,17 @@ +import type { Context } from 'libs/context'; +import type { Writer } from 'libs/io'; + +export interface Program extends Disposable { + run: (ctx: Context) => Promise; +} + +export interface File { + filename: string; + content: string; +} + +export interface Compiler extends Disposable { + compile: (ctx: Context, files: File[]) => Promise; +} + +export type CompilerFactory = (ctx: Context, out: Writer) => Promise; diff --git a/packages/compiler/src/index.ts b/packages/compiler/src/index.ts new file mode 100644 index 0000000..c493aab --- /dev/null +++ b/packages/compiler/src/index.ts @@ -0,0 +1 @@ +export * from './compiler.js' diff --git a/packages/compiler/tsconfig.json b/packages/compiler/tsconfig.json new file mode 100644 index 0000000..5acadfe --- /dev/null +++ b/packages/compiler/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@total-typescript/tsconfig/tsc/dom/library-monorepo", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "lib": ["ES2022", "ESNext.Disposable", "DOM"], + "noUncheckedIndexedAccess": false, + }, +} \ No newline at end of file diff --git a/packages/dotnet-runtime/package.json b/packages/dotnet-runtime/package.json index 190b7ff..6c24581 100644 --- a/packages/dotnet-runtime/package.json +++ b/packages/dotnet-runtime/package.json @@ -9,6 +9,7 @@ }, "dependencies": { "libs": "workspace:*", + "compiler": "workspace:*", "testing": "workspace:*" }, "devDependencies": { diff --git a/packages/dotnet-runtime/src/dotnet-program.ts b/packages/dotnet-runtime/src/dotnet-program.ts new file mode 100644 index 0000000..a65f545 --- /dev/null +++ b/packages/dotnet-runtime/src/dotnet-program.ts @@ -0,0 +1,19 @@ +import type { Program } from "compiler"; +import type { Context } from "libs/context"; + +import type { DotnetRuntime } from "./dotnet-runtime-factory"; + +export class DotnetProgram implements Program { + constructor(protected readonly runtime: DotnetRuntime) {} + + async run(_: Context): Promise { + const status = this.runtime.Run("Program", "Main", []); + if (status !== 0) { + throw new Error("Run failed"); + } + } + + [Symbol.dispose](): void { + this.runtime.DisposeAssembly(); + } +} diff --git a/packages/dotnet-runtime/src/dotnet-runtime-factory.ts b/packages/dotnet-runtime/src/dotnet-runtime-factory.ts index 2c689a3..4312237 100644 --- a/packages/dotnet-runtime/src/dotnet-runtime-factory.ts +++ b/packages/dotnet-runtime/src/dotnet-runtime-factory.ts @@ -5,8 +5,8 @@ export type DotnetRuntime = Omit; export class DotnetRuntimeFactory { constructor(protected readonly compiler: DotnetCompiler) {} - create(code: string, executionCode: string): DotnetCompiler { - const status = this.compiler.Compile([code, executionCode]); + create(...code: string[]): DotnetRuntime { + const status = this.compiler.Compile(code); if (status !== 0) { throw new Error("Compilation failed"); } diff --git a/packages/dotnet-runtime/src/index.ts b/packages/dotnet-runtime/src/index.ts index 9c67e7b..fd31f53 100644 --- a/packages/dotnet-runtime/src/index.ts +++ b/packages/dotnet-runtime/src/index.ts @@ -3,3 +3,4 @@ export * from "./dotnet-runtime-factory.js"; export * from "./dotnet-test-program.js"; export * from "./version.js"; export * from "./execution-code-factory.js"; +export * from "./dotnet-program.js"; diff --git a/packages/gleam-runtime/package.json b/packages/gleam-runtime/package.json index b6e47b4..3301838 100644 --- a/packages/gleam-runtime/package.json +++ b/packages/gleam-runtime/package.json @@ -8,7 +8,8 @@ "build": "vite build" }, "dependencies": { - "libs": "workspace:*" + "libs": "workspace:*", + "compiler": "workspace:*" }, "devDependencies": { "vite": "^5.4.1", diff --git a/packages/gleam-runtime/src/gleam-program.ts b/packages/gleam-runtime/src/gleam-program.ts new file mode 100644 index 0000000..9b19e79 --- /dev/null +++ b/packages/gleam-runtime/src/gleam-program.ts @@ -0,0 +1,26 @@ +import type { Context } from "libs/context"; +import { patch } from 'libs/patcher'; +import type { Program } from "compiler"; + +export interface GleamModule { + main(): void; +} + +export class GleamProgram implements Program { + constructor(protected readonly module: GleamModule, + protected readonly patchedConsole: Console + ) {} + + async run(_: Context): Promise { + const consolePatch = patch(globalThis, "console", this.patchedConsole); + try { + // By default gleam main function is synchronous + // TODO: Find out if gleam can produce asynchronous code + await this.module.main(); + } finally { + consolePatch[Symbol.dispose](); + } + } + + [Symbol.dispose](): void {} +} diff --git a/packages/gleam-runtime/src/index.ts b/packages/gleam-runtime/src/index.ts index 81ed172..549af53 100644 --- a/packages/gleam-runtime/src/index.ts +++ b/packages/gleam-runtime/src/index.ts @@ -1,2 +1,3 @@ +export * from "./gleam-program.js"; export * from "./gleam-module-compiler.js"; export * from "./version.js"; diff --git a/packages/go-runtime/go/go.mod b/packages/go-runtime/go/go.mod index 587a604..56e035f 100644 --- a/packages/go-runtime/go/go.mod +++ b/packages/go-runtime/go/go.mod @@ -1,6 +1,6 @@ module github.com/x0k/ppp -go 1.22.3 +go 1.23.0 require github.com/traefik/yaegi v0.16.1 diff --git a/packages/go-runtime/go/internal/apps/compiler/module.go b/packages/go-runtime/go/internal/apps/compiler/module.go index 6fddbba..da4a0b3 100644 --- a/packages/go-runtime/go/internal/apps/compiler/module.go +++ b/packages/go-runtime/go/internal/apps/compiler/module.go @@ -34,7 +34,7 @@ func New(jsConfig js.Value) js_adapters.Result { root := js_adapters.ObjectConstructor.New() - root.Set("compile", js_adapters.Async(func(args []js.Value) js_adapters.Promise { + root.Set("createEvaluator", js_adapters.Async(func(args []js.Value) js_adapters.Promise { if len(args) < 2 { return js_adapters.ResolveErr(errors.New("Not enough arguments, expected 2")) } @@ -48,7 +48,24 @@ func New(jsConfig js.Value) js_adapters.Result { if err != nil { return js_adapters.ResolveErr(err) } - return js_adapters.Resolve(js_compiler.New(compiler).Compile(ctx, args[1].String())) + return js_adapters.Resolve(js_compiler.New(compiler).CreateEvaluator(ctx, args[1].String())) + })) + + root.Set("createExecutor", js_adapters.Async(func(args []js.Value) js_adapters.Promise { + if len(args) < 2 { + return js_adapters.ResolveErr(errors.New("Not enough arguments, expected 2")) + } + ctx, cancel := js_adapters.WithAbortSignal(context.Background(), args[0]) + defer cancel() + + compiler, err := compiler.New( + js_adapters.NewWriter(*cfg.Stdout.Write), + js_adapters.NewWriter(*cfg.Stderr.Write), + ) + if err != nil { + return js_adapters.ResolveErr(err) + } + return js_adapters.Resolve(js_compiler.New(compiler).CreateExecuter(ctx, args[1].String())) })) return js_adapters.Ok(root) diff --git a/packages/go-runtime/go/internal/compiler/compiler.go b/packages/go-runtime/go/internal/compiler/compiler.go index c1427cd..c5c4383 100644 --- a/packages/go-runtime/go/internal/compiler/compiler.go +++ b/packages/go-runtime/go/internal/compiler/compiler.go @@ -2,6 +2,7 @@ package compiler import ( "context" + "errors" "io" "reflect" @@ -9,6 +10,8 @@ import ( "github.com/traefik/yaegi/stdlib" ) +var ErrProgramNotCompiled = errors.New("program not compiled") + type Compiler struct { inter *interp.Interpreter } @@ -32,7 +35,18 @@ func New( }, nil } -func (c *Compiler) Compile(ctx context.Context, code string) (*Program, error) { +func (c *Compiler) Compile(_ context.Context, code string) (*Program, error) { + prog, err := c.inter.Compile(code) + if err != nil { + return nil, err + } + return &Program{ + inter: c.inter, + prog: prog, + }, nil +} + +func (c *Compiler) Prepare(ctx context.Context, code string) (*Program, error) { _, err := c.inter.EvalWithContext(ctx, code) if err != nil { return nil, err @@ -44,8 +58,17 @@ func (c *Compiler) Compile(ctx context.Context, code string) (*Program, error) { type Program struct { inter *interp.Interpreter + prog *interp.Program +} + +func (p *Program) Exec(ctx context.Context) error { + if p.prog == nil { + return ErrProgramNotCompiled + } + _, err := p.inter.ExecuteWithContext(ctx, p.prog) + return err } -func (p *Program) Exec(ctx context.Context, code string) (reflect.Value, error) { +func (p *Program) Eval(ctx context.Context, code string) (reflect.Value, error) { return p.inter.EvalWithContext(ctx, code) } diff --git a/packages/go-runtime/go/internal/compiler/js/program.go b/packages/go-runtime/go/internal/compiler/js/program.go index ab27d2f..b33f0c4 100644 --- a/packages/go-runtime/go/internal/compiler/js/program.go +++ b/packages/go-runtime/go/internal/compiler/js/program.go @@ -24,8 +24,8 @@ func New( } } -func (c *JsCompiler) Compile(ctx context.Context, code string) js_adapters.Result { - p, err := c.compiler.Compile(ctx, code) +func (c *JsCompiler) CreateEvaluator(ctx context.Context, code string) js_adapters.Result { + p, err := c.compiler.Prepare(ctx, code) if err != nil { return js_adapters.Err(err) } @@ -35,7 +35,7 @@ func (c *JsCompiler) Compile(ctx context.Context, code string) js_adapters.Resul } ctx, cancel := js_adapters.WithAbortSignal(context.Background(), args[0]) defer cancel() - v, err := p.Exec(ctx, args[1].String()) + v, err := p.Eval(ctx, args[1].String()) if err != nil { return js_adapters.ResolveErr(err) } @@ -43,3 +43,22 @@ func (c *JsCompiler) Compile(ctx context.Context, code string) js_adapters.Resul }) return js_adapters.Ok(exec.Value) } + +func (c *JsCompiler) CreateExecuter(ctx context.Context, code string) js_adapters.Result { + p, err := c.compiler.Compile(ctx, code) + if err != nil { + return js_adapters.Err(err) + } + exec := js_adapters.Async(func(args []js.Value) js_adapters.Promise { + if len(args) < 1 { + return js_adapters.ResolveErr(errors.New("Not enough arguments, expected 1")) + } + ctx, cancel := js_adapters.WithAbortSignal(context.Background(), args[0]) + defer cancel() + if err := p.Exec(ctx); err != nil { + return js_adapters.ResolveErr(err) + } + return js_adapters.ResolveOk(js.ValueOf(0)) + }) + return js_adapters.Ok(exec.Value) +} diff --git a/packages/go-runtime/package.json b/packages/go-runtime/package.json index 848f588..51719d8 100644 --- a/packages/go-runtime/package.json +++ b/packages/go-runtime/package.json @@ -9,6 +9,7 @@ }, "dependencies": { "libs": "workspace:*", + "compiler": "workspace:*", "testing": "workspace:*" }, "devDependencies": { diff --git a/packages/go-runtime/public/compiler.wasm b/packages/go-runtime/public/compiler.wasm index 8950f4d..3e95cb6 100755 Binary files a/packages/go-runtime/public/compiler.wasm and b/packages/go-runtime/public/compiler.wasm differ diff --git a/packages/go-runtime/src/compiler-factory.ts b/packages/go-runtime/src/compiler-factory.ts new file mode 100644 index 0000000..4604c58 --- /dev/null +++ b/packages/go-runtime/src/compiler-factory.ts @@ -0,0 +1,20 @@ +import "./vendor/wasm_exec.js"; + +import { + DEFAULT_GLOBAL_COMPILER_INIT_FUNCTION_NAME, + type CompilerFactory, +} from "./model.js"; + +export async function makeCompilerFactory( + instantiate: ( + importObject: WebAssembly.Imports + ) => Promise, + globalCompilerInitFunctionName = DEFAULT_GLOBAL_COMPILER_INIT_FUNCTION_NAME +): Promise { + const go = new Go(); + go.argv.push(globalCompilerInitFunctionName); + void go.run(await instantiate(go.importObject)); + // @ts-expect-error dynamic global property + const factory = globalThis[globalCompilerInitFunctionName] as CompilerFactory; + return factory; +} diff --git a/packages/go-runtime/src/go-compiler-factory.ts b/packages/go-runtime/src/go-compiler-factory.ts index 0dd0a31..de84cc9 100644 --- a/packages/go-runtime/src/go-compiler-factory.ts +++ b/packages/go-runtime/src/go-compiler-factory.ts @@ -1,17 +1,43 @@ -import "./vendor/wasm_exec.js"; +import { COLOR_ENCODED, createLogger, redirect } from "libs/logger"; +import { isErr } from "libs/result"; -import { DEFAULT_GLOBAL_COMPILER_INIT_FUNCTION_NAME, type CompilerFactory } from "./model.js"; +import { + LogLevel, + type CompilerFactory, + type GoCompilerFactory, +} from "./model"; -export async function createCompilerFactory( - instantiate: ( - importObject: WebAssembly.Imports - ) => Promise, - globalCompilerInitFunctionName = DEFAULT_GLOBAL_COMPILER_INIT_FUNCTION_NAME -): Promise { - const go = new Go(); - go.argv.push(globalCompilerInitFunctionName); - void go.run(await instantiate(go.importObject)); - // @ts-ignore - const factory = globalThis[globalCompilerInitFunctionName] as CompilerFactory; - return factory; +export function makeGoCompilerFactory( + makeCompiler: CompilerFactory +): GoCompilerFactory { + return (out) => { + const compiler = makeCompiler({ + logger: { + level: LogLevel.Info, + console: redirect(globalThis.console, createLogger(out)), + }, + stdout: out, + stderr: { + write(text) { + let r = out.write(COLOR_ENCODED.ERROR); + if (isErr(r)) { + return r; + } + const r2 = out.write(text); + if (isErr(r2)) { + return r2; + } + r = out.write(COLOR_ENCODED.RESET); + if (isErr(r)) { + return r; + } + return r2; + }, + }, + }); + if (isErr(compiler)) { + throw new Error(compiler.error); + } + return compiler.value; + }; } diff --git a/packages/go-runtime/src/go-evaluator-factory.ts b/packages/go-runtime/src/go-evaluator-factory.ts new file mode 100644 index 0000000..84c2319 --- /dev/null +++ b/packages/go-runtime/src/go-evaluator-factory.ts @@ -0,0 +1,16 @@ +import { isErr } from "libs/result"; + +import { type Evaluator, type GoCompilerFactory, type GoProgramFactory } from "./model"; + +export function makeGoEvaluatorFactory( + makeCompiler: GoCompilerFactory +): GoProgramFactory> { + return async (ctx, out, code) => { + const compiler = makeCompiler(out); + const executor = await compiler.createEvaluator(ctx.signal, code); + if (isErr(executor)) { + throw new Error(executor.error); + } + return executor.value; + }; +} diff --git a/packages/go-runtime/src/go-executor-factory.ts b/packages/go-runtime/src/go-executor-factory.ts new file mode 100644 index 0000000..ed6952f --- /dev/null +++ b/packages/go-runtime/src/go-executor-factory.ts @@ -0,0 +1,20 @@ +import { isErr } from "libs/result"; + +import { + type Executor, + type GoCompilerFactory, + type GoProgramFactory, +} from "./model"; + +export function makeGoExecutorFactory( + makeCompiler: GoCompilerFactory +): GoProgramFactory { + return async (ctx, out, code) => { + const compiler = makeCompiler(out); + const executor = await compiler.createExecutor(ctx.signal, code); + if (isErr(executor)) { + throw new Error(executor.error); + } + return executor.value; + }; +} diff --git a/packages/go-runtime/src/go-program.ts b/packages/go-runtime/src/go-program.ts new file mode 100644 index 0000000..b5a13c4 --- /dev/null +++ b/packages/go-runtime/src/go-program.ts @@ -0,0 +1,18 @@ +import type { Program } from "compiler"; +import type { Context } from "libs/context"; +import { isErr } from "libs/result"; + +import type { Executor } from "./model"; + +export class GoProgram implements Program { + constructor(protected readonly program: Executor) {} + + async run(ctx: Context): Promise { + const result = await this.program(ctx.signal); + if (isErr(result)) { + throw new Error(result.error); + } + } + + [Symbol.dispose](): void {} +} diff --git a/packages/go-runtime/src/go-runtime-factory.ts b/packages/go-runtime/src/go-runtime-factory.ts deleted file mode 100644 index 4aca6a0..0000000 --- a/packages/go-runtime/src/go-runtime-factory.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { createLogger, redirect, COLOR_ENCODED } from "libs/logger"; -import { isErr } from "libs/result"; - -import { LogLevel, type CompilerFactory, type GoRuntimeFactory } from "./model"; - -export function makeGoRuntimeFactory( - makeCompiler: CompilerFactory -): GoRuntimeFactory { - return async (ctx, out, code) => { - const compiler = makeCompiler({ - logger: { - level: LogLevel.Info, - console: redirect(globalThis.console, createLogger(out)), - }, - stdout: out, - stderr: { - write(text) { - let r = out.write(COLOR_ENCODED.ERROR) - if (isErr(r)) { - return r - } - const r2 = out.write(text); - if (isErr(r2)) { - return r2 - } - r = out.write(COLOR_ENCODED.RESET) - if (isErr(r)) { - return r - } - return r2 - }, - }, - }); - if (isErr(compiler)) { - throw new Error(compiler.error); - } - const executor = await compiler.value.compile(ctx.signal, code); - if (isErr(executor)) { - throw new Error(executor.error); - } - return executor.value; - }; -} diff --git a/packages/go-runtime/src/go-test-program.ts b/packages/go-runtime/src/go-test-program.ts index c12e46a..8dd27ef 100644 --- a/packages/go-runtime/src/go-test-program.ts +++ b/packages/go-runtime/src/go-test-program.ts @@ -2,14 +2,12 @@ import { type Context } from "libs/context"; import { isErr } from "libs/result"; import type { TestProgram } from "testing"; -import type { Executor } from "./model"; +import type { Evaluator } from "./model"; export abstract class GoTestProgram implements TestProgram { - constructor(protected readonly program: Executor) {} + constructor(protected readonly program: Evaluator) {} - protected generateCaseExecutionCode(input: I): string { - throw new Error("Not implemented"); - } + protected abstract generateCaseExecutionCode(input: I): string async run(ctx: Context, input: I): Promise { const result = await this.program( @@ -22,7 +20,5 @@ export abstract class GoTestProgram implements TestProgram { return result.value; } - [Symbol.dispose](): void { - throw new Error("Method not implemented."); - } + [Symbol.dispose](): void {} } diff --git a/packages/go-runtime/src/index.ts b/packages/go-runtime/src/index.ts index 085d3be..26965bc 100644 --- a/packages/go-runtime/src/index.ts +++ b/packages/go-runtime/src/index.ts @@ -1,5 +1,8 @@ -export * from './model.js' -export * from './go-compiler-factory.js' -export * from './go-runtime-factory.js' -export * from './go-test-program.js' -export * from './version.js' +export * from "./model.js"; +export * from "./compiler-factory.js"; +export * from "./go-compiler-factory.js"; +export * from "./go-evaluator-factory.js"; +export * from "./go-executor-factory.js"; +export * from "./go-test-program.js"; +export * from "./go-program.js"; +export * from "./version.js"; diff --git a/packages/go-runtime/src/model.ts b/packages/go-runtime/src/model.ts index 34e48a9..e07848e 100644 --- a/packages/go-runtime/src/model.ts +++ b/packages/go-runtime/src/model.ts @@ -1,6 +1,6 @@ import type { Context } from "libs/context"; import type { Result } from "libs/result"; -import type { Writer } from 'libs/io' +import type { Writer } from "libs/io"; export enum LogLevel { Disabled = -8, @@ -21,27 +21,35 @@ export interface CompilerConfig { stderr: Writer; } -export type Executor = ( +export type Evaluator = ( signal: AbortSignal, code: string -) => Promise>; +) => Promise>; + +export type Executor = (signal: AbortSignal) => Promise>; export interface Compiler { - compile( + createEvaluator( + signal: AbortSignal, + code: string + ): Promise, string>>; + createExecutor( signal: AbortSignal, code: string - ): Promise, string>>; + ): Promise>; } export type CompilerFactory = ( config: CompilerConfig ) => Result; -export type GoRuntimeFactory = ( +export type GoCompilerFactory = (out: Writer) => Compiler; + +export type GoProgramFactory = ( ctx: Context, out: Writer, code: string -) => Promise>; +) => Promise; export const DEFAULT_GLOBAL_COMPILER_INIT_FUNCTION_NAME = "__compiler_init_function"; diff --git a/packages/go-runtime/src/version.ts b/packages/go-runtime/src/version.ts index b492293..1d6c89d 100644 --- a/packages/go-runtime/src/version.ts +++ b/packages/go-runtime/src/version.ts @@ -1 +1 @@ -export const version = "1.22"; +export const version = "1.23"; diff --git a/packages/java-runtime/package.json b/packages/java-runtime/package.json index 734e9bb..dc58805 100644 --- a/packages/java-runtime/package.json +++ b/packages/java-runtime/package.json @@ -11,6 +11,7 @@ "browserfs": "^1.4.3", "doppiojvm": "^0.5.0", "libs": "workspace:*", + "compiler": "workspace:*", "testing": "workspace:*" }, "devDependencies": { diff --git a/packages/java-runtime/src/index.ts b/packages/java-runtime/src/index.ts index b80c7ce..44edf1c 100644 --- a/packages/java-runtime/src/index.ts +++ b/packages/java-runtime/src/index.ts @@ -1,8 +1,9 @@ export * from "./jvm"; export * from "./fs"; +export * from "./java-program.js"; export * from "./java-test-program.js"; export * from "./java-compiler.js"; -export * from './jvm-factory.js'; +export * from "./jvm-factory.js"; export * from "./version.js"; diff --git a/packages/java-runtime/src/java-program.ts b/packages/java-runtime/src/java-program.ts new file mode 100644 index 0000000..4721c33 --- /dev/null +++ b/packages/java-runtime/src/java-program.ts @@ -0,0 +1,29 @@ +import { Program } from "compiler"; +import { Context } from "libs/context"; + +import { JVMFactory } from "./jvm-factory"; + +export class JavaProgram implements Program { + constructor( + protected readonly className: string, + protected readonly jvmFactory: JVMFactory + ) {} + + async run(ctx: Context): Promise { + const [jvm, jvmDispose] = await this.jvmFactory(ctx); + const dispose = ctx.onCancel(() => jvm.halt(1)); + try { + const code = await new Promise((resolve) => + jvm.runClass(this.className, [], resolve) + ); + if (code !== 0) { + throw new Error("Run failed"); + } + } finally { + dispose[Symbol.dispose](); + jvmDispose[Symbol.dispose](); + } + } + + [Symbol.dispose](): void {} +} diff --git a/packages/javascript-runtime/package.json b/packages/javascript-runtime/package.json index b23523f..8bf409a 100644 --- a/packages/javascript-runtime/package.json +++ b/packages/javascript-runtime/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "libs": "workspace:*", + "compiler": "workspace:*", "testing": "workspace:*" } } \ No newline at end of file diff --git a/packages/javascript-runtime/src/index.ts b/packages/javascript-runtime/src/index.ts index 99861c5..571ee8d 100644 --- a/packages/javascript-runtime/src/index.ts +++ b/packages/javascript-runtime/src/index.ts @@ -1 +1,2 @@ +export * from './js-program.js'; export * from "./js-test-program.js"; diff --git a/packages/javascript-runtime/src/js-program.ts b/packages/javascript-runtime/src/js-program.ts new file mode 100644 index 0000000..cd4b506 --- /dev/null +++ b/packages/javascript-runtime/src/js-program.ts @@ -0,0 +1,21 @@ +import type { Program } from "compiler"; +import { type Context } from "libs/context"; +import { patch } from "libs/patcher"; + +export class JsProgram implements Program { + constructor( + protected readonly code: string, + protected readonly patchedConsole: Console + ) {} + + async run(_: Context): Promise { + const consolePatch = patch(globalThis, "console", this.patchedConsole); + try { + eval(this.code); + } finally { + consolePatch[Symbol.dispose](); + } + } + + [Symbol.dispose](): void {} +} diff --git a/packages/libs/package.json b/packages/libs/package.json index 2f9b0d6..29d9e0d 100644 --- a/packages/libs/package.json +++ b/packages/libs/package.json @@ -20,7 +20,6 @@ "./result": "./dist/result.js", "./type": "./dist/type.js", "./io": "./dist/io.js", - "./js": "./dist/js.js", - "./compiler": "./dist/compiler.js" + "./js": "./dist/js.js" } } \ No newline at end of file diff --git a/packages/libs/src/compiler.ts b/packages/libs/src/compiler.ts deleted file mode 100644 index 7b126a9..0000000 --- a/packages/libs/src/compiler.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { Context } from './context.js'; - -export interface Program { - run: (ctx: Context) => Promise; -} - -export interface File { - filename: string; - content: string; -} - -export interface Compiler { - compile: (ctx: Context, files: File[]) => Promise; -} diff --git a/packages/php-runtime/package.json b/packages/php-runtime/package.json index b4e64b5..e96c105 100644 --- a/packages/php-runtime/package.json +++ b/packages/php-runtime/package.json @@ -11,6 +11,7 @@ "@php-wasm/universal": "^0.9.30", "@php-wasm/web": "^0.9.30", "libs": "workspace:*", + "compiler": "workspace:*", "testing": "workspace:*" }, "devDependencies": { diff --git a/packages/php-runtime/src/index.ts b/packages/php-runtime/src/index.ts index 74680db..1b35f4f 100644 --- a/packages/php-runtime/src/index.ts +++ b/packages/php-runtime/src/index.ts @@ -1,3 +1,4 @@ +export * from "./php-program.js"; export * from "./php-test-program"; export * from "./php-compiler-factory"; export * from "./version"; diff --git a/packages/php-runtime/src/php-program.ts b/packages/php-runtime/src/php-program.ts new file mode 100644 index 0000000..da04b2d --- /dev/null +++ b/packages/php-runtime/src/php-program.ts @@ -0,0 +1,30 @@ +import type { PHP } from "@php-wasm/universal"; +import type { Program } from "compiler"; +import type { Context } from "libs/context"; +import type { Writer } from "libs/io"; + +export class PHPProgram implements Program { + constructor( + protected readonly code: string, + protected readonly php: PHP, + protected readonly writer: Writer + ) {} + + async run(ctx: Context): Promise { + const exitSubscription = ctx.onCancel(() => this.php.exit(137)); + try { + const response = await this.php.run({ code: this.code }); + const text = response.bytes; + if (text.byteLength > 0) { + this.writer.write(new Uint8Array(text)); + } + if (response.errors) { + throw new Error(response.errors); + } + } finally { + exitSubscription[Symbol.dispose](); + } + } + + [Symbol.dispose](): void {} +} diff --git a/packages/php-runtime/src/php-test-program.ts b/packages/php-runtime/src/php-test-program.ts index 48d3ddc..7ba37ce 100644 --- a/packages/php-runtime/src/php-test-program.ts +++ b/packages/php-runtime/src/php-test-program.ts @@ -8,7 +8,7 @@ export abstract class PHPTestProgram implements TestProgram { private result?: O; constructor( - protected writer: Writer, + protected readonly writer: Writer, protected readonly php: PHP, protected readonly code: string ) { diff --git a/packages/python-runtime/package.json b/packages/python-runtime/package.json index fa4d7b4..44206a9 100644 --- a/packages/python-runtime/package.json +++ b/packages/python-runtime/package.json @@ -10,6 +10,7 @@ "dependencies": { "libs": "workspace:*", "pyodide": "^0.26.2", + "compiler": "workspace:*", "testing": "workspace:*" }, "devDependencies": { diff --git a/packages/python-runtime/src/index.ts b/packages/python-runtime/src/index.ts index a1d9fed..a069342 100644 --- a/packages/python-runtime/src/index.ts +++ b/packages/python-runtime/src/index.ts @@ -1,3 +1,4 @@ -export * from './py-test-program' -export * from './py-runtime-factory' -export * from './version' +export * from "./py-program.js"; +export * from "./py-test-program"; +export * from "./py-runtime-factory"; +export * from "./version"; diff --git a/packages/python-runtime/src/py-program.ts b/packages/python-runtime/src/py-program.ts new file mode 100644 index 0000000..82af6ee --- /dev/null +++ b/packages/python-runtime/src/py-program.ts @@ -0,0 +1,16 @@ +import type { loadPyodide } from "pyodide"; +import type { Program } from "compiler"; +import { inContext, type Context } from "libs/context"; + +export class PyProgram implements Program { + constructor( + protected readonly code: string, + protected readonly python: Awaited> + ) {} + + async run(ctx: Context): Promise { + await inContext(ctx, this.python.runPythonAsync(this.code)); + } + + [Symbol.dispose](): void {} +} diff --git a/packages/rust-runtime/package.json b/packages/rust-runtime/package.json index e647921..d1106ab 100644 --- a/packages/rust-runtime/package.json +++ b/packages/rust-runtime/package.json @@ -10,6 +10,7 @@ "dependencies": { "@bjorn3/browser_wasi_shim": "^0.3.0", "libs": "workspace:*", + "compiler": "workspace:*", "testing": "workspace:*" }, "devDependencies": { diff --git a/packages/rust-runtime/src/index.ts b/packages/rust-runtime/src/index.ts index 1e43ab1..e94c018 100644 --- a/packages/rust-runtime/src/index.ts +++ b/packages/rust-runtime/src/index.ts @@ -1,2 +1,3 @@ -export * from './rust-runtime-factory.js' -export * from './rust-test-program.js' +export * from "./rust-program.js"; +export * from "./rust-test-program.js"; +export * from "./rust-runtime-factory.js"; diff --git a/packages/rust-runtime/src/rust-program.ts b/packages/rust-runtime/src/rust-program.ts new file mode 100644 index 0000000..053335e --- /dev/null +++ b/packages/rust-runtime/src/rust-program.ts @@ -0,0 +1,60 @@ +import type { OpenDirectory, WASI } from "@bjorn3/browser_wasi_shim"; +import type { Program } from "compiler"; +import { inContext, type Context } from "libs/context"; +import { isErr } from "libs/result"; + +import { assertOpenDir, lookupFile } from "./wasi"; + +// TODO: extract common code with RustTestProgram +export class RustProgram implements Program { + protected threads_count = 1; + + constructor( + protected readonly code: string, + protected readonly wasi: WASI, + protected readonly miriModule: WebAssembly.Module + ) {} + + async run(ctx: Context): Promise { + this.threads_count = 1; + this.writeCaseExecutionCode(this.code); + const instance = await inContext( + ctx, + WebAssembly.instantiate(this.miriModule, { + env: { + memory: new WebAssembly.Memory({ initial: 256, shared: false }), + }, + wasi: { + "thread-spawn": (start_arg: unknown) => { + let id = this.threads_count++; + // @ts-ignore + instance.exports.wasi_thread_start(id, start_arg); + return id; + }, + }, + wasi_snapshot_preview1: this.wasi.wasiImport, + }) + ); + // @ts-expect-error lack of type information + const exitCode = this.wasi.start(instance); + if (exitCode !== 0) { + throw new Error(`Code execution failed with exit code ${exitCode}`); + } + } + + [Symbol.dispose](): void {} + + protected get rootDir(): OpenDirectory { + const dir = this.wasi.fds[5]; + assertOpenDir(dir); + return dir; + } + + protected writeCaseExecutionCode(code: string) { + const file = lookupFile(this.rootDir, "main.rs"); + if (isErr(file)) { + throw new Error(`Failed to read main file: ${file.error}`); + } + file.value.data = new TextEncoder().encode(code); + } +} diff --git a/packages/testing/package.json b/packages/testing/package.json index 8f2bfb9..d6a6a20 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -9,7 +9,8 @@ }, "dependencies": { "fast-deep-equal": "^3.1.3", - "libs": "workspace:*" + "libs": "workspace:*", + "compiler": "workspace:*" }, "exports": { ".": "./dist/index.js", diff --git a/packages/testing/src/actor.ts b/packages/testing/src/actor.ts index 779beb0..a891e26 100644 --- a/packages/testing/src/actor.ts +++ b/packages/testing/src/actor.ts @@ -12,11 +12,11 @@ import { } from "libs/actor"; import { stringifyError } from "libs/error"; import { compileJsModule } from "libs/js"; -import type { File } from "libs/compiler"; +import type { File } from "compiler"; import type { Writer } from "libs/io"; import { ok } from "libs/result"; -import type { TestProgram, TestCompiler } from "./model.js"; +import type { TestProgram, TestCompiler } from "./testing.js"; export interface InitConfig { universalFactoryFunction: string; @@ -60,7 +60,7 @@ export type TestCompilerFactory = ( class TestCompilerActor extends Actor, string> { private ctx: Context = createContext(); - private testProgramCompiler: TestCompiler | null = null; + private testCompiler: TestCompiler | null = null; private testProgram: TestProgram | null = null; constructor( @@ -83,17 +83,17 @@ class TestCompilerActor extends Actor, string> { return ok(buffer.length); }, }; - this.testProgramCompiler = await superFactory( + this.testCompiler = await superFactory( this.ctx, out, universalFactory ); }, compile: async (files) => { - if (this.testProgramCompiler === null) { + if (this.testCompiler === null) { throw new Error("Test runner not initialized"); } - this.testProgram = await this.testProgramCompiler.compile( + this.testProgram = await this.testCompiler.compile( this.ctx, files ); @@ -115,10 +115,12 @@ class TestCompilerActor extends Actor, string> { return this.testProgram.run(this.ctx, input); }, dispose: () => { - if (!this.testProgram) { - return; + if (this.testProgram !== null) { + this.testProgram[Symbol.dispose](); + } + if (this.testCompiler !== null) { + this.testCompiler[Symbol.dispose](); } - this.testProgram[Symbol.dispose](); }, }; super(connection, handlers, stringifyError); diff --git a/packages/testing/src/index.ts b/packages/testing/src/index.ts index 05e0c34..6d793ec 100644 --- a/packages/testing/src/index.ts +++ b/packages/testing/src/index.ts @@ -1 +1 @@ -export * from "./model.js"; +export * from "./testing.js"; diff --git a/packages/testing/src/model.ts b/packages/testing/src/testing.ts similarity index 97% rename from packages/testing/src/model.ts rename to packages/testing/src/testing.ts index e4de2cc..c7d0d92 100644 --- a/packages/testing/src/model.ts +++ b/packages/testing/src/testing.ts @@ -3,7 +3,7 @@ import deepEqual from "fast-deep-equal"; import type { Writer } from "libs/io"; import type { Logger } from "libs/logger"; import type { Context } from "libs/context"; -import type { File } from "libs/compiler"; +import type { File } from "compiler"; export interface TestCase { input: I; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c00f43b..8c25608 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,14 +35,14 @@ importers: specifier: ^0.9.3 version: 0.9.3(typescript@5.5.4) '@astrojs/mdx': - specifier: ^3.1.3 - version: 3.1.3(astro@4.14.2(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4)) + specifier: ^3.1.4 + version: 3.1.4(astro@4.14.5(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4)) '@astrojs/svelte': specifier: ^5.7.0 - version: 5.7.0(astro@4.14.2(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4))(svelte@5.0.0-next.225)(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.2)) + version: 5.7.0(astro@4.14.5(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4))(svelte@5.0.0-next.237)(typescript@5.5.4)(vite@5.4.1(@types/node@20.14.2)) '@astrojs/tailwind': specifier: ^5.1.0 - version: 5.1.0(astro@4.14.2(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4))(tailwindcss@3.4.10) + version: 5.1.0(astro@4.14.5(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4))(tailwindcss@3.4.10) '@xterm/addon-fit': specifier: ^0.10.0 version: 0.10.0(@xterm/xterm@5.5.0) @@ -50,11 +50,14 @@ importers: specifier: ^5.5.0 version: 5.5.0 astro: - specifier: ^4.14.2 - version: 4.14.2(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4) + specifier: ^4.14.5 + version: 4.14.5(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4) astro-icon: specifier: ^1.1.1 version: 1.1.1 + compiler: + specifier: workspace:* + version: link:../../packages/compiler dotnet-runtime: specifier: workspace:* version: link:../../packages/dotnet-runtime @@ -74,17 +77,17 @@ importers: specifier: workspace:* version: link:../../packages/libs monaco-editor: - specifier: ^0.50.0 - version: 0.50.0 + specifier: ^0.51.0 + version: 0.51.0 monaco-editor-textmate: specifier: ^4.0.0 - version: 4.0.0(monaco-editor@0.50.0)(monaco-textmate@3.0.1(onigasm@2.2.5)) + version: 4.0.0(monaco-editor@0.51.0)(monaco-textmate@3.0.1(onigasm@2.2.5)) monaco-textmate: specifier: ^3.0.1 version: 3.0.1(onigasm@2.2.5) monaco-vim: specifier: ^0.4.1 - version: 0.4.1(monaco-editor@0.50.0) + version: 0.4.1(monaco-editor@0.51.0) onigasm: specifier: ^2.2.5 version: 2.2.5 @@ -108,11 +111,11 @@ importers: version: link:../../packages/typescript-runtime devDependencies: '@iconify-json/lucide': - specifier: ^1.1.207 - version: 1.1.207 + specifier: ^1.1.208 + version: 1.1.208 '@iconify/svelte': specifier: ^4.0.2 - version: 4.0.2(svelte@5.0.0-next.225) + version: 4.0.2(svelte@5.0.0-next.237) '@tailwindcss/typography': specifier: ^0.5.14 version: 0.5.14(tailwindcss@3.4.10) @@ -126,17 +129,26 @@ importers: specifier: ^4.12.10 version: 4.12.10(postcss@8.4.41) svelte: - specifier: 5.0.0-next.225 - version: 5.0.0-next.225 + specifier: 5.0.0-next.237 + version: 5.0.0-next.237 vite-plugin-cross-origin-isolation: specifier: ^0.1.6 version: 0.1.6 vite-plugin-static-copy: specifier: ^1.0.6 - version: 1.0.6(vite@5.4.0(@types/node@20.14.2)) + version: 1.0.6(vite@5.4.1(@types/node@20.14.2)) + + packages/compiler: + dependencies: + libs: + specifier: workspace:* + version: link:../libs packages/dotnet-runtime: dependencies: + compiler: + specifier: workspace:* + version: link:../compiler libs: specifier: workspace:* version: link:../libs @@ -156,6 +168,9 @@ importers: packages/gleam-runtime: dependencies: + compiler: + specifier: workspace:* + version: link:../compiler libs: specifier: workspace:* version: link:../libs @@ -172,6 +187,9 @@ importers: packages/go-runtime: dependencies: + compiler: + specifier: workspace:* + version: link:../compiler libs: specifier: workspace:* version: link:../libs @@ -194,6 +212,9 @@ importers: browserfs: specifier: ^1.4.3 version: 1.4.3(patch_hash=hizh2cvfvkcaelm7cnorz5durm) + compiler: + specifier: workspace:* + version: link:../compiler doppiojvm: specifier: ^0.5.0 version: 0.5.0(patch_hash=wsp65hfnbd4f2x2nnq6ijej2xi) @@ -210,6 +231,9 @@ importers: packages/javascript-runtime: dependencies: + compiler: + specifier: workspace:* + version: link:../compiler libs: specifier: workspace:* version: link:../libs @@ -227,6 +251,9 @@ importers: '@php-wasm/web': specifier: ^0.9.30 version: 0.9.30 + compiler: + specifier: workspace:* + version: link:../compiler libs: specifier: workspace:* version: link:../libs @@ -243,6 +270,9 @@ importers: packages/python-runtime: dependencies: + compiler: + specifier: workspace:* + version: link:../compiler libs: specifier: workspace:* version: link:../libs @@ -268,6 +298,9 @@ importers: '@bjorn3/browser_wasi_shim': specifier: ^0.3.0 version: 0.3.0 + compiler: + specifier: workspace:* + version: link:../compiler libs: specifier: workspace:* version: link:../libs @@ -284,6 +317,9 @@ importers: packages/testing: dependencies: + compiler: + specifier: workspace:* + version: link:../compiler fast-deep-equal: specifier: ^3.1.3 version: 3.1.3 @@ -319,9 +355,6 @@ packages: peerDependencies: typescript: ^5.0.0 - '@astrojs/compiler@2.10.2': - resolution: {integrity: sha512-bvH+v8AirwpRWCkYJEyWYdc5Cs/BjG2ZTxIJzttHilXgfKJAdW2496KsUQKzf5j2tOHtaHXKKn9hb9WZiBGpEg==} - '@astrojs/compiler@2.10.3': resolution: {integrity: sha512-bL/O7YBxsFt55YHU021oL+xz+B/9HvGNId3F9xURN16aeqDK9juHGktdkCSXz+U4nqFACq6ZFvWomOzhV+zfPw==} @@ -343,8 +376,8 @@ packages: '@astrojs/markdown-remark@5.2.0': resolution: {integrity: sha512-vWGM24KZXz11jR3JO+oqYU3T2qpuOi4uGivJ9SQLCAI01+vEkHC60YJMRvHPc+hwd60F7euNs1PeOEixIIiNQw==} - '@astrojs/mdx@3.1.3': - resolution: {integrity: sha512-hOM4dMM4RfJI254d3p/AnOZuk2VyKszRtuY5FBm+Xc4XdhIpGrR56OXMNEcWchtwz4HQyPe/eJSgvBjSROcQIQ==} + '@astrojs/mdx@3.1.4': + resolution: {integrity: sha512-AcdcAlDpzTM5LHpur7A3NWoIqyfhH1gZNbTvvjiUlDEo7eJjIxl4gdWrb/kZZRfLBEuM8cptCB+Qk11ncQL4IA==} engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0} peerDependencies: astro: ^4.8.0 @@ -774,8 +807,8 @@ packages: cpu: [x64] os: [win32] - '@iconify-json/lucide@1.1.207': - resolution: {integrity: sha512-RwzmyFchxiTceKtvVMqfFyUNyxpBKtl6QzD5XPzofr8bqpVgBB/SO0gjI9IbsdQ3bTfSXrEMH+lPm1F0gf2vqQ==} + '@iconify-json/lucide@1.1.208': + resolution: {integrity: sha512-wdlljQKeoqmIw3YcYMCObPvuB3Gc0vbb0tocULrb0isl7wqINlHmcCAbL2mtLbHmwbldtW25AsplB6H56njPoA==} '@iconify/svelte@4.0.2': resolution: {integrity: sha512-6BSrU85FzGfhQD3bTXpnkCCvBAglEt8T9QednVnXAYm4C+d3464y+pYMzhQNJm5mPId2cuiw+2wXlDflXllHDw==} @@ -1117,12 +1150,12 @@ packages: '@rushstack/ts-command-line@4.22.3': resolution: {integrity: sha512-edMpWB3QhFFZ4KtSzS8WNjBgR4PXPPOVrOHMbb7kNpmQ1UFS9HdVtjCXg1H5fG+xYAbeE+TMPcVPUyX2p84STA==} - '@shikijs/core@1.12.0': - resolution: {integrity: sha512-mc1cLbm6UQ8RxLc0dZES7v5rkH+99LxQp/ZvTqV3NLyYsO/fD6JhEflP1H5b2SDq9gI0+0G36AVZWxvounfR9w==} - '@shikijs/core@1.12.1': resolution: {integrity: sha512-biCz/mnkMktImI6hMfMX3H9kOeqsInxWEyCHbSlL8C/2TR1FqfmGxTLRNwYCKsyCyxWLbB8rEqXRVZuyxuLFmA==} + '@shikijs/core@1.14.1': + resolution: {integrity: sha512-KyHIIpKNaT20FtFPFjCQB5WVSTpLR/n+jQXhWHWVUMm9MaOaG9BGOG0MSyt7yA4+Lm+4c9rTc03tt3nYzeYSfw==} + '@sveltejs/vite-plugin-svelte-inspector@2.1.0': resolution: {integrity: sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==} engines: {node: ^18.0.0 || >=20} @@ -1406,8 +1439,8 @@ packages: astro-icon@1.1.1: resolution: {integrity: sha512-HKBesWk2Faw/0+klLX+epQVqdTfSzZz/9+5vxXUjTJaN/HnpDf608gRPgHh7ZtwBPNJMEFoU5GLegxoDcT56OQ==} - astro@4.14.2: - resolution: {integrity: sha512-x9VeYx8Ih6kYKBMVwwsfRzsZVq30+SUhiawnYQ6+46qQnEx3zH05KcH24HsJMe6dVpHD8HdH7CWR5C4o7Q/jeg==} + astro@4.14.5: + resolution: {integrity: sha512-sv47kPE6FnvyxxHHcCePNwTKpOMKBq0r1m6WZYg6ag9j3yF9m72ov64NFB7c+hAMDUKgsHfVdLKjOOqDC/c+fA==} engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} hasBin: true @@ -2719,8 +2752,8 @@ packages: monaco-editor: 0.x.x monaco-textmate: ^3.0.0 - monaco-editor@0.50.0: - resolution: {integrity: sha512-8CclLCmrRRh+sul7C08BmPBP3P8wVWfBHomsTcndxg5NRCEPfu/mc2AGU8k37ajjDVXcXFc12ORAMUkmk+lkFA==} + monaco-editor@0.51.0: + resolution: {integrity: sha512-xaGwVV1fq343cM7aOYB6lVE4Ugf0UyimdD/x5PWcWBMKENwectaEu77FAN7c5sFiyumqeJdX1RPTh1ocioyDjw==} monaco-textmate@3.0.1: resolution: {integrity: sha512-ZxxY3OsqUczYP1sGqo97tu+CJmMBwuSW+dL0WEBdDhOZ5G1zntw72hvBc68ZQAirosWvbDKgN1dL5k173QtFww==} @@ -3223,12 +3256,12 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shiki@1.12.0: - resolution: {integrity: sha512-BuAxWOm5JhRcbSOl7XCei8wGjgJJonnV0oipUupPY58iULxUGyHhW5CF+9FRMuM1pcJ5cGEJGll1LusX6FwpPA==} - shiki@1.12.1: resolution: {integrity: sha512-nwmjbHKnOYYAe1aaQyEBHvQymJgfm86ZSS7fT8OaPRr4sbAcBNz7PbfAikMEFSDQ6se2j2zobkXvVKcBOm0ysg==} + shiki@1.14.1: + resolution: {integrity: sha512-FujAN40NEejeXdzPt+3sZ3F2dx1U24BY2XTY01+MG8mbxCiA2XukXdcbyMyLAHJ/1AUUnQd1tZlvIjefWWEJeA==} + side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} @@ -3367,8 +3400,8 @@ packages: svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0 typescript: ^4.9.4 || ^5.0.0 - svelte@5.0.0-next.225: - resolution: {integrity: sha512-4Y1EnXeEQWfRMZLOdmOAIId62HtGDJ804FMmRrW4BUSK88yHP+Zagla6KnxvwLKM+MMi70thyCq3RsJ3QA7jZw==} + svelte@5.0.0-next.237: + resolution: {integrity: sha512-EkNhMFq6cjfrQzBv+YWaxs8mgRyHlfjMTYMlVkx/lsWq4EUTHTRYzNf2nfWn7cXMYE1fXg8DsR3CZ6zv1qlQeQ==} engines: {node: '>=18'} svgo@3.3.2: @@ -3600,37 +3633,6 @@ packages: peerDependencies: vite: ^5.0.0 - vite@5.4.0: - resolution: {integrity: sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - vite@5.4.1: resolution: {integrity: sha512-1oE6yuNXssjrZdblI9AfBbHCC41nnyoVoEZxQnID6yvQZAFBzxxkqoFLtHUMkYunL8hwOLEjgTuxpkRxvba3kA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3954,8 +3956,6 @@ snapshots: - prettier - prettier-plugin-astro - '@astrojs/compiler@2.10.2': {} - '@astrojs/compiler@2.10.3': {} '@astrojs/internal-helpers@0.4.1': {} @@ -3998,7 +3998,7 @@ snapshots: remark-parse: 11.0.0 remark-rehype: 11.1.0 remark-smartypants: 3.0.2 - shiki: 1.12.0 + shiki: 1.12.1 unified: 11.0.5 unist-util-remove-position: 5.0.0 unist-util-visit: 5.0.0 @@ -4007,15 +4007,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/mdx@3.1.3(astro@4.14.2(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4))': + '@astrojs/mdx@3.1.4(astro@4.14.5(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4))': dependencies: '@astrojs/markdown-remark': 5.2.0 '@mdx-js/mdx': 3.0.1 acorn: 8.12.1 - astro: 4.14.2(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4) + astro: 4.14.5(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4) es-module-lexer: 1.5.4 estree-util-visit: 2.0.0 - github-slugger: 2.0.0 gray-matter: 4.0.3 hast-util-to-html: 9.0.1 kleur: 4.1.5 @@ -4032,20 +4031,20 @@ snapshots: dependencies: prismjs: 1.29.0 - '@astrojs/svelte@5.7.0(astro@4.14.2(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4))(svelte@5.0.0-next.225)(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.2))': + '@astrojs/svelte@5.7.0(astro@4.14.5(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4))(svelte@5.0.0-next.237)(typescript@5.5.4)(vite@5.4.1(@types/node@20.14.2))': dependencies: - '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@5.0.0-next.225)(vite@5.4.0(@types/node@20.14.2)) - astro: 4.14.2(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4) - svelte: 5.0.0-next.225 - svelte2tsx: 0.7.13(svelte@5.0.0-next.225)(typescript@5.5.4) + '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@5.0.0-next.237)(vite@5.4.1(@types/node@20.14.2)) + astro: 4.14.5(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4) + svelte: 5.0.0-next.237 + svelte2tsx: 0.7.13(svelte@5.0.0-next.237)(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - supports-color - vite - '@astrojs/tailwind@5.1.0(astro@4.14.2(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4))(tailwindcss@3.4.10)': + '@astrojs/tailwind@5.1.0(astro@4.14.5(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4))(tailwindcss@3.4.10)': dependencies: - astro: 4.14.2(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4) + astro: 4.14.5(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4) autoprefixer: 10.4.19(postcss@8.4.38) postcss: 8.4.38 postcss-load-config: 4.0.2(postcss@8.4.38) @@ -4374,14 +4373,14 @@ snapshots: '@esbuild/win32-x64@0.23.0': optional: true - '@iconify-json/lucide@1.1.207': + '@iconify-json/lucide@1.1.208': dependencies: '@iconify/types': 2.0.0 - '@iconify/svelte@4.0.2(svelte@5.0.0-next.225)': + '@iconify/svelte@4.0.2(svelte@5.0.0-next.237)': dependencies: '@iconify/types': 2.0.0 - svelte: 5.0.0-next.225 + svelte: 5.0.0-next.237 '@iconify/tools@4.0.5': dependencies: @@ -4772,34 +4771,34 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@shikijs/core@1.12.0': + '@shikijs/core@1.12.1': dependencies: '@types/hast': 3.0.4 - '@shikijs/core@1.12.1': + '@shikijs/core@1.14.1': dependencies: '@types/hast': 3.0.4 - '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.225)(vite@5.4.0(@types/node@20.14.2)))(svelte@5.0.0-next.225)(vite@5.4.0(@types/node@20.14.2))': + '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.237)(vite@5.4.1(@types/node@20.14.2)))(svelte@5.0.0-next.237)(vite@5.4.1(@types/node@20.14.2))': dependencies: - '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@5.0.0-next.225)(vite@5.4.0(@types/node@20.14.2)) + '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@5.0.0-next.237)(vite@5.4.1(@types/node@20.14.2)) debug: 4.3.5 - svelte: 5.0.0-next.225 - vite: 5.4.0(@types/node@20.14.2) + svelte: 5.0.0-next.237 + vite: 5.4.1(@types/node@20.14.2) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.225)(vite@5.4.0(@types/node@20.14.2))': + '@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.237)(vite@5.4.1(@types/node@20.14.2))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.225)(vite@5.4.0(@types/node@20.14.2)))(svelte@5.0.0-next.225)(vite@5.4.0(@types/node@20.14.2)) + '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.237)(vite@5.4.1(@types/node@20.14.2)))(svelte@5.0.0-next.237)(vite@5.4.1(@types/node@20.14.2)) debug: 4.3.5 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.10 - svelte: 5.0.0-next.225 - svelte-hmr: 0.16.0(svelte@5.0.0-next.225) - vite: 5.4.0(@types/node@20.14.2) - vitefu: 0.2.5(vite@5.4.0(@types/node@20.14.2)) + svelte: 5.0.0-next.237 + svelte-hmr: 0.16.0(svelte@5.0.0-next.237) + vite: 5.4.1(@types/node@20.14.2) + vitefu: 0.2.5(vite@5.4.1(@types/node@20.14.2)) transitivePeerDependencies: - supports-color @@ -5129,9 +5128,9 @@ snapshots: - debug - supports-color - astro@4.14.2(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4): + astro@4.14.5(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.5.4): dependencies: - '@astrojs/compiler': 2.10.2 + '@astrojs/compiler': 2.10.3 '@astrojs/internal-helpers': 0.4.1 '@astrojs/markdown-remark': 5.2.0 '@astrojs/telemetry': 3.1.0 @@ -5184,14 +5183,14 @@ snapshots: prompts: 2.4.2 rehype: 13.0.1 semver: 7.6.3 - shiki: 1.12.1 + shiki: 1.14.1 string-width: 7.2.0 strip-ansi: 7.1.0 tsconfck: 3.1.1(typescript@5.5.4) unist-util-visit: 5.0.0 vfile: 6.0.2 - vite: 5.4.0(@types/node@20.14.2) - vitefu: 0.2.5(vite@5.4.0(@types/node@20.14.2)) + vite: 5.4.1(@types/node@20.14.2) + vitefu: 0.2.5(vite@5.4.1(@types/node@20.14.2)) which-pm: 3.0.0 xxhash-wasm: 1.0.2 yargs-parser: 21.1.1 @@ -6821,7 +6820,7 @@ snapshots: micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.5 + debug: 4.3.6 decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.1 @@ -6901,21 +6900,21 @@ snapshots: pkg-types: 1.1.1 ufo: 1.5.3 - monaco-editor-textmate@4.0.0(monaco-editor@0.50.0)(monaco-textmate@3.0.1(onigasm@2.2.5)): + monaco-editor-textmate@4.0.0(monaco-editor@0.51.0)(monaco-textmate@3.0.1(onigasm@2.2.5)): dependencies: - monaco-editor: 0.50.0 + monaco-editor: 0.51.0 monaco-textmate: 3.0.1(onigasm@2.2.5) - monaco-editor@0.50.0: {} + monaco-editor@0.51.0: {} monaco-textmate@3.0.1(onigasm@2.2.5): dependencies: fast-plist: 0.1.3 onigasm: 2.2.5 - monaco-vim@0.4.1(monaco-editor@0.50.0): + monaco-vim@0.4.1(monaco-editor@0.51.0): dependencies: - monaco-editor: 0.50.0 + monaco-editor: 0.51.0 mrmime@2.0.0: {} @@ -7527,14 +7526,14 @@ snapshots: shebang-regex@3.0.0: {} - shiki@1.12.0: + shiki@1.12.1: dependencies: - '@shikijs/core': 1.12.0 + '@shikijs/core': 1.12.1 '@types/hast': 3.0.4 - shiki@1.12.1: + shiki@1.14.1: dependencies: - '@shikijs/core': 1.12.1 + '@shikijs/core': 1.14.1 '@types/hast': 3.0.4 side-channel@1.0.6: @@ -7651,18 +7650,18 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-hmr@0.16.0(svelte@5.0.0-next.225): + svelte-hmr@0.16.0(svelte@5.0.0-next.237): dependencies: - svelte: 5.0.0-next.225 + svelte: 5.0.0-next.237 - svelte2tsx@0.7.13(svelte@5.0.0-next.225)(typescript@5.5.4): + svelte2tsx@0.7.13(svelte@5.0.0-next.237)(typescript@5.5.4): dependencies: dedent-js: 1.0.1 pascal-case: 3.1.2 - svelte: 5.0.0-next.225 + svelte: 5.0.0-next.237 typescript: 5.5.4 - svelte@5.0.0-next.225: + svelte@5.0.0-next.237: dependencies: '@ampproject/remapping': 2.3.0 '@jridgewell/sourcemap-codec': 1.5.0 @@ -7942,14 +7941,6 @@ snapshots: - rollup - supports-color - vite-plugin-static-copy@1.0.6(vite@5.4.0(@types/node@20.14.2)): - dependencies: - chokidar: 3.6.0 - fast-glob: 3.3.2 - fs-extra: 11.2.0 - picocolors: 1.0.1 - vite: 5.4.0(@types/node@20.14.2) - vite-plugin-static-copy@1.0.6(vite@5.4.1(@types/node@20.14.2)): dependencies: chokidar: 3.6.0 @@ -7958,15 +7949,6 @@ snapshots: picocolors: 1.0.1 vite: 5.4.1(@types/node@20.14.2) - vite@5.4.0(@types/node@20.14.2): - dependencies: - esbuild: 0.21.5 - postcss: 8.4.41 - rollup: 4.18.0 - optionalDependencies: - '@types/node': 20.14.2 - fsevents: 2.3.3 - vite@5.4.1(@types/node@20.14.2): dependencies: esbuild: 0.21.5 @@ -7976,9 +7958,9 @@ snapshots: '@types/node': 20.14.2 fsevents: 2.3.3 - vitefu@0.2.5(vite@5.4.0(@types/node@20.14.2)): + vitefu@0.2.5(vite@5.4.1(@types/node@20.14.2)): optionalDependencies: - vite: 5.4.0(@types/node@20.14.2) + vite: 5.4.1(@types/node@20.14.2) volar-service-css@0.0.61(@volar/language-service@2.4.0): dependencies: