New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@rabstack/rab-react-sdk

Package Overview
Dependencies
Maintainers
2
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@rabstack/rab-react-sdk - npm Package Compare versions

Comparing version
0.7.0
to
0.8.0
+193
createSDK-BykB15KZ.js
function C(...t) {
if (t.length === 0) return "";
let r = [...t].filter(Boolean);
const o = r[r.length - 1];
if (typeof o == "object" && o !== null) {
const s = o;
r = r.slice(0, -1).map((e) => {
if (typeof e == "string") {
let a = e;
for (const n in s)
a = a.replace(`:${n}`, String(s[n]));
return a;
}
return e;
});
}
return r.filter(
(s) => typeof s == "string" && s.trim() !== ""
).map(
(s, e) => s.startsWith("/") || e === 0 ? s : "/" + s
).join("");
}
function $(t) {
const { endpointUrl: r, baseUrl: o, params: s, appendTrailingSlash: e, method: a } = t, n = C(r, s), c = o.replace(/\/$/, "");
let i = n.startsWith("/") ? n : "/" + n;
return e && a && ["POST", "PUT", "PATCH"].includes(a) && !i.endsWith("/") && (i = i + "/"), new URL(c + i);
}
function k(t, r, o, s) {
if (o && s !== !1) {
const e = o(r);
if (e) {
const a = e.split("&");
for (const n of a) {
const [c, i] = n.split("=");
c && i !== void 0 && t.searchParams.append(decodeURIComponent(c), decodeURIComponent(i));
}
}
} else
Object.entries(r).forEach(([e, a]) => {
a != null && t.searchParams.append(e, String(a));
});
}
function x(t, r, o) {
if (!!(r && t)) {
if (t instanceof FormData)
return { processedBody: t, isFormDataRequest: !0 };
if (o)
return { processedBody: o(t), isFormDataRequest: !0 };
throw new Error("useFormData requires formDataParser in config or body to be FormData instance");
}
return {
processedBody: t ? JSON.stringify(t) : void 0,
isFormDataRequest: !1
};
}
class d extends Error {
constructor(r) {
console.error(`API Error ${r.status}: ${r.message} [${r.method} ${r.url}]`), super(r.message), this.name = "ApiDomainError", this.status = r.status, this.url = r.url, this.method = r.method, this.errorCode = r.errorCode, this.errors = r.errors, this.data = r.data, Error.captureStackTrace && Error.captureStackTrace(this, d);
}
isNetworkError() {
return this.status === 0;
}
isClientError() {
return this.status >= 400 && this.status < 500;
}
isServerError() {
return this.status >= 500;
}
isUnauthorized() {
return this.status === 401;
}
isForbidden() {
return this.status === 403;
}
isNotFound() {
return this.status === 404;
}
isValidationError() {
return this.status === 422 || this.status === 400;
}
isConflict() {
return this.status === 409;
}
getAllMessages() {
const r = [this.message];
return this.errors && this.errors.length > 0 && r.push(...this.errors), r;
}
getDisplayMessage() {
return this.errors && this.errors.length > 0 ? `${this.message}: ${this.errors.join(", ")}` : this.message;
}
}
function B(t, r, o, s, e) {
return {
status: t,
url: o,
method: s,
message: e?.message || r,
errorCode: e?.errorCode,
errors: e?.errors,
data: e
};
}
function z(t) {
return typeof t == "object" && t !== null && "data" in t ? t.data : t;
}
function O(t) {
const {
baseUrl: r,
getAuthorization: o,
extractApiError: s = B,
parseQuery: e,
transformResponse: a = z,
onError: n,
appendTrailingSlash: c,
formDataParser: i,
customFetch: g
} = t;
return async (h, y) => {
try {
const { body: u, query: f, params: m, encodeQuery: E, headers: P, useFormData: b, ...q } = y ?? {}, D = $({
endpointUrl: h.url,
baseUrl: r,
params: m ?? {},
appendTrailingSlash: c,
method: h.method
});
f && k(D, f, e, E);
const { processedBody: j, isFormDataRequest: T } = x(u, b, i), F = {
"Content-Type": T ? "multipart/form-data" : "application/json",
...P
}, R = o?.();
R && Object.assign(F, R);
const p = {
url: D.toString(),
method: h.method,
headers: F,
body: j,
...q
}, l = g ? await g(p) : await fetch(p.url, {
method: p.method,
headers: p.headers,
body: p.body
});
if (!l.ok) {
let w;
try {
w = await l.clone().json();
} catch {
w = await l.text();
}
const A = s(l.status, l.statusText, D.toString(), h.method, w), S = new d(A);
throw n?.(S), S;
}
const U = await l.json();
return a(U, l);
} catch (u) {
if (u instanceof d)
throw u;
const f = {
status: 0,
url: `${r}${h.url}`,
method: h.method,
message: u instanceof Error ? u.message : "Network request failed",
data: u
}, m = new d(f);
throw n?.(m), m;
}
};
}
function I(t) {
const { apiSpecs: r, baseUrl: o, getAuthorization: s, extractApiError: e, parseQuery: a, transformResponse: n, onError: c, appendTrailingSlash: i, formDataParser: g, customFetch: h } = t, y = O({
baseUrl: o,
getAuthorization: s,
extractApiError: e,
parseQuery: a,
transformResponse: n,
onError: c,
appendTrailingSlash: i,
formDataParser: g,
customFetch: h
}), u = {};
for (const [f, m] of Object.entries(r))
u[f] = ((E) => y(m, E));
return u;
}
export {
d as A,
O as a,
B as b,
I as c,
z as d,
C as e
};
"use strict";function P(...t){if(t.length===0)return"";let r=[...t].filter(Boolean);const o=r[r.length-1];if(typeof o=="object"&&o!==null){const s=o;r=r.slice(0,-1).map(e=>{if(typeof e=="string"){let a=e;for(const n in s)a=a.replace(`:${n}`,String(s[n]));return a}return e})}return r.filter(s=>typeof s=="string"&&s.trim()!=="").map((s,e)=>s.startsWith("/")||e===0?s:"/"+s).join("")}function B(t){const{endpointUrl:r,baseUrl:o,params:s,appendTrailingSlash:e,method:a}=t,n=P(r,s),c=o.replace(/\/$/,"");let i=n.startsWith("/")?n:"/"+n;return e&&a&&["POST","PUT","PATCH"].includes(a)&&!i.endsWith("/")&&(i=i+"/"),new URL(c+i)}function z(t,r,o,s){if(o&&s!==!1){const e=o(r);if(e){const a=e.split("&");for(const n of a){const[c,i]=n.split("=");c&&i!==void 0&&t.searchParams.append(decodeURIComponent(c),decodeURIComponent(i))}}}else Object.entries(r).forEach(([e,a])=>{a!=null&&t.searchParams.append(e,String(a))})}function O(t,r,o){if(!!(r&&t)){if(t instanceof FormData)return{processedBody:t,isFormDataRequest:!0};if(o)return{processedBody:o(t),isFormDataRequest:!0};throw new Error("useFormData requires formDataParser in config or body to be FormData instance")}return{processedBody:t?JSON.stringify(t):void 0,isFormDataRequest:!1}}class p extends Error{constructor(r){console.error(`API Error ${r.status}: ${r.message} [${r.method} ${r.url}]`),super(r.message),this.name="ApiDomainError",this.status=r.status,this.url=r.url,this.method=r.method,this.errorCode=r.errorCode,this.errors=r.errors,this.data=r.data,Error.captureStackTrace&&Error.captureStackTrace(this,p)}isNetworkError(){return this.status===0}isClientError(){return this.status>=400&&this.status<500}isServerError(){return this.status>=500}isUnauthorized(){return this.status===401}isForbidden(){return this.status===403}isNotFound(){return this.status===404}isValidationError(){return this.status===422||this.status===400}isConflict(){return this.status===409}getAllMessages(){const r=[this.message];return this.errors&&this.errors.length>0&&r.push(...this.errors),r}getDisplayMessage(){return this.errors&&this.errors.length>0?`${this.message}: ${this.errors.join(", ")}`:this.message}}function q(t,r,o,s,e){return{status:t,url:o,method:s,message:e?.message||r,errorCode:e?.errorCode,errors:e?.errors,data:e}}function b(t){return typeof t=="object"&&t!==null&&"data"in t?t.data:t}function T(t){const{baseUrl:r,getAuthorization:o,extractApiError:s=q,parseQuery:e,transformResponse:a=b,onError:n,appendTrailingSlash:c,formDataParser:i,customFetch:g}=t;return async(h,y)=>{try{const{body:u,query:f,params:m,encodeQuery:E,headers:U,useFormData:j,...A}=y??{},D=B({endpointUrl:h.url,baseUrl:r,params:m??{},appendTrailingSlash:c,method:h.method});f&&z(D,f,e,E);const{processedBody:C,isFormDataRequest:$}=O(u,j,i),F={"Content-Type":$?"multipart/form-data":"application/json",...U},S=o?.();S&&Object.assign(F,S);const d={url:D.toString(),method:h.method,headers:F,body:C,...A},l=g?await g(d):await fetch(d.url,{method:d.method,headers:d.headers,body:d.body});if(!l.ok){let R;try{R=await l.clone().json()}catch{R=await l.text()}const x=s(l.status,l.statusText,D.toString(),h.method,R),w=new p(x);throw n?.(w),w}const k=await l.json();return a(k,l)}catch(u){if(u instanceof p)throw u;const f={status:0,url:`${r}${h.url}`,method:h.method,message:u instanceof Error?u.message:"Network request failed",data:u},m=new p(f);throw n?.(m),m}}}function I(t){const{apiSpecs:r,baseUrl:o,getAuthorization:s,extractApiError:e,parseQuery:a,transformResponse:n,onError:c,appendTrailingSlash:i,formDataParser:g,customFetch:h}=t,y=T({baseUrl:o,getAuthorization:s,extractApiError:e,parseQuery:a,transformResponse:n,onError:c,appendTrailingSlash:i,formDataParser:g,customFetch:h}),u={};for(const[f,m]of Object.entries(r))u[f]=(E=>y(m,E));return u}exports.ApiDomainError=p;exports.buildUrlPath=P;exports.createRequestFunction=T;exports.createSDK=I;exports.defaultExtractApiError=q;exports.defaultTransformResponse=b;
+1
-1

