@nimbus-sh/core 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/dist/_shared/tarball-stream.d.ts +93 -0
  4. package/dist/_shared/tarball-stream.d.ts.map +1 -0
  5. package/dist/_shared/tarball-stream.js +235 -0
  6. package/dist/_shared/tarball.d.ts +17 -0
  7. package/dist/_shared/tarball.d.ts.map +1 -0
  8. package/dist/_shared/tarball.js +39 -0
  9. package/dist/index.d.ts +3 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -0
  12. package/dist/runtime/clang-runner.d.ts +38 -0
  13. package/dist/runtime/clang-runner.d.ts.map +1 -0
  14. package/dist/runtime/clang-runner.js +866 -0
  15. package/dist/runtime/facet-host.d.ts +12 -0
  16. package/dist/runtime/facet-host.d.ts.map +1 -1
  17. package/dist/runtime/local-facet-host.d.ts.map +1 -1
  18. package/dist/runtime/local-facet-host.js +29 -9
  19. package/dist/runtime/ruby-gems.d.ts +30 -0
  20. package/dist/runtime/ruby-gems.d.ts.map +1 -0
  21. package/dist/runtime/ruby-gems.js +636 -0
  22. package/dist/runtime/ruby-runner.d.ts +127 -0
  23. package/dist/runtime/ruby-runner.d.ts.map +1 -0
  24. package/dist/runtime/ruby-runner.js +1357 -0
  25. package/dist/runtime/runtime-package.d.ts +63 -0
  26. package/dist/runtime/runtime-package.d.ts.map +1 -0
  27. package/dist/runtime/runtime-package.js +66 -0
  28. package/dist/runtime/runtime-registry.d.ts +3 -2
  29. package/dist/runtime/runtime-registry.d.ts.map +1 -1
  30. package/dist/runtime/runtime-registry.js +1 -1
  31. package/dist/runtime/session-process-supervisor.d.ts +15 -0
  32. package/dist/runtime/session-process-supervisor.d.ts.map +1 -1
  33. package/dist/runtime/session-process-supervisor.js +30 -0
  34. package/dist/workspace/nimbus-workspace.d.ts +102 -22
  35. package/dist/workspace/nimbus-workspace.d.ts.map +1 -1
  36. package/dist/workspace/nimbus-workspace.js +197 -52
  37. package/package.json +4 -2
  38. package/src/_shared/tarball-stream.ts +263 -0
  39. package/src/_shared/tarball.ts +46 -0
  40. package/src/index.ts +7 -0
  41. package/src/runtime/clang-runner.ts +924 -0
  42. package/src/runtime/facet-host.ts +12 -0
  43. package/src/runtime/local-facet-host.ts +28 -8
  44. package/src/runtime/ruby-gems.ts +682 -0
  45. package/src/runtime/ruby-runner.ts +1484 -0
  46. package/src/runtime/runtime-package.ts +114 -0
  47. package/src/runtime/runtime-registry.ts +4 -3
  48. package/src/runtime/session-process-supervisor.ts +27 -0
  49. package/src/workspace/nimbus-workspace.ts +268 -63
