Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions .github/actions/pack-sample/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,16 @@ description: >

inputs:
sdk:
description: SDK slug matching the samples/ subdirectory (e.g. react, browser, node)
description: SDK slug used to name the zip file (e.g. better-auth)
required: true
version:
description: Version being released (e.g. 1.2.3), used to name the zip file
required: true
path:
description: >
Sample app directory, relative to the repo root. Defaults to samples/<sdk>/quickstart;
pass this for a sample that doesn't follow that convention (e.g. an integration sample
under samples/integrations/<sdk>/<framework>).
required: false
default: ''
Sample app directory, relative to the repo root (e.g. samples/integrations/<sdk>/<framework>).
The quickstarts live in thunder-id/samples, which releases them itself.
required: true

outputs:
archive:
Expand All @@ -41,7 +39,7 @@ runs:
PACK_DIR="$(mktemp -d)"
trap 'rm -rf "$PACK_DIR"' EXIT

cd "${SAMPLE_PATH:-samples/${SDK}/quickstart}"
cd "${SAMPLE_PATH}"
pnpm pack --pack-destination "${PACK_DIR}"
cd "${DEST_DIR}"

Expand Down
51 changes: 28 additions & 23 deletions .github/actions/run-e2e-suite/action.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
name: Run E2E Suite
description: >
Installs and starts a local ThunderID release, imports the sample apps' OAuth2 clients, starts
all six sample apps against the workspace's own SDK packages, and runs the Playwright E2E suite
Installs and starts a local ThunderID release, imports the sample apps' OAuth2 clients, fetches
the quickstarts from thunder-id/samples, starts all six against the workspace's own SDK packages,
and runs the Playwright E2E suite
against them. Shared by pr-builder.yml's label-gated `e2e` job and nightly.yml's scheduled
run against the latest ThunderID release — the two differ only in trigger, not in what they run.
Requires the repo to already be checked out (a local composite action can't check itself out).
Expand Down Expand Up @@ -44,10 +45,16 @@ runs:

- name: 🔨 Build SDK Packages
shell: bash
# Sample apps consume workspace:* SDK packages — this is what actually puts the PR's
# changes under test.
# prepare-samples.ts packs these builds into the sample apps — this is what actually puts
# the PR's changes under test.
run: pnpm build

- name: 📥 Fetch Sample Apps
shell: bash
# Copies each quickstart from thunder-id/samples into tests/e2e/.samples and pins its
# @thunderid/* dependencies to tarballs of the packages built above.
run: node tests/e2e/scripts/prepare-samples.ts

- name: 🚀 Install, Set up, and Start ThunderID (Latest, via npx)
id: install
shell: bash
Expand Down Expand Up @@ -211,12 +218,10 @@ runs:
shell: bash
# Each app needs its own OAuth2 client wired in before it starts (see the OAuth2 clients
# imported above). The apps' own `prepare-dev.cjs --flow=redirect` helper looks like the
# natural fit for nextjs/nuxt but isn't: it rewrites the sample's checked-in package.json
# (workspace:* -> latest, defeating the point of testing this PR's own SDK changes) and
# blanks any value matching its own placeholders — including the literal string
# `https://localhost:8090`, which is also our real server URL. Writing each .env directly
# sidesteps both; variable names come from each app's own scripts/prepare-dev.cjs
# (native-flow-only vars for nextjs/nuxt are omitted).
# natural fit for nextjs/nuxt but isn't: it blanks any value matching its own placeholders,
# including the literal string `https://localhost:8090`, which is also our real server URL.
# Writing each .env directly sidesteps that; variable names come from each app's own
# scripts/prepare-dev.cjs (native-flow-only vars for nextjs/nuxt are omitted).
run: |
NEXTJS_SECRET=$(openssl rand -base64 32)
NUXT_SECRET=$(openssl rand -base64 32)
Expand All @@ -226,32 +231,32 @@ runs:
printf '%s\n' "$@" > "$file"
}

write_env samples/browser/quickstart/.env \
write_env tests/e2e/.samples/quickstarts/browser/.env \
"VITE_THUNDERID_BASE_URL=https://localhost:8090" \
"VITE_THUNDERID_CLIENT_ID=JS_SDK_E2E_BROWSER"

write_env samples/react/quickstart/.env \
write_env tests/e2e/.samples/quickstarts/react/.env \
"VITE_THUNDERID_BASE_URL=https://localhost:8090" \
"VITE_THUNDERID_CLIENT_ID=JS_SDK_E2E_REACT"

