diff --git a/package.json b/package.json
index 80ec52bc..ec5f3d23 100644
--- a/package.json
+++ b/package.json
@@ -54,6 +54,7 @@
"sql-formatter": "^15.7.3",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.2",
+ "tapimo": "0.0.1",
"unified": "^11.0.5",
"unplugin-auto-import": "^21.0.0",
"unplugin-icons": "^23.0.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index eacd4cbf..f9111441 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -120,6 +120,9 @@ importers:
tailwindcss:
specifier: ^4.2.2
version: 4.2.2
+ tapimo:
+ specifier: 0.0.1
+ version: 0.0.1(04687d79abcc92c1f3b5219c6c82df03)
unified:
specifier: ^11.0.5
version: 11.0.5
@@ -645,6 +648,12 @@ packages:
peerDependencies:
hono: ^4
+ '@hono/standard-validator@0.2.3':
+ resolution: {integrity: sha512-bp9vHu6Va6SfMHC3D4ZLBbT/woi+AZ9CRdTXQu3kLJuLh2W/Gb9UO4hijS+BQAGFXi4EGpXdetxpzwTAawSVeg==}
+ peerDependencies:
+ '@standard-schema/spec': ^1.0.0
+ hono: '>=3.9.0'
+
'@iconify-json/lucide@1.2.102':
resolution: {integrity: sha512-Dm3EEqu5NrmzyDMB2U1+8yroEj2/dB9V4KlH0m/szwwF/ofSf0cPaGTZqkd1aExXjCor+vU53ttRMCGuXf+/cg==}
@@ -1208,6 +1217,10 @@ packages:
resolution: {integrity: sha512-E6beEdTsJxUStxOmY1knQvSNJq6LTiXOsRX2WTrfmU6d/kiATn6IKkAU0kXtAZkaYCGU4UCEmBFHCMmNKn0JLA==}
engines: {node: '>=22'}
+ '@scalar/openapi-types@0.8.0':
+ resolution: {integrity: sha512-WmaxVSfvY5K/TwcG2B2TU1WOe1As1uc2s7myswtP6dBlcjU3hM08SApxv/jmyGaCE8t4gO5BBhmHY4pDUfmr2g==}
+ engines: {node: '>=22'}
+
'@scalar/openapi-types@0.9.1':
resolution: {integrity: sha512-gkGhSkxSzADaBiNg+ZAbJuwj+ZUmzP2Pg9CWZ7ZP+0fck2WjPeDDM7aAbouAm0aQQMF9xBjSPXSA9a/qTHYaTw==}
engines: {node: '>=22'}
@@ -1307,6 +1320,67 @@ packages:
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
engines: {node: '>=18'}
+ '@standard-community/standard-json@0.3.5':
+ resolution: {integrity: sha512-4+ZPorwDRt47i+O7RjyuaxHRK/37QY/LmgxlGrRrSTLYoFatEOzvqIc85GTlM18SFZ5E91C+v0o/M37wZPpUHA==}
+ peerDependencies:
+ '@standard-schema/spec': ^1.0.0
+ '@types/json-schema': ^7.0.15
+ '@valibot/to-json-schema': ^1.3.0
+ arktype: ^2.1.20
+ effect: ^3.16.8
+ quansync: ^0.2.11
+ sury: ^10.0.0
+ typebox: ^1.0.17
+ valibot: ^1.1.0
+ zod: ^3.25.0 || ^4.0.0
+ zod-to-json-schema: ^3.24.5
+ peerDependenciesMeta:
+ '@valibot/to-json-schema':
+ optional: true
+ arktype:
+ optional: true
+ effect:
+ optional: true
+ sury:
+ optional: true
+ typebox:
+ optional: true
+ valibot:
+ optional: true
+ zod:
+ optional: true
+ zod-to-json-schema:
+ optional: true
+
+ '@standard-community/standard-openapi@0.2.9':
+ resolution: {integrity: sha512-htj+yldvN1XncyZi4rehbf9kLbu8os2Ke/rfqoZHCMHuw34kiF3LP/yQPdA0tQ940y8nDq3Iou8R3wG+AGGyvg==}
+ peerDependencies:
+ '@standard-community/standard-json': ^0.3.5
+ '@standard-schema/spec': ^1.0.0
+ arktype: ^2.1.20
+ effect: ^3.17.14
+ openapi-types: ^12.1.3
+ sury: ^10.0.0
+ typebox: ^1.0.0
+ valibot: ^1.1.0
+ zod: ^3.25.0 || ^4.0.0
+ zod-openapi: ^4
+ peerDependenciesMeta:
+ arktype:
+ optional: true
+ effect:
+ optional: true
+ sury:
+ optional: true
+ typebox:
+ optional: true
+ valibot:
+ optional: true
+ zod:
+ optional: true
+ zod-openapi:
+ optional: true
+
'@standard-schema/spec@1.0.0':
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
@@ -3018,6 +3092,21 @@ packages:
resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==}
engines: {node: '>=12.0.0'}
+ hono-openapi@1.3.0:
+ resolution: {integrity: sha512-xDvCWpWEIv0weEmnl3EjRQzqbHIO8LnfzMuYOCmbuyE5aes6aXxLg4vM3ybnoZD5TiTUkA6PuRQPJs3R7WRBig==}
+ peerDependencies:
+ '@hono/standard-validator': ^0.2.0
+ '@standard-community/standard-json': ^0.3.5
+ '@standard-community/standard-openapi': ^0.2.9
+ '@types/json-schema': ^7.0.15
+ hono: ^4.8.3
+ openapi-types: ^12.1.3
+ peerDependenciesMeta:
+ '@hono/standard-validator':
+ optional: true
+ hono:
+ optional: true
+
hono@4.12.26:
resolution: {integrity: sha512-uyZtpnYxM9CmQ7QsQknM4zN8EftNqhON1qYeIKM0Se67CCEe2c44xyGURwB0axX2fBDu1dqHrHAc1hmNT8ITkw==}
engines: {node: '>=16.9.0'}
@@ -3070,6 +3159,11 @@ packages:
import-meta-resolve@4.2.0:
resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
+ incur@0.4.10:
+ resolution: {integrity: sha512-u35+Ba0oJOPQnAYxYPo5j91P/rI9kTtpDf/iJe6kOs335iguz8mYnVguex4NP6GmIP6uc7iqUWY5cmdc/0MwTw==}
+ engines: {node: '>=22'}
+ hasBin: true
+
incur@0.4.5:
resolution: {integrity: sha512-4WFlqm5e+IKNoX+lDIjjpebO8ResPx3vPUBChxNfnNo3oGp0ooo6piiiTspOMub2dq2mcG/AmlUq0ejuGfKobg==}
engines: {node: '>=22'}
@@ -3211,6 +3305,10 @@ packages:
khroma@2.1.0:
resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==}
+ kysely@0.28.17:
+ resolution: {integrity: sha512-nbD8lB9EB3wNdMhOCdx5Li8DxnLbvKByylRLcJ1h+4SkrowVeECAyZlyiKMThF7xFdRz0jSQ2MoJr+wXux2y0Q==}
+ engines: {node: '>=20.0.0'}
+
langium@4.2.2:
resolution: {integrity: sha512-JUshTRAfHI4/MF9dH2WupvjSXyn8JBuUEWazB8ZVJUtXutT0doDlAv1XKbZ1Pb5sMexa8FF4CFBc0iiul7gbUQ==}
engines: {node: '>=20.10.0', npm: '>=10.2.3'}
@@ -3578,6 +3676,9 @@ packages:
typescript:
optional: true
+ mitt@3.0.1:
+ resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+
mlly@1.8.2:
resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==}
@@ -3606,6 +3707,25 @@ packages:
hono:
optional: true
+ mppx@0.6.31:
+ resolution: {integrity: sha512-euHHDLjNa9DQdPSzpayRBhUb3YVGSNZhVAzoOPmjzqxcEb77ry59uaeSBsWr3IjSxqmpR45cB6aR0uiNSgO6kA==}
+ hasBin: true
+ peerDependencies:
+ '@modelcontextprotocol/sdk': '>=1.25.0'
+ elysia: '>=1'
+ express: '>=5'
+ hono: '>=4.12.18'
+ viem: '>=2.51.0'
+ peerDependenciesMeta:
+ '@modelcontextprotocol/sdk':
+ optional: true
+ elysia:
+ optional: true
+ express:
+ optional: true
+ hono:
+ optional: true
+
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -3782,6 +3902,9 @@ packages:
oniguruma-to-es@4.3.6:
resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==}
+ openapi-types@12.1.3:
+ resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
+
outvariant@1.4.0:
resolution: {integrity: sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==}
@@ -3793,6 +3916,22 @@ packages:
typescript:
optional: true
+ ox@0.14.25:
+ resolution: {integrity: sha512-8DoibKtxE8yw63Y2jjMhlbjaURev6WCx4QR4MWLusl2/qIaeTzMJMBIYIDl1KOF45+8H1Ur6eLTdPlUoO8PlRw==}
+ peerDependencies:
+ typescript: '>=5.4.0'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ ox@0.14.27:
+ resolution: {integrity: sha512-+xhLHo/f+f4BH121/1Pomm/1vgBBda1wYiFpTvjSo8o5OcEj76Pf1hGPJiepoYMTQoTm2SKdSBvWkFWk5l07PA==}
+ peerDependencies:
+ typescript: '>=5.4.0'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
ox@0.14.29:
resolution: {integrity: sha512-M5j87Ec4V99MQdRct/g09eWXW60g6zhHTUs1lr4deUtrPDnezBdCJTgKd7pxqTpSZBFveV0ALi9jMMuT1qKyNg==}
peerDependencies:
@@ -4319,6 +4458,9 @@ packages:
resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
engines: {node: '>=6'}
+ tapimo@0.0.1:
+ resolution: {integrity: sha512-JjBmtWEGjEnVyrObc9bsG0qnTeswSPLMaYexRKL7EpWwxP3QWkqHxDo16RLnBHeSdSv4xosSNIfbl24ik26EZw==}
+
tar@7.5.13:
resolution: {integrity: sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==}
engines: {node: '>=18'}
@@ -4378,6 +4520,9 @@ packages:
thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+ tidx.ts@0.1.1:
+ resolution: {integrity: sha512-XFoDuwCQwkERGRYB+QGQvzH//Y6kDknttWSts3durreALgkrKOCM9mUDfgRVg3zVisuQgx5RfvoJmO8x8oXm+g==}
+
time-span@5.1.0:
resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==}
engines: {node: '>=12'}
@@ -4606,6 +4751,14 @@ packages:
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+ viem@2.51.3:
+ resolution: {integrity: sha512-DA4EbrsvatzzLo6MwcWWiv6kI6dIr3I9HH9B6qsJaClN/s0AjIDUz5RIxl+VmGrovIUCcIvG8744yuGH7d37zw==}
+ peerDependencies:
+ typescript: '>=5.0.4'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
viem@2.53.1:
resolution: {integrity: sha512-FhfJ/SW73CVosiyVLmIMVgKDRKYV1AGCLzZoHYvmNayyVff63Qi1ocPCk59LqC/cNw244RbBJjHnmxqXkE7NpA==}
peerDependencies:
@@ -4720,6 +4873,26 @@ packages:
jsdom:
optional: true
+ vocs@2.2.5:
+ resolution: {integrity: sha512-BSRSu4EIeHsUTo3BFP6OW/a26F87mlFW6HQASqQAW3+wB52pQMo/6l8Ui3SmuMiIiUIbkzTEYRi765lXZ0aC6Q==}
+ hasBin: true
+ peerDependencies:
+ '@vocs/twoslash-rust': ^0.1.0
+ mermaid: ^11
+ react: ^19
+ react-dom: ^19
+ vite: ^8
+ waku: ^1.0.0-beta.3
+ peerDependenciesMeta:
+ '@vocs/twoslash-rust':
+ optional: true
+ mermaid:
+ optional: true
+ vite:
+ optional: true
+ waku:
+ optional: true
+
vocs@2.3.0:
resolution: {integrity: sha512-pWfoG3J2iUrllEKZi83TkR+53tXl9HAvZ47lPuwH/8r866Hp7B/zBbcaDF2hDK2BCpIsTcZb+EN+yh0ViHph7w==}
hasBin: true
@@ -5408,6 +5581,11 @@ snapshots:
dependencies:
hono: 4.12.26
+ '@hono/standard-validator@0.2.3(@standard-schema/spec@1.1.0)(hono@4.12.26)':
+ dependencies:
+ '@standard-schema/spec': 1.1.0
+ hono: 4.12.26
+
'@iconify-json/lucide@1.2.102':
dependencies:
'@iconify/types': 2.0.0
@@ -6049,6 +6227,8 @@ snapshots:
leven: 4.1.0
yaml: 2.9.0
+ '@scalar/openapi-types@0.8.0': {}
+
'@scalar/openapi-types@0.9.1': {}
'@scalar/openapi-upgrader@0.2.9':
@@ -6225,6 +6405,23 @@ snapshots:
'@sindresorhus/merge-streams@4.0.0': {}
+ '@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6)':
+ dependencies:
+ '@standard-schema/spec': 1.1.0
+ '@types/json-schema': 7.0.15
+ quansync: 0.2.11
+ optionalDependencies:
+ zod: 4.3.6
+ zod-to-json-schema: 3.25.2(zod@4.3.6)
+
+ '@standard-community/standard-openapi@0.2.9(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-schema/spec@1.1.0)(openapi-types@12.1.3)(zod@4.3.6)':
+ dependencies:
+ '@standard-community/standard-json': 0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6)
+ '@standard-schema/spec': 1.1.0
+ openapi-types: 12.1.3
+ optionalDependencies:
+ zod: 4.3.6
+
'@standard-schema/spec@1.0.0': {}
'@standard-schema/spec@1.1.0': {}
@@ -8018,6 +8215,16 @@ snapshots:
highlight.js@11.11.1: {}
+ hono-openapi@1.3.0(@hono/standard-validator@0.2.3(@standard-schema/spec@1.1.0)(hono@4.12.26))(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-community/standard-openapi@0.2.9(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-schema/spec@1.1.0)(openapi-types@12.1.3)(zod@4.3.6))(@types/json-schema@7.0.15)(hono@4.12.26)(openapi-types@12.1.3):
+ dependencies:
+ '@standard-community/standard-json': 0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6)
+ '@standard-community/standard-openapi': 0.2.9(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-schema/spec@1.1.0)(openapi-types@12.1.3)(zod@4.3.6)
+ '@types/json-schema': 7.0.15
+ openapi-types: 12.1.3
+ optionalDependencies:
+ '@hono/standard-validator': 0.2.3(@standard-schema/spec@1.1.0)(hono@4.12.26)
+ hono: 4.12.26
+
hono@4.12.26: {}
html-void-elements@3.0.0: {}
@@ -8067,6 +8274,16 @@ snapshots:
import-meta-resolve@4.2.0: {}
+ incur@0.4.10:
+ dependencies:
+ '@cfworker/json-schema': 4.1.1
+ '@modelcontextprotocol/server': 2.0.0-alpha.2(@cfworker/json-schema@4.1.1)
+ '@scalar/openapi-types': 0.8.0
+ '@toon-format/toon': 2.1.0
+ tokenx: 1.3.0
+ yaml: 2.9.0
+ zod: 4.4.3
+
incur@0.4.5:
dependencies:
'@cfworker/json-schema': 4.1.1
@@ -8168,6 +8385,8 @@ snapshots:
khroma@2.1.0: {}
+ kysely@0.28.17: {}
+
langium@4.2.2:
dependencies:
'@chevrotain/regexp-to-ast': 12.0.0
@@ -8813,6 +9032,8 @@ snapshots:
optionalDependencies:
typescript: 5.9.3
+ mitt@3.0.1: {}
+
mlly@1.8.2:
dependencies:
acorn: 8.17.0
@@ -8840,6 +9061,19 @@ snapshots:
transitivePeerDependencies:
- typescript
+ mppx@0.6.31(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6))(express@5.2.1)(hono@4.12.26)(typescript@5.9.3)(viem@2.51.3(typescript@5.9.3)(zod@4.4.3)):
+ dependencies:
+ incur: 0.4.10
+ ox: 0.14.27(typescript@5.9.3)(zod@4.4.3)
+ viem: 2.51.3(typescript@5.9.3)(zod@4.4.3)
+ zod: 4.4.3
+ optionalDependencies:
+ '@modelcontextprotocol/sdk': 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6)
+ express: 5.2.1
+ hono: 4.12.26
+ transitivePeerDependencies:
+ - typescript
+
ms@2.1.3: {}
mz@2.7.0:
@@ -8906,6 +9140,8 @@ snapshots:
regex: 6.1.0
regex-recursion: 6.0.2
+ openapi-types@12.1.3: {}
+
outvariant@1.4.0: {}
ox@0.14.20(typescript@5.9.3)(zod@4.3.6):
@@ -8938,6 +9174,36 @@ snapshots:
transitivePeerDependencies:
- zod
+ ox@0.14.25(typescript@5.9.3)(zod@4.4.3):
+ dependencies:
+ '@adraffy/ens-normalize': 1.11.1
+ '@noble/ciphers': 1.3.0
+ '@noble/curves': 1.9.1
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.2.3(typescript@5.9.3)(zod@4.4.3)
+ eventemitter3: 5.0.1
+ optionalDependencies:
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - zod
+
+ ox@0.14.27(typescript@5.9.3)(zod@4.4.3):
+ dependencies:
+ '@adraffy/ens-normalize': 1.11.1
+ '@noble/ciphers': 1.3.0
+ '@noble/curves': 1.9.1
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.2.3(typescript@5.9.3)(zod@4.4.3)
+ eventemitter3: 5.0.1
+ optionalDependencies:
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - zod
+
ox@0.14.29(typescript@5.9.3)(zod@4.3.6):
dependencies:
'@adraffy/ens-normalize': 1.11.1
@@ -9645,6 +9911,70 @@ snapshots:
tapable@2.3.3: {}
+ tapimo@0.0.1(04687d79abcc92c1f3b5219c6c82df03):
+ dependencies:
+ '@hono/node-server': 2.0.5(hono@4.12.26)
+ '@hono/standard-validator': 0.2.3(@standard-schema/spec@1.1.0)(hono@4.12.26)
+ hono: 4.12.26
+ hono-openapi: 1.3.0(@hono/standard-validator@0.2.3(@standard-schema/spec@1.1.0)(hono@4.12.26))(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-community/standard-openapi@0.2.9(@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(quansync@0.2.11)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6))(@standard-schema/spec@1.1.0)(openapi-types@12.1.3)(zod@4.3.6))(@types/json-schema@7.0.15)(hono@4.12.26)(openapi-types@12.1.3)
+ incur: 0.4.10
+ jose: 6.2.3
+ mppx: 0.6.31(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6))(express@5.2.1)(hono@4.12.26)(typescript@5.9.3)(viem@2.51.3(typescript@5.9.3)(zod@4.4.3))
+ nanoid: 5.1.15
+ ox: 0.14.27(typescript@5.9.3)(zod@4.4.3)
+ tidx.ts: 0.1.1(typescript@5.9.3)
+ viem: 2.51.3(typescript@5.9.3)(zod@4.4.3)
+ vocs: 2.2.5(@cfworker/json-schema@4.1.1)(@types/react@19.2.14)(@vue/compiler-sfc@3.5.38)(change-case@5.4.4)(idb-keyval@6.2.2)(mermaid@11.14.0)(react-dom@19.2.6(react@19.2.6))(react-server-dom-webpack@19.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(webpack@5.104.1(esbuild@0.27.7)))(react@19.2.6)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.16(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(waku@1.0.0-beta.0(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(react-dom@19.2.6(react@19.2.6))(react-server-dom-webpack@19.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(webpack@5.104.1(esbuild@0.27.7)))(react@19.2.6)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))
+ zod: 4.4.3
+ transitivePeerDependencies:
+ - '@cfworker/json-schema'
+ - '@date-fns/tz'
+ - '@modelcontextprotocol/sdk'
+ - '@remix-run/react'
+ - '@rolldown/plugin-babel'
+ - '@standard-community/standard-json'
+ - '@standard-community/standard-openapi'
+ - '@standard-schema/spec'
+ - '@svgx/core'
+ - '@tanstack/react-router'
+ - '@types/json-schema'
+ - '@types/react'
+ - '@vocs/twoslash-rust'
+ - '@vue/compiler-sfc'
+ - '@vue/composition-api'
+ - async-validator
+ - axios
+ - babel-plugin-react-compiler
+ - bufferutil
+ - change-case
+ - date-fns
+ - drauu
+ - elysia
+ - express
+ - idb-keyval
+ - jwt-decode
+ - mermaid
+ - next
+ - nprogress
+ - openapi-types
+ - qrcode
+ - react
+ - react-dom
+ - react-router
+ - react-router-dom
+ - react-server-dom-webpack
+ - rollup
+ - sortablejs
+ - supports-color
+ - svelte
+ - typescript
+ - universal-cookie
+ - utf-8-validate
+ - vite
+ - vue-template-compiler
+ - vue-template-es2015-compiler
+ - waku
+
tar@7.5.13:
dependencies:
'@isaacs/fs-minipass': 4.0.1
@@ -9678,6 +10008,16 @@ snapshots:
dependencies:
any-promise: 1.3.0
+ tidx.ts@0.1.1(typescript@5.9.3):
+ dependencies:
+ abitype: 1.2.3(typescript@5.9.3)(zod@4.4.3)
+ kysely: 0.28.17
+ mitt: 3.0.1
+ ox: 0.14.20(typescript@5.9.3)(zod@4.4.3)
+ zod: 4.4.3
+ transitivePeerDependencies:
+ - typescript
+
time-span@5.1.0:
dependencies:
convert-hrtime: 5.0.0
@@ -9907,6 +10247,23 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.3
+ viem@2.51.3(typescript@5.9.3)(zod@4.4.3):
+ dependencies:
+ '@noble/curves': 1.9.1
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.2.3(typescript@5.9.3)(zod@4.4.3)
+ isows: 1.0.7(ws@8.20.1)
+ ox: 0.14.25(typescript@5.9.3)(zod@4.4.3)
+ ws: 8.20.1
+ optionalDependencies:
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+ - zod
+
viem@2.53.1(typescript@5.9.3)(zod@4.3.6):
dependencies:
'@noble/curves': 1.9.1
@@ -9986,6 +10343,123 @@ snapshots:
transitivePeerDependencies:
- msw
+ vocs@2.2.5(@cfworker/json-schema@4.1.1)(@types/react@19.2.14)(@vue/compiler-sfc@3.5.38)(change-case@5.4.4)(idb-keyval@6.2.2)(mermaid@11.14.0)(react-dom@19.2.6(react@19.2.6))(react-server-dom-webpack@19.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(webpack@5.104.1(esbuild@0.27.7)))(react@19.2.6)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.16(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(waku@1.0.0-beta.0(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(react-dom@19.2.6(react@19.2.6))(react-server-dom-webpack@19.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(webpack@5.104.1(esbuild@0.27.7)))(react@19.2.6)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)):
+ dependencies:
+ '@base-ui/react': 1.6.0(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@codesandbox/sandpack-react': 2.20.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@hono/node-server': 2.0.5(hono@4.12.26)
+ '@iconify-json/lucide': 1.2.102
+ '@iconify-json/simple-icons': 1.2.77
+ '@iconify-json/vscode-icons': 1.2.58
+ '@iconify/types': 2.0.0
+ '@iconify/utils': 3.1.3
+ '@mdx-js/mdx': 3.1.1
+ '@mdx-js/react': 3.1.1(@types/react@19.2.14)(react@19.2.6)
+ '@mdx-js/rollup': 3.1.1(rollup@4.60.1)
+ '@modelcontextprotocol/sdk': 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.4.3)
+ '@remix-run/node-fetch-server': 0.13.3
+ '@scalar/api-client': 3.10.4(change-case@5.4.4)(idb-keyval@6.2.2)(tailwindcss@4.3.1)(typescript@5.9.3)
+ '@scalar/openapi-parser': 0.28.7
+ '@scalar/snippetz': 0.9.17
+ '@scalar/workspace-store': 0.54.4(typescript@5.9.3)
+ '@shikijs/rehype': 3.23.0
+ '@shikijs/transformers': 3.23.0
+ '@shikijs/twoslash': 3.23.0(typescript@5.9.3)
+ '@shikijs/types': 3.23.0
+ '@svgr/core': 8.1.0(typescript@5.9.3)
+ '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))
+ '@tailwindcss/vite': 4.3.0(vite@8.0.16(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))
+ '@takumi-rs/image-response': 0.62.8
+ '@takumi-rs/wasm': 0.62.8
+ '@vitejs/plugin-react': 6.0.2(vite@8.0.16(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))
+ '@vitejs/plugin-rsc': 0.5.27(react-dom@19.2.6(react@19.2.6))(react-server-dom-webpack@19.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(webpack@5.104.1(esbuild@0.27.7)))(react@19.2.6)(vite@8.0.16(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))
+ cac: 6.7.14
+ cva: class-variance-authority@0.7.1
+ debug: 4.4.3
+ esast-util-from-js: 2.0.1
+ estree-util-value-to-estree: 3.5.0
+ estree-util-visit: 2.0.0
+ extend: 3.0.2
+ github-slugger: 2.0.0
+ hono: 4.12.26
+ image-size: 2.0.2
+ lz-string: 1.5.0
+ magic-string: 0.30.21
+ mdast-util-from-markdown: 2.0.3
+ mdast-util-gfm: 3.1.0
+ mdast-util-mdx: 3.0.0
+ mdast-util-to-hast: 13.2.1
+ mdast-util-to-markdown: 2.1.2
+ mdast-util-to-string: 4.0.0
+ micromark-extension-gfm: 3.0.0
+ minisearch: 7.2.0
+ nuqs: 2.8.9(react@19.2.6)
+ react: 19.2.6
+ react-dom: 19.2.6(react@19.2.6)
+ react-error-boundary: 6.1.2(react@19.2.6)
+ rehype-autolink-headings: 7.1.0
+ rehype-slug: 6.0.0
+ rehype-stringify: 10.0.1
+ remark-directive: 4.0.0
+ remark-frontmatter: 5.0.0
+ remark-gfm: 4.0.1
+ remark-mdx: 3.1.1
+ remark-mdx-frontmatter: 5.2.0
+ remark-parse: 11.0.0
+ remark-rehype: 11.1.2
+ remark-stringify: 11.0.0
+ shiki: 3.23.0
+ sucrase: 3.35.1
+ tailwindcss: 4.3.1
+ tailwindcss-logical: 4.2.0(tailwindcss@4.3.1)
+ tsx: 4.21.0
+ twoslash: 0.3.8(typescript@5.9.3)
+ unified: 11.0.5
+ unist-util-visit: 5.1.0
+ unplugin-icons: 22.5.0(@svgr/core@8.1.0(typescript@5.9.3))(@vue/compiler-sfc@3.5.38)
+ urlpattern-polyfill: 10.1.0
+ vfile: 6.0.3
+ vite-plugin-arraybuffer: 0.1.4
+ vite-plugin-wasm: 3.6.0(vite@8.0.16(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))
+ yaml: 2.9.0
+ zod: 4.4.3
+ optionalDependencies:
+ mermaid: 11.14.0
+ vite: 8.0.16(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)
+ waku: 1.0.0-beta.0(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(react-dom@19.2.6(react@19.2.6))(react-server-dom-webpack@19.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(webpack@5.104.1(esbuild@0.27.7)))(react@19.2.6)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)
+ transitivePeerDependencies:
+ - '@cfworker/json-schema'
+ - '@date-fns/tz'
+ - '@remix-run/react'
+ - '@rolldown/plugin-babel'
+ - '@svgx/core'
+ - '@tanstack/react-router'
+ - '@types/react'
+ - '@vue/compiler-sfc'
+ - '@vue/composition-api'
+ - async-validator
+ - axios
+ - babel-plugin-react-compiler
+ - change-case
+ - date-fns
+ - drauu
+ - idb-keyval
+ - jwt-decode
+ - next
+ - nprogress
+ - qrcode
+ - react-router
+ - react-router-dom
+ - react-server-dom-webpack
+ - rollup
+ - sortablejs
+ - supports-color
+ - svelte
+ - typescript
+ - universal-cookie
+ - vue-template-compiler
+ - vue-template-es2015-compiler
+
vocs@2.3.0(@cfworker/json-schema@4.1.1)(@types/react@19.2.14)(@vue/compiler-sfc@3.5.38)(change-case@5.4.4)(idb-keyval@6.2.2)(mermaid@11.14.0)(react-dom@19.2.6(react@19.2.6))(react-server-dom-webpack@19.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(webpack@5.104.1(esbuild@0.27.7)))(react@19.2.6)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.16(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))(waku@1.0.0-beta.0(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(react-dom@19.2.6(react@19.2.6))(react-server-dom-webpack@19.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(webpack@5.104.1(esbuild@0.27.7)))(react@19.2.6)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)):
dependencies:
'@base-ui/react': 1.6.0(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 46a2117c..daf19a66 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -13,6 +13,7 @@ minimumReleaseAgeExclude:
- viem
- vocs
- vite
+ - tapimo
- es-module-lexer
- '@vocs/twoslash-rust'
- '@vocs/twoslash-rust-darwin-arm64'
diff --git a/src/pages/docs/api/json-rpc.mdx b/src/pages/docs/api/json-rpc.mdx
index 9cb0f52e..b32509e2 100644
--- a/src/pages/docs/api/json-rpc.mdx
+++ b/src/pages/docs/api/json-rpc.mdx
@@ -47,6 +47,25 @@ Use the Tempo JSON-RPC API with command-line tools, TypeScript clients, React ho
```
+
+ The [Tempo API Typed Client](/docs/api/typed-client) is Tempo's typed API client for TypeScript. Use it when you want one client for Tempo REST endpoints and the JSON-RPC passthrough, with typed route parameters, status narrowing, and Tempo API error envelopes.
+
+
+
+ ```ts [example.ts]
+ import { Client } from 'tapimo'
+
+ const client = Client.create({ apiKey: process.env.TEMPO_API_KEY })
+
+ const response = await client.rpc[':chain{mainnet|testnet|[0-9]+}?'].$post({
+ param: { chain: 'testnet' }, // [!code focus]
+ json: { jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] }, // [!code focus]
+ })
+
+ const body = await response.json() // [!code focus]
+ ```
+
+
[Wagmi](https://wagmi.sh/react/api/createConfig) provides React hooks and configuration helpers on top of Viem clients.
diff --git a/src/pages/docs/api/typed-client.mdx b/src/pages/docs/api/typed-client.mdx
new file mode 100644
index 00000000..79ea1fb0
--- /dev/null
+++ b/src/pages/docs/api/typed-client.mdx
@@ -0,0 +1,172 @@
+---
+title: Tempo API Typed Client
+description: Use the Tempo API Typed Client to call REST and JSON-RPC endpoints with autocomplete, API key headers, status narrowing, and error handling in TypeScript.
+---
+
+# Tempo API Typed Client
+
+Use the Tempo API Typed Client for endpoint autocomplete and full end-to-end type-safety across API parameters, response bodies, statuses, and errors.
+
+## Install Tempo API package
+
+Install the [`tapimo`](https://www.npmjs.com/package/tapimo) package with your package manager.
+
+:::code-group
+```bash [npm]
+npm install tapimo
+```
+```bash [pnpm]
+pnpm add tapimo
+```
+```bash [bun]
+bun add tapimo
+```
+:::
+
+## Create typed Tempo API client
+
+Create a Tempo API client with `Client.create`.
+
+```ts twoslash [client.ts]
+import { Client } from 'tapimo'
+
+const client = Client.create({
+ // API key sugar for the canonical `tempo-api-key` header.
+ apiKey: 'tempo_api_key',
+ // Override the API URL. Defaults to `Client.defaultUrl`.
+ url: 'https://api.tempo.xyz',
+ // Or pass custom headers directly.
+ headers: { authorization: 'Bearer tempo_api_key' },
+})
+```
+
+`Client.create()` also accepts Hono client options such as a custom `fetch` implementation. If you pass `apiKey`, the client merges it into the request headers as `tempo-api-key`.
+
+## Make type-safe Tempo API requests
+
+Requests are end-to-end type-safe across parameters, response bodies, statuses, and errors. For example, `GET /v1/tokens/:token` narrows the response body from the HTTP status:
+
+```ts twoslash [tokens.ts]
+import { Client } from 'tapimo'
+
+const client = Client.create()
+
+const response = await client.v1.tokens[':token'].$get({
+ param: { token: '0x20c0000000000000000000000000000000000000' },
+})
+
+// Narrow responses by status.
+if (response.status !== 200) {
+ response.status // status is now typed to non-200 series
+ // ^?
+ if (response.status === 404) {
+ // Handle 404 status specifically
+ const json = await response.json()
+ // ^?
+ json.error.code // which narrows to "token_not_found"
+ // ^?
+ } else if (response.status === 402) {
+ // Handle the payment challenge from `WWW-Authenticate`
+ const challenge = await response.text()
+ // ^?
+ } else {
+ // Handle validation, auth, rate-limit, or upstream errors
+ const json = await response.json()
+ // ^?
+ }
+
+ throw new Error('Request failed')
+}
+
+// Request is successfull
+response.status // Response is narrowed to 200
+// ^?
+// Get typed response body
+const token = await response.json()
+// ^?
+token.symbol
+// ^?
+```
+
+## Tempo API endpoint autocomplete
+
+Routes autocomplete directly on the client, so you can discover endpoints without leaving your editor:
+
+```ts twoslash [endpoints.ts]
+// @noErrors
+import { Client } from 'tapimo'
+
+const client = Client.create()
+
+const response1 = await client.v1.
+// ^|
+const response2 = await client.v1.transactions.
+// ^|
+```
+
+## Call Tempo JSON-RPC through Typed Client
+
+The Typed Client exposes the raw JSON-RPC passthrough as a typed route under `client.rpc`. The route key includes Hono's path pattern because the chain selector is optional:
+
+```ts twoslash [rpc.ts]
+// @noErrors
+import { Client } from 'tapimo'
+
+const client = Client.create()
+
+const response = await client.rpc[':chain{mainnet|testnet|[0-9]+}?'].$post({
+ // Use `undefined` for `/rpc`, or pass `mainnet`, `testnet`, or a numeric chain id.
+ param: { chain: 'testnet' },
+ json: {
+ jsonrpc: '2.0',
+ id: 1,
+ method: 'eth_blockNumber',
+ params: [],
+ },
+})
+
+if (response.status !== 200) {
+ const error = await response.json()
+ throw new Error(error.error.message)
+}
+
+const body = await response.json()
+if (!Array.isArray(body) && body.error)
+ throw new Error(body.error.message)
+
+const blockNumber = Array.isArray(body) ? body[0]?.result : body.result
+```
+
+Chain selectors map to these hosted endpoints:
+
+| `chain` value | Hosted RPC path |
+| --- | --- |
+| `undefined` | `https://api.tempo.xyz/rpc` |
+| `'mainnet'` | `https://api.tempo.xyz/rpc/mainnet` |
+| `'testnet'` | `https://api.tempo.xyz/rpc/testnet` |
+| `'4217'` | `https://api.tempo.xyz/rpc/4217` |
+
+### Send batch JSON-RPC requests
+
+The JSON-RPC body type accepts one request or an array of requests. The client narrows the success body to the same single-or-batch response union:
+
+```ts twoslash [batch-rpc.ts]
+// @noErrors
+import { Client } from 'tapimo'
+
+const client = Client.create()
+
+const response = await client.rpc[':chain{mainnet|testnet|[0-9]+}?'].$post({
+ param: { chain: undefined },
+ json: [
+ { jsonrpc: '2.0', id: 1, method: 'eth_chainId', params: [] },
+ { jsonrpc: '2.0', id: 2, method: 'eth_blockNumber', params: [] },
+ ],
+})
+
+if (response.status === 200) {
+ const results = await response.json()
+ // results is typed as one JSON-RPC response or an array of JSON-RPC responses.
+}
+```
+
diff --git a/vocs.config.ts b/vocs.config.ts
index a06cc516..28e61813 100644
--- a/vocs.config.ts
+++ b/vocs.config.ts
@@ -223,6 +223,10 @@ export default defineConfig({
text: 'Versioning Policy',
link: '/docs/api/versioning-policy',
},
+ {
+ text: 'Typed Client',
+ link: '/docs/api/typed-client',
+ },
],
},
},