@stacksjs/api 0.70.324 → 0.70.325
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/dist/generate-client.d.ts +22 -0
- package/dist/generate-openapi.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +119 -12
- package/package.json +2 -2
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { OpenApiDocument } from './generate-types';
|
|
2
|
+
/**
|
|
3
|
+
* A readable method name for an operation.
|
|
4
|
+
*
|
|
5
|
+
* `get__api_repos_pulls_show` is what the generator produces when a route has
|
|
6
|
+
* no name, and `client.get__api_repos_pulls_show()` is a name nobody would
|
|
7
|
+
* write on purpose. Split it, drop the `api` prefix every route shares, and
|
|
8
|
+
* camel-case the rest: `getReposPullsShow`.
|
|
9
|
+
*
|
|
10
|
+
* A path parameter contributes its name (`{id}` → `ById`), because
|
|
11
|
+
* `getRepository` and `getRepositoryById` are two different operations and
|
|
12
|
+
* numbering the second one tells the reader nothing.
|
|
13
|
+
*/
|
|
14
|
+
export declare function methodName(operationId: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Render the client for a document.
|
|
17
|
+
*
|
|
18
|
+
* Exported separately from the file writing so it can be tested on a document
|
|
19
|
+
* built in memory, which is the only way to assert what it does with a shape
|
|
20
|
+
* the current app happens not to have.
|
|
21
|
+
*/
|
|
22
|
+
export declare function renderApiClient(document: OpenApiDocument, options?: { name?: string }): string;
|
|
@@ -35,7 +35,7 @@ declare interface OpenApiPathItem {
|
|
|
35
35
|
[method: string]: {
|
|
36
36
|
summary?: string
|
|
37
37
|
operationId?: string
|
|
38
|
-
parameters?: Array<{ name: string, in: string, required: boolean, schema:
|
|
38
|
+
parameters?: Array<{ name: string, in: string, required: boolean, schema: SchemaObject }>
|
|
39
39
|
responses: Record<string, { description: string, content?: Record<string, { schema?: SchemaObject }> }>
|
|
40
40
|
requestBody?: { content: Record<string, { schema?: SchemaObject }>, required?: boolean }
|
|
41
41
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,32 +1,139 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var JO=Object.defineProperty;var GO=($)=>$;function ZO($,O){this[$]=GO.bind(null,O)}var zO=($,O)=>{for(var J in O)JO($,J,{get:O[J],enumerable:!0,configurable:!0,set:ZO.bind(O,J)})};var W$=($,O)=>()=>($&&(O=$($=0)),O);var s=import.meta.require;function KO($){let O=$;return O?.code==="SQLITE_CONSTRAINT_UNIQUE"||O?.code==="SQLITE_CONSTRAINT"||O?.code==="23505"||O?.errno===1062||/unique|duplicate/i.test(O?.message??"")}function Y$($,O,J){let G=$;if($ instanceof Error&&typeof G.status==="number"&&Number.isInteger(G.status)&&G.status>=400&&G.status<600){let Z={error:$.message};if(G.details!==void 0)Z.details=G.details;return{status:G.status,body:Z}}if(KO($))return{status:409,body:{error:`${O} already exists`}};return{status:500,body:{error:`Failed to ${J} ${O}`,detail:String($)}}}function M($){return $.replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/([A-Z])([A-Z][a-z])/g,"$1_$2").toLowerCase()}function x$($){let O={};for(let[J,G]of Object.entries($))O[M(J)]=G;return O}function V$($){if(!$)return[];let O=Object.entries($.attributes??{}).filter(([,J])=>J?.fillable===!0).map(([J])=>J);for(let J of $.belongsTo??[]){if(typeof J!=="string"||!J.trim())continue;let G=M(J.trim()).split("_").filter(Boolean);if(G.length===0)continue;let Z=`${G[0]}${G.slice(1).map((z)=>`${z[0]?.toUpperCase()}${z.slice(1)}`).join("")}Id`;O.push(Z)}return[...new Set(O)]}function T$($,O){if(!$||O.length===0)return{};let J={};for(let G of O){if(G in $){J[G]=$[G];continue}let Z=M(G);if(Z!==G&&Z in $)J[G]=$[Z]}return J}function QO($,O){if($?.name!=="date"||O instanceof Date||typeof O!=="string")return O;let J=/^(\d{4})-(\d{2})-(\d{2})$/.exec(O);if(!J)return O;let G=Number(J[1]),Z=Number(J[2]),z=Number(J[3]),K=new Date(Date.UTC(G,Z-1,z));if(K.getUTCFullYear()!==G||K.getUTCMonth()!==Z-1||K.getUTCDate()!==z)return O;return K}function B$($,O){if(!O.length)return $;let J={...$};for(let G of O)delete J[G],delete J[M(G)];return J}function l($,O){if(!$||O.length===0)return $;let J={...$};for(let G of O)delete J[G],delete J[M(G)];return J}function M$($,O){let J=new Map;for(let G of[...Object.keys($??{}),...LO]){let Z=M(G);if(!/^\w+$/.test(Z))continue;J.set(G,Z),J.set(Z,Z)}for(let G of O)J.delete(G),J.delete(M(G));return J}function C$($,O,J){if(!O)return $;let G=String(O).split(",").map((z)=>z.trim()).filter(Boolean),Z=$;for(let z of G){let K=z.startsWith("-"),Y=K?z.slice(1):z;if(!/^\w+$/.test(Y))continue;let W=J.get(Y);if(!W)continue;Z=Z.orderBy(W,K?"desc":"asc")}return Z}function XO($){try{return JSON.parse($)}catch{return $}}function YO($){try{return JSON.parse($)}catch{return[]}}function j$($,O,J){if(!$||typeof $!=="object"||!O||Object.keys(O).length===0)return $;let G={...$};for(let[Z,z]of Object.entries(O)){let K=typeof z==="string"?WO[z]:z;if(!K||typeof K[J]!=="function")continue;if(Object.prototype.hasOwnProperty.call(G,Z))G[Z]=K[J](G[Z]);let Y=M(Z);if(Y!==Z&&Object.prototype.hasOwnProperty.call(G,Y))G[Y]=K[J](G[Y])}return G}function A$($,O,J){let G=O?.attributes??{},Z={};for(let[z,K]of Object.entries(G)){let Y=K?.validation?.rule;if(!Y||typeof Y.validate!=="function")continue;let W=Object.prototype.hasOwnProperty.call($,z);if(!W&&J==="updating")continue;let L=QO(Y,W?$[z]:void 0),H=Y.validate(L);if(!H?.valid&&Array.isArray(H?.errors)&&H.errors.length>0)Z[z]=H.errors.map((C)=>K?.validation?.message?.[C?.code]??C?.message??"invalid")}return Object.keys(Z).length===0?{valid:!0}:{valid:!1,errors:Z}}function S$($){return $.replace(/\{[^}]*\}/g,"{}").replace(/:[^/]+/g,"{}")}function X$($){if(Array.isArray($))return $.filter((O)=>typeof O==="string"&&O.length>0);return typeof $==="string"&&$?[$]:[]}function k$($){let O=typeof $==="object"&&$!==null&&"middleware"in $,J=$?.middleware;if(!O)return{read:["auth"],write:["auth"],declared:!1};if(J&&typeof J==="object"&&!Array.isArray(J)){let Z=J;return{read:"read"in Z?X$(Z.read):["auth"],write:"write"in Z?X$(Z.write):["auth"],declared:!0}}let G=X$(J);return{read:G,write:G,declared:!0}}function R$($){let O=Number.parseInt($.get("page")||String(1),10),J=Number.isFinite(O)?Math.max(1,O):1,G=Number.parseInt($.get("per_page")||String(F$),10),Z=Math.min(Number.isFinite(G)?Math.max(1,G):F$,xO);return{page:J,perPage:Z,offset:(J-1)*Z}}function Z$($,O){let J=new URL($.toString());return J.searchParams.set("page",String(O)),`${J.pathname}${J.search}`}function z$($,O,J,G,Z,z){let K=(O-1)*J,Y=G===0,W={page:O,per_page:J,from:Y?null:K+1,to:Y?null:K+G,has_more_pages:Z,prev_page_url:O>1?Z$($,O-1):null,next_page_url:Z?Z$($,O+1):null};if(z!==void 0&&!Number.isNaN(z)){let L=Math.max(1,Math.ceil(z/J));W.total=z,W.last_page=L,W.first_page_url=Z$($,1),W.last_page_url=Z$($,L)}return W}function _$($,O,J,G,Z,z){let{page:K,...Y}=z$($,O,J,G,Z,z);return{current_page:K,...Y}}var LO,WO,F$=15,xO=100;var f$=W$(()=>{LO=["id","uuid","created_at","updated_at","deleted_at"];WO={string:{get:($)=>$!=null?String($):null,set:($)=>$!=null?String($):null},number:{get:($)=>$!=null?Number($):null,set:($)=>$!=null?Number($):null},integer:{get:($)=>$!=null?Math.trunc(Number($)):null,set:($)=>$!=null?Math.trunc(Number($)):null},float:{get:($)=>$!=null?Number.parseFloat(String($)):null,set:($)=>$!=null?Number.parseFloat(String($)):null},boolean:{get:($)=>$===1||$==="1"||$===!0||$==="true",set:($)=>$===!0||$===1||$==="1"||$==="true"?1:0},json:{get:($)=>$==null?null:typeof $==="string"?XO($):$,set:($)=>$==null?null:typeof $==="string"?$:JSON.stringify($)},datetime:{get:($)=>$?new Date($):null,set:($)=>$ instanceof Date?$.toISOString():$},date:{get:($)=>$?new Date($):null,set:($)=>$ instanceof Date?$.toISOString().split("T")[0]:$},array:{get:($)=>$==null?[]:Array.isArray($)?$:typeof $==="string"?YO($):[],set:($)=>$==null?null:Array.isArray($)?JSON.stringify($):$}}});import{readdirSync as TO}from"fs";import{basename as BO,extname as y$,join as jO}from"path";function AO($){let O=[],J=(G)=>{let Z;try{Z=TO(G,{withFileTypes:!0})}catch{return}for(let z of Z){let K=jO(G,z.name);if(z.isDirectory()){J(K);continue}if(![".ts",".js"].includes(y$(z.name)))continue;if(z.name.startsWith(".")||z.name.startsWith("index"))continue;O.push(K)}};return J($),O.sort()}async function P$($,O,J){for(let G of AO($))try{let Z=y$(G),z=await import(`${G}?t=${Date.now()}`),K=z.default??z,Y=K.name??BO(G,Z);O[Y]={...K,name:Y}}catch(Z){J?.(G,Z)}}async function g$($){let O={};return await P$($.defaultsRoot,O,$.onImportError),await P$($.userRoot,O,$.onImportError),O}var b$=()=>{};var o$={};zO(o$,{default:()=>UO});import{route as p}from"@stacksjs/router";import{env as u}from"@stacksjs/env";import{projectPath as i$,storagePath as SO}from"@stacksjs/path";import{createQueryBuilder as _O,defaultConfig as q$,setConfig as c$}from"@stacksjs/query-builder";import{HttpError as i}from"@stacksjs/error-handling";import{log as L$}from"@stacksjs/logging";function t($,O){let J=S$(O);return p.routes.some((G)=>G.method===$&&S$(String(G.path??""))===J)}function n$($){if(!$.attributes)return[];return Object.entries($.attributes).filter(([O,J])=>J.hidden===!0).map(([O])=>O)}function w$($){if($.endsWith("y")&&!/[aeiou]y$/i.test($))return`${$.slice(0,-1)}ies`;if(/(?:s|x|z|ch|sh)$/i.test($))return`${$}es`;return`${$}s`}async function v$($,O,J,G){if(!$.length)return $;let Z=J.split(",").map((L)=>L.trim()).filter(Boolean);if(!Z.length)return $;let z=Array.isArray(O.belongsTo)?O.belongsTo:[],K=Array.isArray(O.hasMany)?O.hasMany:[],Y=Array.isArray(O.hasOne)?O.hasOne:[],W=new Map;for(let L of z)W.set(M(L),{kind:"belongsTo",modelName:L});for(let L of Y)W.set(M(L),{kind:"hasOne",modelName:L});for(let L of K)W.set(w$(M(L)),{kind:"hasMany",modelName:L});for(let L of Z){let H=W.get(L);if(!H)continue;let C=G[H.modelName];if(!C)continue;let y=C.table||w$(M(H.modelName)),F=n$(C),q=(D)=>l(O$(D,C),F);if(H.kind==="belongsTo"||H.kind==="hasOne"){let D=`${M(H.modelName)}_id`,g=[...new Set($.map((E)=>E[D]).filter((E)=>E!=null))];if(!g.length){for(let E of $)E[L]=null;continue}let w=await P.selectFrom(y).whereIn("id",g).get(),U=new Map;for(let E of w??[])U.set(String(E.id),q(E));for(let E of $)E[L]=U.get(String(E[D]))??null}else{let D=String(O.name??""),g=`${M(D)}_id`,w=[...new Set($.map((x)=>x.id).filter((x)=>x!=null))];if(!w.length){for(let x of $)x[L]=[];continue}let U=await P.selectFrom(y).whereIn(g,w).get(),E=new Map;for(let x of U??[]){let Q=String(x[g]),X=E.get(Q)??[];X.push(q(x)),E.set(Q,X)}for(let x of $)x[L]=E.get(String(x.id))??[]}}return $}async function h$($,O){let J=O?.set;if(!J||typeof J!=="object")return $;let G={...$};for(let[Z,z]of Object.entries(J)){if(typeof z!=="function"||!(Z in G))continue;try{G[Z]=await z(G)}catch{}}return G}function p$($,O){return j$($,O?.casts,"set")}function O$($,O){return j$($,O?.casts,"get")}function B($,O=200){return new Response(JSON.stringify($),{status:O,headers:{"Content-Type":"application/json"}})}async function H$($){if(Number($.headers?.get?.("content-length")??0)>K$)throw new i(413,`Request body exceeds ${K$}-byte limit`);if($.jsonBody&&typeof $.jsonBody==="object")return $.jsonBody;if($.formBody&&typeof $.formBody==="object")return $.formBody;try{let J=await $.clone().text();if(J.length>K$)throw new i(413,`Request body exceeds ${K$}-byte limit`);if(!J)return{};let G=JSON.parse(J);return G&&typeof G==="object"&&!Array.isArray(G)?G:{}}catch(J){if(J instanceof i)throw J;throw new i(400,`Invalid JSON body: ${J.message}`)}}function Q$($){if($==null)return null;if(typeof $==="number")return Number.isFinite($)&&$>0?$:null;if(typeof $==="string"){let O=$.trim();if(!O)return null;if(/^\d+$/.test(O)){let J=Number(O);return Number.isFinite(J)&&J>0?J:null}return O}return null}function u$($){let O=$.bearerToken;if(typeof O==="function"){let G=O.call($);if(G)return G}let J=$.headers?.get?.("authorization")||$.headers?.get?.("Authorization")||"";if(typeof J==="string"&&J.startsWith("Bearer "))return J.substring(7);return null}async function d($){let O=$._authenticatedUser;if(O)return O;let J=u$($);if(!J)return null;try{let{Auth:G}=await import("@stacksjs/auth"),Z=await G.getUserFromToken(J);if(Z)$._authenticatedUser=Z;return Z||null}catch{return null}}function e($,O){if($==null||O==null)return!1;if(Array.isArray(O))return O.some((J)=>String(J)===String($));return String($)===String(O)}function HO($){let O=$?.attributes||{},J=(G)=>Object.prototype.hasOwnProperty.call(O,G);return J("teamId")||J("team_id")?"team_id":null}function DO($){let O=u$($),J=$.headers?.get?.("cookie")||"";return{bearerToken:()=>O,cookies:{get:(G)=>{for(let Z of J.split(";")){let z=Z.indexOf("=");if(z===-1)continue;if(Z.slice(0,z).trim()===G)return decodeURIComponent(Z.slice(z+1).trim())}return null}}}}function t$($){if($?.ownership)return $.ownership;let O=HO($);if(!O)return null;return{field:O,resolve:async(J,G)=>{let{resolveAuthenticatedTeamId:Z}=await import("@stacksjs/auth");return Z(DO(G))}}}async function $$($,O,J){let G=t$($);if(!G||!G.field||typeof G.resolve!=="function")return{enforced:!1,value:null,field:"",bypass:!1};let Z=typeof G.bypass==="function"?!!G.bypass(O,J):!1,z=null;try{z=await G.resolve(O,J)}catch{z=null}return{enforced:!0,value:z,field:String(G.field),bypass:Z}}async function m$($,O,J,G){let Z=$?.authedFill?.[O];if(!Z)return{};if(typeof Z==="function")try{let z=await Z(J,G);return z&&typeof z==="object"?z:{}}catch{return{}}if(typeof Z==="object"){let z={};for(let[K,Y]of Object.entries(Z))try{z[K]=typeof Y==="function"?await Y(J,G):Y}catch{}return z}return{}}var NO,N$,P,K$=1048576,UO;var s$=W$(async()=>{f$();b$();NO=i$("config/qb.ts");try{let $=(await import(NO)).default;c$($??q$)}catch{L$.debug("[orm] No config/qb.ts override found \u2014 deriving config from DB_CONNECTION");let $=u.DB_CONNECTION||"sqlite";c$({...q$,dialect:$,database:$==="sqlite"?{database:u.DB_DATABASE_PATH||"database/stacks.sqlite"}:{database:u.DB_DATABASE||"stacks",host:u.DB_HOST||"127.0.0.1",port:u.DB_PORT||($==="postgres"?5432:3306),username:u.DB_USERNAME||($==="postgres"?"postgres":"root"),password:u.DB_PASSWORD||""}})}N$=await g$({defaultsRoot:SO("framework/defaults/app/Models"),userRoot:i$("app/Models"),onImportError:($,O)=>L$.warn(`[orm] Could not load model ${$}: ${O.message}`)}),P=_O();for(let[$,O]of Object.entries(N$)){let J=O.traits?.useApi;if(!J)continue;let G=typeof J==="object"?J:{},Z=G.uri||O.table||$.toLowerCase()+"s",z=G.routes||["index","show","store","update","destroy"],K=O.table||Z,Y=V$(O),W=n$(O),L=`/api/${Z}`,H=M$(O.attributes,W),{read:C,write:y,declared:F}=k$(J);if(["store","update","destroy"].some((x)=>z.includes(x))&&F&&y.length===0)L$.warn(`[orm] ${$}: registering UNAUTHENTICATED mutating routes at ${L} (explicit \`middleware: []\` opt-out)`);if(["index","show"].some((x)=>z.includes(x))&&F&&C.length===0)L$.debug(`[orm] ${$}: registering public read routes at ${L} (declared \`middleware.read: []\`)`);let g=!!t$(O),w=g&&!C.includes("auth")?["auth",...C]:C,U=(x,Q=w)=>{let X=x;for(let T of Q)if(X&&typeof X.middleware==="function")X=X.middleware(T);return X};if(z.includes("index")&&!t("GET",L))U(p.get(L,async(x)=>{try{let Q=new URL(x.url),{page:X,perPage:T,offset:_}=R$(Q.searchParams),A=Q.searchParams.get("sort"),j=P.selectFrom(K);j=C$(j,A,H);let b=new Set(["page","per_page","sort","fields","search","include","limit","offset","with_count","withTrashed","onlyTrashed"]);for(let[R,f]of Q.searchParams.entries()){if(b.has(R)||!f)continue;if(!/^[a-z_][a-z0-9_]*$/i.test(R))continue;let n=H.get(R);if(n)j=j.where(n,"=",f)}let N=Q.searchParams.get("search");if(N&&Y.length>0){let R=Y.slice(0,5);j=j.where((f)=>{for(let n of R)f=f.orWhere(M(n),"like",`%${N}%`);return f})}let I=Q.searchParams.get("fields");if(I){let R=I.split(",").map((f)=>f.trim()).filter((f)=>/^[a-z_][a-z0-9_]*$/i.test(f)).map((f)=>M(f));if(R.length>0)j=j.select(R)}let V=!!O.traits?.useSoftDeletes,k=Q.searchParams.get("withTrashed")==="true",m=Q.searchParams.get("onlyTrashed")==="true";if(V&&!k)j=m?j.whereNotNull("deleted_at"):j.whereNull("deleted_at");let S=g?await $$(O,await d(x),x):{enforced:!1,value:null,field:"",bypass:!1};if(S.enforced&&!S.bypass){if(S.value==null||Array.isArray(S.value)&&S.value.length===0){let R=_$(Q,X,T,0,!1,void 0);return new Response(JSON.stringify({data:[],...R,meta:z$(Q,X,T,0,!1,void 0)}),{status:200,headers:{"Content-Type":"application/json","Cache-Control":"private, no-store",Vary:"Authorization"}})}j=Array.isArray(S.value)?j.where(S.field,"in",S.value):j.where(S.field,"=",S.value)}let v=await j.limit(T+1).offset(_).get()||[],h=v.length>T,a=(h?v.slice(0,T):v).map((R)=>l(O$(R,O),W)),U$=Q.searchParams.get("include");if(U$)a=await v$(a,O,U$,N$);let $O=Q.searchParams.get("with_count")==="true",o;if($O)try{let R=P.selectFrom(K);if(S.enforced&&!S.bypass&&S.value!=null)R=Array.isArray(S.value)?R.where(S.field,"in",S.value):R.where(S.field,"=",S.value);let f=await R.count(),n=typeof f==="number"?f:Number(f?.count??f);if(Number.isFinite(n))o=n}catch{}let G$={"Content-Type":"application/json"};if(o!==void 0&&!Number.isNaN(o))G$["X-Total-Count"]=String(o);G$["Cache-Control"]=S.enforced&&!S.bypass?"private, no-store":"public, max-age=15, must-revalidate",G$.Vary="Authorization";let OO=_$(Q,X,T,a.length,h,o);return new Response(JSON.stringify({data:a,...OO,meta:z$(Q,X,T,a.length,h,o)}),{status:200,headers:G$})}catch(Q){if(Q instanceof i){let X={error:Q.message};if(Q.details!==void 0)X.details=Q.details;return B(X,Q.status||400)}return B({error:`Failed to fetch ${Z}`,detail:String(Q)},500)}}));if(z.includes("show")&&!t("GET",`${L}/{id}`))U(p.get(`${L}/{id}`,async(x)=>{try{let Q=Q$(x.params?.id);if(Q==null)return B({error:"Invalid ID parameter"},400);let X=await P.selectFrom(K).where({id:Q}).executeTakeFirst();if(!X)return B({error:`${$} not found`},404);let T=new URL(x.url);if(O.traits?.useSoftDeletes&&X.deleted_at&&T.searchParams.get("withTrashed")!=="true")return B({error:`${$} not found`},404);let _=g?await $$(O,await d(x),x):{enforced:!1,value:null,field:"",bypass:!1};if(_.enforced&&!_.bypass){if(_.value==null||!e(X[_.field],_.value))return B({error:`${$} not found`},404)}let A=l(O$(X,O),W),j=T.searchParams.get("include");if(j){let[k]=await v$([A],O,j,N$);A=k}let b=A?.updated_at||A?.created_at,N=b?`W/"${A.id}-${String(b)}"`:void 0,I=x.headers?.get?.("if-none-match");if(N&&I&&I===N)return new Response(null,{status:304,headers:{ETag:N}});let V={"Content-Type":"application/json"};if(N)V.ETag=N;return V["Cache-Control"]=_.enforced&&!_.bypass?"private, no-store":"public, max-age=30, must-revalidate",V.Vary="Authorization",new Response(JSON.stringify({data:A}),{status:200,headers:V})}catch(Q){if(Q instanceof i){let X={error:Q.message};if(Q.details!==void 0)X.details=Q.details;return B(X,Q.status||400)}return B({error:`Failed to fetch ${$}`,detail:String(Q)},500)}}));if(z.includes("store")&&!t("POST",L))U(p.post(L,async(x)=>{try{let Q=await H$(x),X=B$(Q,W),T=T$(X,Y),_=await d(x);if(_){let V=await m$(O,"creating",_,x);for(let[k,m]of Object.entries(V))if(m!==void 0&&m!==null&&T[k]===void 0)T[k]=m}if(Object.keys(T).length===0)return B({error:"No fillable fields provided"},422);let A=A$(T,O,"creating");if(!A.valid)return B({success:!1,message:"Validation failed",errors:A.errors},422);if(O.traits?.useTimestamps!==!1){let V=new Date().toISOString();T.created_at=V,T.updated_at=V}let j=await h$(T,O),b=x$(p$(j,O)),N=await P.insertInto(K).values(b).execute(),I=b;try{let V=N?.lastInsertRowid??N?.insertId??N;if(V){let k=await P.selectFrom(K).where({id:V}).executeTakeFirst();if(k)I=k}}catch{}return B({data:l(O$(I,O),W)},201)}catch(Q){let{status:X,body:T}=Y$(Q,$,"create");return B(T,X)}}),y);if(z.includes("update")){let x=async(Q)=>{try{let X=Q$(Q.params?.id);if(X==null)return B({error:"Invalid ID parameter"},400);let T=await H$(Q),_=B$(T,W),A=T$(_,Y),j=await d(Q);if(j){let S=await m$(O,"updating",j,Q);for(let[v,h]of Object.entries(S))if(h!==void 0&&h!==null&&A[v]===void 0)A[v]=h}if(Object.keys(A).length===0)return B({error:"No fillable fields provided"},422);let b=A$(A,O,"updating");if(!b.valid)return B({success:!1,message:"Validation failed",errors:b.errors},422);let N=await P.selectFrom(K).where({id:X}).executeTakeFirst();if(!N)return B({error:`${$} not found`},404);let I=await $$(O,j,Q);if(I.enforced&&!I.bypass){if(!j)return B({error:"Auth required"},401);let S=N[I.field];if(!e(S,I.value))return B({error:`Not your ${$}`},403);let v=Object.keys(A).find((h)=>M(h)===M(I.field));if(v!==void 0&&!e(A[v],I.value))return B({error:`Cannot reassign ${$} ownership`},403)}if(O.traits?.useTimestamps!==!1)A.updated_at=new Date().toISOString();let V=await h$(A,O),k=x$(p$(V,O));await P.updateTable(K).set(k).where({id:X}).execute();let m={...N,...k};try{let S=await P.selectFrom(K).where({id:X}).executeTakeFirst();if(S)m=S}catch{}return B({data:l(O$(m,O),W)})}catch(X){let{status:T,body:_}=Y$(X,$,"update");return B(_,T)}};if(!t("PUT",`${L}/{id}`))U(p.put(`${L}/{id}`,x),y);if(!t("PATCH",`${L}/{id}`))U(p.patch(`${L}/{id}`,x),y)}let E=!!O.traits?.useSoftDeletes;if(z.includes("destroy")&&!t("DELETE",`${L}/{id}`))U(p.delete(`${L}/{id}`,async(x)=>{try{let Q=Q$(x.params?.id);if(Q==null)return B({error:"Invalid ID parameter"},400);let X=await P.selectFrom(K).where({id:Q}).executeTakeFirst();if(!X)return B({error:`${$} not found`},404);let T=await d(x),_=await $$(O,T,x);if(_.enforced&&!_.bypass){if(!T)return B({error:"Auth required"},401);let A=X[_.field];if(!e(A,_.value))return B({error:`Not your ${$}`},403)}if(E){let A=new Date().toISOString();await P.updateTable(K).set({deleted_at:A,updated_at:A}).where({id:Q}).execute()}else await P.deleteFrom(K).where({id:Q}).execute();return new Response(null,{status:204})}catch(Q){if(Q instanceof i){let X={error:Q.message};if(Q.details!==void 0)X.details=Q.details;return B(X,Q.status||400)}return B({error:`Failed to delete ${$}`,detail:String(Q)},500)}}),y);if(z.includes("destroy")&&!t("POST",`${L}/bulk-delete`))U(p.post(`${L}/bulk-delete`,async(x)=>{try{let X=(await H$(x))?.ids;if(!Array.isArray(X)||X.length===0)return B({error:"An array of IDs is required"},422);if(X.length>100)return B({error:"Cannot delete more than 100 records at once"},422);let T=[],_=[];for(let N of X){let I=Q$(N);if(I!=null)T.push(I);else _.push(N)}if(_.length>0)return B({error:"Invalid IDs in batch",invalid:_},422);let A=await d(x),j=await $$(O,A,x);if(j.enforced&&!j.bypass){if(!A)return B({error:"Auth required"},401);if(j.value==null)return B({error:"Caller has no ownership identity"},403);let N=await P.selectFrom(K).select(["id",j.field]).execute(),I=new Set(N.filter((k)=>e(k[j.field],j.value)).map((k)=>String(k.id))),V=T.filter((k)=>!I.has(String(k)));if(V.length>0)return B({error:`Cannot delete ${$} you don't own`,ids:V},403)}let b=new Date().toISOString();for(let N of T)if(E)await P.updateTable(K).set({deleted_at:b,updated_at:b}).where({id:N}).execute();else await P.deleteFrom(K).where({id:N}).execute();return B({message:`Successfully deleted ${T.length} ${Z}`})}catch(Q){if(Q instanceof i){let X={error:Q.message};if(Q.details!==void 0)X.details=Q.details;return B(X,Q.status||400)}return B({error:`Failed to bulk delete ${Z}`,detail:String(Q)},500)}}),y)}UO=p});class E${data;status;headers;isOk;constructor($,O,J,G){this.data=$;this.status=O;this.headers=J;this.isOk=G}ok(){return this.status===200}created(){return this.status===201}accepted(){return this.status===202}noContent(){return this.status===204}movedPermanently(){return this.status===301}found(){return this.status===302}badRequest(){return this.status===400}unauthorized(){return this.status===401}paymentRequired(){return this.status===402}forbidden(){return this.status===403}notFound(){return this.status===404}requestTimeout(){return this.status===408}conflict(){return this.status===409}unprocessableEntity(){return this.status===422}tooManyRequests(){return this.status===429}serverError(){return this.status===500}}class I${defaultHeaders={Accept:"application/json","Content-Type":"application/json"};queryParams;bodyData;isFormData=!1;customHeaders={};attachments=[];isMultipart=!1;digestAuth;withQueryParams($){return this.queryParams=$,this}withHeaders($){return this.customHeaders={...this.customHeaders,...$},this}accept($){return this.defaultHeaders.Accept=$,this}acceptJson(){return this.accept("application/json")}withToken($){return this.customHeaders.Authorization=`Bearer ${$}`,this}withBasicAuth($,O){let J=btoa(`${$}:${O}`);return this.customHeaders.Authorization=`Basic ${J}`,this}withDigestAuth($,O){return this.digestAuth={username:$,password:O},this}withBody($){return this.bodyData=$,this}asForm(){return this.isFormData=!0,this.defaultHeaders["Content-Type"]="application/x-www-form-urlencoded",this}attach($,O,J,G){let Z=typeof O==="string"?new Blob([O],{type:G?.["Content-Type"]||"text/plain"}):O;return this.attachments.push({name:$,content:Z,filename:J,headers:G}),this.isMultipart=!0,delete this.defaultHeaders["Content-Type"],this}getHeaders(){return{...this.defaultHeaders,...this.customHeaders}}addQueryParams($){if(!this.queryParams)return $;let O=new URLSearchParams;Object.entries(this.queryParams).forEach(([Z,z])=>{if(z!=null)O.append(Z,String(z))});let J=O.toString();if(!J)return $;let G=$.includes("?");return $+(G?"&":"?")+J}async formatBody($){let O=$||this.bodyData;if(this.isMultipart){let J=new FormData;if(O)Object.entries(O).forEach(([G,Z])=>{J.append(G,String(Z))});for(let G of this.attachments)if(G.headers){let Z=G.content instanceof Blob?new Blob([G.content],{type:G.headers["Content-Type"]}):G.content;J.append(G.name,Z,G.filename)}else J.append(G.name,G.content,G.filename);return J}if(this.isFormData&&O){let J=new URLSearchParams;return Object.entries(O).forEach(([G,Z])=>{if(Z!=null)J.append(G,String(Z))}),J.toString()}return O?JSON.stringify(O):void 0}resetState(){this.queryParams=void 0,this.bodyData=void 0,this.isFormData=!1,this.isMultipart=!1,this.customHeaders={},this.attachments=[],this.digestAuth=void 0,this.defaultHeaders["Content-Type"]="application/json"}async handleDigestAuth($,O,J,G){if($.status!==401||!this.digestAuth)return $;let Z=$.headers.get("WWW-Authenticate");if(!Z?.startsWith("Digest "))return $;let z=Z.substring(7).split(",").reduce((F,q)=>{let[D,g]=q.trim().split("=");if(D)F[D]=g?.replace(/["']/g,"")??"";return F},{}),K=await this.generateMD5(`${this.digestAuth.username}:${z.realm}:${this.digestAuth.password}`),Y=await this.generateMD5(`${J}:${O}`),W="00000001",L=await this.generateMD5(Date.now().toString()).then((F)=>F.substring(0,8)),H=await this.generateMD5(`${K}:${z.nonce}:${W}:${L}:${z.qop}:${Y}`),C={username:`"${this.digestAuth.username}"`,realm:`"${z.realm}"`,nonce:`"${z.nonce}"`,uri:`"${O}"`,algorithm:z.algorithm?`"${z.algorithm}"`:void 0,qop:`"${z.qop}"`,nc:W,cnonce:`"${L}"`,response:`"${H}"`,opaque:z.opaque?`"${z.opaque}"`:void 0},y=Object.entries(C).filter(([F,q])=>q!==void 0).map(([F,q])=>`${F}=${q}`).join(", ");return this.customHeaders.Authorization=`Digest ${y}`,fetch(O,{method:J,headers:this.getHeaders(),body:G?this.formatBody(G):void 0})}async generateMD5($){let J=new TextEncoder().encode($),G=await crypto.subtle.digest("SHA-256",J);return Array.from(new Uint8Array(G)).map((z)=>z.toString(16).padStart(2,"0")).join("")}async request($,O,J){let G=this.addQueryParams($),Z=await this.formatBody(J),z=await fetch(G,{method:O,headers:this.getHeaders(),body:Z});if(this.digestAuth)z=await this.handleDigestAuth(z,G,O,J);let K=await z.json();return this.resetState(),new E$(K,z.status,z.headers,z.ok)}async get($){return this.request($,"GET")}async post($,O){return this.request($,"POST",O)}async put($,O){return this.request($,"PUT",O)}async patch($,O){return this.request($,"PATCH",O)}async delete($){return this.request($,"DELETE")}}var fO=new I$;import{path as l$}from"@stacksjs/path";function EO($){if(!$||typeof $!=="object")return{type:"string"};let O=$,J=O._type??"string",G={type:J==="array"?"array":J};if(typeof O._min==="number")G[J==="string"?"minLength":"minimum"]=O._min;if(typeof O._max==="number")G[J==="string"?"maxLength":"maximum"]=O._max;if(O._email)G.format="email";if(O._url)G.format="uri";if(O._uuid)G.format="uuid";if(Array.isArray(O._enum))G.enum=O._enum;return G}async function IO($){if(typeof $!=="string"||!$.includes("Actions"))return null;try{let{path:O}=await import("@stacksjs/path"),J=[O.appPath(`${$}.ts`),O.storagePath(`framework/defaults/app/${$}.ts`)];for(let G of J)try{if(!await Bun.file(G).exists())continue;let K=(await import(G)).default;if(K&&typeof K==="object"&&"validations"in K){let Y=K.validations;return Y&&typeof Y==="object"?Y:null}return null}catch{}}catch{}return null}async function oO($={}){let O=$.portable??!0,{listRegisteredRoutes:J}=await import("@stacksjs/router");try{let{loadRoutes:W}=await import("@stacksjs/router"),{default:L}=await import(l$.appPath("Routes.ts"));await W(L)}catch(W){let L=W instanceof Error?W.message:String(W);if(O)throw Error(`[generateOpenApi] Refusing to emit a partial spec: failed to load routes/api.ts + framework routes: ${L}`);console.warn(`[generateOpenApi] Failed to load routes/api.ts + framework routes: ${L}`)}try{await s$().then(() => o$)}catch(W){let L=W instanceof Error?W.message:String(W);if(O)throw Error(`[generateOpenApi] Refusing to emit a partial spec: failed to load useApi-generated ORM routes: ${L}`);console.warn(`[generateOpenApi] Failed to load useApi-generated ORM routes: ${L}`)}let G=(W)=>W.replace(/(^|\/):(\w+)(?=$|\/)/g,"$1{$2}"),Z=[...J()].map((W)=>({route:W,oasPath:G(W.path),method:W.method.toLowerCase()})).sort((W,L)=>{if(W.oasPath!==L.oasPath)return W.oasPath<L.oasPath?-1:1;return W.method<L.method?-1:W.method>L.method?1:0});if(O&&Z.length===0)throw Error("[generateOpenApi] Refusing to emit an empty spec: no routes were registered. This usually means route loading failed silently rather than that the app has no API.");let z="Stacks API";if(!O)try{let{config:W}=await import("@stacksjs/config");if(W.app?.name)z=`${W.app.name} API`}catch{}let K={openapi:"3.0.0",info:{title:z,version:"1.0.0"},paths:{},components:{schemas:{}}},Y=new Set;for(let{route:W,oasPath:L,method:H}of Z){if(!K.paths[L])K.paths[L]={};let C=[];for(let D of W.path.matchAll(/\{(\w+)\}/g))if(D[1])C.push(D[1]);for(let D of W.path.matchAll(/(?:^|\/):(\w+)(?=$|\/)/g))if(D[1])C.push(D[1]);let y=W.name??`${H}_${L.replace(/[^a-z0-9]/gi,"_")}`,F=y;if(Y.has(F)){F=`${y}_${H}`;let D=2;while(Y.has(F))F=`${y}_${H}_${D++}`}Y.add(F);let q={summary:W.name??W.path,operationId:F,parameters:C.map((D)=>({name:D,in:"path",required:!0,schema:{type:"string"}})),responses:{200:{description:"Successful response",content:{"application/json":{schema:{type:"object"}}}},422:{description:"Validation failed"},500:{description:"Server error"}}};if(H!=="get"&&H!=="head"&&W.name){let g=`Actions/${W.name.split(".").map((U)=>U.charAt(0).toUpperCase()+U.slice(1)).join("")}Action`,w=await IO(g);if(w&&Object.keys(w).length>0){let U={type:"object",properties:{},required:[]},E=U.properties,x=U.required;for(let[Q,X]of Object.entries(w)){let T=X?.rule;if(E[Q]=EO(T),T?._required)x.push(Q)}if(x.length===0)delete U.required;q.requestBody={required:x.length>0,content:{"application/json":{schema:U}}}}}K.paths[L][H]=q}if($.write!==!1){let L=Bun.file(l$.frameworkPath("api/openapi.json")).writer();L.write(JSON.stringify(K,null,2)),await L.end()}return K}var FO=["get","put","post","delete","options","head","patch","trace"];function VO($){return typeof $==="string"?JSON.stringify($):String($)}function r($){if(!$)return"unknown";if($.enum?.length)return $.enum.map(VO).join(" | ");if($.type==="string")return"string";if($.type==="number"||$.type==="integer")return"number";if($.type==="boolean")return"boolean";if($.type==="null")return"null";if($.type==="array")return`Array<${r($.items)}>`;if($.type==="object"||$.properties||$.additionalProperties){let O=new Set($.required||[]),J=Object.entries($.properties||{}).map(([G,Z])=>`${JSON.stringify(G)}${O.has(G)?"":"?"}: ${r(Z)}`);if($.additionalProperties){let G=$.additionalProperties===!0?"unknown":r($.additionalProperties);J.push(`[key: string]: ${G}`)}return J.length?`{ ${J.join("; ")} }`:"Record<string, unknown>"}return"unknown"}function MO($=[]){return` parameters: {
|
|
3
|
-
${["query","header","path","cookie"].map((
|
|
2
|
+
var WJ=Object.defineProperty;var LJ=($)=>$;function XJ($,J){this[$]=LJ.bind(null,J)}var x$=($,J)=>{for(var O in J)WJ($,O,{get:J[O],enumerable:!0,configurable:!0,set:XJ.bind(J,O)})};var e=($,J)=>()=>($&&(J=$($=0)),J);var d=import.meta.require;var R$={};x$(R$,{schemaType:()=>w,renderOpenApiTypes:()=>TJ});function xJ($){return typeof $==="string"?JSON.stringify($):String($)}function w($){if(!$)return"unknown";if($.enum?.length)return $.enum.map(xJ).join(" | ");if($.type==="string")return"string";if($.type==="number"||$.type==="integer")return"number";if($.type==="boolean")return"boolean";if($.type==="null")return"null";if($.type==="array")return`Array<${w($.items)}>`;if($.type==="object"||$.properties||$.additionalProperties){let J=new Set($.required||[]),O=Object.entries($.properties||{}).map(([G,Z])=>`${JSON.stringify(G)}${J.has(G)?"":"?"}: ${w(Z)}`);if($.additionalProperties){let G=$.additionalProperties===!0?"unknown":w($.additionalProperties);O.push(`[key: string]: ${G}`)}return O.length?`{ ${O.join("; ")} }`:"Record<string, unknown>"}return"unknown"}function BJ($=[]){return` parameters: {
|
|
3
|
+
${["query","header","path","cookie"].map((O)=>{let G=$.filter((z)=>z.in===O);if(!G.length)return` ${O}?: never`;let Z=G.map((z)=>`${JSON.stringify(z.name)}${z.required?"":"?"}: ${w(z.schema)}`);return` ${O}${G.every((z)=>!z.required)?"?":""}: { ${Z.join("; ")} }`}).join(`
|
|
4
4
|
`)}
|
|
5
|
-
}`}function
|
|
6
|
-
requestBody${$.requestBody.required?"":"?"}: { content: ${
|
|
7
|
-
requestBody?: never`,
|
|
8
|
-
${
|
|
5
|
+
}`}function k$($){if(!$||!Object.keys($).length)return"never";return`{ ${Object.entries($).map(([J,O])=>`${JSON.stringify(J)}: ${w(O.schema)}`).join("; ")} }`}function jJ($){let J=$.requestBody?`
|
|
6
|
+
requestBody${$.requestBody.required?"":"?"}: { content: ${k$($.requestBody.content)} }`:`
|
|
7
|
+
requestBody?: never`,O=Object.entries($.responses||{}).map(([G,Z])=>` ${JSON.stringify(G)}: { content: ${k$(Z.content)} }`);return`{
|
|
8
|
+
${BJ($.parameters)}${J}
|
|
9
9
|
responses: {
|
|
10
|
-
${
|
|
10
|
+
${O.join(`
|
|
11
11
|
`)}
|
|
12
12
|
}
|
|
13
|
-
}`}function
|
|
13
|
+
}`}function TJ($){let J=Object.entries($.paths||{}).map(([G,Z])=>{let z=YJ.map((K)=>{let X=Z[K];return X?` ${K}: ${jJ(X)}`:` ${K}?: never`});return` ${JSON.stringify(G)}: {
|
|
14
14
|
${z.join(`
|
|
15
15
|
`)}
|
|
16
|
-
}`}),
|
|
16
|
+
}`}),O=Object.entries($.components?.schemas||{}).map(([G,Z])=>` ${JSON.stringify(G)}: ${w(Z)}`);return`/**
|
|
17
17
|
* Generated from storage/framework/api/openapi.json by @stacksjs/api.
|
|
18
18
|
* Do not edit this file directly; run bun run docs:artifacts to regenerate it.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
export interface paths {
|
|
22
|
-
${
|
|
22
|
+
${J.join(`
|
|
23
23
|
`)}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export interface components {
|
|
27
27
|
schemas: {
|
|
28
|
-
${
|
|
28
|
+
${O.join(`
|
|
29
|
+
`)}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
`}var YJ;var z$=e(()=>{YJ=["get","put","post","delete","options","head","patch","trace"]});var f$={};x$(f$,{renderApiClient:()=>SJ,methodName:()=>P$});function P$($){let J=$.replace(/[{}]/g,"").split(/[^a-z0-9]+/i).filter(Boolean),O=J[0]?.toLowerCase()==="api"?J.slice(1):J[1]?.toLowerCase()==="api"?[J[0],...J.slice(2)]:J,[G,...Z]=O;if(!G)return"call";let z=G.charAt(0).toLowerCase()+G.slice(1),K=Z.map((W)=>W.charAt(0).toUpperCase()+W.slice(1)),X=[z,...K].join("");return/^[a-z_$]/i.test(X)?X:`call${X.charAt(0).toUpperCase()}${X.slice(1)}`}function _J($){let J=$.responses??{},O=Object.keys(J).find((z)=>z.startsWith("2"));if(!O)return"unknown";let G=J[O]?.content,Z=G?.["application/json"]??Object.values(G??{})[0];return Z?.schema?w(Z.schema):"unknown"}function NJ($,J=" "){let O=$.filter(Boolean);if(!O.length)return"";return`${J}/**
|
|
33
|
+
${O.map((G)=>`${J} * ${G}`).join(`
|
|
29
34
|
`)}
|
|
35
|
+
${J} */
|
|
36
|
+
`}function HJ($,J,O,G){let Z=O.parameters??[],z=Z.filter((N)=>N.in==="path"),K=Z.filter((N)=>N.in==="query"),X=O.requestBody?.content?.["application/json"]?.schema,W=Boolean(O.requestBody?.required),Q=[];for(let N of[...z,...K]){let g=N.in==="path"||N.required;Q.push(`${JSON.stringify(N.name)}${g?"":"?"}: ${w(N.schema)}`)}if(X)Q.push(`body${W?"":"?"}: ${w(X)}`);let T=[...z,...K].every((N)=>N.in!=="path"&&!N.required)&&(!X||!W),I=Q.length?`input${T?"?":""}: { ${Q.join("; ")} }, `:"",P=K.length?`[${K.map((N)=>JSON.stringify(N.name)).join(", ")}]`:"[]";return`${NJ([O.summary&&O.summary!==$?O.summary:`${J.toUpperCase()} ${$}`,O.description??""])} ${G}(${I}options?: RequestOptions): Promise<ApiResult<${_J(O)}>> {
|
|
37
|
+
return request(config, ${JSON.stringify(J.toUpperCase())}, ${JSON.stringify($)}, ${Q.length?"input ?? {}":"{}"}, ${P}, ${X?"true":"false"}, options)
|
|
38
|
+
},`}function SJ($,J={}){let O=J.name??"API",G=new Map,Z=[];for(let[z,K]of Object.entries($.paths??{}))for(let X of AJ){let W=K[X];if(!W)continue;let Q=P$(W.operationId??`${X}_${z}`),T=G.get(Q)??0;G.set(Q,T+1),Z.push(HJ(z,X,W,T===0?Q:`${Q}${T+1}`))}return`/* eslint-disable */
|
|
39
|
+
/**
|
|
40
|
+
* ${O} client, generated from the OpenAPI document.
|
|
41
|
+
*
|
|
42
|
+
* Do not edit. Run \`buddy generate:openapi\` to rebuild it from the routes.
|
|
43
|
+
*
|
|
44
|
+
* No imports and no dependencies: copy this file anywhere that has \`fetch\`.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/** Every call answers with one of these. Nothing here throws for a 4xx. */
|
|
48
|
+
export type ApiResult<T> =
|
|
49
|
+
| { ok: true, status: number, data: T, headers: Headers }
|
|
50
|
+
| { ok: false, status: number, error: unknown, headers: Headers }
|
|
51
|
+
|
|
52
|
+
export interface ClientConfig {
|
|
53
|
+
/** Where the API lives, e.g. \`https://example.com\`. No trailing slash needed. */
|
|
54
|
+
baseUrl: string
|
|
55
|
+
/** Sent as \`Authorization: Bearer \u2026\` when present. */
|
|
56
|
+
token?: string
|
|
57
|
+
/** Extra headers on every request. */
|
|
58
|
+
headers?: Record<string, string>
|
|
59
|
+
/** Swap in a different fetch - a test double, or one that retries. */
|
|
60
|
+
fetch?: typeof fetch
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface RequestOptions {
|
|
64
|
+
/** Abort in flight. */
|
|
65
|
+
signal?: AbortSignal
|
|
66
|
+
/** Headers for this call only, merged over the client's. */
|
|
67
|
+
headers?: Record<string, string>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function buildUrl(config: ClientConfig, route: string, input: Record<string, unknown>, query: string[]): string {
|
|
71
|
+
// Path parameters are substituted, never appended: a \`{id}\` left in the URL
|
|
72
|
+
// is a 404 whose message is about the literal string "{id}".
|
|
73
|
+
const filled = route.replace(/\\{(\\w+)\\}/g, (_, key: string) => encodeURIComponent(String(input[key] ?? '')))
|
|
74
|
+
const url = new URL(filled.replace(/^\\/+/, '/'), config.baseUrl.endsWith('/') ? config.baseUrl : \`\${config.baseUrl}/\`)
|
|
75
|
+
|
|
76
|
+
for (const key of query) {
|
|
77
|
+
const value = input[key]
|
|
78
|
+
// Absent means absent. Sending \`?path=\` asks for the file called empty
|
|
79
|
+
// string, which is a different question from not filtering.
|
|
80
|
+
if (value === undefined || value === null) continue
|
|
81
|
+
url.searchParams.set(key, String(value))
|
|
30
82
|
}
|
|
83
|
+
|
|
84
|
+
return url.toString()
|
|
31
85
|
}
|
|
32
|
-
|
|
86
|
+
|
|
87
|
+
async function request<T>(
|
|
88
|
+
config: ClientConfig,
|
|
89
|
+
method: string,
|
|
90
|
+
route: string,
|
|
91
|
+
input: Record<string, unknown>,
|
|
92
|
+
query: string[],
|
|
93
|
+
hasBody: boolean,
|
|
94
|
+
options?: RequestOptions,
|
|
95
|
+
): Promise<ApiResult<T>> {
|
|
96
|
+
const call = config.fetch ?? fetch
|
|
97
|
+
const url = buildUrl(config, route, input, query)
|
|
98
|
+
|
|
99
|
+
const headers: Record<string, string> = {
|
|
100
|
+
Accept: 'application/json',
|
|
101
|
+
...(config.headers ?? {}),
|
|
102
|
+
...(options?.headers ?? {}),
|
|
103
|
+
}
|
|
104
|
+
if (config.token) headers.Authorization = \`Bearer \${config.token}\`
|
|
105
|
+
|
|
106
|
+
const body = hasBody && input.body !== undefined ? JSON.stringify(input.body) : undefined
|
|
107
|
+
if (body !== undefined) headers['Content-Type'] = 'application/json'
|
|
108
|
+
|
|
109
|
+
const response = await call(url, { method, headers, body, signal: options?.signal })
|
|
110
|
+
|
|
111
|
+
const text = await response.text()
|
|
112
|
+
let parsed: unknown = text
|
|
113
|
+
if (text.length) {
|
|
114
|
+
// A non-JSON body is kept verbatim rather than replaced with a parse error.
|
|
115
|
+
// An endpoint that answered with HTML is worth seeing.
|
|
116
|
+
try { parsed = JSON.parse(text) } catch { parsed = text }
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
parsed = null
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return response.ok
|
|
123
|
+
? { ok: true, status: response.status, data: parsed as T, headers: response.headers }
|
|
124
|
+
: { ok: false, status: response.status, error: parsed, headers: response.headers }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function createClient(config: ClientConfig) {
|
|
128
|
+
return {
|
|
129
|
+
/** The configuration in use, so a caller can rebuild a variant of it. */
|
|
130
|
+
config,
|
|
131
|
+
|
|
132
|
+
${Z.join(`
|
|
133
|
+
|
|
134
|
+
`)}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export type Client = ReturnType<typeof createClient>
|
|
139
|
+
`}var AJ;var B$=e(()=>{z$();AJ=["get","put","post","delete","options","head","patch"]});function UJ($){let J=$;return J?.code==="SQLITE_CONSTRAINT_UNIQUE"||J?.code==="SQLITE_CONSTRAINT"||J?.code==="23505"||J?.errno===1062||/unique|duplicate/i.test(J?.message??"")}function T$($,J,O){let G=$;if($ instanceof Error&&typeof G.status==="number"&&Number.isInteger(G.status)&&G.status>=400&&G.status<600){let Z={error:$.message};if(G.details!==void 0)Z.details=G.details;return{status:G.status,body:Z}}if(UJ($))return{status:409,body:{error:`${J} already exists`}};return{status:500,body:{error:`Failed to ${O} ${J}`,detail:String($)}}}function k($){return $.replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/([A-Z])([A-Z][a-z])/g,"$1_$2").toLowerCase()}function A$($){let J={};for(let[O,G]of Object.entries($))J[k(O)]=G;return J}function g$($){if(!$)return[];let J=Object.entries($.attributes??{}).filter(([,O])=>O?.fillable===!0).map(([O])=>O);for(let O of $.belongsTo??[]){if(typeof O!=="string"||!O.trim())continue;let G=k(O.trim()).split("_").filter(Boolean);if(G.length===0)continue;let Z=`${G[0]}${G.slice(1).map((z)=>`${z[0]?.toUpperCase()}${z.slice(1)}`).join("")}Id`;J.push(Z)}return[...new Set(J)]}function _$($,J){if(!$||J.length===0)return{};let O={};for(let G of J){if(G in $){O[G]=$[G];continue}let Z=k(G);if(Z!==G&&Z in $)O[G]=$[Z]}return O}function DJ($,J){if($?.name!=="date"||J instanceof Date||typeof J!=="string")return J;let O=/^(\d{4})-(\d{2})-(\d{2})$/.exec(J);if(!O)return J;let G=Number(O[1]),Z=Number(O[2]),z=Number(O[3]),K=new Date(Date.UTC(G,Z-1,z));if(K.getUTCFullYear()!==G||K.getUTCMonth()!==Z-1||K.getUTCDate()!==z)return J;return K}function N$($,J){if(!J.length)return $;let O={...$};for(let G of J)delete O[G],delete O[k(G)];return O}function t($,J){if(!$||J.length===0)return $;let O={...$};for(let G of J)delete O[G],delete O[k(G)];return O}function b$($,J){let O=new Map;for(let G of[...Object.keys($??{}),...EJ]){let Z=k(G);if(!/^\w+$/.test(Z))continue;O.set(G,Z),O.set(Z,Z)}for(let G of J)O.delete(G),O.delete(k(G));return O}function q$($,J,O){if(!J)return $;let G=String(J).split(",").map((z)=>z.trim()).filter(Boolean),Z=$;for(let z of G){let K=z.startsWith("-"),X=K?z.slice(1):z;if(!/^\w+$/.test(X))continue;let W=O.get(X);if(!W)continue;Z=Z.orderBy(W,K?"desc":"asc")}return Z}function FJ($){try{return JSON.parse($)}catch{return $}}function VJ($){try{return JSON.parse($)}catch{return[]}}function H$($,J,O){if(!$||typeof $!=="object"||!J||Object.keys(J).length===0)return $;let G={...$};for(let[Z,z]of Object.entries(J)){let K=typeof z==="string"?IJ[z]:z;if(!K||typeof K[O]!=="function")continue;if(Object.prototype.hasOwnProperty.call(G,Z))G[Z]=K[O](G[Z]);let X=k(Z);if(X!==Z&&Object.prototype.hasOwnProperty.call(G,X))G[X]=K[O](G[X])}return G}function S$($,J,O){let G=J?.attributes??{},Z={};for(let[z,K]of Object.entries(G)){let X=K?.validation?.rule;if(!X||typeof X.validate!=="function")continue;let W=Object.prototype.hasOwnProperty.call($,z);if(!W&&O==="updating")continue;let Q=DJ(X,W?$[z]:void 0),T=X.validate(Q);if(!T?.valid&&Array.isArray(T?.errors)&&T.errors.length>0)Z[z]=T.errors.map((I)=>K?.validation?.message?.[I?.code]??I?.message??"invalid")}return Object.keys(Z).length===0?{valid:!0}:{valid:!1,errors:Z}}function U$($){return $.replace(/\{[^}]*\}/g,"{}").replace(/:[^/]+/g,"{}")}function j$($){if(Array.isArray($))return $.filter((J)=>typeof J==="string"&&J.length>0);return typeof $==="string"&&$?[$]:[]}function c$($){let J=typeof $==="object"&&$!==null&&"middleware"in $,O=$?.middleware;if(!J)return{read:["auth"],write:["auth"],declared:!1};if(O&&typeof O==="object"&&!Array.isArray(O)){let Z=O;return{read:"read"in Z?j$(Z.read):["auth"],write:"write"in Z?j$(Z.write):["auth"],declared:!0}}let G=j$(O);return{read:G,write:G,declared:!0}}function w$($){let J=Number.parseInt($.get("page")||String(1),10),O=Number.isFinite(J)?Math.max(1,J):1,G=Number.parseInt($.get("per_page")||String(y$),10),Z=Math.min(Number.isFinite(G)?Math.max(1,G):y$,MJ);return{page:O,perPage:Z,offset:(O-1)*Z}}function K$($,J){let O=new URL($.toString());return O.searchParams.set("page",String(J)),`${O.pathname}${O.search}`}function Q$($,J,O,G,Z,z){let K=(J-1)*O,X=G===0,W={page:J,per_page:O,from:X?null:K+1,to:X?null:K+G,has_more_pages:Z,prev_page_url:J>1?K$($,J-1):null,next_page_url:Z?K$($,J+1):null};if(z!==void 0&&!Number.isNaN(z)){let Q=Math.max(1,Math.ceil(z/O));W.total=z,W.last_page=Q,W.first_page_url=K$($,1),W.last_page_url=K$($,Q)}return W}function D$($,J,O,G,Z,z){let{page:K,...X}=Q$($,J,O,G,Z,z);return{current_page:K,...X}}var EJ,IJ,y$=15,MJ=100;var v$=e(()=>{EJ=["id","uuid","created_at","updated_at","deleted_at"];IJ={string:{get:($)=>$!=null?String($):null,set:($)=>$!=null?String($):null},number:{get:($)=>$!=null?Number($):null,set:($)=>$!=null?Number($):null},integer:{get:($)=>$!=null?Math.trunc(Number($)):null,set:($)=>$!=null?Math.trunc(Number($)):null},float:{get:($)=>$!=null?Number.parseFloat(String($)):null,set:($)=>$!=null?Number.parseFloat(String($)):null},boolean:{get:($)=>$===1||$==="1"||$===!0||$==="true",set:($)=>$===!0||$===1||$==="1"||$==="true"?1:0},json:{get:($)=>$==null?null:typeof $==="string"?FJ($):$,set:($)=>$==null?null:typeof $==="string"?$:JSON.stringify($)},datetime:{get:($)=>$?new Date($):null,set:($)=>$ instanceof Date?$.toISOString():$},date:{get:($)=>$?new Date($):null,set:($)=>$ instanceof Date?$.toISOString().split("T")[0]:$},array:{get:($)=>$==null?[]:Array.isArray($)?$:typeof $==="string"?VJ($):[],set:($)=>$==null?null:Array.isArray($)?JSON.stringify($):$}}});import{readdirSync as CJ}from"fs";import{basename as kJ,extname as p$,join as RJ}from"path";function PJ($){let J=[],O=(G)=>{let Z;try{Z=CJ(G,{withFileTypes:!0})}catch{return}for(let z of Z){let K=RJ(G,z.name);if(z.isDirectory()){O(K);continue}if(![".ts",".js"].includes(p$(z.name)))continue;if(z.name.startsWith(".")||z.name.startsWith("index"))continue;J.push(K)}};return O($),J.sort()}async function h$($,J,O){for(let G of PJ($))try{let Z=p$(G),z=await import(`${G}?t=${Date.now()}`),K=z.default??z,X=K.name??kJ(G,Z);J[X]={...K,name:X}}catch(Z){O?.(G,Z)}}async function m$($){let J={};return await h$($.defaultsRoot,J,$.onImportError),await h$($.userRoot,J,$.onImportError),J}var i$=()=>{};var JJ={};x$(JJ,{default:()=>cJ});import{route as m}from"@stacksjs/router";import{env as l}from"@stacksjs/env";import{projectPath as r$,storagePath as fJ}from"@stacksjs/path";import{createQueryBuilder as yJ,defaultConfig as n$,setConfig as u$}from"@stacksjs/query-builder";import{HttpError as n}from"@stacksjs/error-handling";import{log as X$}from"@stacksjs/logging";function o($,J){let O=U$(J);return m.routes.some((G)=>G.method===$&&U$(String(G.path??""))===O)}function a$($){if(!$.attributes)return[];return Object.entries($.attributes).filter(([J,O])=>O.hidden===!0).map(([J])=>J)}function l$($){if($.endsWith("y")&&!/[aeiou]y$/i.test($))return`${$.slice(0,-1)}ies`;if(/(?:s|x|z|ch|sh)$/i.test($))return`${$}es`;return`${$}s`}async function o$($,J,O,G){if(!$.length)return $;let Z=O.split(",").map((Q)=>Q.trim()).filter(Boolean);if(!Z.length)return $;let z=Array.isArray(J.belongsTo)?J.belongsTo:[],K=Array.isArray(J.hasMany)?J.hasMany:[],X=Array.isArray(J.hasOne)?J.hasOne:[],W=new Map;for(let Q of z)W.set(k(Q),{kind:"belongsTo",modelName:Q});for(let Q of X)W.set(k(Q),{kind:"hasOne",modelName:Q});for(let Q of K)W.set(l$(k(Q)),{kind:"hasMany",modelName:Q});for(let Q of Z){let T=W.get(Q);if(!T)continue;let I=G[T.modelName];if(!I)continue;let P=I.table||l$(k(T.modelName)),F=a$(I),N=(g)=>t(O$(g,I),F);if(T.kind==="belongsTo"||T.kind==="hasOne"){let g=`${k(T.modelName)}_id`,D=[...new Set($.map((H)=>H[g]).filter((H)=>H!=null))];if(!D.length){for(let H of $)H[Q]=null;continue}let q=await b.selectFrom(P).whereIn("id",D).get(),M=new Map;for(let H of q??[])M.set(String(H.id),N(H));for(let H of $)H[Q]=M.get(String(H[g]))??null}else{let g=String(J.name??""),D=`${k(g)}_id`,q=[...new Set($.map((Y)=>Y.id).filter((Y)=>Y!=null))];if(!q.length){for(let Y of $)Y[Q]=[];continue}let M=await b.selectFrom(P).whereIn(D,q).get(),H=new Map;for(let Y of M??[]){let L=String(Y[D]),x=H.get(L)??[];x.push(N(Y)),H.set(L,x)}for(let Y of $)Y[Q]=H.get(String(Y.id))??[]}}return $}async function s$($,J){let O=J?.set;if(!O||typeof O!=="object")return $;let G={...$};for(let[Z,z]of Object.entries(O)){if(typeof z!=="function"||!(Z in G))continue;try{G[Z]=await z(G)}catch{}}return G}function d$($,J){return H$($,J?.casts,"set")}function O$($,J){return H$($,J?.casts,"get")}function B($,J=200){return new Response(JSON.stringify($),{status:J,headers:{"Content-Type":"application/json"}})}async function I$($){if(Number($.headers?.get?.("content-length")??0)>W$)throw new n(413,`Request body exceeds ${W$}-byte limit`);if($.jsonBody&&typeof $.jsonBody==="object")return $.jsonBody;if($.formBody&&typeof $.formBody==="object")return $.formBody;try{let O=await $.clone().text();if(O.length>W$)throw new n(413,`Request body exceeds ${W$}-byte limit`);if(!O)return{};let G=JSON.parse(O);return G&&typeof G==="object"&&!Array.isArray(G)?G:{}}catch(O){if(O instanceof n)throw O;throw new n(400,`Invalid JSON body: ${O.message}`)}}function L$($){if($==null)return null;if(typeof $==="number")return Number.isFinite($)&&$>0?$:null;if(typeof $==="string"){let J=$.trim();if(!J)return null;if(/^\d+$/.test(J)){let O=Number(J);return Number.isFinite(O)&&O>0?O:null}return J}return null}function e$($){let J=$.bearerToken;if(typeof J==="function"){let G=J.call($);if(G)return G}let O=$.headers?.get?.("authorization")||$.headers?.get?.("Authorization")||"";if(typeof O==="string"&&O.startsWith("Bearer "))return O.substring(7);return null}async function r($){let J=$._authenticatedUser;if(J)return J;let O=e$($);if(!O)return null;try{let{Auth:G}=await import("@stacksjs/auth"),Z=await G.getUserFromToken(O);if(Z)$._authenticatedUser=Z;return Z||null}catch{return null}}function $$($,J){if($==null||J==null)return!1;if(Array.isArray(J))return J.some((O)=>String(O)===String($));return String($)===String(J)}function bJ($){let J=$?.attributes||{},O=(G)=>Object.prototype.hasOwnProperty.call(J,G);return O("teamId")||O("team_id")?"team_id":null}function qJ($){let J=e$($),O=$.headers?.get?.("cookie")||"";return{bearerToken:()=>J,cookies:{get:(G)=>{for(let Z of O.split(";")){let z=Z.indexOf("=");if(z===-1)continue;if(Z.slice(0,z).trim()===G)return decodeURIComponent(Z.slice(z+1).trim())}return null}}}}function $J($){if($?.ownership)return $.ownership;let J=bJ($);if(!J)return null;return{field:J,resolve:async(O,G)=>{let{resolveAuthenticatedTeamId:Z}=await import("@stacksjs/auth");return Z(qJ(G))}}}async function J$($,J,O){let G=$J($);if(!G||!G.field||typeof G.resolve!=="function")return{enforced:!1,value:null,field:"",bypass:!1};let Z=typeof G.bypass==="function"?!!G.bypass(J,O):!1,z=null;try{z=await G.resolve(J,O)}catch{z=null}return{enforced:!0,value:z,field:String(G.field),bypass:Z}}async function t$($,J,O,G){let Z=$?.authedFill?.[J];if(!Z)return{};if(typeof Z==="function")try{let z=await Z(O,G);return z&&typeof z==="object"?z:{}}catch{return{}}if(typeof Z==="object"){let z={};for(let[K,X]of Object.entries(Z))try{z[K]=typeof X==="function"?await X(O,G):X}catch{}return z}return{}}var gJ,E$,b,W$=1048576,cJ;var OJ=e(async()=>{v$();i$();gJ=r$("config/qb.ts");try{let $=(await import(gJ)).default;u$($??n$)}catch{X$.debug("[orm] No config/qb.ts override found \u2014 deriving config from DB_CONNECTION");let $=l.DB_CONNECTION||"sqlite";u$({...n$,dialect:$,database:$==="sqlite"?{database:l.DB_DATABASE_PATH||"database/stacks.sqlite"}:{database:l.DB_DATABASE||"stacks",host:l.DB_HOST||"127.0.0.1",port:l.DB_PORT||($==="postgres"?5432:3306),username:l.DB_USERNAME||($==="postgres"?"postgres":"root"),password:l.DB_PASSWORD||""}})}E$=await m$({defaultsRoot:fJ("framework/defaults/app/Models"),userRoot:r$("app/Models"),onImportError:($,J)=>X$.warn(`[orm] Could not load model ${$}: ${J.message}`)}),b=yJ();for(let[$,J]of Object.entries(E$)){let O=J.traits?.useApi;if(!O)continue;let G=typeof O==="object"?O:{},Z=G.uri||J.table||$.toLowerCase()+"s",z=G.routes||["index","show","store","update","destroy"],K=J.table||Z,X=g$(J),W=a$(J),Q=`/api/${Z}`,T=b$(J.attributes,W),{read:I,write:P,declared:F}=c$(O);if(["store","update","destroy"].some((Y)=>z.includes(Y))&&F&&P.length===0)X$.warn(`[orm] ${$}: registering UNAUTHENTICATED mutating routes at ${Q} (explicit \`middleware: []\` opt-out)`);if(["index","show"].some((Y)=>z.includes(Y))&&F&&I.length===0)X$.debug(`[orm] ${$}: registering public read routes at ${Q} (declared \`middleware.read: []\`)`);let D=!!$J(J),q=D&&!I.includes("auth")?["auth",...I]:I,M=(Y,L=q)=>{let x=Y;for(let j of L)if(x&&typeof x.middleware==="function")x=x.middleware(j);return x};if(z.includes("index")&&!o("GET",Q))M(m.get(Q,async(Y)=>{try{let L=new URL(Y.url),{page:x,perPage:j,offset:U}=w$(L.searchParams),_=L.searchParams.get("sort"),A=b.selectFrom(K);A=q$(A,_,T);let c=new Set(["page","per_page","sort","fields","search","include","limit","offset","with_count","withTrashed","onlyTrashed"]);for(let[f,y]of L.searchParams.entries()){if(c.has(f)||!y)continue;if(!/^[a-z_][a-z0-9_]*$/i.test(f))continue;let u=T.get(f);if(u)A=A.where(u,"=",y)}let E=L.searchParams.get("search");if(E&&X.length>0){let f=X.slice(0,5);A=A.where((y)=>{for(let u of f)y=y.orWhere(k(u),"like",`%${E}%`);return y})}let V=L.searchParams.get("fields");if(V){let f=V.split(",").map((y)=>y.trim()).filter((y)=>/^[a-z_][a-z0-9_]*$/i.test(y)).map((y)=>k(y));if(f.length>0)A=A.select(f)}let C=!!J.traits?.useSoftDeletes,R=L.searchParams.get("withTrashed")==="true",i=L.searchParams.get("onlyTrashed")==="true";if(C&&!R)A=i?A.whereNotNull("deleted_at"):A.whereNull("deleted_at");let S=D?await J$(J,await r(Y),Y):{enforced:!1,value:null,field:"",bypass:!1};if(S.enforced&&!S.bypass){if(S.value==null||Array.isArray(S.value)&&S.value.length===0){let f=D$(L,x,j,0,!1,void 0);return new Response(JSON.stringify({data:[],...f,meta:Q$(L,x,j,0,!1,void 0)}),{status:200,headers:{"Content-Type":"application/json","Cache-Control":"private, no-store",Vary:"Authorization"}})}A=Array.isArray(S.value)?A.where(S.field,"in",S.value):A.where(S.field,"=",S.value)}let h=await A.limit(j+1).offset(U).get()||[],p=h.length>j,a=(p?h.slice(0,j):h).map((f)=>t(O$(f,J),W)),V$=L.searchParams.get("include");if(V$)a=await o$(a,J,V$,E$);let KJ=L.searchParams.get("with_count")==="true",s;if(KJ)try{let f=b.selectFrom(K);if(S.enforced&&!S.bypass&&S.value!=null)f=Array.isArray(S.value)?f.where(S.field,"in",S.value):f.where(S.field,"=",S.value);let y=await f.count(),u=typeof y==="number"?y:Number(y?.count??y);if(Number.isFinite(u))s=u}catch{}let Z$={"Content-Type":"application/json"};if(s!==void 0&&!Number.isNaN(s))Z$["X-Total-Count"]=String(s);Z$["Cache-Control"]=S.enforced&&!S.bypass?"private, no-store":"public, max-age=15, must-revalidate",Z$.Vary="Authorization";let QJ=D$(L,x,j,a.length,p,s);return new Response(JSON.stringify({data:a,...QJ,meta:Q$(L,x,j,a.length,p,s)}),{status:200,headers:Z$})}catch(L){if(L instanceof n){let x={error:L.message};if(L.details!==void 0)x.details=L.details;return B(x,L.status||400)}return B({error:`Failed to fetch ${Z}`,detail:String(L)},500)}}));if(z.includes("show")&&!o("GET",`${Q}/{id}`))M(m.get(`${Q}/{id}`,async(Y)=>{try{let L=L$(Y.params?.id);if(L==null)return B({error:"Invalid ID parameter"},400);let x=await b.selectFrom(K).where({id:L}).executeTakeFirst();if(!x)return B({error:`${$} not found`},404);let j=new URL(Y.url);if(J.traits?.useSoftDeletes&&x.deleted_at&&j.searchParams.get("withTrashed")!=="true")return B({error:`${$} not found`},404);let U=D?await J$(J,await r(Y),Y):{enforced:!1,value:null,field:"",bypass:!1};if(U.enforced&&!U.bypass){if(U.value==null||!$$(x[U.field],U.value))return B({error:`${$} not found`},404)}let _=t(O$(x,J),W),A=j.searchParams.get("include");if(A){let[R]=await o$([_],J,A,E$);_=R}let c=_?.updated_at||_?.created_at,E=c?`W/"${_.id}-${String(c)}"`:void 0,V=Y.headers?.get?.("if-none-match");if(E&&V&&V===E)return new Response(null,{status:304,headers:{ETag:E}});let C={"Content-Type":"application/json"};if(E)C.ETag=E;return C["Cache-Control"]=U.enforced&&!U.bypass?"private, no-store":"public, max-age=30, must-revalidate",C.Vary="Authorization",new Response(JSON.stringify({data:_}),{status:200,headers:C})}catch(L){if(L instanceof n){let x={error:L.message};if(L.details!==void 0)x.details=L.details;return B(x,L.status||400)}return B({error:`Failed to fetch ${$}`,detail:String(L)},500)}}));if(z.includes("store")&&!o("POST",Q))M(m.post(Q,async(Y)=>{try{let L=await I$(Y),x=N$(L,W),j=_$(x,X),U=await r(Y);if(U){let C=await t$(J,"creating",U,Y);for(let[R,i]of Object.entries(C))if(i!==void 0&&i!==null&&j[R]===void 0)j[R]=i}if(Object.keys(j).length===0)return B({error:"No fillable fields provided"},422);let _=S$(j,J,"creating");if(!_.valid)return B({success:!1,message:"Validation failed",errors:_.errors},422);if(J.traits?.useTimestamps!==!1){let C=new Date().toISOString();j.created_at=C,j.updated_at=C}let A=await s$(j,J),c=A$(d$(A,J)),E=await b.insertInto(K).values(c).execute(),V=c;try{let C=E?.lastInsertRowid??E?.insertId??E;if(C){let R=await b.selectFrom(K).where({id:C}).executeTakeFirst();if(R)V=R}}catch{}return B({data:t(O$(V,J),W)},201)}catch(L){let{status:x,body:j}=T$(L,$,"create");return B(j,x)}}),P);if(z.includes("update")){let Y=async(L)=>{try{let x=L$(L.params?.id);if(x==null)return B({error:"Invalid ID parameter"},400);let j=await I$(L),U=N$(j,W),_=_$(U,X),A=await r(L);if(A){let S=await t$(J,"updating",A,L);for(let[h,p]of Object.entries(S))if(p!==void 0&&p!==null&&_[h]===void 0)_[h]=p}if(Object.keys(_).length===0)return B({error:"No fillable fields provided"},422);let c=S$(_,J,"updating");if(!c.valid)return B({success:!1,message:"Validation failed",errors:c.errors},422);let E=await b.selectFrom(K).where({id:x}).executeTakeFirst();if(!E)return B({error:`${$} not found`},404);let V=await J$(J,A,L);if(V.enforced&&!V.bypass){if(!A)return B({error:"Auth required"},401);let S=E[V.field];if(!$$(S,V.value))return B({error:`Not your ${$}`},403);let h=Object.keys(_).find((p)=>k(p)===k(V.field));if(h!==void 0&&!$$(_[h],V.value))return B({error:`Cannot reassign ${$} ownership`},403)}if(J.traits?.useTimestamps!==!1)_.updated_at=new Date().toISOString();let C=await s$(_,J),R=A$(d$(C,J));await b.updateTable(K).set(R).where({id:x}).execute();let i={...E,...R};try{let S=await b.selectFrom(K).where({id:x}).executeTakeFirst();if(S)i=S}catch{}return B({data:t(O$(i,J),W)})}catch(x){let{status:j,body:U}=T$(x,$,"update");return B(U,j)}};if(!o("PUT",`${Q}/{id}`))M(m.put(`${Q}/{id}`,Y),P);if(!o("PATCH",`${Q}/{id}`))M(m.patch(`${Q}/{id}`,Y),P)}let H=!!J.traits?.useSoftDeletes;if(z.includes("destroy")&&!o("DELETE",`${Q}/{id}`))M(m.delete(`${Q}/{id}`,async(Y)=>{try{let L=L$(Y.params?.id);if(L==null)return B({error:"Invalid ID parameter"},400);let x=await b.selectFrom(K).where({id:L}).executeTakeFirst();if(!x)return B({error:`${$} not found`},404);let j=await r(Y),U=await J$(J,j,Y);if(U.enforced&&!U.bypass){if(!j)return B({error:"Auth required"},401);let _=x[U.field];if(!$$(_,U.value))return B({error:`Not your ${$}`},403)}if(H){let _=new Date().toISOString();await b.updateTable(K).set({deleted_at:_,updated_at:_}).where({id:L}).execute()}else await b.deleteFrom(K).where({id:L}).execute();return new Response(null,{status:204})}catch(L){if(L instanceof n){let x={error:L.message};if(L.details!==void 0)x.details=L.details;return B(x,L.status||400)}return B({error:`Failed to delete ${$}`,detail:String(L)},500)}}),P);if(z.includes("destroy")&&!o("POST",`${Q}/bulk-delete`))M(m.post(`${Q}/bulk-delete`,async(Y)=>{try{let x=(await I$(Y))?.ids;if(!Array.isArray(x)||x.length===0)return B({error:"An array of IDs is required"},422);if(x.length>100)return B({error:"Cannot delete more than 100 records at once"},422);let j=[],U=[];for(let E of x){let V=L$(E);if(V!=null)j.push(V);else U.push(E)}if(U.length>0)return B({error:"Invalid IDs in batch",invalid:U},422);let _=await r(Y),A=await J$(J,_,Y);if(A.enforced&&!A.bypass){if(!_)return B({error:"Auth required"},401);if(A.value==null)return B({error:"Caller has no ownership identity"},403);let E=await b.selectFrom(K).select(["id",A.field]).execute(),V=new Set(E.filter((R)=>$$(R[A.field],A.value)).map((R)=>String(R.id))),C=j.filter((R)=>!V.has(String(R)));if(C.length>0)return B({error:`Cannot delete ${$} you don't own`,ids:C},403)}let c=new Date().toISOString();for(let E of j)if(H)await b.updateTable(K).set({deleted_at:c,updated_at:c}).where({id:E}).execute();else await b.deleteFrom(K).where({id:E}).execute();return B({message:`Successfully deleted ${j.length} ${Z}`})}catch(L){if(L instanceof n){let x={error:L.message};if(L.details!==void 0)x.details=L.details;return B(x,L.status||400)}return B({error:`Failed to bulk delete ${Z}`,detail:String(L)},500)}}),P)}cJ=m});class M${data;status;headers;isOk;constructor($,J,O,G){this.data=$;this.status=J;this.headers=O;this.isOk=G}ok(){return this.status===200}created(){return this.status===201}accepted(){return this.status===202}noContent(){return this.status===204}movedPermanently(){return this.status===301}found(){return this.status===302}badRequest(){return this.status===400}unauthorized(){return this.status===401}paymentRequired(){return this.status===402}forbidden(){return this.status===403}notFound(){return this.status===404}requestTimeout(){return this.status===408}conflict(){return this.status===409}unprocessableEntity(){return this.status===422}tooManyRequests(){return this.status===429}serverError(){return this.status===500}}class C${defaultHeaders={Accept:"application/json","Content-Type":"application/json"};queryParams;bodyData;isFormData=!1;customHeaders={};attachments=[];isMultipart=!1;digestAuth;withQueryParams($){return this.queryParams=$,this}withHeaders($){return this.customHeaders={...this.customHeaders,...$},this}accept($){return this.defaultHeaders.Accept=$,this}acceptJson(){return this.accept("application/json")}withToken($){return this.customHeaders.Authorization=`Bearer ${$}`,this}withBasicAuth($,J){let O=btoa(`${$}:${J}`);return this.customHeaders.Authorization=`Basic ${O}`,this}withDigestAuth($,J){return this.digestAuth={username:$,password:J},this}withBody($){return this.bodyData=$,this}asForm(){return this.isFormData=!0,this.defaultHeaders["Content-Type"]="application/x-www-form-urlencoded",this}attach($,J,O,G){let Z=typeof J==="string"?new Blob([J],{type:G?.["Content-Type"]||"text/plain"}):J;return this.attachments.push({name:$,content:Z,filename:O,headers:G}),this.isMultipart=!0,delete this.defaultHeaders["Content-Type"],this}getHeaders(){return{...this.defaultHeaders,...this.customHeaders}}addQueryParams($){if(!this.queryParams)return $;let J=new URLSearchParams;Object.entries(this.queryParams).forEach(([Z,z])=>{if(z!=null)J.append(Z,String(z))});let O=J.toString();if(!O)return $;let G=$.includes("?");return $+(G?"&":"?")+O}async formatBody($){let J=$||this.bodyData;if(this.isMultipart){let O=new FormData;if(J)Object.entries(J).forEach(([G,Z])=>{O.append(G,String(Z))});for(let G of this.attachments)if(G.headers){let Z=G.content instanceof Blob?new Blob([G.content],{type:G.headers["Content-Type"]}):G.content;O.append(G.name,Z,G.filename)}else O.append(G.name,G.content,G.filename);return O}if(this.isFormData&&J){let O=new URLSearchParams;return Object.entries(J).forEach(([G,Z])=>{if(Z!=null)O.append(G,String(Z))}),O.toString()}return J?JSON.stringify(J):void 0}resetState(){this.queryParams=void 0,this.bodyData=void 0,this.isFormData=!1,this.isMultipart=!1,this.customHeaders={},this.attachments=[],this.digestAuth=void 0,this.defaultHeaders["Content-Type"]="application/json"}async handleDigestAuth($,J,O,G){if($.status!==401||!this.digestAuth)return $;let Z=$.headers.get("WWW-Authenticate");if(!Z?.startsWith("Digest "))return $;let z=Z.substring(7).split(",").reduce((F,N)=>{let[g,D]=N.trim().split("=");if(g)F[g]=D?.replace(/["']/g,"")??"";return F},{}),K=await this.generateMD5(`${this.digestAuth.username}:${z.realm}:${this.digestAuth.password}`),X=await this.generateMD5(`${O}:${J}`),W="00000001",Q=await this.generateMD5(Date.now().toString()).then((F)=>F.substring(0,8)),T=await this.generateMD5(`${K}:${z.nonce}:${W}:${Q}:${z.qop}:${X}`),I={username:`"${this.digestAuth.username}"`,realm:`"${z.realm}"`,nonce:`"${z.nonce}"`,uri:`"${J}"`,algorithm:z.algorithm?`"${z.algorithm}"`:void 0,qop:`"${z.qop}"`,nc:W,cnonce:`"${Q}"`,response:`"${T}"`,opaque:z.opaque?`"${z.opaque}"`:void 0},P=Object.entries(I).filter(([F,N])=>N!==void 0).map(([F,N])=>`${F}=${N}`).join(", ");return this.customHeaders.Authorization=`Digest ${P}`,fetch(J,{method:O,headers:this.getHeaders(),body:G?this.formatBody(G):void 0})}async generateMD5($){let O=new TextEncoder().encode($),G=await crypto.subtle.digest("SHA-256",O);return Array.from(new Uint8Array(G)).map((z)=>z.toString(16).padStart(2,"0")).join("")}async request($,J,O){let G=this.addQueryParams($),Z=await this.formatBody(O),z=await fetch(G,{method:J,headers:this.getHeaders(),body:Z});if(this.digestAuth)z=await this.handleDigestAuth(z,G,J,O);let K=await z.json();return this.resetState(),new M$(K,z.status,z.headers,z.ok)}async get($){return this.request($,"GET")}async post($,J){return this.request($,"POST",J)}async put($,J){return this.request($,"PUT",J)}async patch($,J){return this.request($,"PATCH",J)}async delete($){return this.request($,"DELETE")}}var mJ=new C$;B$();import{path as Y$}from"@stacksjs/path";function wJ($){if(!$||typeof $!=="object")return{type:"string"};let J=$,O=J._type??"string",G={type:O==="array"?"array":O};if(typeof J._min==="number")G[O==="string"?"minLength":"minimum"]=J._min;if(typeof J._max==="number")G[O==="string"?"maxLength":"maximum"]=J._max;if(J._email)G.format="email";if(J._url)G.format="uri";if(J._uuid)G.format="uuid";if(Array.isArray(J._enum))G.enum=J._enum;return G}async function vJ($){if(typeof $!=="string"||!$||$.includes(".."))return null;try{let{path:J}=await import("@stacksjs/path"),O=[J.appPath(`${$}.ts`),J.storagePath(`framework/defaults/app/${$}.ts`)];for(let G of O)try{if(!await Bun.file(G).exists())continue;let K=(await import(G)).default;if(K&&typeof K==="object"&&"validations"in K){let X=K.validations;return X&&typeof X==="object"?X:null}return null}catch{}}catch{}return null}async function zO($={}){let J=$.portable??!0,{listRegisteredRoutes:O}=await import("@stacksjs/router");try{let{loadRoutes:W}=await import("@stacksjs/router"),{default:Q}=await import(Y$.appPath("Routes.ts"));await W(Q)}catch(W){let Q=W instanceof Error?W.message:String(W);if(J)throw Error(`[generateOpenApi] Refusing to emit a partial spec: failed to load routes/api.ts + framework routes: ${Q}`);console.warn(`[generateOpenApi] Failed to load routes/api.ts + framework routes: ${Q}`)}try{await OJ().then(() => JJ)}catch(W){let Q=W instanceof Error?W.message:String(W);if(J)throw Error(`[generateOpenApi] Refusing to emit a partial spec: failed to load useApi-generated ORM routes: ${Q}`);console.warn(`[generateOpenApi] Failed to load useApi-generated ORM routes: ${Q}`)}let G=(W)=>W.replace(/(^|\/):(\w+)(?=$|\/)/g,"$1{$2}"),Z=[...O()].map((W)=>({route:W,oasPath:G(W.path),method:W.method.toLowerCase()})).sort((W,Q)=>{if(W.oasPath!==Q.oasPath)return W.oasPath<Q.oasPath?-1:1;return W.method<Q.method?-1:W.method>Q.method?1:0});if(J&&Z.length===0)throw Error("[generateOpenApi] Refusing to emit an empty spec: no routes were registered. This usually means route loading failed silently rather than that the app has no API.");let z="Stacks API";if(!J)try{let{config:W}=await import("@stacksjs/config");if(W.app?.name)z=`${W.app.name} API`}catch{}let K={openapi:"3.0.0",info:{title:z,version:"1.0.0"},paths:{},components:{schemas:{}}},X=new Set;for(let{route:W,oasPath:Q,method:T}of Z){if(!K.paths[Q])K.paths[Q]={};let I=[];for(let D of W.path.matchAll(/\{(\w+)\}/g))if(D[1])I.push(D[1]);for(let D of W.path.matchAll(/(?:^|\/):(\w+)(?=$|\/)/g))if(D[1])I.push(D[1]);let P=W.name??`${T}_${Q.replace(/[^a-z0-9]/gi,"_")}`,F=P;if(X.has(F)){F=`${P}_${T}`;let D=2;while(X.has(F))F=`${P}_${T}_${D++}`}X.add(F);let N={summary:W.name??W.path,operationId:F,parameters:I.map((D)=>({name:D,in:"path",required:!0,schema:{type:"string"}})),responses:{200:{description:"Successful response",content:{"application/json":{schema:{type:"object"}}}},422:{description:"Validation failed"},500:{description:"Server error"}}},g=W.handler?await vJ(W.handler):null;if(g&&Object.keys(g).length>0){let D=Object.entries(g).map(([q,M])=>{let H=M?.rule;return{field:q,schema:wJ(H),required:Boolean(H?._required)}});if(T==="get"||T==="head"||T==="delete"){let q=new Set(I);for(let{field:M,schema:H,required:Y}of D){if(q.has(M))continue;N.parameters?.push({name:M,in:"query",required:Y,schema:H})}}else{let q={type:"object",properties:{},required:[]},M=q.properties,H=q.required;for(let Y of D)if(M[Y.field]=Y.schema,Y.required)H.push(Y.field);if(H.length===0)delete q.required;N.requestBody={required:H.length>0,content:{"application/json":{schema:q}}}}}K.paths[Q][T]=N}if($.write!==!1){let Q=Bun.file(Y$.frameworkPath("api/openapi.json")).writer();Q.write(JSON.stringify(K,null,2)),await Q.end();let{renderOpenApiTypes:T}=await Promise.resolve().then(() => (z$(),R$)),{renderApiClient:I}=await Promise.resolve().then(() => (B$(),f$));await Bun.write(Y$.frameworkPath("api/api-types.ts"),T(K)),await Bun.write(Y$.frameworkPath("api/client.ts"),I(K,{name:K.info.title}))}return K}z$();class v{static instance=new v;isMissing(){return!0}}class G${data;constructor($){this.data=$}}class GJ{condition;value;defaultValue;constructor($,J,O=new v){this.condition=$;this.value=J;this.defaultValue=O}resolve(){return(typeof this.condition==="function"?this.condition():this.condition)?this.value:this.defaultValue}}class ZJ{resource;additional={};request;static wrap="data";static additionalMeta={};constructor($){this.resource=$}withRequest($){return this.request=$,this}withAdditional($){return this.additional={...this.additional,...$},this}resolve($){let J=this.toArray($||this.request);return this.filterAndResolve(J)}toResponse($){let J=this.resolve($),O=this.constructor.wrap;if(O)return{[O]:J,...this.additional};return{...J,...this.additional}}toJson($){return JSON.stringify(this.toResponse($))}toJSON(){return this.toResponse()}static collection($){return new F$($,this)}when($,J,O){if(typeof $==="function"?$():$)return typeof J==="function"?J():J;return O!==void 0?O:v.instance}whenNotNull($,J){if($===null||$===void 0)return v.instance;return J?J($):$}whenLoaded($,J,O){let G=this.resource;if(!(($ in G)&&G[$]!==void 0))return O!==void 0?O:v.instance;if(J!==void 0)return typeof J==="function"?J():J;return G[$]}whenCounted($,J){let O=this.resource,G=`${$}_count`;if(G in O)return O[G];return J!==void 0?J:v.instance}merge($){return new G$($)}mergeWhen($,J){if(!(typeof $==="function"?$():$))return v.instance;let G=typeof J==="function"?J():J;return new G$(G)}static MAX_RESOLVE_DEPTH=20;filterAndResolve($,J=0){if(J>=ZJ.MAX_RESOLVE_DEPTH)return $;let O={};for(let[G,Z]of Object.entries($)){if(Z instanceof v)continue;if(Z instanceof G$){Object.assign(O,this.filterAndResolve(Z.data,J+1));continue}if(Z instanceof GJ){let z=Z.resolve();if(!(z instanceof v))O[G]=z;continue}if(Z!==null&&typeof Z==="object"&&!Array.isArray(Z)&&!(Z instanceof Date)){O[G]=this.filterAndResolve(Z,J+1);continue}if(Array.isArray(Z)){O[G]=Z.filter((z)=>!(z instanceof v)).map((z)=>{if(z instanceof G$)return z.data;if(z!==null&&typeof z==="object"&&!(z instanceof Date))return this.filterAndResolve(z,J+1);return z});continue}O[G]=Z}return O}static withoutWrapping(){this.wrap=null}static wrapWith($){this.wrap=$}}class F${resources;ResourceClass;additional={};request;static wrap="data";constructor($,J){this.resources=$,this.ResourceClass=J}withRequest($){return this.request=$,this}withAdditional($){return this.additional={...this.additional,...$},this}resolve($){let J=$||this.request;return this.resources.map((O)=>{let G=new this.ResourceClass(O);if(J)G.withRequest(J);return G.resolve(J)})}toResponse($){let J=this.resolve($),O=this.constructor.wrap;if(O)return{[O]:J,...this.additional};return{data:J,...this.additional}}toJson($){return JSON.stringify(this.toResponse($))}count(){return this.resources.length}isEmpty(){return this.resources.length===0}isNotEmpty(){return this.resources.length>0}}class zJ extends F${meta;links;constructor($,J){super($.data,J);this.meta=$.meta,this.links=$.links}static fromPagination($,J,O){let{currentPage:G,perPage:Z,total:z,baseUrl:K=""}=O,X=Math.ceil(z/Z),W=z>0?(G-1)*Z+1:null,Q=z>0?Math.min(G*Z,z):null;return new zJ({data:$,meta:{current_page:G,from:W,last_page:X,per_page:Z,to:Q,total:z},links:{first:`${K}?page=1`,last:`${K}?page=${X}`,prev:G>1?`${K}?page=${G-1}`:null,next:G<X?`${K}?page=${G+1}`:null}},J)}toResponse($){return{data:this.resolve($),meta:this.meta,links:this.links,...this.additional}}}function QO($,J){let O=J($);return{toResponse:()=>({data:O}),toJson:()=>JSON.stringify({data:O})}}function WO($,J){let O=$.map(J);return{toResponse:()=>({data:O}),toJson:()=>JSON.stringify({data:O})}}export{w as schemaType,QO as resource,TJ as renderOpenApiTypes,SJ as renderApiClient,P$ as methodName,zO as generateOpenApi,mJ as fetcher,WO as collection,F$ as ResourceCollection,zJ as PaginatedResourceCollection,v as MissingValue,G$ as MergeValue,ZJ as JsonResource,GJ as ConditionalValue};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/api",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.70.
|
|
5
|
+
"version": "0.70.325",
|
|
6
6
|
"description": "The Stacks API utilities.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -62,6 +62,6 @@
|
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"better-dx": "^0.2.17",
|
|
65
|
-
"@stacksjs/utils": "0.70.
|
|
65
|
+
"@stacksjs/utils": "0.70.325"
|
|
66
66
|
}
|
|
67
67
|
}
|