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 @@
+
+
+
+
+ {t(title)}
+
+
+
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 @@
+
+
+
+
+ {t(title)}
+ {#if alt}
+ {t(alt)}
+ {/if}
+
+
+
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()}>
-
-
-
{LANGUAGE_TITLE[selectedLang]}
-
-
-
-
-
-
\ 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 @@
-
-
-
-
- {#if isRunning}
-
- Stop
- {:else}
-
- Run
- {/if}
-
-
- {#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 @@
+
+
+
+ {#if isRunning}
+
+ {t(Label.EditorStopButton)}
+ {:else}
+
+ {t(Label.EditorRunButton)}
+ {/if}
+
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()}>
+
+
+
{LANGUAGE_TITLE[describedLanguage]}
+
+
+
+
+
+
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;
----
-
-
-
-
-
-
-
-
-
-
-
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()}>
+
+
+
{LANGUAGE_TITLE[describedLanguage]}
+
+
+
+
+
+
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: