@urun-sh/next 0.2.26 → 0.2.29
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 +76 -7
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +111 -21
- package/dist/index.d.ts +39 -10
- package/dist/index.js +1 -1
- package/package.json +11 -10
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -3,27 +3,62 @@
|
|
|
3
3
|
Next.js on-ramp for uRun **scoped client tokens** — never ship your org API
|
|
4
4
|
key to the browser.
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## Zero-config: the entire connection story
|
|
7
|
+
|
|
8
|
+
With only `URUN_API_KEY` in the server env (raw `pnpm dev` / `pnpm build`,
|
|
9
|
+
no CLI), this is a complete uRun frontend:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
// app/api/urun-token/route.ts — the entire server leg
|
|
13
|
+
import { createTokenRoute } from '@urun-sh/next'
|
|
14
|
+
export const POST = createTokenRoute()
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
// Client code: hardcode YOUR app name, mirroring the backend's App("...").
|
|
19
|
+
// Sessions bind by method call — no function-name strings.
|
|
20
|
+
<UrunProvider app="rolling-sink">…</UrunProvider>
|
|
21
|
+
// const app = useApp(); const session = app.rolling_sink()
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`<UrunProvider>` (from `@urun-sh/react`) POSTs `/api/urun-token` after mount;
|
|
25
|
+
the route answers `{ token, expiresAt, orgId, gatewayUrl }` — org derived from
|
|
26
|
+
the key, gateway the platform front door (never user configuration). Nothing
|
|
27
|
+
else is set anywhere: no org id, no gateway URL, no `NEXT_PUBLIC_*`.
|
|
28
|
+
|
|
29
|
+
`createTokenRoute()` mints for ANY caller by default (the public-demo
|
|
30
|
+
posture — a server-side warning reminds you once); pass
|
|
31
|
+
`createTokenRoute({ authorize })` to restrict minting to your authenticated
|
|
32
|
+
users. All the scoping options below apply to it as well.
|
|
33
|
+
|
|
34
|
+
## App Router (explicit authorization)
|
|
7
35
|
|
|
8
36
|
```ts
|
|
9
37
|
// app/api/urun-token/route.ts
|
|
10
38
|
import { urunTokenRoute } from '@urun-sh/next'
|
|
11
|
-
|
|
39
|
+
|
|
40
|
+
export const POST = urunTokenRoute({
|
|
41
|
+
authorize: (req) => Boolean(getSessionUser(req)), // required: do not mint for anonymous callers
|
|
42
|
+
subject: (req) => getSessionUser(req)?.id, // stable per-end-user identity
|
|
43
|
+
})
|
|
12
44
|
```
|
|
13
45
|
|
|
14
|
-
Set the `URUN_API_KEY` env secret and
|
|
15
|
-
|
|
16
|
-
|
|
46
|
+
Set the `URUN_API_KEY` env secret and provide an `authorize` callback that
|
|
47
|
+
returns `true` only for an authenticated application user/session. The route
|
|
48
|
+
mints short-lived tokens the browser passes to the SDK through the existing
|
|
49
|
+
`jwt` / `getAccessToken` auth options. Requests are rejected with `401` before
|
|
50
|
+
minting when `authorize` is omitted or returns false.
|
|
17
51
|
|
|
18
52
|
## Scoping
|
|
19
53
|
|
|
20
54
|
```ts
|
|
21
55
|
export const POST = urunTokenRoute({
|
|
56
|
+
authorize: (req) => Boolean(getSessionUser(req)),
|
|
22
57
|
allowedFunctions: ['zimage/generate'], // only these app/functions
|
|
23
58
|
allowedOrigins: ['https://app.example.com'], // only from these Origins
|
|
24
59
|
maxSessionS: 900, // per-session length cap
|
|
25
60
|
expiresIn: 120, // token start-window (max 3600)
|
|
26
|
-
subject: (req) =>
|
|
61
|
+
subject: (req) => getSessionUser(req)?.id, // stable per-end-user identity
|
|
27
62
|
})
|
|
28
63
|
```
|
|
29
64
|
|
|
@@ -40,9 +75,43 @@ cap (folded with the function's own `max_session_s`).
|
|
|
40
75
|
```ts
|
|
41
76
|
// pages/api/urun-token.ts
|
|
42
77
|
import { urunTokenPagesHandler } from '@urun-sh/next'
|
|
43
|
-
|
|
78
|
+
|
|
79
|
+
export default urunTokenPagesHandler({
|
|
80
|
+
authorize: (req) => Boolean(getSessionUserFromPagesReq(req)),
|
|
81
|
+
})
|
|
44
82
|
```
|
|
45
83
|
|
|
84
|
+
## Session config — hardcode nothing
|
|
85
|
+
|
|
86
|
+
The other half of the on-ramp: `urun deploy` derives your frontend's
|
|
87
|
+
session-plane config from the deploy (gateway from your login, app id +
|
|
88
|
+
function from the deployed app) and writes it to `frontend/.env.local`.
|
|
89
|
+
`urunSessionConfig()` reads it back so the frontend hardcodes **nothing**:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
// app/page.tsx — a Server Component
|
|
93
|
+
import { urunSessionConfig } from '@urun-sh/next'
|
|
94
|
+
import { Client } from './client'
|
|
95
|
+
|
|
96
|
+
export default function Page() {
|
|
97
|
+
const { baseUrl, appId, functionName } = urunSessionConfig()
|
|
98
|
+
return <Client baseUrl={baseUrl} appId={appId} functionName={functionName} />
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The **org id and auth provider are never here** — a frontend never hardcodes
|
|
103
|
+
its org. They are derived server-side from `URUN_API_KEY` when the token route
|
|
104
|
+
mints the token: the minted token carries the org claim, and `createClientToken`
|
|
105
|
+
returns the resolved `orgId` (in the `{ token, expiresAt, orgId }` response) for
|
|
106
|
+
the browser to hand to `<UrunProvider orgId=… jwt=… />`. The gateway ignores the
|
|
107
|
+
auth provider for platform-minted client tokens, so no `authProvider` is needed.
|
|
108
|
+
|
|
109
|
+
The end state: a user sets only `URUN_API_KEY` and runs `urun deploy` — no
|
|
110
|
+
`NEXT_PUBLIC_SESSION_*` editing.
|
|
111
|
+
|
|
112
|
+
Canonical env vars (plain server env, not `NEXT_PUBLIC_*`): `URUN_GATEWAY_URL`
|
|
113
|
+
(default `https://api.urun.sh`), `URUN_APP`, `URUN_FUNCTION`.
|
|
114
|
+
|
|
46
115
|
## Server SDK
|
|
47
116
|
|
|
48
117
|
The underlying mint call is `createClientToken(apiKey, options)` from
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var f=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var S=(e,n)=>{for(var o in n)f(e,o,{get:n[o],enumerable:!0})},O=(e,n,o,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of x(n))!T.call(e,t)&&t!==o&&f(e,t,{get:()=>n[t],enumerable:!(r=h(n,t))||r.enumerable});return e};var N=e=>O(f({},"__esModule",{value:!0}),e);var q={};S(q,{URUN_API_KEY_ENV:()=>l,URUN_APP_ENV:()=>m,URUN_FUNCTION_ENV:()=>k,URUN_GATEWAY_URL_ENV:()=>g,createTokenRoute:()=>C,urunSessionConfig:()=>I,urunTokenPagesHandler:()=>E,urunTokenRoute:()=>_});module.exports=N(q);var d=require("@urun-sh/core"),l="URUN_API_KEY",g="URUN_GATEWAY_URL",m="URUN_APP",k="URUN_FUNCTION";function I(e=process.env){return{baseUrl:e[g]?.trim()||d.DEFAULT_GATEWAY_URL,appId:e[m]?.trim()||void 0,functionName:e[k]?.trim()||void 0}}var P=60,u=class extends Error{};function A(e){return Array.isArray(e)&&e.every(n=>typeof n=="string")}function j(e,n){let o=n&&typeof n=="object"&&!Array.isArray(n)?n:{},r={expiresIn:e.expiresIn,allowedFunctions:e.allowedFunctions,allowedOrigins:e.allowedOrigins,maxSessionS:e.maxSessionS},t=o.expiresIn;if(t!==void 0){if(typeof t!="number"||!Number.isInteger(t)||t<1)throw new u("expiresIn must be a positive integer");let a=e.expiresIn??P;if(t>a)throw new u(`expiresIn may not exceed the configured ${a}s`);r.expiresIn=t}let s=(a,c,w)=>{if(a===void 0)return c;if(!A(a))throw new u(`${w} must be an array of strings`);if(c){let y=a.filter(b=>!c.includes(b));if(y.length>0)throw new u(`${w} may only narrow the configured allowlist (rejected: ${y.join(", ")})`)}return a};r.allowedFunctions=s(o.allowedFunctions,e.allowedFunctions,"allowedFunctions"),r.allowedOrigins=s(o.allowedOrigins,e.allowedOrigins,"allowedOrigins");let i=o.maxSessionS;if(i!==void 0){if(typeof i!="number"||!Number.isInteger(i)||i<1)throw new u("maxSessionS must be a positive integer");r.maxSessionS=e.maxSessionS===void 0?i:Math.min(e.maxSessionS,i)}return r}async function U(e,n){return e.authorize?await e.authorize(n)?void 0:{status:401,body:{message:"unauthorized"}}:(console.error("[urun] token route: options.authorize is required before minting"),{status:401,body:{message:"unauthorized"}})}async function p(e,n,o){let r=e.apiKey??process.env[l];if(!r)return console.error(`[urun] token route: no API key \u2014 set ${l} or pass options.apiKey`),{status:500,body:{message:"token route not configured"}};let t;try{t=j(e,n)}catch(s){if(s instanceof u)return{status:400,body:{message:s.message}};throw s}try{let s=await(0,d.createClientToken)(r,{baseUrl:e.baseUrl,expiresIn:t.expiresIn,allowedFunctions:t.allowedFunctions,allowedOrigins:t.allowedOrigins,maxSessionS:t.maxSessionS,metadata:e.metadata,subject:o,fetch:e.fetch});return{status:200,body:{token:s.token,expiresAt:s.expiresAt.toISOString(),orgId:s.orgId}}}catch(s){let i=s instanceof d.ClientTokenError?`${s.message} (status ${s.status??"n/a"})`:s instanceof Error?s.message:String(s);return console.error(`[urun] token route: mint failed: ${i}`),{status:502,body:{message:"token mint failed"}}}}function _(e){return async n=>{let o;try{o=await n.json()}catch{o=void 0}let r=await U(e,n);if(r)return new Response(JSON.stringify(r.body),{status:r.status,headers:{"content-type":"application/json"}});let t=e.subject?await e.subject(n):void 0,{status:s,body:i}=await p(e,o,t);return new Response(JSON.stringify(i),{status:s,headers:{"content-type":"application/json"}})}}function z(){return(process.env[g]?.trim()||d.DEFAULT_GATEWAY_URL).replace(/\/+$/,"")}var R=!1;function C(e={}){return async n=>{let o;try{o=await n.json()}catch{o=void 0}if(e.authorize){if(!await e.authorize(n))return new Response(JSON.stringify({message:"unauthorized"}),{status:401,headers:{"content-type":"application/json"}})}else R||(R=!0,console.warn("[urun] createTokenRoute: minting for ANY caller \u2014 pass { authorize } to restrict the endpoint to your authenticated users"));let r=e.subject?await e.subject(n):void 0,t=z(),{status:s,body:i}=await p({...e,baseUrl:t},o,r);if(s!==200)return new Response(JSON.stringify(i),{status:s,headers:{"content-type":"application/json"}});let a={...i,gatewayUrl:t};return new Response(JSON.stringify(a),{status:200,headers:{"content-type":"application/json"}})}}function E(e){return async(n,o)=>{if((n.method??"GET").toUpperCase()!=="POST"){o.status(405).json({message:"method not allowed"});return}let r=await U(e,n);if(r){o.status(r.status).json(r.body);return}let{status:t,body:s}=await p(e,n.body,void 0);o.status(t).json(s)}}0&&(module.exports={URUN_API_KEY_ENV,URUN_APP_ENV,URUN_FUNCTION_ENV,URUN_GATEWAY_URL_ENV,createTokenRoute,urunSessionConfig,urunTokenPagesHandler,urunTokenRoute});
|
package/dist/index.d.cts
CHANGED
|
@@ -3,11 +3,8 @@ import { CreateClientTokenOptions } from '@urun-sh/core';
|
|
|
3
3
|
/**
|
|
4
4
|
* @urun-sh/next — the Next.js on-ramp for uRun scoped client tokens.
|
|
5
5
|
*
|
|
6
|
-
* The
|
|
7
|
-
*
|
|
8
|
-
* // app/api/urun-token/route.ts
|
|
9
|
-
* import { urunTokenRoute } from '@urun-sh/next'
|
|
10
|
-
* export const POST = urunTokenRoute()
|
|
6
|
+
* The integration requires an application authorization callback so the
|
|
7
|
+
* token endpoint is never public by default.
|
|
11
8
|
*
|
|
12
9
|
* The route mints short-lived scoped client tokens with your org API key
|
|
13
10
|
* (the standard `URUN_API_KEY` env secret) via `createClientToken` from
|
|
@@ -21,13 +18,60 @@ import { CreateClientTokenOptions } from '@urun-sh/core';
|
|
|
21
18
|
* never widen anything, and it can never choose its own `subject` (identity
|
|
22
19
|
* is server-derived via `options.subject`, defaulting to a random subject per
|
|
23
20
|
* token so distinct end-users never share a session).
|
|
21
|
+
*
|
|
22
|
+
* SECURITY MODEL of the route itself: `options.authorize` is required and
|
|
23
|
+
* must return true before the route mints a token.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
/** The standard env secret the route reads the org API key from. */
|
|
27
27
|
declare const URUN_API_KEY_ENV = "URUN_API_KEY";
|
|
28
|
+
/**
|
|
29
|
+
* Canonical env var names carrying a frontend's session-plane config — the
|
|
30
|
+
* values `urun deploy` writes into `frontend/.env.local` and `urun dev`
|
|
31
|
+
* injects into the frontend dev server. Plain server env (NOT `NEXT_PUBLIC_*`):
|
|
32
|
+
* read them in a Server Component / route handler and pass them to your client
|
|
33
|
+
* provider as props.
|
|
34
|
+
*/
|
|
35
|
+
declare const URUN_GATEWAY_URL_ENV = "URUN_GATEWAY_URL";
|
|
36
|
+
declare const URUN_APP_ENV = "URUN_APP";
|
|
37
|
+
declare const URUN_FUNCTION_ENV = "URUN_FUNCTION";
|
|
38
|
+
/** The frontend's resolved session-plane config (see {@link urunSessionConfig}). */
|
|
39
|
+
interface UrunSessionConfig {
|
|
40
|
+
/** Session gateway base URL — `URUN_GATEWAY_URL`, default the public gateway. */
|
|
41
|
+
baseUrl: string;
|
|
42
|
+
/** Deployed app id — `URUN_APP`; undefined until `urun deploy` emits it. */
|
|
43
|
+
appId?: string;
|
|
44
|
+
/** Session function name — `URUN_FUNCTION`; undefined until emitted. */
|
|
45
|
+
functionName?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Read a frontend's session-plane config from the environment.
|
|
49
|
+
*
|
|
50
|
+
* This is the READER half of the CLI on-ramp: `urun deploy` derives these
|
|
51
|
+
* values from your deploy (gateway from your login, app id + function from the
|
|
52
|
+
* deployed app) and writes them to `frontend/.env.local`; this reads them back
|
|
53
|
+
* so the frontend hardcodes nothing.
|
|
54
|
+
*
|
|
55
|
+
* The org id and auth provider are DELIBERATELY absent — a frontend never
|
|
56
|
+
* hardcodes its org. They are derived SERVER-SIDE from `URUN_API_KEY` when the
|
|
57
|
+
* token route mints the client token: the minted token already carries the org
|
|
58
|
+
* claim, and {@link createClientToken} returns the resolved `orgId` for the
|
|
59
|
+
* browser to hand to the provider. The gateway ignores the auth provider for
|
|
60
|
+
* these platform-minted client tokens (trust = JWKS verify + org-claim match),
|
|
61
|
+
* so the provider's default is correct and no `authProvider` need be set.
|
|
62
|
+
*
|
|
63
|
+
* Read this SERVER-SIDE (plain env, not `NEXT_PUBLIC_*`) and pass the values
|
|
64
|
+
* to your client provider as props.
|
|
65
|
+
*/
|
|
66
|
+
declare function urunSessionConfig(env?: Record<string, string | undefined>): UrunSessionConfig;
|
|
28
67
|
interface UrunTokenRouteOptions extends Omit<CreateClientTokenOptions, 'fetch' | 'subject'> {
|
|
29
68
|
/** Org API key (default: `process.env.URUN_API_KEY`). */
|
|
30
69
|
apiKey?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Authorize the caller before minting. Return true only for an
|
|
72
|
+
* authenticated application user/session that may receive a client token.
|
|
73
|
+
*/
|
|
74
|
+
authorize: (request: Request | UrunTokenPagesRequest) => boolean | Promise<boolean>;
|
|
31
75
|
/**
|
|
32
76
|
* Derive a STABLE per-end-user subject from the request (e.g. your auth
|
|
33
77
|
* session). A stable subject coalesces one user's tabs into one session;
|
|
@@ -38,17 +82,6 @@ interface UrunTokenRouteOptions extends Omit<CreateClientTokenOptions, 'fetch' |
|
|
|
38
82
|
/** Fetch override (tests / custom agents). */
|
|
39
83
|
fetch?: typeof fetch;
|
|
40
84
|
}
|
|
41
|
-
/** The JSON shape a successful token response carries to the browser. */
|
|
42
|
-
interface UrunTokenResponseBody {
|
|
43
|
-
token: string;
|
|
44
|
-
expiresAt: string;
|
|
45
|
-
orgId: string;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* A fully-built Next.js App Router route handler that mints scoped client
|
|
49
|
-
* tokens: `export const POST = urunTokenRoute()`.
|
|
50
|
-
*/
|
|
51
|
-
declare function urunTokenRoute(options?: UrunTokenRouteOptions): (request: Request) => Promise<Response>;
|
|
52
85
|
/**
|
|
53
86
|
* Structural Pages Router types — no dependency on `next` itself, so the
|
|
54
87
|
* package builds/tests standalone and works with any Next version.
|
|
@@ -56,16 +89,73 @@ declare function urunTokenRoute(options?: UrunTokenRouteOptions): (request: Requ
|
|
|
56
89
|
interface UrunTokenPagesRequest {
|
|
57
90
|
method?: string;
|
|
58
91
|
body?: unknown;
|
|
92
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
59
93
|
}
|
|
60
94
|
interface UrunTokenPagesResponse {
|
|
61
95
|
status(code: number): UrunTokenPagesResponse;
|
|
62
96
|
json(body: unknown): void;
|
|
63
97
|
}
|
|
98
|
+
/** The JSON shape a successful token response carries to the browser. */
|
|
99
|
+
interface UrunTokenResponseBody {
|
|
100
|
+
token: string;
|
|
101
|
+
expiresAt: string;
|
|
102
|
+
orgId: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* A fully-built Next.js App Router route handler that mints scoped client
|
|
106
|
+
* tokens after the required authorization callback approves the caller.
|
|
107
|
+
*/
|
|
108
|
+
declare function urunTokenRoute(options: UrunTokenRouteOptions): (request: Request) => Promise<Response>;
|
|
109
|
+
interface CreateTokenRouteOptions extends Omit<UrunTokenRouteOptions, 'authorize' | 'baseUrl'> {
|
|
110
|
+
/**
|
|
111
|
+
* OPTIONAL here (unlike {@link urunTokenRoute}, where it is required): the
|
|
112
|
+
* zero-config on-ramp exists precisely for public demo frontends where an
|
|
113
|
+
* anonymous browser legitimately receives a scoped short-lived token. When
|
|
114
|
+
* omitted, every caller mints (a server-side warning is logged once);
|
|
115
|
+
* provide it to restrict minting to your authenticated users.
|
|
116
|
+
*/
|
|
117
|
+
authorize?: UrunTokenRouteOptions['authorize'];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The zero-config response: everything the API KEY implies, and nothing more.
|
|
121
|
+
* The app identity is deliberately ABSENT — the frontend hardcodes its app
|
|
122
|
+
* name (`<UrunProvider app="rolling-sink">`) exactly like the backend's
|
|
123
|
+
* `App("rolling-sink")`. Owner ruling 2026-07-15: no app introspection, no
|
|
124
|
+
* inference — an app author knows their own app.
|
|
125
|
+
*/
|
|
126
|
+
interface CreateTokenRouteResponseBody extends UrunTokenResponseBody {
|
|
127
|
+
/**
|
|
128
|
+
* The platform endpoint the browser should connect to. NOT user
|
|
129
|
+
* configuration: the package hardcodes the public gateway, and the browser
|
|
130
|
+
* merely receives the server's resolution (owner ruling 2026-07-15: the
|
|
131
|
+
* gateway IS the platform front door, not something to discover or
|
|
132
|
+
* configure — users never see or set it).
|
|
133
|
+
*/
|
|
134
|
+
gatewayUrl: string;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The ENTIRE server leg of a uRun frontend, zero-config:
|
|
138
|
+
*
|
|
139
|
+
* ```ts
|
|
140
|
+
* // app/api/urun-token/route.ts
|
|
141
|
+
* export const POST = createTokenRoute()
|
|
142
|
+
* ```
|
|
143
|
+
*
|
|
144
|
+
* With only `URUN_API_KEY` in the server env (raw `pnpm dev` / `pnpm build`,
|
|
145
|
+
* no urun CLI), the route mints a scoped client token and answers
|
|
146
|
+
* `{ token, expiresAt, orgId, gatewayUrl }` — org derived from the key,
|
|
147
|
+
* gateway the hardcoded platform front door (internal dev-cluster override
|
|
148
|
+
* via the buried `URUN_GATEWAY_URL` server env, never user config). The
|
|
149
|
+
* browser side is `<UrunProvider app="your-app">` — the app name is
|
|
150
|
+
* HARDCODED in app code, mirroring the backend's `App("your-app")` (owner
|
|
151
|
+
* ruling: no introspection, no inference). The API key never ships to the
|
|
152
|
+
* browser.
|
|
153
|
+
*/
|
|
154
|
+
declare function createTokenRoute(options?: CreateTokenRouteOptions): (request: Request) => Promise<Response>;
|
|
64
155
|
/**
|
|
65
|
-
* The Pages Router variant
|
|
66
|
-
*
|
|
67
|
-
* derive identity inside your own wrapper if you need it on Pages Router.
|
|
156
|
+
* The Pages Router variant. Wrap this handler if Pages-specific subject
|
|
157
|
+
* derivation is needed; authorization receives the structural Pages request.
|
|
68
158
|
*/
|
|
69
|
-
declare function urunTokenPagesHandler(options
|
|
159
|
+
declare function urunTokenPagesHandler(options: UrunTokenRouteOptions): (req: UrunTokenPagesRequest, res: UrunTokenPagesResponse) => Promise<void>;
|
|
70
160
|
|
|
71
|
-
export { URUN_API_KEY_ENV, type UrunTokenPagesRequest, type UrunTokenPagesResponse, type UrunTokenResponseBody, type UrunTokenRouteOptions, urunTokenPagesHandler, urunTokenRoute };
|
|
161
|
+
export { type CreateTokenRouteOptions, type CreateTokenRouteResponseBody, URUN_API_KEY_ENV, URUN_APP_ENV, URUN_FUNCTION_ENV, URUN_GATEWAY_URL_ENV, type UrunSessionConfig, type UrunTokenPagesRequest, type UrunTokenPagesResponse, type UrunTokenResponseBody, type UrunTokenRouteOptions, createTokenRoute, urunSessionConfig, urunTokenPagesHandler, urunTokenRoute };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,32 +1,61 @@
|
|
|
1
1
|
import { CreateClientTokenOptions } from '@urun-sh/core';
|
|
2
2
|
|
|
3
3
|
declare const URUN_API_KEY_ENV = "URUN_API_KEY";
|
|
4
|
+
|
|
5
|
+
declare const URUN_GATEWAY_URL_ENV = "URUN_GATEWAY_URL";
|
|
6
|
+
declare const URUN_APP_ENV = "URUN_APP";
|
|
7
|
+
declare const URUN_FUNCTION_ENV = "URUN_FUNCTION";
|
|
8
|
+
|
|
9
|
+
interface UrunSessionConfig {
|
|
10
|
+
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
|
|
13
|
+
appId?: string;
|
|
14
|
+
|
|
15
|
+
functionName?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
declare function urunSessionConfig(env?: Record<string, string | undefined>): UrunSessionConfig;
|
|
4
19
|
interface UrunTokenRouteOptions extends Omit<CreateClientTokenOptions, 'fetch' | 'subject'> {
|
|
5
20
|
|
|
6
21
|
apiKey?: string;
|
|
7
22
|
|
|
23
|
+
authorize: (request: Request | UrunTokenPagesRequest) => boolean | Promise<boolean>;
|
|
24
|
+
|
|
8
25
|
subject?: (request: Request) => string | undefined | Promise<string | undefined>;
|
|
9
26
|
|
|
10
27
|
fetch?: typeof fetch;
|
|
11
28
|
}
|
|
12
29
|
|
|
13
|
-
interface UrunTokenResponseBody {
|
|
14
|
-
token: string;
|
|
15
|
-
expiresAt: string;
|
|
16
|
-
orgId: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
declare function urunTokenRoute(options?: UrunTokenRouteOptions): (request: Request) => Promise<Response>;
|
|
20
|
-
|
|
21
30
|
interface UrunTokenPagesRequest {
|
|
22
31
|
method?: string;
|
|
23
32
|
body?: unknown;
|
|
33
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
24
34
|
}
|
|
25
35
|
interface UrunTokenPagesResponse {
|
|
26
36
|
status(code: number): UrunTokenPagesResponse;
|
|
27
37
|
json(body: unknown): void;
|
|
28
38
|
}
|
|
29
39
|
|
|
30
|
-
|
|
40
|
+
interface UrunTokenResponseBody {
|
|
41
|
+
token: string;
|
|
42
|
+
expiresAt: string;
|
|
43
|
+
orgId: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
declare function urunTokenRoute(options: UrunTokenRouteOptions): (request: Request) => Promise<Response>;
|
|
47
|
+
interface CreateTokenRouteOptions extends Omit<UrunTokenRouteOptions, 'authorize' | 'baseUrl'> {
|
|
48
|
+
|
|
49
|
+
authorize?: UrunTokenRouteOptions['authorize'];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface CreateTokenRouteResponseBody extends UrunTokenResponseBody {
|
|
53
|
+
|
|
54
|
+
gatewayUrl: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
declare function createTokenRoute(options?: CreateTokenRouteOptions): (request: Request) => Promise<Response>;
|
|
58
|
+
|
|
59
|
+
declare function urunTokenPagesHandler(options: UrunTokenRouteOptions): (req: UrunTokenPagesRequest, res: UrunTokenPagesResponse) => Promise<void>;
|
|
31
60
|
|
|
32
|
-
export { URUN_API_KEY_ENV, type UrunTokenPagesRequest, type UrunTokenPagesResponse, type UrunTokenResponseBody, type UrunTokenRouteOptions, urunTokenPagesHandler, urunTokenRoute };
|
|
61
|
+
export { type CreateTokenRouteOptions, type CreateTokenRouteResponseBody, URUN_API_KEY_ENV, URUN_APP_ENV, URUN_FUNCTION_ENV, URUN_GATEWAY_URL_ENV, type UrunSessionConfig, type UrunTokenPagesRequest, type UrunTokenPagesResponse, type UrunTokenResponseBody, type UrunTokenRouteOptions, createTokenRoute, urunSessionConfig, urunTokenPagesHandler, urunTokenRoute };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createClientToken as
|
|
1
|
+
import{createClientToken as k,ClientTokenError as U,DEFAULT_GATEWAY_URL as w}from"@urun-sh/core";var g="URUN_API_KEY",y="URUN_GATEWAY_URL",b="URUN_APP",h="URUN_FUNCTION";function I(e=process.env){return{baseUrl:e[y]?.trim()||w,appId:e[b]?.trim()||void 0,functionName:e[h]?.trim()||void 0}}var x=60,u=class extends Error{};function T(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function S(e,t){let o=t&&typeof t=="object"&&!Array.isArray(t)?t:{},r={expiresIn:e.expiresIn,allowedFunctions:e.allowedFunctions,allowedOrigins:e.allowedOrigins,maxSessionS:e.maxSessionS},s=o.expiresIn;if(s!==void 0){if(typeof s!="number"||!Number.isInteger(s)||s<1)throw new u("expiresIn must be a positive integer");let a=e.expiresIn??x;if(s>a)throw new u(`expiresIn may not exceed the configured ${a}s`);r.expiresIn=s}let n=(a,d,f)=>{if(a===void 0)return d;if(!T(a))throw new u(`${f} must be an array of strings`);if(d){let l=a.filter(m=>!d.includes(m));if(l.length>0)throw new u(`${f} may only narrow the configured allowlist (rejected: ${l.join(", ")})`)}return a};r.allowedFunctions=n(o.allowedFunctions,e.allowedFunctions,"allowedFunctions"),r.allowedOrigins=n(o.allowedOrigins,e.allowedOrigins,"allowedOrigins");let i=o.maxSessionS;if(i!==void 0){if(typeof i!="number"||!Number.isInteger(i)||i<1)throw new u("maxSessionS must be a positive integer");r.maxSessionS=e.maxSessionS===void 0?i:Math.min(e.maxSessionS,i)}return r}async function R(e,t){return e.authorize?await e.authorize(t)?void 0:{status:401,body:{message:"unauthorized"}}:(console.error("[urun] token route: options.authorize is required before minting"),{status:401,body:{message:"unauthorized"}})}async function c(e,t,o){let r=e.apiKey??process.env[g];if(!r)return console.error(`[urun] token route: no API key \u2014 set ${g} or pass options.apiKey`),{status:500,body:{message:"token route not configured"}};let s;try{s=S(e,t)}catch(n){if(n instanceof u)return{status:400,body:{message:n.message}};throw n}try{let n=await k(r,{baseUrl:e.baseUrl,expiresIn:s.expiresIn,allowedFunctions:s.allowedFunctions,allowedOrigins:s.allowedOrigins,maxSessionS:s.maxSessionS,metadata:e.metadata,subject:o,fetch:e.fetch});return{status:200,body:{token:n.token,expiresAt:n.expiresAt.toISOString(),orgId:n.orgId}}}catch(n){let i=n instanceof U?`${n.message} (status ${n.status??"n/a"})`:n instanceof Error?n.message:String(n);return console.error(`[urun] token route: mint failed: ${i}`),{status:502,body:{message:"token mint failed"}}}}function P(e){return async t=>{let o;try{o=await t.json()}catch{o=void 0}let r=await R(e,t);if(r)return new Response(JSON.stringify(r.body),{status:r.status,headers:{"content-type":"application/json"}});let s=e.subject?await e.subject(t):void 0,{status:n,body:i}=await c(e,o,s);return new Response(JSON.stringify(i),{status:n,headers:{"content-type":"application/json"}})}}function O(){return(process.env[y]?.trim()||w).replace(/\/+$/,"")}var p=!1;function A(e={}){return async t=>{let o;try{o=await t.json()}catch{o=void 0}if(e.authorize){if(!await e.authorize(t))return new Response(JSON.stringify({message:"unauthorized"}),{status:401,headers:{"content-type":"application/json"}})}else p||(p=!0,console.warn("[urun] createTokenRoute: minting for ANY caller \u2014 pass { authorize } to restrict the endpoint to your authenticated users"));let r=e.subject?await e.subject(t):void 0,s=O(),{status:n,body:i}=await c({...e,baseUrl:s},o,r);if(n!==200)return new Response(JSON.stringify(i),{status:n,headers:{"content-type":"application/json"}});let a={...i,gatewayUrl:s};return new Response(JSON.stringify(a),{status:200,headers:{"content-type":"application/json"}})}}function j(e){return async(t,o)=>{if((t.method??"GET").toUpperCase()!=="POST"){o.status(405).json({message:"method not allowed"});return}let r=await R(e,t);if(r){o.status(r.status).json(r.body);return}let{status:s,body:n}=await c(e,t.body,void 0);o.status(s).json(n)}}export{g as URUN_API_KEY_ENV,b as URUN_APP_ENV,h as URUN_FUNCTION_ENV,y as URUN_GATEWAY_URL_ENV,A as createTokenRoute,I as urunSessionConfig,j as urunTokenPagesHandler,P as urunTokenRoute};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@urun-sh/next",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.29",
|
|
4
4
|
"description": "Next.js on-ramp for uRun scoped client tokens — a fully-built token route in one import and one line.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -31,22 +31,23 @@
|
|
|
31
31
|
"files": [
|
|
32
32
|
"dist"
|
|
33
33
|
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsup && node ../../scripts/sanitize-dist.mjs .",
|
|
36
|
+
"prepack": "pnpm build",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"dev": "tsup --watch"
|
|
39
|
+
},
|
|
34
40
|
"peerDependencies": {
|
|
35
|
-
"@urun-sh/core": "^0.2.
|
|
41
|
+
"@urun-sh/core": "^0.2.2"
|
|
36
42
|
},
|
|
37
43
|
"devDependencies": {
|
|
38
44
|
"@types/node": "^24.0.0",
|
|
45
|
+
"@urun-sh/core": "workspace:*",
|
|
39
46
|
"tsup": "^8.5.0",
|
|
40
47
|
"typescript": "^5.3.0",
|
|
41
|
-
"vitest": "^3.0.0"
|
|
42
|
-
"@urun-sh/core": "0.2.26"
|
|
48
|
+
"vitest": "^3.0.0"
|
|
43
49
|
},
|
|
44
50
|
"publishConfig": {
|
|
45
51
|
"access": "public"
|
|
46
|
-
},
|
|
47
|
-
"scripts": {
|
|
48
|
-
"build": "tsup && node ../../scripts/sanitize-dist.mjs .",
|
|
49
|
-
"test": "vitest run",
|
|
50
|
-
"dev": "tsup --watch"
|
|
51
52
|
}
|
|
52
|
-
}
|
|
53
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 urun contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|