@symbo.ls/fetch 3.14.663 → 3.14.665

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.
@@ -1,19 +1,20 @@
1
- import{buildUrl as l,selectQuery as f}from"./adapters/rest.js";import{EARLY_KEY as g,earlyKey as y,isScalarParams as h,matchEarlyRoute as m,withReads as p}from"./early.js";const d="smbls_lang",v=["credentials","mode","cache","redirect","referrerPolicy","priority"],c=r=>{try{return new Function(r),!0}catch{return!1}},b=(r,t)=>{const e=typeof r=="function"?r.toString():String(r).trim();if(c("return ("+e+")"))return"("+e+")";if(c("return ({"+e+"})"))return"Object.values({"+e+"})[0]";throw new Error(`@symbo.ls/fetch early: the request function of ${t} does not compile on its own`)},i=(r,t)=>typeof r=="function"||typeof r=="string"?b(r,t):JSON.stringify(Array.isArray(r)?r:[]),q=r=>{const t=r.state&&typeof r.state.lang=="string"?r.state.lang:"",e=r.polyglot;return e&&typeof e=="object"?{storage:e.storageLangKey||d,fallback:t||e.defaultLang||"en"}:{storage:"",fallback:t}},S=(r,t)=>{const e={};for(const a in r||{}){const o=Object.keys(t||{}).find(s=>s.toLowerCase()===a.toLowerCase());if(!o)throw new Error(`@symbo.ls/fetch early.query: "${a}" is not one of the adapter's configured headers`);e[r[a]]=t[o]}return e},w=r=>{const t={};for(const e of v)r&&r[e]!==void 0&&(t[e]=r[e]);return t.cache===void 0&&(t.cache="no-store"),t},O=(r={})=>{const t=r&&r.fetch,e=t&&p(t.early,t.reads);if(!e||typeof e!="object"||t.adapter!=="rest"||!t.url)return"";const a=e.routes&&typeof e.routes=="object"?Object.keys(e.routes):[];if(!a.length&&!e.always)return"";const o={url:t.url,query:S(e.query,t.headers),rpc:e.rpc==="get",fetchOptions:w(t.fetchOptions),lang:q(r)},s=a.map(n=>`[${JSON.stringify(n)}, ${i(e.routes[n],JSON.stringify(n))}]`).join(`,
2
- `),u=e.always?i(e.always,"`always`"):"[]";return`/* @symbo.ls/fetch \u2014 fetch.early: the first view's requests, sent before the app boots */
1
+ import{polyglotLangStorageKey as l,polyglotFallbackLang as f}from"@symbo.ls/utils";import{buildUrl as g,selectQuery as y,withQuery as h}from"./adapters/rest.js";import{EARLY_KEY as m,earlyKey as p,isScalarParams as d,matchEarlyRoute as v,withReads as q,queryHeaderEntries as S}from"./early.js";const b=["credentials","mode","cache","redirect","referrerPolicy","priority"],i=r=>{try{return new Function(r),!0}catch{return!1}},w=(r,t)=>{const e=typeof r=="function"?r.toString():String(r).trim();if(i("return ("+e+")"))return"("+e+")";if(i("return ({"+e+"})"))return"Object.values({"+e+"})[0]";throw new Error(`@symbo.ls/fetch early: the request function of ${t} does not compile on its own`)},u=(r,t)=>typeof r=="function"||typeof r=="string"?w(r,t):JSON.stringify(Array.isArray(r)?r:[]),O=r=>{const t=r.state&&typeof r.state.lang=="string"?r.state.lang:"",e=r.polyglot;return e&&typeof e=="object"?{storage:l(e),fallback:f(t,e)}:{storage:"",fallback:t}},$=(r,t)=>{const e={};for(const{name:a,param:o,found:n,value:s}of S(r,t)){if(!n)throw new Error(`@symbo.ls/fetch early.query: "${a}" is not one of the adapter's configured headers`);e[o]=s}return e},k=r=>{const t={};for(const e of b)r&&r[e]!==void 0&&(t[e]=r[e]);return t.cache===void 0&&(t.cache="no-store"),t},E=(r={})=>{const t=r&&r.fetch,e=t&&q(t.early,t.reads);if(!e||typeof e!="object"||t.adapter!=="rest"||!t.url)return"";const a=e.routes&&typeof e.routes=="object"?Object.keys(e.routes):[];if(!a.length&&!e.always)return"";const o={url:t.url,query:$(e.query,t.headers),rpc:e.rpc==="get",fetchOptions:k(t.fetchOptions),lang:O(r)},n=a.map(c=>`[${JSON.stringify(c)}, ${u(e.routes[c],JSON.stringify(c))}]`).join(`,
2
+ `),s=e.always?u(e.always,"`always`"):"[]";return`/* @symbo.ls/fetch \u2014 fetch.early: the first view's requests, sent before the app boots */
3
3
  ;(function () {
4
4
  var g = globalThis
5
- var K = Symbol.for(${JSON.stringify(g)})
5
+ var K = Symbol.for(${JSON.stringify(m)})
6
6
  if (g[K] || typeof g.fetch !== 'function' || !g.location) return
7
- var registry = g[K] = { time: Date.now(), entries: {} }
7
+ var registry = g[K] = { entries: {} }
8
8
  var config = ${JSON.stringify(o)}
9
- var buildUrl = ${l.toString()}
10
- var selectQuery = ${f.toString()}
11
- var earlyKey = ${y.toString()}
12
- var isScalarParams = ${h.toString()}
13
- var matchEarlyRoute = ${m.toString()}
14
- var always = ${u}
9
+ var buildUrl = ${g.toString()}
10
+ var selectQuery = ${y.toString()}
11
+ var withQuery = ${h.toString()}
12
+ var earlyKey = ${p.toString()}
13
+ var isScalarParams = ${d.toString()}
14
+ var matchEarlyRoute = ${v.toString()}
15
+ var always = ${s}
15
16
  var routes = [
16
- ${s}
17
+ ${n}
17
18
  ]
18
19
  var storage = function (key) {
19
20
  try { return key ? g.localStorage.getItem(key) : null } catch (e) { return null }
@@ -42,12 +43,10 @@ import{buildUrl as l,selectQuery as f}from"./adapters/rest.js";import{EARLY_KEY
42
43
  var language = request.lang === false ? '' : lang
43
44
  var key = earlyKey(url, language)
44
45
  if (registry.entries[key]) continue
45
- var address = new URL(url)
46
- for (var name in config.query) address.searchParams.set(name, config.query[name])
47
46
  var options = Object.assign({}, config.fetchOptions, { method: 'GET' })
48
47
  if (language) options.headers = { 'Accept-Language': language }
49
- registry.entries[key] = { time: Date.now(), answer: g.fetch(address.toString(), options).catch(function () { return null }) }
48
+ registry.entries[key] = { time: Date.now(), answer: g.fetch(withQuery(url, config.query), options).catch(function () { return null }) }
50
49
  } catch (e) {}
51
50
  }
52
51
  })()
53
- `.replace(/<\/script/gi,"<\\/script")};var L=O;export{O as createEarlyScript,L as default};
52
+ `.replace(/<\/script/gi,"<\\/script")};var A=E;export{E as createEarlyScript,A as default};
package/dist/esm/index.js CHANGED
@@ -1 +1 @@
1
- import{isArray as Q,isFunction as l,isObject as Z,isString as _e,exec as Be}from"@symbo.ls/utils";import{createEffect as it}from"@symbo.ls/signal";import{dropEarly as ut}from"./early.js";const ct=["select","rpc","insert","update","upsert","delete","subscribe"],Ge={rest:()=>import("./adapters/rest.js"),local:()=>import("./adapters/local.js")},ye=Symbol.for("@symbo.ls/fetch:CUSTOM_ADAPTERS"),ee=(()=>{if(typeof globalThis<"u"&&globalThis[ye])return globalThis[ye];const e={};if(typeof globalThis<"u")try{return Object.defineProperty(globalThis,ye,{value:e,writable:!1,configurable:!1,enumerable:!1}),globalThis[ye]}catch{}return e})(),Se=Symbol.for("@symbo.ls/fetch:PENDING_ADAPTERS"),Ce=(()=>{if(typeof globalThis<"u"&&globalThis[Se])return globalThis[Se];const e={};if(typeof globalThis<"u")try{return Object.defineProperty(globalThis,Se,{value:e,writable:!1,configurable:!1,enumerable:!1}),globalThis[Se]}catch{}return e})(),ft=5e3,lt=e=>new Promise(t=>{if(ee[e])return t(ee[e]);const r=Ce[e]||=[];let s=!1;const o=()=>{s||(s=!0,t(ee[e]||null))};r.push(o),setTimeout(o,ft)}),Ct=(e,t)=>{ee[e]=t;const r=Ce[e];if(r&&r.length){Ce[e]=[];for(const s of r)s()}},Mt=e=>typeof e=="string"&&!!(ee[e]||Ge[e]),Lt=e=>{const t={};for(const r of ct)e[r]&&(t[r]=e[r]);return t.name=e.name||"custom",t},dt=async e=>{if(!e)return null;if(typeof e.select=="function")return e;const{adapter:t,...r}=e,s=typeof t=="string"?t:typeof e=="string"?e:null;if(!s)return null;let o=ee[s]||Ge[s];if(!o&&(o=await lt(s),!o))throw new Error(`Unknown db adapter: "${s}". Use registerAdapter() for adapters with optional deps.`);const n=await o();if(!n||typeof n.setup!="function")throw new Error(`Adapter "${s}" loader returned a module without a setup() function. Got keys: [${Object.keys(n||{}).join(", ")}]. Likely a bundler chunk-resolution issue.`);return n.setup(r)},se=e=>{if(!e)return 0;if(typeof e=="number")return e;const t=e.match(/^(\d+)(ms|s|m|h)$/);if(!t)return 0;const r=parseInt(t[1]),s=t[2];return s==="ms"?r:s==="s"?r*1e3:s==="m"?r*6e4:s==="h"?r*36e5:0},v=new Map,Y=new Map,R=new Map;let I=0;const q=new Map,Me=new WeakMap,Le=(e,t,r,s,o=e.page,n=e.cursor)=>{if(e.cache?.key)return e.cache.key;const{from:i,method:u,infinite:a}=e,d=r?`${r}:`:"",_=s?`:${s}`:"",h=o?":p"+JSON.stringify(o):"",D=n!=null?":c"+JSON.stringify(n):"";return`${i}:${u}:${d}${JSON.stringify(t||"")}${a?":infinite":""}${h}${D}${_}`},ne=e=>v.get(e)||null,oe=(e,t,r,s=++I,o=!1)=>{const n={data:t,error:r,time:Date.now(),stale:o,seq:s};v.set(e,n);const i=Y.get(e);if(i)for(const u of i)l(u)&&u(n);return n},be=(e,t)=>{const r=e?v.get(e):null;return r&&!r.error&&r.seq>t?r:null},ke=(e,t)=>(q.get(e)||0)>t,ge=e=>{if(e){v.delete(e),q.set(e,++I);return}for(const[t]of v)q.set(t,++I);for(const[t]of R)q.set(t,++I);v.clear()},kt=(e,t)=>(Y.has(e)||Y.set(e,new Set),Y.get(e).add(t),()=>{const r=Y.get(e);r&&(r.delete(t),r.size===0&&Y.delete(e))}),Oe=e=>!e&&e!==!1?{staleTime:6e4,gcTime:3e5}:e===!1?null:e===!0?{staleTime:6e4,gcTime:3e5}:typeof e=="number"?{staleTime:e,gcTime:Math.max(e*5,3e5)}:_e(e)?{staleTime:se(e),gcTime:3e5}:{staleTime:se(e.stale||e.staleTime)||6e4,gcTime:se(e.gc||e.gcTime||e.expire)||3e5,key:e.key},x=new Set,Ye=e=>!!e.ref&&e.element.__ref===e.ref,pt=({config:e,element:t,context:r})=>{try{return qe(e,t,r)}catch{return null}};let we=null;const ht=()=>{we||(we=setInterval(()=>{const e=Date.now();for(const[t,r]of v){const s=Y.get(t);!(s&&s.size>0)&&e-r.time>(r.gcTime||3e5)&&v.delete(t)}for(const[t]of q)!v.has(t)&&!R.has(t)&&q.delete(t);for(const t of x)Ye(t)||x.delete(t)},3e4),we.unref&&we.unref())};ht();const xe=3,Re=e=>Math.min(1e3*2**e,3e4),Ve=(e,t)=>{const r=e.retry;return r===void 0&&t?{count:0}:r===!1?{count:0}:r===!0||r===void 0?{count:xe,delay:Re}:typeof r=="number"?{count:r,delay:Re}:{count:r.count??xe,delay:l(r.delay)?r.delay:typeof r.delay=="number"?()=>r.delay:Re}},We=async(e,t)=>{const{count:r,delay:s}=t;let o;for(let n=0;n<=r;n++)try{const i=await e();if(i?.error){if(o=i.error,n<r){const u=l(s)?s(n,o):s;await new Promise(a=>setTimeout(a,u));continue}return i}return i}catch(i){if(o=i,n<r){const u=l(s)?s(n,i):s;await new Promise(a=>setTimeout(a,u));continue}throw i}return{data:null,error:o}},te={focus:new Set,online:new Set};let Je=!1;const _t=()=>{if(Je||typeof window>"u")return;Je=!0;const e=()=>{for(const r of te.focus)r()},t=()=>{for(const r of te.online)r()};window.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&e()}),window.addEventListener("focus",e),window.addEventListener("online",t)},He=(e,t)=>{const r=t.__ref;if(e===!0)return{from:r.__state||t.key,method:"select",query:!0,on:"create"};if(_e(e))return{from:e,method:"select",query:!0,on:"create"};if(Z(e)){const s={...e};return s.from=s.from||r.__state||t.key,s.method=s.method||"select",s.on=s.on||"create",s.query===void 0&&(s.query=!0),s}return null},yt=["insert","update","upsert","delete"],Xe=e=>yt.includes(e),Ie=e=>e.mutation===!0?!0:e.mutation===!1||e.method!=="rpc"?!1:e.on==="submit"||e.on==="click"||e.invalidates!=null,ae=(e,t)=>{const r=e?.state?.root||t?.state?.root;if(r?.lang)return r.lang},Te=(e,t)=>{const s=(e?.state?.root||t?.state?.root)?.__fetchScope;return s?String(s):void 0},ve=(e,t,r)=>{let s;if(!e)s=void 0;else if(l(e))s=e(t,t.state);else{s={};for(const o in e){const n=e[o];s[o]=l(n)?n(t,t.state):n}}return s},ie=(e,t)=>l(e)?e(t,t.state):e,qe=(e,t,r)=>Le(e,ve(e.params,t,r),Te(t,r),ae(t,r),ie(e.page,t),ie(e.cursor,t)),Ne=async(e,t)=>{if(!e||e.__authInitialized||(e.__authInitialized=!0,!e.getSession))return;const r=(s,o)=>{const n=t.state?.root;n?.update&&n.update({auth:{user:s,session:o,loading:!1}})};try{const s=await e.getSession();r(s?.user||null,s)}catch(s){console.warn("[fetch] Failed to restore auth session:",s.message)}e.onAuthStateChange&&e.onAuthStateChange((s,o)=>{r(o?.user||null,o)})},Ue=async(e,t)=>{if(l(e.select))return e.auth!==!1&&await Ne(e,t),e;if(e.__resolved)return e.auth!==!1&&await Ne(e.__resolved,t),e.__resolved;if(e.__resolving)return e.__resolving;e.__resolving=dt(e);const r=await e.__resolving;return delete e.__resolving,r?(r.auth=e.auth,r.url=e.url,r.key=e.key,e.__resolved=r,t.fetch=r,e.auth!==!1&&await Ne(r,t),r):null},E=(e,t,...r)=>{const s=l(e[t])?e[t]:null;s&&s.call(e,...r,e,e.state,e.context)},F=(e,t,r,...s)=>{const o=e&&l(e[r])?e[r]:null;o&&o.call(t,...s,t,t.state,t.context)},ue=e=>{const t={},r=e.node;if(!r)return t;if(r.tagName==="FORM"){const o=new FormData(r);for(const[n,i]of o.entries())t[n]=i;return t}const s=r.querySelectorAll("input, textarea, select");for(let o=0;o<s.length;o++){const n=s[o],i=n.name||n.getAttribute("name");i&&(n.type==="checkbox"?t[i]=n.checked:n.type==="file"?t[i]=n.files:t[i]=n.value)}return t},Ee=e=>e.responseType==="stream"||e.responseType==="response",St=["Blob","File","ArrayBuffer","ReadableStream","Response"],Qe=e=>e!==null&&typeof e=="object"&&(ArrayBuffer.isView(e)||St.includes(Object.prototype.toString.call(e).slice(8,-1))),$=(e,t,r,s={})=>{if(!r&&Qe(t))return;const o=r&&r.startsWith("~/"),n=o&&e.state?.root||e.state;if(n?.update)if(o){const i=r.slice(2);t&&typeof t=="object"&&!Array.isArray(t)&&!Qe(t)&&i in t?n.update(t,{preventFetch:!0,...s}):n.update({[i]:t},{preventFetch:!0,...s})}else{const i=r?{[r]:t}:t;n.update(i,{preventFetch:!0,...s})}},C=(e,t)=>{const r=e?.__ref;r&&(r.__fetchStatus={...t,isError:!!t.error},r.__fetching=t.isFetching,r.__fetchError=t.error)},Ze=(e,t,r)=>{const s=e.__pages&&e.__pages.length?e.__pages:[r];l(t.getNextPageParam)&&(e.__nextPageParam=t.getNextPageParam(s[s.length-1],s),e.__hasNextPage=e.__nextPageParam!=null),l(t.getPreviousPageParam)&&(e.__prevPageParam=t.getPreviousPageParam(s[0],s),e.__hasPreviousPage=e.__prevPageParam!=null)},N=async(e,t,r,s={})=>{const o=r?.fetch;if(!o||!t||!t.__ref)return;const n=t.__ref,i=Te(t,r);(!n.__fetchScopeOwner||n.__fetchScopeOwner.scope!==i)&&(n.__fetchScopeOwner={scope:i});const u=n.__fetchScopeOwner,a=()=>t.__ref===n&&n.__fetchScopeOwner===u&&Te(t,r)===i;if(e.enabled===!1||l(e.enabled)&&!e.enabled(t,t.state)||e.skip===!0||l(e.skip)&&e.skip(t,t.state))return;const d=await Ue(o,r);if(!d||!a())return;const{from:_,method:h,query:D,params:De,cache:Pe,transform:P,single:ce,auth:fe,fields:V,as:L,limit:z,offset:U,order:b,headers:re,baseUrl:m,responseType:W,page:T,cursor:M,infinite:K,placeholderData:J,initialData:H,select:g,keepPreviousData:k}=e;let j=_e(g)?g:void 0;if(D&&l(t.getQuery)){const p=t.getQuery(d.name||"paths");p&&(j=p.select||p.length&&p.join(",")||j)}const B=ve(De,t,r),A=ie(T,t),me=ie(M,t),le=Ie(e),de=Ee(e)||le?null:Oe(Pe),nt=Ve(e,le||Xe(h)),Ke=ae(t,r),y=de?Le(e,B,i,Ke,A,me):null;if(!n.__fetchInitialized&&y&&(n.__fetchInitialized=!0,H!==void 0)){const p=l(H)?H():H;oe(y,p,null)}if((h==="select"||h==="rpc")&&y&&de&&!s.refresh&&!s.force){const p=ne(y);if(p&&!p.error){const c=Date.now()-p.time,w=p.stale||c>(de.staleTime||0);K&&Q(p.data)&&!n.__pages&&(n.__pages=[p.data],Ze(n,e,p.data));let f=p.data;if(l(g)&&(f=g(f,t,t.state)),P&&(f=P(f,t,t.state)),!a())return;if(!w){C(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!0,error:null,status:"success",fetchStatus:"idle"}),$(t,f,L);return}$(t,f,L)}else if(J!==void 0){const c=l(J)?J(t,t.state):J;c!==void 0&&$(t,c,L)}}const pe=y&&!s.force?R.get(y):null;if(pe&&!ke(y,Me.get(pe)||0)){const p=c=>{C(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:c,status:"error",fetchStatus:"idle"}),E(t,"onFetchError",c),F(e,t,"onFetchError",c)};try{const c=await Promise.race([pe,new Promise((w,f)=>setTimeout(()=>f("DEDUP_TIMEOUT"),15e3))]);if(!a())return;if(c?.error)p(c.error);else if(c?.data!==void 0){const w=!K&&be(y,Me.get(pe)||0);let f=w?w.data:c.data;if(l(g)&&(f=g(f,t,t.state)),P&&(f=P(f,t,t.state)),!a())return;$(t,f,L),E(t,"onFetchComplete",f),F(e,t,"onFetchComplete",f)}}catch(c){if(!a())return;c!=="DEDUP_TIMEOUT"&&p(c)}return}const ot=!ne(y)?.data;C(t,{isFetching:!0,isLoading:ot,isStale:!1,isSuccess:!1,error:null,status:"pending",fetchStatus:"fetching"}),E(t,"onFetchStart"),F(e,t,"onFetchStart");const O=r?.analyze?.emitNetwork,Ae=O?typeof performance<"u"?performance.now():Date.now():0;O&&O({phase:"start",mode:"query",from:_,method:h,cacheKey:y,lang:Ke});const at=async()=>{if(!a())return;if(fe!==!1&&d.auth!==!1&&d.getSession){const f=await d.getSession();if(!a())return;if(fe===!0&&!f){const S={message:"Not authenticated"};return C(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:S,status:"error",fetchStatus:"idle"}),E(t,"onFetchError",S),F(e,t,"onFetchError",S),{data:null,error:S}}}const p=d[h];if(!l(p))return{data:null,error:{message:`Method "${h}" not found on adapter`}};const c={from:_,select:j,params:B,single:ce,limit:z,offset:U,order:b,headers:re,baseUrl:m};W!==void 0&&(c.responseType=W);const w=ae(t,r);if(w&&(c.headers={...c.headers,"Accept-Language":w}),A!==void 0){if(Z(A))A.offset!==void 0&&(c.offset=A.offset),A.limit!==void 0&&(c.limit=A.limit),A.cursor!==void 0&&(c.params={...c.params,cursor:A.cursor});else if(typeof A=="number"){const f=e.pageSize||z||20;c.offset=(A-1)*f,c.limit=f}}if(me!==void 0&&(c.params={...c.params,cursor:me}),h==="insert"||h==="update"||h==="upsert"){let f;if(V===!0||e.on==="submit")f=ue(t);else if(Q(V)){const S=ue(t);f={};for(const G of V)f[G]=S[G]}else t.state?.parse?f=t.state.parse():Z(t.state)&&(f={...t.state});P&&(f=P(f,t,t.state)),c.data=f}if(h==="rpc"){let f=B;if(e.on==="submit"){const S=ue(t);f=e.transformParams?e.transformParams(S,t,t.state):S}c.params=f,c.read=!le&&e.read!==!1}if(h==="subscribe"){if(l(n.__unsubscribe)){const S=n.__unsubscribe;n.__unsubscribe=null;try{S()}catch{}}const f=d.subscribe({from:_,params:B,on:e.subscribeOn},(S,G,je)=>{a()&&($(t,S,L),E(t,"onFetchComplete",S),F(e,t,"onFetchComplete",S))});return a()?(n.__unsubscribe=f,{data:null,error:null,subscribed:!0}):(l(f)&&f(),{data:null,error:null,subscribed:!0})}return p(c)},X=++I,he=We(at,nt);Me.set(he,X),y&&R.set(y,he);try{const p=await he;if(le&&e.invalidates&&p&&!p.error&&!p.subscribed&&tt(e.invalidates,_),!a()||p?.subscribed)return;const{data:c,error:w}=p||{};if(w){y&&!be(y,X)&&oe(y,null,w,X),C(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:w,status:"error",fetchStatus:"idle"}),E(t,"onFetchError",w),F(e,t,"onFetchError",w),O&&O({phase:"error",mode:"query",from:_,method:h,cacheKey:y,error:w.message||w,durationMs:(typeof performance<"u"?performance.now():Date.now())-Ae});return}if(c!==void 0){let f=c;if((h==="select"||h==="rpc")&&y){const G=!K&&be(y,X);if(G)f=G.data;else{const je=oe(y,c,null,X,ke(y,X));je.gcTime=de?.gcTime||3e5}}let S=f;if(K&&Q(c)&&((!n.__pages||!s.direction)&&(n.__pages=[]),s.direction==="previous"?n.__pages.unshift(c):n.__pages.push(c),S=n.__pages.flat(),Ze(n,e,c)),l(g)&&(S=g(S,t,t.state)),P&&(S=P(S,t,t.state)),!a())return;C(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!0,error:null,status:"success",fetchStatus:"idle"}),(!k||S!==void 0)&&$(t,S,L),E(t,"onFetchComplete",S),F(e,t,"onFetchComplete",S),O&&O({phase:"success",mode:"query",ok:!0,from:_,method:h,cacheKey:y,durationMs:(typeof performance<"u"?performance.now():Date.now())-Ae})}}catch(p){if(!a())return;C(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:p,status:"error",fetchStatus:"idle"}),E(t,"onFetchError",p),F(e,t,"onFetchError",p),O&&O({phase:"error",mode:"query",from:_,method:h,cacheKey:y,error:p.message||String(p),durationMs:(typeof performance<"u"?performance.now():Date.now())-Ae})}finally{y&&R.get(y)===he&&R.delete(y)}},bt=(e,t,r)=>{const s=e.on,o=t.__ref;if(!o)return;o.__fetchListeners||(o.__fetchListeners=[]);const n=e.__runner||N;if(s==="submit"){const i=a=>{a.preventDefault(),n(e,t,r)},u=t.node;u&&(u.addEventListener("submit",i),o.__fetchListeners.push(()=>u.removeEventListener("submit",i)))}else if(s==="click"){const i=()=>n(e,t,r),u=t.node;u&&(u.addEventListener("click",i),o.__fetchListeners.push(()=>u.removeEventListener("click",i)))}else s==="stateChange"&&gt(e,t,r,n)},gt=(e,t,r,s)=>{const o=t.__ref;let n;const i=it(()=>{let u;try{u=JSON.stringify([ve(e.params,t,r)??null,l(e.enabled)?!!e.enabled(t,t.state):e.enabled!==!1,l(e.skip)?!!e.skip(t,t.state):e.skip===!0,ie(e.page,t)??null])}catch{return}if(n===void 0){n=u;return}u!==n&&(n=u,Promise.resolve().then(()=>s(e,t,r)))});Array.isArray(o.__effects)?o.__effects.push(i):o.__fetchListeners.push(()=>i.dispose())},wt=(e,t,r)=>{const s=t.__ref;s.__fetchCleanups||(s.__fetchCleanups=[]),_t();const o=e.refetchInterval;if(o){const a=typeof o=="number"?o:se(o);if(a>0){const d=setInterval(()=>{(e.refetchIntervalInBackground||typeof document>"u"||document.visibilityState==="visible")&&N(e,t,r,{force:!0})},a);s.__fetchCleanups.push(()=>clearInterval(d))}}const n=Ee(e),i=e.refetchOnWindowFocus===void 0?!n:e.refetchOnWindowFocus!==!1,u=e.refetchOnReconnect===void 0?!n:e.refetchOnReconnect!==!1;if(i){const a=()=>{const d=qe(e,t,r),_=ne(d),h=Oe(e.cache);(!_||_.stale||Date.now()-_.time>(h?.staleTime||0))&&N(e,t,r,{force:!0})};te.focus.add(a),s.__fetchCleanups.push(()=>te.focus.delete(a))}if(u){const a=()=>N(e,t,r,{force:!0});te.online.add(a),s.__fetchCleanups.push(()=>te.online.delete(a))}},Tt=(e,t,r)=>{if(!t.optimistic)return null;const s=e.__ref,o=t.as,n=e.state?.parse?e.state.parse():Z(e.state)?{...e.state}:e.state;s.__optimisticSnapshot=n;const i=l(t.optimistic)?t.optimistic(r,n,e):t.optimistic;return i!==void 0&&$(e,i,o),n},$e=(e,t)=>{const r=e.__ref;if(!r||!t.optimistic)return;const s=r.__optimisticSnapshot;s!==void 0&&($(e,s,t.as),delete r.__optimisticSnapshot)},ze=e=>{if(!e)return()=>!0;if(_e(e))return t=>t.includes(e);if(Q(e)){const t=e.join(":");return r=>r.includes(t)}return e instanceof RegExp?t=>(e.lastIndex=0,e.test(t)):l(e)?t=>{try{return!!e(t)}catch{return!1}}:()=>!1},vt=e=>{const t=[];for(const r of x){if(!Ye(r)){x.delete(r);continue}const s=pt(r);s&&e(s)&&t.push(N(r.config,r.element,r.context,{refresh:!0}))}return Promise.allSettled(t).then(()=>{})},et=e=>{ut();for(const[t,r]of v)e(t)&&(r.stale=!0,q.set(t,++I));for(const[t]of R)!v.has(t)&&e(t)&&q.set(t,++I);return vt(e)},tt=(e,t)=>{const r=(Q(e)?e:[e]).map(s=>s===!0||s==="*"?o=>o.startsWith(t+":"):s?ze(s):()=>!1);return et(s=>r.some(o=>o(s)))},Et=e=>{for(const[t]of v)e(t)&&ge(t);for(const[t]of R)e(t)&&q.set(t,++I)},Ft=async(e,t,r)=>{const s=r?.fetch;if(!s)return;const o=await Ue(s,r);if(!o)return;const n=t.__ref,{method:i,from:u,fields:a,transform:d,as:_,on:h,auth:D,headers:De,baseUrl:Pe,responseType:P,invalidates:ce,optimistic:fe,onMutate:V,onSuccess:L,onError:z,onSettled:U}=e;let b;if(a===!0||h==="submit")b=ue(t);else if(Q(a)){const T=ue(t);b={};for(const M of a)b[M]=T[M]}else t.state?.parse?b=t.state.parse():Z(t.state)&&(b={...t.state});d&&(b=d(b,t,t.state)),l(V)&&V(b,t,t.state);const re=fe?Tt(t,e,b):null;n.__fetching=!0,C(t,{isFetching:!0,isLoading:!1,isStale:!1,isSuccess:!1,error:null,status:"pending",fetchStatus:"fetching"}),E(t,"onFetchStart"),F(e,t,"onFetchStart");const m=r?.analyze?.emitNetwork,W=m?typeof performance<"u"?performance.now():Date.now():0;m&&m({phase:"start",mode:"mutation",from:u,method:i});try{if(D!==!1&&o.auth!==!1&&o.getSession){const j=await o.getSession();if(D===!0&&!j){re!==void 0&&$e(t,e);const B={message:"Not authenticated"};E(t,"onFetchError",B),F(e,t,"onFetchError",B);return}}const T=o[i];if(!l(T))return;const M={from:u,data:b,headers:De,baseUrl:Pe};P!==void 0&&(M.responseType=P),e.params&&(M.params=ve(e.params,t,r));const K=ae(t,r);K&&(M.headers={...M.headers,"Accept-Language":K});const J=Ve(e,!0),H=await We(()=>T(M),J),{data:g,error:k}=H||{};if(k){re!==void 0&&$e(t,e),C(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:k,status:"error",fetchStatus:"idle"}),E(t,"onFetchError",k),l(z)&&z(k,b,t),l(U)&&U(null,k,b,t),m&&m({phase:"error",mode:"mutation",from:u,method:i,error:k.message||String(k),durationMs:(typeof performance<"u"?performance.now():Date.now())-W});return}if(delete n.__optimisticSnapshot,C(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!0,error:null,status:"success",fetchStatus:"idle"}),g!==void 0&&(_||!Qe(g))){const j=_?{[_]:g}:g;t.state?.update&&t.state.update(j,{preventFetch:!0})}E(t,"onFetchComplete",g),F(e,t,"onFetchComplete",g),l(L)&&L(g,b,t),l(U)&&U(g,null,b,t),m&&m({phase:"success",mode:"mutation",ok:!0,from:u,method:i,durationMs:(typeof performance<"u"?performance.now():Date.now())-W}),ce&&tt(ce,u)}catch(T){re!==void 0&&$e(t,e),C(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:T,status:"error",fetchStatus:"idle"}),E(t,"onFetchError",T),F(e,t,"onFetchError",T),l(z)&&z(T,b,t),l(U)&&U(null,T,b,t),m&&m({phase:"error",mode:"mutation",from:u,method:i,error:T.message||String(T),durationMs:(typeof performance<"u"?performance.now():Date.now())-W})}finally{n.__fetching=!1}},Dt=e=>{const t=r=>{try{r()}catch{}};for(const r of e.__fetchCleanups||[])t(r);for(const r of e.__fetchListeners||[])t(r);l(e.__unsubscribe)&&t(e.__unsubscribe);for(const r of e.__fetchMounted||[])x.delete(r);e.__fetchCleanups=[],e.__fetchListeners=[],e.__unsubscribe=null,e.__fetchMounted=[]},rt=(e,t,r,s)=>{if(!e||!s?.fetch)return;const n=Be(e,t);if(!n)return;const i=Q(n)?n.map(a=>He(Be(a,t),t)).filter(Boolean):[He(n,t)].filter(Boolean),u=t.__ref;if(u.__fetchMounted)for(const a of u.__fetchMounted)x.delete(a);u.__fetchMounted=[],!u.__fetchDisposer&&Q(u.__effects)&&(u.__fetchDisposer={dispose:()=>Dt(u)},u.__effects.push(u.__fetchDisposer));for(const a of i){const d=Xe(a.method),_=d?Ft:N,h=!d&&!Ie(a)&&a.method!=="subscribe";if(a.on==="create"?_(a,t,s):Promise.resolve().then(()=>{bt({...a,__runner:_},t,s)}),h&&a.on==="create"&&((a.refetchInterval||a.refetchOnWindowFocus!==!1||a.refetchOnReconnect!==!1)&&wt(a,t,s),!Ee(a))){const D={element:t,ref:u,config:a,context:s};x.add(D),u.__fetchMounted.push(D)}}u.refetch=a=>{for(const d of i)N(d,t,s,{force:!0,...a})},u.fetchNextPage=()=>{const a=i[0];if(!a||!a.infinite)return;const d=u.__nextPageParam;if(d==null)return;const _={...a,cursor:d};N(_,t,s,{direction:"next",force:!0})},u.fetchPreviousPage=()=>{const a=i[0];if(!a||!a.infinite)return;const d=u.__prevPageParam;if(d==null)return;const _={...a,cursor:d};N(_,t,s,{direction:"previous",force:!0})}},Fe={invalidateQueries:e=>et(ze(e)),removeQueries:e=>{if(!e){ge();return}Et(ze(e))},getQueryData:e=>ne(e)?.data??void 0,setQueryData:(e,t)=>{const r=ne(e),s=l(t)?t(r?.data):t;oe(e,s,null)},prefetchQuery:async(e,t)=>{const r=t?.fetch;if(!r)return;const s=await Ue(r,t);if(!s)return;const o=e.method||"select",n=Te(null,t),i=ae(null,t),u=Le({...e,method:o},e.params,n,i),a=s[o];if(!l(a))return;const d={from:e.from,params:e.params,limit:e.limit,offset:e.offset,order:e.order,single:e.single,headers:e.headers,baseUrl:e.baseUrl};i&&(d.headers={...d.headers,"Accept-Language":i}),e.responseType!==void 0&&(d.responseType=e.responseType),o==="rpc"&&(d.read=!Ie(e)&&e.read!==!1);const _=++I,h=await a(d);if(h?.data!==void 0&&!h?.error&&!Ee(e)&&!be(u,_)){const D=oe(u,h.data,null,_,ke(u,_));D.gcTime=Oe(e.cache)?.gcTime||3e5}return h},getCache:()=>v,clear:()=>ge(),dropNonLang:e=>{if(!e)return;const t=`:${e}`;for(const[r]of v)r.match(/:([a-z]{2,3})$/i)&&(r.endsWith(t)||ge(r))}},st=(e,t)=>Z(e)?qe({method:"select",...e},t,t?.context):e,Ot={invalidateQueries(e){return Fe.invalidateQueries(e)},removeQueries(e){return Fe.removeQueries(e)},getQueryData(e){return Fe.getQueryData(st(e,this))},setQueryData(e,t){return Fe.setQueryData(st(e,this),t)}},Rt={name:"fetch",create(e){const t=e.fetch;t&&(e.parent||R.clear(),rt(t,e,e.state,e.context))}};import{earlyKey as Ut,EARLY_KEY as Qt,createEarlySeedScript as $t}from"./early.js";var It=rt;export{Qt as EARLY_KEY,Lt as createAdapter,$t as createEarlySeedScript,It as default,Ut as earlyKey,rt as executeFetch,Ot as fetchFunctions,Rt as fetchPlugin,Mt as hasAdapter,Ne as initAdapterAuth,se as parseDuration,Fe as queryClient,Ct as registerAdapter,dt as resolveDb};
1
+ import{isArray as q,isFunction as f,isObject as H,isString as _e,exec as He}from"@symbo.ls/utils";import{createEffect as yt}from"@symbo.ls/signal";import{dropEarly as bt}from"./early.js";import*as St from"./adapters/rest.js";const wt=["select","rpc","insert","update","upsert","delete","subscribe"],Xe={rest:()=>Promise.resolve(St),local:()=>import("./adapters/local.js")},ye=Symbol.for("@symbo.ls/fetch:CUSTOM_ADAPTERS"),X=(()=>{if(typeof globalThis<"u"&&globalThis[ye])return globalThis[ye];const e={};if(typeof globalThis<"u")try{return Object.defineProperty(globalThis,ye,{value:e,writable:!1,configurable:!1,enumerable:!1}),globalThis[ye]}catch{}return e})(),be=Symbol.for("@symbo.ls/fetch:PENDING_ADAPTERS"),Oe=(()=>{if(typeof globalThis<"u"&&globalThis[be])return globalThis[be];const e={};if(typeof globalThis<"u")try{return Object.defineProperty(globalThis,be,{value:e,writable:!1,configurable:!1,enumerable:!1}),globalThis[be]}catch{}return e})(),gt=5e3,Tt=e=>new Promise(t=>{if(X[e])return t(X[e]);const r=Oe[e]||=[];let s=!1;const o=()=>{s||(s=!0,t(X[e]||null))};r.push(o),setTimeout(o,gt)}),Ut=(e,t)=>{X[e]=t;const r=Oe[e];if(r&&r.length){Oe[e]=[];for(const s of r)s()}},Qt=e=>typeof e=="string"&&!!(X[e]||Xe[e]),$t=e=>{const t={};for(const r of wt)e[r]&&(t[r]=e[r]);return t.name=e.name||"custom",t},mt=async e=>{if(!e)return null;if(typeof e.select=="function")return e;const{adapter:t,...r}=e,s=typeof t=="string"?t:typeof e=="string"?e:null;if(!s)return null;let o=X[s]||Xe[s];if(!o&&(o=await Tt(s),!o))throw new Error(`Unknown db adapter: "${s}". Use registerAdapter() for adapters with optional deps.`);const n=await o();if(!n||typeof n.setup!="function")throw new Error(`Adapter "${s}" loader returned a module without a setup() function. Got keys: [${Object.keys(n||{}).join(", ")}]. Likely a bundler chunk-resolution issue.`);return n.setup(r)},re=e=>{if(!e)return 0;if(typeof e=="number")return e;const t=e.match(/^(\d+)(ms|s|m|h)$/);if(!t)return 0;const r=parseInt(t[1]),s=t[2];return s==="ms"?r:s==="s"?r*1e3:s==="m"?r*6e4:s==="h"?r*36e5:0},m=new Map,x=new Map,I=new Map;let Se=0;const we=new Map,Le=new WeakMap,Re=(e,t,r,s,o=e.page,n=e.cursor)=>{if(e.cache?.key)return e.cache.key;const{from:i,method:u,infinite:a}=e,l=r?`${r}:`:"",h=s?`:${s}`:"",p=o?":p"+JSON.stringify(o):"",v=n!=null?":c"+JSON.stringify(n):"";return`${i}:${u}:${l}${JSON.stringify(t||"")}${a?":infinite":""}${p}${v}${h}`},se=e=>m.get(e)||null,ne=(e,t,r,s=++Se,o=!1)=>{const n={data:t,error:r,time:Date.now(),stale:o,seq:s};m.set(e,n);const i=x.get(e);if(i)for(const u of i)f(u)&&u(n);return n},ge=(e,t)=>{const r=e?m.get(e):null;return r&&!r.error&&r.seq>t?r:null},qe=(e,t)=>(we.get(e)||0)>t,Te=e=>{we.set(e,++Se)},Ze=e=>{m.delete(e),Te(e)},zt=(e,t)=>(x.has(e)||x.set(e,new Set),x.get(e).add(t),()=>{const r=x.get(e);r&&(r.delete(t),r.size===0&&x.delete(e))}),Ie=e=>!e&&e!==!1?{staleTime:6e4,gcTime:3e5}:e===!1?null:e===!0?{staleTime:6e4,gcTime:3e5}:typeof e=="number"?{staleTime:e,gcTime:Math.max(e*5,3e5)}:_e(e)?{staleTime:re(e),gcTime:3e5}:{staleTime:re(e.stale||e.staleTime)||6e4,gcTime:re(e.gc||e.gcTime||e.expire)||3e5,key:e.key},N=new Set,et=e=>!!e.ref&&e.element.__ref===e.ref,vt=({config:e,element:t,context:r})=>{try{return Qe(e,t,r)}catch{return null}};let me=null;const Et=()=>{me||(me=setInterval(()=>{const e=Date.now();for(const[t,r]of m){const s=x.get(t);!(s&&s.size>0)&&e-r.time>(r.gcTime||3e5)&&m.delete(t)}for(const[t]of we)!m.has(t)&&!I.has(t)&&we.delete(t);for(const t of N)et(t)||N.delete(t)},3e4),me.unref&&me.unref())};Et();const tt=3,Ne=e=>Math.min(1e3*2**e,3e4),rt=(e,t)=>{const r=e.retry;return r===void 0&&t?{count:0}:r===!1?{count:0}:r===!0||r===void 0?{count:tt,delay:Ne}:typeof r=="number"?{count:r,delay:Ne}:{count:r.count??tt,delay:f(r.delay)?r.delay:typeof r.delay=="number"?()=>r.delay:Ne}},st=async(e,t)=>{const{count:r,delay:s}=t;let o;for(let n=0;n<=r;n++)try{const i=await e();if(i?.error){if(o=i.error,n<r){const u=f(s)?s(n,o):s;await new Promise(a=>setTimeout(a,u));continue}return i}return i}catch(i){if(o=i,n<r){const u=f(s)?s(n,i):s;await new Promise(a=>setTimeout(a,u));continue}throw i}return{data:null,error:o}},Z={focus:new Set,online:new Set};let nt=!1;const Dt=()=>{if(nt||typeof window>"u")return;nt=!0;const e=()=>{for(const r of Z.focus)r()},t=()=>{for(const r of Z.online)r()};window.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&e()}),window.addEventListener("focus",e),window.addEventListener("online",t)},ot=(e,t)=>{const r=t.__ref;if(e===!0)return{from:r.__state||t.key,method:"select",query:!0,on:"create"};if(_e(e))return{from:e,method:"select",query:!0,on:"create"};if(H(e)){const s={...e};return s.from=s.from||r.__state||t.key,s.method=s.method||"select",s.on=s.on||"create",s.query===void 0&&(s.query=!0),s}return null},Pt=["insert","update","upsert","delete"],at=e=>Pt.includes(e),Ue=e=>e.mutation===!0?!0:e.mutation===!1||e.method!=="rpc"?!1:e.on==="submit"||e.on==="click"||e.invalidates!=null,oe=(e,t)=>{const r=e?.state?.root||t?.state?.root;if(r?.lang)return r.lang},ve=(e,t)=>{const s=(e?.state?.root||t?.state?.root)?.__fetchScope;return s?String(s):void 0},Ee=(e,t,r)=>{let s;if(!e)s=void 0;else if(f(e))s=e(t,t.state);else{s={};for(const o in e){const n=e[o];s[o]=f(n)?n(t,t.state):n}}return s},ae=(e,t)=>f(e)?e(t,t.state):e,Qe=(e,t,r)=>Re(e,Ee(e.params,t,r),ve(t,r),oe(t,r),ae(e.page,t),ae(e.cursor,t)),$e=async(e,t)=>{if(!e||e.__authInitialized||(e.__authInitialized=!0,!e.getSession))return;const r=(s,o)=>{const n=t.state?.root;n?.update&&n.update({auth:{user:s,session:o,loading:!1}})};try{const s=await e.getSession();r(s?.user||null,s)}catch(s){console.warn("[fetch] Failed to restore auth session:",s.message)}e.onAuthStateChange&&e.onAuthStateChange((s,o)=>{r(o?.user||null,o)})},ze=async(e,t)=>{if(f(e.select))return e.auth!==!1&&await $e(e,t),e;if(e.__resolved)return e.auth!==!1&&await $e(e.__resolved,t),e.__resolved;if(e.__resolving)return e.__resolving;e.__resolving=mt(e);const r=await e.__resolving;return delete e.__resolving,r?(r.auth=e.auth,r.url=e.url,r.key=e.key,e.__resolved=r,t.fetch=r,e.auth!==!1&&await $e(r,t),r):null},F=(e,t,...r)=>{const s=f(e[t])?e[t]:null;s&&s.call(e,...r,e,e.state,e.context)},k=(e,t,r,...s)=>{const o=e&&f(e[r])?e[r]:null;o&&o.call(t,...s,t,t.state,t.context)},ie=e=>{const t={},r=e.node;if(!r)return t;if(r.tagName==="FORM"){const o=new FormData(r);for(const[n,i]of o.entries())t[n]=i;return t}const s=r.querySelectorAll("input, textarea, select");for(let o=0;o<s.length;o++){const n=s[o],i=n.name||n.getAttribute("name");i&&(n.type==="checkbox"?t[i]=n.checked:n.type==="file"?t[i]=n.files:t[i]=n.value)}return t},De=e=>e.responseType==="stream"||e.responseType==="response",Ft=["Blob","File","ArrayBuffer","ReadableStream","Response"],je=e=>e!==null&&typeof e=="object"&&(ArrayBuffer.isView(e)||Ft.includes(Object.prototype.toString.call(e).slice(8,-1))),U=(e,t,r,s={})=>{if(!r&&je(t))return;const o=r&&r.startsWith("~/"),n=o&&e.state?.root||e.state;if(n?.update)if(o){const i=r.slice(2);t&&typeof t=="object"&&!Array.isArray(t)&&!je(t)&&i in t?n.update(t,{preventFetch:!0,...s}):n.update({[i]:t},{preventFetch:!0,...s})}else{const i=r?{[r]:t}:t;n.update(i,{preventFetch:!0,...s})}},Be=Object.freeze({isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!0,error:null,status:"success",fetchStatus:"idle"}),Ke=e=>({isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:e,status:"error",fetchStatus:"idle"}),Q=(e,t)=>{const r=e?.__ref;r&&(r.__fetchStatus={...t,isError:!!t.error},r.__fetching=t.isFetching,r.__fetchError=t.error)},it=(e,t,r)=>{const s=e.__pages&&e.__pages.length?e.__pages:[r];f(t.getNextPageParam)&&(e.__nextPageParam=t.getNextPageParam(s[s.length-1],s),e.__hasNextPage=e.__nextPageParam!=null),f(t.getPreviousPageParam)&&(e.__prevPageParam=t.getPreviousPageParam(s[0],s),e.__hasPreviousPage=e.__prevPageParam!=null)},O=async(e,t,r,s={})=>{const o=r?.fetch;if(!o||!t||!t.__ref)return;const n=t.__ref,i=ve(t,r);(!n.__fetchScopeOwner||n.__fetchScopeOwner.scope!==i)&&(n.__fetchScopeOwner={scope:i});const u=n.__fetchScopeOwner,a=()=>t.__ref===n&&n.__fetchScopeOwner===u&&ve(t,r)===i;if(e.enabled===!1||f(e.enabled)&&!e.enabled(t,t.state)||e.skip===!0||f(e.skip)&&e.skip(t,t.state))return;const l=await ze(o,r);if(!l||!a())return;const{from:h,method:p,query:v,params:Fe,cache:Ae,transform:$,single:ue,auth:ce,fields:Y,as:A,limit:z,offset:L,order:S,headers:te,baseUrl:E,responseType:G,page:g,cursor:P,infinite:j,placeholderData:W,initialData:V,select:T,keepPreviousData:C}=e,B=c=>(f(T)&&(c=T(c,t,t.state)),$&&(c=$(c,t,t.state)),c),R=c=>{Q(t,Ke(c)),F(t,"onFetchError",c),k(e,t,"onFetchError",c)};let Ce=_e(T)?T:void 0;if(v&&f(t.getQuery)){const c=t.getQuery(l.name||"paths");c&&(Ce=c.select||c.length&&c.join(",")||Ce)}const fe=Ee(Fe,t,r),D=ae(g,t),Me=ae(P,t),le=Ue(e),de=De(e)||le?null:Ie(Ae),pt=rt(e,le||at(p)),Ve=oe(t,r),y=de?Re(e,fe,i,Ve,D,Me):null;if(!n.__fetchInitialized&&y&&(n.__fetchInitialized=!0,V!==void 0)){const c=f(V)?V():V;ne(y,c,null)}if((p==="select"||p==="rpc")&&y&&de&&!s.refresh&&!s.force){const c=se(y);if(c&&!c.error){const d=Date.now()-c.time,w=c.stale||d>(de.staleTime||0);j&&q(c.data)&&!n.__pages&&(n.__pages=[c.data],it(n,e,c.data));const _=B(c.data);if(!a())return;if(!w){Q(t,Be),U(t,_,A);return}U(t,_,A)}else if(W!==void 0){const d=f(W)?W(t,t.state):W;d!==void 0&&U(t,d,A)}}const pe=y&&!s.force?I.get(y):null;if(pe&&!qe(y,Le.get(pe)||0)){try{const c=await Promise.race([pe,new Promise((d,w)=>setTimeout(()=>w("DEDUP_TIMEOUT"),15e3))]);if(!a())return;if(c?.error)R(c.error);else if(c?.data!==void 0){const d=!j&&ge(y,Le.get(pe)||0),w=B(d?d.data:c.data);if(!a())return;U(t,w,A),F(t,"onFetchComplete",w),k(e,t,"onFetchComplete",w)}}catch(c){if(!a())return;c!=="DEDUP_TIMEOUT"&&R(c)}return}const ht=!se(y)?.data;Q(t,{isFetching:!0,isLoading:ht,isStale:!1,isSuccess:!1,error:null,status:"pending",fetchStatus:"fetching"}),F(t,"onFetchStart"),k(e,t,"onFetchStart");const M=r?.analyze?.emitNetwork,ke=M?typeof performance<"u"?performance.now():Date.now():0;M&&M({phase:"start",mode:"query",from:h,method:p,cacheKey:y,lang:Ve});const _t=async()=>{if(!a())return;if(ce!==!1&&l.auth!==!1&&l.getSession){const _=await l.getSession();if(!a())return;if(ce===!0&&!_){const b={message:"Not authenticated"};return R(b),{data:null,error:b}}}const c=l[p];if(!f(c))return{data:null,error:{message:`Method "${p}" not found on adapter`}};const d={from:h,select:Ce,params:fe,single:ue,limit:z,offset:L,order:S,headers:te,baseUrl:E};G!==void 0&&(d.responseType=G);const w=oe(t,r);if(w&&(d.headers={...d.headers,"Accept-Language":w}),D!==void 0){if(H(D))D.offset!==void 0&&(d.offset=D.offset),D.limit!==void 0&&(d.limit=D.limit),D.cursor!==void 0&&(d.params={...d.params,cursor:D.cursor});else if(typeof D=="number"){const _=e.pageSize||z||20;d.offset=(D-1)*_,d.limit=_}}if(Me!==void 0&&(d.params={...d.params,cursor:Me}),p==="insert"||p==="update"||p==="upsert"){let _;if(Y===!0||e.on==="submit")_=ie(t);else if(q(Y)){const b=ie(t);_={};for(const K of Y)_[K]=b[K]}else t.state?.parse?_=t.state.parse():H(t.state)&&(_={...t.state});$&&(_=$(_,t,t.state)),d.data=_}if(p==="rpc"){let _=fe;if(e.on==="submit"){const b=ie(t);_=e.transformParams?e.transformParams(b,t,t.state):b}d.params=_,d.read=!le&&e.read!==!1}if(p==="subscribe"){if(f(n.__unsubscribe)){const b=n.__unsubscribe;n.__unsubscribe=null;try{b()}catch{}}const _=l.subscribe({from:h,params:fe,on:e.subscribeOn},(b,K,Je)=>{a()&&(U(t,b,A),F(t,"onFetchComplete",b),k(e,t,"onFetchComplete",b))});return a()?(n.__unsubscribe=_,{data:null,error:null,subscribed:!0}):(f(_)&&_(),{data:null,error:null,subscribed:!0})}return c(d)},J=++Se,he=st(_t,pt);Le.set(he,J),y&&I.set(y,he);try{const c=await he;if(le&&e.invalidates&&c&&!c.error&&!c.subscribed&&ct(e.invalidates,h),!a()||c?.subscribed)return;const{data:d,error:w}=c||{};if(w){y&&!ge(y,J)&&ne(y,null,w,J),R(w),M&&M({phase:"error",mode:"query",from:h,method:p,cacheKey:y,error:w.message||w,durationMs:(typeof performance<"u"?performance.now():Date.now())-ke});return}if(d!==void 0){let _=d;if((p==="select"||p==="rpc")&&y){const K=!j&&ge(y,J);if(K)_=K.data;else{const Je=ne(y,d,null,J,qe(y,J));Je.gcTime=de?.gcTime||3e5}}let b=_;if(j&&q(d)&&((!n.__pages||!s.direction)&&(n.__pages=[]),s.direction==="previous"?n.__pages.unshift(d):n.__pages.push(d),b=n.__pages.flat(),it(n,e,d)),b=B(b),!a())return;Q(t,Be),(!C||b!==void 0)&&U(t,b,A),F(t,"onFetchComplete",b),k(e,t,"onFetchComplete",b),M&&M({phase:"success",mode:"query",ok:!0,from:h,method:p,cacheKey:y,durationMs:(typeof performance<"u"?performance.now():Date.now())-ke})}}catch(c){if(!a())return;R(c),M&&M({phase:"error",mode:"query",from:h,method:p,cacheKey:y,error:c.message||String(c),durationMs:(typeof performance<"u"?performance.now():Date.now())-ke})}finally{y&&I.get(y)===he&&I.delete(y)}},At=(e,t,r)=>{const s=e.on,o=t.__ref;if(!o)return;o.__fetchListeners||(o.__fetchListeners=[]);const n=e.__runner||O;if(s==="submit"){const i=a=>{a.preventDefault(),n(e,t,r)},u=t.node;u&&(u.addEventListener("submit",i),o.__fetchListeners.push(()=>u.removeEventListener("submit",i)))}else if(s==="click"){const i=()=>n(e,t,r),u=t.node;u&&(u.addEventListener("click",i),o.__fetchListeners.push(()=>u.removeEventListener("click",i)))}else s==="stateChange"&&Ct(e,t,r,n)},Ct=(e,t,r,s)=>{const o=t.__ref;let n;const i=yt(()=>{let u;try{u=JSON.stringify([Ee(e.params,t,r)??null,f(e.enabled)?!!e.enabled(t,t.state):e.enabled!==!1,f(e.skip)?!!e.skip(t,t.state):e.skip===!0,ae(e.page,t)??null])}catch{return}if(n===void 0){n=u;return}u!==n&&(n=u,queueMicrotask(()=>s(e,t,r)))});Array.isArray(o.__effects)?o.__effects.push(i):o.__fetchListeners.push(()=>i.dispose())},Mt=(e,t,r)=>{const s=t.__ref;s.__fetchCleanups||(s.__fetchCleanups=[]),Dt();const o=e.refetchInterval;if(o){const a=typeof o=="number"?o:re(o);if(a>0){const l=setInterval(()=>{(e.refetchIntervalInBackground||typeof document>"u"||document.visibilityState==="visible")&&O(e,t,r,{force:!0})},a);s.__fetchCleanups.push(()=>clearInterval(l))}}const n=De(e),i=e.refetchOnWindowFocus===void 0?!n:e.refetchOnWindowFocus!==!1,u=e.refetchOnReconnect===void 0?!n:e.refetchOnReconnect!==!1;if(i){const a=()=>{const l=Qe(e,t,r),h=se(l),p=Ie(e.cache);(!h||h.stale||Date.now()-h.time>(p?.staleTime||0))&&O(e,t,r,{force:!0})};Z.focus.add(a),s.__fetchCleanups.push(()=>Z.focus.delete(a))}if(u){const a=()=>O(e,t,r,{force:!0});Z.online.add(a),s.__fetchCleanups.push(()=>Z.online.delete(a))}},kt=(e,t,r)=>{if(!t.optimistic)return null;const s=e.__ref,o=t.as,n=e.state?.parse?e.state.parse():H(e.state)?{...e.state}:e.state;s.__optimisticSnapshot=n;const i=f(t.optimistic)?t.optimistic(r,n,e):t.optimistic;return i!==void 0&&U(e,i,o),n},xe=(e,t)=>{const r=e.__ref;if(!r||!t.optimistic)return;const s=r.__optimisticSnapshot;s!==void 0&&(U(e,s,t.as),delete r.__optimisticSnapshot)},Ye=e=>{if(!e)return()=>!0;if(_e(e))return t=>t.includes(e);if(q(e)){const t=e.join(":");return r=>r.includes(t)}return e instanceof RegExp?t=>(e.lastIndex=0,e.test(t)):f(e)?t=>{try{return!!e(t)}catch{return!1}}:()=>!1},Ot=e=>{const t=[];for(const r of N){if(!et(r)){N.delete(r);continue}const s=vt(r);s&&e(s)&&t.push(O(r.config,r.element,r.context,{refresh:!0}))}return Promise.allSettled(t).then(()=>{})},ut=e=>{bt();for(const[t,r]of m)e(t)&&(r.stale=!0,Te(t));for(const[t]of I)!m.has(t)&&e(t)&&Te(t);return Ot(e)},ct=(e,t)=>{const r=(q(e)?e:[e]).map(s=>s===!0||s==="*"?o=>o.startsWith(t+":"):s?Ye(s):()=>!1);return ut(s=>r.some(o=>o(s)))},Ge=e=>{for(const[t]of m)e(t)&&Ze(t);for(const[t]of I)e(t)&&Te(t)},We=()=>!0,ft=new WeakSet,ee=e=>{!e||!e.isolateFetchCache||ft.has(e)||(ft.add(e),Ge(We),N.clear())},Lt=async(e,t,r)=>{const s=r?.fetch;if(!s)return;const o=await ze(s,r);if(!o)return;const n=t.__ref,{method:i,from:u,fields:a,transform:l,as:h,on:p,auth:v,headers:Fe,baseUrl:Ae,responseType:$,invalidates:ue,optimistic:ce,onMutate:Y,onSuccess:A,onError:z,onSettled:L}=e;let S;if(a===!0||p==="submit")S=ie(t);else if(q(a)){const g=ie(t);S={};for(const P of a)S[P]=g[P]}else t.state?.parse?S=t.state.parse():H(t.state)&&(S={...t.state});l&&(S=l(S,t,t.state)),f(Y)&&Y(S,t,t.state);const te=ce?kt(t,e,S):null;Q(t,{isFetching:!0,isLoading:!1,isStale:!1,isSuccess:!1,error:null,status:"pending",fetchStatus:"fetching"}),F(t,"onFetchStart"),k(e,t,"onFetchStart");const E=r?.analyze?.emitNetwork,G=E?typeof performance<"u"?performance.now():Date.now():0;E&&E({phase:"start",mode:"mutation",from:u,method:i});try{if(v!==!1&&o.auth!==!1&&o.getSession){const B=await o.getSession();if(v===!0&&!B){te!==void 0&&xe(t,e);const R={message:"Not authenticated"};F(t,"onFetchError",R),k(e,t,"onFetchError",R);return}}const g=o[i];if(!f(g))return;const P={from:u,data:S,headers:Fe,baseUrl:Ae};$!==void 0&&(P.responseType=$),e.params&&(P.params=Ee(e.params,t,r));const j=oe(t,r);j&&(P.headers={...P.headers,"Accept-Language":j});const W=rt(e,!0),V=await st(()=>g(P),W),{data:T,error:C}=V||{};if(C){te!==void 0&&xe(t,e),Q(t,Ke(C)),F(t,"onFetchError",C),f(z)&&z(C,S,t),f(L)&&L(null,C,S,t),E&&E({phase:"error",mode:"mutation",from:u,method:i,error:C.message||String(C),durationMs:(typeof performance<"u"?performance.now():Date.now())-G});return}if(delete n.__optimisticSnapshot,Q(t,Be),T!==void 0&&(h||!je(T))){const B=h?{[h]:T}:T;t.state?.update&&t.state.update(B,{preventFetch:!0})}F(t,"onFetchComplete",T),k(e,t,"onFetchComplete",T),f(A)&&A(T,S,t),f(L)&&L(T,null,S,t),E&&E({phase:"success",mode:"mutation",ok:!0,from:u,method:i,durationMs:(typeof performance<"u"?performance.now():Date.now())-G}),ue&&ct(ue,u)}catch(g){te!==void 0&&xe(t,e),Q(t,Ke(g)),F(t,"onFetchError",g),k(e,t,"onFetchError",g),f(z)&&z(g,S,t),f(L)&&L(null,g,S,t),E&&E({phase:"error",mode:"mutation",from:u,method:i,error:g.message||String(g),durationMs:(typeof performance<"u"?performance.now():Date.now())-G})}finally{n.__fetching=!1}},Rt=e=>{const t=r=>{try{r()}catch{}};for(const r of e.__fetchCleanups||[])t(r);for(const r of e.__fetchListeners||[])t(r);f(e.__unsubscribe)&&t(e.__unsubscribe);for(const r of e.__fetchMounted||[])N.delete(r);e.__fetchCleanups=[],e.__fetchListeners=[],e.__unsubscribe=null,e.__fetchMounted=[]},lt=(e,t,r,s)=>{if(!e||!s?.fetch)return;ee(s);const n=He(e,t);if(!n)return;const i=q(n)?n.map(a=>ot(He(a,t),t)).filter(Boolean):[ot(n,t)].filter(Boolean),u=t.__ref;if(u.__fetchMounted)for(const a of u.__fetchMounted)N.delete(a);u.__fetchMounted=[],!u.__fetchDisposer&&q(u.__effects)&&(u.__fetchDisposer={dispose:()=>Rt(u)},u.__effects.push(u.__fetchDisposer));for(const a of i){const l=at(a.method),h=l?Lt:O,p=!l&&!Ue(a)&&a.method!=="subscribe";if(a.on==="create"?h(a,t,s):queueMicrotask(()=>At({...a,__runner:h},t,s)),p&&a.on==="create"&&((a.refetchInterval||a.refetchOnWindowFocus!==!1||a.refetchOnReconnect!==!1)&&Mt(a,t,s),!De(a))){const v={element:t,ref:u,config:a,context:s};N.add(v),u.__fetchMounted.push(v)}}u.refetch=a=>{for(const l of i)O(l,t,s,{force:!0,...a})},u.fetchNextPage=()=>{const a=i[0];if(!a||!a.infinite)return;const l=u.__nextPageParam;if(l==null)return;const h={...a,cursor:l};O(h,t,s,{direction:"next",force:!0})},u.fetchPreviousPage=()=>{const a=i[0];if(!a||!a.infinite)return;const l=u.__prevPageParam;if(l==null)return;const h={...a,cursor:l};O(h,t,s,{direction:"previous",force:!0})}},Pe={invalidateQueries:e=>ut(Ye(e)),removeQueries:e=>{Ge(e?Ye(e):We)},getQueryData:e=>se(e)?.data??void 0,setQueryData:(e,t)=>{const r=se(e),s=f(t)?t(r?.data):t;ne(e,s,null)},prefetchQuery:async(e,t)=>{const r=t?.fetch;if(!r)return;ee(t);const s=await ze(r,t);if(!s)return;const o=e.method||"select",n=ve(null,t),i=oe(null,t),u=Re({...e,method:o},e.params,n,i),a=s[o];if(!f(a))return;const l={from:e.from,params:e.params,limit:e.limit,offset:e.offset,order:e.order,single:e.single,headers:e.headers,baseUrl:e.baseUrl};i&&(l.headers={...l.headers,"Accept-Language":i}),e.responseType!==void 0&&(l.responseType=e.responseType),o==="rpc"&&(l.read=!Ue(e)&&e.read!==!1);const h=++Se,p=await a(l);if(p?.data!==void 0&&!p?.error&&!De(e)&&!ge(u,h)){const v=ne(u,p.data,null,h,qe(u,h));v.gcTime=Ie(e.cache)?.gcTime||3e5}return p},getCache:()=>m,clear:()=>Ge(We),dropNonLang:e=>{if(!e)return;const t=`:${e}`;for(const[r]of m)r.match(/:([a-z]{2,3})$/i)&&(r.endsWith(t)||Ze(r))}},dt=(e,t)=>H(e)?Qe({method:"select",...e},t,t?.context):e,jt={invalidateQueries(e){return ee(this?.context),Pe.invalidateQueries(e)},removeQueries(e){return ee(this?.context),Pe.removeQueries(e)},getQueryData(e){return ee(this?.context),Pe.getQueryData(dt(e,this))},setQueryData(e,t){return ee(this?.context),Pe.setQueryData(dt(e,this),t)}},Bt={name:"fetch",create(e){const t=e.fetch;t&&(e.parent||I.clear(),lt(t,e,e.state,e.context))}};import{earlyKey as Gt,EARLY_KEY as Wt,createEarlySeedScript as Vt,createEarlySeedBlock as Jt,SEED_BLOCK as Ht}from"./early.js";import{requestsInFlight as Zt,whenRequestsIdle as er}from"./inflight.js";var Kt=lt;export{Wt as EARLY_KEY,Ht as SEED_BLOCK,$t as createAdapter,Jt as createEarlySeedBlock,Vt as createEarlySeedScript,Kt as default,Gt as earlyKey,lt as executeFetch,jt as fetchFunctions,Bt as fetchPlugin,Qt as hasAdapter,$e as initAdapterAuth,re as parseDuration,Pe as queryClient,Ut as registerAdapter,Zt as requestsInFlight,mt as resolveDb,er as whenRequestsIdle};
@@ -0,0 +1 @@
1
+ const o=Symbol.for("@symbo.ls/fetch:IN_FLIGHT"),s=()=>globalThis[o]||(globalThis[o]={count:0,waiters:[]}),r=()=>{s().count++},c=()=>{const t=s();if(t.count=t.count>0?t.count-1:0,t.count===0&&t.waiters.length){const e=t.waiters;t.waiters=[];for(const n of e)n()}},i=()=>s().count,u=()=>{const t=s();return t.count===0?Promise.resolve():new Promise(e=>{t.waiters.push(e)})};export{c as requestFinished,r as requestStarted,i as requestsInFlight,u as whenRequestsIdle};
package/early.js CHANGED
@@ -13,10 +13,10 @@
13
13
  // the pre-boot script runs before any module, so a global is the only thing
