@edpire/sdk 0.6.3 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +445 -443
- package/dist/chunk-76CZ6Z7T.mjs +1 -0
- package/dist/client.d.mts +44 -3
- package/dist/client.mjs +15 -5
- package/dist/dist-3D2JPDV3.mjs +11 -0
- package/dist/esm-RQP7AIMT.mjs +136 -0
- package/dist/index.d.mts +8 -1
- package/dist/index.mjs +52 -7
- package/dist/react.d.mts +9 -1
- package/dist/react.mjs +52 -7
- package/dist/sql-wasm-browser-NICPVFVD.mjs +2 -0
- package/dist/umd/index.global.js +454 -251
- package/examples/nextjs/README.md +66 -66
- package/examples/vite-express/README.md +69 -69
- package/examples/vite-express/server.ts +96 -84
- package/examples/vite-express/src/App.tsx +56 -56
- package/package.json +5 -4
- package/src/styles/runtime-utilities.css +1 -1
- package/src/styles/shell.css +1 -1
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
# Edpire SDK — Next.js example
|
|
2
|
-
|
|
3
|
-
Embeds an Edpire assessment player inside a Next.js App Router page using:
|
|
4
|
-
- `createEdpireTokenHandler()` for the server token endpoint (one call, one file)
|
|
5
|
-
- `<EdpireAssessmentPlayer>` for the browser component (no useRef/useEffect needed)
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
Your browser → POST /api/edpire/token
|
|
9
|
-
Next.js server → mintEmbedToken(assessmentId, session.userId) ← API key never leaves the server
|
|
10
|
-
Browser → EdpireAssessmentPlayer renders the full assessment UI
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Quickstart
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
cp .env.example .env.local
|
|
17
|
-
# Fill in EDPIRE_API_KEY (from edpire.com/org/integrations) and NEXT_PUBLIC_ASSESSMENT_ID
|
|
18
|
-
npm install
|
|
19
|
-
npm run dev
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
Open http://localhost:3000 — the player loads.
|
|
23
|
-
|
|
24
|
-
## How the token endpoint works
|
|
25
|
-
|
|
26
|
-
`app/api/edpire/token/route.ts` exports `createEdpireTokenHandler()` directly as the `POST` handler. In production you replace the cookie-reading `resolveLearner` stub with your real auth session:
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
// With NextAuth
|
|
30
|
-
resolveLearner: async (req) => {
|
|
31
|
-
const session = await getServerSession(authOptions)
|
|
32
|
-
return session?.user.id ?? null
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// With Better Auth
|
|
36
|
-
resolveLearner: async (req) => {
|
|
37
|
-
const session = await auth.api.getSession({ headers: req.headers })
|
|
38
|
-
return session?.user.id ?? null
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Returning `null` rejects with 401 — the player never receives a token.
|
|
43
|
-
|
|
44
|
-
## Allowed Origins
|
|
45
|
-
|
|
46
|
-
If you see a blank player (no error, no content), your app's origin is not in Edpire's allow-list:
|
|
47
|
-
|
|
48
|
-
1. Go to **edpire.com →
|
|
49
|
-
2. Add `http://localhost:3000` for dev, your production domain for prod
|
|
50
|
-
3. Reload — the player should appear
|
|
51
|
-
|
|
52
|
-
See [SDK Troubleshooting](https://docs.edpire.com/developer/sdk/troubleshooting) for more.
|
|
53
|
-
|
|
54
|
-
## Production
|
|
55
|
-
|
|
56
|
-
This example works in both `next dev` and `next build && next start`. The `/api/edpire/token` route is a real server-side endpoint in both modes — not a dev-only workaround.
|
|
57
|
-
|
|
58
|
-
## Project structure
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
app/
|
|
62
|
-
api/edpire/token/route.ts ← server: mints embed tokens
|
|
63
|
-
layout.tsx
|
|
64
|
-
page.tsx ← client: renders <EdpireAssessmentPlayer>
|
|
65
|
-
.env.example
|
|
66
|
-
```
|
|
1
|
+
# Edpire SDK — Next.js example
|
|
2
|
+
|
|
3
|
+
Embeds an Edpire assessment player inside a Next.js App Router page using:
|
|
4
|
+
- `createEdpireTokenHandler()` for the server token endpoint (one call, one file)
|
|
5
|
+
- `<EdpireAssessmentPlayer>` for the browser component (no useRef/useEffect needed)
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Your browser → POST /api/edpire/token
|
|
9
|
+
Next.js server → mintEmbedToken(assessmentId, session.userId) ← API key never leaves the server
|
|
10
|
+
Browser → EdpireAssessmentPlayer renders the full assessment UI
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cp .env.example .env.local
|
|
17
|
+
# Fill in EDPIRE_API_KEY (from edpire.com/org/integrations) and NEXT_PUBLIC_ASSESSMENT_ID
|
|
18
|
+
npm install
|
|
19
|
+
npm run dev
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Open http://localhost:3000 — the player loads.
|
|
23
|
+
|
|
24
|
+
## How the token endpoint works
|
|
25
|
+
|
|
26
|
+
`app/api/edpire/token/route.ts` exports `createEdpireTokenHandler()` directly as the `POST` handler. In production you replace the cookie-reading `resolveLearner` stub with your real auth session:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// With NextAuth
|
|
30
|
+
resolveLearner: async (req) => {
|
|
31
|
+
const session = await getServerSession(authOptions)
|
|
32
|
+
return session?.user.id ?? null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// With Better Auth
|
|
36
|
+
resolveLearner: async (req) => {
|
|
37
|
+
const session = await auth.api.getSession({ headers: req.headers })
|
|
38
|
+
return session?.user.id ?? null
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Returning `null` rejects with 401 — the player never receives a token.
|
|
43
|
+
|
|
44
|
+
## Allowed Origins
|
|
45
|
+
|
|
46
|
+
If you see a blank player (no error, no content), your app's origin is not in Edpire's allow-list:
|
|
47
|
+
|
|
48
|
+
1. Go to **edpire.com → Integrations → Security tab → Allowed Embed Origins**
|
|
49
|
+
2. Add `http://localhost:3000` for dev, your production domain for prod
|
|
50
|
+
3. Reload — the player should appear
|
|
51
|
+
|
|
52
|
+
See [SDK Troubleshooting](https://docs.edpire.com/developer/sdk/troubleshooting) for more.
|
|
53
|
+
|
|
54
|
+
## Production
|
|
55
|
+
|
|
56
|
+
This example works in both `next dev` and `next build && next start`. The `/api/edpire/token` route is a real server-side endpoint in both modes — not a dev-only workaround.
|
|
57
|
+
|
|
58
|
+
## Project structure
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
app/
|
|
62
|
+
api/edpire/token/route.ts ← server: mints embed tokens
|
|
63
|
+
layout.tsx
|
|
64
|
+
page.tsx ← client: renders <EdpireAssessmentPlayer>
|
|
65
|
+
.env.example
|
|
66
|
+
```
|
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
# Edpire SDK — Vite + Express example
|
|
2
|
-
|
|
3
|
-
Embeds an Edpire assessment player using:
|
|
4
|
-
- **Vite** for the React frontend (browser)
|
|
5
|
-
- **Express** for the token endpoint (server) — works in both dev and production
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
Browser → POST /api/edpire/token (via Vite proxy in dev, Express directly in prod)
|
|
9
|
-
Express → mintEmbedToken(assessmentId, session.userId) ← API key never leaves the server
|
|
10
|
-
Browser → <EdpireAssessmentPlayer> renders the full assessment UI
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
This is the production-correct version of the "put it in vite.config.ts" pattern. The Express server runs separately and survives `vite build` + `vite preview`.
|
|
14
|
-
|
|
15
|
-
## Quickstart
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
cp .env.example .env
|
|
19
|
-
# Fill in EDPIRE_API_KEY (from edpire.com/org/integrations) and VITE_ASSESSMENT_ID
|
|
20
|
-
npm install
|
|
21
|
-
npm run dev
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Two processes start:
|
|
25
|
-
- **Express** on port 3001 (token endpoint)
|
|
26
|
-
- **Vite** on port 5173 (dev server, proxies /api → port 3001)
|
|
27
|
-
|
|
28
|
-
Open http://localhost:5173 — the player loads.
|
|
29
|
-
|
|
30
|
-
## How it differs from vite.config.ts middleware
|
|
31
|
-
|
|
32
|
-
The test project at `edpire-sdk-test` put the token endpoint inside a Vite dev-server middleware. That's clean for local dev, but it only runs during `vite dev` — a production build has no `/api/edpire/token` and the embed silently fails.
|
|
33
|
-
|
|
34
|
-
This example runs a real Express server that handles the token endpoint in both dev and production. The Vite proxy in dev routes `/api` requests to Express; in production, Express serves everything.
|
|
35
|
-
|
|
36
|
-
## Env variables
|
|
37
|
-
|
|
38
|
-
| Variable | Where used | VITE_ prefix? |
|
|
39
|
-
|---|---|---|
|
|
40
|
-
| `EDPIRE_API_KEY` | Express server | **No** — prefixing it would leak it to the browser bundle |
|
|
41
|
-
| `VITE_ASSESSMENT_ID` | Vite/browser | **Yes** — this is a public ID, not a secret |
|
|
42
|
-
|
|
43
|
-
This is the key insight: `VITE_` prefixed variables are compiled into the browser bundle. The API key must never be prefixed.
|
|
44
|
-
|
|
45
|
-
## Allowed Origins
|
|
46
|
-
|
|
47
|
-
If you see a blank player, your app's origin is not in Edpire's allow-list:
|
|
48
|
-
|
|
49
|
-
1. Go to **edpire.com →
|
|
50
|
-
2. Add `http://localhost:5173` for dev, your production domain for prod
|
|
51
|
-
3. Reload — the player should appear
|
|
52
|
-
|
|
53
|
-
## Production
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
npm run build # builds Vite frontend → dist/ + compiles server.ts
|
|
57
|
-
NODE_ENV=production npm run preview # starts Express serving dist/ + /api/edpire/token
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## Project structure
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
server.ts ← Express: /api/edpire/token endpoint + static files in prod
|
|
64
|
-
vite.config.ts ← Vite: dev server, proxies /api → Express
|
|
65
|
-
src/
|
|
66
|
-
App.tsx ← React: <EdpireAssessmentPlayer>
|
|
67
|
-
main.tsx
|
|
68
|
-
.env.example
|
|
69
|
-
```
|
|
1
|
+
# Edpire SDK — Vite + Express example
|
|
2
|
+
|
|
3
|
+
Embeds an Edpire assessment player using:
|
|
4
|
+
- **Vite** for the React frontend (browser)
|
|
5
|
+
- **Express** for the token endpoint (server) — works in both dev and production
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Browser → POST /api/edpire/token (via Vite proxy in dev, Express directly in prod)
|
|
9
|
+
Express → mintEmbedToken(assessmentId, session.userId) ← API key never leaves the server
|
|
10
|
+
Browser → <EdpireAssessmentPlayer> renders the full assessment UI
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This is the production-correct version of the "put it in vite.config.ts" pattern. The Express server runs separately and survives `vite build` + `vite preview`.
|
|
14
|
+
|
|
15
|
+
## Quickstart
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cp .env.example .env
|
|
19
|
+
# Fill in EDPIRE_API_KEY (from edpire.com/org/integrations) and VITE_ASSESSMENT_ID
|
|
20
|
+
npm install
|
|
21
|
+
npm run dev
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Two processes start:
|
|
25
|
+
- **Express** on port 3001 (token endpoint)
|
|
26
|
+
- **Vite** on port 5173 (dev server, proxies /api → port 3001)
|
|
27
|
+
|
|
28
|
+
Open http://localhost:5173 — the player loads.
|
|
29
|
+
|
|
30
|
+
## How it differs from vite.config.ts middleware
|
|
31
|
+
|
|
32
|
+
The test project at `edpire-sdk-test` put the token endpoint inside a Vite dev-server middleware. That's clean for local dev, but it only runs during `vite dev` — a production build has no `/api/edpire/token` and the embed silently fails.
|
|
33
|
+
|
|
34
|
+
This example runs a real Express server that handles the token endpoint in both dev and production. The Vite proxy in dev routes `/api` requests to Express; in production, Express serves everything.
|
|
35
|
+
|
|
36
|
+
## Env variables
|
|
37
|
+
|
|
38
|
+
| Variable | Where used | VITE_ prefix? |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `EDPIRE_API_KEY` | Express server | **No** — prefixing it would leak it to the browser bundle |
|
|
41
|
+
| `VITE_ASSESSMENT_ID` | Vite/browser | **Yes** — this is a public ID, not a secret |
|
|
42
|
+
|
|
43
|
+
This is the key insight: `VITE_` prefixed variables are compiled into the browser bundle. The API key must never be prefixed.
|
|
44
|
+
|
|
45
|
+
## Allowed Origins
|
|
46
|
+
|
|
47
|
+
If you see a blank player, your app's origin is not in Edpire's allow-list:
|
|
48
|
+
|
|
49
|
+
1. Go to **edpire.com → Integrations → Security tab → Allowed Embed Origins**
|
|
50
|
+
2. Add `http://localhost:5173` for dev, your production domain for prod
|
|
51
|
+
3. Reload — the player should appear
|
|
52
|
+
|
|
53
|
+
## Production
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm run build # builds Vite frontend → dist/ + compiles server.ts
|
|
57
|
+
NODE_ENV=production npm run preview # starts Express serving dist/ + /api/edpire/token
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Project structure
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
server.ts ← Express: /api/edpire/token endpoint + static files in prod
|
|
64
|
+
vite.config.ts ← Vite: dev server, proxies /api → Express
|
|
65
|
+
src/
|
|
66
|
+
App.tsx ← React: <EdpireAssessmentPlayer>
|
|
67
|
+
main.tsx
|
|
68
|
+
.env.example
|
|
69
|
+
```
|
|
@@ -1,84 +1,96 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Express server — serves the token endpoint + static files in production.
|
|
3
|
-
*
|
|
4
|
-
* In development: Vite dev server runs separately (port 5173) and proxies
|
|
5
|
-
* /api requests here (port 3001). Run `npm run dev` to start both.
|
|
6
|
-
*
|
|
7
|
-
* In production: this server serves the Vite build output (dist/) and the
|
|
8
|
-
* /api/edpire/token endpoint. Run `npm run build` then `npm run preview`.
|
|
9
|
-
*/
|
|
10
|
-
import "dotenv/config"
|
|
11
|
-
import express from "express"
|
|
12
|
-
import cors from "cors"
|
|
13
|
-
import path from "node:path"
|
|
14
|
-
import { fileURLToPath } from "node:url"
|
|
15
|
-
import { createEdpireTokenHandler, toNodeHandler } from "@edpire/sdk/client"
|
|
16
|
-
|
|
17
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
18
|
-
const PORT = Number(process.env.PORT ?? 3001)
|
|
19
|
-
const isDev = process.env.NODE_ENV !== "production"
|
|
20
|
-
|
|
21
|
-
const app = express()
|
|
22
|
-
|
|
23
|
-
// In dev, the Vite dev server (port 5173) calls this server for /api requests.
|
|
24
|
-
// Allow cross-origin requests from localhost in development.
|
|
25
|
-
if (isDev) {
|
|
26
|
-
app.use(cors({ origin: /localhost/ }))
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// ── Token endpoint ─────────────────────────────────────────────────────────────
|
|
30
|
-
//
|
|
31
|
-
// The browser POSTs { assessmentId } here and receives { token }.
|
|
32
|
-
// The learner ID is resolved from the server session — never from the body.
|
|
33
|
-
//
|
|
34
|
-
// toNodeHandler() adapts the Web-standard createEdpireTokenHandler() response
|
|
35
|
-
// to Express's (req, res) signature.
|
|
36
|
-
const edpireTokenHandler = toNodeHandler(
|
|
37
|
-
createEdpireTokenHandler({
|
|
38
|
-
apiKey: process.env.EDPIRE_API_KEY!,
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Resolve the learner ID from YOUR session.
|
|
42
|
-
*
|
|
43
|
-
* This example reads a `userId` cookie set by your login flow.
|
|
44
|
-
* Replace with your real session middleware in production:
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
app.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Express server — serves the token endpoint + static files in production.
|
|
3
|
+
*
|
|
4
|
+
* In development: Vite dev server runs separately (port 5173) and proxies
|
|
5
|
+
* /api requests here (port 3001). Run `npm run dev` to start both.
|
|
6
|
+
*
|
|
7
|
+
* In production: this server serves the Vite build output (dist/) and the
|
|
8
|
+
* /api/edpire/token endpoint. Run `npm run build` then `npm run preview`.
|
|
9
|
+
*/
|
|
10
|
+
import "dotenv/config"
|
|
11
|
+
import express from "express"
|
|
12
|
+
import cors from "cors"
|
|
13
|
+
import path from "node:path"
|
|
14
|
+
import { fileURLToPath } from "node:url"
|
|
15
|
+
import { createEdpireTokenHandler, toNodeHandler } from "@edpire/sdk/client"
|
|
16
|
+
|
|
17
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
18
|
+
const PORT = Number(process.env.PORT ?? 3001)
|
|
19
|
+
const isDev = process.env.NODE_ENV !== "production"
|
|
20
|
+
|
|
21
|
+
const app = express()
|
|
22
|
+
|
|
23
|
+
// In dev, the Vite dev server (port 5173) calls this server for /api requests.
|
|
24
|
+
// Allow cross-origin requests from localhost in development.
|
|
25
|
+
if (isDev) {
|
|
26
|
+
app.use(cors({ origin: /localhost/ }))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ── Token endpoint ─────────────────────────────────────────────────────────────
|
|
30
|
+
//
|
|
31
|
+
// The browser POSTs { assessmentId } here and receives { token }.
|
|
32
|
+
// The learner ID is resolved from the server session — never from the body.
|
|
33
|
+
//
|
|
34
|
+
// toNodeHandler() adapts the Web-standard createEdpireTokenHandler() response
|
|
35
|
+
// to Express's (req, res) signature.
|
|
36
|
+
const edpireTokenHandler = toNodeHandler(
|
|
37
|
+
createEdpireTokenHandler({
|
|
38
|
+
apiKey: process.env.EDPIRE_API_KEY!,
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Resolve the learner ID from YOUR session.
|
|
42
|
+
*
|
|
43
|
+
* This example reads a `userId` cookie set by your login flow.
|
|
44
|
+
* Replace with your real session middleware in production:
|
|
45
|
+
*
|
|
46
|
+
* IMPORTANT: `req` is a Web-standard Request, not an Express request.
|
|
47
|
+
* `req.session` and `req.cookies` do NOT exist here — read the raw header
|
|
48
|
+
* and do your own lookup, as below.
|
|
49
|
+
*
|
|
50
|
+
* @example session store
|
|
51
|
+
* resolveLearner: async (req) => {
|
|
52
|
+
* const sid = parseCookie(req.headers.get("cookie"), "sid")
|
|
53
|
+
* return sid ? (await sessionStore.get(sid))?.userId ?? null : null
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* @example JWT
|
|
57
|
+
* resolveLearner: (req) => {
|
|
58
|
+
* const token = parseCookie(req.headers.get("cookie"), "token")
|
|
59
|
+
* return token ? jwt.verify(token, JWT_SECRET).userId ?? null : null
|
|
60
|
+
* }
|
|
61
|
+
*
|
|
62
|
+
* Returning null → 401 (no token minted).
|
|
63
|
+
*/
|
|
64
|
+
resolveLearner: (req) => {
|
|
65
|
+
// In development, use a fixed test ID so the player works out of the box.
|
|
66
|
+
// Replace this with your real session logic before going to production —
|
|
67
|
+
// always derived from the cookie header, never from the request body.
|
|
68
|
+
if (isDev) return "dev-user-001"
|
|
69
|
+
|
|
70
|
+
const cookie = req.headers.get("cookie") ?? ""
|
|
71
|
+
const match = /(?:^|;\s*)userId=([^;]+)/.exec(cookie)
|
|
72
|
+
return match ? decodeURIComponent(match[1]) : null
|
|
73
|
+
},
|
|
74
|
+
}),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
app.post("/api/edpire/token", edpireTokenHandler)
|
|
78
|
+
|
|
79
|
+
// ── Static files (production only) ────────────────────────────────────────────
|
|
80
|
+
if (!isDev) {
|
|
81
|
+
const distPath = path.join(__dirname, "dist")
|
|
82
|
+
app.use(express.static(distPath))
|
|
83
|
+
// SPA fallback — send index.html for all non-API routes
|
|
84
|
+
app.get(/^(?!\/api).*$/, (_req, res) => {
|
|
85
|
+
res.sendFile(path.join(distPath, "index.html"))
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
app.listen(PORT, () => {
|
|
90
|
+
if (isDev) {
|
|
91
|
+
console.log(`[server] Token endpoint ready at http://localhost:${PORT}/api/edpire/token`)
|
|
92
|
+
console.log(`[server] Start Vite dev server with: npm run dev`)
|
|
93
|
+
} else {
|
|
94
|
+
console.log(`[server] Production server at http://localhost:${PORT}`)
|
|
95
|
+
}
|
|
96
|
+
})
|
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Edpire SDK — Vite + Express example
|
|
3
|
-
*
|
|
4
|
-
* How it works:
|
|
5
|
-
* 1. App.tsx renders <EdpireAssessmentPlayer> which POSTs { assessmentId }
|
|
6
|
-
* to /api/edpire/token (proxied to the Express server in dev).
|
|
7
|
-
* 2. The Express server (server.ts) resolves the learner from the session,
|
|
8
|
-
* mints a token via EdpireClient, and returns { token }.
|
|
9
|
-
* 3. The player fetches assessment content from Edpire using the token and
|
|
10
|
-
* renders the full UI.
|
|
11
|
-
*
|
|
12
|
-
* The secret EDPIRE_API_KEY never touches the browser.
|
|
13
|
-
*/
|
|
14
|
-
import { EdpireAssessmentPlayer } from "@edpire/sdk/react"
|
|
15
|
-
|
|
16
|
-
// VITE_ASSESSMENT_ID is safe to expose in the browser (it's a public ID, not a secret).
|
|
17
|
-
const ASSESSMENT_ID = import.meta.env.VITE_ASSESSMENT_ID as string | undefined
|
|
18
|
-
|
|
19
|
-
export default function App() {
|
|
20
|
-
if (!ASSESSMENT_ID) {
|
|
21
|
-
return (
|
|
22
|
-
<div style={{ padding: 32, fontFamily: "sans-serif" }}>
|
|
23
|
-
<strong>Set VITE_ASSESSMENT_ID in .env</strong>
|
|
24
|
-
<p>Copy .env.example → .env and fill in your assessment ID.</p>
|
|
25
|
-
<p>
|
|
26
|
-
<em>Note: EDPIRE_API_KEY must NOT have the VITE_ prefix — that would leak it to the
|
|
27
|
-
browser. Only the assessment ID is prefixed.</em>
|
|
28
|
-
</p>
|
|
29
|
-
</div>
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<EdpireAssessmentPlayer
|
|
35
|
-
tokenEndpoint="/api/edpire/token"
|
|
36
|
-
assessmentId={ASSESSMENT_ID}
|
|
37
|
-
onComplete={(result) => {
|
|
38
|
-
console.log("Assessment complete!", {
|
|
39
|
-
score: result.score,
|
|
40
|
-
max: result.max_score,
|
|
41
|
-
passed: result.passed,
|
|
42
|
-
})
|
|
43
|
-
// TODO: redirect to results page, update progress, etc.
|
|
44
|
-
}}
|
|
45
|
-
onError={(err) => {
|
|
46
|
-
console.error("Embed error:", err.code, err.message)
|
|
47
|
-
// Common errors:
|
|
48
|
-
// ORIGIN_NOT_ALLOWED
|
|
49
|
-
// TOKEN_INVALID → check EDPIRE_API_KEY in .env
|
|
50
|
-
// ASSESSMENT_NOT_FOUND → check VITE_ASSESSMENT_ID in .env
|
|
51
|
-
}}
|
|
52
|
-
// The player fills its container. Set a height here or via CSS.
|
|
53
|
-
style={{ width: "100%", height: "100vh" }}
|
|
54
|
-
/>
|
|
55
|
-
)
|
|
56
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Edpire SDK — Vite + Express example
|
|
3
|
+
*
|
|
4
|
+
* How it works:
|
|
5
|
+
* 1. App.tsx renders <EdpireAssessmentPlayer> which POSTs { assessmentId }
|
|
6
|
+
* to /api/edpire/token (proxied to the Express server in dev).
|
|
7
|
+
* 2. The Express server (server.ts) resolves the learner from the session,
|
|
8
|
+
* mints a token via EdpireClient, and returns { token }.
|
|
9
|
+
* 3. The player fetches assessment content from Edpire using the token and
|
|
10
|
+
* renders the full UI.
|
|
11
|
+
*
|
|
12
|
+
* The secret EDPIRE_API_KEY never touches the browser.
|
|
13
|
+
*/
|
|
14
|
+
import { EdpireAssessmentPlayer } from "@edpire/sdk/react"
|
|
15
|
+
|
|
16
|
+
// VITE_ASSESSMENT_ID is safe to expose in the browser (it's a public ID, not a secret).
|
|
17
|
+
const ASSESSMENT_ID = import.meta.env.VITE_ASSESSMENT_ID as string | undefined
|
|
18
|
+
|
|
19
|
+
export default function App() {
|
|
20
|
+
if (!ASSESSMENT_ID) {
|
|
21
|
+
return (
|
|
22
|
+
<div style={{ padding: 32, fontFamily: "sans-serif" }}>
|
|
23
|
+
<strong>Set VITE_ASSESSMENT_ID in .env</strong>
|
|
24
|
+
<p>Copy .env.example → .env and fill in your assessment ID.</p>
|
|
25
|
+
<p>
|
|
26
|
+
<em>Note: EDPIRE_API_KEY must NOT have the VITE_ prefix — that would leak it to the
|
|
27
|
+
browser. Only the assessment ID is prefixed.</em>
|
|
28
|
+
</p>
|
|
29
|
+
</div>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<EdpireAssessmentPlayer
|
|
35
|
+
tokenEndpoint="/api/edpire/token"
|
|
36
|
+
assessmentId={ASSESSMENT_ID}
|
|
37
|
+
onComplete={(result) => {
|
|
38
|
+
console.log("Assessment complete!", {
|
|
39
|
+
score: result.score,
|
|
40
|
+
max: result.max_score,
|
|
41
|
+
passed: result.passed,
|
|
42
|
+
})
|
|
43
|
+
// TODO: redirect to results page, update progress, etc.
|
|
44
|
+
}}
|
|
45
|
+
onError={(err) => {
|
|
46
|
+
console.error("Embed error:", err.code, err.message)
|
|
47
|
+
// Common errors:
|
|
48
|
+
// ORIGIN_NOT_ALLOWED -> add localhost to *Allowed Embed Origins* (Integrations -> Security)
|
|
49
|
+
// TOKEN_INVALID → check EDPIRE_API_KEY in .env
|
|
50
|
+
// ASSESSMENT_NOT_FOUND → check VITE_ASSESSMENT_ID in .env
|
|
51
|
+
}}
|
|
52
|
+
// The player fills its container. Set a height here or via CSS.
|
|
53
|
+
style={{ width: "100%", height: "100vh" }}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edpire/sdk",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "Embeddable JS SDK for Edpire assessments",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"homepage": "https://docs.edpire.com/developer/sdk/overview",
|
|
@@ -70,8 +70,8 @@
|
|
|
70
70
|
"@tailwindcss/oxide": "^4.2.0",
|
|
71
71
|
"@types/react": "^19.2.14",
|
|
72
72
|
"@types/react-dom": "^19.2.3",
|
|
73
|
-
"@youssefalmia/edpire-runtime": "^3.
|
|
74
|
-
"@youssefalmia/edpire-schema": "^5.
|
|
73
|
+
"@youssefalmia/edpire-runtime": "^3.13.0",
|
|
74
|
+
"@youssefalmia/edpire-schema": "^5.5.0",
|
|
75
75
|
"tailwindcss": "^3.4.0",
|
|
76
76
|
"tsup": "^8.5.1",
|
|
77
77
|
"typescript": "^5.0.0"
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
"build:css": "node scripts/gen-runtime-css.mjs",
|
|
82
82
|
"build:shell-css": "node scripts/gen-shell-css.mjs",
|
|
83
83
|
"dev": "pnpm build:css && pnpm build:shell-css && tsup --watch",
|
|
84
|
-
"type-check": "tsc --noEmit"
|
|
84
|
+
"type-check": "tsc --noEmit",
|
|
85
|
+
"check:doc-versions": "node ../../scripts/check-doc-versions.mjs"
|
|
85
86
|
}
|
|
86
87
|
}
|