{"id":48812344,"url":"https://github.com/vektornode/selva-compute","last_synced_at":"2026-06-09T15:00:53.871Z","repository":{"id":342046692,"uuid":"1134158770","full_name":"VektorNode/selva-compute","owner":"VektorNode","description":"TypeScript framework for Rhino.Compute \u0026 Grasshopper with Three.js visualization","archived":false,"fork":false,"pushed_at":"2026-06-03T19:25:09.000Z","size":655,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-06-03T21:02:34.752Z","etag":null,"topics":["compute","parametric-design","rhino"],"latest_commit_sha":null,"homepage":"https://vektornode.github.io/selva-compute/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/VektorNode.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-14T10:31:52.000Z","updated_at":"2026-06-03T13:38:45.000Z","dependencies_parsed_at":"2026-06-03T21:00:55.660Z","dependency_job_id":null,"html_url":"https://github.com/VektorNode/selva-compute","commit_stats":null,"previous_names":["vektornode/selva-compute"],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/VektorNode/selva-compute","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VektorNode%2Fselva-compute","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VektorNode%2Fselva-compute/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VektorNode%2Fselva-compute/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VektorNode%2Fselva-compute/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/VektorNode","download_url":"https://codeload.github.com/VektorNode/selva-compute/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VektorNode%2Fselva-compute/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34112225,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compute","parametric-design","rhino"],"created_at":"2026-04-14T09:03:24.252Z","updated_at":"2026-06-09T15:00:53.821Z","avatar_url":"https://github.com/VektorNode.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- Badges --\u003e\n\u003cdiv align=\"center\"\u003e\n\n[![npm version](https://img.shields.io/npm/v/@selvajs/compute.svg)](https://www.npmjs.com/package/@selvajs/compute)\n[![npm downloads](https://img.shields.io/npm/dm/@selvajs/compute.svg)](https://www.npmjs.com/package/@selvajs/compute)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.9+-blue.svg)](https://www.typescriptlang.org/)\n[![Node.js](https://img.shields.io/badge/Node.js-20+-green.svg)](https://nodejs.org/)\n[![GitHub Repository](https://img.shields.io/badge/GitHub-VektorNode/selva--compute-blue?logo=github)](https://github.com/VektorNode/selva-compute)\n\n\u003c/div\u003e\n\n# @selvajs/compute\n\nAn intermediate-level TypeScript framework for building web applications with Rhino Compute and Grasshopper.\n\n`@selvajs/compute` simplifies the process of communicating with Rhino Compute, handling Grasshopper definitions, and visualizing results in the browser with Three.js.\n\n## Installation\n\n```bash\nnpm install @selvajs/compute three\n```\n\n_(Note: `three` is a peer dependency if you use the visualization features)_\n\n## Why this project exists\n\n`@selvajs/compute` provides a type-safe, production-ready foundation for building with Rhino Compute:\n\n- **Type-safe API** — Full TypeScript with structured error codes and rich error context.\n- **High-level client** — `GrasshopperClient` for one-off solves, `client.createScheduler()` for any UI that fires solves frequently.\n- **Robust transport** — Configurable timeout, caller-supplied `AbortSignal`, exponential-backoff retries on transient errors, and `Retry-After` honored on 429.\n- **Slider-friendly** — `latest-wins` scheduling aborts stale solves when newer values arrive. Optional response cache makes repeated inputs instant.\n- **Ready-to-use visualization** — Integrated Three.js setup with `initThree()` and configurable rendering options.\n\nWhether you're building a simple solver, a slider-driven configurator, or a long-running job submission flow, `@selvajs/compute` handles the plumbing so you can focus on your Grasshopper definitions.\n\n\u003e **What this is not:** a job queue. For solves longer than a couple of\n\u003e minutes, run this library server-side behind your own queue\n\u003e (BullMQ / SQS / Cloud Tasks) and expose a status endpoint to the browser.\n\n\u003e **Note:** The library currently focuses on the Grasshopper endpoint but is designed to support other Rhino Compute endpoints in future releases.\n\n## Quickstart\n\nEvery solve in `@selvajs/compute` goes through a **scheduler**. The scheduler\nhandles cancellation, retries, loading state, and (optionally) a response cache\n— things every real app needs and shouldn't have to rebuild.\n\n```ts\nimport { GrasshopperClient, TreeBuilder, GrasshopperResponseProcessor } from '@selvajs/compute';\n\nconst client = await GrasshopperClient.create({\n\tserverUrl: 'http://localhost:6500',\n\tapiKey: 'your-api-key'\n});\n\n// Configure the scheduler for your workload (see \"Configuring the scheduler\" below).\nconst scheduler = client.createScheduler({ mode: 'latest-wins', timeoutMs: 30_000 });\n\n// Inspect the definition's inputs once, build a data tree.\nconst io = await client.getIO('my-definition.gh');\nconst inputTree = TreeBuilder.fromInputParams(io.inputs);\n\n// Solve. Returns a Promise — call it as often as you like.\nconst result = await scheduler.solve('my-definition.gh', inputTree);\nconst { values } = new GrasshopperResponseProcessor(result).getValues();\n```\n\nWire the scheduler's state into your UI for spinners and disabled buttons:\n\n```ts\nscheduler.subscribe(() =\u003e {\n\tshowSpinner = scheduler.isSolving;\n\tdisableSubmit = scheduler.hasPending;\n});\n```\n\nAnd handle expected cancellations gracefully — when newer values supersede an\nin-flight solve, or when the user aborts:\n\n```ts\nscheduler.solve(definition, inputTree).catch((err) =\u003e {\n\tif (/superseded|aborted/i.test(err.message)) return; // expected, not an error\n\tshowError(err);\n});\n```\n\n## Configuring the scheduler\n\nThe scheduler is one API with two knobs that matter — `mode` and `timeoutMs` —\nplus a couple of optional ones. Pick the row that matches what the user is\ndoing in your UI:\n\n| Workload                          | `mode`          | `timeoutMs`      | `retry`           | Notes                                                                                                 |\n| --------------------------------- | --------------- | ---------------- | ----------------- | ----------------------------------------------------------------------------------------------------- |\n| **Slider scrubs / live previews** | `'latest-wins'` | `30_000`         | default           | Aborts in-flight solves when newer values arrive. Add `cache: { ttlMs: 60_000 }` for instant repeats. |\n| **Submit / long-running jobs**    | `'queue'`       | `0` (no timeout) | `{ attempts: 1 }` | Serial queue. Pass a caller `signal` so users can hit Cancel. Bump proxy idle timeouts (see below).   |\n| **Background / batch parallel**   | `'parallel'`    | `60_000`         | `{ attempts: 2 }` | Fires solves concurrently up to `maxConcurrent` (default 4).                                          |\n\nYou can create multiple schedulers from one client — typically one per UI\nsurface. They share the connection pool but their queues, cancel scopes, and\ncaches are independent:\n\n```ts\nconst previewScheduler = client.createScheduler({ mode: 'latest-wins', timeoutMs: 30_000 });\nconst submitScheduler = client.createScheduler({\n\tmode: 'queue',\n\ttimeoutMs: 0,\n\tretry: { attempts: 1 }\n});\n```\n\n### Cancellation\n\nPass a per-call `signal` to cancel just that solve, or call `cancelAll()` to\ncancel everything (e.g. on route change or component unmount):\n\n```ts\nconst ctrl = new AbortController();\nscheduler.solve(definition, tree, { signal: ctrl.signal });\n\n// Later:\nctrl.abort(); // cancel just this call\nscheduler.cancelAll(); // cancel everything in flight + pending\nscheduler.dispose(); // cancel everything and tear down the scheduler\n```\n\n### Long jobs behind a proxy\n\nCloudflare's default idle timeout is 100s; AWS ALB's is 60s; nginx is 60s.\nIf your Compute server is behind any of them, those values must be bumped\nbefore you can run long solves through the browser — the library cannot work\naround proxy timeouts.\n\nFor solves longer than ~2 minutes, the safer architecture is to run this\nlibrary **server-side** behind your own job queue (BullMQ / SQS / Cloud Tasks)\nand expose a status endpoint to the browser.\n\n## Requirements\n\n### Core Requirements\n\n- **Node.js** \u003e= 20\n- **three** \u003e= 0.179.0 (required for visualization features)\n\n### Rhino Compute Compatibility\n\n`@selvajs/compute` works with both standard Rhino Compute and enhanced versions:\n\n**Standard Rhino Compute** – The [official McNeel repository](https://github.com/mcneel/compute.rhino3d) works for basic Grasshopper solving with core features.\n\n**Enhanced Setup** (Recommended) – Unlock advanced features:\n\n1. **Selva Rhino Plugin** – Grasshopper plugin that simplifies building Three.js visualizations and exporting results directly from Grasshopper. [Download from Food4Rhino](https://www.food4rhino.com/en/app/selva?lang=en). Detailed documentation will be available when the Selva project is open-sourced.\n2. **Custom Compute Server** – Our [custom branch](https://github.com/VektorNode/compute.rhino3d) enables:\n   - **Input Grouping** – Organize inputs with the `groupName` property\n   - **Persistent IDs** – Uniquely identify inputs across definition changes using Grasshopper object GUIDs\n\n\u003e Features requiring the enhanced setup will be clearly marked in the documentation.\n\n## Troubleshooting\n\n### `Network error: Failed to fetch`\n\nThe browser couldn't reach the server. Check, in order:\n\n1. **Server is running** — `curl http://localhost:6500/healthcheck` should return\n   a 200.\n2. **CORS** — if your Compute server is on a different origin than your app,\n   the server must send `Access-Control-Allow-Origin`. Standard Rhino Compute\n   does **not** ship with CORS enabled; you'll need to put it behind a proxy\n   that adds the headers, or use the [VektorNode custom branch](https://github.com/VektorNode/compute.rhino3d).\n3. **Mixed content** — an HTTPS app can't fetch from an HTTP server. Either\n   serve Compute over HTTPS or develop locally on HTTP.\n4. **API key** — you'll see the same error if your `apiKey` is missing for a\n   server that requires one (the server typically returns 401 with no CORS\n   headers, which the browser surfaces as a network error).\n\n### Solves timing out before the server finishes (502 / 504 / aborted)\n\nThe bottleneck is almost always a proxy in front of Compute, not the library.\nCommon culprits:\n\n- **Cloudflare** — 100s idle timeout on free/pro plans (525s on enterprise).\n- **AWS ALB** — 60s default; raise via the `idle_timeout` attribute.\n- **nginx** — 60s default; set `proxy_read_timeout` and `proxy_send_timeout`.\n\nFor solves longer than ~2 minutes, prefer running this library **server-side**\nand exposing your own job-status endpoint to the browser. Direct\nbrowser → Compute is fine for short solves but fragile for long ones.\n\n### `Definition URL/content is required`\n\nYou called `client.solve('', tree)` or passed a `Uint8Array` of length 0.\nValidate your input before calling.\n\n### 401 vs 403\n\n- **401 Unauthorized** — `apiKey` (`RhinoComputeKey` header) is missing or\n  invalid. Standard Rhino Compute uses this scheme.\n- **403 Forbidden** — your `authToken` (Bearer) was rejected by an upstream\n  proxy/API gateway. The Compute server itself almost never returns 403.\n\nThe error message includes the response body excerpt so you usually get a hint\nfrom the server itself.\n\n### \"Superseded by newer solve\" errors flooding my console\n\nThat's the scheduler doing its job in `latest-wins` mode — every aborted slider\nsolve rejects with this message. Filter it out:\n\n```ts\nscheduler.solve(def, tree).catch((err) =\u003e {\n\tif (/superseded|aborted/i.test(err.message)) return; // expected, not an error\n\tshowError(err);\n});\n```\n\n### \"Failed to load three.js visualization module\"\n\nThe dynamic import of the visualization layer threw. Make sure `three` is\ninstalled (`npm install three`) — it's a peer dependency, not a direct one.\n\n## Acknowledgement\n\nThis library is built on production experience and draws from several official McNeel repositories. Where code has been adapted, it is clearly marked in the relevant files.\n\n**Key References:**\n\n- [compute.rhino3d.appserver](https://github.com/mcneel/compute.rhino3d.appserver) – Server implementation reference\n- [IO/Schema.cs](https://github.com/mcneel/compute.rhino3d/blob/8.x/src/compute.geometry/IO/Schema.cs) – Grasshopper API structure\n- [GrasshopperDefinition.cs](https://github.com/mcneel/compute.rhino3d/blob/8.x/src/compute.geometry/GrasshopperDefinition.cs) – Definition parsing logic\n- [computeclient_js](https://github.com/mcneel/computeclient_js) – JavaScript client implementation\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvektornode%2Fselva-compute","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvektornode%2Fselva-compute","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvektornode%2Fselva-compute/lists"}