{"id":51341156,"url":"https://github.com/rajtik76/poe2-toolkit","last_synced_at":"2026-07-02T08:00:36.905Z","repository":{"id":366601810,"uuid":"1276941445","full_name":"rajtik76/poe2-toolkit","owner":"rajtik76","description":"Framework-agnostic Path of Exile 2 toolkit: shared GGPK access + passive-tree/item/gem extractors and tree renderers. Code only, no game data.","archived":false,"fork":false,"pushed_at":"2026-07-02T07:31:20.000Z","size":326,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-02T08:00:14.808Z","etag":null,"topics":["dds","extractor","game-data","ggpk","passive-tree","path-of-exile","poe2","react","skill-tree","typescript"],"latest_commit_sha":null,"homepage":"https://poe.rajtik.com/tree","language":"TypeScript","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/rajtik76.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"zenodo":null,"notice":"NOTICE.md","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-22T12:40:51.000Z","updated_at":"2026-07-02T07:31:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rajtik76/poe2-toolkit","commit_stats":null,"previous_names":["rajtik76/poe2-toolkit"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rajtik76/poe2-toolkit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rajtik76%2Fpoe2-toolkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rajtik76%2Fpoe2-toolkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rajtik76%2Fpoe2-toolkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rajtik76%2Fpoe2-toolkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rajtik76","download_url":"https://codeload.github.com/rajtik76/poe2-toolkit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rajtik76%2Fpoe2-toolkit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35038239,"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-07-02T02:00:06.368Z","response_time":173,"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":["dds","extractor","game-data","ggpk","passive-tree","path-of-exile","poe2","react","skill-tree","typescript"],"created_at":"2026-07-02T08:00:19.305Z","updated_at":"2026-07-02T08:00:36.897Z","avatar_url":"https://github.com/rajtik76.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# poe2-toolkit\n\n[![CI](https://github.com/rajtik76/poe2-toolkit/actions/workflows/ci.yml/badge.svg)](https://github.com/rajtik76/poe2-toolkit/actions/workflows/ci.yml)\n[![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)\n[![code only](https://img.shields.io/badge/data-none%20bundled-brightgreen.svg)](#code-only)\n\nA framework-agnostic toolkit for **Path of Exile 2** data. One shared layer for\nreading the official GGPK / patch server, a set of extractors that turn that data\ninto clean, typed output, and renderers for the passive tree.\n\nEverything here is **code**. None of it bundles game data or art.\n\n\u003e The only framework-agnostic, code-only **Path of Exile 2 passive tree renderer**\n\u003e on npm - a headless geometry core plus a React view, with the extractors that\n\u003e feed them. You extract the data at run time; nothing about the game is bundled.\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://poe.rajtik.com/tree\"\u003e\n    \u003cimg src=\"https://github.com/rajtik76/poe2-toolkit/releases/download/tree-react-v0.6.1/demo.gif\" alt=\"Path of Exile 2 passive tree renderer\" width=\"720\"\u003e\n  \u003c/a\u003e\n  \u003cbr\u003e\n  \u003ca href=\"https://poe.rajtik.com/tree\"\u003e\u003cb\u003e\u0026#9654; Try the live, interactive demo\u003c/b\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Why this exists\n\nPath of Exile 2's official passive-tree export is leaner than PoE1's - it ships\nthe nodes and edges but not the orbit, sprite, or zoom constants you need to draw\nthem, so rendering it faithfully is real work. The tools that do that today are\nfull applications (planners) that bundle a snapshot of game data and drift out of\ndate; the one widely-used typed data library, `pathofexile-dat`, extracts data\nbut does not render.\n\nThis toolkit fills that gap as **libraries, not an app**:\n\n- **Code only.** Nothing derived from the game is bundled - no data, no art, not\n  even test fixtures. You extract on your own machine from the official patch\n  server, so it stays current and legally clean.\n- **Headless core, thin view.** `tree-core` computes the geometry with zero\n  dependencies; `tree-react` only draws it and owns pan/zoom/clicks. Swap in your\n  own view layer without touching the engine.\n- **Typed and tested.** Real TypeScript types and golden / characterization tests\n  against the live format, published to npm under semver.\n\n## Quick start\n\nRender a clickable passive tree - core works out the geometry, React draws it:\n\n```sh\nnpm install @poe2-toolkit/tree-react @poe2-toolkit/tree-core\n```\n\n```tsx\nimport { useMemo, useState } from 'react';\nimport { buildScene } from '@poe2-toolkit/tree-core';\nimport { normalizeGggTree } from '@poe2-toolkit/tree-core/ggg';\nimport { TreeView } from '@poe2-toolkit/tree-react';\n\n// A tree `data.json` (from @poe2-toolkit/tree-extractor, or the one the live demo\n// publishes) -\u003e the engine's TreeData. Once per tree version.\nconst data = normalizeGggTree(rawTreeJson, '0_5');\n\nexport function Tree() {\n  const [allocated, setAllocated] = useState\u003cnumber[]\u003e([]);\n\n  // Geometry is the core's job: state in, a positioned Scene out. Rebuild on edits.\n  const scene = useMemo(\n    () =\u003e buildScene(data, { allocation: { classId: 0, allocated } }),\n    [allocated],\n  );\n\n  // Omit `resources` and you get a vector render - a real tree, no art to load.\n  return (\n    \u003cTreeView\n      scene={scene}\n      onNodeClick={(skill) =\u003e\n        setAllocated((a) =\u003e (a.includes(skill) ? a.filter((s) =\u003e s !== skill) : [...a, skill]))\n      }\n    /\u003e\n  );\n}\n```\n\nThat's the whole loop: **the core computes where everything goes, React draws it\nand reports clicks.** Add atlas `resources` for real art - see\n[`tree-react`](./packages/poe2-tree-react) for graphics and the full prop list.\n\n## Live demo and patch webhook\n\nA running instance of the passive tree built from these packages is live at\n**[poe.rajtik.com/tree](https://poe.rajtik.com/tree)**, the same `@poe2-toolkit/*`\ncore and React renderer in a real app.\n\nThe same site also runs a **public patch webhook**: subscribe a URL and you get a\nsigned `POST` the moment a new Path of Exile 2 client version is detected on GGG's\npatch server (`patch.pathofexile2.com`, polled every five minutes). No account and\nno polling on your side; deliveries are HMAC-SHA256 signed so you can verify\nthey're genuine, and retried with backoff. Full docs at\n**[poe.rajtik.com/patch-webhook](https://poe.rajtik.com/patch-webhook)**.\n\n```bash\n# Subscribe; your endpoint echoes the verification `challenge` to prove ownership.\ncurl -X POST https://poe.rajtik.com/api/patch/subscribers \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"url\":\"https://example.com/poe2-hook\"}'\n```\n\nOnce verified, every new patch triggers a `patch.released` delivery carrying the\n`version` and `released_at`. See the docs for signature verification, re-verify /\nunsubscribe endpoints, and retry behavior.\n\n## Packages\n\n| Package | What it does | Status |\n| --- | --- | --- |\n| [`@poe2-toolkit/ggpk`](./packages/poe2-ggpk) | Shared GGPK / patch-server access and the format decoders (images, stat descriptions) every extractor reuses. The only package that touches the network. | Ready |\n| [`@poe2-toolkit/tree-extractor`](./packages/poe2-tree-extractor) | Builds the passive-tree data and sprite atlases from a GGPK source. | Ready |\n| [`@poe2-toolkit/tree-core`](./packages/poe2-tree-core) | Headless geometry engine: tree data in, a fully positioned scene out. | Ready |\n| [`@poe2-toolkit/tree-react`](./packages/poe2-tree-react) | React renderer that draws what the core computed and owns pan/zoom/interaction. | Ready |\n| [`@poe2-toolkit/item-extractor`](./packages/poe2-item-extractor) | Builds item data and icons from a GGPK source. | Ready |\n| [`@poe2-toolkit/gem-extractor`](./packages/poe2-gem-extractor) | Builds gem data and icons from a GGPK source. | Ready |\n| [`@poe2-toolkit/rune-extractor`](./packages/poe2-rune-extractor) | Builds rune / soul-core data from a GGPK source (data only). | Ready |\n\n## How it fits together\n\n```\n                    @poe2-toolkit/ggpk          fetch + decode GGPK / patch server\n                          |                     (GgpkSource: table() / file())\n        +-----------+-----------+-----------+\n        v           v           v           v\n     tree-       item-       gem-        rune-       extraction: data in, typed data out\n   extractor   extractor   extractor   extractor\n        |\n        v\n   tree-core  --\u003e  tree-react                        rendering: scene in, pixels out\n```\n\nAcquisition happens once, in `@poe2-toolkit/ggpk`. Every extractor reads from the same\nsource, so nothing is downloaded twice, and the extractors themselves stay\nagnostic to where the bytes come from.\n\n## \u003ca name=\"code-only\"\u003e\u003c/a\u003eCode only - no game data\n\nThese packages ship code, not data. Each extractor either **returns formatted,\ntyped data** to the caller or, via its CLI, **writes to a configurable output\ndirectory**. Nothing derived from the game is stored in this repository: not\ndata, not art, not even test fixtures. The 1:1 verification fixtures live outside\nthe repo and are located at test time through environment variables.\n\n## Develop\n\nThis is an npm-workspaces monorepo.\n\n```sh\nnpm install        # link all packages\nnpm run build      # build in dependency order\nnpm run typecheck\nnpm run lint\nnpm test\n```\n\nThe unit tests run without any game data. The integration and 1:1 verification\ntests need a local GGPK extract and golden fixtures (both kept outside the repo);\npoint `POE2_GGPK_EXTRACT`, `POE2_TREE_GOLDEN`, and `POE2_TREE_DATA` at them to run\nthose locally. Without the variables, those tests skip.\n\n## Attributions and legal\n\nThis is an unofficial, fan-made project. It is **not** affiliated with, endorsed\nby, or sponsored by Grinding Gear Games.\n\n\"Path of Exile\" and \"Path of Exile 2\" are trademarks of Grinding Gear Games. All\ngame content, data, and art are the property of Grinding Gear Games. This toolkit\ncontains none of it; it reads data at run time from the official patch server\n(or your own game files) and hands back the decoded result.\n\nGGPK access builds on [`pathofexile-dat`](https://github.com/SnosMe/poe-dat-viewer)\n(MIT, © SnosMe). Full attribution is in [NOTICE](./NOTICE.md).\n\n**Thank you to Grinding Gear Games for making Path of Exile 2.**\n\n## License\n\nMIT. See [LICENSE](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frajtik76%2Fpoe2-toolkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frajtik76%2Fpoe2-toolkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frajtik76%2Fpoe2-toolkit/lists"}