{"id":20648696,"url":"https://github.com/webpod/zurk","last_synced_at":"2025-06-29T15:34:43.520Z","repository":{"id":228141422,"uuid":"772261051","full_name":"webpod/zurk","owner":"webpod","description":"A generic process spawner","archived":false,"fork":false,"pushed_at":"2025-06-22T16:11:11.000Z","size":517,"stargazers_count":27,"open_issues_count":1,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-06-22T17:24:25.599Z","etag":null,"topics":["child-process","exec","shell","spawn","zx"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/webpod.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","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-03-14T20:59:40.000Z","updated_at":"2025-06-22T16:11:14.000Z","dependencies_parsed_at":"2024-03-17T08:48:58.267Z","dependency_job_id":"59420d24-a48b-4470-9583-e12b1c2e3141","html_url":"https://github.com/webpod/zurk","commit_stats":null,"previous_names":["webpod/zurk"],"tags_count":43,"template":false,"template_full_name":null,"purl":"pkg:github/webpod/zurk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webpod%2Fzurk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webpod%2Fzurk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webpod%2Fzurk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webpod%2Fzurk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/webpod","download_url":"https://codeload.github.com/webpod/zurk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webpod%2Fzurk/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262619319,"owners_count":23338226,"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","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":["child-process","exec","shell","spawn","zx"],"created_at":"2024-11-16T17:10:00.093Z","updated_at":"2025-06-29T15:34:43.499Z","avatar_url":"https://github.com/webpod.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# zurk\n\n\u003csup\u003e\n– cute sounds but never friendly. \u003cbr/\u003e\n– eats all kinds of materials.\n\u003c/sup\u003e\n\n# 🔬🧫\n\n\u003e This subproject is a kind of experiment, addressed to the [google/zx/issues/589](https://github.com/google/zx/issues/589).\nJust a testing ground for verifying ideas and approaches aimed at improve the [zx](https://github.com/google/zx) architecture.\n\n## Concepts\n* **Layered** architecture:\n  * `spawn` builds a configurable exec context around the `node:child_process` API.\n  * `zurk` implements the API for sync and async executions.\n  * `x` provides the basic template-string API.\n* **Granularity**: the package provides several entry points to help user to choose the right level of abstraction and/or to assist with tree-shaking.\n* **Extensibility**: \n  * The context object at every layer is accessible fo modify.\n  * Typings are mostly represented by interfaces, so it's easy to tweak up if necessary. \n\n## Requirements\n* **OS**\n  * Linux\n  * MacOS\n  * Windows\n* **Runtime**\n  * Node.js \u003e= 6 (CJS)\n  * Node.js \u003e= 12 (ESM)\n  * Bun \u003e= 1.0.0\n  * Deno \u003e= 1.7.0, 2.x\n\n## Install\n```bash\nyarn add zurk\n```\n\n## API\n\n```ts\nimport {$, exec, zurk} from 'zurk'\n\nconst r1 = exec({sync: true, cmd: 'echo foo'})\nconst r2 = await zurk({sync: false, cmd: 'echo foo'})\nconst r3 = await $`echo foo`\n```\n\n## Proposals\n- [x] Promises in cmd literals\n```ts\nconst foo = $`echo foo`\nconst foobarbaz = (await $`echo ${foo} ${$`echo bar`} ${await $`echo baz`}`)\n```\n\n- [x] Both sync and async executions\n```ts\nconst p1 = $`echo foo`\nconst p2 = $({sync: true})`echo foo`\n\nconst o1 = (await p1).toString()  // foo\nconst o2 = await p1.stdout        // foo\nconst o3 = p2.stdout              // foo\n```\n\n- [x] Configurable input\n```ts\nconst input = '{\"name\": \"foo\"}'\nconst name = await $({input})`jq -r .name` // foo\n\nconst stdin = fs.createReadStream(path.join(fixtures, 'foo.json'))\nconst data = await $({stdin})`jq -r .data` // foo\n\nconst p = $`echo \"5\\\\n3\\\\n1\\\\n4\\\\n2\"`\nconst sorted = $({input: p})`sort`          // 1\\n2\\n3\\n4\\n5\n```\n\n- [x] Pipe literals\n```ts\nconst result = $`echo \"5\\\\n3\\\\n1\\\\n4\\\\n2\"`\n\nconst piped0 = result.pipe`sort | cat`     // '1\\n2\\n3\\n4\\n5'\nconst piped1 = result.pipe`sort`.pipe`cat` // ...\nconst piped2 = (await result).pipe`sort`\nconst piped3 = result.pipe($`sort`)\n```\n\n- [x] Pipe splitting\n```ts\nconst result = $`echo 1; sleep 1; echo 2; sleep 1; echo 3`\nconst piped1 = result.pipe`cat`\nlet piped2: any\n\nsetTimeout(() =\u003e {\n  piped2 = result.pipe`cat`\n}, 1500)\n\nawait piped1\nassert.equal((await piped1).toString(), '1\\n2\\n3')\nassert.equal((await piped2).toString(), '1\\n2\\n3')\n```\n\n- [x] Presets\n```ts\nconst $$ = $({sync: true, cmd: 'echo foo'})\nconst $$$ = $$({cmd: 'echo bar'})\n\nconst p1 = $$()           // foo\nconst p2 = $$$()          // bar\nconst p3 = $$`echo baz`   // baz\n```\n\n- [x] AbortController\n```ts\nconst ac = new AbortController()\nconst p = $({nothrow: true, ac})`sleep 10`\nsetTimeout(() =\u003e {\n  ac.signal.abort() // or just `p.abort()`\n}, 500)\n\nconst { error } = await p\nerror.message // 'The operation was aborted'\n```\n\n- [x] Stdout limit\n\n```ts\nimport {type TSpawnStore, $} from 'zurk'\n\nconst getFixedSizeArray = (size: number) =\u003e {\n  const arr: any[] = []\n  return new Proxy(arr, {\n    get: (target: any, prop) =\u003e\n      prop === 'push' \u0026\u0026 arr.length \u003e= size\n        ? () =\u003e {}\n        : target[prop]\n  })\n}\nconst store: TSpawnStore = {\n  stdout: getFixedSizeArray(1),\n  stderr: getFixedSizeArray(2),\n  stdall: getFixedSizeArray(0)\n}\n\nconst result = await $({store})`echo hello`\nresult.stdout // 'hello\\n'\nresult.stdall // ''\n```\n\n- [x] Built-in quote for bash and powershell\n```ts\nimport {quote, quotePwsh} from 'zurk'\n\nconst arg = 'foo bar'\n$({quote})`echo ${arg}`             // \"echo $'foo bar'\"\n$({quote: quotePwsh})`echo ${arg}`  // \"echo 'foo bar'\"\n```\n\n## License\n[MIT](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebpod%2Fzurk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwebpod%2Fzurk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebpod%2Fzurk/lists"}