{"id":18893433,"url":"https://github.com/sentialx/stargate","last_synced_at":"2025-10-08T19:19:08.955Z","repository":{"id":244821073,"uuid":"816369828","full_name":"sentialx/stargate","owner":"sentialx","description":null,"archived":false,"fork":false,"pushed_at":"2024-06-17T15:58:11.000Z","size":8,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-24T05:37:09.591Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"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/sentialx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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}},"created_at":"2024-06-17T15:51:15.000Z","updated_at":"2024-06-17T15:58:15.000Z","dependencies_parsed_at":"2024-06-17T18:09:47.697Z","dependency_job_id":null,"html_url":"https://github.com/sentialx/stargate","commit_stats":null,"previous_names":["sentialx/stargate"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sentialx/stargate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sentialx%2Fstargate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sentialx%2Fstargate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sentialx%2Fstargate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sentialx%2Fstargate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sentialx","download_url":"https://codeload.github.com/sentialx/stargate/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sentialx%2Fstargate/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266657952,"owners_count":23963602,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"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":[],"created_at":"2024-11-08T08:13:39.709Z","updated_at":"2025-10-08T19:19:03.923Z","avatar_url":"https://github.com/sentialx.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Stargate\n\n## Node.js workers\n\n### Example usage\n\nLet's first define our tunnels between Worker and Main thread, there are two ways to do that.\nFirst one is to directly use `ContextTunnel` without any implementation-specific logic and manually define\nits specifics. Second one, is to use `WorkerTunnel` specifically designed for communication between Node's Workers.\n\n```typescript\nconst workerClassTunnel = new ContextTunnel\u003cWorkerClass\u003e(\"worker_class\");\nconst mainClassTunnel = new ContextTunnel\u003cMainClass\u003e(\"main\");\n\n// Separate library for WorkerPool, although this library\n// was designed with worker pools in mind too.\nconst workerPool = new WorkerPool(4);\n\nclass WorkerClass {\n  public myFunction(x: number): number {\n    const mainClass = mainClassTunnel.connect(workerPool.main.port);\n    return await mainClass.myMainFunction() + x;\n  }\n}\n\nclass MainClass {\n  public myMainFunction() {\n    return 2;\n  }\n}\n\nif (workerPool.isMain()) {\n  workerPool.spawn();\n\n  const worker = workerPool.getFreeWorker();\n\n  // We handle incoming tunnels on the worker object.\n  const gate = new WorkerGate(worker)\n    .incoming(mainClassTunnel, new MainClass());\n\n  // Establish a connection to the worker and get the resulting proxy object.\n  const myClass = myClassTunnel.connect(gate);\n\n  // Finally, make a call to the function defined in the worker.\n  await myClass.myFunction(2);\n} else {\n  const mainGate = new MainThreadGate(workerPool.main.port);\n\n  // Handle incoming tunnels on the parentPort object.\n  mainGate\n    .incoming(workerClassTunnel, new WorkerClass());\n}\n```\n\nOur `WorkerPool` library provides with a `TunneledWorkerPool` API which simplifies the process of creating a worker pool with tunneled communication.\n\n```typescript\nconst workerPool = new TunneledWorkerPool(4, {\n  main: MainClass,\n  worker: WorkerClass,\n});\n\nclass WorkerClass {\n  public myFunction(x: number): number {\n    const mainClass = workerPool.main.api();\n    return await mainClass.myMainFunction() + x;\n  }\n}\n\nclass MainClass {\n  public myMainFunction() {\n    return 2;\n  }\n}\n\nif (workerPool.isMain()) {\n  workerPool.spawn();\n\n  await workerPool.getFreeWorker().api().myFunction(2);\n}\n\n```\n\n## Electron renderer and main processes\n\nThis time, proxied objects cannot be used in context of Electron IPC, as the wrapped objects and functions reside in side-specific scopes, e.g. a function that does something with the main process,\nhas imports that are not available in the renderer process. They cannot be imported from both sides.\n\nThis is why `ContextTunnel` was implemented. A `ContextTunnel` defines a tunnel which is a specification of functions that can be invoked or handled by the other side. In some cases they also specify their behavior e.g. which methods are synchronous or asynchronous in `ElectronTunnel`.\n\nThe defined `ContextTunnel` is later used to establish a connection between two contexts using an appropriate `ContextGate`.\n\nA `ContextGate` is a side-specific implementation of a unidirectional communication channel between two contexts and represents a specific side that is currently being used.\n\nTypically, a `ContextTunnel` and its corresponding interface are defined in a shared file that can be safely imported by both sides.\n```typescript\nexport interface MyMainInterface {\n  myFunction: (x: number) =\u003e number;\n  mySyncFunction: (x: number) =\u003e number;\n}\n\nexport const tunnel = new ElectronTunnel\u003cMyMainInterface\u003e('my_portal', {\n  sync: ['mySyncFunction'],\n});\n\nexport const myClassTunnel = new ElectronTunnel\u003cMyClass\u003e('my_class_portal');\n```\n\nMain process example:\n```typescript\n// |ElectronMainHandler| is an interface that transforms a user-defined interface, \n// i.e. injects an Electron-specific |Electron.IPCMainEvent| to each function and makes them async.\nclass MyMainHandler implements ElectronMainHandler\u003cMyMainInterface\u003e {\n  public async myFunction(e: Electron.IPCMainEvent, x: number): Promise\u003cnumber\u003e {\n    return x + 1;\n  }\n\n  public mySyncFunction(e: Electron.IPCMainEvent, x: number): number {\n    return x + 2;\n  }\n}\n\nconst mainGate = new ElectronMainGate(ipcMain)\n\nmainGate\n  // Defines a handler for the tunnel, a handler is a special case of object which inherits from ContextHandler\n  // so that function calls have platform-specific metadata about the message being sent.\n  .incomingHandler(tunnel, new MyReceiverHandler())\n  // Exposes an object to the tunnel, so that it can be accessed from the renderer process as-is.\n  .incoming(myClassTunnel, new MyClass());\n\n// When tunnel is a ContextTunnel\ntunnel.connect(mainGate, new ElectronRendererGateAddress(webContents)).myFunction(1);\n// When tunnel is an ElectronTunnel\ntunnel.connect(mainGate, webContents).myFunction(1);\n\n```\n\nRenderer process example:\n```typescript\nimport { ipcRenderer } from 'electron';\n\n// |ElectronRendererGate| is a side-specific implementation of a |ContextGate| for communication \n// from the renderer process to the main process in Electron.\nconst rendererGate = new ElectronRendererGate(ipcRenderer);\n\n// |EstablishedTunnel| is an established unidirectional connection between both contexts,\n// which is essentialy a proxy to the main process functions.\nconst tunnelToMain: EstablishedTunnel = tunnel.connect(rendererGate);\n// Finally, a type-safe call to a function defined in the main process.\nawait tunnelToMain.myFunction(1);\n\nconst myClass = myClassTunnel.connect(rendererGate);\n\nawait myClass.add2Numbers(1, 2);\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsentialx%2Fstargate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsentialx%2Fstargate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsentialx%2Fstargate/lists"}