14
14
  // it can hand over through; the Symbol keeps it off the visible namespace and
15
15
  // shared by every copy of this package (same rationale as CUSTOM_ADAPTERS).
16
- // { time, closed, entries: { [earlyKey]: { time, answer: Promise<Response|null> } } }
17
- // The script creates it; an adapter that finds none creates it CLOSED, so a
18
- // pre-boot script that arrives after the app booted (an async script on a
19
- // cold cache) sends nothing: no call would be left to adopt its answers.
16
+ // { entries: { [earlyKey]: { time, answer: Promise<Response|null> } }, seeded? }
17
+ // The script creates it; an adapter that finds none creates it, so a pre-boot
18
+ // script that arrives after the app booted (an async script on a cold cache)
19
+ // finds one and sends nothing: no call would be left to adopt its answers.
20
20
  //
21
21
  // The pure helpers below (earlyKey, isScalarParams, matchEarlyRoute) are ALSO
22
22
  // embedded into the pre-boot script by source (`fn.toString()`), so the script
@@ -24,6 +24,8 @@
24
24
  // self-contained: no reference to anything outside their own parameters and
25
25
  // the platform's built-ins (URL, decodeURIComponent, …).
26
26
 
27
+ import { jsonForScript, readJsonDataBlock } from '@symbo.ls/utils'
28
+
27
29
  export const EARLY_KEY = '@symbo.ls/fetch:EARLY'
