Compare commits
25 Commits
@hono/styt
...
main
Author | SHA1 | Date |
---|---|---|
|
9fd6473454 | |
|
368bd067d1 | |
|
363868b0b9 | |
|
95845a50ca | |
|
5178967931 | |
|
aee369e728 | |
|
848ecb889d | |
|
054257a200 | |
|
b62061faec | |
|
39a435b738 | |
|
074bb88b4f | |
|
69a278a2d3 | |
|
6ca78a14da | |
|
6378861217 | |
|
4cf126d38a | |
|
80481dd5ed | |
|
1baa0b281d | |
|
5cba9a4819 | |
|
acff470c7f | |
|
9235709060 | |
|
40f916f944 | |
|
a2409d2314 | |
|
cf3c17e3d1 | |
|
7c6a860814 | |
|
be73703184 |
|
@ -0,0 +1,73 @@
|
||||||
|
name: Sync robots.json
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Runs every day at midnight
|
||||||
|
- cron: '15 0 */3 * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-and-pr:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Node.js and Yarn
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
cache: 'yarn'
|
||||||
|
|
||||||
|
- run: yarn workspaces focus hono-middleware @hono/ua-blocker
|
||||||
|
|
||||||
|
- name: Fetch latest robots.json
|
||||||
|
run: yarn workspace @hono/ua-blocker getrobotstxt
|
||||||
|
|
||||||
|
- name: Generate data
|
||||||
|
run: yarn workspace @hono/ua-blocker prebuild
|
||||||
|
|
||||||
|
- name: Format
|
||||||
|
run: yarn prettier --write . !packages packages/ua-blocker
|
||||||
|
|
||||||
|
- name: Check for changes
|
||||||
|
id: changes
|
||||||
|
run: |
|
||||||
|
if [[ -n $(git status --porcelain) ]]; then
|
||||||
|
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Generate changeset
|
||||||
|
if: steps.changes.outputs.has_changes == 'true'
|
||||||
|
run: |
|
||||||
|
# Use a static changeset filename to avoid duplicates
|
||||||
|
CHANGESET_FILE=".changeset/auto-sync-robots.md"
|
||||||
|
|
||||||
|
# Create the changeset file
|
||||||
|
cat << EOF > "$CHANGESET_FILE"
|
||||||
|
---
|
||||||
|
'@hono/ua-blocker': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
chore(ua-blocker): sync \`robots.json\` with upstream
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Create Pull Request if changes exist
|
||||||
|
if: steps.changes.outputs.has_changes == 'true'
|
||||||
|
uses: peter-evans/create-pull-request@v6
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
commit-message: 'chore(ua-blocker): update robots.json from upstream'
|
||||||
|
title: 'chore(ua-blocker): update robots.json from upstream'
|
||||||
|
body: 'This PR was automatically created after detecting changes in the upstream `robots.json` file.'
|
||||||
|
branch: 'chore/sync-robots-json'
|
||||||
|
delete-branch: true
|
||||||
|
# Assignee and labels
|
||||||
|
assignees: finxol
|
||||||
|
reviewers: finxol
|
||||||
|
labels: robots.json
|
|
@ -41,8 +41,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
- run: yarn workspaces focus hono-middleware @hono/${{ matrix.package }}
|
- run: yarn workspaces focus hono-middleware @hono/${{ matrix.package }}
|
||||||
- run: yarn workspaces foreach --topological --recursive --from @hono/${{ matrix.package }} run build
|
- run: yarn workspaces foreach --topological --recursive --from @hono/${{ matrix.package }} run publint
|
||||||
- run: yarn workspace @hono/${{ matrix.package }} publint
|
|
||||||
- run: yarn workspace @hono/${{ matrix.package }} typecheck
|
- run: yarn workspace @hono/${{ matrix.package }} typecheck
|
||||||
- run: yarn eslint packages/${{ matrix.package }}
|
- run: yarn eslint packages/${{ matrix.package }}
|
||||||
- run: yarn prettier --check . !packages packages/${{ matrix.package }}
|
- run: yarn prettier --check . !packages packages/${{ matrix.package }}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
name: cr
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
tags: ['!**'] # Avoid publishing on tags
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, labeled] # Run on PR creation, updates, and when labels are added
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.number }} # Concurrency group for each PR
|
||||||
|
cancel-in-progress: true # Cancel in progress builds for the same PR
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
if: github.repository == 'honojs/middleware' && (github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'cr-tracked'))
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: 'Publish: pkg.pr.new'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20.x
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: yarn
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: yarn workspaces foreach --all --topological --parallel --no-private run build
|
||||||
|
|
||||||
|
- name: Publish to StackBlitz
|
||||||
|
run: yarn pkg-pr-new publish --compact --no-template './packages/*'
|
|
@ -20,3 +20,6 @@ sandbox
|
||||||
# Claude Code local files
|
# Claude Code local files
|
||||||
CLAUDE.local.md
|
CLAUDE.local.md
|
||||||
settings.local.json
|
settings.local.json
|
||||||
|
|
||||||
|
# Code editor
|
||||||
|
.zed
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
.changeset
|
.changeset
|
||||||
.vscode
|
.vscode
|
||||||
.yarn
|
.yarn
|
||||||
|
|
||||||
|
# Casbin
|
||||||
|
*.conf
|
||||||
|
*.csv
|
||||||
|
|
||||||
|
**/generated.ts
|
||||||
|
|
17
package.json
17
package.json
|
@ -8,8 +8,8 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn workspaces foreach --all --topological --verbose run build",
|
"build": "yarn workspaces foreach --all --topological --parallel --verbose run build",
|
||||||
"publint": "yarn workspaces foreach --all --topological --verbose run publint",
|
"publint": "yarn workspaces foreach --all --topological --parallel --verbose run publint",
|
||||||
"publish": "yarn workspaces foreach --all --no-private --topological --verbose npm publish --tolerate-republish && changeset tag",
|
"publish": "yarn workspaces foreach --all --no-private --topological --verbose npm publish --tolerate-republish && changeset tag",
|
||||||
"typecheck": "yarn tsc --build",
|
"typecheck": "yarn tsc --build",
|
||||||
"typecheck:clean": "yarn tsc --build --clean",
|
"typecheck:clean": "yarn tsc --build --clean",
|
||||||
|
@ -17,8 +17,8 @@
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"lint": "eslint 'packages/**/*.{ts,tsx}'",
|
"lint": "eslint 'packages/**/*.{ts,tsx}'",
|
||||||
"lint:fix": "eslint --fix 'packages/**/*.{ts,tsx}'",
|
"lint:fix": "eslint --fix 'packages/**/*.{ts,tsx}'",
|
||||||
"format": "prettier --check 'packages/**/*.{ts,tsx}'",
|
"format": "prettier --check .",
|
||||||
"format:fix": "prettier --write 'packages/**/*.{ts,tsx}'"
|
"format:fix": "prettier --write ."
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
@ -29,20 +29,21 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@changesets/changelog-github": "^0.4.8",
|
"@changesets/changelog-github": "^0.4.8",
|
||||||
"@changesets/cli": "^2.26.0",
|
"@changesets/cli": "^2.26.0",
|
||||||
"@cloudflare/vitest-pool-workers": "^0.7.8",
|
"@cloudflare/vitest-pool-workers": "^0.8.38",
|
||||||
"@cloudflare/workers-types": "^4.20230307.0",
|
"@cloudflare/workers-types": "^4.20250612.0",
|
||||||
"@hono/eslint-config": "workspace:*",
|
"@hono/eslint-config": "workspace:*",
|
||||||
"@ryoppippi/unplugin-typia": "^1.2.0",
|
"@ryoppippi/unplugin-typia": "^1.2.0",
|
||||||
"@types/bun": "^1.0.0",
|
"@types/bun": "^1.0.0",
|
||||||
"@types/node": "^20.17.28",
|
"@types/node": "^20.17.28",
|
||||||
"@types/ws": "^8.18.0",
|
"@types/ws": "^8.18.0",
|
||||||
"@vitest/coverage-istanbul": "^3.0.8",
|
"@vitest/coverage-istanbul": "^3.2.4",
|
||||||
"eslint": "^9.23.0",
|
"eslint": "^9.23.0",
|
||||||
"hono": "^4.7.11",
|
"hono": "^4.7.11",
|
||||||
|
"pkg-pr-new": "^0.0.51",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@4.9.2"
|
"packageManager": "yarn@4.9.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,6 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/ajv-validator",
|
"outDir": "../../dist/packages/ajv-validator",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,6 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/arktype-validator",
|
"outDir": "../../dist/packages/arktype-validator",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.4.0"
|
"node": ">=18.4.0"
|
||||||
|
|
|
@ -1,14 +1,7 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true,
|
|
||||||
"jsx": "react"
|
"jsx": "react"
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,14 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/auth-js",
|
"outDir": "../../dist/packages/auth-js",
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "react"
|
"jsx": "react"
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,6 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,7 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true,
|
|
||||||
"types": ["node", "bun"]
|
"types": ["node", "bun"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/hello",
|
"outDir": "../../dist/packages/hello",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.14.1"
|
"node": ">=18.14.1"
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/bun-transpiler",
|
"outDir": "../../dist/packages/bun-transpiler",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,6 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["./src/**/*.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/casbin",
|
"outDir": "../../dist/packages/casbin",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts", "vitest.setup.ts"],
|
"include": ["src", "vitest.config.ts", "vitest.setup.ts"],
|
||||||
"references": [
|
"references": []
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/class-validator",
|
"outDir": "../../dist/packages/class-validator",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
# @hono/clerk-auth
|
# @hono/clerk-auth
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
|
### Major Changes
|
||||||
|
|
||||||
|
- [#1190](https://github.com/honojs/middleware/pull/1190) [`6ca78a14dad67d7920a7c70bbfdb752eeaaff9ad`](https://github.com/honojs/middleware/commit/6ca78a14dad67d7920a7c70bbfdb752eeaaff9ad) Thanks [@wobsoriano](https://github.com/wobsoriano)! - Move `@clerk/backend` from peerDependencies to dependencies and bump to `2.x.x`.
|
||||||
|
|
||||||
|
This change ensures the package is directly available without requiring consumers to install it separately. The version bump includes the upcoming machine authentication feature while maintaining backward compatibility.
|
||||||
|
|
||||||
## 2.0.1
|
## 2.0.1
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -9,7 +9,7 @@ This middleware can be used to inject the active Clerk session into the request
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
npm i hono @hono/clerk-auth @clerk/backend
|
npm i hono @hono/clerk-auth
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/clerk-auth",
|
"name": "@hono/clerk-auth",
|
||||||
"version": "2.0.1",
|
"version": "3.0.0",
|
||||||
"description": "A third-party Clerk auth middleware for Hono",
|
"description": "A third-party Clerk auth middleware for Hono",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.cjs",
|
"main": "dist/index.cjs",
|
||||||
|
@ -39,19 +39,21 @@
|
||||||
"directory": "packages/clerk-auth"
|
"directory": "packages/clerk-auth"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/honojs/middleware",
|
"homepage": "https://github.com/honojs/middleware",
|
||||||
|
"dependencies": {
|
||||||
|
"@clerk/backend": "^2.1.0",
|
||||||
|
"@clerk/types": "^4.60.1"
|
||||||
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@clerk/backend": "^1.0.0",
|
|
||||||
"hono": ">=3.*"
|
"hono": ">=3.*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@arethetypeswrong/cli": "^0.17.4",
|
"@arethetypeswrong/cli": "^0.17.4",
|
||||||
"@clerk/backend": "^1.0.0",
|
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.x.x"
|
"node": ">=16.x.x"
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
import { createClerkClient } from '@clerk/backend'
|
import { createClerkClient } from '@clerk/backend'
|
||||||
import type { ClerkClient, ClerkOptions } from '@clerk/backend'
|
import type { ClerkClient, SessionAuthObject } from '@clerk/backend'
|
||||||
|
import type { AuthenticateRequestOptions } from '@clerk/backend/internal'
|
||||||
|
import { TokenType } from '@clerk/backend/internal'
|
||||||
import type { Context, MiddlewareHandler } from 'hono'
|
import type { Context, MiddlewareHandler } from 'hono'
|
||||||
import { env } from 'hono/adapter'
|
import { env } from 'hono/adapter'
|
||||||
|
|
||||||
type ClerkAuth = ReturnType<Awaited<ReturnType<ClerkClient['authenticateRequest']>>['toAuth']>
|
|
||||||
|
|
||||||
declare module 'hono' {
|
declare module 'hono' {
|
||||||
interface ContextVariableMap {
|
interface ContextVariableMap {
|
||||||
clerk: ClerkClient
|
clerk: ClerkClient
|
||||||
clerkAuth: ClerkAuth
|
clerkAuth: () => SessionAuthObject | null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAuth = (c: Context): ClerkAuth => {
|
export const getAuth = (c: Context): SessionAuthObject | null => {
|
||||||
return c.get('clerkAuth')
|
const authFn = c.get('clerkAuth')
|
||||||
|
return authFn()
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClerkEnv = {
|
type ClerkEnv = {
|
||||||
|
@ -23,7 +24,9 @@ type ClerkEnv = {
|
||||||
CLERK_API_VERSION: string
|
CLERK_API_VERSION: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const clerkMiddleware = (options?: ClerkOptions): MiddlewareHandler => {
|
type ClerkMiddlewareOptions = Omit<AuthenticateRequestOptions, 'acceptsToken'>
|
||||||
|
|
||||||
|
export const clerkMiddleware = (options?: ClerkMiddlewareOptions): MiddlewareHandler => {
|
||||||
return async (c, next) => {
|
return async (c, next) => {
|
||||||
const clerkEnv = env<ClerkEnv>(c)
|
const clerkEnv = env<ClerkEnv>(c)
|
||||||
const { secretKey, publishableKey, apiUrl, apiVersion, ...rest } = options || {
|
const { secretKey, publishableKey, apiUrl, apiVersion, ...rest } = options || {
|
||||||
|
@ -52,6 +55,7 @@ export const clerkMiddleware = (options?: ClerkOptions): MiddlewareHandler => {
|
||||||
...rest,
|
...rest,
|
||||||
secretKey,
|
secretKey,
|
||||||
publishableKey,
|
publishableKey,
|
||||||
|
acceptsToken: TokenType.SessionToken,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (requestState.headers) {
|
if (requestState.headers) {
|
||||||
|
@ -66,7 +70,8 @@ export const clerkMiddleware = (options?: ClerkOptions): MiddlewareHandler => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.set('clerkAuth', requestState.toAuth())
|
// Options will be added soon
|
||||||
|
c.set('clerkAuth', () => requestState.toAuth())
|
||||||
c.set('clerk', clerkClient)
|
c.set('clerk', clerkClient)
|
||||||
|
|
||||||
await next()
|
await next()
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/clerk-auth",
|
"outDir": "../../dist/packages/clerk-auth",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,6 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,7 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true,
|
|
||||||
"types": ["@cloudflare/workers-types"]
|
"types": ["@cloudflare/workers-types"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/cloudflare-access",
|
"outDir": "../../dist/packages/cloudflare-access",
|
||||||
"types": ["vitest/globals"]
|
"types": ["@cloudflare/workers-types", "vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"valibot": "^0.36.0",
|
"valibot": "^0.36.0",
|
||||||
"vitest": "^3.0.8",
|
"vitest": "^3.2.4",
|
||||||
"yup": "^1.4.0",
|
"yup": "^1.4.0",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/conform-validator",
|
"outDir": "../../dist/packages/conform-validator",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,6 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/effect-validator",
|
"outDir": "../../dist/packages/effect-validator",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.14.1"
|
"node": ">=18.14.1"
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/esbuild-transpiler",
|
"outDir": "../../dist/packages/esbuild-transpiler",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/event-emitter",
|
"outDir": "../../dist/packages/event-emitter",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,6 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "dist",
|
|
||||||
"types": ["@cloudflare/workers-types"]
|
"types": ["@cloudflare/workers-types"]
|
||||||
},
|
},
|
||||||
"references": []
|
"references": []
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/firebase-auth",
|
"outDir": "../../dist/packages/firebase-auth",
|
||||||
"noEmit": true
|
"types": ["@cloudflare/workers-types", "vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"include": ["src", "firebase-tools.d.ts", "vitest.config.ts"],
|
||||||
"references": [
|
"references": []
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/graphql-server",
|
"name": "@hono/graphql-server",
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"repository": "git@github.com:honojs/middleware.git",
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/honojs/middleware.git",
|
||||||
|
"directory": "packages/graphql-server"
|
||||||
|
},
|
||||||
"author": "Minghe Huang <h.minghe@gmail.com>",
|
"author": "Minghe Huang <h.minghe@gmail.com>",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
@ -45,7 +49,7 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/graphql-server",
|
"outDir": "../../dist/packages/graphql-server",
|
||||||
"types": ["bun"]
|
"types": ["bun"]
|
||||||
},
|
},
|
||||||
"exclude": ["src/**/*.test.ts"],
|
"include": ["src", "bun_test"],
|
||||||
"include": ["bun_test", "vitest.config.ts"],
|
"exclude": ["node_modules", "src/**/*.test.ts"]
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/graphql-server",
|
"outDir": "../../dist/packages/graphql-server",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"exclude": ["bun_test"],
|
"exclude": ["node_modules", "bun_test"],
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,6 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/hello",
|
"outDir": "../../dist/packages/hello",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# @hono/mcp
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- [#1178](https://github.com/honojs/middleware/pull/1178) [`1baa0b281dd4170f0003f9353a5f4c33fdcca610`](https://github.com/honojs/middleware/commit/1baa0b281dd4170f0003f9353a5f4c33fdcca610) Thanks [@MathurAditya724](https://github.com/MathurAditya724)! - init release
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Hono MCP (Model Context Protocol)
|
||||||
|
|
||||||
|
Connect Hono with a Model Context Protocol (MCP) server over HTTP Streaming Transport.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
||||||
|
import { StreamableHTTPTransport } from '@hono/mcp'
|
||||||
|
import { Hono } from 'hono'
|
||||||
|
|
||||||
|
const app = new Hono()
|
||||||
|
|
||||||
|
// Your MCP server implementation
|
||||||
|
const mcpServer = new McpServer({
|
||||||
|
name: 'my-mcp-server',
|
||||||
|
version: '1.0.0',
|
||||||
|
})
|
||||||
|
|
||||||
|
app.all('/mcp', async (c) => {
|
||||||
|
const transport = new StreamableHTTPTransport()
|
||||||
|
await mcpServer.connect(transport)
|
||||||
|
return transport.handleRequest(c)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default app
|
||||||
|
```
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Aditya Mathur <https://github.com/mathuraditya724>
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
"name": "@hono/mcp",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "MCP Middleware for Hono",
|
||||||
|
"type": "module",
|
||||||
|
"module": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsup ./src/index.ts",
|
||||||
|
"prepack": "yarn build",
|
||||||
|
"publint": "attw --pack && publint",
|
||||||
|
"typecheck": "tsc -b tsconfig.json",
|
||||||
|
"test": "vitest"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
"import": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"types": "./dist/index.d.cts",
|
||||||
|
"default": "./dist/index.cjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"publishConfig": {
|
||||||
|
"registry": "https://registry.npmjs.org",
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/honojs/middleware.git",
|
||||||
|
"directory": "packages/mcp"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/honojs/middleware",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
||||||
|
"hono": "*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@arethetypeswrong/cli": "^0.17.4",
|
||||||
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
||||||
|
"publint": "^0.3.9",
|
||||||
|
"tsup": "^8.4.0",
|
||||||
|
"typescript": "^5.8.2",
|
||||||
|
"vitest": "^3.2.4",
|
||||||
|
"zod": "^3.25.34"
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,613 @@
|
||||||
|
/**
|
||||||
|
* @module
|
||||||
|
* MCP HTTP Streaming Helper for Hono.
|
||||||
|
*/
|
||||||
|
import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js'
|
||||||
|
import type {
|
||||||
|
EventStore,
|
||||||
|
StreamableHTTPServerTransportOptions,
|
||||||
|
} from '@modelcontextprotocol/sdk/server/streamableHttp.js'
|
||||||
|
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'
|
||||||
|
import {
|
||||||
|
isInitializeRequest,
|
||||||
|
isJSONRPCError,
|
||||||
|
isJSONRPCRequest,
|
||||||
|
isJSONRPCResponse,
|
||||||
|
JSONRPCMessageSchema,
|
||||||
|
} from '@modelcontextprotocol/sdk/types.js'
|
||||||
|
import type { JSONRPCMessage, RequestId } from '@modelcontextprotocol/sdk/types.js'
|
||||||
|
import type { Context } from 'hono'
|
||||||
|
import { HTTPException } from 'hono/http-exception'
|
||||||
|
import type { SSEStreamingApi } from 'hono/streaming'
|
||||||
|
import { streamSSE } from './streaming'
|
||||||
|
|
||||||
|
export class StreamableHTTPTransport implements Transport {
|
||||||
|
#started = false
|
||||||
|
#initialized = false
|
||||||
|
#onsessioninitialized?: (sessionId: string) => void
|
||||||
|
#sessionIdGenerator?: () => string
|
||||||
|
#eventStore?: EventStore
|
||||||
|
#enableJsonResponse = false
|
||||||
|
#standaloneSseStreamId = '_GET_stream'
|
||||||
|
#streamMapping = new Map<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
ctx: {
|
||||||
|
header: (name: string, value: string) => void
|
||||||
|
json: (data: unknown) => void
|
||||||
|
}
|
||||||
|
stream?: SSEStreamingApi
|
||||||
|
}
|
||||||
|
>()
|
||||||
|
#requestToStreamMapping = new Map<RequestId, string>()
|
||||||
|
#requestResponseMap = new Map<RequestId, JSONRPCMessage>()
|
||||||
|
|
||||||
|
sessionId?: string | undefined
|
||||||
|
onclose?: () => void
|
||||||
|
onerror?: (error: Error) => void
|
||||||
|
onmessage?: (message: JSONRPCMessage, extra?: { authInfo?: AuthInfo }) => void
|
||||||
|
|
||||||
|
constructor(options?: StreamableHTTPServerTransportOptions) {
|
||||||
|
this.#sessionIdGenerator = options?.sessionIdGenerator
|
||||||
|
this.#enableJsonResponse = options?.enableJsonResponse ?? false
|
||||||
|
this.#eventStore = options?.eventStore
|
||||||
|
this.#onsessioninitialized = options?.onsessioninitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the transport. This is required by the Transport interface but is a no-op
|
||||||
|
* for the Streamable HTTP transport as connections are managed per-request.
|
||||||
|
*/
|
||||||
|
async start(): Promise<void> {
|
||||||
|
if (this.#started) {
|
||||||
|
throw new Error('Transport already started')
|
||||||
|
}
|
||||||
|
this.#started = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles an incoming HTTP request, whether GET or POST
|
||||||
|
*/
|
||||||
|
async handleRequest(ctx: Context, parsedBody?: unknown): Promise<Response | undefined> {
|
||||||
|
switch (ctx.req.method) {
|
||||||
|
case 'GET':
|
||||||
|
return this.handleGetRequest(ctx)
|
||||||
|
case 'POST':
|
||||||
|
return this.handlePostRequest(ctx, parsedBody)
|
||||||
|
case 'DELETE':
|
||||||
|
return this.handleDeleteRequest(ctx)
|
||||||
|
default:
|
||||||
|
return this.handleUnsupportedRequest(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles GET requests for SSE stream
|
||||||
|
*/
|
||||||
|
private async handleGetRequest(ctx: Context) {
|
||||||
|
try {
|
||||||
|
// The client MUST include an Accept header, listing text/event-stream as a supported content type.
|
||||||
|
const acceptHeader = ctx.req.header('Accept')
|
||||||
|
if (!acceptHeader?.includes('text/event-stream')) {
|
||||||
|
throw new HTTPException(406, {
|
||||||
|
res: Response.json({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
error: {
|
||||||
|
code: -32000,
|
||||||
|
message: 'Not Acceptable: Client must accept text/event-stream',
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an Mcp-Session-Id is returned by the server during initialization,
|
||||||
|
// clients using the Streamable HTTP transport MUST include it
|
||||||
|
// in the Mcp-Session-Id header on all of their subsequent HTTP requests.
|
||||||
|
this.validateSession(ctx)
|
||||||
|
|
||||||
|
// After initialization, always include the session ID if we have one
|
||||||
|
if (this.sessionId !== undefined) {
|
||||||
|
ctx.header('mcp-session-id', this.sessionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
let streamId: string | ((stream: SSEStreamingApi) => Promise<string>) =
|
||||||
|
this.#standaloneSseStreamId
|
||||||
|
|
||||||
|
// Handle resumability: check for Last-Event-ID header
|
||||||
|
if (this.#eventStore) {
|
||||||
|
const lastEventId = ctx.req.header('last-event-id')
|
||||||
|
if (lastEventId) {
|
||||||
|
streamId = (stream) =>
|
||||||
|
this.#eventStore!.replayEventsAfter(lastEventId, {
|
||||||
|
send: async (eventId: string, message: JSONRPCMessage) => {
|
||||||
|
try {
|
||||||
|
await stream.writeSSE({
|
||||||
|
id: eventId,
|
||||||
|
event: 'message',
|
||||||
|
data: JSON.stringify(message),
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
this.onerror?.(new Error('Failed replay events'))
|
||||||
|
throw new HTTPException(500, {
|
||||||
|
message: 'Failed replay events',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there's already an active standalone SSE stream for this session
|
||||||
|
if (typeof streamId === 'string' && this.#streamMapping.get(streamId) !== undefined) {
|
||||||
|
// Only one GET SSE stream is allowed per session
|
||||||
|
throw new HTTPException(409, {
|
||||||
|
res: Response.json({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
error: {
|
||||||
|
code: -32000,
|
||||||
|
message: 'Conflict: Only one SSE stream is allowed per session',
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return streamSSE(ctx, async (stream) => {
|
||||||
|
const resolvedStreamId = typeof streamId === 'string' ? streamId : await streamId(stream)
|
||||||
|
|
||||||
|
// Assign the response to the standalone SSE stream
|
||||||
|
this.#streamMapping.set(resolvedStreamId, {
|
||||||
|
ctx,
|
||||||
|
stream,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Keep connection alive
|
||||||
|
const keepAlive = setInterval(() => {
|
||||||
|
if (!stream.closed) {
|
||||||
|
stream.writeSSE({ data: '', event: 'ping' }).catch(() => clearInterval(keepAlive))
|
||||||
|
}
|
||||||
|
}, 30000)
|
||||||
|
|
||||||
|
// Set up close handler for client disconnects
|
||||||
|
stream.onAbort(() => {
|
||||||
|
this.#streamMapping.delete(resolvedStreamId)
|
||||||
|
clearInterval(keepAlive)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof HTTPException) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onerror?.(error as Error)
|
||||||
|
|
||||||
|
// return JSON-RPC formatted error
|
||||||
|
throw new HTTPException(400, {
|
||||||
|
res: Response.json({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
error: {
|
||||||
|
code: -32700,
|
||||||
|
message: 'Parse error',
|
||||||
|
data: String(error),
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles POST requests containing JSON-RPC messages
|
||||||
|
*/
|
||||||
|
private async handlePostRequest(ctx: Context, parsedBody?: unknown) {
|
||||||
|
try {
|
||||||
|
// Validate the Accept header
|
||||||
|
const acceptHeader = ctx.req.header('Accept')
|
||||||
|
// The client MUST include an Accept header, listing both application/json and text/event-stream as supported content types.
|
||||||
|
if (
|
||||||
|
!acceptHeader?.includes('application/json') ||
|
||||||
|
!acceptHeader.includes('text/event-stream')
|
||||||
|
) {
|
||||||
|
throw new HTTPException(406, {
|
||||||
|
res: Response.json({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
error: {
|
||||||
|
code: -32000,
|
||||||
|
message:
|
||||||
|
'Not Acceptable: Client must accept both application/json and text/event-stream',
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const ct = ctx.req.header('Content-Type')
|
||||||
|
if (!ct?.includes('application/json')) {
|
||||||
|
throw new HTTPException(415, {
|
||||||
|
res: Response.json({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
error: {
|
||||||
|
code: -32000,
|
||||||
|
message: 'Unsupported Media Type: Content-Type must be application/json',
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const authInfo: AuthInfo | undefined = ctx.get('auth')
|
||||||
|
|
||||||
|
let rawMessage = parsedBody
|
||||||
|
if (rawMessage === undefined) {
|
||||||
|
rawMessage = await ctx.req.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
let messages: JSONRPCMessage[]
|
||||||
|
|
||||||
|
// handle batch and single messages
|
||||||
|
if (Array.isArray(rawMessage)) {
|
||||||
|
messages = rawMessage.map((msg) => JSONRPCMessageSchema.parse(msg))
|
||||||
|
} else {
|
||||||
|
messages = [JSONRPCMessageSchema.parse(rawMessage)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is an initialization request
|
||||||
|
// https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/lifecycle/
|
||||||
|
const isInitializationRequest = messages.some(isInitializeRequest)
|
||||||
|
if (isInitializationRequest) {
|
||||||
|
// If it's a server with session management and the session ID is already set we should reject the request
|
||||||
|
// to avoid re-initialization.
|
||||||
|
if (this.#initialized && this.sessionId !== undefined) {
|
||||||
|
throw new HTTPException(400, {
|
||||||
|
res: Response.json({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
error: {
|
||||||
|
code: -32600,
|
||||||
|
message: 'Invalid Request: Server already initialized',
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messages.length > 1) {
|
||||||
|
throw new HTTPException(400, {
|
||||||
|
res: Response.json({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
error: {
|
||||||
|
code: -32600,
|
||||||
|
message: 'Invalid Request: Only one initialization request is allowed',
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.sessionId = this.#sessionIdGenerator?.()
|
||||||
|
this.#initialized = true
|
||||||
|
|
||||||
|
// If we have a session ID and an onsessioninitialized handler, call it immediately
|
||||||
|
// This is needed in cases where the server needs to keep track of multiple sessions
|
||||||
|
if (this.sessionId && this.#onsessioninitialized) {
|
||||||
|
this.#onsessioninitialized(this.sessionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an Mcp-Session-Id is returned by the server during initialization,
|
||||||
|
// clients using the Streamable HTTP transport MUST include it
|
||||||
|
// in the Mcp-Session-Id header on all of their subsequent HTTP requests.
|
||||||
|
if (!isInitializationRequest) {
|
||||||
|
this.validateSession(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if it contains requests
|
||||||
|
const hasRequests = messages.some(isJSONRPCRequest)
|
||||||
|
|
||||||
|
if (!hasRequests) {
|
||||||
|
// handle each message
|
||||||
|
for (const message of messages) {
|
||||||
|
this.onmessage?.(message, { authInfo })
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it only contains notifications or responses, return 202
|
||||||
|
return ctx.body(null, 202)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasRequests) {
|
||||||
|
// The default behavior is to use SSE streaming
|
||||||
|
// but in some cases server will return JSON responses
|
||||||
|
const streamId = crypto.randomUUID()
|
||||||
|
|
||||||
|
if (!this.#enableJsonResponse && this.sessionId !== undefined) {
|
||||||
|
ctx.header('mcp-session-id', this.sessionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#enableJsonResponse) {
|
||||||
|
// Store the response for this request to send messages back through this connection
|
||||||
|
// We need to track by request ID to maintain the connection
|
||||||
|
const result = await new Promise<any>((resolve) => {
|
||||||
|
for (const message of messages) {
|
||||||
|
if (isJSONRPCRequest(message)) {
|
||||||
|
this.#streamMapping.set(streamId, {
|
||||||
|
ctx: {
|
||||||
|
header: ctx.header,
|
||||||
|
json: resolve,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
this.#requestToStreamMapping.set(message.id, streamId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle each message
|
||||||
|
for (const message of messages) {
|
||||||
|
this.onmessage?.(message, { authInfo })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return ctx.json(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return streamSSE(ctx, async (stream) => {
|
||||||
|
// Store the response for this request to send messages back through this connection
|
||||||
|
// We need to track by request ID to maintain the connection
|
||||||
|
for (const message of messages) {
|
||||||
|
if (isJSONRPCRequest(message)) {
|
||||||
|
this.#streamMapping.set(streamId, {
|
||||||
|
ctx,
|
||||||
|
stream,
|
||||||
|
})
|
||||||
|
this.#requestToStreamMapping.set(message.id, streamId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up close handler for client disconnects
|
||||||
|
stream.onAbort(() => {
|
||||||
|
this.#streamMapping.delete(streamId)
|
||||||
|
})
|
||||||
|
|
||||||
|
// handle each message
|
||||||
|
for (const message of messages) {
|
||||||
|
this.onmessage?.(message, { authInfo })
|
||||||
|
}
|
||||||
|
// The server SHOULD NOT close the SSE stream before sending all JSON-RPC responses
|
||||||
|
// This will be handled by the send() method when responses are ready
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof HTTPException) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onerror?.(error as Error)
|
||||||
|
|
||||||
|
// return JSON-RPC formatted error
|
||||||
|
throw new HTTPException(400, {
|
||||||
|
res: Response.json({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
error: {
|
||||||
|
code: -32700,
|
||||||
|
message: 'Parse error',
|
||||||
|
data: String(error),
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles DELETE requests to terminate sessions
|
||||||
|
*/
|
||||||
|
private async handleDeleteRequest(ctx: Context) {
|
||||||
|
this.validateSession(ctx)
|
||||||
|
|
||||||
|
await this.close()
|
||||||
|
|
||||||
|
return ctx.body(null, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles unsupported requests (PUT, PATCH, etc.)
|
||||||
|
*/
|
||||||
|
private handleUnsupportedRequest(ctx: Context) {
|
||||||
|
return ctx.json(
|
||||||
|
{
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
error: {
|
||||||
|
code: -32000,
|
||||||
|
message: 'Method not allowed.',
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 405,
|
||||||
|
headers: {
|
||||||
|
Allow: 'GET, POST, DELETE',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates session ID for non-initialization requests
|
||||||
|
* Returns true if the session is valid, false otherwise
|
||||||
|
*/
|
||||||
|
private validateSession(ctx: Context): boolean {
|
||||||
|
if (this.#sessionIdGenerator === undefined) {
|
||||||
|
// If the sessionIdGenerator ID is not set, the session management is disabled
|
||||||
|
// and we don't need to validate the session ID
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (!this.#initialized) {
|
||||||
|
// If the server has not been initialized yet, reject all requests
|
||||||
|
throw new HTTPException(400, {
|
||||||
|
res: Response.json({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
error: {
|
||||||
|
code: -32000,
|
||||||
|
message: 'Bad Request: Server not initialized',
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionId = ctx.req.header('mcp-session-id')
|
||||||
|
|
||||||
|
if (!sessionId) {
|
||||||
|
// Non-initialization requests without a session ID should return 400 Bad Request
|
||||||
|
throw new HTTPException(400, {
|
||||||
|
res: Response.json({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
error: {
|
||||||
|
code: -32000,
|
||||||
|
message: 'Bad Request: Mcp-Session-Id header is required',
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(sessionId)) {
|
||||||
|
throw new HTTPException(400, {
|
||||||
|
res: Response.json({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
error: {
|
||||||
|
code: -32000,
|
||||||
|
message: 'Bad Request: Mcp-Session-Id header must be a single value',
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sessionId !== this.sessionId) {
|
||||||
|
// Reject requests with invalid session ID with 404 Not Found
|
||||||
|
throw new HTTPException(404, {
|
||||||
|
res: Response.json({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
error: {
|
||||||
|
code: -32001,
|
||||||
|
message: 'Session not found',
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
async close(): Promise<void> {
|
||||||
|
// Close all SSE connections
|
||||||
|
|
||||||
|
for (const { stream } of this.#streamMapping.values()) {
|
||||||
|
stream?.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#streamMapping.clear()
|
||||||
|
|
||||||
|
// Clear any pending responses
|
||||||
|
this.#requestResponseMap.clear()
|
||||||
|
this.onclose?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
async send(message: JSONRPCMessage, options?: { relatedRequestId?: RequestId }): Promise<void> {
|
||||||
|
let requestId = options?.relatedRequestId
|
||||||
|
if (isJSONRPCResponse(message) || isJSONRPCError(message)) {
|
||||||
|
// If the message is a response, use the request ID from the message
|
||||||
|
requestId = message.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this message should be sent on the standalone SSE stream (no request ID)
|
||||||
|
// Ignore notifications from tools (which have relatedRequestId set)
|
||||||
|
// Those will be sent via dedicated response SSE streams
|
||||||
|
if (requestId === undefined) {
|
||||||
|
// For standalone SSE streams, we can only send requests and notifications
|
||||||
|
if (isJSONRPCResponse(message) || isJSONRPCError(message)) {
|
||||||
|
throw new Error(
|
||||||
|
'Cannot send a response on a standalone SSE stream unless resuming a previous client request'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const standaloneSse = this.#streamMapping.get(this.#standaloneSseStreamId)
|
||||||
|
|
||||||
|
if (standaloneSse === undefined) {
|
||||||
|
// The spec says the server MAY send messages on the stream, so it's ok to discard if no stream
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate and store event ID if event store is provided
|
||||||
|
let eventId: string | undefined
|
||||||
|
if (this.#eventStore) {
|
||||||
|
// Stores the event and gets the generated event ID
|
||||||
|
eventId = await this.#eventStore.storeEvent(this.#standaloneSseStreamId, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the message to the standalone SSE stream
|
||||||
|
return standaloneSse.stream?.writeSSE({
|
||||||
|
id: eventId,
|
||||||
|
event: 'message',
|
||||||
|
data: JSON.stringify(message),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the response for this request
|
||||||
|
const streamId = this.#requestToStreamMapping.get(requestId)
|
||||||
|
const response = this.#streamMapping.get(streamId!)
|
||||||
|
if (!streamId) {
|
||||||
|
throw new Error(`No connection established for request ID: ${String(requestId)}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.#enableJsonResponse) {
|
||||||
|
// For SSE responses, generate event ID if event store is provided
|
||||||
|
let eventId: string | undefined
|
||||||
|
|
||||||
|
if (this.#eventStore) {
|
||||||
|
eventId = await this.#eventStore.storeEvent(streamId, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
// Write the event to the response stream
|
||||||
|
await response.stream?.writeSSE({
|
||||||
|
id: eventId,
|
||||||
|
event: 'message',
|
||||||
|
data: JSON.stringify(message),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isJSONRPCResponse(message) || isJSONRPCError(message)) {
|
||||||
|
this.#requestResponseMap.set(requestId, message)
|
||||||
|
const relatedIds = Array.from(this.#requestToStreamMapping.entries())
|
||||||
|
.filter(([, streamId]) => this.#streamMapping.get(streamId) === response)
|
||||||
|
.map(([id]) => id)
|
||||||
|
|
||||||
|
// Check if we have responses for all requests using this connection
|
||||||
|
const allResponsesReady = relatedIds.every((id) => this.#requestResponseMap.has(id))
|
||||||
|
|
||||||
|
if (allResponsesReady) {
|
||||||
|
if (!response) {
|
||||||
|
throw new Error(`No connection established for request ID: ${String(requestId)}`)
|
||||||
|
}
|
||||||
|
if (this.#enableJsonResponse) {
|
||||||
|
// All responses ready, send as JSON
|
||||||
|
if (this.sessionId !== undefined) {
|
||||||
|
response.ctx.header('mcp-session-id', this.sessionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const responses = relatedIds.map((id) => this.#requestResponseMap.get(id)!)
|
||||||
|
|
||||||
|
response.ctx.json(responses.length === 1 ? responses[0] : responses)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
response.stream?.close()
|
||||||
|
}
|
||||||
|
// Clean up
|
||||||
|
for (const id of relatedIds) {
|
||||||
|
this.#requestResponseMap.delete(id)
|
||||||
|
this.#requestToStreamMapping.delete(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
import type { Context } from 'hono'
|
||||||
|
import { SSEStreamingApi } from 'hono/streaming'
|
||||||
|
|
||||||
|
let isOldBunVersion = (): boolean => {
|
||||||
|
// @ts-expect-error @types/bun is not installed
|
||||||
|
const version: string = typeof Bun !== 'undefined' ? Bun.version : undefined
|
||||||
|
if (version === undefined) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const result = version.startsWith('1.1') || version.startsWith('1.0') || version.startsWith('0.')
|
||||||
|
// Avoid running this check on every call
|
||||||
|
isOldBunVersion = () => result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const run = async (
|
||||||
|
stream: SSEStreamingApi,
|
||||||
|
cb: (stream: SSEStreamingApi) => Promise<void>,
|
||||||
|
onError?: (e: Error, stream: SSEStreamingApi) => Promise<void>
|
||||||
|
): Promise<void> => {
|
||||||
|
try {
|
||||||
|
await cb(stream)
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error && onError) {
|
||||||
|
await onError(e, stream)
|
||||||
|
|
||||||
|
await stream.writeSSE({
|
||||||
|
event: 'error',
|
||||||
|
data: e.message,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const contextStash: WeakMap<ReadableStream, Context> = new WeakMap<ReadableStream, Context>()
|
||||||
|
|
||||||
|
export const streamSSE = (
|
||||||
|
c: Context,
|
||||||
|
cb: (stream: SSEStreamingApi) => Promise<void>,
|
||||||
|
onError?: (e: Error, stream: SSEStreamingApi) => Promise<void>
|
||||||
|
): Response => {
|
||||||
|
const { readable, writable } = new TransformStream()
|
||||||
|
const stream = new SSEStreamingApi(writable, readable)
|
||||||
|
|
||||||
|
// Until Bun v1.1.27, Bun didn't call cancel() on the ReadableStream for Response objects from Bun.serve()
|
||||||
|
if (isOldBunVersion()) {
|
||||||
|
c.req.raw.signal.addEventListener('abort', () => {
|
||||||
|
if (!stream.closed) {
|
||||||
|
stream.abort()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// in bun, `c` is destroyed when the request is returned, so hold it until the end of streaming
|
||||||
|
contextStash.set(stream.responseReadable, c)
|
||||||
|
|
||||||
|
c.header('Transfer-Encoding', 'chunked')
|
||||||
|
c.header('Content-Type', 'text/event-stream')
|
||||||
|
c.header('Cache-Control', 'no-cache')
|
||||||
|
c.header('Connection', 'keep-alive')
|
||||||
|
|
||||||
|
run(stream, cb, onError)
|
||||||
|
|
||||||
|
return c.newResponse(stream.responseReadable)
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.build.json",
|
||||||
|
"compilerOptions": {},
|
||||||
|
"references": []
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.build.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/packages/mcp",
|
||||||
|
"types": ["vitest/globals"]
|
||||||
|
},
|
||||||
|
"references": []
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { defineProject } from 'vitest/config'
|
||||||
|
|
||||||
|
export default defineProject({
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
},
|
||||||
|
})
|
|
@ -47,7 +47,7 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@medley/router": "^0.2.1"
|
"@medley/router": "^0.2.1"
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/medley-router",
|
"outDir": "../../dist/packages/medley-router",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
# @hono/node-ws
|
# @hono/node-ws
|
||||||
|
|
||||||
|
## 1.2.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- [#1213](https://github.com/honojs/middleware/pull/1213) [`5178967931d9f9020892d69b9a929469be25c6a7`](https://github.com/honojs/middleware/commit/5178967931d9f9020892d69b9a929469be25c6a7) Thanks [@palmm](https://github.com/palmm)! - Return the WebSocketServer instance used for createNodeWebSocket
|
||||||
|
|
||||||
## 1.1.7
|
## 1.1.7
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/node-ws",
|
"name": "@hono/node-ws",
|
||||||
"version": "1.1.7",
|
"version": "1.2.0",
|
||||||
"description": "WebSocket helper for Node.js",
|
"description": "WebSocket helper for Node.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ws": "^8.17.0"
|
"ws": "^8.17.0"
|
||||||
|
|
|
@ -13,10 +13,11 @@ describe('WebSocket helper', () => {
|
||||||
let server: ServerType
|
let server: ServerType
|
||||||
let injectWebSocket: ReturnType<typeof createNodeWebSocket>['injectWebSocket']
|
let injectWebSocket: ReturnType<typeof createNodeWebSocket>['injectWebSocket']
|
||||||
let upgradeWebSocket: ReturnType<typeof createNodeWebSocket>['upgradeWebSocket']
|
let upgradeWebSocket: ReturnType<typeof createNodeWebSocket>['upgradeWebSocket']
|
||||||
|
let wss: ReturnType<typeof createNodeWebSocket>['wss']
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
app = new Hono()
|
app = new Hono()
|
||||||
;({ injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app }))
|
;({ injectWebSocket, upgradeWebSocket, wss } = createNodeWebSocket({ app }))
|
||||||
|
|
||||||
server = await new Promise<ServerType>((resolve) => {
|
server = await new Promise<ServerType>((resolve) => {
|
||||||
const server = serve({ fetch: app.fetch, port: 3030 }, () => resolve(server))
|
const server = serve({ fetch: app.fetch, port: 3030 }, () => resolve(server))
|
||||||
|
@ -286,4 +287,25 @@ describe('WebSocket helper', () => {
|
||||||
|
|
||||||
expect(await mainPromise).toBe(true)
|
expect(await mainPromise).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should return the wss used for the websocket helper', async () => {
|
||||||
|
let clientWs: WebSocket | null = null
|
||||||
|
const mainPromise = new Promise<void>((resolve) =>
|
||||||
|
wss.on('connection', (ws) => {
|
||||||
|
clientWs = ws
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
'/',
|
||||||
|
upgradeWebSocket(() => ({}))
|
||||||
|
)
|
||||||
|
new WebSocket('ws://localhost:3030/')
|
||||||
|
|
||||||
|
await mainPromise
|
||||||
|
|
||||||
|
expect(clientWs).toBeTruthy()
|
||||||
|
expect(wss.clients.size).toBe(1)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -17,6 +17,7 @@ export interface NodeWebSocket {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
injectWebSocket(server: Server | Http2Server | Http2SecureServer): void
|
injectWebSocket(server: Server | Http2Server | Http2SecureServer): void
|
||||||
|
wss: WebSocketServer
|
||||||
}
|
}
|
||||||
export interface NodeWebSocketInit {
|
export interface NodeWebSocketInit {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
@ -56,6 +57,7 @@ export const createNodeWebSocket = (init: NodeWebSocketInit): NodeWebSocket => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
wss,
|
||||||
injectWebSocket(server) {
|
injectWebSocket(server) {
|
||||||
server.on('upgrade', async (request, socket: Duplex, head) => {
|
server.on('upgrade', async (request, socket: Duplex, head) => {
|
||||||
const url = new URL(request.url ?? '/', init.baseUrl ?? 'http://localhost')
|
const url = new URL(request.url ?? '/', init.baseUrl ?? 'http://localhost')
|
||||||
|
|
|
@ -1,14 +1,7 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true,
|
|
||||||
"types": ["node", "ws"]
|
"types": ["node", "ws"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/node-ws",
|
"outDir": "../../dist/packages/node-ws",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.test.ts", "vitest.config.ts"],
|
"references": []
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.4.0"
|
"node": ">=18.4.0"
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {},
|
||||||
"rootDir": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"tsBuildInfoFile": "dist/tsconfig.build.tsbuildinfo",
|
|
||||||
"emitDeclarationOnly": false,
|
|
||||||
"isolatedDeclarations": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts"],
|
|
||||||
"exclude": ["**/*.test.ts"],
|
|
||||||
"references": []
|
"references": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
"files": [],
|
||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc/packages/oauth-providers",
|
"outDir": "../../dist/packages/oauth-providers",
|
||||||
"types": ["vitest/globals"]
|
"types": ["vitest/globals"]
|
||||||
},
|
},
|
||||||
"include": ["mocks.ts", "**/*.test.ts", "vitest.config.ts"],
|
"include": ["src", "mocks.ts", "vitest.config.ts"],
|
||||||
"references": [
|
"references": []
|
||||||
{
|
|
||||||
"path": "./tsconfig.build.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
"publint": "^0.3.9",
|
"publint": "^0.3.9",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.0.8"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"oauth4webapi": "^2.6.0"
|
"oauth4webapi": "^2.6.0"
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue