@bagelink/vue 1.8.9 → 1.8.12

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.
@@ -26,7 +26,7 @@ export * as bagelFormUtils from './BagelFormUtils';
26
26
  export { useForm } from './BagelFormUtils';
27
27
  export { useLang } from './lang';
28
28
  export type { ComparisonOperator, FilterCondition, LogicalOperator, QueryConditions, QueryFilter } from './queryFilter';
29
- export { anyOf, parseQuery, query, range, search } from './queryFilter';
29
+ export { anyOf, parseQuery, queryFilter, range, search } from './queryFilter';
30
30
  export { formatString } from './strings';
31
31
  export declare function pathKeyToURL(pathKey?: string): string | undefined;
32
32
  export declare function getNestedValue(obj: any, path?: string, defaultValue?: any): any;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
@@ -57,7 +57,7 @@ export type QueryConditions<T extends Record<string, any> = Record<string, any>>
57
57
  * // → 'name eq "John" and age gt 18'
58
58
  * ```
59
59
  */
60
- export declare function buildQuery<T extends Record<string, any>>(conditions: QueryConditions<T>): string;
60
+ export declare function buildQuery<T extends Record<string, any>>(conditions?: QueryConditions<T>): string;
61
61
  /**
62
62
  * Query Builder for SCIM 2.0 filtering
63
63
  */
@@ -145,22 +145,26 @@ export declare class QueryFilter<T extends Record<string, any>> {
145
145
  private formatValue;
146
146
  }
147
147
  /**
148
- * Build a query string from conditions
148
+ * Build a query string from conditions or create a chainable query builder
149
149
  *
150
150
  * @example
151
151
  * ```typescript
152
152
  * // From conditions array
153
- * query<Person>([
153
+ * queryFilter<Person>([
154
154
  * { field: 'first_name', op: 'eq', value: 'John' },
155
155
  * { field: 'age', op: 'gt', value: 18, connector: 'and' },
156
- * ])
156
+ * ]).build()
157
+ * // → 'first_name eq "John" and age gt 18'
158
+ *
159
+ * // Chainable builder
160
+ * queryFilter<Person>().eq('first_name', 'John').and().gt('age', 18).build()
157
161
  * // → 'first_name eq "John" and age gt 18'
158
162
  *
159
- * // Empty returns empty string
160
- * query([]) // → ''
163
+ * // Empty returns empty builder
164
+ * queryFilter() // → QueryFilter instance
161
165
  * ```
162
166
  */
163
- export declare function query<T extends Record<string, any>>(conditions: QueryConditions<T>): string;
167
+ export declare function queryFilter<T extends Record<string, any>>(conditions?: QueryConditions<T>): QueryFilter<T>;
164
168
  /**
165
169
  * Create a query that checks if any of the fields match the value
166
170
  */
@@ -188,6 +192,6 @@ export declare function search<T extends Record<string, any>>(fields: Array<Deep
188
192
  * // → [{ field: 'email', op: 'pr' }]
189
193
  * ```
190
194
  */
191
- export declare function parseQuery<T extends Record<string, any> = Record<string, any>>(queryString: string): QueryConditions<T>;
195
+ export declare function parseQuery<T extends Record<string, any> = Record<string, any>>(queryString?: string): QueryConditions<T>;
192
196
  export {};
193
197
  //# sourceMappingURL=queryFilter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"queryFilter.d.ts","sourceRoot":"","sources":["../../src/utils/queryFilter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"queryFilter.d.ts","sourceRoot":"","sources":["../../src/utils/queryFilter.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/vue",
3
3
  "type": "module",
4
- "version": "1.8.9",
4
+ "version": "1.8.12",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
@@ -1,25 +1,18 @@
1
1
  <script setup lang="ts" generic="T extends Record<string, any>">
2
2
  import type { Option } from '@bagelink/vue'
3
- import type { ComparisonOperator } from '../utils/queryFilter'
3
+ import type { ComparisonOperator, LogicalOperator, QueryConditions } from './queryFilter'
4
4
  import { Btn, DateInput, Dropdown, Icon, SelectInput, TextInput } from '@bagelink/vue'
5
5
  import { computed } from 'vue'
