UNPKG

@fluentity/core

Version:

Fluentity is a fluent, model-oriented, typed HTTP client for TypeScript and framework agnostic.

7 lines (6 loc) 13.6 kB
class K{constructor(J){if(J)Object.assign(this,J)}setOptions(J){return this.options={...this.options,...J},this}url;model;id;parent;query={};sort;direction;limit;offset;page;perPage;method;body;options={};where(J){return this.filter(J),this}filter(J){return this.query={...this.query,...J},this}reset(){this.id=void 0,this.query={},this.sort=void 0,this.direction=void 0,this.limit=void 0,this.offset=void 0,this.page=void 0,this.perPage=void 0,this.method=void 0,this.body=void 0,this.model=void 0}toObject(){return Object.entries({query:this.query,method:this.method,sort:this.sort,direction:this.direction,limit:this.limit,offset:this.offset,page:this.page,perPage:this.perPage}).filter(([J,V])=>V!==void 0).reduce((J,[V,W])=>{return J[V]=W,J},{})}}class L{static scopes;id;#J;static resource;constructor(J,V){if(J)Object.assign(this,J);return this.#J=V??new K({model:this.constructor,id:this.id}),this}get queryBuilder(){return this.#J}get fluentity(){return _.getInstance()}static getRelationBuilder(J,V){return new V(J)}static id(J){return new this({id:J})}static query(){return L.getRelationBuilder(this,I)}static where(J){return L.getRelationBuilder(this,I).where(J)}static filter(J){return L.getRelationBuilder(this,I).filter(J)}static async all(){return await L.getRelationBuilder(this,I).all()}static async find(J){return await L.getRelationBuilder(this,S).find(J)}static async create(J){return await L.getRelationBuilder(this,I).create(J)}static async update(J,V,W=Z.PUT){return await L.getRelationBuilder(this,I).update(J,V,W)}static async delete(J){return L.getRelationBuilder(this,I).delete(J)}async get(){this.#J.method=Z.GET;let J=await this.fluentity.adapter.call(this.#J);return Object.assign(this,J.data),this}async save(){if(this.id)return this.update();this.#J.method=Z.POST,this.#J.body=this.toObject();let J=await this.fluentity.adapter.call(this.#J);return Object.assign(this,J.data),this}async update(J,V=Z.PUT){if(J)Object.assign(this,J);this.#J.method=V,this.#J.id=this.id,this.#J.body=this.toObject();let W=await this.fluentity.adapter.call(this.#J);return Object.assign(this,W.data),this}async delete(){this.#J.method=Z.DELETE,await this.fluentity.adapter.call(this.#J)}async call(J){return this.constructor.call(J)}static async call(J){return await _.getInstance().adapter.call(J)}toObject(){let J={};Object.keys(this).forEach((W)=>{J[W]=this[W]});let V=Object.getOwnPropertyDescriptors(Object.getPrototypeOf(this));for(let[W,X]of Object.entries(V))if(X.get){let $=this[W];if($ instanceof L)J[W]=$.toObject();else if(Array.isArray($)&&$.length>0)J[W]=$.filter((Y)=>Y instanceof L&&typeof Y.toObject==="function").map((Y)=>Y.toObject())}return J}reset(...J){for(let V of J)this[V]=void 0;return this}}class U{#J;#V;#W;constructor(J,V){if(this.#V=J,this.#J=new K({model:this.#V}),V)this.#J.parent={...V};if(this.#V.scopes)Object.entries(this.#V.scopes).forEach(([W,X])=>{this[W]=(...$)=>{return X(this,...$)}})}get data(){return this.#W}set data(J){this.#W=J}get fluentity(){return _.getInstance()}get queryBuilder(){return this.#J}get relatedModel(){return this.#V}id(J){return this.#J.id=J,new this.#V({id:J},this.#J)}async find(J){this.#J.id=J,this.#J.method=Z.GET;let V=await this.fluentity.adapter.call(this.#J),W=new this.#V(V.data,this.#J);return W.queryBuilder.id=J,W}where(J){return this.#J.where(J),this}filter(J){return this.#J.filter(J),this}orderBy(J="id",V="asc"){return this.#J.sort=J,this.#J.direction=V,this}limit(J){return this.#J.limit=J,this}offset(J){return this.#J.offset=J,this}}class I extends U{async all(){this.queryBuilder.method=Z.GET,this.queryBuilder.id=void 0;let J=await this.fluentity.adapter.call(this.queryBuilder);return this.data=J?.data?.map((V)=>new this.relatedModel(V,{...this.queryBuilder,id:V.id}))}async create(J){this.queryBuilder.method=Z.POST,this.queryBuilder.body=J,this.queryBuilder.id=void 0;let V=await this.fluentity.adapter.call(this.queryBuilder);return this.data=new this.relatedModel(V.data,{...this.queryBuilder,id:V.data.id})}async delete(J){this.queryBuilder.method=Z.DELETE,this.queryBuilder.id=J,await this.fluentity.adapter.call(this.queryBuilder)}async update(J,V,W=Z.PUT){this.queryBuilder.method=W,this.queryBuilder.id=J,this.queryBuilder.body=V;let X=await this.fluentity.adapter.call(this.queryBuilder);return this.data=new this.relatedModel(X.data,{...this.queryBuilder,id:X.data.id})}async paginate(J=1,V=10){this.queryBuilder.page=J,this.queryBuilder.perPage=V,this.queryBuilder.offset=(J-1)*V,this.queryBuilder.limit=V;let W=await this.fluentity.adapter.call(this.queryBuilder);return this.data=W?.data?.map((X)=>new this.relatedModel(X,{...this.queryBuilder,id:X.id}))}}class S extends U{async get(){this.queryBuilder.method=Z.GET;let J=await this.fluentity.adapter.call(this.queryBuilder);return this.data=new this.relatedModel(J.data,{...this.queryBuilder,id:J.data.id})}async update(J,V=Z.PUT){this.queryBuilder.method=V,this.queryBuilder.id=this.relatedModel.id;let W=await this.fluentity.adapter.call(this.queryBuilder);return this.data=new this.relatedModel(W.data,{...this.queryBuilder,id:W.data.id})}async delete(){this.queryBuilder.method=Z.DELETE,this.queryBuilder.id=this.relatedModel.id,await this.fluentity.adapter.call(this.queryBuilder)}}var A=(J,V,W)=>{let X=new WeakMap;return function($,Y){let z=Symbol(String(Y));Object.defineProperty($,Y,{get(){if(W)this.queryBuilder.setOptions(W);let x=X.get(this)??new V(J(),this.queryBuilder);if(X.set(this,x),this[z]!==void 0){let E=this[z],P=J();if(Array.isArray(E))x.data=E.map((O)=>O instanceof P?O:new P(O));else x.data=new P(E)}return x},set(x){this[z]=x},enumerable:!0,configurable:!0})}},r=(J)=>{return function(V,W){let X=Symbol(String(W)),$=J();Object.defineProperty(V,W,{get(){if(!this[X])this[X]=void 0;let Y=this[X];if(!Y||!$)return Y;if(Array.isArray(Y))return Y.map((z)=>z instanceof $?z:new $(z));else if(Y instanceof $)return Y;else return new $(Y)},set(Y){if(!$){this[X]=Y;return}if(Array.isArray(Y))this[X]=Y.map((z)=>z instanceof $?z:new $(z));else if(Y instanceof $)this[X]=Y;else this[X]=new $(Y)},enumerable:!0,configurable:!0})}},C=(J,V)=>{return A(J,S,V)},Q=(J,V)=>{return A(J,I,V)},e=C,JJ=Q,VJ=()=>{return(J,V)=>{if(!J._fieldMetadata)J._fieldMetadata=new Set;J._fieldMetadata.add(String(V))}};class j{options={};async call(J){return Promise.resolve({data:void 0})}configure(J){}}class _{static instance;#J;#V={};constructor(J){if(_.instance)throw new Error("Fluentity instance already exists. Use getInstance() instead.");this.#J=J?.adapter??new j,_.instance=this}configure(J){if(!_.instance)throw new Error("Fluentity has not been initialized. Call initialize() first.");this.#V=J,this.#J=J?.adapter??new j}get adapter(){return this.#J}static initialize(J){if(_.instance)throw new Error("Fluentity has already been initialized. Use getInstance() instead.");return new _(J)}static reset(){_.instance=void 0}static getInstance(){if(!_.instance)throw new Error("Fluentity has not been initialized. Call initialize() first.");return _.instance}call(J){return this.#J.call(J)}static call(J){return _.getInstance().call(J)}}var Z={GET:"GET",POST:"POST",PUT:"PUT",PATCH:"PATCH",DELETE:"DELETE",HEAD:"HEAD",OPTIONS:"OPTIONS"};class G{_cache=new WeakMap;options={baseUrl:"",options:{headers:{"Content-Type":"application/json"}},requestInterceptor:void 0,responseInterceptor:void 0,errorInterceptor:void 0,requestHandler:this.fetchRequestHandler,cacheOptions:{enabled:!1,ttl:300000}};_request=new N;constructor(J){if(!J.baseUrl)throw new Error("baseUrl is required");this.options={...this.options,...J}}configure(J){return this.options={...this.options,...J},this}get request(){return this._request}clearCache(){return this._cache=new WeakMap,this}async call(J){try{if(!this.options.baseUrl)throw new Error("baseUrl is required");if(this._request=this.buildRequest(J),this.options.cacheOptions?.enabled){let W=this._cache.get(this._request);if(W){if(Date.now()-W.timestamp<(this.options.cacheOptions.ttl||0))return W;this._cache.delete(this._request)}}if(this.options.requestInterceptor)Object.assign(this._request,this.options.requestInterceptor.call(this,this._request));let V=await this.options.requestHandler.call(this,this._request);if(this.options.responseInterceptor)V=this.options.responseInterceptor.call(this,V);if(this.options.cacheOptions?.enabled)this._cache.set(this._request,{data:V,timestamp:Date.now()});return V}catch(V){if(this.options.errorInterceptor&&V instanceof Error)this.options.errorInterceptor(V);throw V}}buildRequest(J){return new N({url:"",method:J.method,body:J.body,options:this.options?.options})}async fetchRequestHandler(J){return Promise.resolve(new D)}}class N{url="";options;method;body;constructor(J){if(J)Object.assign(this,J)}}class D{data={};constructor(J){if(J)Object.assign(this,J)}}class T extends G{constructor(J){super(J);this.options={...this.options,...J}}buildRequest(J){return new N({url:this.buildUrl(J),method:J.method,body:J.body,options:this.options?.options})}buildUrl(J){let V=this.toQueryString(J),W=[];W=this.unwrapParents(J,W);let X=W.join("/");if(V)X+=`?${V}`;return decodeURIComponent(X)}unwrapParents(J,V){if(J.parent)this.unwrapParents(J.parent,V);if(V.push(J.options?.resource??J.model?.resource),J.id)V.push(`${J.id}`);return V}toQueryString(J){let V={...J.query};return V.page=J.page,V.perPage=J.perPage,V.sort=J.sort,V.direction=J.direction,V.limit=J.limit,V.offset=J.offset,Object.entries(V).filter(([W,X])=>X!==void 0).map(([W,X])=>`${W}=${X}`).join("&")}async fetchRequestHandler(J){if(J.options?.headers?.["Content-Type"]==="application/json"&&J.body)J.body=JSON.stringify(J.body);try{let V=await fetch(`${this.options.baseUrl}/${J.url}`,{method:J.method,body:["PUT","POST","PATCH"].includes(J.method)?J.body:null,headers:J.options?.headers,credentials:J.options?.credentials,mode:J.options?.mode,redirect:J.options?.redirect,referrer:J.options?.referrer,cache:J.options?.cache,keepalive:J.options?.keepalive,signal:J.options?.signal});return new D({data:await V.json()})}catch(V){throw new Error(`HTTP error: ${V}`)}}}class H extends G{constructor(J){super(J);if(!J.endpoint)throw new Error("GraphqlAdapter requires an endpoint");this.options={...this.options,...J}}buildRequest(J){return new N({url:this.options.endpoint??"/graphql",method:J.method??Z.POST,body:this.buildBody(J),options:this.options?.options})}buildBody(J){return{query:this.buildGraphQLQuery(J),variables:this.buildVariables(J),operationName:this.generateOperationName(J)}}generateOperationType(J){switch(J.method){case Z.GET:return"query";case Z.POST:case Z.PUT:case Z.PATCH:case Z.DELETE:return"mutation";default:return"query"}}buildGraphQLQuery(J){let V=J.options?.operationType??this.generateOperationType(J),W=J.options?.operationName??this.generateOperationName(J),X=J.options?.resource??J.model?.resource??"resource",$=this.buildFields(J),Y=this.buildArguments(J),z=`${V} ${W}`;if(Y.length>0)z+=`(${Y.join(", ")})`;return z+=` { ${X}${this.buildQueryArguments(J)} { ${$} } }`,z}buildFields(J){let V=new Set;if(V.add("id"),J.model)J.model.prototype._fieldMetadata.forEach((W)=>{V.add(W)});if(J.query)Object.keys(J.query).filter((W)=>typeof J.query[W]==="boolean"&&J.query[W]).forEach(V.add);if(J.body&&typeof J.body==="object")Object.keys(J.body).filter((W)=>typeof J.body[W]==="boolean"&&J.body[W]).forEach(V.add);if(J.parent){let W=this.buildFields(J.parent);V.add(W)}return Array.from(V).join(` `)}buildArguments(J){let V=[];if(J.id)V.push("$id: ID!");if(J.limit)V.push("$limit: Int");if(J.offset)V.push("$offset: Int");if(J.page)V.push("$page: Int");if(J.perPage)V.push("$perPage: Int");if(J.sort)V.push("$sort: String");if(J.direction)V.push("$direction: String");if(J.query&&Object.keys(J.query).length>0)V.push("$filters: JSON");if(J.body&&this.generateOperationType(J)==="mutation")V.push("$input: JSON!");return V}buildQueryArguments(J){let V=[];if(J.id)V.push("id: $id");if(J.limit)V.push("limit: $limit");if(J.offset)V.push("offset: $offset");if(J.page)V.push("page: $page");if(J.perPage)V.push("perPage: $perPage");if(J.sort)V.push("sort: $sort");if(J.direction)V.push("direction: $direction");if(J.query&&Object.keys(J.query).length>0)V.push("filters: $filters");if(J.body&&this.generateOperationType(J)==="mutation")V.push("input: $input");return V.length>0?`(${V.join(", ")})`:""}buildVariables(J){let V={};if(J.id)V.id=J.id;if(J.limit)V.limit=J.limit;if(J.offset)V.offset=J.offset;if(J.page)V.page=J.page;if(J.perPage)V.perPage=J.perPage;if(J.sort)V.sort=J.sort;if(J.direction)V.direction=J.direction;if(J.query&&Object.keys(J.query).length>0)V.filters=J.query;if(J.body&&this.generateOperationType(J)==="mutation")V.input=J.body;return V}generateOperationName(J){let V=J.model?.resource||"resource",W=J.method||Z.GET;if(J.id)return`Get${V.charAt(0).toUpperCase()+V.slice(1)}ById`;switch(W){case Z.GET:return`Get${V.charAt(0).toUpperCase()+V.slice(1)}`;case Z.POST:return`Create${V.charAt(0).toUpperCase()+V.slice(1)}`;case Z.PUT:case Z.PATCH:return`Update${V.charAt(0).toUpperCase()+V.slice(1)}`;case Z.DELETE:return`Delete${V.charAt(0).toUpperCase()+V.slice(1)}`;default:return`Get${V.charAt(0).toUpperCase()+V.slice(1)}`}}}var F;((X)=>{X.QUERY="query";X.MUTATION="mutation";X.SUBSCRIPTION="subscription"})(F||={});export{T as RestAdapter,U as RelationBuilder,K as QueryBuilder,L as Model,Z as Methods,D as HttpResponse,N as HttpRequest,G as HttpAdapter,S as HasOneRelationBuilder,C as HasOne,I as HasManyRelationBuilder,Q as HasMany,F as GraphqlOperationType,H as GraphqlAdapter,_ as Fluentity,VJ as Field,j as DefaultAdapter,r as Cast,JJ as BelongsToMany,e as BelongsTo};