@symbo.ls/fetch 3.8.9 → 3.14.1

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,6 +1,14 @@
1
1
  'use strict'
2
2
 
3
- export const setup = async ({ url, key, projectId, createClient, ...options }) => {
3
+ // Cache Supabase clients by (url, key, authMode) so every `setup` call
4
+ // against the same project reuses a single GoTrueClient. Without this
5
+ // cache, each fresh adapter resolution (per db/fetch config, per query)
6
+ // spins up a new GoTrueClient on the same storage key — Supabase logs
7
+ // "Multiple GoTrueClient instances" warnings and auth state can drift.
8
+ const CLIENT_CACHE = new Map()
9
+ const clientCacheKey = (url, key, authMode) => `${url}|${key}|${authMode ? 'off' : 'on'}`
10
+
11
+ export const setup = async ({ url, key, projectId, createClient, auth, retry, adapter, ...supabaseOptions }) => {
4
12
  const supabaseUrl = url || (projectId && `https://${projectId}.supabase.co`)
5
13
  if (!supabaseUrl || !key) {
6
14
  throw new Error('@symbo.ls/fetch supabase: url (or projectId) and key are required')
@@ -10,8 +18,19 @@ export const setup = async ({ url, key, projectId, createClient, ...options }) =
10
18
  const mod = await import(/* webpackIgnore: true */ pkg)
11
19
  createClient = mod.createClient
12
20
  }
13
- const client = createClient(supabaseUrl, key, options)
14
- // Guard against no-op createClient (e.g. deserialized placeholder from SSR)
21
+ // Disable Supabase auto-auth when auth: false (prevents navigator.locks hang)
22
+ if (auth === false) {
23
+ supabaseOptions.auth = { autoRefreshToken: false, persistSession: false, detectSessionInUrl: false }
24
+ }
25
+ const cacheKey = clientCacheKey(supabaseUrl, key, auth === false)
26
+ let client = CLIENT_CACHE.get(cacheKey)
27
+ if (!client) {
28
+ // `createClient` may be sync (default supabase-js) OR async — the SDK's
29
+ // supabasePassthrough now returns a promise so it can lazy-resolve the
30
+ // optional peer dep. Awaiting works for both.
31
+ client = await createClient(supabaseUrl, key, supabaseOptions)
32
+ if (client?.auth) CLIENT_CACHE.set(cacheKey, client)
33
+ }
15
34
  if (!client || !client.auth) return null
16
35
  return supabaseAdapter(client)
17
36
  }
@@ -101,26 +120,42 @@ export const supabaseAdapter = (client) => ({
101
120
  },
102
121
 
103
122
  // CRUD
104
- select: async ({ from, select, params, ...modifiers }) => {
105
- let q = client.from(from).select(select || '*')
123
+ select: async ({ from, schema, select, params, headers, ...modifiers }) => {
124
+ const base = schema ? client.schema(schema) : client
125
+ let q = base.from(from).select(select || '*')
106
126
  q = applyFilters(q, params)
107
127
  q = applyModifiers(q, modifiers)
128
+ if (headers) {
129
+ try {
130
+ for (const k in headers) {
131
+ if (q.setHeader) q = q.setHeader(k, headers[k])
132
+ else if (client.rest) client.rest.headers[k] = headers[k]
133
+ }
134
+ } catch (e) {
135
+ console.warn('[supabase] setHeader failed:', e.message)
136
+ }
137
+ }
108
138
  return q
109
139
  },
110
140
 
111
141
  rpc: ({ from, params }) => client.rpc(from, params),
112
142
 
113
- insert: ({ from, data, select }) =>
114
- client.from(from).insert(data).select(select || '*'),
143
+ insert: ({ from, schema, data, select }) => {
144
+ const base = schema ? client.schema(schema) : client
145
+ return base.from(from).insert(data).select(select || '*')
146
+ },
115
147
 
116
- update: ({ from, data, params, select }) => {
117
- let q = client.from(from).update(data)
148
+ update: ({ from, schema, data, params, select }) => {
149
+ const base = schema ? client.schema(schema) : client
150
+ let q = base.from(from).update(data)
118
151
  q = applyFilters(q, params)
119
152
  return q.select(select || '*')
120
153
  },
121
154
 
122
- upsert: ({ from, data, select }) =>
123
- client.from(from).upsert(data).select(select || '*'),
155
+ upsert: ({ from, schema, data, select }) => {
156
+ const base = schema ? client.schema(schema) : client
157
+ return base.from(from).upsert(data).select(select || '*')
158
+ },
124
159
 
125
160
  delete: ({ from, params }) => {
126
161
  let q = client.from(from).delete()
@@ -0,0 +1 @@
1
+ "use strict";var h=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var O=(r,n)=>{for(var t in n)h(r,t,{get:n[t],enumerable:!0})},T=(r,n,t,s)=>{if(n&&typeof n=="object"||typeof n=="function")for(let l of v(n))!E.call(r,l)&&l!==t&&h(r,l,{get:()=>n[l],enumerable:!(s=x(n,l))||s.enumerable});return r};var w=r=>T(h({},"__esModule",{value:!0}),r);var j={};O(j,{localAdapter:()=>S,setup:()=>N});module.exports=w(j);const A=(r,n)=>{if(!n)return!0;for(const t in n){const s=n[t];if(s===null){if(r[t]!==null&&r[t]!==void 0)return!1}else if(Array.isArray(s)){if(!s.includes(r[t]))return!1}else if(typeof s=="object"){for(const l in s)if(l==="gt"&&!(r[t]>s[l])||l==="gte"&&!(r[t]>=s[l])||l==="lt"&&!(r[t]<s[l])||l==="lte"&&!(r[t]<=s[l])||l==="neq"&&r[t]===s[l]||l==="like"&&!String(r[t]).includes(s[l]))return!1}else if(r[t]!==s)return!1}return!0},I=(r,{order:n,limit:t,offset:s,single:l})=>{let a=[...r];if(n){const y=typeof n=="string"?n:n.by,p=n.asc!==!1;a.sort((b,g)=>b[y]<g[y]?p?-1:1:b[y]>g[y]?p?1:-1:0)}return s&&(a=a.slice(s)),t&&(a=a.slice(0,t)),l?{data:a[0]||null,error:null}:{data:a,error:null}},N=async({data:r,...n})=>S(r,n),S=(r={},n={})=>{const t={},s={},l=n.persist!==!1&&typeof localStorage<"u",a=n.prefix||"smbls_db_",y=e=>{if(!t[e])if(l)try{const u=localStorage.getItem(a+e);t[e]=u?JSON.parse(u):r[e]||[]}catch{t[e]=r[e]||[]}else t[e]=r[e]||[];return t[e]},p=e=>{if(l)try{localStorage.setItem(a+e,JSON.stringify(t[e]))}catch{}},b=(e,u,o,f)=>{const c=s[e];if(c)for(const i of c)i(o,f,{event:u,table:e})};let g=Date.now();return{name:"local",store:t,select:async({from:e,params:u,...o})=>{const c=y(e).filter(i=>A(i,u));return I(c,o)},insert:async({from:e,data:u})=>{const o=y(e),c=(Array.isArray(u)?u:[u]).map(i=>{const d={id:i.id||++g,...i};return o.push(d),d});p(e);for(const i of c)b(e,"INSERT",i,null);return{data:Array.isArray(u)?c:c[0],error:null}},update:async({from:e,data:u,params:o})=>{const f=y(e),c=[];for(let i=0;i<f.length;i++)if(A(f[i],o)){const d={...f[i]};Object.assign(f[i],u),c.push(f[i]),b(e,"UPDATE",f[i],d)}return p(e),{data:c,error:null}},delete:async({from:e,params:u})=>{const o=y(e),f=[];for(let c=o.length-1;c>=0;c--)A(o[c],u)&&(f.push(o[c]),o.splice(c,1),b(e,"DELETE",null,f[f.length-1]));return p(e),{data:f,error:null}},subscribe:({from:e,on:u},o)=>{s[e]||(s[e]=[]);const f=(c,i,d)=>{u&&u!=="*"&&u!==d.event||o(c,i,d)};return s[e].push(f),()=>{const c=s[e].indexOf(f);c>-1&&s[e].splice(c,1)}}}};
@@ -0,0 +1 @@
1
+ "use strict";var O=Object.defineProperty;var T=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var J=(a,r)=>{for(var e in r)O(a,e,{get:r[e],enumerable:!0})},j=(a,r,e,c)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of k(r))!P.call(a,s)&&s!==e&&O(a,s,{get:()=>r[s],enumerable:!(c=T(r,s))||c.enumerable});return a};var A=a=>j(O({},"__esModule",{value:!0}),a);var x={};J(x,{restAdapter:()=>w,setup:()=>N});module.exports=A(x);const E=(a,r,e)=>{const c=r&&!r.startsWith("/")?"/":"",s=new URL(`${a}${c}${r}`);if(e)for(const u in e){const d=e[u];d!=null&&s.searchParams.set(u,typeof d=="object"?JSON.stringify(d):d)}return s.toString()},p=async(a,r)=>{const e=await globalThis.fetch(a,r),c=e.headers.get("content-type"),s=c&&c.includes("json")?await e.json():await e.text();return{data:s,error:e.ok?null:s?.message||s?.error||e.statusText,status:e.status}},N=async({url:a,headers:r,auth:e,fetchOptions:c})=>{if(!a)throw new Error("@symbo.ls/fetch rest: url is required");return w(a,r,e,c)},w=(a,r={},e,c={})=>{let s=e?.token||null;const u=t=>{const n={...r,...t};return s&&(n.Authorization=`Bearer ${s}`),n},d=t=>({...u(t),"Content-Type":"application/json"}),h=t=>({...c,...t}),b=(t,n)=>{const i=n||a;if(t&&(t.startsWith("http://")||t.startsWith("https://")))return t;const l=t&&!t.startsWith("/")?"/":"";return`${i}${l}${t||""}`};return{name:"rest",setToken:t=>{s=t},getSession:async()=>{if(!e?.sessionUrl)return s?{token:s}:null;const t=await p(b(e.sessionUrl,e.baseUrl),h({headers:u()}));return t.error?null:t.data},signIn:async t=>{if(!e?.signInUrl)throw new Error("rest: auth.signInUrl not configured");const n=await p(b(e.signInUrl,e.baseUrl),h({method:"POST",headers:d(),body:JSON.stringify(t)}));return n.data?.token&&(s=n.data.token),n},signOut:async()=>(e?.signOutUrl&&await p(b(e.signOutUrl,e.baseUrl),h({method:"POST",headers:u()})),s=null,{error:null}),select:({from:t,params:n,select:i,limit:l,offset:U,order:o,single:$,headers:g,baseUrl:S})=>{const y={...n};return i&&(y.select=i),l&&(y.limit=l),U&&(y.offset=U),$&&(y.single=!0),o&&(typeof o=="string"?y.order=o:Array.isArray(o)?y.order=o.map(f=>`${f.by}:${f.asc===!1?"desc":"asc"}`).join(","):o.by&&(y.order=`${o.by}:${o.asc===!1?"desc":"asc"}`)),p(E(S||a,t||"",y),h({headers:u(g)}))},rpc:({from:t,params:n,headers:i,baseUrl:l})=>p(b(`rpc/${t}`,l),h({method:"POST",headers:d(i),body:JSON.stringify(n)})),insert:({from:t,data:n,headers:i,baseUrl:l})=>p(b(t,l),h({method:"POST",headers:d(i),body:JSON.stringify(n)})),update:({from:t,data:n,params:i,method:l,headers:U,baseUrl:o})=>{const $=i?.id||n?.id,g=$?`${t}/${$}`:t;return p(b(g,o),h({method:l||"PATCH",headers:d(U),body:JSON.stringify(n)}))},delete:({from:t,params:n,headers:i,baseUrl:l})=>{const U=n?.id,o=U?`${t}/${U}`:t;return p(b(o,l),h({method:"DELETE",headers:u(i)}))}}};
@@ -0,0 +1 @@
1
+ "use strict";var p=Object.defineProperty;var d=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var m=(s,e)=>{for(var t in e)p(s,t,{get:e[t],enumerable:!0})},k=(s,e,t,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of u(e))!w.call(s,n)&&n!==t&&p(s,n,{get:()=>e[n],enumerable:!(a=d(e,n))||a.enumerable});return s};var A=s=>k(p({},"__esModule",{value:!0}),s);var $={};m($,{setup:()=>U,supabaseAdapter:()=>h});module.exports=A($);const b=new Map,S=(s,e,t)=>`${s}|${e}|${t?"off":"on"}`,U=async({url:s,key:e,projectId:t,createClient:a,auth:n,retry:o,adapter:l,...r})=>{const i=s||t&&`https://${t}.supabase.co`;if(!i||!e)throw new Error("@symbo.ls/fetch supabase: url (or projectId) and key are required");a||(a=(await import("@supabase/supabase-js")).createClient),n===!1&&(r.auth={autoRefreshToken:!1,persistSession:!1,detectSessionInUrl:!1});const f=S(i,e,n===!1);let c=b.get(f);return c||(c=await a(i,e,r),c?.auth&&b.set(f,c)),!c||!c.auth?null:h(c)},g=(s,e)=>{if(!e)return s;for(const t in e){const a=e[t];if(a===null)s=s.is(t,null);else if(Array.isArray(a))s=s.in(t,a);else if(typeof a=="object")for(const n in a)n==="gt"?s=s.gt(t,a[n]):n==="gte"?s=s.gte(t,a[n]):n==="lt"?s=s.lt(t,a[n]):n==="lte"?s=s.lte(t,a[n]):n==="neq"?s=s.neq(t,a[n]):n==="like"?s=s.like(t,a[n]):n==="ilike"?s=s.ilike(t,a[n]):n==="in"?s=s.in(t,a[n]):n==="is"?s=s.is(t,a[n]):n==="contains"?s=s.contains(t,a[n]):n==="containedBy"?s=s.containedBy(t,a[n]):n==="textSearch"&&(s=s.textSearch(t,a[n]));else s=s.eq(t,a)}return s},I=(s,{limit:e,offset:t,order:a,single:n}={})=>{if(a)if(Array.isArray(a))for(const o of a){const l=typeof o=="string"?o:o.by;s=s.order(l,{ascending:o.asc!==!1})}else{const o=typeof a=="string"?a:a.by;s=s.order(o,{ascending:a.asc!==!1})}return e&&(s=s.limit(e)),t&&(s=s.range(t,t+(e||20)-1)),n&&(s=s.single()),s},h=s=>({name:"supabase",client:s,getSession:async()=>{const{data:e}=await s.auth.getSession();return e?.session||null},getUser:async()=>{const{data:e}=await s.auth.getUser();return e?.user||null},signIn:e=>e.provider?s.auth.signInWithOAuth({provider:e.provider}):e.token?s.auth.signInWithIdToken(e):s.auth.signInWithPassword(e),signUp:e=>s.auth.signUp(e),signOut:()=>s.auth.signOut(),onAuthStateChange:e=>{const{data:{subscription:t}}=s.auth.onAuthStateChange((a,n)=>e(a,n));return()=>t.unsubscribe()},select:async({from:e,schema:t,select:a,params:n,headers:o,...l})=>{let i=(t?s.schema(t):s).from(e).select(a||"*");if(i=g(i,n),i=I(i,l),o)try{for(const f in o)i.setHeader?i=i.setHeader(f,o[f]):s.rest&&(s.rest.headers[f]=o[f])}catch(f){console.warn("[supabase] setHeader failed:",f.message)}return i},rpc:({from:e,params:t})=>s.rpc(e,t),insert:({from:e,schema:t,data:a,select:n})=>(t?s.schema(t):s).from(e).insert(a).select(n||"*"),update:({from:e,schema:t,data:a,params:n,select:o})=>{let r=(t?s.schema(t):s).from(e).update(a);return r=g(r,n),r.select(o||"*")},upsert:({from:e,schema:t,data:a,select:n})=>(t?s.schema(t):s).from(e).upsert(a).select(n||"*"),delete:({from:e,params:t})=>{let a=s.from(e).delete();return a=g(a,t),a},subscribe:({from:e,params:t,on:a},n)=>{const o=a||"*",l=s.channel(`db-${e}-${Date.now()}`).on("postgres_changes",{event:o,schema:"public",table:e,...t?.id?{filter:`id=eq.${t.id}`}:{}},r=>n(r.new,r.old,r)).subscribe();return()=>s.removeChannel(l)},upload:({bucket:e,path:t,file:a,options:n})=>s.storage.from(e).upload(t,a,n),download:({bucket:e,path:t})=>s.storage.from(e).download(t),getPublicUrl:({bucket:e,path:t})=>s.storage.from(e).getPublicUrl(t)});
@@ -0,0 +1 @@
1
+ "use strict";var Pt=Object.create;var st=Object.defineProperty;var At=Object.getOwnPropertyDescriptor;var kt=Object.getOwnPropertyNames;var Mt=Object.getPrototypeOf,$t=Object.prototype.hasOwnProperty;var Nt=(e,t)=>{for(var s in t)st(e,s,{get:t[s],enumerable:!0})},St=(e,t,s,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of kt(t))!$t.call(e,n)&&n!==s&&st(e,n,{get:()=>t[n],enumerable:!(r=At(t,n))||r.enumerable});return e};var dt=(e,t,s)=>(s=e!=null?Pt(Mt(e)):{},St(t||!e||!e.__esModule?st(s,"default",{value:e,enumerable:!0}):s,e)),qt=e=>St(st({},"__esModule",{value:!0}),e);var Ht={};Nt(Ht,{createAdapter:()=>zt,default:()=>Gt,executeFetch:()=>yt,fetchPlugin:()=>Yt,initAdapterAuth:()=>nt,parseDuration:()=>B,queryClient:()=>Kt,registerAdapter:()=>Rt,resolveDb:()=>Et});module.exports=qt(Ht);var f=require("@symbo.ls/utils");const It=["select","rpc","insert","update","delete","subscribe"],wt={supabase:()=>import("./adapters/supabase.js"),rest:()=>import("./adapters/rest.js"),local:()=>import("./adapters/local.js")},Rt=(e,t)=>{wt[e]=t},zt=e=>{const t={};for(const s of It)e[s]&&(t[s]=e[s]);return t.name=e.name||"custom",t},Et=async e=>{if(!e)return null;if(typeof e.select=="function")return e;const{adapter:t,...s}=e,r=typeof t=="string"?t:typeof e=="string"?e:null;if(!r)return null;const n=wt[r];if(!n)throw new Error(`Unknown db adapter: "${r}". Use registerAdapter() for adapters with optional deps.`);return(await n()).setup(s)},B=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 s=parseInt(t[1]),r=t[2];return r==="ms"?s:r==="s"?s*1e3:r==="m"?s*6e4:r==="h"?s*36e5:0},m=new Map,O=new Map,K=new Map,Ot=(e,t)=>{if(e.cache?.key)return e.cache.key;const s=Qt(e.params),r=`${e.from}:${e.method}:${JSON.stringify(s||"")}`;return t?`${r}:${t}`:r},Y=e=>m.get(e)||null,G=(e,t,s)=>{const r=m.get(e),n={data:t,error:s,time:Date.now(),stale:!1};m.set(e,n);const a=O.get(e);if(a)for(const i of a)(0,f.isFunction)(i)&&i(n);return n},H=e=>{if(e){const t=m.get(e);t&&(t.stale=!0);return}for(const[,t]of m)t.stale=!0},rt=e=>{if(e){m.delete(e);return}m.clear()},te=(e,t)=>(O.has(e)||O.set(e,new Set),O.get(e).add(t),()=>{const s=O.get(e);s&&(s.delete(t),s.size===0&&O.delete(e))}),lt=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)}:(0,f.isString)(e)?{staleTime:B(e),gcTime:3e5}:{staleTime:B(e.stale||e.staleTime)||6e4,gcTime:B(e.gc||e.gcTime||e.expire)||3e5,key:e.key};let at=null;const Ut=()=>{at||(at=setInterval(()=>{const e=Date.now();for(const[t,s]of m){const r=O.get(t);!(r&&r.size>0)&&e-s.time>(s.gcTime||3e5)&&m.delete(t)}},3e4),at.unref&&at.unref())};Ut();const mt=3,ht=e=>Math.min(1e3*2**e,3e4),vt=e=>{const t=e.retry;return t===!1?{count:0}:t===!0||t===void 0?{count:mt,delay:ht}:typeof t=="number"?{count:t,delay:ht}:{count:t.count??mt,delay:(0,f.isFunction)(t.delay)?t.delay:typeof t.delay=="number"?()=>t.delay:ht}},Ft=async(e,t)=>{const{count:s,delay:r}=t;let n;for(let a=0;a<=s;a++)try{const i=await e();if(i?.error){if(n=i.error,a<s){const c=(0,f.isFunction)(r)?r(a,n):r;await new Promise(o=>setTimeout(o,c));continue}return i}return i}catch(i){if(n=i,a<s){const c=(0,f.isFunction)(r)?r(a,i):r;await new Promise(o=>setTimeout(o,c));continue}throw i}return{data:null,error:n}},j={focus:new Set,online:new Set};let bt=!1;const xt=()=>{if(bt||typeof window>"u")return;bt=!0;const e=()=>{for(const s of j.focus)s()},t=()=>{for(const s of j.online)s()};window.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&e()}),window.addEventListener("focus",e),window.addEventListener("online",t)},Dt=(e,t)=>{const s=t.__ref;if(e===!0)return{from:s.__state||t.key,method:"select",query:!0,on:"create"};if((0,f.isString)(e))return{from:e,method:"select",query:!0,on:"create"};if((0,f.isObject)(e)){const r={...e};return r.from=r.from||s.__state||t.key,r.method=r.method||"select",r.on=r.on||"create",r.query===void 0&&(r.query=!0),r}return null},Qt=e=>(!e||(0,f.isFunction)(e),e),it=(e,t)=>{const s=e?.state?.root||t?.state?.root;if(s?.lang)return s.lang},Ct=(e,t,s)=>{let r;if(!e)r=void 0;else if((0,f.isFunction)(e))r=e(t,t.state);else{r={};for(const n in e){const a=e[n];r[n]=(0,f.isFunction)(a)?a(t,t.state):a}}return r},nt=async(e,t)=>{if(!e||e.__authInitialized||(e.__authInitialized=!0,!e.getSession))return;const s=(r,n)=>{const a=t.state?.root;a?.update&&a.update({auth:{user:r,session:n,loading:!1}})};try{const r=await e.getSession();s(r?.user||null,r)}catch(r){console.warn("[fetch] Failed to restore auth session:",r.message)}e.onAuthStateChange&&e.onAuthStateChange((r,n)=>{s(n?.user||null,n)})},pt=async(e,t)=>{if((0,f.isFunction)(e.select))return e.auth!==!1&&await nt(e,t),e;if(e.__resolved)return e.auth!==!1&&await nt(e.__resolved,t),e.__resolved;if(e.__resolving)return e.__resolving;e.__resolving=Et(e);const s=await e.__resolving;return delete e.__resolving,s?(s.auth=e.auth,s.url=e.url,s.key=e.key,e.__resolved=s,t.fetch=s,e.auth!==!1&&await nt(s,t),s):null},F=(e,t,...s)=>{const r=(0,f.isFunction)(e[t])?e[t]:null;r&&r.call(e,...s,e,e.state,e.context)},T=(e,t,s,...r)=>{const n=e&&(0,f.isFunction)(e[s])?e[s]:null;n&&n.call(t,...r,t,t.state,t.context)},V=e=>{const t={},s=e.node;if(!s)return t;if(s.tagName==="FORM"){const n=new FormData(s);for(const[a,i]of n.entries())t[a]=i;return t}const r=s.querySelectorAll("input, textarea, select");for(let n=0;n<r.length;n++){const a=r[n],i=a.name||a.getAttribute("name");i&&(a.type==="checkbox"?t[i]=a.checked:a.type==="file"?t[i]=a.files:t[i]=a.value)}return t},U=(e,t,s,r={})=>{const n=s&&s.startsWith("~/"),a=n&&e.state?.root||e.state;if(a?.update)if(n){const i=s.slice(2);t&&typeof t=="object"&&!Array.isArray(t)&&i in t?a.update(t,{preventFetch:!0,...r}):a.update({[i]:t},{preventFetch:!0,...r})}else{const i=s?{[s]:t}:t;a.update(i,{preventFetch:!0,...r})}},k=(e,t)=>{const s=e?.__ref;s&&(s.__fetchStatus=t,s.__fetching=t.isFetching,s.__fetchError=t.error)},I=async(e,t,s,r={})=>{const n=s?.fetch;if(!n||!t||!t.__ref||e.enabled===!1||(0,f.isFunction)(e.enabled)&&!e.enabled(t,t.state)||e.skip===!0||(0,f.isFunction)(e.skip)&&e.skip(t,t.state))return;const a=await pt(n,s);if(!a||!t.__ref)return;const i=t.__ref,{from:c,method:o,query:y,params:b,cache:X,transform:D,single:ot,auth:Z,fields:N,as:R,limit:J,offset:tt,order:x,headers:q,baseUrl:_,page:g,cursor:v,getNextPageParam:Q,getPreviousPageParam:S,infinite:L,placeholderData:z,initialData:W,select:P,keepPreviousData:M}=e;let C=(0,f.isString)(P)?P:void 0;if(y&&(0,f.isFunction)(t.getQuery)){const l=t.getQuery(a.name||"paths");l&&(C=l.select||l.length&&l.join(",")||C)}const A=Ct(b,t,s),E=lt(X),et=vt(e),ft=it(t,s),Tt=ft?`:${ft}`:"",p=E?X?.key||`${c}:${o}:${JSON.stringify(A||"")}${L?":infinite":""}${g?":p"+JSON.stringify(g):""}${Tt}`:null;if(!i.__fetchInitialized&&p&&(i.__fetchInitialized=!0,W!==void 0)){const l=(0,f.isFunction)(W)?W():W;G(p,l,null)}if((o==="select"||o==="rpc")&&p&&E){const l=Y(p);if(l&&!l.error){const d=Date.now()-l.time,h=l.stale||d>(E.staleTime||0);let u=l.data;if((0,f.isFunction)(P)&&(u=P(u,t,t.state)),D&&(u=D(u,t,t.state)),U(t,u,R),!h){k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!0,error:null,status:"success",fetchStatus:"idle"});return}}else if(z!==void 0){const d=(0,f.isFunction)(z)?z(t,t.state):z;d!==void 0&&U(t,d,R)}}if(p&&K.has(p)&&!r.force){const l=K.get(p);try{const d=await Promise.race([l,new Promise((h,u)=>setTimeout(()=>u("DEDUP_TIMEOUT"),15e3))]);if(d?.data!==void 0){let h=d.data;(0,f.isFunction)(P)&&(h=P(h,t,t.state)),D&&(h=D(h,t,t.state)),U(t,h,R),F(t,"onFetchComplete",h),T(e,t,"onFetchComplete",h)}}catch{}return}const Lt=!Y(p)?.data;k(t,{isFetching:!0,isLoading:Lt,isStale:!1,isSuccess:!1,error:null,status:"pending",fetchStatus:"fetching"}),F(t,"onFetchStart"),T(e,t,"onFetchStart");const $=s?.analyze?.emitNetwork,ct=$?typeof performance<"u"?performance.now():Date.now():0;$&&$({phase:"start",mode:"query",from:c,method:o,cacheKey:p,lang:ft});const gt=Ft(async()=>{if(Z!==!1&&a.auth!==!1&&a.getSession){const u=await a.getSession();if(Z===!0&&!u){const w={message:"Not authenticated"};return k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:w,status:"error",fetchStatus:"idle"}),F(t,"onFetchError",w),T(e,t,"onFetchError",w),{data:null,error:w}}}const l=a[o];if(!(0,f.isFunction)(l))return{data:null,error:{message:`Method "${o}" not found on adapter`}};const d={from:c,select:C,params:A,single:ot,limit:J,offset:tt,order:x,headers:q,baseUrl:_},h=it(t,s);if(h&&(d.headers={...d.headers,"Accept-Language":h}),g!==void 0){if((0,f.isObject)(g))g.offset!==void 0&&(d.offset=g.offset),g.limit!==void 0&&(d.limit=g.limit),g.cursor!==void 0&&(d.params={...d.params,cursor:g.cursor});else if(typeof g=="number"){const u=e.pageSize||J||20;d.offset=(g-1)*u,d.limit=u}}if(v!==void 0&&(d.params={...d.params,cursor:v}),o==="insert"||o==="update"||o==="upsert"){let u;if(N===!0||e.on==="submit")u=V(t);else if((0,f.isArray)(N)){const w=V(t);u={};for(const ut of N)u[ut]=w[ut]}else t.state?.parse?u=t.state.parse():(0,f.isObject)(t.state)&&(u={...t.state});D&&(u=D(u,t,t.state)),d.data=u}if(o==="rpc"){let u=A;if(e.on==="submit"){const w=V(t);u=e.transformParams?e.transformParams(w,t,t.state):w}d.params=u}if(o==="subscribe"){const u=a.subscribe({from:c,params:A,on:e.subscribeOn},(w,ut,Xt)=>{U(t,w,R),F(t,"onFetchComplete",w),T(e,t,"onFetchComplete",w)});return i.__unsubscribe=u,{data:null,error:null,subscribed:!0}}return l(d)},et);p&&K.set(p,gt);try{const l=await gt;if(l?.subscribed)return;const{data:d,error:h}=l||{};if(h){p&&G(p,null,h),k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:h,status:"error",fetchStatus:"idle"}),F(t,"onFetchError",h),T(e,t,"onFetchError",h),$&&$({phase:"error",mode:"query",from:c,method:o,cacheKey:p,error:h.message||h,durationMs:(typeof performance<"u"?performance.now():Date.now())-ct});return}if(d!==void 0){if((o==="select"||o==="rpc")&&p){const w=G(p,d,null);w.gcTime=E?.gcTime||3e5}let u=d;L&&(0,f.isArray)(d)&&(i.__pages||(i.__pages=[]),r.direction==="previous"?i.__pages.unshift(d):i.__pages.push(d),u=i.__pages.flat(),Q&&(i.__nextPageParam=Q(d,i.__pages),i.__hasNextPage=i.__nextPageParam!=null),S&&(i.__prevPageParam=S(d,i.__pages),i.__hasPreviousPage=i.__prevPageParam!=null)),(0,f.isFunction)(P)&&(u=P(u,t,t.state)),D&&(u=D(u,t,t.state)),(!M||u!==void 0)&&U(t,u,R),k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!0,error:null,status:"success",fetchStatus:"idle"}),F(t,"onFetchComplete",u),T(e,t,"onFetchComplete",u),$&&$({phase:"success",mode:"query",ok:!0,from:c,method:o,cacheKey:p,durationMs:(typeof performance<"u"?performance.now():Date.now())-ct})}}catch(l){k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:l,status:"error",fetchStatus:"idle"}),F(t,"onFetchError",l),T(e,t,"onFetchError",l),$&&$({phase:"error",mode:"query",from:c,method:o,cacheKey:p,error:l.message||String(l),durationMs:(typeof performance<"u"?performance.now():Date.now())-ct})}finally{p&&K.delete(p)}},Wt=(e,t,s)=>{const r=e.on,n=t.__ref;n.__fetchListeners||(n.__fetchListeners=[]);const a=e.__runner||I;if(r==="submit"){const i=o=>{o.preventDefault(),a(e,t,s)},c=t.node;c&&(c.addEventListener("submit",i),n.__fetchListeners.push(()=>c.removeEventListener("submit",i)))}else if(r==="click"){const i=()=>a(e,t,s),c=t.node;c&&(c.addEventListener("click",i),n.__fetchListeners.push(()=>c.removeEventListener("click",i)))}else r==="stateChange"&&(n.__fetchOnStateChange=()=>a(e,t,s))},jt=(e,t,s)=>{const r=t.__ref;r.__fetchCleanups||(r.__fetchCleanups=[]),xt();const n=e.refetchInterval;if(n){const a=typeof n=="number"?n:B(n);if(a>0){const i=setInterval(()=>{(e.refetchIntervalInBackground||typeof document>"u"||document.visibilityState==="visible")&&I(e,t,s,{force:!0})},a);r.__fetchCleanups.push(()=>clearInterval(i))}}if(e.refetchOnWindowFocus!==!1){const a=()=>{const i=it(t,s),c=Ot(e,i),o=Y(c),y=lt(e.cache);(!o||o.stale||Date.now()-o.time>(y?.staleTime||0))&&I(e,t,s,{force:!0})};j.focus.add(a),r.__fetchCleanups.push(()=>j.focus.delete(a))}if(e.refetchOnReconnect!==!1){const a=()=>I(e,t,s,{force:!0});j.online.add(a),r.__fetchCleanups.push(()=>j.online.delete(a))}},Bt=(e,t,s)=>{if(!t.optimistic)return null;const r=e.__ref,n=t.as,a=e.state?.parse?e.state.parse():(0,f.isObject)(e.state)?{...e.state}:e.state;r.__optimisticSnapshot=a;const i=(0,f.isFunction)(t.optimistic)?t.optimistic(s,a,e):t.optimistic;return i!==void 0&&U(e,i,n),a},_t=(e,t)=>{const s=e.__ref,r=s.__optimisticSnapshot;r!==void 0&&(U(e,r,t.as),delete s.__optimisticSnapshot)},Jt=async(e,t,s)=>{const r=s?.fetch;if(!r)return;const n=await pt(r,s);if(!n)return;const a=t.__ref,{method:i,from:c,fields:o,transform:y,as:b,on:X,auth:D,headers:ot,baseUrl:Z,invalidates:N,optimistic:R,onMutate:J,onSuccess:tt,onError:x,onSettled:q}=e;let _;if(o===!0||X==="submit")_=V(t);else if((0,f.isArray)(o)){const S=V(t);_={};for(const L of o)_[L]=S[L]}else t.state?.parse?_=t.state.parse():(0,f.isObject)(t.state)&&(_={...t.state});y&&(_=y(_,t,t.state)),(0,f.isFunction)(J)&&J(_,t,t.state);const g=R?Bt(t,e,_):null;a.__fetching=!0,k(t,{isFetching:!0,isLoading:!1,isStale:!1,isSuccess:!1,error:null,status:"pending",fetchStatus:"fetching"}),F(t,"onFetchStart"),T(e,t,"onFetchStart");const v=s?.analyze?.emitNetwork,Q=v?typeof performance<"u"?performance.now():Date.now():0;v&&v({phase:"start",mode:"mutation",from:c,method:i});try{if(D!==!1&&n.auth!==!1&&n.getSession){const A=await n.getSession();if(D===!0&&!A){g!==void 0&&_t(t,e);const E={message:"Not authenticated"};F(t,"onFetchError",E),T(e,t,"onFetchError",E);return}}const S=n[i];if(!(0,f.isFunction)(S))return;const L={from:c,data:_,headers:ot,baseUrl:Z};e.params&&(L.params=Ct(e.params,t,s));const z=it(t,s);z&&(L.headers={...L.headers,"Accept-Language":z});const W=vt(e),P=await Ft(()=>S(L),W),{data:M,error:C}=P||{};if(C){g!==void 0&&_t(t,e),k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:C,status:"error",fetchStatus:"idle"}),F(t,"onFetchError",C),(0,f.isFunction)(x)&&x(C,_,t),(0,f.isFunction)(q)&&q(null,C,_,t),v&&v({phase:"error",mode:"mutation",from:c,method:i,error:C.message||String(C),durationMs:(typeof performance<"u"?performance.now():Date.now())-Q});return}if(delete a.__optimisticSnapshot,M!==void 0){const A=b?{[b]:M}:M;t.state?.update&&t.state.update(A,{preventFetch:!0})}if(k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!0,error:null,status:"success",fetchStatus:"idle"}),F(t,"onFetchComplete",M),T(e,t,"onFetchComplete",M),(0,f.isFunction)(tt)&&tt(M,_,t),(0,f.isFunction)(q)&&q(M,null,_,t),v&&v({phase:"success",mode:"mutation",ok:!0,from:c,method:i,durationMs:(typeof performance<"u"?performance.now():Date.now())-Q}),N){const A=(0,f.isArray)(N)?N:[N];for(const E of A)if(E===!0||E==="*")for(const[et]of m)et.startsWith(c+":")&&H(et);else H(E)}}catch(S){g!==void 0&&_t(t,e),k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:S,status:"error",fetchStatus:"idle"}),F(t,"onFetchError",S),T(e,t,"onFetchError",S),(0,f.isFunction)(x)&&x(S,_,t),(0,f.isFunction)(q)&&q(null,S,_,t),v&&v({phase:"error",mode:"mutation",from:c,method:i,error:S.message||String(S),durationMs:(typeof performance<"u"?performance.now():Date.now())-Q})}finally{a.__fetching=!1}},yt=(e,t,s,r)=>{if(!e||!r?.fetch)return;const a=(0,f.exec)(e,t);if(!a)return;const i=(0,f.isArray)(a)?a.map(o=>Dt((0,f.exec)(o,t),t)).filter(Boolean):[Dt(a,t)].filter(Boolean);for(const o of i){const y=o.method==="insert"||o.method==="update"||o.method==="upsert"||o.method==="delete",b=y?Jt:I;o.on==="create"?b(o,t,r):Promise.resolve().then(()=>{Wt({...o,__runner:b},t,r)}),!y&&o.on==="create"&&(o.refetchInterval||o.refetchOnWindowFocus!==!1||o.refetchOnReconnect!==!1)&&jt(o,t,r)}const c=t.__ref;c.refetch=o=>{for(const y of i)I(y,t,r,{force:!0,...o})},c.fetchNextPage=()=>{const o=i[0];if(!o||!o.infinite)return;const y=c.__nextPageParam;if(y==null)return;const b={...o,cursor:y};I(b,t,r,{direction:"next",force:!0})},c.fetchPreviousPage=()=>{const o=i[0];if(!o||!o.infinite)return;const y=c.__prevPageParam;if(y==null)return;const b={...o,cursor:y};I(b,t,r,{direction:"previous",force:!0})}},Kt={invalidateQueries:e=>{if(!e){H();return}if((0,f.isString)(e))for(const[t]of m)(t.startsWith(e)||t.includes(e))&&H(t);else if((0,f.isArray)(e)){const t=e.join(":");for(const[s]of m)s.includes(t)&&H(s)}},removeQueries:e=>{if(!e){rt();return}if((0,f.isString)(e))for(const[t]of m)(t.startsWith(e)||t.includes(e))&&rt(t)},getQueryData:e=>Y(e)?.data??void 0,setQueryData:(e,t)=>{const s=Y(e),r=(0,f.isFunction)(t)?t(s?.data):t;G(e,r,null)},prefetchQuery:async(e,t)=>{const s=t?.fetch;if(!s)return;const r=await pt(s,t);if(!r)return;const n=e.cache?.key||`${e.from}:${e.method||"select"}:${JSON.stringify(e.params||"")}`,a=r[e.method||"select"];if(!(0,f.isFunction)(a))return;const i=await a({from:e.from,params:e.params,limit:e.limit,offset:e.offset,order:e.order,single:e.single});if(i?.data!==void 0&&!i?.error){const c=G(n,i.data,null);c.gcTime=lt(e.cache)?.gcTime||3e5}return i},getCache:()=>m,clear:()=>rt(),dropNonLang:e=>{if(!e)return;const t=`:${e}`;for(const[s]of m)s.match(/:([a-z]{2,3})$/i)&&(s.endsWith(t)||rt(s))}},Yt={name:"fetch",create(e){const t=e.fetch;t&&(e.parent||K.clear(),yt(t,e,e.state,e.context))}};var Gt=yt;
@@ -0,0 +1 @@
1
+ const h=(l,i)=>{if(!i)return!0;for(const t in i){const r=i[t];if(r===null){if(l[t]!==null&&l[t]!==void 0)return!1}else if(Array.isArray(r)){if(!r.includes(l[t]))return!1}else if(typeof r=="object"){for(const f in r)if(f==="gt"&&!(l[t]>r[f])||f==="gte"&&!(l[t]>=r[f])||f==="lt"&&!(l[t]<r[f])||f==="lte"&&!(l[t]<=r[f])||f==="neq"&&l[t]===r[f]||f==="like"&&!String(l[t]).includes(r[f]))return!1}else if(l[t]!==r)return!1}return!0},A=(l,{order:i,limit:t,offset:r,single:f})=>{let a=[...l];if(i){const y=typeof i=="string"?i:i.by,p=i.asc!==!1;a.sort((b,g)=>b[y]<g[y]?p?-1:1:b[y]>g[y]?p?1:-1:0)}return r&&(a=a.slice(r)),t&&(a=a.slice(0,t)),f?{data:a[0]||null,error:null}:{data:a,error:null}},x=async({data:l,...i})=>S(l,i),S=(l={},i={})=>{const t={},r={},f=i.persist!==!1&&typeof localStorage<"u",a=i.prefix||"smbls_db_",y=e=>{if(!t[e])if(f)try{const n=localStorage.getItem(a+e);t[e]=n?JSON.parse(n):l[e]||[]}catch{t[e]=l[e]||[]}else t[e]=l[e]||[];return t[e]},p=e=>{if(f)try{localStorage.setItem(a+e,JSON.stringify(t[e]))}catch{}},b=(e,n,o,c)=>{const s=r[e];if(s)for(const u of s)u(o,c,{event:n,table:e})};let g=Date.now();return{name:"local",store:t,select:async({from:e,params:n,...o})=>{const s=y(e).filter(u=>h(u,n));return A(s,o)},insert:async({from:e,data:n})=>{const o=y(e),s=(Array.isArray(n)?n:[n]).map(u=>{const d={id:u.id||++g,...u};return o.push(d),d});p(e);for(const u of s)b(e,"INSERT",u,null);return{data:Array.isArray(n)?s:s[0],error:null}},update:async({from:e,data:n,params:o})=>{const c=y(e),s=[];for(let u=0;u<c.length;u++)if(h(c[u],o)){const d={...c[u]};Object.assign(c[u],n),s.push(c[u]),b(e,"UPDATE",c[u],d)}return p(e),{data:s,error:null}},delete:async({from:e,params:n})=>{const o=y(e),c=[];for(let s=o.length-1;s>=0;s--)h(o[s],n)&&(c.push(o[s]),o.splice(s,1),b(e,"DELETE",null,c[c.length-1]));return p(e),{data:c,error:null}},subscribe:({from:e,on:n},o)=>{r[e]||(r[e]=[]);const c=(s,u,d)=>{n&&n!=="*"&&n!==d.event||o(s,u,d)};return r[e].push(c),()=>{const s=r[e].indexOf(c);s>-1&&r[e].splice(s,1)}}}};export{S as localAdapter,x as setup};
@@ -0,0 +1 @@
1
+ const w=(o,c,e)=>{const d=c&&!c.startsWith("/")?"/":"",r=new URL(`${o}${d}${c}`);if(e)for(const u in e){const l=e[u];l!=null&&r.searchParams.set(u,typeof l=="object"?JSON.stringify(l):l)}return r.toString()},p=async(o,c)=>{const e=await globalThis.fetch(o,c),d=e.headers.get("content-type"),r=d&&d.includes("json")?await e.json():await e.text();return{data:r,error:e.ok?null:r?.message||r?.error||e.statusText,status:e.status}},k=async({url:o,headers:c,auth:e,fetchOptions:d})=>{if(!o)throw new Error("@symbo.ls/fetch rest: url is required");return S(o,c,e,d)},S=(o,c={},e,d={})=>{let r=e?.token||null;const u=t=>{const s={...c,...t};return r&&(s.Authorization=`Bearer ${r}`),s},l=t=>({...u(t),"Content-Type":"application/json"}),h=t=>({...d,...t}),b=(t,s)=>{const n=s||o;if(t&&(t.startsWith("http://")||t.startsWith("https://")))return t;const a=t&&!t.startsWith("/")?"/":"";return`${n}${a}${t||""}`};return{name:"rest",setToken:t=>{r=t},getSession:async()=>{if(!e?.sessionUrl)return r?{token:r}:null;const t=await p(b(e.sessionUrl,e.baseUrl),h({headers:u()}));return t.error?null:t.data},signIn:async t=>{if(!e?.signInUrl)throw new Error("rest: auth.signInUrl not configured");const s=await p(b(e.signInUrl,e.baseUrl),h({method:"POST",headers:l(),body:JSON.stringify(t)}));return s.data?.token&&(r=s.data.token),s},signOut:async()=>(e?.signOutUrl&&await p(b(e.signOutUrl,e.baseUrl),h({method:"POST",headers:u()})),r=null,{error:null}),select:({from:t,params:s,select:n,limit:a,offset:U,order:i,single:$,headers:g,baseUrl:f})=>{const y={...s};return n&&(y.select=n),a&&(y.limit=a),U&&(y.offset=U),$&&(y.single=!0),i&&(typeof i=="string"?y.order=i:Array.isArray(i)?y.order=i.map(O=>`${O.by}:${O.asc===!1?"desc":"asc"}`).join(","):i.by&&(y.order=`${i.by}:${i.asc===!1?"desc":"asc"}`)),p(w(f||o,t||"",y),h({headers:u(g)}))},rpc:({from:t,params:s,headers:n,baseUrl:a})=>p(b(`rpc/${t}`,a),h({method:"POST",headers:l(n),body:JSON.stringify(s)})),insert:({from:t,data:s,headers:n,baseUrl:a})=>p(b(t,a),h({method:"POST",headers:l(n),body:JSON.stringify(s)})),update:({from:t,data:s,params:n,method:a,headers:U,baseUrl:i})=>{const $=n?.id||s?.id,g=$?`${t}/${$}`:t;return p(b(g,i),h({method:a||"PATCH",headers:l(U),body:JSON.stringify(s)}))},delete:({from:t,params:s,headers:n,baseUrl:a})=>{const U=s?.id,i=U?`${t}/${U}`:t;return p(b(i,a),h({method:"DELETE",headers:u(n)}))}}};export{S as restAdapter,k as setup};
@@ -0,0 +1 @@
1
+ const g=new Map,b=(s,t,e)=>`${s}|${t}|${e?"off":"on"}`,m=async({url:s,key:t,projectId:e,createClient:a,auth:n,retry:o,adapter:l,...r})=>{const i=s||e&&`https://${e}.supabase.co`;if(!i||!t)throw new Error("@symbo.ls/fetch supabase: url (or projectId) and key are required");a||(a=(await import("@supabase/supabase-js")).createClient),n===!1&&(r.auth={autoRefreshToken:!1,persistSession:!1,detectSessionInUrl:!1});const f=b(i,t,n===!1);let c=g.get(f);return c||(c=await a(i,t,r),c?.auth&&g.set(f,c)),!c||!c.auth?null:d(c)},p=(s,t)=>{if(!t)return s;for(const e in t){const a=t[e];if(a===null)s=s.is(e,null);else if(Array.isArray(a))s=s.in(e,a);else if(typeof a=="object")for(const n in a)n==="gt"?s=s.gt(e,a[n]):n==="gte"?s=s.gte(e,a[n]):n==="lt"?s=s.lt(e,a[n]):n==="lte"?s=s.lte(e,a[n]):n==="neq"?s=s.neq(e,a[n]):n==="like"?s=s.like(e,a[n]):n==="ilike"?s=s.ilike(e,a[n]):n==="in"?s=s.in(e,a[n]):n==="is"?s=s.is(e,a[n]):n==="contains"?s=s.contains(e,a[n]):n==="containedBy"?s=s.containedBy(e,a[n]):n==="textSearch"&&(s=s.textSearch(e,a[n]));else s=s.eq(e,a)}return s},h=(s,{limit:t,offset:e,order:a,single:n}={})=>{if(a)if(Array.isArray(a))for(const o of a){const l=typeof o=="string"?o:o.by;s=s.order(l,{ascending:o.asc!==!1})}else{const o=typeof a=="string"?a:a.by;s=s.order(o,{ascending:a.asc!==!1})}return t&&(s=s.limit(t)),e&&(s=s.range(e,e+(t||20)-1)),n&&(s=s.single()),s},d=s=>({name:"supabase",client:s,getSession:async()=>{const{data:t}=await s.auth.getSession();return t?.session||null},getUser:async()=>{const{data:t}=await s.auth.getUser();return t?.user||null},signIn:t=>t.provider?s.auth.signInWithOAuth({provider:t.provider}):t.token?s.auth.signInWithIdToken(t):s.auth.signInWithPassword(t),signUp:t=>s.auth.signUp(t),signOut:()=>s.auth.signOut(),onAuthStateChange:t=>{const{data:{subscription:e}}=s.auth.onAuthStateChange((a,n)=>t(a,n));return()=>e.unsubscribe()},select:async({from:t,schema:e,select:a,params:n,headers:o,...l})=>{let i=(e?s.schema(e):s).from(t).select(a||"*");if(i=p(i,n),i=h(i,l),o)try{for(const f in o)i.setHeader?i=i.setHeader(f,o[f]):s.rest&&(s.rest.headers[f]=o[f])}catch(f){console.warn("[supabase] setHeader failed:",f.message)}return i},rpc:({from:t,params:e})=>s.rpc(t,e),insert:({from:t,schema:e,data:a,select:n})=>(e?s.schema(e):s).from(t).insert(a).select(n||"*"),update:({from:t,schema:e,data:a,params:n,select:o})=>{let r=(e?s.schema(e):s).from(t).update(a);return r=p(r,n),r.select(o||"*")},upsert:({from:t,schema:e,data:a,select:n})=>(e?s.schema(e):s).from(t).upsert(a).select(n||"*"),delete:({from:t,params:e})=>{let a=s.from(t).delete();return a=p(a,e),a},subscribe:({from:t,params:e,on:a},n)=>{const o=a||"*",l=s.channel(`db-${t}-${Date.now()}`).on("postgres_changes",{event:o,schema:"public",table:t,...e?.id?{filter:`id=eq.${e.id}`}:{}},r=>n(r.new,r.old,r)).subscribe();return()=>s.removeChannel(l)},upload:({bucket:t,path:e,file:a,options:n})=>s.storage.from(t).upload(e,a,n),download:({bucket:t,path:e})=>s.storage.from(t).download(e),getPublicUrl:({bucket:t,path:e})=>s.storage.from(t).getPublicUrl(e)});export{m as setup,d as supabaseAdapter};
@@ -0,0 +1 @@
1
+ import{isArray as j,isFunction as d,isObject as K,isString as Y,exec as St}from"@symbo.ls/utils";const Pt=["select","rpc","insert","update","delete","subscribe"],wt={supabase:()=>import("./adapters/supabase.js"),rest:()=>import("./adapters/rest.js"),local:()=>import("./adapters/local.js")},Qt=(e,t)=>{wt[e]=t},Wt=e=>{const t={};for(const s of Pt)e[s]&&(t[s]=e[s]);return t.name=e.name||"custom",t},At=async e=>{if(!e)return null;if(typeof e.select=="function")return e;const{adapter:t,...s}=e,r=typeof t=="string"?t:typeof e=="string"?e:null;if(!r)return null;const n=wt[r];if(!n)throw new Error(`Unknown db adapter: "${r}". Use registerAdapter() for adapters with optional deps.`);return(await n()).setup(s)},G=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 s=parseInt(t[1]),r=t[2];return r==="ms"?s:r==="s"?s*1e3:r==="m"?s*6e4:r==="h"?s*36e5:0},m=new Map,O=new Map,H=new Map,kt=(e,t)=>{if(e.cache?.key)return e.cache.key;const s=Nt(e.params),r=`${e.from}:${e.method}:${JSON.stringify(s||"")}`;return t?`${r}:${t}`:r},V=e=>m.get(e)||null,X=(e,t,s)=>{const r=m.get(e),n={data:t,error:s,time:Date.now(),stale:!1};m.set(e,n);const a=O.get(e);if(a)for(const i of a)d(i)&&i(n);return n},Z=e=>{if(e){const t=m.get(e);t&&(t.stale=!0);return}for(const[,t]of m)t.stale=!0},it=e=>{if(e){m.delete(e);return}m.clear()},jt=(e,t)=>(O.has(e)||O.set(e,new Set),O.get(e).add(t),()=>{const s=O.get(e);s&&(s.delete(t),s.size===0&&O.delete(e))}),lt=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)}:Y(e)?{staleTime:G(e),gcTime:3e5}:{staleTime:G(e.stale||e.staleTime)||6e4,gcTime:G(e.gc||e.gcTime||e.expire)||3e5,key:e.key};let nt=null;const Mt=()=>{nt||(nt=setInterval(()=>{const e=Date.now();for(const[t,s]of m){const r=O.get(t);!(r&&r.size>0)&&e-s.time>(s.gcTime||3e5)&&m.delete(t)}},3e4),nt.unref&&nt.unref())};Mt();const mt=3,ht=e=>Math.min(1e3*2**e,3e4),vt=e=>{const t=e.retry;return t===!1?{count:0}:t===!0||t===void 0?{count:mt,delay:ht}:typeof t=="number"?{count:t,delay:ht}:{count:t.count??mt,delay:d(t.delay)?t.delay:typeof t.delay=="number"?()=>t.delay:ht}},Ft=async(e,t)=>{const{count:s,delay:r}=t;let n;for(let a=0;a<=s;a++)try{const i=await e();if(i?.error){if(n=i.error,a<s){const f=d(r)?r(a,n):r;await new Promise(o=>setTimeout(o,f));continue}return i}return i}catch(i){if(n=i,a<s){const f=d(r)?r(a,i):r;await new Promise(o=>setTimeout(o,f));continue}throw i}return{data:null,error:n}},B={focus:new Set,online:new Set};let bt=!1;const $t=()=>{if(bt||typeof window>"u")return;bt=!0;const e=()=>{for(const s of B.focus)s()},t=()=>{for(const s of B.online)s()};window.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&e()}),window.addEventListener("focus",e),window.addEventListener("online",t)},Dt=(e,t)=>{const s=t.__ref;if(e===!0)return{from:s.__state||t.key,method:"select",query:!0,on:"create"};if(Y(e))return{from:e,method:"select",query:!0,on:"create"};if(K(e)){const r={...e};return r.from=r.from||s.__state||t.key,r.method=r.method||"select",r.on=r.on||"create",r.query===void 0&&(r.query=!0),r}return null},Nt=e=>(!e||d(e),e),ot=(e,t)=>{const s=e?.state?.root||t?.state?.root;if(s?.lang)return s.lang},Ct=(e,t,s)=>{let r;if(!e)r=void 0;else if(d(e))r=e(t,t.state);else{r={};for(const n in e){const a=e[n];r[n]=d(a)?a(t,t.state):a}}return r},pt=async(e,t)=>{if(!e||e.__authInitialized||(e.__authInitialized=!0,!e.getSession))return;const s=(r,n)=>{const a=t.state?.root;a?.update&&a.update({auth:{user:r,session:n,loading:!1}})};try{const r=await e.getSession();s(r?.user||null,r)}catch(r){console.warn("[fetch] Failed to restore auth session:",r.message)}e.onAuthStateChange&&e.onAuthStateChange((r,n)=>{s(n?.user||null,n)})},_t=async(e,t)=>{if(d(e.select))return e.auth!==!1&&await pt(e,t),e;if(e.__resolved)return e.auth!==!1&&await pt(e.__resolved,t),e.__resolved;if(e.__resolving)return e.__resolving;e.__resolving=At(e);const s=await e.__resolving;return delete e.__resolving,s?(s.auth=e.auth,s.url=e.url,s.key=e.key,e.__resolved=s,t.fetch=s,e.auth!==!1&&await pt(s,t),s):null},F=(e,t,...s)=>{const r=d(e[t])?e[t]:null;r&&r.call(e,...s,e,e.state,e.context)},T=(e,t,s,...r)=>{const n=e&&d(e[s])?e[s]:null;n&&n.call(t,...r,t,t.state,t.context)},tt=e=>{const t={},s=e.node;if(!s)return t;if(s.tagName==="FORM"){const n=new FormData(s);for(const[a,i]of n.entries())t[a]=i;return t}const r=s.querySelectorAll("input, textarea, select");for(let n=0;n<r.length;n++){const a=r[n],i=a.name||a.getAttribute("name");i&&(a.type==="checkbox"?t[i]=a.checked:a.type==="file"?t[i]=a.files:t[i]=a.value)}return t},U=(e,t,s,r={})=>{const n=s&&s.startsWith("~/"),a=n&&e.state?.root||e.state;if(a?.update)if(n){const i=s.slice(2);t&&typeof t=="object"&&!Array.isArray(t)&&i in t?a.update(t,{preventFetch:!0,...r}):a.update({[i]:t},{preventFetch:!0,...r})}else{const i=s?{[s]:t}:t;a.update(i,{preventFetch:!0,...r})}},k=(e,t)=>{const s=e?.__ref;s&&(s.__fetchStatus=t,s.__fetching=t.isFetching,s.__fetchError=t.error)},I=async(e,t,s,r={})=>{const n=s?.fetch;if(!n||!t||!t.__ref||e.enabled===!1||d(e.enabled)&&!e.enabled(t,t.state)||e.skip===!0||d(e.skip)&&e.skip(t,t.state))return;const a=await _t(n,s);if(!a||!t.__ref)return;const i=t.__ref,{from:f,method:o,query:y,params:b,cache:et,transform:D,single:ft,auth:st,fields:N,as:R,limit:J,offset:rt,order:x,headers:q,baseUrl:_,page:g,cursor:v,getNextPageParam:Q,getPreviousPageParam:S,infinite:L,placeholderData:z,initialData:W,select:P,keepPreviousData:M}=e;let C=Y(P)?P:void 0;if(y&&d(t.getQuery)){const l=t.getQuery(a.name||"paths");l&&(C=l.select||l.length&&l.join(",")||C)}const A=Ct(b,t,s),E=lt(et),at=vt(e),ct=ot(t,s),Tt=ct?`:${ct}`:"",p=E?et?.key||`${f}:${o}:${JSON.stringify(A||"")}${L?":infinite":""}${g?":p"+JSON.stringify(g):""}${Tt}`:null;if(!i.__fetchInitialized&&p&&(i.__fetchInitialized=!0,W!==void 0)){const l=d(W)?W():W;X(p,l,null)}if((o==="select"||o==="rpc")&&p&&E){const l=V(p);if(l&&!l.error){const u=Date.now()-l.time,h=l.stale||u>(E.staleTime||0);let c=l.data;if(d(P)&&(c=P(c,t,t.state)),D&&(c=D(c,t,t.state)),U(t,c,R),!h){k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!0,error:null,status:"success",fetchStatus:"idle"});return}}else if(z!==void 0){const u=d(z)?z(t,t.state):z;u!==void 0&&U(t,u,R)}}if(p&&H.has(p)&&!r.force){const l=H.get(p);try{const u=await Promise.race([l,new Promise((h,c)=>setTimeout(()=>c("DEDUP_TIMEOUT"),15e3))]);if(u?.data!==void 0){let h=u.data;d(P)&&(h=P(h,t,t.state)),D&&(h=D(h,t,t.state)),U(t,h,R),F(t,"onFetchComplete",h),T(e,t,"onFetchComplete",h)}}catch{}return}const Lt=!V(p)?.data;k(t,{isFetching:!0,isLoading:Lt,isStale:!1,isSuccess:!1,error:null,status:"pending",fetchStatus:"fetching"}),F(t,"onFetchStart"),T(e,t,"onFetchStart");const $=s?.analyze?.emitNetwork,ut=$?typeof performance<"u"?performance.now():Date.now():0;$&&$({phase:"start",mode:"query",from:f,method:o,cacheKey:p,lang:ct});const gt=Ft(async()=>{if(st!==!1&&a.auth!==!1&&a.getSession){const c=await a.getSession();if(st===!0&&!c){const w={message:"Not authenticated"};return k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:w,status:"error",fetchStatus:"idle"}),F(t,"onFetchError",w),T(e,t,"onFetchError",w),{data:null,error:w}}}const l=a[o];if(!d(l))return{data:null,error:{message:`Method "${o}" not found on adapter`}};const u={from:f,select:C,params:A,single:ft,limit:J,offset:rt,order:x,headers:q,baseUrl:_},h=ot(t,s);if(h&&(u.headers={...u.headers,"Accept-Language":h}),g!==void 0){if(K(g))g.offset!==void 0&&(u.offset=g.offset),g.limit!==void 0&&(u.limit=g.limit),g.cursor!==void 0&&(u.params={...u.params,cursor:g.cursor});else if(typeof g=="number"){const c=e.pageSize||J||20;u.offset=(g-1)*c,u.limit=c}}if(v!==void 0&&(u.params={...u.params,cursor:v}),o==="insert"||o==="update"||o==="upsert"){let c;if(N===!0||e.on==="submit")c=tt(t);else if(j(N)){const w=tt(t);c={};for(const dt of N)c[dt]=w[dt]}else t.state?.parse?c=t.state.parse():K(t.state)&&(c={...t.state});D&&(c=D(c,t,t.state)),u.data=c}if(o==="rpc"){let c=A;if(e.on==="submit"){const w=tt(t);c=e.transformParams?e.transformParams(w,t,t.state):w}u.params=c}if(o==="subscribe"){const c=a.subscribe({from:f,params:A,on:e.subscribeOn},(w,dt,Ut)=>{U(t,w,R),F(t,"onFetchComplete",w),T(e,t,"onFetchComplete",w)});return i.__unsubscribe=c,{data:null,error:null,subscribed:!0}}return l(u)},at);p&&H.set(p,gt);try{const l=await gt;if(l?.subscribed)return;const{data:u,error:h}=l||{};if(h){p&&X(p,null,h),k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:h,status:"error",fetchStatus:"idle"}),F(t,"onFetchError",h),T(e,t,"onFetchError",h),$&&$({phase:"error",mode:"query",from:f,method:o,cacheKey:p,error:h.message||h,durationMs:(typeof performance<"u"?performance.now():Date.now())-ut});return}if(u!==void 0){if((o==="select"||o==="rpc")&&p){const w=X(p,u,null);w.gcTime=E?.gcTime||3e5}let c=u;L&&j(u)&&(i.__pages||(i.__pages=[]),r.direction==="previous"?i.__pages.unshift(u):i.__pages.push(u),c=i.__pages.flat(),Q&&(i.__nextPageParam=Q(u,i.__pages),i.__hasNextPage=i.__nextPageParam!=null),S&&(i.__prevPageParam=S(u,i.__pages),i.__hasPreviousPage=i.__prevPageParam!=null)),d(P)&&(c=P(c,t,t.state)),D&&(c=D(c,t,t.state)),(!M||c!==void 0)&&U(t,c,R),k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!0,error:null,status:"success",fetchStatus:"idle"}),F(t,"onFetchComplete",c),T(e,t,"onFetchComplete",c),$&&$({phase:"success",mode:"query",ok:!0,from:f,method:o,cacheKey:p,durationMs:(typeof performance<"u"?performance.now():Date.now())-ut})}}catch(l){k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:l,status:"error",fetchStatus:"idle"}),F(t,"onFetchError",l),T(e,t,"onFetchError",l),$&&$({phase:"error",mode:"query",from:f,method:o,cacheKey:p,error:l.message||String(l),durationMs:(typeof performance<"u"?performance.now():Date.now())-ut})}finally{p&&H.delete(p)}},qt=(e,t,s)=>{const r=e.on,n=t.__ref;n.__fetchListeners||(n.__fetchListeners=[]);const a=e.__runner||I;if(r==="submit"){const i=o=>{o.preventDefault(),a(e,t,s)},f=t.node;f&&(f.addEventListener("submit",i),n.__fetchListeners.push(()=>f.removeEventListener("submit",i)))}else if(r==="click"){const i=()=>a(e,t,s),f=t.node;f&&(f.addEventListener("click",i),n.__fetchListeners.push(()=>f.removeEventListener("click",i)))}else r==="stateChange"&&(n.__fetchOnStateChange=()=>a(e,t,s))},It=(e,t,s)=>{const r=t.__ref;r.__fetchCleanups||(r.__fetchCleanups=[]),$t();const n=e.refetchInterval;if(n){const a=typeof n=="number"?n:G(n);if(a>0){const i=setInterval(()=>{(e.refetchIntervalInBackground||typeof document>"u"||document.visibilityState==="visible")&&I(e,t,s,{force:!0})},a);r.__fetchCleanups.push(()=>clearInterval(i))}}if(e.refetchOnWindowFocus!==!1){const a=()=>{const i=ot(t,s),f=kt(e,i),o=V(f),y=lt(e.cache);(!o||o.stale||Date.now()-o.time>(y?.staleTime||0))&&I(e,t,s,{force:!0})};B.focus.add(a),r.__fetchCleanups.push(()=>B.focus.delete(a))}if(e.refetchOnReconnect!==!1){const a=()=>I(e,t,s,{force:!0});B.online.add(a),r.__fetchCleanups.push(()=>B.online.delete(a))}},Rt=(e,t,s)=>{if(!t.optimistic)return null;const r=e.__ref,n=t.as,a=e.state?.parse?e.state.parse():K(e.state)?{...e.state}:e.state;r.__optimisticSnapshot=a;const i=d(t.optimistic)?t.optimistic(s,a,e):t.optimistic;return i!==void 0&&U(e,i,n),a},yt=(e,t)=>{const s=e.__ref,r=s.__optimisticSnapshot;r!==void 0&&(U(e,r,t.as),delete s.__optimisticSnapshot)},zt=async(e,t,s)=>{const r=s?.fetch;if(!r)return;const n=await _t(r,s);if(!n)return;const a=t.__ref,{method:i,from:f,fields:o,transform:y,as:b,on:et,auth:D,headers:ft,baseUrl:st,invalidates:N,optimistic:R,onMutate:J,onSuccess:rt,onError:x,onSettled:q}=e;let _;if(o===!0||et==="submit")_=tt(t);else if(j(o)){const S=tt(t);_={};for(const L of o)_[L]=S[L]}else t.state?.parse?_=t.state.parse():K(t.state)&&(_={...t.state});y&&(_=y(_,t,t.state)),d(J)&&J(_,t,t.state);const g=R?Rt(t,e,_):null;a.__fetching=!0,k(t,{isFetching:!0,isLoading:!1,isStale:!1,isSuccess:!1,error:null,status:"pending",fetchStatus:"fetching"}),F(t,"onFetchStart"),T(e,t,"onFetchStart");const v=s?.analyze?.emitNetwork,Q=v?typeof performance<"u"?performance.now():Date.now():0;v&&v({phase:"start",mode:"mutation",from:f,method:i});try{if(D!==!1&&n.auth!==!1&&n.getSession){const A=await n.getSession();if(D===!0&&!A){g!==void 0&&yt(t,e);const E={message:"Not authenticated"};F(t,"onFetchError",E),T(e,t,"onFetchError",E);return}}const S=n[i];if(!d(S))return;const L={from:f,data:_,headers:ft,baseUrl:st};e.params&&(L.params=Ct(e.params,t,s));const z=ot(t,s);z&&(L.headers={...L.headers,"Accept-Language":z});const W=vt(e),P=await Ft(()=>S(L),W),{data:M,error:C}=P||{};if(C){g!==void 0&&yt(t,e),k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:C,status:"error",fetchStatus:"idle"}),F(t,"onFetchError",C),d(x)&&x(C,_,t),d(q)&&q(null,C,_,t),v&&v({phase:"error",mode:"mutation",from:f,method:i,error:C.message||String(C),durationMs:(typeof performance<"u"?performance.now():Date.now())-Q});return}if(delete a.__optimisticSnapshot,M!==void 0){const A=b?{[b]:M}:M;t.state?.update&&t.state.update(A,{preventFetch:!0})}if(k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!0,error:null,status:"success",fetchStatus:"idle"}),F(t,"onFetchComplete",M),T(e,t,"onFetchComplete",M),d(rt)&&rt(M,_,t),d(q)&&q(M,null,_,t),v&&v({phase:"success",mode:"mutation",ok:!0,from:f,method:i,durationMs:(typeof performance<"u"?performance.now():Date.now())-Q}),N){const A=j(N)?N:[N];for(const E of A)if(E===!0||E==="*")for(const[at]of m)at.startsWith(f+":")&&Z(at);else Z(E)}}catch(S){g!==void 0&&yt(t,e),k(t,{isFetching:!1,isLoading:!1,isStale:!1,isSuccess:!1,error:S,status:"error",fetchStatus:"idle"}),F(t,"onFetchError",S),T(e,t,"onFetchError",S),d(x)&&x(S,_,t),d(q)&&q(null,S,_,t),v&&v({phase:"error",mode:"mutation",from:f,method:i,error:S.message||String(S),durationMs:(typeof performance<"u"?performance.now():Date.now())-Q})}finally{a.__fetching=!1}},Et=(e,t,s,r)=>{if(!e||!r?.fetch)return;const a=St(e,t);if(!a)return;const i=j(a)?a.map(o=>Dt(St(o,t),t)).filter(Boolean):[Dt(a,t)].filter(Boolean);for(const o of i){const y=o.method==="insert"||o.method==="update"||o.method==="upsert"||o.method==="delete",b=y?zt:I;o.on==="create"?b(o,t,r):Promise.resolve().then(()=>{qt({...o,__runner:b},t,r)}),!y&&o.on==="create"&&(o.refetchInterval||o.refetchOnWindowFocus!==!1||o.refetchOnReconnect!==!1)&&It(o,t,r)}const f=t.__ref;f.refetch=o=>{for(const y of i)I(y,t,r,{force:!0,...o})},f.fetchNextPage=()=>{const o=i[0];if(!o||!o.infinite)return;const y=f.__nextPageParam;if(y==null)return;const b={...o,cursor:y};I(b,t,r,{direction:"next",force:!0})},f.fetchPreviousPage=()=>{const o=i[0];if(!o||!o.infinite)return;const y=f.__prevPageParam;if(y==null)return;const b={...o,cursor:y};I(b,t,r,{direction:"previous",force:!0})}},Bt={invalidateQueries:e=>{if(!e){Z();return}if(Y(e))for(const[t]of m)(t.startsWith(e)||t.includes(e))&&Z(t);else if(j(e)){const t=e.join(":");for(const[s]of m)s.includes(t)&&Z(s)}},removeQueries:e=>{if(!e){it();return}if(Y(e))for(const[t]of m)(t.startsWith(e)||t.includes(e))&&it(t)},getQueryData:e=>V(e)?.data??void 0,setQueryData:(e,t)=>{const s=V(e),r=d(t)?t(s?.data):t;X(e,r,null)},prefetchQuery:async(e,t)=>{const s=t?.fetch;if(!s)return;const r=await _t(s,t);if(!r)return;const n=e.cache?.key||`${e.from}:${e.method||"select"}:${JSON.stringify(e.params||"")}`,a=r[e.method||"select"];if(!d(a))return;const i=await a({from:e.from,params:e.params,limit:e.limit,offset:e.offset,order:e.order,single:e.single});if(i?.data!==void 0&&!i?.error){const f=X(n,i.data,null);f.gcTime=lt(e.cache)?.gcTime||3e5}return i},getCache:()=>m,clear:()=>it(),dropNonLang:e=>{if(!e)return;const t=`:${e}`;for(const[s]of m)s.match(/:([a-z]{2,3})$/i)&&(s.endsWith(t)||it(s))}},Jt={name:"fetch",create(e){const t=e.fetch;t&&(e.parent||H.clear(),Et(t,e,e.state,e.context))}};var Kt=Et;export{Wt as createAdapter,Kt as default,Et as executeFetch,Jt as fetchPlugin,pt as initAdapterAuth,G as parseDuration,Bt as queryClient,Qt as registerAdapter,At as resolveDb};
package/package.json CHANGED
@@ -1,31 +1,46 @@
1
1
  {
2
2
  "name": "@symbo.ls/fetch",
3
- "version": "3.8.9",
3
+ "version": "3.14.1",
4
4
  "license": "CC-BY-NC-4.0",
5
5
  "type": "module",
6
- "module": "./index.js",
7
- "main": "./index.js",
6
+ "module": "./dist/esm/index.js",
7
+ "main": "./dist/cjs/index.cjs",
8
8
  "exports": {
9
9
  ".": {
10
- "import": "./index.js"
10
+ "development": "./index.js",
11
+ "import": "./dist/esm/index.js",
12
+ "require": "./dist/cjs/index.cjs",
13
+ "default": "./dist/esm/index.js"
11
14
  },
15
+ "./package.json": "./package.json",
12
16
  "./supabase": {
13
- "import": "./adapters/supabase.js"
17
+ "development": "./supabase.js",
18
+ "import": "./dist/esm/supabase.js",
19
+ "require": "./dist/cjs/supabase.cjs",
20
+ "default": "./dist/esm/supabase.js"
14
21
  },
15
22
  "./rest": {
16
- "import": "./adapters/rest.js"
23
+ "development": "./rest.js",
24
+ "import": "./dist/esm/rest.js",
25
+ "require": "./dist/cjs/rest.cjs",
26
+ "default": "./dist/esm/rest.js"
17
27
  },
18
28
  "./local": {
19
- "import": "./adapters/local.js"
29
+ "development": "./local.js",
30
+ "import": "./dist/esm/local.js",
31
+ "require": "./dist/cjs/local.cjs",
32
+ "default": "./dist/esm/local.js"
20
33
  }
21
34
  },
22
35
  "source": "index.js",
23
36
  "files": [
24
- "*.js",
37
+ "dist",
38
+ "*.md",
39
+ "LICENSE",
25
40
  "adapters"
26
41
  ],
27
42
  "dependencies": {
28
- "@domql/utils": "^3.8.9"
43
+ "@symbo.ls/utils": "^3.14.0"
29
44
  },
30
45
  "peerDependencies": {
31
46
  "@supabase/supabase-js": "^2.0.0"
@@ -37,6 +52,12 @@
37
52
  },
38
53
  "sideEffects": false,
39
54
  "publishConfig": {
40
- "access": "public"
55
+ "access": "restricted"
56
+ },
57
+ "scripts": {
58
+ "build:esm": "esbuild $(find . -type f -name \"*.js\" -not -path \"*/dist/*\" -not -path \"*/node_modules/*\" -not -path \"*/__tests__/*\" -not -path \"*/test/*\" -not -name \"*.test.js\") --target=es2020 --format=esm --outbase=. --outdir=dist/esm --minify --log-level=warning",
59
+ "build:cjs": "esbuild $(find . -type f -name \"*.js\" -not -path \"*/dist/*\" -not -path \"*/node_modules/*\" -not -path \"*/__tests__/*\" -not -path \"*/test/*\" -not -name \"*.test.js\") --target=node18 --format=cjs --outbase=. --outdir=dist/cjs --out-extension:.js=.cjs --minify --log-level=warning",
60
+ "build": "rimraf dist && npm run build:esm && npm run build:cjs",
61
+ "prepublishOnly": "npm run build"
41
62
  }
42
63
  }