6
6
 
7
7
  type OptionsSource = Option[] | ((query: string) => Promise<Option[]>)
8
8
  type Language = 'he' | 'en'
9
9
 
10
- // Define missing types locally
11
- type QueryObject<T = Record<string, any>> = Record<string, any> & T
12
- interface Condition {
13
- pr?: boolean
14
- [operator: string]: any
15
- }
16
-
17
- interface FilterCondition {
10
+ interface UICondition {
18
11
  id: string
19
12
  field: string
20
13
  operator: ComparisonOperator
21
14
  value: string
22
- connector: 'and' | 'or' | ''
15
+ connector: LogicalOperator | ''
23
16
  }
24
17
 
25
18
  const props = defineProps<{
@@ -33,10 +26,15 @@ const props = defineProps<{
33
26
  }>()
34
27
 
35
28
  const emit = defineEmits<{
36
- change: [value: QueryObject<T>]
29
+ change: [value: QueryConditions<T>]
37
30
  }>()
38
31
 
39
- const model = defineModel<QueryObject<T>>({ default: () => ({}) })
32
+ const model = defineModel<QueryConditions<T>>({ default: () => [] })
33
+
34
+ let conditionIdCounter = 0
35
+ function generateId() {
36
+ return `cond_${++conditionIdCounter}_${Date.now()}`
37
+ }
40
38
 
41
39
  // Localization data
42
40
  const texts = {
@@ -113,45 +111,27 @@ const texts = {
113
111
  // Get current language texts
114
112
  const currentTexts = computed(() => texts[props.language || 'en'])
115
113
 
116
- // Derive conditions directly from model (read-only view)
117
- const conditions = computed(() => {
118
- const obj = model.value
119
- if (Object.keys(obj).filter(k => !k.startsWith('_')).length === 0) {
120
- return [] as FilterCondition[]
121
- }
122
-
123
- const result: FilterCondition[] = []
124
- const useOr = (obj as any)._or === true
125
-
126
- for (const [field, value] of Object.entries(obj)) {
127
- if (field.startsWith('_')) continue
114
+ // Map model conditions to UI conditions with stable IDs
115
+ const conditionIds = new Map<number, string>()
128
116
 
129
- const condition: FilterCondition = {
130
- id: field, // use field as id for stability
131
- field,
132
- operator: 'eq',
133
- value: '',
134
- connector: result.length > 0 ? (useOr ? 'or' : 'and') : '',
117
+ const conditions = computed(() => {
118
+ const arr = model.value || []
119
+ return arr.map((cond, index) => {
120
+ // Get or create stable ID for this index
121
+ if (!conditionIds.has(index)) {
122
+ conditionIds.set(index, generateId())
135
123
  }
136
-
137
- if (value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value)) {
138
- const cond = value as Condition
139
- if (cond.pr === true) {
140
- condition.operator = 'pr'
141
- } else {
142
- const op = Object.keys(cond)[0] as ComparisonOperator
143
- condition.operator = op
144
- const val = (cond as any)[op]
145
- condition.value = val === null ? 'null' : String(val)
146
- }
147
- } else if (value !== undefined) {
148
- condition.value = value === null ? 'null' : String(value)
124
+ const id = conditionIds.get(index)!
125
+
126
+ const uiCond: UICondition = {
127
+ id,
128
+ field: cond.field as string,
129
+ operator: cond.op,
130
+ value: cond.value === null ? 'null' : cond.value === undefined ? '' : String(cond.value),
131
+ connector: index > 0 ? (cond.connector || 'and') : '',
149
132
  }
150
-
151
- result.push(condition)
152
- }
153
-
154
- return result
133
+ return uiCond
134
+ })
155
135
  })
156
136
 
157
137
  const allOperatorOptions = computed(() => [
@@ -191,43 +171,60 @@ const booleanOptions = computed(() => [
191
171
  { label: currentTexts.value.boolean.false, value: 'false' },
192
172
  ])
193
173
 
194
- // Update a single field in model
195
- function updateCondition(field: string, operator: ComparisonOperator, value: string) {
196
- const newModel = { ...model.value }
197
-
198
- let parsedValue: any = value
199
- if (value === 'true') parsedValue = true
200
- else if (value === 'false') parsedValue = false
201
- else if (value === 'null') parsedValue = null
202
- else if (!Number.isNaN(Number(value)) && value.trim() !== '') parsedValue = Number(value)
203
-
204
- if (operator === 'eq') {
205
- (newModel as any)[field] = parsedValue
206
- } else if (operator === 'pr') {
207
- (newModel as any)[field] = { pr: true }
208
- } else {
209
- (newModel as any)[field] = { [operator]: parsedValue }
210
- }
174
+ function parseValue(value: string): string | number | boolean | null {
175
+ if (value === 'true') return true
176
+ if (value === 'false') return false
177
+ if (value === 'null') return null
178
+ if (!Number.isNaN(Number(value)) && value.trim() !== '') return Number(value)
179
+ return value
180
+ }
181
+
182
+ function updateConditionAtIndex(index: number, updates: Partial<{ field: string, op: ComparisonOperator, value: string, connector: LogicalOperator }>) {
183
+ const newModel = [...model.value]
184
+ const existing = newModel[index]
185
+ if (!existing) return
211
186
 
187
+ const updated = { ...existing }
188
+ if (updates.field !== undefined) updated.field = updates.field
189
+ if (updates.op !== undefined) updated.op = updates.op
190
+ if (updates.value !== undefined) updated.value = parseValue(updates.value)
191
+ if (updates.connector !== undefined) updated.connector = updates.connector
192
+
193
+ newModel[index] = updated
212
194
  model.value = newModel
213
195
  emit('change', newModel)
214
196
  }
215
197
 
216
198
  function addCondition() {
217
- const field = props.fields[0]?.value || 'new_field'
218
- updateCondition(field, 'eq', '')
199
+ const field = props.fields[0]?.value || 'field'
200
+ const newCondition = {
201
+ field,
202
+ op: 'eq' as ComparisonOperator,
203
+ value: '' as any,
204
+ connector: model.value.length > 0 ? 'and' as LogicalOperator : undefined,
205
+ }
206
+ const newModel = [...model.value, newCondition]
207
+ model.value = newModel
208
+ emit('change', newModel)
219
209
  }
220
210
 
221
- function removeCondition(field: string) {
222
- const newModel = { ...model.value }
223
- Reflect.deleteProperty(newModel, field)
211
+ function removeCondition(index: number) {
212
+ const newModel = [...model.value]
213
+ newModel.splice(index, 1)
214
+ // Clear connector on new first item
215
+ if (newModel.length > 0 && newModel[0].connector) {
216
+ newModel[0] = { ...newModel[0], connector: undefined }
217
+ }
218
+ // Clean up ID mapping
219
+ conditionIds.delete(index)
224
220
  model.value = newModel
225
221
  emit('change', newModel)
226
222
  }
227
223
 
228
224
  function clearAll() {
229
- model.value = {} as QueryObject<T>
230
- emit('change', {} as QueryObject<T>)
225
+ conditionIds.clear()
226
+ model.value = []
227
+ emit('change', [])
231
228
  }
232
229
 
233
230
  function getFieldType(fieldValue: string): string {
@@ -242,35 +239,28 @@ const activeFiltersCount = computed(() => {
242
239
  return conditions.value.filter(c => Boolean(c.field) && (c.operator === 'pr' || Boolean(c.value))).length
243
240
  })
244
241
 
245
- // Handle field change - need to rename the key in model
246
- function onFieldChange(oldField: string, newField: string, condition: FilterCondition) {
247
- const newModel = { ...model.value }
248
- Reflect.deleteProperty(newModel, oldField)
249
-
250
- let parsedValue: any = condition.value
251
- if (condition.value === 'true') parsedValue = true
252
- else if (condition.value === 'false') parsedValue = false
253
- else if (condition.value === 'null') parsedValue = null
254
- else if (!Number.isNaN(Number(condition.value)) && condition.value.trim() !== '') parsedValue = Number(condition.value)
255
-
256
- if (condition.operator === 'eq') {
257
- (newModel as any)[newField] = parsedValue
258
- } else if (condition.operator === 'pr') {
259
- (newModel as any)[newField] = { pr: true }
260
- } else {
261
- (newModel as any)[newField] = { [condition.operator]: parsedValue }
262
- }
242
+ function getConditionIndex(id: string): number {
243
+ return conditions.value.findIndex(c => c.id === id)
244
+ }
263
245
 
264
- model.value = newModel
265
- emit('change', newModel)
246
+ function onFieldChange(id: string, newField: string) {
247
+ const index = getConditionIndex(id)
248
+ if (index >= 0) updateConditionAtIndex(index, { field: newField })
249
+ }
250
+
251
+ function onOperatorChange(id: string, operator: ComparisonOperator) {
252
+ const index = getConditionIndex(id)
253
+ if (index >= 0) updateConditionAtIndex(index, { op: operator })
266
254
  }
267
255
 
268
- function onOperatorChange(field: string, operator: ComparisonOperator, value: string) {
269
- updateCondition(field, operator, value)
256
+ function onValueChange(id: string, value: string) {
257
+ const index = getConditionIndex(id)
258
+ if (index >= 0) updateConditionAtIndex(index, { value })
270
259
  }
271
260
 
272
- function onValueChange(field: string, operator: ComparisonOperator, value: string) {
273
- updateCondition(field, operator, value)
261
+ function onConnectorChange(id: string, connector: LogicalOperator) {
262
+ const index = getConditionIndex(id)
263
+ if (index >= 0) updateConditionAtIndex(index, { connector })
274
264
  }
275
265
  </script>
276
266
 
@@ -293,21 +283,31 @@ function onValueChange(field: string, operator: ComparisonOperator, value: strin
293
283
  <TransitionGroup name="condition">
294
284
  <div
295
285
  v-for="(condition, index) in conditions" :key="condition.id"
296
- class="grid filter-row gap-025 align-items-center p-025"
286
+ class="grid filter-row gap-025 align-items-center pt-025"
287
+ :class="{
288
+ 'mt-025': index > 0 && condition.connector === 'or',
289
+ 'pt-075 border-top-or': index > 0 && condition.connector === 'or',
290
+ }"
297
291
  >
298
- <!-- Connector (AND/OR) - display only for now -->
299
- <div v-if="index > 0" class="min-w-20px">
300
- <span v-if="condition.connector === 'or'" class="txt12 opacity-6">{{
301
- currentTexts.connectors.or }}</span>
302
- <span v-else class="txt12 opacity-6">{{ currentTexts.connectors.and }}</span>
292
+ <!-- Connector (AND/OR) -->
293
+ <div v-if="index > 0" class="min-w-60px">
294
+ <SelectInput
295
+ :model-value="condition.connector || 'and'"
296
+ :options="[
297
+ { label: currentTexts.connectors.and, value: 'and' },
298
+ { label: currentTexts.connectors.or, value: 'or' },
299
+ ]"
300
+ class="m-0 and-or-select txt-12"
301
+ @update:model-value="(v: LogicalOperator) => onConnectorChange(condition.id, v)"
302
+ />
303
303
  </div>
304
- <div v-else class="min-w-20px" />
304
+ <div v-else class="" />
305
305
 
306
306
  <!-- Field selector -->
307
307
  <SelectInput
308
308
  :model-value="condition.field" :options="fields"
309
309
  :placeholder="currentTexts.placeholders.selectField" class="m-0 light-input borderHover"
310
- @update:model-value="(v: string) => onFieldChange(condition.field, v, condition)"
310
+ @update:model-value="(v: string) => onFieldChange(condition.id, v)"
311
311
  />
312
312
 
313
313
  <!-- Operator selector -->
@@ -315,7 +315,7 @@ function onValueChange(field: string, operator: ComparisonOperator, value: strin
315
315
  :model-value="condition.operator"
316
316
  :options="getOperatorsForType(getFieldType(condition.field), !!getFieldOptions(condition.field))"
317
317
  class="m-0 light-input borderHover"
318
- @update:model-value="(v: ComparisonOperator) => onOperatorChange(condition.field, v, condition.value)"
318
+ @update:model-value="(v: ComparisonOperator) => onOperatorChange(condition.id, v)"
319
319
  />
320
320
 
321
321
  <!-- Value input - type-specific -->
@@ -325,31 +325,31 @@ function onValueChange(field: string, operator: ComparisonOperator, value: strin
325
325
  :options="getFieldOptions(condition.field)!"
326
326
  :placeholder="currentTexts.placeholders.selectOption"
327
327
  class="m-0 light-input borderHover" searchable
328
- @update:model-value="(v: string) => onValueChange(condition.field, condition.operator, v)"
328
+ @update:model-value="(v: string) => onValueChange(condition.id, v)"
329
329
  />
330
330
  <SelectInput
331
331
  v-else-if="getFieldType(condition.field) === 'boolean'"
332
332
  :model-value="condition.value" :options="booleanOptions"
333
333
  :placeholder="currentTexts.placeholders.selectOption"
334
334
  class="m-0 light-input borderHover"
335
- @update:model-value="(v: string) => onValueChange(condition.field, condition.operator, v)"
335
+ @update:model-value="(v: string) => onValueChange(condition.id, v)"
336
336
  />
337
337
  <DateInput
338
338
  v-else-if="getFieldType(condition.field) === 'date'"
339
339
  :model-value="condition.value" :placeholder="currentTexts.placeholders.selectDate"
340
340
  class="m-0 light-input borderHover"
341
- @update:model-value="(v: any) => onValueChange(condition.field, condition.operator, String(v))"
341
+ @update:model-value="(v: any) => onValueChange(condition.id, String(v))"
342
342
  />
343
343
  <TextInput
344
344
  v-else-if="getFieldType(condition.field) === 'number'"
345
345
  :model-value="condition.value" placeholder="0" type="number"
346
346
  class="m-0 light-input borderHover"
347
- @update:model-value="(v: string) => onValueChange(condition.field, condition.operator, v)"
347
+ @update:model-value="(v: string) => onValueChange(condition.id, v)"
348
348
  />
349
349
  <TextInput
350
350
  v-else :model-value="condition.value"
351
351
  :placeholder="currentTexts.placeholders.enterValue" class="m-0 light-input borderHover"
352
- @update:model-value="(v: string) => onValueChange(condition.field, condition.operator, v)"
352
+ @update:model-value="(v: string) => onValueChange(condition.id, v)"
353
353
  />
354
354
  </template>
355
355
  <div v-else />
@@ -357,7 +357,7 @@ function onValueChange(field: string, operator: ComparisonOperator, value: strin
357
357
  <!-- Remove button -->
358
358
  <Btn
359
359
  icon="close" thin flat size="small" class="m_bg-gray-40 m_px-3 m_mx-auto"
360
- @click="removeCondition(condition.field)"
360
+ @click="removeCondition(index)"
361
361
  />
362
362
  </div>
363
363
  </TransitionGroup>
@@ -375,8 +375,20 @@ function onValueChange(field: string, operator: ComparisonOperator, value: strin
375
375
  </template>
376
376
 
377
377
  <style scoped>
378
+ .and-or-select {
379
+ --input-font-size: 12px !important;
380
+ --input-background-color: transparent !important;
381
+ }
382
+ .and-or-select .selectinput-btn {
383
+ border: none;
384
+ width: 100%;
385
+ box-shadow: none;
386
+ padding: 0.25rem !important;
387
+ font-size: 12px !important;
388
+ }
389
+
378
390
  .filter-row {
379
- grid-template-columns: auto minmax(90px, 0.5fr) minmax(170px, 0.75fr) minmax(120px, 0.5fr) auto;
391
+ grid-template-columns: 50px minmax(90px, 0.5fr) minmax(170px, 0.75fr) minmax(120px, 0.5fr) auto;
380
392
  }
381
393
 
382
394
  /* Transition animations */
package/src/index.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import './styles/bagel.css'
2
+
1
3
  export * from './components'
2
4
  export * from './components/form/useBagelFormState'
3
5
  export * from './composables'
@@ -14,7 +16,6 @@ export * from './types'
14
16
  export * from './utils'
15
17
  export * from './utils/allCountries'
16
18
  export * from './utils/BagelFormUtils'
17
- import './styles/bagel.css'
18
19
 
19
20
  export * from './utils/calendar/dateUtils'
20
21
  export * from './utils/constants'
@@ -204,7 +204,7 @@ export { useForm } from './BagelFormUtils'
204
204
  export { useLang } from './lang'
205
205
 
206
206
  export type { ComparisonOperator, FilterCondition, LogicalOperator, QueryConditions, QueryFilter } from './queryFilter'
207
- export { anyOf, parseQuery, query, range, search } from './queryFilter'
207
+ export { anyOf, parseQuery, queryFilter, range, search } from './queryFilter'
208
208
  export { formatString } from './strings'
209
209
 
210
210
  const URL_REGEX = /^https?:\/\/|^\/\//
@@ -32,17 +32,17 @@ type DeepKeyOf<T> = T extends Primitive
32
32
  /**
33
33
  * SCIM 2.0 comparison operators
34
34
  */
35
- export type ComparisonOperator =
36
- | 'eq' // equal
37
- | 'ne' // not equal
38
- | 'gt' // greater than
39
- | 'ge' // greater than or equal
40
- | 'lt' // less than
41
- | 'le' // less than or equal
42
- | 'co' // contains
43
- | 'sw' // starts with
44
- | 'ew' // ends with
45
- | 'pr' // present (has value)
35
+ export type ComparisonOperator
36
+ = | 'eq' // equal
37
+ | 'ne' // not equal
38
+ | 'gt' // greater than
39
+ | 'ge' // greater than or equal
40
+ | 'lt' // less than
41
+ | 'le' // less than or equal
42
+ | 'co' // contains
43
+ | 'sw' // starts with
44
+ | 'ew' // ends with
45
+ | 'pr' // present (has value)
46
46
 
47
47
  /**
48
48
  * SCIM 2.0 logical operators
@@ -90,7 +90,7 @@ function formatValue(value: Primitive): string {
90
90
  * ```
91
91
  */
92
92
  export function buildQuery<T extends Record<string, any>>(
93
- conditions: QueryConditions<T>
93
+ conditions?: QueryConditions<T>
94
94
  ): string {
95
95
  if (!conditions || conditions.length === 0) return ''
96
96
 
@@ -298,25 +298,33 @@ export class QueryFilter<T extends Record<string, any>> {
298
298
  }
299
299
 
300
300
  /**
301
- * Build a query string from conditions
301
+ * Build a query string from conditions or create a chainable query builder
302
302
  *
303
303
  * @example
304
304
  * ```typescript
305
305
  * // From conditions array
306
- * query<Person>([
306
+ * queryFilter<Person>([
307
307
  * { field: 'first_name', op: 'eq', value: 'John' },
308
308
  * { field: 'age', op: 'gt', value: 18, connector: 'and' },
309
- * ])
309
+ * ]).build()
310
+ * // → 'first_name eq "John" and age gt 18'
311
+ *
312
+ * // Chainable builder
313
+ * queryFilter<Person>().eq('first_name', 'John').and().gt('age', 18).build()
310
314
  * // → 'first_name eq "John" and age gt 18'
311
315
  *
312
- * // Empty returns empty string
313
- * query([]) // → ''
316
+ * // Empty returns empty builder
317
+ * queryFilter() // → QueryFilter instance
314
318
  * ```
315
319
  */
316
- export function query<T extends Record<string, any>>(
317
- conditions: QueryConditions<T>
318
- ): string {
319
- return buildQuery(conditions)
320
+ export function queryFilter<T extends Record<string, any>>(
321
+ conditions?: QueryConditions<T>
322
+ ): QueryFilter<T> {
323
+ const builder = new QueryFilter<T>()
324
+ if (conditions && conditions.length > 0) {
325
+ builder.raw(buildQuery(conditions))
326
+ }
327
+ return builder
320
328
  }
321
329
 
322
330
  /**
@@ -401,7 +409,7 @@ function parseValue(valueStr: string): Primitive {
401
409
  * ```
402
410
  */
403
411
  export function parseQuery<T extends Record<string, any> = Record<string, any>>(
404
- queryString: string
412
+ queryString?: string
405
413
  ): QueryConditions<T> {
406
414
  if (!queryString || !queryString.trim()) return []
407
415