write_env samples/vue/quickstart/.env \
write_env tests/e2e/.samples/quickstarts/vue/.env \
"VITE_THUNDERID_BASE_URL=https://localhost:8090" \
"VITE_THUNDERID_CLIENT_ID=JS_SDK_E2E_VUE"

write_env samples/express/quickstart/.env \
write_env tests/e2e/.samples/quickstarts/express/.env \
"THUNDERID_BASE_URL=https://localhost:8090" \
"THUNDERID_CLIENT_ID=JS_SDK_E2E_EXPRESS" \
"THUNDERID_CLIENT_SECRET=e2e-express-secret" \
"NODE_TLS_REJECT_UNAUTHORIZED=0"

write_env samples/nextjs/quickstart/.env \
write_env tests/e2e/.samples/quickstarts/nextjs/.env \
"NEXT_PUBLIC_THUNDERID_BASE_URL=https://localhost:8090" \
"NEXT_PUBLIC_THUNDERID_CLIENT_ID=JS_SDK_E2E_NEXTJS" \
"THUNDERID_CLIENT_SECRET=e2e-nextjs-secret" \
"THUNDERID_SECRET=${NEXTJS_SECRET}" \
"NODE_TLS_REJECT_UNAUTHORIZED=0"

write_env samples/nuxt/quickstart/.env \
write_env tests/e2e/.samples/quickstarts/nuxt/.env \
"NUXT_PUBLIC_THUNDERID_BASE_URL=https://localhost:8090" \
"NUXT_PUBLIC_THUNDERID_CLIENT_ID=JS_SDK_E2E_NUXT" \
"THUNDERID_CLIENT_SECRET=e2e-nuxt-secret" \
Expand All @@ -261,12 +266,12 @@ runs:
- name: 🚀 Start Sample Apps
shell: bash
run: |
( cd samples/browser/quickstart && pnpm exec vite --port 5173 & )
( cd samples/react/quickstart && pnpm exec vite --port 5174 & )
( cd samples/vue/quickstart && pnpm exec vite --port 5175 & )
( cd samples/nextjs/quickstart && pnpm exec next dev -p 3001 & )
( cd samples/nuxt/quickstart && pnpm exec nuxt dev --port 3002 & )
( cd samples/express/quickstart && pnpm exec node --env-file-if-exists=.env index.mjs & )
( cd tests/e2e/.samples/quickstarts/browser && pnpm exec vite --port 5173 & )
( cd tests/e2e/.samples/quickstarts/react && pnpm exec vite --port 5174 & )
( cd tests/e2e/.samples/quickstarts/vue && pnpm exec vite --port 5175 & )
( cd tests/e2e/.samples/quickstarts/nextjs && pnpm exec next dev -p 3001 & )
( cd tests/e2e/.samples/quickstarts/nuxt && pnpm exec nuxt dev --port 3002 & )
( cd tests/e2e/.samples/quickstarts/express && pnpm exec node --env-file-if-exists=.env index.mjs & )
for url in http://localhost:5173 http://localhost:5174 http://localhost:5175 http://localhost:3000 http://localhost:3001 http://localhost:3002; do
ready=0
for i in $(seq 1 60); do
Expand Down Expand Up @@ -299,7 +304,7 @@ runs:
NEXTJS_APP_URL: http://localhost:3001
NUXT_APP_URL: http://localhost:3002
EXPRESS_APP_URL: http://localhost:3000
# node/quickstart has no page to drive — client-credentials.spec.ts spawns it directly
# quickstarts/node has no page to drive — client-credentials.spec.ts spawns it directly
# with `env: process.env`, so its credentials just need to be here, not a .env file.
THUNDERID_BASE_URL: https://localhost:8090
THUNDERID_CLIENT_ID: JS_SDK_E2E_NODE
Expand Down
70 changes: 7 additions & 63 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -330,21 +330,13 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: 📦 Pack Sample
id: pack
uses: ./.github/actions/pack-sample
with:
sdk: browser
version: ${{ steps.bump.outputs.version }}

- name: 🚀 Create GitHub Release
env:
GH_TOKEN: ${{ secrets.THUNDERID_AUTOMATION_BOT }}
run: |
gh release create "sdk/browser/v${{ steps.bump.outputs.version }}" \
--title "ThunderID Browser SDK v${{ steps.bump.outputs.version }}" \
--generate-notes \
"${{ steps.pack.outputs.archive }}"
--generate-notes