package/index.js DELETED
@@ -1,1020 +0,0 @@
1
- 'use strict'
2
-
3
- import { isArray, isFunction, isObject, isString, exec } from '@domql/utils'
4
-
5
- // --- Adapter resolution ---
6
-
7
- const ADAPTER_METHODS = ['select', 'rpc', 'insert', 'update', 'delete', 'subscribe']
8
-
9
- const BUILTIN_ADAPTERS = {
10
- supabase: () => import('./adapters/supabase.js'),
11
- rest: () => import('./adapters/rest.js'),
12
- local: () => import('./adapters/local.js')
13
- }
14
-
15
- export const registerAdapter = (name, loader) => {
16
- BUILTIN_ADAPTERS[name] = loader
17
- }
18
-
19
- export const createAdapter = (config) => {
20
- const adapter = {}
21
- for (const method of ADAPTER_METHODS) {
22
- if (config[method]) adapter[method] = config[method]
23
- }
24
- adapter.name = config.name || 'custom'
25
- return adapter
26
- }
27
-
28
- export const resolveDb = async (config) => {
29
- if (!config) return null
30
- if (typeof config.select === 'function') return config
31
-
32
- const { adapter, ...options } = config
33
- const name = typeof adapter === 'string' ? adapter : typeof config === 'string' ? config : null
34
- if (!name) return null
35
-
36
- const loader = BUILTIN_ADAPTERS[name]
37
- if (!loader) throw new Error(`Unknown db adapter: "${name}". Use registerAdapter() for adapters with optional deps.`)
38
-
39
- const mod = await loader()
40
- return mod.setup(options)
41
- }
42
-
43
- // --- Duration parsing ---
44
-
45
- const parseDuration = (val) => {
46
- if (!val) return 0
47
- if (typeof val === 'number') return val
48
- const match = val.match(/^(\d+)(ms|s|m|h)$/)
49
- if (!match) return 0
50
- const n = parseInt(match[1])
51
- const unit = match[2]
52
- if (unit === 'ms') return n
53
- if (unit === 's') return n * 1000
54
- if (unit === 'm') return n * 60000
55
- if (unit === 'h') return n * 3600000
56
- return 0
57
- }
58
-
59
- // --- Query cache ---
60
-
61
- const cacheStore = new Map()
62
- const querySubscribers = new Map() // key -> Set of elements
63
- const activeQueries = new Map() // key -> Promise (deduplication)
64
-
65
- const buildCacheKey = (config) => {
66
- if (config.cache?.key) return config.cache.key
67
- const params = resolveParamsSync(config.params)
68
- return `${config.from}:${config.method}:${JSON.stringify(params || '')}`
69
- }
70
-
71
- const getCacheEntry = (key) => cacheStore.get(key) || null
72
-
73
- const setCacheEntry = (key, data, error) => {
74
- const existing = cacheStore.get(key)
75
- const entry = {
76
- data,
77
- error,
78
- time: Date.now(),
79
- stale: false
80
- }
81
- cacheStore.set(key, entry)
82
-
83
- // Notify all subscribers of this query
84
- const subs = querySubscribers.get(key)
85
- if (subs) {
86
- for (const sub of subs) {
87
- if (isFunction(sub)) sub(entry)
88
- }
89
- }
90
-
91
- return entry
92
- }
93
-
94
- const invalidateCache = (key) => {
95
- if (key) {
96
- const entry = cacheStore.get(key)
97
- if (entry) entry.stale = true
98
- return
99
- }
100
- // Invalidate all
101
- for (const [, entry] of cacheStore) {
102
- entry.stale = true
103
- }
104
- }
105
-
106
- const removeCache = (key) => {
107
- if (key) {
108
- cacheStore.delete(key)
109
- return
110
- }
111
- cacheStore.clear()
112
- }
113
-
114
- const subscribeToCacheKey = (key, callback) => {
115
- if (!querySubscribers.has(key)) querySubscribers.set(key, new Set())
116
- querySubscribers.get(key).add(callback)
117
- return () => {
118
- const subs = querySubscribers.get(key)
119
- if (subs) {
120
- subs.delete(callback)
121
- if (subs.size === 0) querySubscribers.delete(key)
122
- }
123
- }
124
- }
125
-
126
- // --- Cache config resolution ---
127
-
128
- const parseCacheConfig = (cache) => {
129
- if (!cache && cache !== false) return { staleTime: 60000, gcTime: 300000 }
130
- if (cache === false) return null
131
- if (cache === true) return { staleTime: 60000, gcTime: 300000 }
132
- if (typeof cache === 'number') return { staleTime: cache, gcTime: Math.max(cache * 5, 300000) }
133
- if (isString(cache)) return { staleTime: parseDuration(cache), gcTime: 300000 }
134
- return {
135
- staleTime: parseDuration(cache.stale || cache.staleTime) || 60000,
136
- gcTime: parseDuration(cache.gc || cache.gcTime || cache.expire) || 300000,
137
- key: cache.key
138
- }
139
- }
140
-
141
- // --- GC ---
142
-
143
- let gcTimer = null
144
-
145
- const startGC = () => {
146
- if (gcTimer) return
147
- gcTimer = setInterval(() => {
148
- const now = Date.now()
149
- for (const [key, entry] of cacheStore) {
150
- const subs = querySubscribers.get(key)
151
- const hasSubscribers = subs && subs.size > 0
152
- if (!hasSubscribers && (now - entry.time) > (entry.gcTime || 300000)) {
153
- cacheStore.delete(key)
154
- }
155
- }
156
- }, 30000)
157
- if (gcTimer.unref) gcTimer.unref()
158
- }
159
-
160
- startGC()
161
-
162
- // --- Retry logic ---
163
-
164
- const DEFAULT_RETRY = 3
165
- const DEFAULT_RETRY_DELAY = (attempt) => Math.min(1000 * 2 ** attempt, 30000)
166
-
167
- const resolveRetryConfig = (config) => {
168
- const retry = config.retry
169
- if (retry === false) return { count: 0 }
170
- if (retry === true || retry === undefined) return { count: DEFAULT_RETRY, delay: DEFAULT_RETRY_DELAY }
171
- if (typeof retry === 'number') return { count: retry, delay: DEFAULT_RETRY_DELAY }
172
- return {
173
- count: retry.count ?? DEFAULT_RETRY,
174
- delay: isFunction(retry.delay) ? retry.delay : (typeof retry.delay === 'number' ? () => retry.delay : DEFAULT_RETRY_DELAY)
175
- }
176
- }
177
-
178
- const withRetry = async (fn, retryConfig) => {
179
- const { count, delay } = retryConfig
180
- let lastError
181
- for (let attempt = 0; attempt <= count; attempt++) {
182
- try {
183
- const result = await fn()
184
- if (result?.error) {
185
- lastError = result.error
186
- if (attempt < count) {
187
- const ms = isFunction(delay) ? delay(attempt, lastError) : delay
188
- await new Promise(r => setTimeout(r, ms))
189
- continue
190
- }
191
- return result
192
- }
193
- return result
194
- } catch (e) {
195
- lastError = e
196
- if (attempt < count) {
197
- const ms = isFunction(delay) ? delay(attempt, e) : delay
198
- await new Promise(r => setTimeout(r, ms))
199
- continue
200
- }
201
- throw e
202
- }
203
- }
204
- return { data: null, error: lastError }
205
- }
206
-
207
- // --- Refetch on window focus / reconnect ---
208
-
209
- const globalListeners = { focus: new Set(), online: new Set() }
210
- let globalListenersAttached = false
211
-
212
- const attachGlobalListeners = () => {
213
- if (globalListenersAttached || typeof window === 'undefined') return
214
- globalListenersAttached = true
215
-
216
- const onFocus = () => {
217
- for (const fn of globalListeners.focus) fn()
218
- }
219
- const onOnline = () => {
220
- for (const fn of globalListeners.online) fn()
221
- }
222
-
223
- window.addEventListener('visibilitychange', () => {
224
- if (document.visibilityState === 'visible') onFocus()
225
- })
226
- window.addEventListener('focus', onFocus)
227
- window.addEventListener('online', onOnline)
228
- }
229
-
230
- // --- Fetch config resolution ---
231
-
232
- const resolveFetchConfig = (fetchProp, element) => {
233
- const ref = element.__ref
234
-
235
- if (fetchProp === true) {
236
- return { from: ref.__state || element.key, method: 'select', query: true, on: 'create' }
237
- }
238
-
239
- if (isString(fetchProp)) {
240
- return { from: fetchProp, method: 'select', query: true, on: 'create' }
241
- }
242
-
243
- if (isObject(fetchProp)) {
244
- const config = { ...fetchProp }
245
- config.from = config.from || ref.__state || element.key
246
- config.method = config.method || 'select'
247
- config.on = config.on || 'create'
248
- if (config.query === undefined) config.query = true
249
- return config
250
- }
251
-
252
- return null
253
- }
254
-
255
- const resolveParamsSync = (params) => {
256
- if (!params || isFunction(params)) return params
257
- return params
258
- }
259
-
260
- const resolveLanguage = (element, context) => {
261
- const root = element?.state?.root || context?.state?.root
262
- if (root?.lang) return root.lang
263
- return undefined
264
- }
265
-
266
- const resolveParams = (params, element, context) => {
267
- let resolved
268
- if (!params) {
269
- resolved = undefined
270
- } else if (isFunction(params)) {
271
- resolved = params(element, element.state)
272
- } else {
273
- resolved = {}
274
- for (const key in params) {
275
- const val = params[key]
276
- resolved[key] = isFunction(val) ? val(element, element.state) : val
277
- }
278
- }
279
- return resolved
280
- }
281
-
282
- export const initAdapterAuth = async (adapter, context) => {
283
- if (!adapter || adapter.__authInitialized) return
284
- adapter.__authInitialized = true
285
- if (!adapter.getSession) return
286
- const updateAuth = (user, session) => {
287
- const root = context.state?.root
288
- if (root?.update) {
289
- root.update({ auth: { user, session, loading: false } })
290
- }
291
- }
292
- try {
293
- const session = await adapter.getSession()
294
- updateAuth(session?.user || null, session)
295
- } catch (e) {
296
- console.warn('[fetch] Failed to restore auth session:', e.message)
297
- }
298
- if (adapter.onAuthStateChange) {
299
- adapter.onAuthStateChange((event, session) => {
300
- updateAuth(session?.user || null, session)
301
- })
302
- }
303
- }
304
-
305
- const resolveAdapter = async (db, context) => {
306
- // When adapter is already resolved (e.g. by getDB()), still init auth
307
- if (isFunction(db.select)) {
308
- if (db.auth !== false) await initAdapterAuth(db, context)
309
- return db
310
- }
311
- if (db.__resolved) {
312
- if (db.auth !== false) await initAdapterAuth(db.__resolved, context)
313
- return db.__resolved
314
- }
315
- if (db.__resolving) return db.__resolving
316
-
317
- db.__resolving = resolveDb(db)
318
- const resolved = await db.__resolving
319
- delete db.__resolving
320
- if (!resolved) return null
321
- db.__resolved = resolved
322
- context.fetch = resolved
323
-
324
- // Auto-init auth when adapter supports it and db.auth is enabled
325
- if (db.auth !== false) await initAdapterAuth(resolved, context)
326
-
327
- return resolved
328
- }
329
-
330
- const triggerCallback = (element, name, ...args) => {
331
- const fn = isFunction(element[name]) ? element[name]
332
- : (element.props && isFunction(element.props[name])) ? element.props[name]
333
- : null
334
- if (fn) fn.call(element, ...args, element, element.state, element.context)
335
- }
336
-
337
- const collectFormData = (element) => {
338
- const data = {}
339
- const node = element.node
340
- if (!node) return data
341
-
342
- if (node.tagName === 'FORM') {
343
- const formData = new FormData(node)
344
- for (const [key, value] of formData.entries()) {
345
- data[key] = value
346
- }
347
- return data
348
- }
349
-
350
- const inputs = node.querySelectorAll('input, textarea, select')
351
- for (let i = 0; i < inputs.length; i++) {
352
- const input = inputs[i]
353
- const name = input.name || input.getAttribute('name')
354
- if (!name) continue
355
- if (input.type === 'checkbox') {
356
- data[name] = input.checked
357
- } else if (input.type === 'file') {
358
- data[name] = input.files
359
- } else {
360
- data[name] = input.value
361
- }
362
- }
363
- return data
364
- }
365
-
366
- // --- State update helpers ---
367
-
368
- const updateElementState = (element, data, stateKey, options = {}) => {
369
- const stateData = stateKey ? { [stateKey]: data } : data
370
- if (element.state?.update) {
371
- element.state.update(stateData, { preventFetch: true, ...options })
372
- }
373
- }
374
-
375
- const setFetchStatus = (element, status) => {
376
- const ref = element.__ref
377
- ref.__fetchStatus = status
378
- ref.__fetching = status.isFetching
379
- ref.__fetchError = status.error
380
- }
381
-
382
- // --- Core fetch runner ---
383
-
384
- const runFetch = async (config, element, context, opts = {}) => {
385
- const db = context?.fetch
386
- if (!db) return
387
-
388
- // enabled check
389
- if (config.enabled === false) return
390
- if (isFunction(config.enabled) && !config.enabled(element, element.state)) return
391
-
392
- const adapter = await resolveAdapter(db, context)
393
- if (!adapter) return
394
-
395
- const ref = element.__ref
396
- const {
397
- from, method, query, params: rawParams, cache: cacheRaw, transform,
398
- single, auth, fields, as: stateKey, limit, offset, order,
399
- headers, baseUrl,
400
- // Pagination
401
- page, cursor, getNextPageParam, getPreviousPageParam, infinite,
402
- // TanStack-style options
403
- placeholderData, initialData, select: selectTransform,
404
- keepPreviousData
405
- } = config
406
-
407
- let select
408
- if (query && isFunction(element.getQuery)) {
409
- const q = element.getQuery(adapter.name || 'paths')
410
- if (q) select = q.select || (q.length && q.join(',')) || undefined
411
- }
412
-
413
- const params = resolveParams(rawParams, element, context)
414
- const cacheConfig = parseCacheConfig(cacheRaw)
415
- const retryConfig = resolveRetryConfig(config)
416
-
417
- // Build cache key
418
- const cacheKey = cacheConfig
419
- ? (cacheRaw?.key || `${from}:${method}:${JSON.stringify(params || '')}${infinite ? ':infinite' : ''}${page ? ':p' + JSON.stringify(page) : ''}`)
420
- : null
421
-
422
- // Apply initial/placeholder data on first mount
423
- if (!ref.__fetchInitialized && cacheKey) {
424
- ref.__fetchInitialized = true
425
- if (initialData !== undefined) {
426
- const initData = isFunction(initialData) ? initialData() : initialData
427
- setCacheEntry(cacheKey, initData, null)
428
- }
429
- }
430
-
431
- // Check cache (stale-while-revalidate)
432
- if ((method === 'select' || method === 'rpc') && cacheKey && cacheConfig) {
433
- const cached = getCacheEntry(cacheKey)
434
- if (cached && !cached.error) {
435
- const age = Date.now() - cached.time
436
- const isStale = cached.stale || age > (cacheConfig.staleTime || 0)
437
- let data = cached.data
438
- if (selectTransform) data = selectTransform(data, element, element.state)
439
- if (transform) data = transform(data, element, element.state)
440
- updateElementState(element, data, stateKey)
441
- if (!isStale) {
442
- setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: true, error: null, status: 'success', fetchStatus: 'idle' })
443
- return
444
- }
445
- // Stale — continue to background refetch
446
- } else if (placeholderData !== undefined) {
447
- const phData = isFunction(placeholderData) ? placeholderData(element, element.state) : placeholderData
448
- if (phData !== undefined) {
449
- updateElementState(element, phData, stateKey)
450
- }
451
- }
452
- }
453
-
454
- // Deduplication — if same query is already in-flight, share the promise
455
- if (cacheKey && activeQueries.has(cacheKey) && !opts.force) {
456
- const existing = activeQueries.get(cacheKey)
457
- try {
458
- const result = await existing
459
- if (result?.data !== undefined) {
460
- let finalData = result.data
461
- if (selectTransform) finalData = selectTransform(finalData, element, element.state)
462
- if (transform) finalData = transform(finalData, element, element.state)
463
- updateElementState(element, finalData, stateKey)
464
- triggerCallback(element, 'onFetchComplete', finalData)
465
- }
466
- } catch (e) {
467
- // Error handled by the original caller
468
- }
469
- return
470
- }
471
-
472
- // Set loading status
473
- const isFirstLoad = !getCacheEntry(cacheKey)?.data
474
- setFetchStatus(element, {
475
- isFetching: true,
476
- isLoading: isFirstLoad,
477
- isStale: false,
478
- isSuccess: false,
479
- error: null,
480
- status: 'pending',
481
- fetchStatus: 'fetching'
482
- })
483
- triggerCallback(element, 'onFetchStart')
484
-
485
- const doFetch = async () => {
486
- if (auth !== false && adapter.getSession) {
487
- const session = await adapter.getSession()
488
- if (auth === true && !session) {
489
- const err = { message: 'Not authenticated' }
490
- setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: false, error: err, status: 'error', fetchStatus: 'idle' })
491
- triggerCallback(element, 'onFetchError', err)
492
- return { data: null, error: err }
493
- }
494
- }
495
-
496
- const fn = adapter[method]
497
- if (!isFunction(fn)) return { data: null, error: { message: `Method "${method}" not found on adapter` } }
498
-
499
- const request = { from, select, params, single, limit, offset, order, headers, baseUrl }
500
-
501
- // Inject language into request headers
502
- const lang = resolveLanguage(element, context)
503
- if (lang) {
504
- request.headers = { ...request.headers, 'Accept-Language': lang }
505
- }
506
-
507
- // Pagination support
508
- if (page !== undefined) {
509
- if (isObject(page)) {
510
- if (page.offset !== undefined) request.offset = page.offset
511
- if (page.limit !== undefined) request.limit = page.limit
512
- if (page.cursor !== undefined) request.params = { ...request.params, cursor: page.cursor }
513
- } else if (typeof page === 'number') {
514
- const pageSize = config.pageSize || limit || 20
515
- request.offset = (page - 1) * pageSize
516
- request.limit = pageSize
517
- }
518
- }
519
-
520
- if (cursor !== undefined) {
521
- request.params = { ...request.params, cursor }
522
- }
523
-
524
- if (method === 'insert' || method === 'update' || method === 'upsert') {
525
- let data
526
- if (fields === true || config.on === 'submit') {
527
- data = collectFormData(element)
528
- } else if (isArray(fields)) {
529
- const formData = collectFormData(element)
530
- data = {}
531
- for (const f of fields) data[f] = formData[f]
532
- } else if (element.state?.parse) {
533
- data = element.state.parse()
534
- } else if (isObject(element.state)) {
535
- data = { ...element.state }
536
- }
537
-
538
- if (transform) data = transform(data, element, element.state)
539
- request.data = data
540
- }
541
-
542
- if (method === 'rpc') {
543
- let rpcParams = params
544
- if (config.on === 'submit') {
545
- const formData = collectFormData(element)
546
- rpcParams = config.transformParams
547
- ? config.transformParams(formData, element, element.state)
548
- : formData
549
- }
550
- request.params = rpcParams
551
- }
552
-
553
- if (method === 'subscribe') {
554
- const unsubscribe = adapter.subscribe(
555
- { from, params, on: config.subscribeOn },
556
- (newData, oldData, payload) => {
557
- updateElementState(element, newData, stateKey)
558
- triggerCallback(element, 'onFetchComplete', newData)
559
- }
560
- )
561
- ref.__unsubscribe = unsubscribe
562
- return { data: null, error: null, subscribed: true }
563
- }
564
-
565
- return fn(request)
566
- }
567
-
568
- // Execute with retry
569
- const fetchPromise = withRetry(doFetch, retryConfig)
570
-
571
- // Store for deduplication
572
- if (cacheKey) activeQueries.set(cacheKey, fetchPromise)
573
-
574
- try {
575
- const result = await fetchPromise
576
- if (result?.subscribed) return
577
-
578
- const { data, error } = result || {}
579
-
580
- if (error) {
581
- if (cacheKey) setCacheEntry(cacheKey, null, error)
582
- setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: false, error, status: 'error', fetchStatus: 'idle' })
583
- triggerCallback(element, 'onFetchError', error)
584
- return
585
- }
586
-
587
- if (data !== undefined) {
588
- // Update cache
589
- if ((method === 'select' || method === 'rpc') && cacheKey) {
590
- const entry = setCacheEntry(cacheKey, data, null)
591
- entry.gcTime = cacheConfig?.gcTime || 300000
592
- }
593
-
594
- let finalData = data
595
-
596
- // Infinite query — append pages
597
- if (infinite && isArray(data)) {
598
- if (!ref.__pages) ref.__pages = []
599
- if (opts.direction === 'previous') {
600
- ref.__pages.unshift(data)
601
- } else {
602
- ref.__pages.push(data)
603
- }
604
- finalData = ref.__pages.flat()
605
-
606
- // Resolve pagination cursors
607
- if (getNextPageParam) {
608
- ref.__nextPageParam = getNextPageParam(data, ref.__pages)
609
- ref.__hasNextPage = ref.__nextPageParam != null
610
- }
611
- if (getPreviousPageParam) {
612
- ref.__prevPageParam = getPreviousPageParam(data, ref.__pages)
613
- ref.__hasPreviousPage = ref.__prevPageParam != null
614
- }
615
- }
616
-
617
- if (selectTransform) finalData = selectTransform(finalData, element, element.state)
618
- if (transform) finalData = transform(finalData, element, element.state)
619
-
620
- // keepPreviousData — don't clear state while fetching new page
621
- if (!keepPreviousData || finalData !== undefined) {
622
- updateElementState(element, finalData, stateKey)
623
- }
624
-
625
- setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: true, error: null, status: 'success', fetchStatus: 'idle' })
626
- triggerCallback(element, 'onFetchComplete', finalData)
627
- }
628
- } catch (e) {
629
- setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: false, error: e, status: 'error', fetchStatus: 'idle' })
630
- triggerCallback(element, 'onFetchError', e)
631
- } finally {
632
- if (cacheKey) activeQueries.delete(cacheKey)
633
- }
634
- }
635
-
636
- // --- Event binding ---
637
-
638
- const bindEvent = (config, element, context) => {
639
- const on = config.on
640
- const ref = element.__ref
641
- if (!ref.__fetchListeners) ref.__fetchListeners = []
642
- const runner = config.__runner || runFetch
643
-
644
- if (on === 'submit') {
645
- const handler = (e) => {
646
- e.preventDefault()
647
- runner(config, element, context)
648
- }
649
- const node = element.node
650
- if (node) {
651
- node.addEventListener('submit', handler)
652
- ref.__fetchListeners.push(() => node.removeEventListener('submit', handler))
653
- }
654
- } else if (on === 'click') {
655
- const handler = () => runner(config, element, context)
656
- const node = element.node
657
- if (node) {
658
- node.addEventListener('click', handler)
659
- ref.__fetchListeners.push(() => node.removeEventListener('click', handler))
660
- }
661
- } else if (on === 'stateChange') {
662
- ref.__fetchOnStateChange = () => runner(config, element, context)
663
- }
664
- }
665
-
666
- // --- Refetch interval / focus / reconnect binding ---
667
-
668
- const bindAutoRefetch = (config, element, context) => {
669
- const ref = element.__ref
670
- if (!ref.__fetchCleanups) ref.__fetchCleanups = []
671
-
672
- attachGlobalListeners()
673
-
674
- // refetchInterval
675
- const interval = config.refetchInterval
676
- if (interval) {
677
- const ms = typeof interval === 'number' ? interval : parseDuration(interval)
678
- if (ms > 0) {
679
- const timer = setInterval(() => {
680
- // Only refetch if visible (unless refetchIntervalInBackground is set)
681
- if (config.refetchIntervalInBackground || typeof document === 'undefined' || document.visibilityState === 'visible') {
682
- runFetch(config, element, context, { force: true })
683
- }
684
- }, ms)
685
- ref.__fetchCleanups.push(() => clearInterval(timer))
686
- }
687
- }
688
-
689
- // refetchOnWindowFocus
690
- if (config.refetchOnWindowFocus !== false) {
691
- const onFocus = () => {
692
- const cacheKey = buildCacheKey(config)
693
- const entry = getCacheEntry(cacheKey)
694
- const cacheConfig = parseCacheConfig(config.cache)
695
- if (!entry || entry.stale || (Date.now() - entry.time) > (cacheConfig?.staleTime || 0)) {
696
- runFetch(config, element, context, { force: true })
697
- }
698
- }
699
- globalListeners.focus.add(onFocus)
700
- ref.__fetchCleanups.push(() => globalListeners.focus.delete(onFocus))
701
- }
702
-
703
- // refetchOnReconnect
704
- if (config.refetchOnReconnect !== false) {
705
- const onOnline = () => runFetch(config, element, context, { force: true })
706
- globalListeners.online.add(onOnline)
707
- ref.__fetchCleanups.push(() => globalListeners.online.delete(onOnline))
708
- }
709
- }
710
-
711
- // --- Optimistic updates ---
712
-
713
- const applyOptimisticUpdate = (element, config, mutationData) => {
714
- if (!config.optimistic) return null
715
- const ref = element.__ref
716
- const stateKey = config.as
717
-
718
- // Snapshot current state for rollback
719
- const snapshot = element.state?.parse ? element.state.parse() : (isObject(element.state) ? { ...element.state } : element.state)
720
- ref.__optimisticSnapshot = snapshot
721
-
722
- const optimisticData = isFunction(config.optimistic)
723
- ? config.optimistic(mutationData, snapshot, element)
724
- : config.optimistic
725
-
726
- if (optimisticData !== undefined) {
727
- updateElementState(element, optimisticData, stateKey)
728
- }
729
-
730
- return snapshot
731
- }
732
-
733
- const rollbackOptimistic = (element, config) => {
734
- const ref = element.__ref
735
- const snapshot = ref.__optimisticSnapshot
736
- if (snapshot !== undefined) {
737
- updateElementState(element, snapshot, config.as)
738
- delete ref.__optimisticSnapshot
739
- }
740
- }
741
-
742
- // --- Mutation runner (insert/update/delete with optimistic + invalidation) ---
743
-
744
- const runMutation = async (config, element, context) => {
745
- const db = context?.fetch
746
- if (!db) return
747
-
748
- const adapter = await resolveAdapter(db, context)
749
- if (!adapter) return
750
-
751
- const ref = element.__ref
752
- const { method, from, fields, transform, as: stateKey, on: trigger, auth, headers, baseUrl, invalidates, optimistic, onMutate, onSuccess, onError, onSettled } = config
753
-
754
- // Collect mutation data
755
- let mutationData
756
- if (fields === true || trigger === 'submit') {
757
- mutationData = collectFormData(element)
758
- } else if (isArray(fields)) {
759
- const formData = collectFormData(element)
760
- mutationData = {}
761
- for (const f of fields) mutationData[f] = formData[f]
762
- } else if (element.state?.parse) {
763
- mutationData = element.state.parse()
764
- } else if (isObject(element.state)) {
765
- mutationData = { ...element.state }
766
- }
767
-
768
- if (transform) mutationData = transform(mutationData, element, element.state)
769
-
770
- // onMutate callback (runs before fetch)
771
- if (isFunction(onMutate)) onMutate(mutationData, element, element.state)
772
-
773
- // Apply optimistic update
774
- const snapshot = optimistic ? applyOptimisticUpdate(element, config, mutationData) : null
775
-
776
- ref.__fetching = true
777
- setFetchStatus(element, { isFetching: true, isLoading: false, isStale: false, isSuccess: false, error: null, status: 'pending', fetchStatus: 'fetching' })
778
- triggerCallback(element, 'onFetchStart')
779
-
780
- try {
781
- if (auth !== false && adapter.getSession) {
782
- const session = await adapter.getSession()
783
- if (auth === true && !session) {
784
- if (snapshot !== undefined) rollbackOptimistic(element, config)
785
- const err = { message: 'Not authenticated' }
786
- triggerCallback(element, 'onFetchError', err)
787
- return
788
- }
789
- }
790
-
791
- const fn = adapter[method]
792
- if (!isFunction(fn)) return
793
-
794
- const request = { from, data: mutationData, headers, baseUrl }
795
- if (config.params) request.params = resolveParams(config.params, element, context)
796
-
797
- // Inject language into mutation request headers
798
- const lang = resolveLanguage(element, context)
799
- if (lang) {
800
- request.headers = { ...request.headers, 'Accept-Language': lang }
801
- }
802
-
803
- const retryConfig = resolveRetryConfig(config)
804
- const result = await withRetry(() => fn(request), retryConfig)
805
- const { data, error } = result || {}
806
-
807
- if (error) {
808
- if (snapshot !== undefined) rollbackOptimistic(element, config)
809
- setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: false, error, status: 'error', fetchStatus: 'idle' })
810
- triggerCallback(element, 'onFetchError', error)
811
- if (isFunction(onError)) onError(error, mutationData, element)
812
- if (isFunction(onSettled)) onSettled(null, error, mutationData, element)
813
- return
814
- }
815
-
816
- delete ref.__optimisticSnapshot
817
-
818
- if (data !== undefined) {
819
- const finalData = stateKey ? { [stateKey]: data } : data
820
- if (element.state?.update) element.state.update(finalData, { preventFetch: true })
821
- }
822
-
823
- setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: true, error: null, status: 'success', fetchStatus: 'idle' })
824
- triggerCallback(element, 'onFetchComplete', data)
825
- if (isFunction(onSuccess)) onSuccess(data, mutationData, element)
826
- if (isFunction(onSettled)) onSettled(data, null, mutationData, element)
827
-
828
- // Invalidate related queries
829
- if (invalidates) {
830
- const keys = isArray(invalidates) ? invalidates : [invalidates]
831
- for (const k of keys) {
832
- if (k === true || k === '*') {
833
- // Invalidate all queries matching this `from`
834
- for (const [ck] of cacheStore) {
835
- if (ck.startsWith(from + ':')) invalidateCache(ck)
836
- }
837
- } else {
838
- invalidateCache(k)
839
- }
840
- }
841
- }
842
- } catch (e) {
843
- if (snapshot !== undefined) rollbackOptimistic(element, config)
844
- setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: false, error: e, status: 'error', fetchStatus: 'idle' })
845
- triggerCallback(element, 'onFetchError', e)
846
- if (isFunction(onError)) onError(e, mutationData, element)
847
- if (isFunction(onSettled)) onSettled(null, e, mutationData, element)
848
- } finally {
849
- ref.__fetching = false
850
- }
851
- }
852
-
853
- // --- Public API ---
854
-
855
- export const executeFetch = (param, element, state, context) => {
856
- if (!param) return
857
- const db = context?.fetch
858
- if (!db) return
859
-
860
- const fetchProp = exec(param, element)
861
- if (!fetchProp) return
862
-
863
- const configs = isArray(fetchProp)
864
- ? fetchProp.map(c => resolveFetchConfig(exec(c, element), element)).filter(Boolean)
865
- : [resolveFetchConfig(fetchProp, element)].filter(Boolean)
866
-
867
- for (const config of configs) {
868
- const isMutation = config.method === 'insert' || config.method === 'update' || config.method === 'upsert' || config.method === 'delete'
869
- const runner = isMutation ? runMutation : runFetch
870
-
871
- if (config.on === 'create') {
872
- runner(config, element, context)
873
- } else {
874
- Promise.resolve().then(() => {
875
- bindEvent({ ...config, __runner: runner }, element, context)
876
- })
877
- }
878
-
879
- // Bind auto-refetch for queries
880
- if (!isMutation && config.on === 'create') {
881
- if (config.refetchInterval || config.refetchOnWindowFocus !== false || config.refetchOnReconnect !== false) {
882
- bindAutoRefetch(config, element, context)
883
- }
884
- }
885
- }
886
-
887
- // Expose imperative methods on element
888
- const ref = element.__ref
889
- ref.refetch = (opts) => {
890
- for (const config of configs) {
891
- runFetch(config, element, context, { force: true, ...opts })
892
- }
893
- }
894
-
895
- ref.fetchNextPage = () => {
896
- const config = configs[0]
897
- if (!config || !config.infinite) return
898
- const nextParam = ref.__nextPageParam
899
- if (nextParam == null) return
900
- const nextConfig = { ...config, cursor: nextParam }
901
- runFetch(nextConfig, element, context, { direction: 'next', force: true })
902
- }
903
-
904
- ref.fetchPreviousPage = () => {
905
- const config = configs[0]
906
- if (!config || !config.infinite) return
907
- const prevParam = ref.__prevPageParam
908
- if (prevParam == null) return
909
- const prevConfig = { ...config, cursor: prevParam }
910
- runFetch(prevConfig, element, context, { direction: 'previous', force: true })
911
- }
912
- }
913
-
914
- // --- Query client (global cache management) ---
915
-
916
- export const queryClient = {
917
- invalidateQueries: (keyOrPattern) => {
918
- if (!keyOrPattern) {
919
- invalidateCache()
920
- return
921
- }
922
- if (isString(keyOrPattern)) {
923
- for (const [key] of cacheStore) {
924
- if (key.startsWith(keyOrPattern) || key.includes(keyOrPattern)) {
925
- invalidateCache(key)
926
- }
927
- }
928
- } else if (isArray(keyOrPattern)) {
929
- const pattern = keyOrPattern.join(':')
930
- for (const [key] of cacheStore) {
931
- if (key.includes(pattern)) invalidateCache(key)
932
- }
933
- }
934
- },
935
-
936
- removeQueries: (keyOrPattern) => {
937
- if (!keyOrPattern) {
938
- removeCache()
939
- return
940
- }
941
- if (isString(keyOrPattern)) {
942
- for (const [key] of cacheStore) {
943
- if (key.startsWith(keyOrPattern) || key.includes(keyOrPattern)) {
944
- removeCache(key)
945
- }
946
- }
947
- }
948
- },
949
-
950
- getQueryData: (key) => {
951
- const entry = getCacheEntry(key)
952
- return entry?.data ?? undefined
953
- },
954
-
955
- setQueryData: (key, updater) => {
956
- const existing = getCacheEntry(key)
957
- const data = isFunction(updater) ? updater(existing?.data) : updater
958
- setCacheEntry(key, data, null)
959
- },
960
-
961
- prefetchQuery: async (config, context) => {
962
- const db = context?.fetch
963
- if (!db) return
964
-
965
- const adapter = await resolveAdapter(db, context)
966
- if (!adapter) return
967
-
968
- const cacheKey = config.cache?.key || `${config.from}:${config.method || 'select'}:${JSON.stringify(config.params || '')}`
969
- const fn = adapter[config.method || 'select']
970
- if (!isFunction(fn)) return
971
-
972
- const result = await fn({
973
- from: config.from,
974
- params: config.params,
975
- limit: config.limit,
976
- offset: config.offset,
977
- order: config.order,
978
- single: config.single
979
- })
980
-
981
- if (result?.data !== undefined && !result?.error) {
982
- const entry = setCacheEntry(cacheKey, result.data, null)
983
- entry.gcTime = parseCacheConfig(config.cache)?.gcTime || 300000
984
- }
985
-
986
- return result
987
- },
988
-
989
- getCache: () => cacheStore,
990
- clear: () => removeCache()
991
- }
992
-
993
- // --- Plugin interface ---
994
-
995
- /**
996
- * domql plugin for declarative data fetching.
997
- *
998
- * Usage:
999
- * context.plugins = [fetchPlugin]
1000
- *
1001
- * Or with language:
1002
- * context.plugins = [fetchPlugin]
1003
- * context.language = 'en'
1004
- *
1005
- * When used as a plugin, add `fetch` to `context.define` or
1006
- * it is automatically registered via `defaultDefine.fetch`.
1007
- */
1008
- export const fetchPlugin = {
1009
- name: 'fetch',
1010
-
1011
- // Hook into element creation to auto-execute fetch configs
1012
- create (element) {
1013
- const fetchProp = element.fetch || element.props?.fetch
1014
- if (!fetchProp) return
1015
- executeFetch(fetchProp, element, element.state, element.context)
1016
- }
1017
- }
1018
-
1019
- export { parseDuration }
1020
- export default executeFetch