Comparing version 0.7.0 to 0.7.1
@@ -1,2 +0,2 @@ | ||
var pe=(r=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(r,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):r)(function(r){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+r+'" is not supported')});var oe=(r,e,t)=>{if(!e.has(r))throw TypeError("Cannot "+t)};var g=(r,e,t)=>(oe(r,e,"read from private field"),t?t.call(r):e.get(r)),U=(r,e,t)=>{if(e.has(r))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(r):e.set(r,t)},ae=(r,e,t,n)=>(oe(r,e,"write to private field"),n?n.call(r,t):e.set(r,t),t);var W=(r,e,t)=>(oe(r,e,"access private method"),t);var ie={default:new URL("https://db.fauna.com"),local:new URL("http://localhost:8443"),localhost:new URL("http://localhost:8443")};var D=class extends Error{constructor(...e){super(...e)}},a=class extends D{httpStatus;code;queryInfo;constraint_failures;constructor(e,t){super(e.error.message),Error.captureStackTrace&&Error.captureStackTrace(this,a),this.name="ServiceError",this.code=e.error.code,this.httpStatus=t;let n={txn_ts:e.txn_ts,summary:e.summary,query_tags:e.query_tags,stats:e.stats};this.queryInfo=n,this.constraint_failures=e.error.constraint_failures}},I=class extends a{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,I),this.name="QueryRuntimeError"}},Q=class extends a{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,Q),this.name="QueryCheckError"}},E=class extends a{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,E),this.name="InvalidRequestError"}},X=class extends a{abort;constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,Q),this.name="AbortError",this.abort=e.error.abort}},V=class extends a{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,V),this.name="AuthenticationError"}},k=class extends a{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,k),this.name="AuthorizationError"}},Z=class extends a{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,E),this.name="ContendedTransactionError"}},F=class extends a{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,F),this.name="ThrottlingError"}},O=class extends a{stats;constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,O),this.name="QueryTimeoutError",this.stats=e.stats}},N=class extends a{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,N),this.name="ServiceInternalError"}},A=class extends a{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,A),this.name="ServiceTimeoutError"}},y=class extends D{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,y),this.name="ClientError"}},_=class extends D{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,_),this.name="ClientClosedError"}},p=class extends D{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,p),this.name="NetworkError"}},T=class extends D{httpStatus;constructor(e){super(e.message),Error.captureStackTrace&&Error.captureStackTrace(this,T),this.name="ProtocolError",this.httpStatus=e.httpStatus}};var Re=/(?:\d{4}|[\u2212-]\d{4,}|\+\d{5,})/,Pe=/(?:0[1-9]|1[0-2])/,Ce=/(?:0[1-9]|[12]\d|3[01])/,fe=/(?:[01][0-9]|2[0-3])/,ee=/(?:[0-5][0-9])/,He=/(?:\.\d+)/,ce=new RegExp(`(${Re.source}-(${Pe.source})-(${Ce.source}))`),qe=new RegExp(`(${fe.source}:${ee.source}:${ee.source}${He.source}?)`),De=new RegExp(`([zZ]|[+\u2212-]${fe.source}(?::?${ee.source}|:${ee.source}:${ee.source}))`),ge=new RegExp(`^${ce.source}$`),Te=new RegExp(`^${ce.source}`),he=new RegExp(`^${ce.source}T${qe.source}${De.source}$`);var h=class{isoString;constructor(e){this.isoString=e}static from(e){if(typeof e!="string")throw new TypeError(`Expected string but received ${typeof e}: ${e}`);if(he.exec(e)===null)throw new RangeError(`(regex) Expected an ISO date string but received '${e}'`);return new h(e)}static fromDate(e){return new h(e.toISOString())}toDate(){let e=new Date(this.isoString);if(e.toString()==="Invalid Date")throw new RangeError("Fauna Date could not be converted to Javascript Date");return e}toString(){return`TimeStub("${this.isoString}")`}},b=class{dateString;constructor(e){this.dateString=e}static from(e){if(typeof e!="string")throw new TypeError(`Expected string but received ${typeof e}: ${e}`);let t=ge.exec(e);if(t===null)throw new RangeError(`Expected a plain date string but received '${e}'`);return new b(t[0])}static fromDate(e){let t=e.toISOString(),n=Te.exec(t);if(n===null)throw new y(`Failed to parse date '${e}'`);return new b(n[0])}toDate(){let e=new Date(this.dateString+"T00:00:00Z");if(e.toString()==="Invalid Date")throw new RangeError("Fauna Date could not be converted to Javascript Date");return e}toString(){return`DateStub("${this.dateString}")`}};var R=class{coll;id;constructor({coll:e,id:t}){this.id=t,typeof e=="string"?this.coll=new P(e):this.coll=e}},v=class extends R{ts;constructor(e){let{coll:t,id:n,ts:s,...o}=e;super({coll:t,id:n}),this.ts=s,Object.assign(this,o)}toObject(){return{...this}}},$=class{coll;name;constructor({coll:e,name:t}){this.name=t,typeof e=="string"?this.coll=new P(e):this.coll=e}},G=class extends ${ts;data;constructor(e){let{coll:t,name:n,ts:s,data:o,...i}=e;super({coll:t,name:n}),this.ts=s,this.data=o||{},Object.assign(this,i)}toObject(){return{...this}}},P=class{name;constructor(e){this.name=e}},z=class{ref;cause;constructor(e,t){this.ref=e,this.cause=t}};var x=class{data;after;constructor({data:e,after:t}){this.data=e,this.after=t}},C=class{after;constructor(e){this.after=e}},S=class{#e;constructor(e,t){if(t instanceof Function)this.#e=Ve(e,t);else if(t instanceof x||t instanceof C)this.#e=be(e,t);else throw new TypeError(`Expected 'Page<T> | EmbeddedSet | (() => Promise<T | Page<T> | EmbeddedSet>)', but received ${JSON.stringify(t)}`)}static fromQuery(e,t){return new S(e,async()=>(await e.query(t)).data)}static fromPageable(e,t){return new S(e,t)}flatten(){return new ue(this)}async next(){return this.#e.next()}async return(){return this.#e.return()}async throw(e){return this.#e.throw(e)}[Symbol.asyncIterator](){return this}},ue=class{#e;constructor(e){this.#e=ke(e)}async next(){return this.#e.next()}async return(){return this.#e.return()}async throw(e){return this.#e.throw(e)}[Symbol.asyncIterator](){return this}};async function*be(r,e){let t=e;for(t instanceof x&&(yield t.data);t.after;){let n=de`Set.paginate(${t.after})`;t=(await r.query(n)).data,yield t.data}}async function*Ve(r,e){let t=await e();if(t instanceof x||t instanceof C){for await(let n of be(r,t))yield n;return}yield[t]}async function*ke(r){for await(let e of r)for(let t of e)yield t}var H=class{static encode(e){return re(e)}static decode(e){return JSON.parse(e,(t,n)=>{if(n==null)return null;if(n["@mod"])return new P(n["@mod"]);if(n["@doc"]){if(typeof n["@doc"]=="string"){let[o,i]=n["@doc"].split(":");return new R({coll:o,id:i})}let s=n["@doc"];return s.id?new v(s):new G(s)}else if(n["@ref"]){let s=n["@ref"],o;return s.id?o=new R(s):o=new $(s),"exists"in s&&s.exists===!1?new z(o,s.cause):o}else{if(n["@set"])return typeof n["@set"]=="string"?new C(n["@set"]):new x(n["@set"]);if(n["@int"])return Number(n["@int"]);if(n["@long"])return BigInt(n["@long"]);if(n["@double"])return Number(n["@double"]);if(n["@date"])return b.from(n["@date"]);if(n["@time"])return h.from(n["@time"]);if(n["@object"])return n["@object"]}return n})}},xe=BigInt("-9223372036854775808"),Se=BigInt("9223372036854775807"),c={bigint:r=>{if(r<xe||r>Se)throw new RangeError("Precision loss when converting BigInt to Fauna type");return{"@long":r.toString()}},number:r=>{if(r===Number.POSITIVE_INFINITY||r===Number.NEGATIVE_INFINITY)throw new RangeError(`Cannot convert ${r} to a Fauna type.`);return`${r}`.includes(".")?{"@double":r.toString()}:r>=-(2**31)&&r<=2**31-1?{"@int":r.toString()}:Number.isSafeInteger(r)?{"@long":r.toString()}:{"@double":r.toString()}},string:r=>r,object:r=>{let e=!1,t={};for(let n in r)n.startsWith("@")&&(e=!0),r[n]!==void 0&&(t[n]=re(r[n]));return e?{"@object":t}:t},array:r=>{let e=[];for(let t in r)e.push(re(r[t]));return e},date:r=>({"@time":r.toISOString()}),faunadate:r=>({"@date":r.dateString}),faunatime:r=>({"@time":r.isoString}),module:r=>({"@mod":r.name}),documentReference:r=>({"@ref":{id:r.id,coll:{"@mod":r.coll.name}}}),document:r=>({"@ref":{id:r.id,coll:{"@mod":r.coll.name}}}),namedDocumentReference:r=>({"@ref":{name:r.name,coll:{"@mod":r.coll.name}}}),namedDocument:r=>({"@ref":{name:r.name,coll:{"@mod":r.coll.name}}}),set:r=>{throw new y("Page could not be encoded. Fauna does not accept encoded Set values, yet. Use Page.data and Page.after as arguments, instead.")}},re=r=>{if(r===void 0)throw new TypeError("Passing undefined as a QueryValue is not supported");switch(typeof r){case"bigint":return c.bigint(r);case"string":return c.string(r);case"number":return c.number(r);case"boolean":return r;case"object":return r==null?null:Array.isArray(r)?c.array(r):r instanceof Date?c.date(r):r instanceof b?c.faunadate(r):r instanceof h?c.faunatime(r):r instanceof P?c.module(r):r instanceof v?c.document(r):r instanceof R?c.documentReference(r):r instanceof G?c.namedDocument(r):r instanceof $?c.namedDocumentReference(r):r instanceof z?re(r.ref):r instanceof x||r instanceof C?c.set(r):c.object(r)}};function de(r,...e){return new j(r,...e)}var j=class{#e;#r;constructor(e,...t){if(e.length===0||e.length!==t.length+1)throw new Error("invalid query constructed");this.#e=e,this.#r=t}toQuery(e={}){return{...this.#t(e),...e}}#t(e){if(this.#e.length===1)return{query:{fql:[this.#e[0]]},arguments:{}};let t={};return{query:{fql:this.#e.flatMap((s,o)=>{if(o===this.#e.length-1)return s===""?[]:[s];let i=this.#r[o],d;if(i instanceof j){let u=i.toQuery(e);d=u.query,t={...t,...u.arguments}}else d={value:H.encode(i)};return[s,d].filter(u=>u!=="")})},arguments:t}}};var we=r=>r instanceof Object&&"data"in r,Qe=r=>r instanceof Object&&"error"in r&&r.error instanceof Object&&"code"in r.error&&"message"in r.error;var te=class{#e;constructor({url:e}){this.#e=new URL("/query/1",e).toString()}async request({data:e,headers:t,method:n,client_timeout_ms:s}){let o=new AbortController,i=o.signal;setTimeout(()=>o.abort(),s);let d=await fetch(this.#e,{method:n,headers:{...t,"Content-Type":"application/json"},body:JSON.stringify(e),signal:i}).catch(f=>{throw new p("The network connection encountered a problem.",{cause:f})}),u=d.status,l={};d.headers.forEach((f,w)=>l[w]=f);let m=await d.text();return{status:u,body:m,headers:l}}close(){}};var M;try{M=pe("node:http2")}catch{M=void 0}var q,K,Y,B,ne,se,Ee,L=class{constructor({http2_session_idle_ms:e,url:t}){U(this,B);U(this,se);U(this,K,void 0);U(this,Y,void 0);if(M===void 0)throw new Error("Your platform does not support Node's http2 library");ae(this,K,e),ae(this,Y,t)}async request({client_timeout_ms:e,data:t,headers:n,method:s}){let o,i=new Promise((d,u)=>{let l=m=>{let f=Number(m[M.constants.HTTP2_HEADER_STATUS]),w="";o.on("data",_e=>{w+=_e}),o.on("end",()=>{d({status:f,body:w,headers:m})})};try{let m={...n,[M.constants.HTTP2_HEADER_PATH]:"/query/1",[M.constants.HTTP2_HEADER_METHOD]:s};o=W(this,se,Ee).call(this).request(m).setEncoding("utf8").on("error",w=>{u(w)}).on("response",l),o.write(JSON.stringify(t),"utf8"),o.setTimeout(e,()=>{o.destroy(new Error("Client timeout"))}),o.end()}catch(m){u(m)}});try{return await i}catch(d){throw new p("The network connection encountered a problem.",{cause:d})}}close(){let e=g(L,q).get(this.sessionKey);if(!!e&&(e.refs.delete(this),e.refs.size===0)){let t=e.session;t&&!t.closed&&t.close(),e.session=null}}isClosed(){return(g(L,q).get(this.sessionKey)?.refs.size??0)===0}get sessionKey(){return`${g(this,Y)}|${g(this,K)}`}},J=L;q=new WeakMap,K=new WeakMap,Y=new WeakMap,B=new WeakSet,ne=function(){let e=g(L,q).get(this.sessionKey);if(!e)return;e.refs.clear();let t=e.session;t&&!t.closed&&t.close(),e.session=null},se=new WeakSet,Ee=function(){let e=g(L,q).get(this.sessionKey);if(e||(e={session:null,refs:new Set},g(L,q).set(this.sessionKey,e)),e.session===null||e.session.closed){let t=g(this,K),n=g(this,Y),s=M.connect(n).once("error",()=>W(this,B,ne).call(this)).once("goaway",()=>W(this,B,ne).call(this));s.setTimeout(t,()=>{W(this,B,ne).call(this)}),e.session=s}return e.refs.add(this),e.session},U(J,q,new Map);var le=r=>Fe()?new J(r):new te(r),me=r=>r instanceof Object&&"body"in r&&"headers"in r&&"status"in r,Fe=()=>{if(typeof process<"u"&&process.release?.name==="node")try{return pe("node:http2"),!0}catch{return!1}return!1};var Oe={client_timeout_buffer_ms:5e3,endpoint:ie.default,format:"tagged",http2_session_idle_ms:500,max_conns:10,query_timeout_ms:5e3},ye=class{#e;#r;#t;#n=!1;constructor(e,t){this.#e={...Oe,...e,secret:this.#o(e)},this.#u(),t?this.#r=t:this.#r=le({url:this.#e.endpoint.toString(),http2_session_idle_ms:this.#e.http2_session_idle_ms})}get lastTxnTs(){return this.#t}set lastTxnTs(e){this.#t=this.#t?Math.max(e,this.#t):e}get clientConfiguration(){let{secret:e,...t}=this.#e;return t}close(){if(this.#n)throw new _("Your client is closed. You cannot close it again.");this.#r.close(),this.#n=!0}paginate(e){return e instanceof j?S.fromQuery(this,e):S.fromPageable(this,e)}async query(e,t){if(this.#n)throw new _("Your client is closed. No further requests can be issued.");return this.#i(e.toQuery(t))}#s(e){if(e instanceof y||e instanceof p||e instanceof T||e instanceof a)return e;if(me(e)){if(Qe(e.body)){let t=e.body,n=e.status;return this.#a(t,n)}return new T({message:`Response is in an unkown format: ${e.body}`,httpStatus:e.status})}return new y("A client level error occurred. Fauna was not called.",{cause:e})}#o(e){let t;typeof process=="object"&&(t=process.env.FAUNA_SECRET);let n=e?.secret||t;if(n===void 0)throw new Error("You must provide a secret to the driver. Set it in an environmental variable named FAUNA_SECRET or pass it to the Client constructor.");return n}#a(e,t){switch(t){case 400:return Ne.includes(e.error.code)?new Q(e,t):e.error.code==="invalid_request"?new E(e,t):e.error.code==="abort"&&e.error.abort!==void 0?new X(e,t):new I(e,t);case 401:return new V(e,t);case 403:return new k(e,t);case 409:return new Z(e,t);case 429:return new F(e,t);case 440:return new O(e,t);case 500:return new N(e,t);case 503:return new A(e,t);default:return new a(e,t)}}async#i(e){try{let t={Authorization:`Bearer ${this.#e.secret}`};this.#c({...this.clientConfiguration,...e},t);let n={...this.#e,...e},s=n.format==="tagged"||e.format==="tagged",o=s?H.encode(e.arguments):e.arguments,i={query:e.query,arguments:o},d=n.query_timeout_ms+this.#e.client_timeout_buffer_ms,u=await this.#r.request({data:i,headers:t,method:"POST",client_timeout_ms:d}),l;try{if(l={...u,body:s?H.decode(u.body):JSON.parse(u.body)},l.body.query_tags){let f=l.body.query_tags.split(",").map(w=>w.split("="));l.body.query_tags=Object.fromEntries(f)}}catch(f){throw new T({message:`Error parsing response as JSON: ${f}`,httpStatus:u.status})}if(!we(l.body))throw this.#s(l);let m=l.body.txn_ts;return(this.#t===void 0&&m!==void 0||m!==void 0&&this.#t!==void 0&&this.#t<m)&&(this.#t=m),l.body}catch(t){throw this.#s(t)}}#c(e,t){for(let n of Object.entries(e))if(["format","query_timeout_ms","linearized","max_contention_retries","traceparent","typecheck","query_tags"].includes(n[0])){let s,o=`x-${n[0].replaceAll("_","-")}`;n[0]==="query_tags"?s=Object.entries(n[1]).map(i=>i.join("=")).join(","):typeof n[1]=="string"?s=n[1]:s=String(n[1]),n[0]==="traceparent"&&(o=n[0]),t[o]=s}t["x-last-txn-ts"]===void 0&&this.#t!==void 0&&(t["x-last-txn-ts"]=this.#t)}#u(){let e=this.#e;if(["client_timeout_buffer_ms","endpoint","format","http2_session_idle_ms","max_conns","query_timeout_ms"].forEach(n=>{if(e[n]===void 0)throw new TypeError(`ClientConfiguration option '${n}' must be defined.`)}),e.client_timeout_buffer_ms<=0)throw new RangeError("'client_timeout_buffer_ms' must be greater than zero.");if(e.query_timeout_ms<=0)throw new RangeError("'query_timeout_ms' must be greater than zero.")}},Ne=["invalid_function_definition","invalid_identifier","invalid_query","invalid_syntax","invalid_type"];export{X as AbortError,V as AuthenticationError,k as AuthorizationError,ye as Client,_ as ClientClosedError,y as ClientError,Z as ContendedTransactionError,b as DateStub,v as Document,R as DocumentReference,C as EmbeddedSet,te as FetchClient,E as InvalidRequestError,Se as LONG_MAX,xe as LONG_MIN,P as Module,G as NamedDocument,$ as NamedDocumentReference,p as NetworkError,J as NodeHTTP2Client,z as NullDocument,x as Page,T as ProtocolError,Q as QueryCheckError,I as QueryRuntimeError,O as QueryTimeoutError,a as ServiceError,N as ServiceInternalError,A as ServiceTimeoutError,S as SetIterator,H as TaggedTypeFormat,F as ThrottlingError,h as TimeStub,ie as endpoints,de as fql,le as getDefaultHTTPClient,me as isHTTPResponse}; | ||
var be=(r=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(r,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):r)(function(r){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+r+'" is not supported')});var de=(r,e,t)=>{if(!e.has(r))throw TypeError("Cannot "+t)};var u=(r,e,t)=>(de(r,e,"read from private field"),t?t.call(r):e.get(r)),g=(r,e,t)=>{if(e.has(r))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(r):e.set(r,t)},I=(r,e,t,n)=>(de(r,e,"write to private field"),n?n.call(r,t):e.set(r,t),t),le=(r,e,t,n)=>({set _(s){I(r,e,s,t)},get _(){return u(r,e,n)}}),k=(r,e,t)=>(de(r,e,"access private method"),t);var me={default:new URL("https://db.fauna.com"),local:new URL("http://localhost:8443"),localhost:new URL("http://localhost:8443")};var V=class extends Error{constructor(...e){super(...e)}},i=class extends V{httpStatus;code;queryInfo;constraint_failures;constructor(e,t){super(e.error.message),Error.captureStackTrace&&Error.captureStackTrace(this,i),this.name="ServiceError",this.code=e.error.code,this.httpStatus=t;let n={txn_ts:e.txn_ts,summary:e.summary,query_tags:e.query_tags,stats:e.stats};this.queryInfo=n,this.constraint_failures=e.error.constraint_failures}},F=class extends i{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,F),this.name="QueryRuntimeError"}},Q=class extends i{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,Q),this.name="QueryCheckError"}},R=class extends i{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,R),this.name="InvalidRequestError"}},X=class extends i{abort;constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,Q),this.name="AbortError",this.abort=e.error.abort}},O=class extends i{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,O),this.name="AuthenticationError"}},N=class extends i{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,N),this.name="AuthorizationError"}},Z=class extends i{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,R),this.name="ContendedTransactionError"}},A=class extends i{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,A),this.name="ThrottlingError"}},$=class extends i{stats;constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,$),this.name="QueryTimeoutError",this.stats=e.stats}},j=class extends i{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,j),this.name="ServiceInternalError"}},M=class extends i{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,M),this.name="ServiceTimeoutError"}},p=class extends V{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,p),this.name="ClientError"}},_=class extends V{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,_),this.name="ClientClosedError"}},f=class extends V{constructor(e,t){super(e,t),Error.captureStackTrace&&Error.captureStackTrace(this,f),this.name="NetworkError"}},h=class extends V{httpStatus;constructor(e){super(e.message),Error.captureStackTrace&&Error.captureStackTrace(this,h),this.name="ProtocolError",this.httpStatus=e.httpStatus}};var Ie=/(?:\d{4}|[\u2212-]\d{4,}|\+\d{5,})/,ke=/(?:0[1-9]|1[0-2])/,Ve=/(?:0[1-9]|[12]\d|3[01])/,xe=/(?:[01][0-9]|2[0-3])/,ee=/(?:[0-5][0-9])/,Fe=/(?:\.\d+)/,ye=new RegExp(`(${Ie.source}-(${ke.source})-(${Ve.source}))`),Oe=new RegExp(`(${xe.source}:${ee.source}:${ee.source}${Fe.source}?)`),Ne=new RegExp(`([zZ]|[+\u2212-]${xe.source}(?::?${ee.source}|:${ee.source}:${ee.source}))`),Se=new RegExp(`^${ye.source}$`),we=new RegExp(`^${ye.source}`),Ee=new RegExp(`^${ye.source}T${Oe.source}${Ne.source}$`);var b=class{isoString;constructor(e){this.isoString=e}static from(e){if(typeof e!="string")throw new TypeError(`Expected string but received ${typeof e}: ${e}`);if(Ee.exec(e)===null)throw new RangeError(`(regex) Expected an ISO date string but received '${e}'`);return new b(e)}static fromDate(e){return new b(e.toISOString())}toDate(){let e=new Date(this.isoString);if(e.toString()==="Invalid Date")throw new RangeError("Fauna Date could not be converted to Javascript Date");return e}toString(){return`TimeStub("${this.isoString}")`}},x=class{dateString;constructor(e){this.dateString=e}static from(e){if(typeof e!="string")throw new TypeError(`Expected string but received ${typeof e}: ${e}`);let t=Se.exec(e);if(t===null)throw new RangeError(`Expected a plain date string but received '${e}'`);return new x(t[0])}static fromDate(e){let t=e.toISOString(),n=we.exec(t);if(n===null)throw new p(`Failed to parse date '${e}'`);return new x(n[0])}toDate(){let e=new Date(this.dateString+"T00:00:00Z");if(e.toString()==="Invalid Date")throw new RangeError("Fauna Date could not be converted to Javascript Date");return e}toString(){return`DateStub("${this.dateString}")`}};var P=class{coll;id;constructor({coll:e,id:t}){this.id=t,typeof e=="string"?this.coll=new C(e):this.coll=e}},Y=class extends P{ts;constructor(e){let{coll:t,id:n,ts:s,...o}=e;super({coll:t,id:n}),this.ts=s,Object.assign(this,o)}toObject(){return{...this}}},L=class{coll;name;constructor({coll:e,name:t}){this.name=t,typeof e=="string"?this.coll=new C(e):this.coll=e}},J=class extends L{ts;data;constructor(e){let{coll:t,name:n,ts:s,data:o,...c}=e;super({coll:t,name:n}),this.ts=s,this.data=o||{},Object.assign(this,c)}toObject(){return{...this}}},C=class{name;constructor(e){this.name=e}},B=class{ref;cause;constructor(e,t){this.ref=e,this.cause=t}};var S=class{data;after;constructor({data:e,after:t}){this.data=e,this.after=t}},H=class{after;constructor(e){this.after=e}},w=class{#e;constructor(e,t){if(t instanceof Function)this.#e=$e(e,t);else if(t instanceof S||t instanceof H)this.#e=Qe(e,t);else throw new TypeError(`Expected 'Page<T> | EmbeddedSet | (() => Promise<T | Page<T> | EmbeddedSet>)', but received ${JSON.stringify(t)}`)}static fromQuery(e,t){return new w(e,async()=>(await e.query(t)).data)}static fromPageable(e,t){return new w(e,t)}flatten(){return new pe(this)}async next(){return this.#e.next()}async return(){return this.#e.return()}async throw(e){return this.#e.throw(e)}[Symbol.asyncIterator](){return this}},pe=class{#e;constructor(e){this.#e=je(e)}async next(){return this.#e.next()}async return(){return this.#e.return()}async throw(e){return this.#e.throw(e)}[Symbol.asyncIterator](){return this}};async function*Qe(r,e){let t=e;for(t instanceof S&&(yield t.data);t.after;){let n=fe`Set.paginate(${t.after})`;t=(await r.query(n)).data,yield t.data}}async function*$e(r,e){let t=await e();if(t instanceof S||t instanceof H){for await(let n of Qe(r,t))yield n;return}yield[t]}async function*je(r){for await(let e of r)for(let t of e)yield t}var q=class{static encode(e){return se(e)}static decode(e){return JSON.parse(e,(t,n)=>{if(n==null)return null;if(n["@mod"])return new C(n["@mod"]);if(n["@doc"]){if(typeof n["@doc"]=="string"){let[o,c]=n["@doc"].split(":");return new P({coll:o,id:c})}let s=n["@doc"];return s.id?new Y(s):new J(s)}else if(n["@ref"]){let s=n["@ref"],o;return s.id?o=new P(s):o=new L(s),"exists"in s&&s.exists===!1?new B(o,s.cause):o}else{if(n["@set"])return typeof n["@set"]=="string"?new H(n["@set"]):new S(n["@set"]);if(n["@int"])return Number(n["@int"]);if(n["@long"])return BigInt(n["@long"]);if(n["@double"])return Number(n["@double"]);if(n["@date"])return x.from(n["@date"]);if(n["@time"])return b.from(n["@time"]);if(n["@object"])return n["@object"]}return n})}},Re=BigInt("-9223372036854775808"),_e=BigInt("9223372036854775807"),l={bigint:r=>{if(r<Re||r>_e)throw new RangeError("Precision loss when converting BigInt to Fauna type");return{"@long":r.toString()}},number:r=>{if(r===Number.POSITIVE_INFINITY||r===Number.NEGATIVE_INFINITY)throw new RangeError(`Cannot convert ${r} to a Fauna type.`);return`${r}`.includes(".")?{"@double":r.toString()}:r>=-(2**31)&&r<=2**31-1?{"@int":r.toString()}:Number.isSafeInteger(r)?{"@long":r.toString()}:{"@double":r.toString()}},string:r=>r,object:r=>{let e=!1,t={};for(let n in r)n.startsWith("@")&&(e=!0),r[n]!==void 0&&(t[n]=se(r[n]));return e?{"@object":t}:t},array:r=>{let e=[];for(let t in r)e.push(se(r[t]));return e},date:r=>({"@time":r.toISOString()}),faunadate:r=>({"@date":r.dateString}),faunatime:r=>({"@time":r.isoString}),module:r=>({"@mod":r.name}),documentReference:r=>({"@ref":{id:r.id,coll:{"@mod":r.coll.name}}}),document:r=>({"@ref":{id:r.id,coll:{"@mod":r.coll.name}}}),namedDocumentReference:r=>({"@ref":{name:r.name,coll:{"@mod":r.coll.name}}}),namedDocument:r=>({"@ref":{name:r.name,coll:{"@mod":r.coll.name}}}),set:r=>{throw new p("Page could not be encoded. Fauna does not accept encoded Set values, yet. Use Page.data and Page.after as arguments, instead.")}},se=r=>{if(r===void 0)throw new TypeError("Passing undefined as a QueryValue is not supported");switch(typeof r){case"bigint":return l.bigint(r);case"string":return l.string(r);case"number":return l.number(r);case"boolean":return r;case"object":return r==null?null:Array.isArray(r)?l.array(r):r instanceof Date?l.date(r):r instanceof x?l.faunadate(r):r instanceof b?l.faunatime(r):r instanceof C?l.module(r):r instanceof Y?l.document(r):r instanceof P?l.documentReference(r):r instanceof J?l.namedDocument(r):r instanceof L?l.namedDocumentReference(r):r instanceof B?se(r.ref):r instanceof S||r instanceof H?l.set(r):l.object(r)}};function fe(r,...e){return new U(r,...e)}var U=class{#e;#r;constructor(e,...t){if(e.length===0||e.length!==t.length+1)throw new Error("invalid query constructed");this.#e=e,this.#r=t}toQuery(e={}){return{...this.#t(e),...e}}#t(e){if(this.#e.length===1)return{query:{fql:[this.#e[0]]},arguments:{}};let t={};return{query:{fql:this.#e.flatMap((s,o)=>{if(o===this.#e.length-1)return s===""?[]:[s];let c=this.#r[o],d;if(c instanceof U){let y=c.toQuery(e);d=y.query,t={...t,...y.arguments}}else d={value:q.encode(c)};return[s,d].filter(y=>y!=="")})},arguments:t}}};var Pe=r=>r instanceof Object&&"data"in r,Ce=r=>r instanceof Object&&"error"in r&&r.error instanceof Object&&"code"in r.error&&"message"in r.error;var te=class{#e;constructor({url:e}){this.#e=new URL("/query/1",e).toString()}async request({data:e,headers:t,method:n,client_timeout_ms:s}){let o=await fetch(this.#e,{method:n,headers:{...t,"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(s)}).catch(a=>{throw new f("The network connection encountered a problem.",{cause:a})}),c=o.status,d={};o.headers.forEach((a,T)=>d[T]=a);let y=await o.text();return{status:c,body:y,headers:d}}close(){}};var v;try{v=be("node:http2")}catch{v=void 0}var K,re,ne,D,m,ae,He,W,oe,ie,qe,ue,De,G=class{constructor({http2_session_idle_ms:e,url:t}){g(this,W);g(this,ie);g(this,ue);g(this,re,void 0);g(this,ne,void 0);g(this,D,0);g(this,m,void 0);if(v===void 0)throw new Error("Your platform does not support Node's http2 library");I(this,re,e),I(this,ne,t),I(this,m,null)}static getClient(e){var s;let t=k(s=G,ae,He).call(s,e);u(G,K).has(t)||u(G,K).set(t,new G(e));let n=u(G,K).get(t);return le(n,D)._++,n}async request(e){let t=0,n;do try{return await k(this,ue,De).call(this,e)}catch(s){if(s?.code!=="ERR_HTTP2_GOAWAY_SESSION")throw new f("The network connection encountered a problem.",{cause:s});n=s,t++}while(t<3);throw new f("The network connection encountered a problem.",{cause:n})}close(){this.isClosed()||(le(this,D)._--,u(this,D)===0&&u(this,m)&&!u(this,m).closed&&u(this,m).close())}isClosed(){return u(this,D)===0}},z=G;K=new WeakMap,re=new WeakMap,ne=new WeakMap,D=new WeakMap,m=new WeakMap,ae=new WeakSet,He=function({http2_session_idle_ms:e,url:t}){return`${t}|${e}`},W=new WeakSet,oe=function(){I(this,D,0),u(this,m)&&!u(this,m).closed&&u(this,m).close()},ie=new WeakSet,qe=function(){if(!u(this,m)||u(this,m).closed){let e=v.connect(u(this,ne)).once("error",()=>k(this,W,oe).call(this)).once("goaway",()=>k(this,W,oe).call(this));e.setTimeout(u(this,re),()=>{k(this,W,oe).call(this)}),I(this,m,e)}return u(this,m)},ue=new WeakSet,De=function({client_timeout_ms:e,data:t,headers:n,method:s}){return new Promise((o,c)=>{let d,y=a=>{let T=Number(a[v.constants.HTTP2_HEADER_STATUS]),E="";d.on("data",ce=>{E+=ce}),d.on("end",()=>{o({status:T,body:E,headers:a})})};try{let a={...n,[v.constants.HTTP2_HEADER_PATH]:"/query/1",[v.constants.HTTP2_HEADER_METHOD]:s};d=k(this,ie,qe).call(this).request(a).setEncoding("utf8").on("error",E=>{c(E)}).on("response",y),d.write(JSON.stringify(t),"utf8"),d.setTimeout(e,()=>{d.destroy(new Error("Client timeout"))}),d.end()}catch(a){c(a)}})},g(z,ae),g(z,K,new Map);var ge=r=>Me()?z.getClient(r):new te(r),Te=r=>r instanceof Object&&"body"in r&&"headers"in r&&"status"in r,Me=()=>{if(typeof process<"u"&&process.release?.name==="node")try{return be("node:http2"),!0}catch{return!1}return!1};var Le={client_timeout_buffer_ms:5e3,endpoint:me.default,format:"tagged",http2_session_idle_ms:5e3,max_conns:10,query_timeout_ms:5e3},he=class{#e;#r;#t;#n=!1;constructor(e,t){this.#e={...Le,...e,secret:this.#o(e)},this.#c(),t?this.#r=t:this.#r=ge({url:this.#e.endpoint.toString(),http2_session_idle_ms:this.#e.http2_session_idle_ms})}get lastTxnTs(){return this.#t}set lastTxnTs(e){this.#t=this.#t?Math.max(e,this.#t):e}get clientConfiguration(){let{secret:e,...t}=this.#e;return t}close(){if(this.#n)throw new _("Your client is closed. You cannot close it again.");this.#r.close(),this.#n=!0}paginate(e){return e instanceof U?w.fromQuery(this,e):w.fromPageable(this,e)}async query(e,t){if(this.#n)throw new _("Your client is closed. No further requests can be issued.");return this.#i(e.toQuery(t))}#s(e){if(e instanceof p||e instanceof f||e instanceof h||e instanceof i)return e;if(Te(e)){if(Ce(e.body)){let t=e.body,n=e.status;return this.#a(t,n)}return new h({message:`Response is in an unkown format: ${e.body}`,httpStatus:e.status})}return new p("A client level error occurred. Fauna was not called.",{cause:e})}#o(e){let t;typeof process=="object"&&(t=process.env.FAUNA_SECRET);let n=e?.secret||t;if(n===void 0)throw new Error("You must provide a secret to the driver. Set it in an environmental variable named FAUNA_SECRET or pass it to the Client constructor.");return n}#a(e,t){switch(t){case 400:return Ue.includes(e.error.code)?new Q(e,t):e.error.code==="invalid_request"?new R(e,t):e.error.code==="abort"&&e.error.abort!==void 0?new X(e,t):new F(e,t);case 401:return new O(e,t);case 403:return new N(e,t);case 409:return new Z(e,t);case 429:return new A(e,t);case 440:return new $(e,t);case 500:return new j(e,t);case 503:return new M(e,t);default:return new i(e,t)}}async#i(e){try{let t={Authorization:`Bearer ${this.#e.secret}`};this.#u({...this.clientConfiguration,...e},t);let n={...this.#e,...e},s=n.format==="tagged"||e.format==="tagged",o=s?q.encode(e.arguments):e.arguments,c={query:e.query,arguments:o},d=n.query_timeout_ms+this.#e.client_timeout_buffer_ms,y=await this.#r.request({data:c,headers:t,method:"POST",client_timeout_ms:d}),a;try{if(a={...y,body:s?q.decode(y.body):JSON.parse(y.body)},a.body.query_tags){let E=a.body.query_tags.split(",").map(ce=>ce.split("="));a.body.query_tags=Object.fromEntries(E)}}catch(E){throw new h({message:`Error parsing response as JSON: ${E}`,httpStatus:y.status})}if(!Pe(a.body))throw this.#s(a);let T=a.body.txn_ts;return(this.#t===void 0&&T!==void 0||T!==void 0&&this.#t!==void 0&&this.#t<T)&&(this.#t=T),a.body}catch(t){throw this.#s(t)}}#u(e,t){for(let n of Object.entries(e))if(["format","query_timeout_ms","linearized","max_contention_retries","traceparent","typecheck","query_tags"].includes(n[0])){let s,o=`x-${n[0].replaceAll("_","-")}`;n[0]==="query_tags"?s=Object.entries(n[1]).map(c=>c.join("=")).join(","):typeof n[1]=="string"?s=n[1]:s=String(n[1]),n[0]==="traceparent"&&(o=n[0]),t[o]=s}t["x-last-txn-ts"]===void 0&&this.#t!==void 0&&(t["x-last-txn-ts"]=this.#t)}#c(){let e=this.#e;if(["client_timeout_buffer_ms","endpoint","format","http2_session_idle_ms","max_conns","query_timeout_ms"].forEach(n=>{if(e[n]===void 0)throw new TypeError(`ClientConfiguration option '${n}' must be defined.`)}),e.client_timeout_buffer_ms<=0)throw new RangeError("'client_timeout_buffer_ms' must be greater than zero.");if(e.query_timeout_ms<=0)throw new RangeError("'query_timeout_ms' must be greater than zero.")}},Ue=["invalid_function_definition","invalid_identifier","invalid_query","invalid_syntax","invalid_type"];export{X as AbortError,O as AuthenticationError,N as AuthorizationError,he as Client,_ as ClientClosedError,p as ClientError,Z as ContendedTransactionError,x as DateStub,Y as Document,P as DocumentReference,H as EmbeddedSet,te as FetchClient,R as InvalidRequestError,_e as LONG_MAX,Re as LONG_MIN,C as Module,J as NamedDocument,L as NamedDocumentReference,f as NetworkError,z as NodeHTTP2Client,B as NullDocument,S as Page,h as ProtocolError,Q as QueryCheckError,F as QueryRuntimeError,$ as QueryTimeoutError,i as ServiceError,j as ServiceInternalError,M as ServiceTimeoutError,w as SetIterator,q as TaggedTypeFormat,A as ThrottlingError,b as TimeStub,me as endpoints,fe as fql,ge as getDefaultHTTPClient,Te as isHTTPResponse}; | ||
//# sourceMappingURL=index.js.map |
@@ -38,3 +38,3 @@ import { ValueFormat } from "./wire-protocol"; | ||
* Time in milliseconds the client will keep an HTTP2 session open after all | ||
* requests are completed. The default is 500 ms. | ||
* requests are completed. The default is 5000 ms. | ||
*/ | ||
@@ -41,0 +41,0 @@ http2_session_idle_ms: number; |
@@ -7,5 +7,12 @@ import { HTTPClient, HTTPClientOptions, HTTPRequest, HTTPResponse } from "./http-client"; | ||
#private; | ||
constructor({ http2_session_idle_ms, url }: HTTPClientOptions); | ||
private constructor(); | ||
/** | ||
* Gets a {@link NodeHTTP2Client} matching the {@link HTTTPClientOptions} | ||
* @param httpClientOptions - the {@link HTTTPClientOptions} | ||
* @returns a {@link NodeHTTP2Client} matching the {@link HTTTPClientOptions} | ||
*/ | ||
static getClient(httpClientOptions: HTTPClientOptions): NodeHTTP2Client; | ||
/** {@inheritDoc HTTPClient.request} */ | ||
request({ client_timeout_ms, data: requestData, headers: requestHeaders, method, }: HTTPRequest): Promise<HTTPResponse>; | ||
request(req: HTTPRequest): Promise<HTTPResponse>; | ||
/** {@inheritDoc HTTPClient.close} */ | ||
close(): void; | ||
@@ -16,6 +23,2 @@ /** | ||
isClosed(): boolean; | ||
/** | ||
* Creates a key common the client that should share the same session | ||
*/ | ||
get sessionKey(): string; | ||
} |
@@ -37,2 +37,10 @@ "use strict"; | ||
}; | ||
var __privateWrapper = (obj, member, setter, getter) => ({ | ||
set _(value) { | ||
__privateSet(obj, member, value, setter); | ||
}, | ||
get _() { | ||
return __privateGet(obj, member, getter); | ||
} | ||
}); | ||
var __privateMethod = (obj, member, method) => { | ||
@@ -774,5 +782,2 @@ __accessCheck(obj, member, "access private method"); | ||
}) { | ||
const controller = new AbortController(); | ||
const signal = controller.signal; | ||
setTimeout(() => controller.abort(), client_timeout_ms); | ||
const response = await fetch(this.#url, { | ||
@@ -782,3 +787,3 @@ method, | ||
body: JSON.stringify(data), | ||
signal | ||
signal: AbortSignal.timeout(client_timeout_ms) | ||
}).catch((error) => { | ||
@@ -810,3 +815,3 @@ throw new NetworkError("The network connection encountered a problem.", { | ||
} | ||
var _sessionMap, _http2_session_idle_ms, _url, _closeForAll, closeForAll_fn, _connect, connect_fn; | ||
var _clients, _http2_session_idle_ms, _url, _numberOfUsers, _session, _getClientKey, getClientKey_fn, _closeForAll, closeForAll_fn, _connect, connect_fn, _doRequest, doRequest_fn; | ||
var _NodeHTTP2Client = class { | ||
@@ -816,4 +821,7 @@ constructor({ http2_session_idle_ms, url }) { | ||
__privateAdd(this, _connect); | ||
__privateAdd(this, _doRequest); | ||
__privateAdd(this, _http2_session_idle_ms, void 0); | ||
__privateAdd(this, _url, void 0); | ||
__privateAdd(this, _numberOfUsers, 0); | ||
__privateAdd(this, _session, void 0); | ||
if (http2 === void 0) { | ||
@@ -824,117 +832,131 @@ throw new Error("Your platform does not support Node's http2 library"); | ||
__privateSet(this, _url, url); | ||
__privateSet(this, _session, null); | ||
} | ||
async request({ | ||
client_timeout_ms, | ||
data: requestData, | ||
headers: requestHeaders, | ||
method | ||
}) { | ||
let req; | ||
const requestPromise = new Promise( | ||
(resolvePromise, rejectPromise) => { | ||
const onResponse = (http2ResponseHeaders) => { | ||
const status = Number( | ||
http2ResponseHeaders[http2.constants.HTTP2_HEADER_STATUS] | ||
static getClient(httpClientOptions) { | ||
var _a; | ||
const clientKey = __privateMethod(_a = _NodeHTTP2Client, _getClientKey, getClientKey_fn).call(_a, httpClientOptions); | ||
if (!__privateGet(_NodeHTTP2Client, _clients).has(clientKey)) { | ||
__privateGet(_NodeHTTP2Client, _clients).set( | ||
clientKey, | ||
new _NodeHTTP2Client(httpClientOptions) | ||
); | ||
} | ||
const client = __privateGet(_NodeHTTP2Client, _clients).get(clientKey); | ||
__privateWrapper(client, _numberOfUsers)._++; | ||
return client; | ||
} | ||
async request(req) { | ||
let retryCount = 0; | ||
let memoizedError; | ||
do { | ||
try { | ||
return await __privateMethod(this, _doRequest, doRequest_fn).call(this, req); | ||
} catch (error) { | ||
if (error?.code !== "ERR_HTTP2_GOAWAY_SESSION") { | ||
throw new NetworkError( | ||
"The network connection encountered a problem.", | ||
{ | ||
cause: error | ||
} | ||
); | ||
let responseData = ""; | ||
req.on("data", (chunk) => { | ||
responseData += chunk; | ||
}); | ||
req.on("end", () => { | ||
resolvePromise({ | ||
status, | ||
body: responseData, | ||
headers: http2ResponseHeaders | ||
}); | ||
}); | ||
}; | ||
try { | ||
const httpRequestHeaders = { | ||
...requestHeaders, | ||
[http2.constants.HTTP2_HEADER_PATH]: "/query/1", | ||
[http2.constants.HTTP2_HEADER_METHOD]: method | ||
}; | ||
const session = __privateMethod(this, _connect, connect_fn).call(this); | ||
req = session.request(httpRequestHeaders).setEncoding("utf8").on("error", (error) => { | ||
rejectPromise(error); | ||
}).on("response", onResponse); | ||
req.write(JSON.stringify(requestData), "utf8"); | ||
req.setTimeout(client_timeout_ms, () => { | ||
req.destroy(new Error(`Client timeout`)); | ||
}); | ||
req.end(); | ||
} catch (error) { | ||
rejectPromise(error); | ||
} | ||
memoizedError = error; | ||
retryCount++; | ||
} | ||
); | ||
try { | ||
return await requestPromise; | ||
} catch (error) { | ||
throw new NetworkError("The network connection encountered a problem.", { | ||
cause: error | ||
}); | ||
} | ||
} while (retryCount < 3); | ||
throw new NetworkError("The network connection encountered a problem.", { | ||
cause: memoizedError | ||
}); | ||
} | ||
close() { | ||
const session_rc = __privateGet(_NodeHTTP2Client, _sessionMap).get(this.sessionKey); | ||
if (!session_rc) | ||
if (this.isClosed()) { | ||
return; | ||
session_rc.refs.delete(this); | ||
if (session_rc.refs.size === 0) { | ||
const session = session_rc.session; | ||
if (session && !session.closed) | ||
session.close(); | ||
session_rc.session = null; | ||
} | ||
__privateWrapper(this, _numberOfUsers)._--; | ||
if (__privateGet(this, _numberOfUsers) === 0 && __privateGet(this, _session) && !__privateGet(this, _session).closed) { | ||
__privateGet(this, _session).close(); | ||
} | ||
} | ||
isClosed() { | ||
const session_rc = __privateGet(_NodeHTTP2Client, _sessionMap).get(this.sessionKey); | ||
return (session_rc?.refs.size ?? 0) === 0; | ||
return __privateGet(this, _numberOfUsers) === 0; | ||
} | ||
get sessionKey() { | ||
return `${__privateGet(this, _url)}|${__privateGet(this, _http2_session_idle_ms)}`; | ||
} | ||
}; | ||
var NodeHTTP2Client = _NodeHTTP2Client; | ||
_sessionMap = new WeakMap(); | ||
_clients = new WeakMap(); | ||
_http2_session_idle_ms = new WeakMap(); | ||
_url = new WeakMap(); | ||
_numberOfUsers = new WeakMap(); | ||
_session = new WeakMap(); | ||
_getClientKey = new WeakSet(); | ||
getClientKey_fn = function({ http2_session_idle_ms, url }) { | ||
return `${url}|${http2_session_idle_ms}`; | ||
}; | ||
_closeForAll = new WeakSet(); | ||
closeForAll_fn = function() { | ||
const session_rc = __privateGet(_NodeHTTP2Client, _sessionMap).get(this.sessionKey); | ||
if (!session_rc) | ||
return; | ||
session_rc.refs.clear(); | ||
const session = session_rc.session; | ||
if (session && !session.closed) | ||
session.close(); | ||
session_rc.session = null; | ||
__privateSet(this, _numberOfUsers, 0); | ||
if (__privateGet(this, _session) && !__privateGet(this, _session).closed) { | ||
__privateGet(this, _session).close(); | ||
} | ||
}; | ||
_connect = new WeakSet(); | ||
connect_fn = function() { | ||
let session_rc = __privateGet(_NodeHTTP2Client, _sessionMap).get(this.sessionKey); | ||
if (!session_rc) { | ||
session_rc = { | ||
session: null, | ||
refs: /* @__PURE__ */ new Set() | ||
}; | ||
__privateGet(_NodeHTTP2Client, _sessionMap).set(this.sessionKey, session_rc); | ||
} | ||
if (session_rc.session === null || session_rc.session.closed) { | ||
const http2_session_idle_ms = __privateGet(this, _http2_session_idle_ms); | ||
const url = __privateGet(this, _url); | ||
const new_session = http2.connect(url).once("error", () => __privateMethod(this, _closeForAll, closeForAll_fn).call(this)).once("goaway", () => __privateMethod(this, _closeForAll, closeForAll_fn).call(this)); | ||
new_session.setTimeout(http2_session_idle_ms, () => { | ||
if (!__privateGet(this, _session) || __privateGet(this, _session).closed) { | ||
const new_session = http2.connect(__privateGet(this, _url)).once("error", () => __privateMethod(this, _closeForAll, closeForAll_fn).call(this)).once("goaway", () => __privateMethod(this, _closeForAll, closeForAll_fn).call(this)); | ||
new_session.setTimeout(__privateGet(this, _http2_session_idle_ms), () => { | ||
__privateMethod(this, _closeForAll, closeForAll_fn).call(this); | ||
}); | ||
session_rc.session = new_session; | ||
__privateSet(this, _session, new_session); | ||
} | ||
session_rc.refs.add(this); | ||
return session_rc.session; | ||
return __privateGet(this, _session); | ||
}; | ||
__privateAdd(NodeHTTP2Client, _sessionMap, /* @__PURE__ */ new Map()); | ||
_doRequest = new WeakSet(); | ||
doRequest_fn = function({ | ||
client_timeout_ms, | ||
data: requestData, | ||
headers: requestHeaders, | ||
method | ||
}) { | ||
return new Promise((resolvePromise, rejectPromise) => { | ||
let req; | ||
const onResponse = (http2ResponseHeaders) => { | ||
const status = Number( | ||
http2ResponseHeaders[http2.constants.HTTP2_HEADER_STATUS] | ||
); | ||
let responseData = ""; | ||
req.on("data", (chunk) => { | ||
responseData += chunk; | ||
}); | ||
req.on("end", () => { | ||
resolvePromise({ | ||
status, | ||
body: responseData, | ||
headers: http2ResponseHeaders | ||
}); | ||
}); | ||
}; | ||
try { | ||
const httpRequestHeaders = { | ||
...requestHeaders, | ||
[http2.constants.HTTP2_HEADER_PATH]: "/query/1", | ||
[http2.constants.HTTP2_HEADER_METHOD]: method | ||
}; | ||
const session = __privateMethod(this, _connect, connect_fn).call(this); | ||
req = session.request(httpRequestHeaders).setEncoding("utf8").on("error", (error) => { | ||
rejectPromise(error); | ||
}).on("response", onResponse); | ||
req.write(JSON.stringify(requestData), "utf8"); | ||
req.setTimeout(client_timeout_ms, () => { | ||
req.destroy(new Error(`Client timeout`)); | ||
}); | ||
req.end(); | ||
} catch (error) { | ||
rejectPromise(error); | ||
} | ||
}); | ||
}; | ||
__privateAdd(NodeHTTP2Client, _getClientKey); | ||
__privateAdd(NodeHTTP2Client, _clients, /* @__PURE__ */ new Map()); | ||
// src/http-client/index.ts | ||
var getDefaultHTTPClient = (options) => isNode() ? new NodeHTTP2Client(options) : new FetchClient(options); | ||
var getDefaultHTTPClient = (options) => isNode() ? NodeHTTP2Client.getClient(options) : new FetchClient(options); | ||
var isHTTPResponse = (res) => res instanceof Object && "body" in res && "headers" in res && "status" in res; | ||
@@ -958,3 +980,3 @@ var isNode = () => { | ||
format: "tagged", | ||
http2_session_idle_ms: 500, | ||
http2_session_idle_ms: 5e3, | ||
max_conns: 10, | ||
@@ -961,0 +983,0 @@ query_timeout_ms: 5e3 |
{ | ||
"name": "fauna", | ||
"version": "0.7.0", | ||
"version": "0.7.1", | ||
"description": "A driver to query Fauna databases in browsers, Node.js, and other Javascript runtimes", | ||
@@ -5,0 +5,0 @@ "homepage": "https://fauna.com", |
@@ -42,3 +42,3 @@ import { ValueFormat } from "./wire-protocol"; | ||
* Time in milliseconds the client will keep an HTTP2 session open after all | ||
* requests are completed. The default is 500 ms. | ||
* requests are completed. The default is 5000 ms. | ||
*/ | ||
@@ -45,0 +45,0 @@ http2_session_idle_ms: number; |
@@ -42,3 +42,3 @@ import { ClientConfiguration, endpoints } from "./client-configuration"; | ||
format: "tagged", | ||
http2_session_idle_ms: 500, | ||
http2_session_idle_ms: 5000, | ||
max_conns: 10, | ||
@@ -45,0 +45,0 @@ query_timeout_ms: 5000, |
@@ -29,6 +29,2 @@ /** following reference needed to include types for experimental fetch API in Node */ | ||
}: HTTPRequest): Promise<HTTPResponse> { | ||
const controller = new AbortController(); | ||
const signal = controller.signal; | ||
setTimeout(() => controller.abort(), client_timeout_ms); | ||
const response = await fetch(this.#url, { | ||
@@ -38,3 +34,3 @@ method, | ||
body: JSON.stringify(data), | ||
signal, | ||
signal: AbortSignal.timeout(client_timeout_ms), | ||
}).catch((error) => { | ||
@@ -41,0 +37,0 @@ throw new NetworkError("The network connection encountered a problem.", { |
@@ -11,3 +11,3 @@ import { FetchClient } from "./fetch-client"; | ||
export const getDefaultHTTPClient = (options: HTTPClientOptions): HTTPClient => | ||
isNode() ? new NodeHTTP2Client(options) : new FetchClient(options); | ||
isNode() ? NodeHTTP2Client.getClient(options) : new FetchClient(options); | ||
@@ -14,0 +14,0 @@ export const isHTTPResponse = (res: any): res is HTTPResponse => |
@@ -26,8 +26,10 @@ let http2: any; | ||
export class NodeHTTP2Client implements HTTPClient { | ||
static #sessionMap: Map<string, SessionRC> = new Map(); | ||
static #clients: Map<string, NodeHTTP2Client> = new Map(); | ||
#http2_session_idle_ms: number; | ||
#url: string; | ||
#numberOfUsers = 0; | ||
#session: ClientHttp2Session | null; | ||
constructor({ http2_session_idle_ms, url }: HTTPClientOptions) { | ||
private constructor({ http2_session_idle_ms, url }: HTTPClientOptions) { | ||
if (http2 === undefined) { | ||
@@ -39,92 +41,76 @@ throw new Error("Your platform does not support Node's http2 library"); | ||
this.#url = url; | ||
this.#session = null; | ||
} | ||
/** | ||
* Gets a {@link NodeHTTP2Client} matching the {@link HTTTPClientOptions} | ||
* @param httpClientOptions - the {@link HTTTPClientOptions} | ||
* @returns a {@link NodeHTTP2Client} matching the {@link HTTTPClientOptions} | ||
*/ | ||
static getClient(httpClientOptions: HTTPClientOptions): NodeHTTP2Client { | ||
const clientKey = NodeHTTP2Client.#getClientKey(httpClientOptions); | ||
if (!NodeHTTP2Client.#clients.has(clientKey)) { | ||
NodeHTTP2Client.#clients.set( | ||
clientKey, | ||
new NodeHTTP2Client(httpClientOptions) | ||
); | ||
} | ||
// we know that we have a client here | ||
const client = NodeHTTP2Client.#clients.get(clientKey) as NodeHTTP2Client; | ||
client.#numberOfUsers++; | ||
return client; | ||
} | ||
static #getClientKey({ http2_session_idle_ms, url }: HTTPClientOptions) { | ||
return `${url}|${http2_session_idle_ms}`; | ||
} | ||
/** {@inheritDoc HTTPClient.request} */ | ||
async request({ | ||
client_timeout_ms, | ||
data: requestData, | ||
headers: requestHeaders, | ||
method, | ||
}: HTTPRequest): Promise<HTTPResponse> { | ||
let req: ClientHttp2Stream; | ||
const requestPromise = new Promise<HTTPResponse>( | ||
(resolvePromise, rejectPromise) => { | ||
const onResponse = ( | ||
http2ResponseHeaders: IncomingHttpHeaders & IncomingHttpStatusHeader | ||
) => { | ||
const status = Number( | ||
http2ResponseHeaders[http2.constants.HTTP2_HEADER_STATUS] | ||
async request(req: HTTPRequest): Promise<HTTPResponse> { | ||
let retryCount = 0; | ||
let memoizedError: any; | ||
do { | ||
try { | ||
return await this.#doRequest(req); | ||
} catch (error: any) { | ||
// see https://github.com/nodejs/node/pull/42190/files | ||
// and https://github.com/nodejs/help/issues/2105 | ||
// | ||
// TLDR; In Node, there is a race condition between handling | ||
// GOAWAY and submitting requests - that can cause | ||
// clients that safely handle go away to submit | ||
// requests after a GOAWAY was received anyway. | ||
// | ||
// technical explanation: node HTTP2 request gets put | ||
// on event queue before it is actually executed. In the iterim, | ||
// a GOAWAY can come and cause the request to fail | ||
// with a GOAWAY. | ||
if (error?.code !== "ERR_HTTP2_GOAWAY_SESSION") { | ||
// TODO: be more discernable about error types | ||
throw new NetworkError( | ||
"The network connection encountered a problem.", | ||
{ | ||
cause: error, | ||
} | ||
); | ||
let responseData = ""; | ||
// append response data to the data string every time we receive new | ||
// data chunks in the response | ||
req.on("data", (chunk: any) => { | ||
responseData += chunk; | ||
}); | ||
// Once the response is finished, resolve the promise | ||
req.on("end", () => { | ||
resolvePromise({ | ||
status, | ||
body: responseData, | ||
headers: http2ResponseHeaders, | ||
}); | ||
}); | ||
}; | ||
try { | ||
const httpRequestHeaders: OutgoingHttpHeaders = { | ||
...requestHeaders, | ||
[http2.constants.HTTP2_HEADER_PATH]: "/query/1", | ||
[http2.constants.HTTP2_HEADER_METHOD]: method, | ||
}; | ||
const session = this.#connect(); | ||
req = session | ||
.request(httpRequestHeaders) | ||
.setEncoding("utf8") | ||
.on("error", (error: any) => { | ||
rejectPromise(error); | ||
}) | ||
.on("response", onResponse); | ||
req.write(JSON.stringify(requestData), "utf8"); | ||
// req.setTimeout must be called before req.end() | ||
req.setTimeout(client_timeout_ms, () => { | ||
req.destroy(new Error(`Client timeout`)); | ||
}); | ||
req.end(); | ||
} catch (error) { | ||
rejectPromise(error); | ||
} | ||
memoizedError = error; | ||
retryCount++; | ||
} | ||
); | ||
try { | ||
return await requestPromise; | ||
} catch (error) { | ||
// TODO: be more discernable about error types | ||
throw new NetworkError("The network connection encountered a problem.", { | ||
cause: error, | ||
}); | ||
} | ||
} while (retryCount < 3); | ||
throw new NetworkError("The network connection encountered a problem.", { | ||
cause: memoizedError, | ||
}); | ||
} | ||
/** {@inheritDoc HTTPClient.close} */ | ||
close() { | ||
const session_rc = NodeHTTP2Client.#sessionMap.get(this.sessionKey); | ||
if (!session_rc) return; | ||
session_rc.refs.delete(this); | ||
// if there are no clients referencing the session, then we can close it | ||
if (session_rc.refs.size === 0) { | ||
const session = session_rc.session; | ||
if (session && !session.closed) session.close(); | ||
session_rc.session = null; | ||
// defend against redundant close calls | ||
if (this.isClosed()) { | ||
return; | ||
} | ||
this.#numberOfUsers--; | ||
if (this.#numberOfUsers === 0 && this.#session && !this.#session.closed) { | ||
this.#session.close(); | ||
} | ||
} | ||
@@ -136,74 +122,90 @@ | ||
isClosed(): boolean { | ||
const session_rc = NodeHTTP2Client.#sessionMap.get(this.sessionKey); | ||
return (session_rc?.refs.size ?? 0) === 0; | ||
return this.#numberOfUsers === 0; | ||
} | ||
/** | ||
* Creates a key common the client that should share the same session | ||
*/ | ||
get sessionKey() { | ||
return `${this.#url}|${this.#http2_session_idle_ms}`; | ||
} | ||
#closeForAll() { | ||
const session_rc = NodeHTTP2Client.#sessionMap.get(this.sessionKey); | ||
if (!session_rc) return; | ||
session_rc.refs.clear(); | ||
const session = session_rc.session; | ||
if (session && !session.closed) session.close(); | ||
session_rc.session = null; | ||
this.#numberOfUsers = 0; | ||
if (this.#session && !this.#session.closed) { | ||
this.#session.close(); | ||
} | ||
} | ||
#connect() { | ||
let session_rc = NodeHTTP2Client.#sessionMap.get(this.sessionKey); | ||
// initialize the Map if necessary | ||
if (!session_rc) { | ||
session_rc = { | ||
session: null, | ||
refs: new Set(), | ||
}; | ||
NodeHTTP2Client.#sessionMap.set(this.sessionKey, session_rc); | ||
} | ||
// create a new session if necessary | ||
if (session_rc.session === null || session_rc.session.closed) { | ||
const http2_session_idle_ms = this.#http2_session_idle_ms; | ||
const url = this.#url; | ||
// create the session if it does not exist or is closed | ||
if (!this.#session || this.#session.closed) { | ||
const new_session: ClientHttp2Session = http2 | ||
.connect(url) | ||
.connect(this.#url) | ||
.once("error", () => this.#closeForAll()) | ||
.once("goaway", () => this.#closeForAll()); | ||
new_session.setTimeout(http2_session_idle_ms, () => { | ||
new_session.setTimeout(this.#http2_session_idle_ms, () => { | ||
this.#closeForAll(); | ||
}); | ||
session_rc.session = new_session; | ||
this.#session = new_session; | ||
} | ||
return this.#session; | ||
} | ||
session_rc.refs.add(this); | ||
#doRequest({ | ||
client_timeout_ms, | ||
data: requestData, | ||
headers: requestHeaders, | ||
method, | ||
}: HTTPRequest): Promise<HTTPResponse> { | ||
return new Promise<HTTPResponse>((resolvePromise, rejectPromise) => { | ||
let req: ClientHttp2Stream; | ||
const onResponse = ( | ||
http2ResponseHeaders: IncomingHttpHeaders & IncomingHttpStatusHeader | ||
) => { | ||
const status = Number( | ||
http2ResponseHeaders[http2.constants.HTTP2_HEADER_STATUS] | ||
); | ||
let responseData = ""; | ||
return session_rc.session; | ||
// append response data to the data string every time we receive new | ||
// data chunks in the response | ||
req.on("data", (chunk: any) => { | ||
responseData += chunk; | ||
}); | ||
// Once the response is finished, resolve the promise | ||
req.on("end", () => { | ||
resolvePromise({ | ||
status, | ||
body: responseData, | ||
headers: http2ResponseHeaders, | ||
}); | ||
}); | ||
}; | ||
try { | ||
const httpRequestHeaders: OutgoingHttpHeaders = { | ||
...requestHeaders, | ||
[http2.constants.HTTP2_HEADER_PATH]: "/query/1", | ||
[http2.constants.HTTP2_HEADER_METHOD]: method, | ||
}; | ||
const session = this.#connect(); | ||
req = session | ||
.request(httpRequestHeaders) | ||
.setEncoding("utf8") | ||
.on("error", (error: any) => { | ||
rejectPromise(error); | ||
}) | ||
.on("response", onResponse); | ||
req.write(JSON.stringify(requestData), "utf8"); | ||
// req.setTimeout must be called before req.end() | ||
req.setTimeout(client_timeout_ms, () => { | ||
req.destroy(new Error(`Client timeout`)); | ||
}); | ||
req.end(); | ||
} catch (error) { | ||
rejectPromise(error); | ||
} | ||
}); | ||
} | ||
} | ||
/** | ||
* Wrapper to provide reference counting for sessions. | ||
* | ||
* @internal | ||
*/ | ||
type SessionRC = { | ||
/** A session to be shared among http clients */ | ||
session: ClientHttp2Session | null; | ||
/** | ||
* A Setof client references. We will only close the session when there are no | ||
* references left. This cannot be a WeakSet, because WeakSets are not | ||
* enumerable (can't check if they are empty). | ||
*/ | ||
refs: Set<NodeHTTP2Client>; | ||
}; |
@@ -179,2 +179,3 @@ import { ClientError } from "./errors"; | ||
}), | ||
// es-lint-disable-next-line @typescript-eslint/no-unused-vars | ||
set: (value: Page<QueryValue> | EmbeddedSet) => { | ||
@@ -181,0 +182,0 @@ throw new ClientError( |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
408203
5075