@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.
- package/adapters/supabase.js +46 -11
- package/dist/cjs/adapters/local.cjs +1 -0
- package/dist/cjs/adapters/rest.cjs +1 -0
- package/dist/cjs/adapters/supabase.cjs +1 -0
- package/dist/cjs/index.cjs +1 -0
- package/dist/esm/adapters/local.js +1 -0
- package/dist/esm/adapters/rest.js +1 -0
- package/dist/esm/adapters/supabase.js +1 -0
- package/dist/esm/index.js +1 -0
- package/package.json +31 -10
- package/index.js +0 -1020
package/adapters/supabase.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
3
|
+
"version": "3.14.1",
|
|
4
4
|
"license": "CC-BY-NC-4.0",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"module": "./index.js",
|
|
7
|
-
"main": "./index.
|
|
6
|
+
"module": "./dist/esm/index.js",
|
|
7
|
+
"main": "./dist/cjs/index.cjs",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
37
|
+
"dist",
|
|
38
|
+
"*.md",
|
|
39
|
+
"LICENSE",
|
|
25
40
|
"adapters"
|
|
26
41
|
],
|
|
27
42
|
"dependencies": {
|
|
28
|
-
"@
|
|
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": "
|
|
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
|