@@ -1,1 +0,1 @@

"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const P=require("react/jsx-runtime"),S=require("react"),D=require("@tanstack/react-query"),c=require("./createSDK-DmyWwBIp.cjs"),y=require("@rabstack/rab-api-spec");function f(){const t=S.createContext(null);function d({children:e,sdk:s,baseUrl:a}){return P.jsx(t.Provider,{value:{sdk:s,baseUrl:a},children:e})}function n(){const e=S.useContext(t);if(!e)throw new Error("useSDK must be used within SDKProvider");return e.sdk}return{SDKContext:t,SDKProvider:d,useSDK:n}}function m(t){return function(n,...e){const s=t(),a=e[0],{query:u,queryKey:o,params:r,headers:i,encodeQuery:l,...p}=a||{},K=[n,u,r];return D.useQuery({queryKey:o?[...K,...o]:K,queryFn:async()=>{const b=s[n];return b({query:u,params:r,headers:i,encodeQuery:l})},...p})}}function A(t){return function(n,e){const s=t(),a=D.useQueryClient(),{invalidateQueries:u,...o}=e||{};return D.useMutation({mutationFn:async r=>{const i=s[n];return i(r)},onSettled:async(...r)=>{o.onSettled&&await o.onSettled(...r),u&&u.length>0&&await Promise.all(u.map(i=>a.invalidateQueries({queryKey:[i]})))},...o})}}function q(t){const{SDKContext:d,SDKProvider:n,useSDK:e}=f();function s({children:o,baseUrl:r,getAuthorization:i,onError:l}){const p=S.useMemo(()=>c.createSDK({...t,baseUrl:r,getAuthorization:i||t.getAuthorization,onError:l||t.onError}),[r,i,l]);return P.jsx(n,{sdk:p,baseUrl:r,children:o})}const a=m(e),u=A(e);return{SDKContext:d,SDKProvider:s,useSDK:e,useQuery:a,useMutation:u}}exports.ApiDomainError=c.ApiDomainError;exports.buildUrlPath=c.buildUrlPath;exports.createRequestFunction=c.createRequestFunction;exports.createSDK=c.createSDK;exports.defaultExtractApiError=c.defaultExtractApiError;exports.defaultTransformResponse=c.defaultTransformResponse;Object.defineProperty(exports,"DeleteApi",{enumerable:!0,get:()=>y.DeleteApi});Object.defineProperty(exports,"GetApi",{enumerable:!0,get:()=>y.GetApi});Object.defineProperty(exports,"PatchApi",{enumerable:!0,get:()=>y.PatchApi});Object.defineProperty(exports,"PostApi",{enumerable:!0,get:()=>y.PostApi});Object.defineProperty(exports,"PutApi",{enumerable:!0,get:()=>y.PutApi});exports.createSDKContext=f;exports.createSDKProvider=q;exports.createUseMutation=A;exports.createUseQuery=m;
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const P=require("react/jsx-runtime"),S=require("react"),D=require("@tanstack/react-query"),c=require("./createSDK-DH-GKtYY.cjs"),y=require("@rabstack/rab-api-spec");function f(){const t=S.createContext(null);function d({children:e,sdk:s,baseUrl:a}){return P.jsx(t.Provider,{value:{sdk:s,baseUrl:a},children:e})}function n(){const e=S.useContext(t);if(!e)throw new Error("useSDK must be used within SDKProvider");return e.sdk}return{SDKContext:t,SDKProvider:d,useSDK:n}}function m(t){return function(n,...e){const s=t(),a=e[0],{query:u,queryKey:o,params:r,headers:i,encodeQuery:l,...p}=a||{},K=[n,u,r];return D.useQuery({queryKey:o?[...K,...o]:K,queryFn:async()=>{const b=s[n];return b({query:u,params:r,headers:i,encodeQuery:l})},...p})}}function A(t){return function(n,e){const s=t(),a=D.useQueryClient(),{invalidateQueries:u,...o}=e||{};return D.useMutation({mutationFn:async r=>{const i=s[n];return i(r)},onSettled:async(...r)=>{o.onSettled&&await o.onSettled(...r),u&&u.length>0&&await Promise.all(u.map(i=>a.invalidateQueries({queryKey:[i]})))},...o})}}function q(t){const{SDKContext:d,SDKProvider:n,useSDK:e}=f();function s({children:o,baseUrl:r,getAuthorization:i,onError:l}){const p=S.useMemo(()=>c.createSDK({...t,baseUrl:r,getAuthorization:i||t.getAuthorization,onError:l||t.onError}),[r,i,l]);return P.jsx(n,{sdk:p,baseUrl:r,children:o})}const a=m(e),u=A(e);return{SDKContext:d,SDKProvider:s,useSDK:e,useQuery:a,useMutation:u}}exports.ApiDomainError=c.ApiDomainError;exports.buildUrlPath=c.buildUrlPath;exports.createRequestFunction=c.createRequestFunction;exports.createSDK=c.createSDK;exports.defaultExtractApiError=c.defaultExtractApiError;exports.defaultTransformResponse=c.defaultTransformResponse;Object.defineProperty(exports,"DeleteApi",{enumerable:!0,get:()=>y.DeleteApi});Object.defineProperty(exports,"GetApi",{enumerable:!0,get:()=>y.GetApi});Object.defineProperty(exports,"PatchApi",{enumerable:!0,get:()=>y.PatchApi});Object.defineProperty(exports,"PostApi",{enumerable:!0,get:()=>y.PostApi});Object.defineProperty(exports,"PutApi",{enumerable:!0,get:()=>y.PutApi});exports.createSDKContext=f;exports.createSDKProvider=q;exports.createUseMutation=A;exports.createUseQuery=m;

