@urun-sh/next 0.2.26
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/LICENSE +21 -0
- package/README.md +49 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +71 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +1 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# @urun-sh/next
|
|
2
|
+
|
|
3
|
+
Next.js on-ramp for uRun **scoped client tokens** — never ship your org API
|
|
4
|
+
key to the browser.
|
|
5
|
+
|
|
6
|
+
## One import, one line
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
// app/api/urun-token/route.ts
|
|
10
|
+
import { urunTokenRoute } from '@urun-sh/next'
|
|
11
|
+
export const POST = urunTokenRoute()
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Set the `URUN_API_KEY` env secret and you are done: the route mints
|
|
15
|
+
short-lived tokens the browser passes to the SDK through the existing `jwt` /
|
|
16
|
+
`getAccessToken` auth options.
|
|
17
|
+
|
|
18
|
+
## Scoping
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
export const POST = urunTokenRoute({
|
|
22
|
+
allowedFunctions: ['zimage/generate'], // only these app/functions
|
|
23
|
+
allowedOrigins: ['https://app.example.com'], // only from these Origins
|
|
24
|
+
maxSessionS: 900, // per-session length cap
|
|
25
|
+
expiresIn: 120, // token start-window (max 3600)
|
|
26
|
+
subject: (req) => getUserId(req), // stable per-end-user identity
|
|
27
|
+
})
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Constraints are enforced **server-side at session admission**. A per-request
|
|
31
|
+
body may only *narrow* these (subset of an allowlist, lower expiry, tighter
|
|
32
|
+
cap) — never widen, and never choose its own `subject`.
|
|
33
|
+
|
|
34
|
+
Token expiry gates **starting** sessions only: a live session rides its lease
|
|
35
|
+
and is never killed by token expiry; `maxSessionS` is the honest per-session
|
|
36
|
+
cap (folded with the function's own `max_session_s`).
|
|
37
|
+
|
|
38
|
+
## Pages Router
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
// pages/api/urun-token.ts
|
|
42
|
+
import { urunTokenPagesHandler } from '@urun-sh/next'
|
|
43
|
+
export default urunTokenPagesHandler()
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Server SDK
|
|
47
|
+
|
|
48
|
+
The underlying mint call is `createClientToken(apiKey, options)` from
|
|
49
|
+
`@urun-sh/core` — plain `fetch`, Node-safe, framework-free.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var l=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var b=(e,n)=>{for(var r in n)l(e,r,{get:n[r],enumerable:!0})},R=(e,n,r,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of k(n))!x.call(e,t)&&t!==r&&l(e,t,{get:()=>n[t],enumerable:!(o=y(n,t))||o.enumerable});return e};var S=e=>R(l({},"__esModule",{value:!0}),e);var P={};b(P,{URUN_API_KEY_ENV:()=>w,urunTokenPagesHandler:()=>U,urunTokenRoute:()=>O});module.exports=S(P);var d=require("@urun-sh/core"),w="URUN_API_KEY",I=60,a=class extends Error{};function h(e){return Array.isArray(e)&&e.every(n=>typeof n=="string")}function T(e,n){let r=n&&typeof n=="object"&&!Array.isArray(n)?n:{},o={expiresIn:e.expiresIn,allowedFunctions:e.allowedFunctions,allowedOrigins:e.allowedOrigins,maxSessionS:e.maxSessionS},t=r.expiresIn;if(t!==void 0){if(typeof t!="number"||!Number.isInteger(t)||t<1)throw new a("expiresIn must be a positive integer");let u=e.expiresIn??I;if(t>u)throw new a(`expiresIn may not exceed the configured ${u}s`);o.expiresIn=t}let s=(u,c,f)=>{if(u===void 0)return c;if(!h(u))throw new a(`${f} must be an array of strings`);if(c){let g=u.filter(p=>!c.includes(p));if(g.length>0)throw new a(`${f} may only narrow the configured allowlist (rejected: ${g.join(", ")})`)}return u};o.allowedFunctions=s(r.allowedFunctions,e.allowedFunctions,"allowedFunctions"),o.allowedOrigins=s(r.allowedOrigins,e.allowedOrigins,"allowedOrigins");let i=r.maxSessionS;if(i!==void 0){if(typeof i!="number"||!Number.isInteger(i)||i<1)throw new a("maxSessionS must be a positive integer");o.maxSessionS=e.maxSessionS===void 0?i:Math.min(e.maxSessionS,i)}return o}async function m(e,n,r){let o=e.apiKey??process.env[w];if(!o)return console.error(`[urun] token route: no API key \u2014 set ${w} or pass options.apiKey`),{status:500,body:{message:"token route not configured"}};let t;try{t=T(e,n)}catch(s){if(s instanceof a)return{status:400,body:{message:s.message}};throw s}try{let s=await(0,d.createClientToken)(o,{baseUrl:e.baseUrl,expiresIn:t.expiresIn,allowedFunctions:t.allowedFunctions,allowedOrigins:t.allowedOrigins,maxSessionS:t.maxSessionS,metadata:e.metadata,subject:r,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 O(e={}){return async n=>{let r;try{r=await n.json()}catch{r=void 0}let o=e.subject?await e.subject(n):void 0,{status:t,body:s}=await m(e,r,o);return new Response(JSON.stringify(s),{status:t,headers:{"content-type":"application/json"}})}}function U(e={}){return async(n,r)=>{if((n.method??"GET").toUpperCase()!=="POST"){r.status(405).json({message:"method not allowed"});return}let{status:o,body:t}=await m(e,n.body,void 0);r.status(o).json(t)}}0&&(module.exports={URUN_API_KEY_ENV,urunTokenPagesHandler,urunTokenRoute});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { CreateClientTokenOptions } from '@urun-sh/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @urun-sh/next — the Next.js on-ramp for uRun scoped client tokens.
|
|
5
|
+
*
|
|
6
|
+
* The whole integration is ONE import + ONE line:
|
|
7
|
+
*
|
|
8
|
+
* // app/api/urun-token/route.ts
|
|
9
|
+
* import { urunTokenRoute } from '@urun-sh/next'
|
|
10
|
+
* export const POST = urunTokenRoute()
|
|
11
|
+
*
|
|
12
|
+
* The route mints short-lived scoped client tokens with your org API key
|
|
13
|
+
* (the standard `URUN_API_KEY` env secret) via `createClientToken` from
|
|
14
|
+
* `@urun-sh/core`, so the key NEVER ships to the browser. The browser hands
|
|
15
|
+
* the minted token to the SDK through the existing `jwt` / `getAccessToken`
|
|
16
|
+
* auth options.
|
|
17
|
+
*
|
|
18
|
+
* SECURITY MODEL of the per-request body: the browser may only NARROW the
|
|
19
|
+
* scope your options configured — request a SUBSET of a configured function /
|
|
20
|
+
* origin allowlist, LOWER the expiry, and maxSessionS folds by MIN. It can
|
|
21
|
+
* never widen anything, and it can never choose its own `subject` (identity
|
|
22
|
+
* is server-derived via `options.subject`, defaulting to a random subject per
|
|
23
|
+
* token so distinct end-users never share a session).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** The standard env secret the route reads the org API key from. */
|
|
27
|
+
declare const URUN_API_KEY_ENV = "URUN_API_KEY";
|
|
28
|
+
interface UrunTokenRouteOptions extends Omit<CreateClientTokenOptions, 'fetch' | 'subject'> {
|
|
29
|
+
/** Org API key (default: `process.env.URUN_API_KEY`). */
|
|
30
|
+
apiKey?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Derive a STABLE per-end-user subject from the request (e.g. your auth
|
|
33
|
+
* session). A stable subject coalesces one user's tabs into one session;
|
|
34
|
+
* omitted, every token gets a random subject (users never share sessions).
|
|
35
|
+
* The browser body can never set this.
|
|
36
|
+
*/
|
|
37
|
+
subject?: (request: Request) => string | undefined | Promise<string | undefined>;
|
|
38
|
+
/** Fetch override (tests / custom agents). */
|
|
39
|
+
fetch?: typeof fetch;
|
|
40
|
+
}
|
|
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
|
+
/**
|
|
53
|
+
* Structural Pages Router types — no dependency on `next` itself, so the
|
|
54
|
+
* package builds/tests standalone and works with any Next version.
|
|
55
|
+
*/
|
|
56
|
+
interface UrunTokenPagesRequest {
|
|
57
|
+
method?: string;
|
|
58
|
+
body?: unknown;
|
|
59
|
+
}
|
|
60
|
+
interface UrunTokenPagesResponse {
|
|
61
|
+
status(code: number): UrunTokenPagesResponse;
|
|
62
|
+
json(body: unknown): void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The Pages Router variant: `export default urunTokenPagesHandler()`.
|
|
66
|
+
* NOTE: `options.subject` receives no Request here (Pages req is structural);
|
|
67
|
+
* derive identity inside your own wrapper if you need it on Pages Router.
|
|
68
|
+
*/
|
|
69
|
+
declare function urunTokenPagesHandler(options?: UrunTokenRouteOptions): (req: UrunTokenPagesRequest, res: UrunTokenPagesResponse) => Promise<void>;
|
|
70
|
+
|
|
71
|
+
export { URUN_API_KEY_ENV, type UrunTokenPagesRequest, type UrunTokenPagesResponse, type UrunTokenResponseBody, type UrunTokenRouteOptions, urunTokenPagesHandler, urunTokenRoute };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { CreateClientTokenOptions } from '@urun-sh/core';
|
|
2
|
+
|
|
3
|
+
declare const URUN_API_KEY_ENV = "URUN_API_KEY";
|
|
4
|
+
interface UrunTokenRouteOptions extends Omit<CreateClientTokenOptions, 'fetch' | 'subject'> {
|
|
5
|
+
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
|
|
8
|
+
subject?: (request: Request) => string | undefined | Promise<string | undefined>;
|
|
9
|
+
|
|
10
|
+
fetch?: typeof fetch;
|
|
11
|
+
}
|
|
12
|
+
|
|
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
|
+
interface UrunTokenPagesRequest {
|
|
22
|
+
method?: string;
|
|
23
|
+
body?: unknown;
|
|
24
|
+
}
|
|
25
|
+
interface UrunTokenPagesResponse {
|
|
26
|
+
status(code: number): UrunTokenPagesResponse;
|
|
27
|
+
json(body: unknown): void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
declare function urunTokenPagesHandler(options?: UrunTokenRouteOptions): (req: UrunTokenPagesRequest, res: UrunTokenPagesResponse) => Promise<void>;
|
|
31
|
+
|
|
32
|
+
export { URUN_API_KEY_ENV, type UrunTokenPagesRequest, type UrunTokenPagesResponse, type UrunTokenResponseBody, type UrunTokenRouteOptions, urunTokenPagesHandler, urunTokenRoute };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createClientToken as m,ClientTokenError as p}from"@urun-sh/core";var w="URUN_API_KEY",y=60,a=class extends Error{};function k(e){return Array.isArray(e)&&e.every(s=>typeof s=="string")}function x(e,s){let r=s&&typeof s=="object"&&!Array.isArray(s)?s:{},o={expiresIn:e.expiresIn,allowedFunctions:e.allowedFunctions,allowedOrigins:e.allowedOrigins,maxSessionS:e.maxSessionS},t=r.expiresIn;if(t!==void 0){if(typeof t!="number"||!Number.isInteger(t)||t<1)throw new a("expiresIn must be a positive integer");let u=e.expiresIn??y;if(t>u)throw new a(`expiresIn may not exceed the configured ${u}s`);o.expiresIn=t}let n=(u,d,c)=>{if(u===void 0)return d;if(!k(u))throw new a(`${c} must be an array of strings`);if(d){let l=u.filter(g=>!d.includes(g));if(l.length>0)throw new a(`${c} may only narrow the configured allowlist (rejected: ${l.join(", ")})`)}return u};o.allowedFunctions=n(r.allowedFunctions,e.allowedFunctions,"allowedFunctions"),o.allowedOrigins=n(r.allowedOrigins,e.allowedOrigins,"allowedOrigins");let i=r.maxSessionS;if(i!==void 0){if(typeof i!="number"||!Number.isInteger(i)||i<1)throw new a("maxSessionS must be a positive integer");o.maxSessionS=e.maxSessionS===void 0?i:Math.min(e.maxSessionS,i)}return o}async function f(e,s,r){let o=e.apiKey??process.env[w];if(!o)return console.error(`[urun] token route: no API key \u2014 set ${w} or pass options.apiKey`),{status:500,body:{message:"token route not configured"}};let t;try{t=x(e,s)}catch(n){if(n instanceof a)return{status:400,body:{message:n.message}};throw n}try{let n=await m(o,{baseUrl:e.baseUrl,expiresIn:t.expiresIn,allowedFunctions:t.allowedFunctions,allowedOrigins:t.allowedOrigins,maxSessionS:t.maxSessionS,metadata:e.metadata,subject:r,fetch:e.fetch});return{status:200,body:{token:n.token,expiresAt:n.expiresAt.toISOString(),orgId:n.orgId}}}catch(n){let i=n instanceof p?`${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 R(e={}){return async s=>{let r;try{r=await s.json()}catch{r=void 0}let o=e.subject?await e.subject(s):void 0,{status:t,body:n}=await f(e,r,o);return new Response(JSON.stringify(n),{status:t,headers:{"content-type":"application/json"}})}}function S(e={}){return async(s,r)=>{if((s.method??"GET").toUpperCase()!=="POST"){r.status(405).json({message:"method not allowed"});return}let{status:o,body:t}=await f(e,s.body,void 0);r.status(o).json(t)}}export{w as URUN_API_KEY_ENV,S as urunTokenPagesHandler,R as urunTokenRoute};
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@urun-sh/next",
|
|
3
|
+
"version": "0.2.26",
|
|
4
|
+
"description": "Next.js on-ramp for uRun scoped client tokens — a fully-built token route in one import and one line.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/urun-sh/urun-ts.git",
|
|
8
|
+
"directory": "packages/next"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/urun-sh/urun-ts/issues"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/urun-sh/urun-ts/tree/main/packages/next#readme",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./dist/index.cjs",
|
|
16
|
+
"module": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"import": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"require": {
|
|
25
|
+
"types": "./dist/index.d.cts",
|
|
26
|
+
"default": "./dist/index.cjs"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@urun-sh/core": "^0.2.26"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^24.0.0",
|
|
39
|
+
"tsup": "^8.5.0",
|
|
40
|
+
"typescript": "^5.3.0",
|
|
41
|
+
"vitest": "^3.0.0",
|
|
42
|
+
"@urun-sh/core": "0.2.26"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsup && node ../../scripts/sanitize-dist.mjs .",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"dev": "tsup --watch"
|
|
51
|
+
}
|
|
52
|
+
}
|