@domql/utils 3.6.3 → 3.6.6

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/extends.js CHANGED
@@ -334,7 +334,7 @@ export const getExtendsInElement = obj => {
334
334
  function traverse (o) {
335
335
  for (const key in o) {
336
336
  if (Object.prototype.hasOwnProperty.call(o, key)) {
337
- // Check if the key starts with a capital letter and exclude keys like @mobileL, $propsCollection
337
+ // Check if the key starts with a capital letter and exclude keys like @mobileL, $router
338
338
  if (matchesComponentNaming(key)) {
339
339
  result.push(key)
340
340
  }
package/keys.js CHANGED
@@ -35,6 +35,7 @@ export const DOMQ_PROPERTIES = new Set([
35
35
  'node',
36
36
  'variables',
37
37
  'on',
38
+ 'fetch',
38
39
  'component',
39
40
  'context'
40
41
  ])
@@ -138,6 +139,8 @@ export const METHODS = new Set([
138
139
  'getRootData',
139
140
  'getRootContext',
140
141
  'getContext',
142
+ 'getQuery',
143
+ 'getDB',
141
144
  'getChildren'
142
145
  ])
143
146
 
@@ -163,5 +166,7 @@ export const DOMQL_EVENTS = new Set([
163
166
  'create',
164
167
  'complete',
165
168
  'frame',
166
- 'update'
169
+ 'update',
170
+ 'fetchError',
171
+ 'fetchComplete'
167
172
  ])
package/methods.js CHANGED
@@ -6,6 +6,7 @@ import { isDefined, isFunction, isObject, isObjectLike } from './types.js'
6
6
  import { deepClone } from './object.js'
7
7
  import { isProduction } from './env.js'
8
8
  import { removeValueFromArray } from './array.js'
9
+ import { OPTIONS } from './cache.js'
9
10
  const ENV = process.env.NODE_ENV
10
11
 
11
12
  // TODO: update these files
@@ -398,6 +399,238 @@ export function call (fnKey, ...args) {
398
399
  }
399
400
  }
400
401
 
402
+ export async function getDB () {
403
+ const element = this
404
+ const db = element.context?.db
405
+ if (!db) return null
406
+ if (typeof db.select === 'function') {
407
+ if (!db.__authInitialized && db.getSession) {
408
+ const { initAdapterAuth } = await import('@symbo.ls/fetch')
409
+ await initAdapterAuth(db, element.context)
410
+ }
411
+ return db
412
+ }
413
+ if (db.__resolved) {
414
+ if (!db.__resolved.__authInitialized && db.__resolved.getSession) {
415
+ const { initAdapterAuth } = await import('@symbo.ls/fetch')
416
+ await initAdapterAuth(db.__resolved, element.context)
417
+ }
418
+ return db.__resolved
419
+ }
420
+ if (db.__resolving) return db.__resolving
421
+ // Set __resolving synchronously BEFORE the async import to prevent
422
+ // resolveAdapter() from starting a parallel resolveDb() during the yield
423
+ const resolvePromise = import('@symbo.ls/fetch').then(({ resolveDb }) => resolveDb(db))
424
+ db.__resolving = resolvePromise
425
+ const resolved = await resolvePromise
426
+ db.__resolved = resolved
427
+ element.context.db = resolved
428
+ delete db.__resolving
429
+ if (resolved.getSession) {
430
+ const { initAdapterAuth } = await import('@symbo.ls/fetch')
431
+ await initAdapterAuth(resolved, element.context)
432
+ }
433
+ return resolved
434
+ }
435
+
436
+ export function getQuery (format) {
437
+ const element = this
438
+ const useStateQuery = format || OPTIONS.useStateQuery || element.context?.useStateQuery
439
+ if (!useStateQuery) return null
440
+
441
+ const query = {}
442
+ buildQueryFromElement(element, query)
443
+
444
+ if (useStateQuery === true) return query
445
+ const formatter = QUERY_FORMATTERS[useStateQuery]
446
+ return formatter ? formatter(query) : query
447
+ }
448
+
449
+ const buildQueryFromElement = (element, query) => {
450
+ const ref = element.__ref
451
+ if (!ref) return
452
+
453
+ const stateKey = ref.__state
454
+ if (stateKey && typeof stateKey === 'string') {
455
+ setQueryPath(query, stateKey)
456
+ }
457
+
458
+ const children = ref.__children
459
+ if (children) {
460
+ for (let i = 0; i < children.length; i++) {
461
+ const child = element[children[i]]
462
+ if (child && child.__ref) {
463
+ buildQueryFromElement(child, query)
464
+ }
465
+ }
466
+ }
467
+
468
+ const contentKey = ref.contentElementKey || 'content'
469
+ const content = element[contentKey]
470
+ if (content && content.__ref) {
471
+ buildQueryFromElement(content, query)
472
+ }
473
+ }
474
+
475
+ const setQueryPath = (query, path) => {
476
+ const clean = path.replaceAll('../', '').replaceAll('~/', '')
477
+ const parts = clean.split('/')
478
+ let current = query
479
+ for (let i = 0; i < parts.length; i++) {
480
+ const part = parts[i]
481
+ if (i === parts.length - 1) {
482
+ if (!current[part]) current[part] = true
483
+ } else {
484
+ if (current[part] === true) current[part] = {}
485
+ if (!current[part]) current[part] = {}
486
+ current = current[part]
487
+ }
488
+ }
489
+ }
490
+
491
+ // --- Query formatters ---
492
+
493
+ const queryToGraphQL = (query, indent = 0) => {
494
+ const pad = ' '.repeat(indent)
495
+ const entries = Object.entries(query)
496
+ if (!entries.length) return ''
497
+ const fields = entries.map(([key, value]) => {
498
+ if (value === true) return `${pad}${key}`
499
+ return `${pad}${key} {\n${queryToGraphQL(value, indent + 1)}\n${pad}}`
500
+ })
501
+ return fields.join('\n')
502
+ }
503
+
504
+ const formatGraphQL = query => {
505
+ return `{\n${queryToGraphQL(query, 1)}\n}`
506
+ }
507
+
508
+ const formatJsonApi = query => {
509
+ const result = { fields: {}, include: [] }
510
+ const walk = (obj, prefix) => {
511
+ const fields = []
512
+ for (const key in obj) {
513
+ if (obj[key] === true) {
514
+ fields.push(key)
515
+ } else {
516
+ const nested = prefix ? `${prefix}.${key}` : key
517
+ result.include.push(nested)
518
+ walk(obj[key], nested)
519
+ }
520
+ }
521
+ if (fields.length) {
522
+ result.fields[prefix || '_root'] = fields
523
+ }
524
+ }
525
+ walk(query, '')
526
+ return result
527
+ }
528
+
529
+ const formatOData = query => {
530
+ const build = obj => {
531
+ const select = []
532
+ const expand = []
533
+ for (const key in obj) {
534
+ if (obj[key] === true) {
535
+ select.push(key)
536
+ } else {
537
+ const nested = build(obj[key])
538
+ const parts = []
539
+ if (nested.select) parts.push(`$select=${nested.select}`)
540
+ if (nested.expand) parts.push(`$expand=${nested.expand}`)
541
+ expand.push(parts.length ? `${key}(${parts.join(';')})` : key)
542
+ }
543
+ }
544
+ return {
545
+ select: select.length ? select.join(',') : '',
546
+ expand: expand.length ? expand.join(',') : ''
547
+ }
548
+ }
549
+ const { select, expand } = build(query)
550
+ const parts = []
551
+ if (select) parts.push(`$select=${select}`)
552
+ if (expand) parts.push(`$expand=${expand}`)
553
+ return parts.join('&')
554
+ }
555
+
556
+ const formatSQL = (query, options) => {
557
+ const columns = []
558
+ const joins = []
559
+ const tables = new Set()
560
+
561
+ const walk = (obj, table, parentTable) => {
562
+ tables.add(table)
563
+ if (parentTable) {
564
+ joins.push(
565
+ `LEFT JOIN ${table} ON ${table}.${parentTable}_id = ${parentTable}.id`
566
+ )
567
+ }
568
+ for (const key in obj) {
569
+ if (obj[key] === true) {
570
+ columns.push(`${table}.${key}`)
571
+ } else {
572
+ walk(obj[key], key, table)
573
+ }
574
+ }
575
+ }
576
+
577
+ const roots = Object.keys(query)
578
+ if (roots.length === 1 && query[roots[0]] !== true) {
579
+ walk(query[roots[0]], roots[0], null)
580
+ } else {
581
+ walk(query, '_root', null)
582
+ }
583
+
584
+ const from = [...tables][0] || '_root'
585
+ const select = columns.length ? columns.join(', ') : '*'
586
+ const joinStr = joins.length ? '\n' + joins.join('\n') : ''
587
+ return `SELECT ${select}\nFROM ${from}${joinStr}`
588
+ }
589
+
590
+ const formatPaths = query => {
591
+ const paths = []
592
+ const walk = (obj, prefix) => {
593
+ for (const key in obj) {
594
+ const path = prefix ? `${prefix}/${key}` : key
595
+ if (obj[key] === true) {
596
+ paths.push(path)
597
+ } else {
598
+ walk(obj[key], path)
599
+ }
600
+ }
601
+ }
602
+ walk(query, '')
603
+ return paths
604
+ }
605
+
606
+ const formatSupabase = query => {
607
+ const build = obj => {
608
+ const parts = []
609
+ for (const key in obj) {
610
+ if (obj[key] === true) {
611
+ parts.push(key)
612
+ } else {
613
+ parts.push(`${key}(${build(obj[key])})`)
614
+ }
615
+ }
616
+ return parts.join(', ')
617
+ }
618
+ const roots = Object.keys(query)
619
+ if (roots.length === 1 && query[roots[0]] !== true) {
620
+ return { from: roots[0], select: build(query[roots[0]]) }
621
+ }
622
+ return { from: null, select: build(query) }
623
+ }
624
+
625
+ const QUERY_FORMATTERS = {
626
+ graphql: formatGraphQL,
627
+ 'json-api': formatJsonApi,
628
+ odata: formatOData,
629
+ sql: formatSQL,
630
+ supabase: formatSupabase,
631
+ paths: formatPaths
632
+ }
633
+
401
634
  export function isMethod (param, element) {
402
635
  return Boolean(METHODS.has(param) || element?.context?.methods?.[param])
403
636
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@domql/utils",
3
- "version": "3.6.3",
3
+ "version": "3.6.6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "module": "./dist/esm/index.js",
@@ -37,6 +37,14 @@
37
37
  "build:iife": "cross-env NODE_ENV=$NODE_ENV esbuild index.js --bundle --target=es2020 --format=iife --global-name=DomqlUtils --outfile=dist/iife/index.js --define:process.env.NODE_ENV=process.env.NODE_ENV --external:@domql/element"
38
38
  },
39
39
  "gitHead": "9fc1b79b41cdc725ca6b24aec64920a599634681",
40
+ "peerDependencies": {
41
+ "@symbo.ls/fetch": "^3.6.6"
42
+ },
43
+ "peerDependenciesMeta": {
44
+ "@symbo.ls/fetch": {
45
+ "optional": true
46
+ }
47
+ },
40
48
  "devDependencies": {
41
49
  "@babel/core": "^7.26.0"
42
50
  },
package/state.js CHANGED
@@ -5,13 +5,11 @@ import { STATE_METHODS } from './keys.js'
5
5
  import {
6
6
  deepClone,
7
7
  deepMerge,
8
- exec,
9
8
  overwriteDeep,
10
9
  overwriteShallow
11
10
  } from './object.js'
12
11
  import {
13
12
  is,
14
- isFunction,
15
13
  isObject,
16
14
  isObjectLike,
17
15
  isString
@@ -20,10 +18,7 @@ import {
20
18
  export const checkForStateTypes = element => {
21
19
  const { state: orig, props, __ref: ref } = element
22
20
  const state = props?.state || orig
23
- if (isFunction(state)) {
24
- ref.__state = state
25
- return exec(state, element)
26
- } else if (is(state)('string', 'number')) {
21
+ if (is(state)('string', 'number')) {
27
22
  ref.__state = state
28
23
  return { value: state }
29
24
  } else if (state === true) {
@@ -166,8 +161,8 @@ export const createNestedObjectByKeyPath = (path, value) => {
166
161
  }
167
162
 
168
163
  export const applyDependentState = (element, state) => {
169
- const { __element } = state //
170
- const origState = exec(__element?.state, element)
164
+ const { __element } = state
165
+ const origState = __element?.state
171
166
  if (!origState) return
172
167
  const dependentState = deepClone(origState, STATE_METHODS)
173
168
  const newDepends = { [element.key]: dependentState }