@@ -0,0 +1,127 @@
1
+ /**
2
+ * ruby-runner.ts — ruby.wasm (Ruby 3.3.x) runner.
3
+ *
4
+ * Mirror of python-runner.ts patterns adapted to Ruby's wasi-vfs +
5
+ * canonical-abi binding. v1 scope:
6
+ * - `ruby --version` / `ruby -e '<code>'` / `ruby <file.rb>`
7
+ * - stdout/stderr → the process supervisor's log ring (Process tab integration)
8
+ * - exit code via `exit N` / unhandled exception → 1
9
+ * - argv passed through to ARGV; $PROGRAM_NAME / $0 set
10
+ * - stdlib loaded from the packed wasi-vfs inside the wasm
11
+ * - compatible pure Ruby gems through Nimbus RubyGems
12
+ * - WEBrick/Rack-style preview through Nimbus virtual sockets
13
+ *
14
+ * Out of v1:
15
+ * - native extension gems
16
+ *
17
+ * `ruby` with no args is handled by the session-level ruby-repl wrapper;
18
+ * this file owns args-bearing Ruby execution and Ruby package commands.
19
+ *
20
+ * Architecture: the interpreter runs in a FACET (runtime/facet-host.ts), and
21
+ * ruby+stdlib.wasm reaches it as a `wasmModules` entry the HOST compiles —
22
+ * because on workerd nothing else may. Per-user-VFS path:
23
+ * ~/.nimbus/runtimes/ruby/3.3.4/share/ruby/.
24
+ *
25
+ * Two things follow from that being a port rather than a Durable Object, and
26
+ * they are the whole of what is host-specific here:
27
+ * - the seed is whatever `seedFilesystem` returned — a manifest the facet
28
+ * demand-loads against where a guest can be parked mid-syscall, the bytes
29
+ * themselves where it cannot. Nothing below branches on which it got.
30
+ * - a program that keeps serving needs an actor to hold it, which is
31
+ * {@link RubyResidentStart}: supplied on Cloudflare, absent elsewhere, and
32
+ * where it is absent such a program is refused by name.
33
+ *
34
+ * - Wasm size 34.3 MiB (well under empirical 32 MiB-ish per-call
35
+ * ceiling we cleared with Pyodide + clang).
36
+ * - 35 wasi_snapshot_preview1 imports (provided by wasi-instance.ts).
37
+ * - 21 rb-js-abi-host imports (implemented for the `js` bridge used
38
+ * by the Ruby socket adapter).
39
+ * - 3 canonical_abi imports (resource lifecycle — implemented as
40
+ * a minimal Slab<number,object>).
41
+ * - Exports: _initialize, __wasi_vfs_rt_init, ruby-init,
42
+ * ruby-init-loadpath, rb-eval-string-protect, cabi_realloc,
43
+ * canonical_abi_drop_rb-abi-value, memory.
44
+ */
45
+ import type { RuntimeManifest } from './runtime-manifest.js';
46
+ import type { SqliteVFS } from '../vfs/sqlite-vfs.js';
47
+ import type { Command } from '../substrate/lifo/commands/types.js';
48
+ import type { FacetHost } from './facet-host.js';
49
+ import { type WasiFsSnapshot } from './wasi-instance.js';
50
+ type RubyRunnerFactory = (manifest: RuntimeManifest, installRoot: string, binName: string, binKind: string | undefined) => Command;
51
+ /**
52
+ * Build the ruby-runner factory. Called once at session init; the
53
+ * returned factory binds the manifest + install root for each
54
+ * registered entrypoint (`ruby`, `ruby3`).
55
+ */
56
+ export declare function makeRubyRunnerFactory(deps: {
57
+ facets: FacetHost;
58
+ vfs: SqliteVFS;
59
+ registry?: {
60
+ register(name: string, handler: Command): void;
61
+ resolve?(name: string): Promise<Command | null | undefined> | Command | null | undefined;
62
+ };
63
+ /** Where a program that keeps serving goes. See {@link RubyResidentStart}. */
64
+ startResident?: RubyResidentStart;
65
+ }): RubyRunnerFactory;
66
+ /** What one invocation hands the VM. Identical for both process shapes. */
67
+ export interface RubyFacetCallArgs {
68
+ userCode: string;
69
+ rbArgv: string[];
70
+ userEnv: Record<string, string>;
71
+ progName: string;
72
+ cwd: string;
73
+ fsSnapshot: WasiFsSnapshot;
74
+ }
75
+ export interface RubyFacetResult {
76
+ exitCode: number;
77
+ stdout: string;
78
+ stderr: string;
79
+ error?: string;
80
+ }
81
+ /**
82
+ * Start a program that outlives the invocation, and report where it went.
83
+ *
84
+ * A separate dependency rather than a branch, for the reason CPython's is
85
+ * ({@link ./cpython-runner.ts}): a resident process is a property of the
86
+ * DEPLOYMENT, not of Ruby. It needs a substrate that keeps an actor alive
87
+ * between requests and routes inbound HTTP into it, and a host with none gets
88
+ * no degraded version — it gets none, and says so.
89
+ */
90
+ export type RubyResidentStart = (spawn: {
91
+ /** VFS path of the interpreter. By path, not by value: it is 34.3 MiB. */
92
+ wasmVfsPath: string;
93
+ startArgs: RubyFacetCallArgs;
94
+ cwd: string;
95
+ command: string;
96
+ }) => Promise<RubyFacetResult>;
97
+ /**
98
+ * A facet's answer, checked at the trust boundary. Exported for the resident
99
+ * substrate, whose boot payload carries the same object from the same VM.
100
+ */
101
+ export declare function normalizeRubyFacetResult(raw: unknown): RubyFacetResult | null;
102
+ /**
103
+ * Compose the facet preamble. It is evaluated once in the facet's scope,
104
+ * instantiates ruby+stdlib.wasm from the module the host compiled, and
105
+ * bootstraps the Ruby VM. Per-call __rubyRun then drives
106
+ * `rb-eval-string-protect` for each invocation.
107
+ *
108
+ * Exported because the resident-process substrate composes the same source
109
+ * into its own worker module: a server and a `ruby -e` one-liner are the same
110
+ * language, and a second hand-rolled copy of this is how ruby-repl once booted
111
+ * a VM whose language prelude was missing.
112
+ */
113
+ export declare function buildRubyPreamble(): string;
114
+ /**
115
+ * The Ruby-specific portion of the preamble. Wires the wasm imports
116
+ * (wasi_snapshot_preview1 from __wasiMakeImports, canonical_abi from a
117
+ * tiny Slab implementation, rb-js-abi-host for the `js` bridge),
118
+ * instantiates the wasm Module from __NIMBUS_WASM at module-init, and
119
+ * runs Ruby's bootstrap sequence.
120
+ *
121
+ * Per-call __rubyRun then mutates WASI argv/env, clears the stdout/
122
+ * stderr capture buffers, and invokes rb-eval-string-protect with a
123
+ * wrapper that captures SystemExit to extract the exit code.
124
+ */
125
+ export declare const RUBY_RUNNER_PREAMBLE_TAIL = "\n// \u2500\u2500 BEGIN: ruby-runner preamble (Ruby 3.3.4, Nimbus v1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// Capture buffers shared across the bootstrap and per-call paths. The\n// preamble's WASI imports route fd_write stdout/stderr into these via\n// __wasiMakeImports({stdoutWrite, stderrWrite}). Per-call __rubyRun\n// slices from these to isolate output per invocation.\nglobalThis.__nimbusRubyStdout = globalThis.__nimbusRubyStdout || [];\nglobalThis.__nimbusRubyStderr = globalThis.__nimbusRubyStderr || [];\n\n// Whether this facet can suspend the VM mid-syscall, asked of the engine\n// rather than passed in: the answer is a property of where this scope was\n// built, and the scope is the only thing that knows.\nconst __nimbusRubyParking = typeof WebAssembly.promising === 'function' ? 'jspi' : 'none';\n\nfunction __nimbusInstallRubyFsSnapshot(snapshot) {\n const dirs = new Set(['tmp', 'home']);\n const files = {};\n // Null-safe like every other field here: a REPL eval calls __rubyRun with\n // no snapshot at all and must get the bootstrap defaults, not a TypeError.\n const modes = { '': 7, tmp: 7, home: 7, ...(snapshot && snapshot.modes) };\n for (const dir of (snapshot && snapshot.dirs) || []) dirs.add(String(dir).replace(/^\\/+/, '').replace(/\\/+$/, ''));\n for (const [path, b64] of Object.entries((snapshot && snapshot.files) || {})) {\n files[String(path).replace(/^\\/+/, '')] = b64;\n }\n // Metadata-only entries: the manifest carries each file's size and content\n // arrives on first read. Canonicalized exactly like the content entries.\n const sizes = {};\n for (const [path, size] of Object.entries((snapshot && snapshot.sizes) || {})) {\n sizes[String(path).replace(/^\\/+/, '')] = size;\n }\n __wasiInitFS({\n root: '',\n preopens: [\n { wasiPath: '/', vfsPath: '' },\n { wasiPath: '/tmp', vfsPath: 'tmp' },\n { wasiPath: '/home', vfsPath: 'home' },\n ],\n files,\n sizes,\n dirs: Array.from(dirs).filter(Boolean),\n modes,\n // Forwarded, never invented here: only the producer knows whether it\n // walked those roots completely.\n enumeratedRoots: (snapshot && snapshot.enumeratedRoots) || [],\n revision: snapshot && snapshot.revision,\n });\n}\n\n// \u2500\u2500 Canonical-ABI resource Slab \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Pyodide-style minimal resource manager. Ruby's rb-abi-guest.js uses\n// these 4 functions for resource_drop / resource_new / resource_get /\n// resource_clone, but the wasm itself only imports 3:\n// resource_drop_js-abi-value, resource_new_rb-abi-value, resource_get_rb-abi-value\nclass __NimbusRubySlab {\n constructor() { this._map = new Map(); this._next = 1; }\n insert(obj) { const id = this._next++; this._map.set(id, obj); return id; }\n get(id) { return this._map.get(id); }\n remove(id) { const v = this._map.get(id); this._map.delete(id); return v; }\n}\n\n// \u2500\u2500 Bootstrap promise: runs at child-facet module-init time \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Mirrors pyodide v2's __pyodideBootstrap pattern. The synchronous\n// portion (WebAssembly.instantiate + _initialize + ruby-init-loadpath\n// + ruby-init) all completes before the first await \u2014 so it executes\n// in module-init CSP context where workerd permits wasm code-gen\n// from the LOADER-provided Module.\nglobalThis.__rubyBootstrap = (async function nimbusRubyBootstrap() {\n const wasmTable = globalThis.__NIMBUS_WASM || {};\n const rubyMod = wasmTable['ruby+stdlib.wasm'];\n if (!rubyMod) {\n return { ok: false, error: '__NIMBUS_WASM missing ruby+stdlib.wasm' };\n }\n\n // WASI init \u2014 empty preopens initially. Per-call __rubyRun can mount\n // a cwd preopen if needed (for ruby <file.rb> reading via WASI).\n // For v1 (-e mode) we just need stdout/stderr capture + a minimal\n // FS so Ruby's stdlib init (which probes /tmp + $HOME) doesn't crash.\n __wasiInitFS({\n root: '',\n preopens: [\n // Preopen / so Ruby can resolve all FS paths through WASI.\n // Ruby's __wasi_vfs_rt_init mounts its packed stdlib under /usr\n // inside the wasm's internal VFS \u2014 these preopens are for the\n // OUTER (host-visible) FS that wasi_snapshot_preview1 exposes.\n { wasiPath: '/', vfsPath: '' },\n { wasiPath: '/tmp', vfsPath: 'tmp' },\n { wasiPath: '/home', vfsPath: 'home' },\n ],\n files: {},\n dirs: ['tmp', 'home'],\n modes: { '': 7, tmp: 7, home: 7 },\n });\n\n // Initial argv/env (bootstrap defaults). Per-call __rubyRun re-\n // initializes WASI with the actual user argv/env before evaluating\n // user code.\n let memRef = null;\n const wasi = __wasiMakeImports({\n argv: ['ruby'],\n env: { HOME: '/home/ruby', LANG: 'C.UTF-8', LC_ALL: 'C.UTF-8' },\n // Stated, not defaulted. A host that cannot park a guest hands over the\n // whole filesystem instead of a manifest, so nothing here needs to block \u2014\n // and saying so is what makes a syscall that blocks anyway fail loudly\n // instead of returning a Promise where the guest expects an errno.\n parking: __nimbusRubyParking,\n getMemory: () => memRef,\n stdoutWrite: (s) => { globalThis.__nimbusRubyStdout.push(s); },\n stderrWrite: (s) => { globalThis.__nimbusRubyStderr.push(s); },\n });\n\n // canonical_abi imports \u2014 3 resource lifecycle fns. The Slab is\n // shared across the lifetime of the facet (single call, then the\n // facet is reaped).\n const rbValueSlab = new __NimbusRubySlab();\n const jsValueSlab = new __NimbusRubySlab();\n const canonical_abi = {\n 'resource_drop_js-abi-value': (i) => { jsValueSlab.remove(i); },\n 'resource_new_rb-abi-value': (i) => rbValueSlab.insert({ _wasm_val: i }),\n 'resource_get_rb-abi-value': (i) => {\n const r = rbValueSlab.get(i);\n return r ? r._wasm_val : 0;\n },\n };\n\n const jsAbiResources = jsValueSlab;\n function readGuestString(ptr, len) {\n return new TextDecoder().decode(new Uint8Array(memRef.buffer, ptr, len));\n }\n function writeGuestString(outPtr, value) {\n const bytes = new TextEncoder().encode(String(value));\n const strPtr = cabiRealloc(0, 0, 1, bytes.length);\n new Uint8Array(memRef.buffer).set(bytes, strPtr);\n const dv = new DataView(memRef.buffer);\n dv.setUint32(outPtr + 0, strPtr, true);\n dv.setUint32(outPtr + 4, bytes.length, true);\n }\n function writeJsResult(outPtr, tag, value) {\n const dv = new DataView(memRef.buffer);\n dv.setInt8(outPtr + 0, tag === 'success' ? 0 : 1, true);\n dv.setInt32(outPtr + 4, jsAbiResources.insert(value), true);\n }\n function readJsHandle(id) {\n return jsAbiResources.get(id);\n }\n function readJsHandleList(ptr, len) {\n const dv = new DataView(memRef.buffer);\n const out = [];\n for (let i = 0; i < len; i++) out.push(readJsHandle(dv.getInt32(ptr + i * 4, true)));\n return out;\n }\n function jsFailure(error) {\n return error instanceof Error ? error : new Error(String(error));\n }\n const rb_js_abi_host = {\n rb_wasm_throw_prohibit_rewind_exception: () => {\n // This one CAN fire from Ruby internals (Fiber rewind guard).\n // Make it a no-op so Ruby's continuation machinery proceeds.\n },\n 'eval-js: func(code: string) -> variant { success(handle<js-abi-value>), failure(handle<js-abi-value>) }': (ptr, len, outPtr) => {\n try {\n writeJsResult(outPtr, 'success', Function(readGuestString(ptr, len))());\n } catch (e) {\n writeJsResult(outPtr, 'failure', jsFailure(e));\n }\n },\n 'is-js: func(value: handle<js-abi-value>) -> bool': () => 1,\n 'instance-of: func(value: handle<js-abi-value>, klass: handle<js-abi-value>) -> bool': (value, klass) => {\n const ctor = readJsHandle(klass);\n return typeof ctor === 'function' && readJsHandle(value) instanceof ctor ? 1 : 0;\n },\n 'global-this: func() -> handle<js-abi-value>': () => jsAbiResources.insert(globalThis),\n 'int-to-js-number: func(value: s32) -> handle<js-abi-value>': (value) => jsAbiResources.insert(value),\n 'float-to-js-number: func(value: float64) -> handle<js-abi-value>': (value) => jsAbiResources.insert(value),\n 'string-to-js-string: func(value: string) -> handle<js-abi-value>': (ptr, len) => jsAbiResources.insert(readGuestString(ptr, len)),\n 'bool-to-js-bool: func(value: bool) -> handle<js-abi-value>': (value) => {\n if (value !== 0 && value !== 1) throw new TypeError('Ruby JS bridge received an invalid bool value');\n return jsAbiResources.insert(value === 1);\n },\n 'proc-to-js-function: func(value: u32) -> handle<js-abi-value>': () => jsAbiResources.insert(() => {\n throw new Error('Nimbus Ruby JS bridge does not expose Ruby Proc callbacks yet');\n }),\n 'rb-object-to-js-rb-value: func(raw-rb-abi-value: u32) -> handle<js-abi-value>': (value) => jsAbiResources.insert({ __nimbusRubyValue: value >>> 0 }),\n 'js-value-to-string: func(value: handle<js-abi-value>) -> string': (value, outPtr) => writeGuestString(outPtr, String(readJsHandle(value))),\n 'js-value-to-integer: func(value: handle<js-abi-value>) -> variant { as-float(float64), bignum(string) }': (value, outPtr) => {\n const raw = readJsHandle(value);\n const dv = new DataView(memRef.buffer);\n if (typeof raw === 'bigint') {\n dv.setInt8(outPtr + 0, 1, true);\n writeGuestString(outPtr + 8, raw.toString());\n return;\n }\n dv.setInt8(outPtr + 0, 0, true);\n dv.setFloat64(outPtr + 8, Number(raw), true);\n },\n 'export-js-value-to-host: func(value: handle<js-abi-value>) -> ()': (value) => {\n globalThis.__nimbusRubyExportedJsValue = readJsHandle(value);\n },\n 'import-js-value-from-host: func() -> handle<js-abi-value>': () => jsAbiResources.insert(globalThis.__nimbusRubyExportedJsValue),\n 'js-value-typeof: func(value: handle<js-abi-value>) -> string': (value, outPtr) => writeGuestString(outPtr, typeof readJsHandle(value)),\n 'js-value-equal: func(lhs: handle<js-abi-value>, rhs: handle<js-abi-value>) -> bool': (lhs, rhs) => readJsHandle(lhs) == readJsHandle(rhs) ? 1 : 0,\n 'js-value-strictly-equal: func(lhs: handle<js-abi-value>, rhs: handle<js-abi-value>) -> bool': (lhs, rhs) => readJsHandle(lhs) === readJsHandle(rhs) ? 1 : 0,\n 'reflect-apply: func(target: handle<js-abi-value>, this-argument: handle<js-abi-value>, arguments: list<handle<js-abi-value>>) -> variant { success(handle<js-abi-value>), failure(handle<js-abi-value>) }': (target, thisArg, argsPtr, argsLen, outPtr) => {\n try {\n writeJsResult(outPtr, 'success', Reflect.apply(readJsHandle(target), readJsHandle(thisArg), readJsHandleList(argsPtr, argsLen)));\n } catch (e) {\n writeJsResult(outPtr, 'failure', jsFailure(e));\n }\n },\n 'reflect-get: func(target: handle<js-abi-value>, property-key: string) -> variant { success(handle<js-abi-value>), failure(handle<js-abi-value>) }': (target, keyPtr, keyLen, outPtr) => {\n try {\n writeJsResult(outPtr, 'success', Reflect.get(readJsHandle(target), readGuestString(keyPtr, keyLen)));\n } catch (e) {\n writeJsResult(outPtr, 'failure', jsFailure(e));\n }\n },\n 'reflect-set: func(target: handle<js-abi-value>, property-key: string, value: handle<js-abi-value>) -> variant { success(handle<js-abi-value>), failure(handle<js-abi-value>) }': (target, keyPtr, keyLen, value, outPtr) => {\n try {\n writeJsResult(outPtr, 'success', Reflect.set(readJsHandle(target), readGuestString(keyPtr, keyLen), readJsHandle(value)));\n } catch (e) {\n writeJsResult(outPtr, 'failure', jsFailure(e));\n }\n },\n };\n\n const imports = {\n wasi_snapshot_preview1: wasi.wasiImport,\n canonical_abi,\n 'rb-js-abi-host': rb_js_abi_host,\n };\n\n let instance;\n try {\n const result = await WebAssembly.instantiate(rubyMod, imports);\n instance = (result instanceof WebAssembly.Instance ? result : result.instance);\n } catch (e) {\n return { ok: false, error: 'WebAssembly.instantiate failed: ' + (e && e.message), stack: e && e.stack };\n }\n memRef = instance.exports.memory;\n\n // Entering the Ruby VM.\n //\n // The WASI imports this instance is given include ones wrapped in\n // WebAssembly.Suspending \u2014 fd_read, fd_write, fd_pread, path_filestat_get,\n // poll_oneoff and the sock_* family. V8 requires an active\n // WebAssembly.promising suspender for ANY call into a suspending import,\n // whether or not that import returns a Promise (measured on workerd:\n // a Suspending import returning a plain i32 off a raw stack throws\n // SuspendError \"trying to suspend without WebAssembly.promising\"). So every\n // entry into this instance is promising-wrapped, not just the ones that are\n // known to park today: which WASI calls the guest makes is the guest's\n // business, and the suspending set grows.\n //\n // cabi_realloc is deliberately not wrapped. It is the guest allocator, not a\n // VM entry \u2014 it never reaches WASI, and it is reached from the synchronous\n // rb-js-abi-host callbacks, which cannot await.\n //\n // Where there is no JSPI there is also nothing suspending to enter, so the\n // wrapper is the identity: same VM, same call, on a plain stack.\n const enterVm = (fn) => (__nimbusRubyParking === 'jspi' ? WebAssembly.promising(fn) : fn);\n\n // \u2500\u2500 Ruby bootstrap sequence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Order matters (per ruby.wasm DefaultRubyVM):\n // 1. _initialize (reactor entry; runs static initializers)\n // 2. __wasi_vfs_rt_init (mount packed stdlib at the wasi-vfs's\n // internal FS \u2014 needed for require to find Ruby's *.rb files)\n // 3. ruby-init([progName]) \u2014 initialize VM with argv[0]\n // 4. ruby-init-loadpath() \u2014 set $LOAD_PATH from packed stdlib\n try {\n if (typeof instance.exports._initialize === 'function') {\n await enterVm(instance.exports._initialize)();\n }\n if (typeof instance.exports.__wasi_vfs_rt_init === 'function') {\n await enterVm(instance.exports.__wasi_vfs_rt_init)();\n }\n } catch (e) {\n return { ok: false, error: '_initialize/wasi_vfs_rt_init failed: ' + (e && e.message), stack: e && e.stack };\n }\n\n // Locate the canonical Ruby ABI exports. Names embed the WIT\n // signature literal (e.g. 'ruby-init: func(args: list<string>) -> ()')\n // because rb-abi-guest is wit-bindgen-generated.\n const rubyInit = instance.exports['ruby-init: func(args: list<string>) -> ()'];\n const rubyInitLoadpath = instance.exports['ruby-init-loadpath: func() -> ()'];\n const rbEvalStringProtect = instance.exports['rb-eval-string-protect: func(str: string) -> tuple<handle<rb-abi-value>, s32>'];\n const cabiRealloc = instance.exports.cabi_realloc;\n if (!rubyInit || !rubyInitLoadpath || !rbEvalStringProtect || !cabiRealloc) {\n return { ok: false, error: 'Required Ruby ABI exports missing (ruby-init/init-loadpath/eval-string-protect/cabi_realloc)' };\n }\n\n // Encode a list<string> argument for ruby-init. WIT canonical-ABI\n // shape: caller allocates list buffer; each element is (ptr, len).\n // Strings are UTF-8 encoded into separately-allocated buffers.\n function writeListString(strings) {\n const memory = instance.exports.memory;\n const enc = new TextEncoder();\n const len = strings.length;\n const listBufPtr = cabiRealloc(0, 0, 4, len * 8); // align=4, size=len*8\n const encoded = strings.map((s) => enc.encode(s));\n for (let i = 0; i < len; i++) {\n const bytes = encoded[i];\n const strPtr = cabiRealloc(0, 0, 1, bytes.length);\n new Uint8Array(memory.buffer).set(bytes, strPtr);\n const dv = new DataView(memory.buffer);\n dv.setUint32(listBufPtr + i * 8 + 0, strPtr, true);\n dv.setUint32(listBufPtr + i * 8 + 4, bytes.length, true);\n }\n return { ptr: listBufPtr, len };\n }\n\n function writeString(s) {\n const memory = instance.exports.memory;\n const enc = new TextEncoder();\n const bytes = enc.encode(s);\n const ptr = cabiRealloc(0, 0, 1, bytes.length);\n new Uint8Array(memory.buffer).set(bytes, ptr);\n return { ptr, len: bytes.length };\n }\n\n // NOTE: We DO NOT call ruby-init or ruby-init-loadpath here. Both\n // invoke CPython-like random-seed initialization (random_get via\n // wasi_snapshot_preview1.random_get), which workerd blocks in the\n // global-scope (module-init) context. Same constraint that bit us\n // for Pyodide v2 P21. The per-call __rubyRun runs them at request-\n // handler time where crypto.getRandomValues is permitted.\n //\n // _initialize and __wasi_vfs_rt_init are safe at module-init because\n // they only do static initialization (no entropy reads).\n\n return {\n ok: true,\n instance,\n wasi,\n rubyInit: enterVm(rubyInit),\n rubyInitLoadpath: enterVm(rubyInitLoadpath),\n rbEvalStringProtect: enterVm(rbEvalStringProtect),\n writeListString,\n writeString,\n rubyInitialized: false, // mutated to true by __rubyRun on first call\n };\n})();\n\n// \u2500\u2500 Per-call entry point \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Invoked from the LOADER child facet's execute() (which calls the\n// serialized facetFn that does globalThis.__rubyRun(args)).\n//\n// At this point the bootstrap promise has resolved (since it's\n// awaited inside the child facet's module-init context \u2014 the\n// instantiate finishes before the request handler runs). We:\n// 1. Update Ruby's $0 / $PROGRAM_NAME / ARGV via rb-eval-string-protect\n// 2. Wrap the user code in a begin/rescue SystemExit/StandardError\n// handler so we can extract exit code without losing stdout\n// 3. Read stdout/stderr buffers and slice from the per-call start\n// Evaluate Ruby source in the booted VM. Hoisted out of __rubyRun so a\n// process can also be DRIVEN (resumed) without re-running the whole\n// per-invocation wrapper.\nasync function __nimbusRubyEval(boot, rubyCode) {\n const memory = boot.instance.exports.memory;\n const bytes = new TextEncoder().encode(rubyCode);\n const codePtr = boot.instance.exports.cabi_realloc(0, 0, 1, bytes.length);\n new Uint8Array(memory.buffer).set(bytes, codePtr);\n const retPtr = await boot.rbEvalStringProtect(codePtr, bytes.length);\n // Return is a tuple: (rb-abi-value handle u32, status s32) \u2014 8 bytes\n const dv = new DataView(memory.buffer);\n return { handle: dv.getUint32(retPtr + 0, true), status: dv.getInt32(retPtr + 4, true) };\n}\n\n// Resume the process's main fiber, and report what it wants next.\n//\n// A workerd request context cannot resume a wasm stack suspended by a\n// DIFFERENT request, so a server cannot simply block in accept across\n// requests. A Ruby fiber can: its state lives in the VM's own memory, so it\n// survives the context boundary. The process body therefore runs in a fiber\n// that parks when its accept queue is empty, and each inbound request resumes\n// it. Returns resumed=false when there is no live process to drive, which the\n// kernel reports as \"nothing accepted the request\".\n//\n// The report is what makes the process drivable at all:\n// alive the body is still running - it parked rather than finished\n// hostDriven it has listened, so inbound requests are what resume it now\n// wakeAfter seconds until the earliest deadline it owes, or null for none\nglobalThis.__nimbusRubyResumeMain = async function __nimbusRubyResumeMain() {\n const boot = await globalThis.__rubyBootstrap;\n if (!boot.ok) return { resumed: false, alive: false, hostDriven: false, wakeAfter: null };\n const stderrStart = globalThis.__nimbusRubyStderr.length;\n await __nimbusRubyEval(boot, [\n '$__nimbus_resumed = ($__nimbus_main && $__nimbus_main.alive?) ? (begin; $__nimbus_main.resume; true; ' +\n 'rescue Exception => e; $stderr.write(e.full_message(highlight: false, order: :top)); $__nimbus_exit = 1; false; end) : false',\n '$stderr.write(\"__NIMBUS_RESUMED_\" + $__nimbus_resumed.to_s' +\n ' + \"_\" + (($__nimbus_main && $__nimbus_main.alive?) ? \"1\" : \"0\")' +\n ' + \"_\" + ((defined?(Nimbus::Threading) && Nimbus::Threading.host_driven) ? \"1\" : \"0\")' +\n ' + \"_\" + ($__nimbus_wake_after ? $__nimbus_wake_after.to_s : \"nil\") + \"\\n\")',\n ].join(\"\\n\"));\n // Scrub the marker so it never reaches the user's stderr, keeping whatever\n // the resumed program itself wrote.\n const written = globalThis.__nimbusRubyStderr.slice(stderrStart).join('');\n globalThis.__nimbusRubyStderr.length = stderrStart;\n const scrubbed = written.replace(/__NIMBUS_RESUMED_(true|false)_[^\\n]*\\n?/g, '');\n if (scrubbed) globalThis.__nimbusRubyStderr.push(scrubbed);\n const marker = /__NIMBUS_RESUMED_(true|false)_([01])_([01])_([^\\n]*)/.exec(written);\n const wake = marker && marker[4] !== 'nil' ? Number(marker[4]) : NaN;\n return {\n resumed: !!marker && marker[1] === 'true',\n alive: !!marker && marker[2] === '1',\n hostDriven: !!marker && marker[3] === '1',\n wakeAfter: Number.isFinite(wake) ? wake : null,\n };\n};\n\n// One resume at a time, for the whole process. Several drivers can be live at\n// once \u2014 the request that queued a connection, another request waiting out a\n// deadline, the invocation that started the process \u2014 and two of them entering\n// a live fiber together would corrupt it. The queue is on globalThis because\n// no single request may own it: a request context is torn down without warning\n// when its response is sent, taking anything anchored to it.\nglobalThis.__nimbusRubyResumeQueue = globalThis.__nimbusRubyResumeQueue || Promise.resolve();\nglobalThis.__nimbusRubyStep = function __nimbusRubyStep() {\n const run = () => globalThis.__nimbusRubyResumeMain();\n const task = globalThis.__nimbusRubyResumeQueue.then(run, run);\n globalThis.__nimbusRubyResumeQueue = task.then(() => {}, () => {});\n return task;\n};\n\n// Drive a process that has just been started, until it no longer owes the\n// clock anything.\n//\n// This is the whole of what a \"boot driver\" is: the clock only advances\n// between turns, so a body that parked on a deadline needs someone outside the\n// guest to wait out that deadline on a real timer and resume it. Without one,\n// the deadline can never pass and the invocation burns its CPU budget instead.\n//\n// It stops the moment the process listens: from there the process is resumed\n// by inbound requests, and those requests carry the deadlines \u2014 a driver\n// anchored to this invocation would be cancelled with it.\nglobalThis.__nimbusRubyDriveBoot = async function __nimbusRubyDriveBoot() {\n for (;;) {\n const step = await globalThis.__nimbusRubyStep();\n if (!step.resumed || !step.alive) return step;\n if (step.hostDriven || step.wakeAfter === null) return step;\n // Always through a timer, even at zero: the turn boundary is what moves\n // the clock, so resuming without one would leave the deadline where it was.\n await new Promise((resolve) => setTimeout(resolve, Math.max(0, step.wakeAfter) * 1000));\n }\n};\n\nglobalThis.__rubyRun = async function __rubyRun(args) {\n const stdoutStart = globalThis.__nimbusRubyStdout.length;\n const stderrStart = globalThis.__nimbusRubyStderr.length;\n\n const boot = await globalThis.__rubyBootstrap;\n if (!boot.ok) {\n return {\n exitCode: 1,\n stdout: globalThis.__nimbusRubyStdout.slice(stdoutStart).join(''),\n stderr: globalThis.__nimbusRubyStderr.slice(stderrStart).join(''),\n error: 'ruby bootstrap failed: ' + (boot.error || 'unknown') + (boot.stack ? ' [stack=' + boot.stack + ']' : ''),\n };\n }\n\n try {\n __nimbusInstallRubyFsSnapshot(args.fsSnapshot);\n // AFTER the mount, never before. __wasiInitFS deliberately drops the\n // supervisor so a pooled isolate cannot serve the previous tenant's\n // filesystem, which means adopting first \u2014 as both ruby entry points do,\n // since they must adopt before they know whether a mount is coming \u2014\n // leaves the seed with no backing store for the whole script load.\n // Every require then read a manifest entry with nothing behind it.\n __wasiAdoptSupervisor(globalThis.__nimbusRubySupervisor);\n } catch (e) {\n globalThis.__nimbusRubyStderr.push('[ruby-runner] VFS mount failed: ' + (e && e.message) + '\\n');\n }\n\n // First call into __rubyRun: complete Ruby VM init (ruby-init +\n // ruby-init-loadpath) now that we're in request-handler context\n // where crypto.getRandomValues is permitted. Subsequent calls skip.\n //\n // The language prelude goes in here, once, with the rest of VM startup.\n // Threads, queues, mutexes and the socket classes are what Ruby IS on this\n // runtime, so a program gets them because it is Ruby - not because the\n // invocation was classified one way rather than another. The two process\n // shapes differ in how long the process lives, and in nothing else.\n if (!boot.rubyInitialized) {\n try {\n const initArgs = boot.writeListString(['ruby', '-e_=0']);\n await boot.rubyInit(initArgs.ptr, initArgs.len);\n await boot.rubyInitLoadpath();\n boot.rubyInitialized = true;\n } catch (e) {\n return {\n exitCode: 1,\n stdout: globalThis.__nimbusRubyStdout.slice(stdoutStart).join(''),\n stderr: globalThis.__nimbusRubyStderr.slice(stderrStart).join(''),\n error: 'ruby-init / ruby-init-loadpath failed at request time: ' + (e && e.message),\n };\n }\n // A broken language prelude is a broken interpreter, so it fails the call\n // rather than leaving the program to trip over whatever is missing.\n let preludeStatus;\n try {\n preludeStatus = await __nimbusRubyEval(boot, RUBY_LANGUAGE_PRELUDE);\n } catch (e) {\n preludeStatus = { status: -1, error: (e && e.message) || String(e) };\n }\n if (!preludeStatus || preludeStatus.status !== 0) {\n boot.rubyInitialized = false;\n return {\n exitCode: 1,\n stdout: globalThis.__nimbusRubyStdout.slice(stdoutStart).join(''),\n stderr: globalThis.__nimbusRubyStderr.slice(stderrStart).join(''),\n error: 'ruby language prelude failed to load: ' +\n (preludeStatus && preludeStatus.error ? preludeStatus.error : 'eval status ' + (preludeStatus && preludeStatus.status)),\n };\n }\n }\n\n function rubyStringLiteral(value) {\n const s = String(value ?? '');\n let out = \"'\";\n for (let i = 0; i < s.length; i++) {\n const ch = s[i];\n if (ch === \"\\\\\") out += \"\\\\\\\\\";\n else if (ch === \"'\") out += \"\\\\'\";\n else out += ch;\n }\n return out + \"'\";\n }\n\n function rubyArrayLiteral(values) {\n return '[' + (values || []).map((v) => rubyStringLiteral(v)).join(', ') + ']';\n }\n\n function rubyHashLiteral(obj) {\n return '{' + Object.entries(obj || {})\n .map(([k, v]) => rubyStringLiteral(k) + ' => ' + rubyStringLiteral(v))\n .join(', ') + '}';\n }\n\n // Wrapper code: set $0/$PROGRAM_NAME/ARGV/ENV, run user code,\n // capture SystemExit. User-controlled strings are emitted as Ruby\n // single-quoted literals so Ruby interpolation inside source text is\n // preserved for the user's eval, not consumed by this wrapper.\n //\n // The wrapper sets __NIMBUS_RUBY_EXIT to the desired exit code so\n // we can read it via a second rb-eval-string-protect call. Failing\n // SystemExit (raise) ends up with __NIMBUS_RUBY_EXIT = 1 + stderr\n // message.\n const userCodeRb = rubyStringLiteral(args.userCode);\n const argvRb = rubyArrayLiteral(args.rbArgv.slice(1)); // exclude argv[0]\n const progNameRb = rubyStringLiteral(args.progName);\n\n // STAGED execution: we split the prelude (stdout sync + ARGV/ENV/$0\n // setup) from the user-code eval. The prelude has no failure modes\n // we care about; user-code is wrapped in begin/rescue for SystemExit\n // and Exception. Wrapper failures are reported through the captured\n // stderr diagnostic stream.\n // Build env list as string-keyed Ruby hash via the rocket-syntax.\n // Ruby treats colon-style hash literals as Symbol-keyed; we need\n // String keys so ENV[k] = v works without TypeError.\n const envHashRb = rubyHashLiteral(args.userEnv || {});\n const cwdRb = rubyStringLiteral(args.cwd || '/home/user');\n\n const preludeRb = [\n // Reset exit state FIRST so partial prelude failures still\n // surface a clean exit code (previously: exit 7 left $__nimbus_exit\n // = 7 \u2192 next call's prelude could fail before resetting \u2192 second\n // exit 0 returned 7).\n '$__nimbus_exit = 0',\n '$stdout.sync = true',\n '$stderr.sync = true',\n '$0 = ' + progNameRb,\n '$PROGRAM_NAME = ' + progNameRb,\n 'ARGV.replace(' + argvRb + ')',\n envHashRb + '.each_pair { |k, v| ENV[k] = v }',\n 'ENV[\"HOME\"] ||= \"/home/user\"',\n 'ENV[\"GEM_HOME\"] ||= File.join(ENV[\"HOME\"], \".gem\")',\n 'ENV[\"GEM_PATH\"] ||= ENV[\"GEM_HOME\"]',\n 'begin; Dir.mkdir(ENV[\"GEM_HOME\"]) unless Dir.exist?(ENV[\"GEM_HOME\"]); rescue Exception; end',\n 'begin; Dir.chdir(' + cwdRb + '); rescue Exception; end',\n 'begin; $LOAD_PATH.unshift(Dir.pwd) unless $LOAD_PATH.include?(Dir.pwd); rescue Exception; end',\n 'begin; (ENV[\"NIMBUS_GEM_LIBS\"] || \"\").split(\":\").reverse_each { |p| $LOAD_PATH.unshift(p) if p && p != \"\" && !$LOAD_PATH.include?(p) }; rescue Exception; end',\n ].join('; ');\n\n // The body runs in a fiber; every resume of it goes through the driver\n // below. A program with no server runs to completion across as many turns as\n // its deadlines need; a server parks in accept when its queue is empty and\n // is driven from there, one inbound request at a time. Same fiber, same\n // driver, so this is the single path for every Ruby invocation.\n const userWrapper = [\n '$__nimbus_main = Fiber.new do',\n ' begin',\n ' ' + 'eval(' + userCodeRb + ', TOPLEVEL_BINDING, ' + progNameRb + ', 1)',\n ' rescue SystemExit => e',\n ' $__nimbus_exit = e.status',\n ' rescue Exception => e',\n ' $stderr.write(e.full_message(highlight: false, order: :top))',\n ' $__nimbus_exit = 1',\n ' ensure',\n ' begin; Nimbus::Threading.shutdown if defined?(Nimbus::Threading); rescue Exception; end',\n ' $stdout.flush rescue nil',\n ' $stderr.flush rescue nil',\n ' end',\n 'end',\n ].join(\"\\n\");\n\n const callEvalStringProtect = (rubyCode) => __nimbusRubyEval(boot, rubyCode);\n\n // Stage 1: run the prelude (sync flags, ARGV, ENV, $0/$PROGRAM_NAME).\n let preludeStatus;\n try {\n preludeStatus = await callEvalStringProtect(preludeRb);\n } catch (e) {\n return {\n exitCode: 1,\n stdout: globalThis.__nimbusRubyStdout.slice(stdoutStart).join(''),\n stderr: globalThis.__nimbusRubyStderr.slice(stderrStart).join(''),\n error: 'ruby prelude threw: ' + (e && e.message),\n };\n }\n if (preludeStatus && preludeStatus.status !== 0) {\n globalThis.__nimbusRubyStderr.push('[ruby-runner-diag] prelude returned non-zero status: ' + preludeStatus.status + '\\n');\n }\n\n // Stage 2: build the body fiber wrapped for SystemExit/Exception capture,\n // then drive it until it finishes or hands itself to the host.\n let evalStatus;\n try {\n evalStatus = await callEvalStringProtect(userWrapper);\n await globalThis.__nimbusRubyDriveBoot();\n } catch (e) {\n return {\n exitCode: 1,\n stdout: globalThis.__nimbusRubyStdout.slice(stdoutStart).join(''),\n stderr: globalThis.__nimbusRubyStderr.slice(stderrStart).join(''),\n error: 'rb-eval-string-protect threw: ' + (e && e.message),\n };\n }\n if (evalStatus && evalStatus.status !== 0) {\n globalThis.__nimbusRubyStderr.push('[ruby-runner-diag] user wrapper returned non-zero status: ' + evalStatus.status + '\\n');\n }\n\n // Read $__nimbus_exit through a sentinel on captured stderr, then remove\n // the sentinel before returning user-visible output.\n const NIMBUS_EXIT_MARKER = '__NIMBUS_RUBY_EXIT_';\n let exitCode = 0;\n try {\n // Print the marker + exit code to stderr (a side channel separate\n // from user-visible stdout). We strip it before returning.\n await callEvalStringProtect(\n '$stderr.write(' + JSON.stringify(NIMBUS_EXIT_MARKER) + ' + $__nimbus_exit.to_s + \"\\\\n\")'\n );\n // Scrape the marker from stderr buffer \u2014 using ONLY this call's\n // slice (from stderrStart). The same facet can be reused across\n // multiple __rubyRun invocations (loader-pool dedup by tag), so\n // a previous call's marker would otherwise be matched first.\n const callStderr = globalThis.__nimbusRubyStderr.slice(stderrStart).join('');\n // Match the LAST marker in this slice (the one our just-completed\n // call emitted; if the user wrapper also emitted writes, the\n // marker is appended after them).\n const markerRe = new RegExp(NIMBUS_EXIT_MARKER + '(-?\\\\d+)', 'g');\n let lastMatch = null;\n let mit;\n while ((mit = markerRe.exec(callStderr)) !== null) lastMatch = mit;\n if (lastMatch) exitCode = parseInt(lastMatch[1], 10);\n } catch (e) {\n // Failure to read exit code \u2192 assume 0 if no errors observed.\n exitCode = 0;\n }\n\n // Scrub the marker out of the BUFFER, not just out of what this call\n // returns. A process that parked instead of exiting - any server - leaves\n // __rubyRun finished while the program is still live, and whoever reads the\n // buffer next would otherwise hand the user our side channel.\n const stdoutOut = globalThis.__nimbusRubyStdout.slice(stdoutStart).join('');\n const markerLine = new RegExp(NIMBUS_EXIT_MARKER + '-?\\\\d+\\\\n?', 'g');\n const stderrOut = globalThis.__nimbusRubyStderr.slice(stderrStart).join('').replace(markerLine, '');\n globalThis.__nimbusRubyStderr.length = stderrStart;\n if (stderrOut) globalThis.__nimbusRubyStderr.push(stderrOut);\n\n return {\n exitCode: exitCode,\n stdout: stdoutOut,\n stderr: stderrOut,\n };\n};\n\n// \u2500\u2500 END: ruby-runner preamble \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n";
126
+ export {};
127
+ //# sourceMappingURL=ruby-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ruby-runner.d.ts","sourceRoot":"","sources":["../../src/runtime/ruby-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAmB,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,KAAK,EAAE,OAAO,EAAkB,MAAM,qCAAqC,CAAC;AAGnF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAA8B,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAiBrF,KAAK,iBAAiB,GAAG,CACvB,QAAQ,EAAE,eAAe,EACzB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,SAAS,KACxB,OAAO,CAAC;AAEb;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,MAAM,EAAE,SAAS,CAAC;IAClB,GAAG,EAAE,SAAS,CAAC;IACf,QAAQ,CAAC,EAAE;QACT,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;QAC/C,OAAO,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;KAC1F,CAAC;IACF,8EAA8E;IAC9E,aAAa,CAAC,EAAE,iBAAiB,CAAC;CACnC,GAAG,iBAAiB,CA6LpB;AA2TD,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,cAAc,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE;IACtC,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;AAS/B;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAS7E;AAgGD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CA2B1C;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,q4kCAgrBrC,CAAC"}