28
30
 
29
31
  // How long an early answer stays adoptable, counted from when the pre-boot
@@ -97,6 +99,30 @@ export const matchEarlyRoute = (patterns, path) => {
97
99
 
98
100
  const registryKey = () => Symbol.for(EARLY_KEY)
99
101
 
102
+ // The configured headers a `query` mapping (header name → query parameter —
103
+ // `reads.query`, `early.query`) moves into the URL: one entry per mapped name,
104
+ // `{ name, header: lower-cased name, param, found, value }`, matched against
105
+ // `headers` case-insensitively (of two spellings the later wins, as the
106
+ // adapter's mergeHeaders does). One reading for the three that need it: the
107
+ // adapter's anonymous reads (rest.js openReads), the early hand-off
108
+ // (openEarly) and the pre-boot script's build (earlyScript.js queryParams) —
109
+ // so the key the script sends is the key the adapter expects.
110
+ export const queryHeaderEntries = (mapping, headers) => {
111
+ const entries = []
112
+ for (const name in mapping || {}) {
113
+ const header = name.toLowerCase()
114
+ const entry = { name, header, param: mapping[name], found: false, value: undefined }
115
+ for (const key in headers || {}) {
116
+ if (key.toLowerCase() === header) {
117
+ entry.found = true
118
+ entry.value = headers[key]
119
+ }
120
+ }
121
+ entries.push(entry)
122
+ }
123
+ return entries
124
+ }
125
+
100
126
  // `fetch.early` with its `query` and `rpc` defaulting to the adapter's
101
127
  // `reads` (adapters/rest.js): the same two options, declared once, drive both
102
128
  // the pre-boot script and the adapter's own GETs. Used by both sides — the
@@ -118,30 +144,28 @@ export const withReads = (early, reads) => {
118
144
  // No `early` config → null, and the adapter never looks (the opt-in).
119
145
  export const openEarly = (config, headers) => {
120
146
  const key = registryKey()
147
+ // The page's seed block, if it carries one, fills the registry first.
148
+ readSeedBlock()
121
149
  if (!config || typeof config !== 'object') {
122
- // A server-seeded registry (createEarlySeedScript: the answers a
123
- // brender pre-render already got) is adoptable without `fetch.early`:
124
- // the page carries the answers, nothing is sent early. Only a call with
125
- // no header beyond Accept-Language can take one — exactly what the
126
- // server recorded (an anonymous GET).
150
+ // A server-seeded registry (the answers a brender pre-render already
151
+ // got: createEarlySeedBlock, or an older page's createEarlySeedScript)
152
+ // is adoptable without `fetch.early`: the page carries the answers,
153
+ // nothing is sent early. Only a call that sends exactly what the server
154
+ // recorded — an anonymous GET, no header beyond Accept-Language — can
155
+ // take one.
127
156
  const seeded = globalThis[key]
128
- if (seeded && seeded.seeded) return { carried: {}, rpc: false, maxAge: EARLY_MAX_AGE }
157
+ if (seeded && seeded.seeded) return { carried: {}, rpc: false, maxAge: EARLY_MAX_AGE, seeded: true }
129
158
  return null
130
159
  }
131
- let registry = globalThis[key]
132
- if (!registry) {
133
- registry = { time: Date.now(), entries: {} }
134
- globalThis[key] = registry
135
- }
136
- registry.closed = true
160
+ // Any registry closes the hand-off: the pre-boot script sends nothing once
161
+ // one exists (earlyScript.js).
162
+ if (!globalThis[key]) globalThis[key] = { entries: {} }
137
163
  const carried = {}
138
- for (const name in config.query || {}) {
139
- for (const header in headers || {}) {
140
- if (header.toLowerCase() === name.toLowerCase()) carried[header.toLowerCase()] = headers[header]
141
- }
164
+ for (const { header, found, value } of queryHeaderEntries(config.query, headers)) {
165
+ if (found) carried[header] = value
142
166
  }
143
167
  const maxAge = typeof config.maxAge === 'number' && config.maxAge >= 0 ? config.maxAge : EARLY_MAX_AGE
144
- return { carried, rpc: config.rpc === 'get', maxAge }
168
+ return { carried, rpc: config.rpc === 'get', maxAge, seeded: !!globalThis[key].seeded }
145
169
  }
146
170
 
147
171
  // The waiting answer for `key`, taken ONCE (a body can be read once; a second
@@ -149,12 +173,18 @@ export const openEarly = (config, headers) => {
149
173
  // the early request failed, was refused (a 4xx/5xx may be the early form's
150
174
  // own problem — a query-string key or a GET the backend does not serve — so
151
175
  // the call's own request decides), or is older than `maxAge`.
176
+ //
177
+ // A seed block's answer (readSeedBlock) resolves to the answer itself —
178
+ // `{ seeded: true, status, type, body }`, always an ok one — which the REST
179
+ // adapter reads without a Response: no body stream, so the data lands in the
180
+ // same task the app booted in, before the first frame paints.
152
181
  export const takeEarly = (key, maxAge = EARLY_MAX_AGE) => {
153
182
  const registry = globalThis[registryKey()]
154
183
  const entry = registry && registry.entries && registry.entries[key]
155
184
  if (!entry) return null
156
185
  delete registry.entries[key]
157
186
  if (!(Date.now() - entry.time <= maxAge)) return null
187
+ if (entry.seeded) return Promise.resolve(entry.seeded)
158
188
  return Promise.resolve(entry.answer).then(
159
189
  (res) => (res && res.ok && !res.bodyUsed ? res : null),
160
190
  () => null
@@ -170,25 +200,62 @@ export const dropEarly = () => {
170
200
 
171
201
  // --- Server-seeded answers (FW-BRENDER-HYDRATION-AND-CF-DEPLOY-PROVIDER-1) ---
172
202
  // A @symbo.ls/brender pre-render records the anonymous GETs its fetch
173
- // adapter made (url, Accept-Language, status, content type, body). The page
174
- // carries them as this script, which runs before the app and fills the same
175
- // registry a `fetch.early` pre-boot script fills — keyed with earlyKey, so
176
- // the REST adapter adopts each answer for the first call that asks for
177
- // exactly that request instead of asking the network again. Marked
178
- // `seeded`, so an adapter without `fetch.early` adopts them too (openEarly).
179
- // `entries`: [{ url, lang, status, type, body }]. JSON is embedded with `<`
180
- // escaped, so a body can never close the <script>.
203
+ // adapter made (url as sent, Accept-Language, status, content type, body).
204
+ // The page carries them, and they fill the same registry a `fetch.early`
205
+ // pre-boot script fills — keyed with earlyKey, so the REST adapter adopts
206
+ // each answer for the first call that sends exactly that request instead of
207
+ // asking the network again. Marked `seeded`, so an adapter without
208
+ // `fetch.early` adopts them too (openEarly).
209
+ // `entries`: [{ url, lang, status, type, body }].
210
+
211
+ // The recorded answers as the page carries them: `{ k: earlyKey, s: status,
212
+ // t: content type, b: body }`.
213
+ const seedList = (entries) => (Array.isArray(entries) ? entries : [])
214
+ .filter((e) => e && typeof e.url === 'string' && typeof e.body === 'string')
215
+ .map((e) => ({ k: earlyKey(e.url, e.lang || ''), s: e.status || 200, t: e.type || 'application/json', b: e.body }))
216
+
217
+ // The page's answers as a data block — `<script type="application/json"
218
+ // data-fetch-seed>` — which no Content-Security-Policy blocks: XMA's
219
+ // `script-src 'self'` (no 'unsafe-inline') refused the inline
220
+ // createEarlySeedScript below, so a pre-rendered page under it had no seed
221
+ // (XMA report, brender items 1 and 3). The REST adapter reads the block when
222
+ // it opens (readSeedBlock). JSON is embedded with utils jsonForScript, so a
223
+ // body can never close the block.
224
+ export const SEED_BLOCK = 'script[type="application/json"][data-fetch-seed]'
225
+
226
+ export const createEarlySeedBlock = (entries) => {
227
+ const list = seedList(entries)
228
+ if (!list.length) return ''
229
+ return '<script type="application/json" data-fetch-seed>' + jsonForScript(list) + '</script>'
230
+ }
231
+
232
+ // Fill the registry from the page's seed block — once: the block goes when
233
+ // read. An answer already waiting under the same key (a pre-boot script's)
234
+ // is kept. Each seeded answer is adoptable for EARLY_MAX_AGE from now.
235
+ export const readSeedBlock = () => {
236
+ const read = readJsonDataBlock(globalThis.document, SEED_BLOCK, { remove: true })
237
+ const list = read && read.value
238
+ if (!Array.isArray(list) || !list.length) return
239
+ const key = registryKey()
240
+ const registry = globalThis[key] || (globalThis[key] = { entries: {} })
241
+ if (!registry.entries) registry.entries = {}
242
+ registry.seeded = true
243
+ const time = Date.now()
244
+ for (const e of list) {
245
+ if (!e || typeof e.k !== 'string' || typeof e.b !== 'string' || registry.entries[e.k]) continue
246
+ registry.entries[e.k] = { time, seeded: { seeded: true, status: e.s || 200, type: e.t || 'application/json', body: e.b } }
247
+ }
248
+ }
249
+
250
+ // The same answers as an inline script (pages from brender up to 3.14.664;
251
+ // the platform's renderer still ships it). Kept for those: a page under a
252
+ // strict Content-Security-Policy needs createEarlySeedBlock.
181
253
  export const createEarlySeedScript = (entries) => {
182
- const list = (Array.isArray(entries) ? entries : []).filter((e) => e && typeof e.url === 'string' && typeof e.body === 'string')
254
+ const list = seedList(entries)
183
255
  if (!list.length) return ''
184
- const json = JSON.stringify(list.map((e) => ({
185
- k: earlyKey(e.url, e.lang || ''),
186
- s: e.status || 200,
187
- t: e.type || 'application/json',
188
- b: e.body
189
- }))).replace(/</g, '\\u003c').replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029')
256
+ const json = jsonForScript(list)
190
257
  return '<script>(function(){try{var k=Symbol.for(' + JSON.stringify(EARLY_KEY) + '),' +
191
- 'r=globalThis[k]||(globalThis[k]={time:Date.now(),entries:{}}),t=Date.now(),l=' + json + ';' +
258
+ 'r=globalThis[k]||(globalThis[k]={entries:{}}),t=Date.now(),l=' + json + ';' +
192
259
  'r.seeded=true;r.entries=r.entries||{};' +
193
260
  'for(var i=0;i<l.length;i++){var e=l[i];r.entries[e.k]={time:t,answer:Promise.resolve(new Response(e.b,{status:e.s,headers:{"content-type":e.t}}))}}' +
194
261
  '}catch(e){}})()</script>'
package/earlyScript.js CHANGED
@@ -8,7 +8,7 @@
8
8
  // before any module, so it carries everything itself: the adapter's address,
9
9
  // the configured headers it sends as query parameters, the language rule and
10
10
  // the declared requests as data, and the address helpers BY SOURCE — the REST
11
- // adapter's own buildUrl / selectQuery and early.js's earlyKey /
11
+ // adapter's own buildUrl / selectQuery / withQuery and early.js's earlyKey /
12
12
  // isScalarParams / matchEarlyRoute — so an early request has exactly the
13
13
  // address and identity the adapter computes for the call that adopts it.
14
14
  //
@@ -36,13 +36,9 @@
36
36
  // makes (they send none). `ctx` is { path, query (URLSearchParams), lang,
37
37
  // storage(key) → localStorage value or null }.
38
38
 
39
- import { buildUrl, selectQuery } from './adapters/rest.js'
40
- import { EARLY_KEY, earlyKey, isScalarParams, matchEarlyRoute, withReads } from './early.js'
41
-
42
- // polyglot's own default for where it keeps the chosen language
43
- // (@symbo.ls/polyglot STORAGE_KEY_LANG): the script must read the same slot
44
- // the app will, or its requests ask in another language than the app's.
45
- const POLYGLOT_LANG_KEY = 'smbls_lang'
39
+ import { polyglotLangStorageKey, polyglotFallbackLang } from '@symbo.ls/utils'
40
+ import { buildUrl, selectQuery, withQuery } from './adapters/rest.js'
41
+ import { EARLY_KEY, earlyKey, isScalarParams, matchEarlyRoute, withReads, queryHeaderEntries } from './early.js'
46
42
 
47
43
  // The fetch() options an early GET may copy from the adapter's — the ones
48
44
  // that change what comes back. Headers never: every header but the language
@@ -78,13 +74,16 @@ const requestsSource = (requests, where) => {
78
74
 
79
75
  // The language the app will ask in on its first view: polyglot's stored
80
76
  // choice, else the project's `state.lang`, else polyglot's default — the
81
- // order polyglot's initPolyglot resolves it in. No polyglot: `state.lang`, or
82
- // none (a declarative fetch then sends no Accept-Language either).
77
+ // slot and the order polyglot's initPolyglot uses, from the same utils
78
+ // helpers (polyglotLangStorageKey, polyglotFallbackLang): the script must
79
+ // read what the app will, or its requests ask in another language than the
80
+ // app's. No polyglot: `state.lang`, or none (a declarative fetch then sends
81
+ // no Accept-Language either).
83
82
  const languageRule = (project) => {
84
83
  const stateLang = project.state && typeof project.state.lang === 'string' ? project.state.lang : ''
85
84
  const polyglot = project.polyglot
86
85
  if (polyglot && typeof polyglot === 'object') {
87
- return { storage: polyglot.storageLangKey || POLYGLOT_LANG_KEY, fallback: stateLang || polyglot.defaultLang || 'en' }
86
+ return { storage: polyglotLangStorageKey(polyglot), fallback: polyglotFallbackLang(stateLang, polyglot) }
88
87
  }
89
88
  return { storage: '', fallback: stateLang }
90
89
  }
@@ -94,10 +93,9 @@ const languageRule = (project) => {
94
93
  // for: those GETs would go out without the key and be refused, every time.
95
94
  const queryParams = (mapping, headers) => {
96
95
  const params = {}
97
- for (const name in mapping || {}) {
98
- const header = Object.keys(headers || {}).find((key) => key.toLowerCase() === name.toLowerCase())
99
- if (!header) throw new Error(`@symbo.ls/fetch early.query: "${name}" is not one of the adapter's configured headers`)
100
- params[mapping[name]] = headers[header]
96
+ for (const { name, param, found, value } of queryHeaderEntries(mapping, headers)) {
97
+ if (!found) throw new Error(`@symbo.ls/fetch early.query: "${name}" is not one of the adapter's configured headers`)
98
+ params[param] = value
101
99
  }
102
100
  return params
103
101
  }
@@ -150,10 +148,11 @@ export const createEarlyScript = (project = {}) => {
150
148
  var g = globalThis
151
149
  var K = Symbol.for(${JSON.stringify(EARLY_KEY)})
152
150
  if (g[K] || typeof g.fetch !== 'function' || !g.location) return
153
- var registry = g[K] = { time: Date.now(), entries: {} }
151
+ var registry = g[K] = { entries: {} }
154
152
  var config = ${JSON.stringify(config)}
155
153
  var buildUrl = ${buildUrl.toString()}
156
154
  var selectQuery = ${selectQuery.toString()}
155
+ var withQuery = ${withQuery.toString()}
157
156
  var earlyKey = ${earlyKey.toString()}
158
157
  var isScalarParams = ${isScalarParams.toString()}
159
158
  var matchEarlyRoute = ${matchEarlyRoute.toString()}
@@ -188,11 +187,9 @@ export const createEarlyScript = (project = {}) => {
188
187
  var language = request.lang === false ? '' : lang
189
188
  var key = earlyKey(url, language)
190
189
  if (registry.entries[key]) continue
191
- var address = new URL(url)
192
- for (var name in config.query) address.searchParams.set(name, config.query[name])
193
190
  var options = Object.assign({}, config.fetchOptions, { method: 'GET' })
194
191
  if (language) options.headers = { 'Accept-Language': language }
195
- registry.entries[key] = { time: Date.now(), answer: g.fetch(address.toString(), options).catch(function () { return null }) }
192
+ registry.entries[key] = { time: Date.now(), answer: g.fetch(withQuery(url, config.query), options).catch(function () { return null }) }
196
193
  } catch (e) {}
197
194
  }
198
195
  })()