release-node:
name: 🟢 Release @thunderid/node
Expand Down Expand Up @@ -403,21 +395,13 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: 📦 Pack Sample
id: pack
uses: ./.github/actions/pack-sample
with:
sdk: node
version: ${{ steps.bump.outputs.version }}

- name: 🚀 Create GitHub Release
env:
GH_TOKEN: ${{ secrets.THUNDERID_AUTOMATION_BOT }}
run: |
gh release create "sdk/node/v${{ steps.bump.outputs.version }}" \
--title "ThunderID Node SDK v${{ steps.bump.outputs.version }}" \
--generate-notes \
"${{ steps.pack.outputs.archive }}"
--generate-notes

# ── Level 2: depends on browser / node ─────────────────────────────────────
release-express:
Expand Down Expand Up @@ -477,21 +461,13 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: 📦 Pack Sample
id: pack
uses: ./.github/actions/pack-sample
with:
sdk: express
version: ${{ steps.bump.outputs.version }}

- name: 🚀 Create GitHub Release
env:
GH_TOKEN: ${{ secrets.THUNDERID_AUTOMATION_BOT }}
run: |
gh release create "sdk/express/v${{ steps.bump.outputs.version }}" \
--title "ThunderID Express SDK v${{ steps.bump.outputs.version }}" \
--generate-notes \
"${{ steps.pack.outputs.archive }}"
--generate-notes

release-react:
name: ⚛️ Release @thunderid/react
Expand Down Expand Up @@ -550,21 +526,13 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: 📦 Pack Sample
id: pack
uses: ./.github/actions/pack-sample
with:
sdk: react
version: ${{ steps.bump.outputs.version }}

- name: 🚀 Create GitHub Release
env:
GH_TOKEN: ${{ secrets.THUNDERID_AUTOMATION_BOT }}
run: |
gh release create "sdk/react/v${{ steps.bump.outputs.version }}" \
--title "ThunderID React SDK v${{ steps.bump.outputs.version }}" \
--generate-notes \
"${{ steps.pack.outputs.archive }}"
--generate-notes

release-vue:
name: 🟩 Release @thunderid/vue
Expand Down Expand Up @@ -623,21 +591,13 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: 📦 Pack Sample
id: pack
uses: ./.github/actions/pack-sample
with:
sdk: vue
version: ${{ steps.bump.outputs.version }}

- name: 🚀 Create GitHub Release
env:
GH_TOKEN: ${{ secrets.THUNDERID_AUTOMATION_BOT }}
run: |
gh release create "sdk/vue/v${{ steps.bump.outputs.version }}" \
--title "ThunderID Vue SDK v${{ steps.bump.outputs.version }}" \
--generate-notes \
"${{ steps.pack.outputs.archive }}"
--generate-notes

# ── Level 3: depends on react / node+react / browser+node+vue ──────────────
release-react-router:
Expand Down Expand Up @@ -827,21 +787,13 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: 📦 Pack Sample
id: pack
uses: ./.github/actions/pack-sample
with:
sdk: nextjs
version: ${{ steps.bump.outputs.version }}

- name: 🚀 Create GitHub Release
env:
GH_TOKEN: ${{ secrets.THUNDERID_AUTOMATION_BOT }}
run: |
gh release create "sdk/nextjs/v${{ steps.bump.outputs.version }}" \
--title "ThunderID Next.js SDK v${{ steps.bump.outputs.version }}" \
--generate-notes \
"${{ steps.pack.outputs.archive }}"
--generate-notes

release-nuxt:
name: 💚 Release @thunderid/nuxt
Expand Down Expand Up @@ -900,18 +852,10 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: 📦 Pack Sample
id: pack
uses: ./.github/actions/pack-sample
with:
sdk: nuxt
version: ${{ steps.bump.outputs.version }}