@@ -53,4 +53,5 @@ import { default as default_2 } from 'react';

* Base config shared between request function and SDK provider
* @template TRequestOptions - Extended request options type passed to customFetch
*/
declare interface BaseRequestConfig {
declare interface BaseRequestConfig<TRequestOptions = EmptyRequestOptions> {
getAuthorization?: () => Record<string, string> | undefined;

@@ -70,2 +71,15 @@ extractApiError?: (status: number, statusText: string, url: string, method: string, data?: any) => ApiErrorDetails;

formDataParser?: <T>(body: T) => FormData;
/**
* Custom fetch function to replace the default fetch
* Receives request info merged with extended options from SDK method calls
* @example
* customFetch: async (request) => {
* return fetch(request.url, {
* method: request.method,
* headers: request.headers,
* body: request.body,
* });
* }
*/
customFetch?: (request: CustomFetchRequest<TRequestOptions>) => Promise<Response>;
}

@@ -91,8 +105,9 @@

export declare function createRequestFunction(config: CreateRequestFunctionConfig): <RESPONSE, BODY = unknown, QUERY = unknown, PARAMS = unknown>(endpoint: EndpointSpec<RESPONSE, BODY, QUERY, PARAMS>, options?: BaseSdkRequestOptions<BODY, QUERY, PARAMS>) => Promise<RESPONSE>;
export declare function createRequestFunction<TRequestOptions = EmptyRequestOptions>(config: CreateRequestFunctionConfig<TRequestOptions>): <RESPONSE, BODY = unknown, QUERY = unknown, PARAMS = unknown>(endpoint: EndpointSpec<RESPONSE, BODY, QUERY, PARAMS>, options?: BaseSdkRequestOptions<BODY, QUERY, PARAMS> & TRequestOptions) => Promise<RESPONSE>;
/**
* Config for creating request function
* @template TRequestOptions - Extended request options type
*/
export declare interface CreateRequestFunctionConfig extends BaseRequestConfig {
export declare interface CreateRequestFunctionConfig<TRequestOptions = EmptyRequestOptions> extends BaseRequestConfig<TRequestOptions> {
baseUrl: string;

@@ -103,6 +118,8 @@ }

* Create SDK instance from API specs
* @template TApiSpecs - API specs record
* @template TRequestOptions - Extended request options type for customFetch
*/
export declare function createSDK<TApiSpecs extends Record<string, EndpointSpec<any, any, any, any>>>(config: CreateSDKProviderConfig<TApiSpecs> & {
export declare function createSDK<TApiSpecs extends Record<string, EndpointSpec<any, any, any, any>>, TRequestOptions = EmptyRequestOptions>(config: CreateSDKProviderConfig<TApiSpecs, TRequestOptions> & {
baseUrl: string;
}): GeneratedSDK<TApiSpecs>;
}): GeneratedSDK<TApiSpecs, TRequestOptions>;

@@ -119,3 +136,3 @@ /**

}) => JSX_2.Element;
useSDK: () => GeneratedSDK<TApiSpecs>;
useSDK: () => GeneratedSDK<TApiSpecs, EmptyRequestOptions>;
};

@@ -135,3 +152,3 @@

}) => JSX_2.Element;
useSDK: () => GeneratedSDK<TApiSpecs>;
useSDK: () => GeneratedSDK<TApiSpecs, EmptyRequestOptions>;
useQuery: TypedUseQuery<TApiSpecs>;

@@ -143,4 +160,6 @@ useMutation: TypedUseMutation<TApiSpecs>;

* Config for createSDKProvider
* @template TApiSpecs - API specs record
* @template TRequestOptions - Extended request options type
*/
export declare interface CreateSDKProviderConfig<TApiSpecs extends Record<string, EndpointSpec<any, any, any, any>>> extends BaseRequestConfig {
export declare interface CreateSDKProviderConfig<TApiSpecs extends Record<string, EndpointSpec<any, any, any, any>>, TRequestOptions = EmptyRequestOptions> extends BaseRequestConfig<TRequestOptions> {
apiSpecs: TApiSpecs;

@@ -162,2 +181,13 @@ }

/**
* Request info passed to customFetch function
* @template TRequestOptions - Extended request options from SDK method calls
*/
export declare type CustomFetchRequest<TRequestOptions = EmptyRequestOptions> = {
url: string;
method: string;
headers: Record<string, string>;
body?: BodyInit;
} & TRequestOptions;
/**
* Default error extractor - extracts basic error details

@@ -177,2 +207,9 @@ */

/**
* Empty object type for default TRequestOptions
* Using interface instead of Record<string, never> because it works correctly with intersections
*/
export declare interface EmptyRequestOptions {
}
export { EndpointSpec }

@@ -202,5 +239,7 @@

* Generate SDK type from API specs
* @template TApiSpecs - API specs record
* @template TRequestOptions - Extended request options type
*/
export declare type GeneratedSDK<TApiSpecs extends Record<string, EndpointSpec<any, any, any, any>>> = {
[K in keyof TApiSpecs]: SDKMethod<TApiSpecs[K]>;
export declare type GeneratedSDK<TApiSpecs extends Record<string, EndpointSpec<any, any, any, any>>, TRequestOptions = EmptyRequestOptions> = {
[K in keyof TApiSpecs]: SDKMethod<TApiSpecs[K], TRequestOptions>;
};

@@ -226,4 +265,6 @@

* SDK method signature from endpoint spec
* @template T - Endpoint spec type
* @template TRequestOptions - Extended request options type
*/
export declare type SDKMethod<T> = T extends EndpointSpec<infer R, infer B, infer Q, infer P> ? (options?: SdkRequestOptions<B, Q, P>) => Promise<R> : never;
export declare type SDKMethod<T, TRequestOptions = EmptyRequestOptions> = T extends EndpointSpec<infer R, infer B, infer Q, infer P> ? (options?: SdkRequestOptions<B, Q, P> & TRequestOptions) => Promise<R> : never;

@@ -230,0 +271,0 @@ /**

import { jsx as f } from "react/jsx-runtime";
import { createContext as K, useContext as S, useMemo as D } from "react";
import { useQuery as l, useQueryClient as P, useMutation as x } from "@tanstack/react-query";
import { c as Q } from "./createSDK-B_c0ZLmm.js";
import { A as b, e as O, a as R, b as j, d as z } from "./createSDK-B_c0ZLmm.js";
import { c as Q } from "./createSDK-BykB15KZ.js";
import { A as b, e as O, a as R, b as j, d as z } from "./createSDK-BykB15KZ.js";
import { DeleteApi as G, GetApi as T, PatchApi as U, PostApi as g, PutApi as H } from "@rabstack/rab-api-spec";

@@ -7,0 +7,0 @@ function v() {

{
"name": "@rabstack/rab-react-sdk",
"version": "0.7.0",
"version": "0.8.0",
"description": "React SDK for building applications with React Query and other utilities",

@@ -5,0 +5,0 @@ "keywords": [

+183
-0

@@ -924,2 +924,185 @@ # @rabstack/rab-react-sdk

### Server-Side SDK with Next.js
The SDK provides a server-compatible entry point for use in Next.js Server Components, API routes, and other server-side contexts. It supports custom fetch functions and extended request options for features like Next.js revalidation.
#### Basic Server-Side Usage
```typescript
// lib/server-sdk.ts
import { createSDK } from '@rabstack/rab-react-sdk/server';
import { apiSpecs } from './api-specs';
export const serverSdk = createSDK({
apiSpecs,
baseUrl: process.env.API_URL!,
getAuthorization: () => ({
Authorization: `Bearer ${process.env.API_SECRET}`,
}),
});
// app/products/page.tsx (Server Component)
import { serverSdk } from '@/lib/server-sdk';
export default async function ProductsPage() {
const products = await serverSdk.listProducts();
return (
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
```
#### Custom Fetch with Next.js Revalidation
For Next.js Server Components, you can pass a custom fetch function with extended options like `next.revalidate` for ISR (Incremental Static Regeneration) and `next.tags` for on-demand revalidation.
**Step 1: Define Extended Request Options**
```typescript
// lib/next-sdk.ts
import { createSDK, CustomFetchRequest } from '@rabstack/rab-react-sdk/server';
import { apiSpecs } from './api-specs';
// Define Next.js-specific request options
interface NextRequestOptions {
next?: {
revalidate?: number | false;
tags?: string[];
};
cache?: RequestCache;
}
// Create SDK with extended options
export const serverSdk = createSDK<typeof apiSpecs, NextRequestOptions>({
apiSpecs,
baseUrl: process.env.API_URL!,
getAuthorization: () => ({
Authorization: `Bearer ${process.env.API_SECRET}`,
}),
// Custom fetch that passes Next.js options
customFetch: async (request: CustomFetchRequest<NextRequestOptions>) => {
return fetch(request.url, {
method: request.method,
headers: request.headers,
body: request.body,
next: request.next,
cache: request.cache,
});
},
});
```
**Step 2: Use in Server Components with Revalidation**
```typescript
// app/products/page.tsx
import { serverSdk } from '@/lib/next-sdk';
export default async function ProductsPage() {
// Revalidate every 60 seconds (ISR)
const products = await serverSdk.listProducts({
next: { revalidate: 60 },
});
return (
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
// app/products/[id]/page.tsx
import { serverSdk } from '@/lib/next-sdk';
export default async function ProductPage({ params }: { params: { id: string } }) {
// Use tags for on-demand revalidation
const product = await serverSdk.getProduct({
params: { productId: params.id },
next: {
revalidate: 3600, // 1 hour
tags: [`product-${params.id}`],
},
});
return <div>{product.name}</div>;
}
```
**Step 3: On-Demand Revalidation (API Route)**
```typescript
// app/api/revalidate/route.ts
import { revalidateTag } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const { productId } = await request.json();
// Revalidate the specific product
revalidateTag(`product-${productId}`);
return NextResponse.json({ revalidated: true });
}
```
#### Common Patterns
**No Cache (Always Fresh)**
```typescript
const data = await serverSdk.getProfile({
cache: 'no-store',
});
```
**Static with Revalidation**
```typescript
const data = await serverSdk.listProducts({
next: { revalidate: 300 }, // 5 minutes
});
```
**Force Cache (Static)**
```typescript
const data = await serverSdk.getSettings({
cache: 'force-cache',
});
```
**Tagged for On-Demand Revalidation**
```typescript
const data = await serverSdk.getUser({
params: { userId },
next: {
revalidate: false, // Never auto-revalidate
tags: ['users', `user-${userId}`],
},
});
```
#### Type Safety
The extended options are fully type-safe. TypeScript will autocomplete and validate the options:
```typescript
// ✅ Valid - all options are typed
serverSdk.getProduct({
params: { productId: '123' },
next: { revalidate: 60, tags: ['products'] },
cache: 'force-cache',
});
// ❌ TypeScript error - invalid cache value
serverSdk.getProduct({
params: { productId: '123' },
cache: 'invalid-value', // Error!
});
```
## What's New in v0.0.1

@@ -926,0 +1109,0 @@

@@ -1,1 +0,1 @@

"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./createSDK-DmyWwBIp.cjs"),t=require("@rabstack/rab-api-spec");exports.ApiDomainError=e.ApiDomainError;exports.buildUrlPath=e.buildUrlPath;exports.createRequestFunction=e.createRequestFunction;exports.createSDK=e.createSDK;exports.defaultExtractApiError=e.defaultExtractApiError;exports.defaultTransformResponse=e.defaultTransformResponse;Object.defineProperty(exports,"DeleteApi",{enumerable:!0,get:()=>t.DeleteApi});Object.defineProperty(exports,"GetApi",{enumerable:!0,get:()=>t.GetApi});Object.defineProperty(exports,"PatchApi",{enumerable:!0,get:()=>t.PatchApi});Object.defineProperty(exports,"PostApi",{enumerable:!0,get:()=>t.PostApi});Object.defineProperty(exports,"PutApi",{enumerable:!0,get:()=>t.PutApi});
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./createSDK-DH-GKtYY.cjs"),t=require("@rabstack/rab-api-spec");exports.ApiDomainError=e.ApiDomainError;exports.buildUrlPath=e.buildUrlPath;exports.createRequestFunction=e.createRequestFunction;exports.createSDK=e.createSDK;exports.defaultExtractApiError=e.defaultExtractApiError;exports.defaultTransformResponse=e.defaultTransformResponse;Object.defineProperty(exports,"DeleteApi",{enumerable:!0,get:()=>t.DeleteApi});Object.defineProperty(exports,"GetApi",{enumerable:!0,get:()=>t.GetApi});Object.defineProperty(exports,"PatchApi",{enumerable:!0,get:()=>t.PatchApi});Object.defineProperty(exports,"PostApi",{enumerable:!0,get:()=>t.PostApi});Object.defineProperty(exports,"PutApi",{enumerable:!0,get:()=>t.PutApi});

@@ -46,4 +46,5 @@ import { DeleteApi } from '@rabstack/rab-api-spec';

* Base config shared between request function and SDK provider
* @template TRequestOptions - Extended request options type passed to customFetch
*/
declare interface BaseRequestConfig {
declare interface BaseRequestConfig<TRequestOptions = EmptyRequestOptions> {
getAuthorization?: () => Record<string, string> | undefined;

@@ -63,2 +64,15 @@ extractApiError?: (status: number, statusText: string, url: string, method: string, data?: any) => ApiErrorDetails;

formDataParser?: <T>(body: T) => FormData;
/**
* Custom fetch function to replace the default fetch
* Receives request info merged with extended options from SDK method calls
* @example
* customFetch: async (request) => {
* return fetch(request.url, {
* method: request.method,
* headers: request.headers,
* body: request.body,
* });
* }
*/
customFetch?: (request: CustomFetchRequest<TRequestOptions>) => Promise<Response>;
}

@@ -84,8 +98,9 @@

export declare function createRequestFunction(config: CreateRequestFunctionConfig): <RESPONSE, BODY = unknown, QUERY = unknown, PARAMS = unknown>(endpoint: EndpointSpec<RESPONSE, BODY, QUERY, PARAMS>, options?: BaseSdkRequestOptions<BODY, QUERY, PARAMS>) => Promise<RESPONSE>;
export declare function createRequestFunction<TRequestOptions = EmptyRequestOptions>(config: CreateRequestFunctionConfig<TRequestOptions>): <RESPONSE, BODY = unknown, QUERY = unknown, PARAMS = unknown>(endpoint: EndpointSpec<RESPONSE, BODY, QUERY, PARAMS>, options?: BaseSdkRequestOptions<BODY, QUERY, PARAMS> & TRequestOptions) => Promise<RESPONSE>;
/**
* Config for creating request function
* @template TRequestOptions - Extended request options type
*/
export declare interface CreateRequestFunctionConfig extends BaseRequestConfig {
export declare interface CreateRequestFunctionConfig<TRequestOptions = EmptyRequestOptions> extends BaseRequestConfig<TRequestOptions> {
baseUrl: string;

@@ -96,11 +111,15 @@ }

* Create SDK instance from API specs
* @template TApiSpecs - API specs record
* @template TRequestOptions - Extended request options type for customFetch
*/
export declare function createSDK<TApiSpecs extends Record<string, EndpointSpec<any, any, any, any>>>(config: CreateSDKProviderConfig<TApiSpecs> & {
export declare function createSDK<TApiSpecs extends Record<string, EndpointSpec<any, any, any, any>>, TRequestOptions = EmptyRequestOptions>(config: CreateSDKProviderConfig<TApiSpecs, TRequestOptions> & {
baseUrl: string;
}): GeneratedSDK<TApiSpecs>;
}): GeneratedSDK<TApiSpecs, TRequestOptions>;
/**
* Config for createSDKProvider
* @template TApiSpecs - API specs record
* @template TRequestOptions - Extended request options type
*/
export declare interface CreateSDKProviderConfig<TApiSpecs extends Record<string, EndpointSpec<any, any, any, any>>> extends BaseRequestConfig {
export declare interface CreateSDKProviderConfig<TApiSpecs extends Record<string, EndpointSpec<any, any, any, any>>, TRequestOptions = EmptyRequestOptions> extends BaseRequestConfig<TRequestOptions> {
apiSpecs: TApiSpecs;

@@ -110,2 +129,13 @@ }

/**
* Request info passed to customFetch function
* @template TRequestOptions - Extended request options from SDK method calls
*/
export declare type CustomFetchRequest<TRequestOptions = EmptyRequestOptions> = {
url: string;
method: string;
headers: Record<string, string>;
body?: BodyInit;
} & TRequestOptions;
/**
* Default error extractor - extracts basic error details

@@ -125,2 +155,9 @@ */

/**
* Empty object type for default TRequestOptions
* Using interface instead of Record<string, never> because it works correctly with intersections
*/
export declare interface EmptyRequestOptions {
}
export { EndpointSpec }

@@ -150,5 +187,7 @@

* Generate SDK type from API specs
* @template TApiSpecs - API specs record
* @template TRequestOptions - Extended request options type
*/
export declare type GeneratedSDK<TApiSpecs extends Record<string, EndpointSpec<any, any, any, any>>> = {
[K in keyof TApiSpecs]: SDKMethod<TApiSpecs[K]>;
export declare type GeneratedSDK<TApiSpecs extends Record<string, EndpointSpec<any, any, any, any>>, TRequestOptions = EmptyRequestOptions> = {
[K in keyof TApiSpecs]: SDKMethod<TApiSpecs[K], TRequestOptions>;
};

@@ -166,4 +205,6 @@

* SDK method signature from endpoint spec
* @template T - Endpoint spec type
* @template TRequestOptions - Extended request options type
*/
export declare type SDKMethod<T> = T extends EndpointSpec<infer R, infer B, infer Q, infer P> ? (options?: SdkRequestOptions<B, Q, P>) => Promise<R> : never;
export declare type SDKMethod<T, TRequestOptions = EmptyRequestOptions> = T extends EndpointSpec<infer R, infer B, infer Q, infer P> ? (options?: SdkRequestOptions<B, Q, P> & TRequestOptions) => Promise<R> : never;

@@ -170,0 +211,0 @@ /**

@@ -1,2 +0,2 @@

import { A as r, e as t, a as o, c as s, b as i, d as p } from "./createSDK-B_c0ZLmm.js";
import { A as r, e as t, a as o, c as s, b as i, d as p } from "./createSDK-BykB15KZ.js";
import { DeleteApi as c, GetApi as u, PatchApi as f, PostApi as l, PutApi as n } from "@rabstack/rab-api-spec";

@@ -3,0 +3,0 @@ export {

function x(...o) {
if (o.length === 0) return "";
let r = [...o].filter(Boolean);
const a = r[r.length - 1];
if (typeof a == "object" && a !== null) {
const t = a;
r = r.slice(0, -1).map((e) => {
if (typeof e == "string") {
let c = e;
for (const u in t)
c = c.replace(`:${u}`, String(t[u]));
return c;
}
return e;
});
}
return r.filter(
(t) => typeof t == "string" && t.trim() !== ""
).map(
(t, e) => t.startsWith("/") || e === 0 ? t : "/" + t
).join("");
}
class m extends Error {
constructor(r) {
console.error(`API Error ${r.status}: ${r.message} [${r.method} ${r.url}]`), super(r.message), this.name = "ApiDomainError", this.status = r.status, this.url = r.url, this.method = r.method, this.errorCode = r.errorCode, this.errors = r.errors, this.data = r.data, Error.captureStackTrace && Error.captureStackTrace(this, m);
}
isNetworkError() {
return this.status === 0;
}
isClientError() {
return this.status >= 400 && this.status < 500;
}
isServerError() {
return this.status >= 500;
}
isUnauthorized() {
return this.status === 401;
}
isForbidden() {
return this.status === 403;
}
isNotFound() {
return this.status === 404;
}
isValidationError() {
return this.status === 422 || this.status === 400;
}
isConflict() {
return this.status === 409;
}
getAllMessages() {
const r = [this.message];
return this.errors && this.errors.length > 0 && r.push(...this.errors), r;
}
getDisplayMessage() {
return this.errors && this.errors.length > 0 ? `${this.message}: ${this.errors.join(", ")}` : this.message;
}
}
function q(o, r, a, t, e) {
return {
status: o,
url: a,
method: t,
message: e?.message || r,
errorCode: e?.errorCode,
errors: e?.errors,
data: e
};
}
function z(o) {
return typeof o == "object" && o !== null && "data" in o ? o.data : o;
}
function O(o) {
const {
baseUrl: r,
getAuthorization: a,
extractApiError: t = q,
parseQuery: e,
transformResponse: c = z,
onError: u,
appendTrailingSlash: w,
formDataParser: d
} = o;
return async (n, S = {}) => {
try {
const { body: s, query: h, params: p, encodeQuery: D, headers: k, useFormData: R } = S, P = x(n.url, p ?? {}), F = r.replace(/\/$/, "");
let g = P.startsWith("/") ? P : "/" + P;
w && ["POST", "PUT", "PATCH"].includes(n.method) && !g.endsWith("/") && (g = g + "/");
const y = new URL(F + g);
if (h)
if (e && D !== !1) {
const i = e(h);
if (i) {
const l = i.split("&");
for (const b of l) {
const [T, $] = b.split("=");
T && $ !== void 0 && y.searchParams.append(decodeURIComponent(T), decodeURIComponent($));
}
}
} else
Object.entries(h).forEach(([i, l]) => {
l != null && y.searchParams.append(i, String(l));
});
const j = R && s;
let E;
if (j)
if (s instanceof FormData)
E = s;
else if (d)
E = d(s);
else
throw new Error("useFormData requires formDataParser in config or body to be FormData instance");
else
E = s ? JSON.stringify(s) : void 0;
const A = {
"Content-Type": j ? "multipart/form-data" : "application/json",
...k
}, C = a?.();
C && Object.assign(A, C);
const f = await fetch(y.toString(), {
method: n.method,
headers: A,
body: E
});
if (!f.ok) {
let i;
try {
i = await f.clone().json();
} catch {
i = await f.text();
}
const l = t(f.status, f.statusText, y.toString(), n.method, i), b = new m(l);
throw u?.(b), b;
}
const U = await f.json();
return c(U, f);
} catch (s) {
if (s instanceof m)
throw s;
const h = {
status: 0,
url: `${r}${n.url}`,
method: n.method,
message: s instanceof Error ? s.message : "Network request failed",
data: s
}, p = new m(h);
throw u?.(p), p;
}
};
}
function N(o) {
const { apiSpecs: r, baseUrl: a, getAuthorization: t, extractApiError: e, parseQuery: c, transformResponse: u, onError: w, appendTrailingSlash: d, formDataParser: n } = o, S = O({
baseUrl: a,
getAuthorization: t,
extractApiError: e,
parseQuery: c,
transformResponse: u,
onError: w,
appendTrailingSlash: d,
formDataParser: n
}), s = {};
for (const [h, p] of Object.entries(r))
s[h] = ((D) => S(p, D));
return s;
}
export {
m as A,
O as a,
q as b,
N as c,
z as d,
x as e
};
"use strict";function $(...o){if(o.length===0)return"";let r=[...o].filter(Boolean);const a=r[r.length-1];if(typeof a=="object"&&a!==null){const t=a;r=r.slice(0,-1).map(e=>{if(typeof e=="string"){let u=e;for(const c in t)u=u.replace(`:${c}`,String(t[c]));return u}return e})}return r.filter(t=>typeof t=="string"&&t.trim()!=="").map((t,e)=>t.startsWith("/")||e===0?t:"/"+t).join("")}class m extends Error{constructor(r){console.error(`API Error ${r.status}: ${r.message} [${r.method} ${r.url}]`),super(r.message),this.name="ApiDomainError",this.status=r.status,this.url=r.url,this.method=r.method,this.errorCode=r.errorCode,this.errors=r.errors,this.data=r.data,Error.captureStackTrace&&Error.captureStackTrace(this,m)}isNetworkError(){return this.status===0}isClientError(){return this.status>=400&&this.status<500}isServerError(){return this.status>=500}isUnauthorized(){return this.status===401}isForbidden(){return this.status===403}isNotFound(){return this.status===404}isValidationError(){return this.status===422||this.status===400}isConflict(){return this.status===409}getAllMessages(){const r=[this.message];return this.errors&&this.errors.length>0&&r.push(...this.errors),r}getDisplayMessage(){return this.errors&&this.errors.length>0?`${this.message}: ${this.errors.join(", ")}`:this.message}}function k(o,r,a,t,e){return{status:o,url:a,method:t,message:e?.message||r,errorCode:e?.errorCode,errors:e?.errors,data:e}}function F(o){return typeof o=="object"&&o!==null&&"data"in o?o.data:o}function U(o){const{baseUrl:r,getAuthorization:a,extractApiError:t=k,parseQuery:e,transformResponse:u=F,onError:c,appendTrailingSlash:S,formDataParser:d}=o;return async(n,b={})=>{try{const{body:s,query:h,params:p,encodeQuery:w,headers:q,useFormData:x}=b,P=$(n.url,p??{}),z=r.replace(/\/$/,"");let g=P.startsWith("/")?P:"/"+P;S&&["POST","PUT","PATCH"].includes(n.method)&&!g.endsWith("/")&&(g=g+"/");const y=new URL(z+g);if(h)if(e&&w!==!1){const i=e(h);if(i){const f=i.split("&");for(const D of f){const[T,C]=D.split("=");T&&C!==void 0&&y.searchParams.append(decodeURIComponent(T),decodeURIComponent(C))}}}else Object.entries(h).forEach(([i,f])=>{f!=null&&y.searchParams.append(i,String(f))});const j=x&&s;let E;if(j)if(s instanceof FormData)E=s;else if(d)E=d(s);else throw new Error("useFormData requires formDataParser in config or body to be FormData instance");else E=s?JSON.stringify(s):void 0;const A={"Content-Type":j?"multipart/form-data":"application/json",...q},R=a?.();R&&Object.assign(A,R);const l=await fetch(y.toString(),{method:n.method,headers:A,body:E});if(!l.ok){let i;try{i=await l.clone().json()}catch{i=await l.text()}const f=t(l.status,l.statusText,y.toString(),n.method,i),D=new m(f);throw c?.(D),D}const O=await l.json();return u(O,l)}catch(s){if(s instanceof m)throw s;const h={status:0,url:`${r}${n.url}`,method:n.method,message:s instanceof Error?s.message:"Network request failed",data:s},p=new m(h);throw c?.(p),p}}}function N(o){const{apiSpecs:r,baseUrl:a,getAuthorization:t,extractApiError:e,parseQuery:u,transformResponse:c,onError:S,appendTrailingSlash:d,formDataParser:n}=o,b=U({baseUrl:a,getAuthorization:t,extractApiError:e,parseQuery:u,transformResponse:c,onError:S,appendTrailingSlash:d,formDataParser:n}),s={};for(const[h,p]of Object.entries(r))s[h]=(w=>b(p,w));return s}exports.ApiDomainError=m;exports.buildUrlPath=$;exports.createRequestFunction=U;exports.createSDK=N;exports.defaultExtractApiError=k;exports.defaultTransformResponse=F;