@symbo.ls/fetch 3.8.9 → 3.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,14 @@
1
1
  'use strict'
2
2
 
3
- export const setup = async ({ url, key, projectId, createClient, ...options }) => {
3
+ // Cache Supabase clients by (url, key, authMode) so every `setup` call
4
+ // against the same project reuses a single GoTrueClient. Without this
5
+ // cache, each fresh adapter resolution (per db/fetch config, per query)
6
+ // spins up a new GoTrueClient on the same storage key — Supabase logs
7
+ // "Multiple GoTrueClient instances" warnings and auth state can drift.
8
+ const CLIENT_CACHE = new Map()
9
+ const clientCacheKey = (url, key, authMode) => `${url}|${key}|${authMode ? 'off' : 'on'}`
10
+
11
+ export const setup = async ({ url, key, projectId, createClient, auth, retry, adapter, ...supabaseOptions }) => {
4
12
  const supabaseUrl = url || (projectId && `https://${projectId}.supabase.co`)
5
13
  if (!supabaseUrl || !key) {
6
14
  throw new Error('@symbo.ls/fetch supabase: url (or projectId) and key are required')
@@ -10,8 +18,16 @@ export const setup = async ({ url, key, projectId, createClient, ...options }) =
10
18
  const mod = await import(/* webpackIgnore: true */ pkg)
11
19
  createClient = mod.createClient
12
20
  }
13
- const client = createClient(supabaseUrl, key, options)
14
- // Guard against no-op createClient (e.g. deserialized placeholder from SSR)
21
+ // Disable Supabase auto-auth when auth: false (prevents navigator.locks hang)
22
+ if (auth === false) {
23
+ supabaseOptions.auth = { autoRefreshToken: false, persistSession: false, detectSessionInUrl: false }
24
+ }
25
+ const cacheKey = clientCacheKey(supabaseUrl, key, auth === false)
26
+ let client = CLIENT_CACHE.get(cacheKey)
27
+ if (!client) {
28
+ client = createClient(supabaseUrl, key, supabaseOptions)
29
+ if (client?.auth) CLIENT_CACHE.set(cacheKey, client)
30
+ }
15
31
  if (!client || !client.auth) return null
16
32
  return supabaseAdapter(client)
17
33
  }
@@ -101,26 +117,42 @@ export const supabaseAdapter = (client) => ({
101
117
  },
102
118
 
103
119
  // CRUD
104
- select: async ({ from, select, params, ...modifiers }) => {
105
- let q = client.from(from).select(select || '*')
120
+ select: async ({ from, schema, select, params, headers, ...modifiers }) => {
121
+ const base = schema ? client.schema(schema) : client
122
+ let q = base.from(from).select(select || '*')
106
123
  q = applyFilters(q, params)
107
124
  q = applyModifiers(q, modifiers)
125
+ if (headers) {
126
+ try {
127
+ for (const k in headers) {
128
+ if (q.setHeader) q = q.setHeader(k, headers[k])
129
+ else if (client.rest) client.rest.headers[k] = headers[k]
130
+ }
131
+ } catch (e) {
132
+ console.warn('[supabase] setHeader failed:', e.message)
133
+ }
134
+ }
108
135
  return q
109
136
  },
110
137
 
111
138
  rpc: ({ from, params }) => client.rpc(from, params),
112
139
 
113
- insert: ({ from, data, select }) =>
114
- client.from(from).insert(data).select(select || '*'),
140
+ insert: ({ from, schema, data, select }) => {
141
+ const base = schema ? client.schema(schema) : client
142
+ return base.from(from).insert(data).select(select || '*')
143
+ },
115
144
 
116
- update: ({ from, data, params, select }) => {
117
- let q = client.from(from).update(data)
145
+ update: ({ from, schema, data, params, select }) => {
146
+ const base = schema ? client.schema(schema) : client
147
+ let q = base.from(from).update(data)
118
148
  q = applyFilters(q, params)
119
149
  return q.select(select || '*')
120
150
  },
121
151
 
122
- upsert: ({ from, data, select }) =>
123
- client.from(from).upsert(data).select(select || '*'),
152
+ upsert: ({ from, schema, data, select }) => {
153
+ const base = schema ? client.schema(schema) : client
154
+ return base.from(from).upsert(data).select(select || '*')
155
+ },
124
156
 
125
157
  delete: ({ from, params }) => {
126
158
  let q = client.from(from).delete()
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- import { isArray, isFunction, isObject, isString, exec } from '@domql/utils'
3
+ import { isArray, isFunction, isObject, isString, exec } from '@symbo.ls/utils'
4
4
 
5
5
  // --- Adapter resolution ---
6
6
 
@@ -62,10 +62,16 @@ const cacheStore = new Map()
62
62
  const querySubscribers = new Map() // key -> Set of elements
63
63
  const activeQueries = new Map() // key -> Promise (deduplication)
64
64
 
65
- const buildCacheKey = (config) => {
65
+ // Cache key includes lang when provided so different-locale responses don't
66
+ // collide. The fetch plugin injects Accept-Language on requests, so the same
67
+ // (from, method, params) tuple yields different bodies per lang. queryClient.
68
+ // dropNonLang(keepLang) leans on this suffix to evict stale-language entries
69
+ // on a setLang() event without touching the kept lang's cache.
70
+ const buildCacheKey = (config, lang) => {
66
71
  if (config.cache?.key) return config.cache.key
67
72
  const params = resolveParamsSync(config.params)
68
- return `${config.from}:${config.method}:${JSON.stringify(params || '')}`
73
+ const base = `${config.from}:${config.method}:${JSON.stringify(params || '')}`
74
+ return lang ? `${base}:${lang}` : base
69
75
  }
70
76
 
71
77
  const getCacheEntry = (key) => cacheStore.get(key) || null
@@ -318,6 +324,9 @@ const resolveAdapter = async (db, context) => {
318
324
  const resolved = await db.__resolving
319
325
  delete db.__resolving
320
326
  if (!resolved) return null
327
+ resolved.auth = db.auth
328
+ resolved.url = db.url
329
+ resolved.key = db.key
321
330
  db.__resolved = resolved
322
331
  context.fetch = resolved
323
332
 
@@ -328,9 +337,12 @@ const resolveAdapter = async (db, context) => {
328
337
  }
329
338
 
330
339
  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
340
+ const fn = isFunction(element[name]) ? element[name] : null
341
+ if (fn) fn.call(element, ...args, element, element.state, element.context)
342
+ }
343
+
344
+ const triggerConfigCallback = (config, element, name, ...args) => {
345
+ const fn = config && isFunction(config[name]) ? config[name] : null
334
346
  if (fn) fn.call(element, ...args, element, element.state, element.context)
335
347
  }
336
348
 
@@ -366,14 +378,33 @@ const collectFormData = (element) => {
366
378
  // --- State update helpers ---
367
379
 
368
380
  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 })
381
+ const isRoot = stateKey && stateKey.startsWith('~/')
382
+ const target = isRoot ? (element.state?.root || element.state) : element.state
383
+ if (!target?.update) return
384
+
385
+ if (isRoot) {
386
+ const key = stateKey.slice(2)
387
+ // If transform returned an object containing the key itself, spread on root
388
+ // e.g. transform returns { transactions: [...], totalSaved: 755 } for as: '~/transactions'
389
+ if (data && typeof data === 'object' && !Array.isArray(data) && key in data) {
390
+ target.update(data, { preventFetch: true, ...options })
391
+ } else {
392
+ target.update({ [key]: data }, { preventFetch: true, ...options })
393
+ }
394
+ } else {
395
+ const stateData = stateKey ? { [stateKey]: data } : data
396
+ target.update(stateData, { preventFetch: true, ...options })
372
397
  }
373
398
  }
374
399
 
375
400
  const setFetchStatus = (element, status) => {
376
- const ref = element.__ref
401
+ // Element may have been torn down (route change, modal close, parent
402
+ // unmount) between the runFetch entry guard and this call site —
403
+ // `runFetch` checks `__ref` once at start, but a long-lived async fetch
404
+ // can outlive the element. Without this null-guard the framework throws
405
+ // `TypeError: Cannot set properties of null` from every status write.
406
+ const ref = element?.__ref
407
+ if (!ref) return
377
408
  ref.__fetchStatus = status
378
409
  ref.__fetching = status.isFetching
379
410
  ref.__fetchError = status.error
@@ -385,13 +416,26 @@ const runFetch = async (config, element, context, opts = {}) => {
385
416
  const db = context?.fetch
386
417
  if (!db) return
387
418
 
419
+ // Element may have been torn down (unmounted, navigated away, modal
420
+ // closed) while a reactive dependency on its fetch config was still
421
+ // scheduling this run. `__ref` is the framework's marker for "alive" —
422
+ // if it's gone, bail before anything else touches `element`.
423
+ if (!element || !element.__ref) return
424
+
388
425
  // enabled check
389
426
  if (config.enabled === false) return
390
427
  if (isFunction(config.enabled) && !config.enabled(element, element.state)) return
428
+ // Also honor `skip` as an alias — some call sites already use it.
429
+ if (config.skip === true) return
430
+ if (isFunction(config.skip) && config.skip(element, element.state)) return
391
431
 
392
432
  const adapter = await resolveAdapter(db, context)
393
433
  if (!adapter) return
394
434
 
435
+ // Re-check after the await — a slow adapter-resolve can outlive the
436
+ // element that triggered the fetch.
437
+ if (!element.__ref) return
438
+
395
439
  const ref = element.__ref
396
440
  const {
397
441
  from, method, query, params: rawParams, cache: cacheRaw, transform,
@@ -404,19 +448,22 @@ const runFetch = async (config, element, context, opts = {}) => {
404
448
  keepPreviousData
405
449
  } = config
406
450
 
407
- let select
451
+ let select = isString(selectTransform) ? selectTransform : undefined
408
452
  if (query && isFunction(element.getQuery)) {
409
453
  const q = element.getQuery(adapter.name || 'paths')
410
- if (q) select = q.select || (q.length && q.join(',')) || undefined
454
+ if (q) select = q.select || (q.length && q.join(',')) || select
411
455
  }
412
456
 
413
457
  const params = resolveParams(rawParams, element, context)
414
458
  const cacheConfig = parseCacheConfig(cacheRaw)
415
459
  const retryConfig = resolveRetryConfig(config)
460
+ const lang = resolveLanguage(element, context)
416
461
 
417
- // Build cache key
462
+ // Build cache key — lang suffix lets queryClient.dropNonLang(keepLang)
463
+ // evict stale-language entries on setLang() without flushing the rest.
464
+ const langSuffix = lang ? `:${lang}` : ''
418
465
  const cacheKey = cacheConfig
419
- ? (cacheRaw?.key || `${from}:${method}:${JSON.stringify(params || '')}${infinite ? ':infinite' : ''}${page ? ':p' + JSON.stringify(page) : ''}`)
466
+ ? (cacheRaw?.key || `${from}:${method}:${JSON.stringify(params || '')}${infinite ? ':infinite' : ''}${page ? ':p' + JSON.stringify(page) : ''}${langSuffix}`)
420
467
  : null
421
468
 
422
469
  // Apply initial/placeholder data on first mount
@@ -435,7 +482,7 @@ const runFetch = async (config, element, context, opts = {}) => {
435
482
  const age = Date.now() - cached.time
436
483
  const isStale = cached.stale || age > (cacheConfig.staleTime || 0)
437
484
  let data = cached.data
438
- if (selectTransform) data = selectTransform(data, element, element.state)
485
+ if (isFunction(selectTransform)) data = selectTransform(data, element, element.state)
439
486
  if (transform) data = transform(data, element, element.state)
440
487
  updateElementState(element, data, stateKey)
441
488
  if (!isStale) {
@@ -451,17 +498,18 @@ const runFetch = async (config, element, context, opts = {}) => {
451
498
  }
452
499
  }
453
500
 
454
- // Deduplication — if same query is already in-flight, share the promise
501
+ // Deduplication — if same query is already in-flight, share the promise (with timeout)
455
502
  if (cacheKey && activeQueries.has(cacheKey) && !opts.force) {
456
503
  const existing = activeQueries.get(cacheKey)
457
504
  try {
458
- const result = await existing
505
+ const result = await Promise.race([existing, new Promise((_, r) => setTimeout(() => r('DEDUP_TIMEOUT'), 15000))])
459
506
  if (result?.data !== undefined) {
460
507
  let finalData = result.data
461
- if (selectTransform) finalData = selectTransform(finalData, element, element.state)
508
+ if (isFunction(selectTransform)) finalData = selectTransform(finalData, element, element.state)
462
509
  if (transform) finalData = transform(finalData, element, element.state)
463
510
  updateElementState(element, finalData, stateKey)
464
511
  triggerCallback(element, 'onFetchComplete', finalData)
512
+ triggerConfigCallback(config, element, 'onFetchComplete', finalData)
465
513
  }
466
514
  } catch (e) {
467
515
  // Error handled by the original caller
@@ -481,14 +529,20 @@ const runFetch = async (config, element, context, opts = {}) => {
481
529
  fetchStatus: 'fetching'
482
530
  })
483
531
  triggerCallback(element, 'onFetchStart')
532
+ triggerConfigCallback(config, element, 'onFetchStart')
533
+
534
+ const _emitNet = context?.analyze?.emitNetwork
535
+ const __analyzeStart = _emitNet ? (typeof performance !== 'undefined' ? performance.now() : Date.now()) : 0
536
+ if (_emitNet) _emitNet({ phase: 'start', mode: 'query', from, method, cacheKey, lang })
484
537
 
485
538
  const doFetch = async () => {
486
- if (auth !== false && adapter.getSession) {
539
+ if (auth !== false && adapter.auth !== false && adapter.getSession) {
487
540
  const session = await adapter.getSession()
488
541
  if (auth === true && !session) {
489
542
  const err = { message: 'Not authenticated' }
490
543
  setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: false, error: err, status: 'error', fetchStatus: 'idle' })
491
544
  triggerCallback(element, 'onFetchError', err)
545
+ triggerConfigCallback(config, element, 'onFetchError', err)
492
546
  return { data: null, error: err }
493
547
  }
494
548
  }
@@ -556,6 +610,7 @@ const runFetch = async (config, element, context, opts = {}) => {
556
610
  (newData, oldData, payload) => {
557
611
  updateElementState(element, newData, stateKey)
558
612
  triggerCallback(element, 'onFetchComplete', newData)
613
+ triggerConfigCallback(config, element, 'onFetchComplete', newData)
559
614
  }
560
615
  )
561
616
  ref.__unsubscribe = unsubscribe
@@ -581,6 +636,12 @@ const runFetch = async (config, element, context, opts = {}) => {
581
636
  if (cacheKey) setCacheEntry(cacheKey, null, error)
582
637
  setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: false, error, status: 'error', fetchStatus: 'idle' })
583
638
  triggerCallback(element, 'onFetchError', error)
639
+ triggerConfigCallback(config, element, 'onFetchError', error)
640
+ if (_emitNet) _emitNet({
641
+ phase: 'error', mode: 'query', from, method, cacheKey,
642
+ error: error.message || error,
643
+ durationMs: (typeof performance !== 'undefined' ? performance.now() : Date.now()) - __analyzeStart
644
+ })
584
645
  return
585
646
  }
586
647
 
@@ -614,7 +675,7 @@ const runFetch = async (config, element, context, opts = {}) => {
614
675
  }
615
676
  }
616
677
 
617
- if (selectTransform) finalData = selectTransform(finalData, element, element.state)
678
+ if (isFunction(selectTransform)) finalData = selectTransform(finalData, element, element.state)
618
679
  if (transform) finalData = transform(finalData, element, element.state)
619
680
 
620
681
  // keepPreviousData — don't clear state while fetching new page
@@ -624,10 +685,21 @@ const runFetch = async (config, element, context, opts = {}) => {
624
685
 
625
686
  setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: true, error: null, status: 'success', fetchStatus: 'idle' })
626
687
  triggerCallback(element, 'onFetchComplete', finalData)
688
+ triggerConfigCallback(config, element, 'onFetchComplete', finalData)
689
+ if (_emitNet) _emitNet({
690
+ phase: 'success', mode: 'query', ok: true, from, method, cacheKey,
691
+ durationMs: (typeof performance !== 'undefined' ? performance.now() : Date.now()) - __analyzeStart
692
+ })
627
693
  }
628
694
  } catch (e) {
629
695
  setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: false, error: e, status: 'error', fetchStatus: 'idle' })
630
696
  triggerCallback(element, 'onFetchError', e)
697
+ triggerConfigCallback(config, element, 'onFetchError', e)
698
+ if (_emitNet) _emitNet({
699
+ phase: 'error', mode: 'query', from, method, cacheKey,
700
+ error: e.message || String(e),
701
+ durationMs: (typeof performance !== 'undefined' ? performance.now() : Date.now()) - __analyzeStart
702
+ })
631
703
  } finally {
632
704
  if (cacheKey) activeQueries.delete(cacheKey)
633
705
  }
@@ -689,7 +761,8 @@ const bindAutoRefetch = (config, element, context) => {
689
761
  // refetchOnWindowFocus
690
762
  if (config.refetchOnWindowFocus !== false) {
691
763
  const onFocus = () => {
692
- const cacheKey = buildCacheKey(config)
764
+ const lang = resolveLanguage(element, context)
765
+ const cacheKey = buildCacheKey(config, lang)
693
766
  const entry = getCacheEntry(cacheKey)
694
767
  const cacheConfig = parseCacheConfig(config.cache)
695
768
  if (!entry || entry.stale || (Date.now() - entry.time) > (cacheConfig?.staleTime || 0)) {
@@ -776,14 +849,20 @@ const runMutation = async (config, element, context) => {
776
849
  ref.__fetching = true
777
850
  setFetchStatus(element, { isFetching: true, isLoading: false, isStale: false, isSuccess: false, error: null, status: 'pending', fetchStatus: 'fetching' })
778
851
  triggerCallback(element, 'onFetchStart')
852
+ triggerConfigCallback(config, element, 'onFetchStart')
853
+
854
+ const _emitNet = context?.analyze?.emitNetwork
855
+ const __analyzeStart = _emitNet ? (typeof performance !== 'undefined' ? performance.now() : Date.now()) : 0
856
+ if (_emitNet) _emitNet({ phase: 'start', mode: 'mutation', from, method })
779
857
 
780
858
  try {
781
- if (auth !== false && adapter.getSession) {
859
+ if (auth !== false && adapter.auth !== false && adapter.getSession) {
782
860
  const session = await adapter.getSession()
783
861
  if (auth === true && !session) {
784
862
  if (snapshot !== undefined) rollbackOptimistic(element, config)
785
863
  const err = { message: 'Not authenticated' }
786
864
  triggerCallback(element, 'onFetchError', err)
865
+ triggerConfigCallback(config, element, 'onFetchError', err)
787
866
  return
788
867
  }
789
868
  }
@@ -810,6 +889,11 @@ const runMutation = async (config, element, context) => {
810
889
  triggerCallback(element, 'onFetchError', error)
811
890
  if (isFunction(onError)) onError(error, mutationData, element)
812
891
  if (isFunction(onSettled)) onSettled(null, error, mutationData, element)
892
+ if (_emitNet) _emitNet({
893
+ phase: 'error', mode: 'mutation', from, method,
894
+ error: error.message || String(error),
895
+ durationMs: (typeof performance !== 'undefined' ? performance.now() : Date.now()) - __analyzeStart
896
+ })
813
897
  return
814
898
  }
815
899
 
@@ -822,8 +906,13 @@ const runMutation = async (config, element, context) => {
822
906
 
823
907
  setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: true, error: null, status: 'success', fetchStatus: 'idle' })
824
908
  triggerCallback(element, 'onFetchComplete', data)
909
+ triggerConfigCallback(config, element, 'onFetchComplete', data)
825
910
  if (isFunction(onSuccess)) onSuccess(data, mutationData, element)
826
911
  if (isFunction(onSettled)) onSettled(data, null, mutationData, element)
912
+ if (_emitNet) _emitNet({
913
+ phase: 'success', mode: 'mutation', ok: true, from, method,
914
+ durationMs: (typeof performance !== 'undefined' ? performance.now() : Date.now()) - __analyzeStart
915
+ })
827
916
 
828
917
  // Invalidate related queries
829
918
  if (invalidates) {
@@ -843,8 +932,14 @@ const runMutation = async (config, element, context) => {
843
932
  if (snapshot !== undefined) rollbackOptimistic(element, config)
844
933
  setFetchStatus(element, { isFetching: false, isLoading: false, isStale: false, isSuccess: false, error: e, status: 'error', fetchStatus: 'idle' })
845
934
  triggerCallback(element, 'onFetchError', e)
935
+ triggerConfigCallback(config, element, 'onFetchError', e)
846
936
  if (isFunction(onError)) onError(e, mutationData, element)
847
937
  if (isFunction(onSettled)) onSettled(null, e, mutationData, element)
938
+ if (_emitNet) _emitNet({
939
+ phase: 'error', mode: 'mutation', from, method,
940
+ error: e.message || String(e),
941
+ durationMs: (typeof performance !== 'undefined' ? performance.now() : Date.now()) - __analyzeStart
942
+ })
848
943
  } finally {
849
944
  ref.__fetching = false
850
945
  }
@@ -987,7 +1082,31 @@ export const queryClient = {
987
1082
  },
988
1083
 
989
1084
  getCache: () => cacheStore,
990
- clear: () => removeCache()
1085
+ clear: () => removeCache(),
1086
+
1087
+ // Drop every cache entry whose lang suffix isn't `keepLang`.
1088
+ // Cache keys are of the form `${from}:${method}:${params}[:lang]` — entries
1089
+ // without a lang suffix are kept (they were inserted before lang gating
1090
+ // was active). Used by setLang() to evict stale-language entries while
1091
+ // keeping the new active lang's entries warm.
1092
+ //
1093
+ // The English-fallback policy: callers typically pass `'en'` as keepLang
1094
+ // so English bundle stays cached even after switching to e.g. French,
1095
+ // since the page's English fallback may still render where French data
1096
+ // hasn't loaded yet.
1097
+ dropNonLang: (keepLang) => {
1098
+ if (!keepLang) return
1099
+ const keepSuffix = `:${keepLang}`
1100
+ for (const [key] of cacheStore) {
1101
+ // Entry without any lang suffix: presence of `:` in middle but not
1102
+ // matching any 2-3 letter ISO suffix at the end. Conservative: keep
1103
+ // entries whose tail doesn't look like `:xx` or `:xxx`.
1104
+ const tailMatch = key.match(/:([a-z]{2,3})$/i)
1105
+ if (!tailMatch) continue // no lang suffix → keep
1106
+ if (key.endsWith(keepSuffix)) continue // matches keepLang → keep
1107
+ removeCache(key)
1108
+ }
1109
+ }
991
1110
  }
992
1111
 
993
1112
  // --- Plugin interface ---
@@ -1010,8 +1129,10 @@ export const fetchPlugin = {
1010
1129
 
1011
1130
  // Hook into element creation to auto-execute fetch configs
1012
1131
  create (element) {
1013
- const fetchProp = element.fetch || element.props?.fetch
1132
+ const fetchProp = element.fetch
1014
1133
  if (!fetchProp) return
1134
+ // Clear stale in-flight queries on root element creation
1135
+ if (!element.parent) activeQueries.clear()
1015
1136
  executeFetch(fetchProp, element, element.state, element.context)
1016
1137
  }
1017
1138
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/fetch",
3
- "version": "3.8.9",
3
+ "version": "3.14.0",
4
4
  "license": "CC-BY-NC-4.0",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -25,7 +25,7 @@
25
25
  "adapters"
26
26
  ],
27
27
  "dependencies": {
28
- "@domql/utils": "^3.8.9"
28
+ "@symbo.ls/utils": "^3.14.0"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "@supabase/supabase-js": "^2.0.0"