- name: 🚀 Create GitHub Release
env:
GH_TOKEN: ${{ secrets.THUNDERID_AUTOMATION_BOT }}
run: |
gh release create "sdk/nuxt/v${{ steps.bump.outputs.version }}" \
--title "ThunderID Nuxt SDK v${{ steps.bump.outputs.version }}" \
--generate-notes \
"${{ steps.pack.outputs.archive }}"
--generate-notes
11 changes: 8 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
pnpm + Turborepo monorepo of TypeScript SDKs under `packages/`: a framework-agnostic core
(`@thunderid/javascript`), a browser layer (`@thunderid/browser`), a server layer (`@thunderid/node`), and
framework SDKs built on top of those (`react`, `vue`, `nuxt`, `nextjs`, `express`, `react-router`,
`tanstack-router`). `samples/<framework>/quickstart` directories contain standalone demo apps — not part of
any published package.
`tanstack-router`). The per-framework quickstarts live in
[thunder-id/samples](https://github.com/thunder-id/samples) and depend on the published packages; the E2E
suite fetches them into `tests/e2e/.samples` and wires them to this workspace's builds
(`tests/e2e/scripts/prepare-samples.ts`). `samples/integrations/` holds integration samples that are not
part of any published package.

## SDK development specification

Expand Down Expand Up @@ -112,5 +115,7 @@ packages/
express/ Express middleware
react-router/ React Router integration
tanstack-router/ TanStack Router integration
samples/ Standalone quickstart demo apps, one per framework
playgrounds/ Playground apps that exercise the SDKs from source (not published)
samples/ Integration samples (quickstarts live in thunder-id/samples)
tests/e2e/ Playwright suite driving the quickstarts against a real ThunderID server
```
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ JavaScript SDKs for ThunderID. Provides authentication and user management for b
| [`@thunderid/tanstack-router`](packages/tanstack-router) | ![npm](https://img.shields.io/npm/v/@thunderid/tanstack-router) | TanStack Router integration |
| [`@thunderid/better-auth`](packages/better-auth) | ![npm](https://img.shields.io/npm/v/@thunderid/better-auth) | Better Auth provider helper |

## Playgrounds

Apps under [`playgrounds/`](playgrounds) exercise the SDKs from the source in this workspace. Build the SDKs with
`pnpm build`, then start one:

| Playground | Run | What it shows |
|---|---|---|
| [Express](playgrounds/express) | `pnpm --filter @thunderid/playground-express dev` | API explorer for the `@thunderid/express` handlers and protected routes, with an SDK trace |
| [React](playgrounds/react) | `pnpm --filter @thunderid/playground-react dev` | Capability checks, a component explorer for the `@thunderid/react` components, and live hooks and tokens |

## Contributing

Please refer to the [Contributing Guide](https://thunderid.dev/community/overview) for the different ways to contribute to this project and the relevant guidelines.
Expand Down
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import thunderIdPlugin from '@thunderid/eslint-plugin';

export default [
{
ignores: ['dist/**', 'build/**', 'node_modules/**', 'coverage/**'],
ignores: ['dist/**', 'build/**', 'node_modules/**', 'coverage/**', 'tests/e2e/.samples/**'],
},
...thunderIdPlugin.configs.typescript,
{
Expand Down
32 changes: 32 additions & 0 deletions playgrounds/express/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copy to .env and fill in the values of the application you registered in ThunderID.

# ThunderID server.
THUNDERID_BASE_URL=https://localhost:8090

# OAuth2 client of the application. The client secret is required for a confidential client.
THUNDERID_CLIENT_ID=
THUNDERID_CLIENT_SECRET=

# Must match the redirect URIs registered for the application. They default to APP_URL/login and
# APP_URL/logout, which are the routes handleSignIn() and handleSignOut() are mounted on.
THUNDERID_AFTER_SIGN_IN_URL=http://localhost:3000/login
THUNDERID_AFTER_SIGN_OUT_URL=http://localhost:3000/logout

# Scopes to request, separated by spaces.
THUNDERID_SCOPES=openid profile email

# Only for POST /flow/sign-in (handleFlow()): the application ID, and the flow secret if the
# application requires one.
THUNDERID_APPLICATION_ID=
THUNDERID_FLOW_SECRET=

# Scope that GET /api/admin/users requires in the access token.
PLAYGROUND_ADMIN_SCOPE=system

# Where the playground listens.
PORT=3000
APP_URL=http://localhost:3000

# A local ThunderID server uses a self-signed certificate, which Node rejects. For local
# development only, turn certificate checks off:
# NODE_TLS_REJECT_UNAUTHORIZED=0
4 changes: 4 additions & 0 deletions playgrounds/express/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
.env
.env.*
!.env.example
4 changes: 4 additions & 0 deletions playgrounds/express/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/dist
/build
/node_modules
/coverage
Loading
Loading