@bagelink/vue 1.15.323 → 1.15.331

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.
@@ -27,7 +27,7 @@ type DeepKeyOf<T> = T extends Primitive ? never : {
27
27
  /**
28
28
  * SCIM 2.0 comparison operators
29
29
  */
30
- export type ComparisonOperator = 'eq' | 'ne' | 'gt' | 'ge' | 'lt' | 'le' | 'co' | 'sw' | 'ew' | 'pr' | 'in' | 'nin' | 'between';
30
+ export type ComparisonOperator = 'eq' | 'ne' | 'gt' | 'ge' | 'lt' | 'le' | 'co' | 'sw' | 'sw_any' | 'ew' | 'pr' | 'is_null' | 'in' | 'nin' | 'between';
31
31
  /**
32
32
  * SCIM 2.0 logical operators
33
33
  */
@@ -95,6 +95,10 @@ export declare class QueryFilter<T extends Record<string, any>> {
95
95
  * Starts with comparison (for strings)
96
96
  */
97
97
  sw<K extends DeepKeyOf<T>>(field: K, value: string): this;
98
+ /**
99
+ * Starts with any listed prefix.
100
+ */
101
+ swAny<K extends DeepKeyOf<T>>(field: K, values: string[]): this;
98
102
  /**
99
103
  * Ends with comparison (for strings)
100
104
  */
@@ -1 +1 @@
1
- {"version":3,"file":"queryFilter.d.ts","sourceRoot":"","sources":["../../src/utils/queryFilter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;AAEjD,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,GACpC,KAAK,GACL;KACA,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;CACtD,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAA;AAEpB;;GAEG;AACH,MAAM,MAAM,kBAAkB,GACzB,IAAI,GACL,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,KAAK,GACL,SAAS,CAAA;AAEb;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,IAAI,CAAA;AAE1C;;GAEG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACnF,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,CAAA;IAC5B,EAAE,EAAE,kBAAkB,CAAA;IACtB,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAA;IAC/B,SAAS,CAAC,EAAE,eAAe,CAAA;CAC3B;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,EAAE,CAAA;AAuBvG;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACvD,UAAU,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,GAC7B,MAAM,CAgCR;AAED;;GAEG;AACH,qBAAa,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACrD,OAAO,CAAC,KAAK,CAAe;IAE5B;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI;IAK5D;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI;IAK5D;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKlE;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKlE;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKlE;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKlE;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKzD;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKzD;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKzD;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAK1C;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI;IAK/D;;OAEG;IACH,GAAG,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI;IAKhE;;OAEG;IACH,OAAO,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKzF;;OAEG;IACH,GAAG,IAAI,IAAI;IAKX;;OAEG;IACH,EAAE,IAAI,IAAI;IAKV;;OAEG;IACH,GAAG,IAAI,IAAI;IAKX;;OAEG;IACH,KAAK,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI;IAU9D;;OAEG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKxB;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,KAAK,IAAI,MAAM;IAIf;;OAEG;IACH,QAAQ,IAAI,MAAM;IAIlB;;OAEG;IACH,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM;IAOnD;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;CAc1B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACxD,UAAU,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,GAC7B,WAAW,CAAC,CAAC,CAAC,CAMhB;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClD,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,EACpC,KAAK,EAAE,SAAS,GACd,MAAM,CAQR;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClD,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,EAC5B,GAAG,EAAE,MAAM,GAAG,MAAM,EACpB,GAAG,EAAE,MAAM,GAAG,MAAM,GAClB,MAAM,CAIR;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACnD,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,EACpC,UAAU,EAAE,MAAM,GAChB,MAAM,CAQR;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CA6C/E;AA+ED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC7E,WAAW,CAAC,EAAE,MAAM,GAClB,eAAe,CAAC,CAAC,CAAC,CAKpB"}
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.15.323",
4
+ "version": "1.15.331",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
@@ -18,4 +18,6 @@ export interface FilterField {
18
18
  /** Preset query options — each option injects a raw SCIM query. Mutually exclusive with `options`. */
19
19
  queryOptions?: QueryOption[]
20
20
  multiple?: boolean
21
+ /** Additional operators intentionally supported by this filter backend. */
22
+ additionalOperators?: Array<'sw_any'>
21
23
  }
@@ -57,15 +57,15 @@ const dropdownProps = computed(() => props.inline
57
57
  placement: props.placement,
58
58
  icon: 'filter_alt',
59
59
  value: filterLabel.value,
60
- thin: true,
60
+ sm: true,
61
61
  })
62
62
 
63
63
  function isComplete(c: { field: string, op: ComparisonOperator, value?: any }) {
64
64
  if (!c.field || !c.op) return false
65
65
  // queryOption presets are complete if they have a selected value
66
66
  if (isQueryOptionField(c.field as string)) return c.value !== '' && c.value != null
67
- if (c.op === 'pr') return true
68
- if (c.op === 'in' || c.op === 'nin') return Array.isArray(c.value) && c.value.length > 0
67
+ if (c.op === 'pr' || c.op === 'is_null') return true
68
+ if (c.op === 'in' || c.op === 'nin' || c.op === 'sw_any') return Array.isArray(c.value) && c.value.length > 0
69
69
  if (c.op === 'between') {
70
70
  return Array.isArray(c.value) && c.value.length === 2
71
71
  && c.value[0] !== '' && c.value[0] != null
@@ -103,8 +103,10 @@ const currentTexts = computed(() => ({
103
103
  le: $t('filter.operators.le'),
104
104
  co: $t('filter.operators.co'),
105
105
  sw: $t('filter.operators.sw'),
106
+ sw_any: $t('filter.operators.swAny'),
106
107
  ew: $t('filter.operators.ew'),
107
108
  pr: $t('filter.operators.pr'),
109
+ is_null: $t('filter.operators.isNull'),
108
110
  in: $t('filter.operators.in'),
109
111
  nin: $t('filter.operators.nin'),
110
112
  between: $t('filter.operators.between'),
@@ -162,29 +164,35 @@ const allOperatorOptions = computed(() => [
162
164
  { label: currentTexts.value.operators.le, value: 'le' as ComparisonOperator },
163
165
  { label: currentTexts.value.operators.co, value: 'co' as ComparisonOperator },
164
166
  { label: currentTexts.value.operators.sw, value: 'sw' as ComparisonOperator },
167
+ { label: currentTexts.value.operators.sw_any, value: 'sw_any' as ComparisonOperator },
165
168
  { label: currentTexts.value.operators.ew, value: 'ew' as ComparisonOperator },
166
169
  { label: currentTexts.value.operators.pr, value: 'pr' as ComparisonOperator },
170
+ { label: currentTexts.value.operators.is_null, value: 'is_null' as ComparisonOperator },
167
171
  { label: currentTexts.value.operators.in, value: 'in' as ComparisonOperator },
168
172
  { label: currentTexts.value.operators.nin, value: 'nin' as ComparisonOperator },
169
173
  { label: currentTexts.value.operators.between, value: 'between' as ComparisonOperator },
170
174
  ])
171
175
 
172
- function getOperatorsForType(fieldType: string, hasOptions: boolean, isMultiple: boolean) {
176
+ function getOperatorsForType(fieldType: string, hasOptions: boolean, isMultiple: boolean, fieldValue: string) {
173
177
  const allow = (ops: string[]) => allOperatorOptions.value.filter(o => ops.includes(o.value))
178
+ const withAdditional = (ops: string[]) => [
179
+ ...ops,
180
+ ...(getFieldDef(fieldValue)?.additionalOperators ?? []),
181
+ ]
174
182
  if (isMultiple && hasOptions) {
175
- return allow(['in', 'nin', 'pr'])
183
+ return allow(withAdditional(['in', 'nin', 'pr', 'is_null']))
176
184
  }
177
185
  if (hasOptions) {
178
- return allow(['eq', 'ne', 'in', 'nin', 'pr'])
186
+ return allow(withAdditional(['eq', 'ne', 'in', 'nin', 'pr', 'is_null']))
179
187
  }
180
188
  switch (fieldType) {
181
189
  case 'boolean':
182
- return allow(['eq', 'ne', 'pr'])
190
+ return allow(withAdditional(['eq', 'ne', 'pr', 'is_null']))
183
191
  case 'number':
184
192
  case 'date':
185
- return allow(['eq', 'ne', 'gt', 'ge', 'lt', 'le', 'between', 'in', 'nin', 'pr'])
193
+ return allow(withAdditional(['eq', 'ne', 'gt', 'ge', 'lt', 'le', 'between', 'in', 'nin', 'pr', 'is_null']))
186
194
  default:
187
- return allow(['eq', 'ne', 'co', 'sw', 'ew', 'in', 'nin', 'pr'])
195
+ return allow(withAdditional(['eq', 'ne', 'co', 'sw', 'ew', 'in', 'nin', 'pr', 'is_null']))
188
196
  }
189
197
  }
190
198
 
@@ -346,11 +354,11 @@ function getFieldType(fieldValue: string): string {
346
354
  }
347
355
 
348
356
  function needsValue(operator: ComparisonOperator): boolean {
349
- return operator !== 'pr'
357
+ return operator !== 'pr' && operator !== 'is_null'
350
358
  }
351
359
 
352
360
  function isMultiOperator(operator: ComparisonOperator): boolean {
353
- return operator === 'in' || operator === 'nin'
361
+ return operator === 'in' || operator === 'nin' || operator === 'sw_any'
354
362
  }
355
363
 
356
364
  function isBetweenOperator(operator: ComparisonOperator): boolean {
@@ -367,6 +375,14 @@ function listToText(value: UICondition['value']): string {
367
375
  return Array.isArray(value) ? value.join(', ') : String(value ?? '')
368
376
  }
369
377
 
378
+ function selectModelValue(value: UICondition['value']): any {
379
+ return value ?? undefined
380
+ }
381
+
382
+ function selectMultiModelValue(value: UICondition['value']): any[] {
383
+ return Array.isArray(value) ? value.filter(item => item != null) : []
384
+ }
385
+
370
386
  /** Split comma-separated free-list text into a trimmed string[]. */
371
387
  function textToList(text: string): string[] {
372
388
  return text.split(',').map(s => s.trim()).filter(s => s !== '')
@@ -402,7 +418,7 @@ function onFieldChange(id: string, newField: string) {
402
418
  return
403
419
  }
404
420
  const multi = !!(fieldDef?.multiple && fieldDef?.options)
405
- const validOps = getOperatorsForType(fieldDef?.type || 'string', !!fieldDef?.options, multi)
421
+ const validOps = getOperatorsForType(fieldDef?.type || 'string', !!fieldDef?.options, multi, newField)
406
422
  const defaultOp = (multi ? 'in' : validOps[0]?.value ?? 'eq') as ComparisonOperator
407
423
  updateConditionAtIndex(index, {
408
424
  field: newField,
@@ -419,9 +435,9 @@ function onOperatorChange(id: string, operator: ComparisonOperator) {
419
435
  const updates: Parameters<typeof updateConditionAtIndex>[1] = { op: operator }
420
436
 
421
437
  // Reset the value shape when the operator's expected value type changes.
422
- const wasList = prevOp === 'in' || prevOp === 'nin'
438
+ const wasList = prevOp === 'in' || prevOp === 'nin' || prevOp === 'sw_any'
423
439
  const wasBetween = prevOp === 'between'
424
- const isList = operator === 'in' || operator === 'nin'
440
+ const isList = operator === 'in' || operator === 'nin' || operator === 'sw_any'
425
441
  const isBetween = operator === 'between'
426
442
 
427
443
  if (isBetween && !wasBetween) updates.value = ['', '']
@@ -518,7 +534,7 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
518
534
  <!-- Operator selector -->
519
535
  <SelectInput
520
536
  :model-value="condition.operator"
521
- :options="getOperatorsForType(getFieldType(condition.field), !!getFieldOptions(condition.field), isMultipleField(condition.field))"
537
+ :options="getOperatorsForType(getFieldType(condition.field), !!getFieldOptions(condition.field), isMultipleField(condition.field), condition.field)"
522
538
  class="m-0 light-input borderHover"
523
539
  @update:model-value="(v: ComparisonOperator) => onOperatorChange(condition.id, v)"
524
540
  />
@@ -563,7 +579,7 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
563
579
  <SelectInput
564
580
  v-else-if="getFieldOptions(condition.field) && isMultiOperator(condition.operator)"
565
581
  :key="`${condition.id}-${condition.field}-multi`"
566
- :model-value="Array.isArray(condition.value) ? condition.value : []"
582
+ :model-value="selectMultiModelValue(condition.value)"
567
583
  :options="getFieldOptions(condition.field)!"
568
584
  :placeholder="currentTexts.placeholders.selectOption"
569
585
  class="m-0 light-input borderHover" searchable multiselect
@@ -581,7 +597,7 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
581
597
  <SelectInput
582
598
  v-else-if="getFieldOptions(condition.field)"
583
599
  :key="`${condition.id}-${condition.field}-single`"
584
- :model-value="condition.value"
600
+ :model-value="selectModelValue(condition.value)"
585
601
  :options="getFieldOptions(condition.field)!"
586
602
  :placeholder="currentTexts.placeholders.selectOption"
587
603
  class="m-0 light-input borderHover" searchable
@@ -590,7 +606,7 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
590
606
  <SelectInput
591
607
  v-else-if="getFieldType(condition.field) === 'boolean'"
592
608
  :key="`${condition.id}-${condition.field}-bool`"
593
- :model-value="condition.value" :options="booleanOptions"
609
+ :model-value="selectModelValue(condition.value)" :options="booleanOptions"
594
610
  :placeholder="currentTexts.placeholders.selectOption"
595
611
  class="m-0 light-input borderHover"
596
612
  @update:model-value="(v: boolean) => onValueChange(condition.id, v)"
@@ -620,7 +636,7 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
620
636
 
621
637
  <!-- Remove button -->
622
638
  <Btn
623
- icon="close" thin flat size="sm"
639
+ icon="close" sm flat size="sm"
624
640
  :class="{ 'm_bg-gray-40 m_px-3 m_mx-auto': !props.inline && !props.desktopLayoutOnMobile }"
625
641
  @click="removeCondition(index)"
626
642
  />
@@ -629,14 +645,14 @@ function onConnectorChange(id: string, connector: LogicalOperator) {
629
645
  </div>
630
646
 
631
647
  <div class="flex gap-1 space-between m_flex-wrap">
632
- <Btn icon="add" flat thin :value="currentTexts.buttons.addCondition" @click="addCondition" />
648
+ <Btn icon="add" flat sm :value="currentTexts.buttons.addCondition" @click="addCondition" />
633
649
  <div class="flex gap-05">
634
650
  <Btn
635
- v-if="conditions.length > 0" icon="delete_sweep" flat thin color="gray"
651
+ v-if="conditions.length > 0" icon="delete_sweep" flat sm color="gray"
636
652
  :value="currentTexts.buttons.clearAll" @click="clearAll"
637
653
  />
638
654
  <Btn
639
- v-if="hasSaveListener" :disabled="conditions.length === 0" class="color-green" icon="save" thin
655
+ v-if="hasSaveListener" :disabled="conditions.length === 0" class="color-green" icon="save" sm
640
656
  color="green-light" :value="$t('filter.buttons.save')"
641
657
  @click="emit('save', internalModel.filter(isComplete))"
642
658
  />
@@ -230,7 +230,7 @@ function updateField(key: string, value: any, field: FieldBuilder) {
230
230
  }
231
231
 
232
232
  function isFieldEmpty(value: any, field: FieldBuilder): boolean {
233
- return value == null || value === ''
233
+ return value == null || value === '' || value === false
234
234
  || (Array.isArray(value) && value.length === 0)
235
235
  || (field._type === 'tel' && typeof value === 'string' && /^\+\d+$/.test(value))
236
236
  }
@@ -75,8 +75,10 @@
75
75
  "le": "Less or equal",
76
76
  "co": "Contains",
77
77
  "sw": "Starts with",
78
+ "swAny": "Starts with any",
78
79
  "ew": "Ends with",
79
- "pr": "Present",
80
+ "pr": "Is present",
81
+ "isNull": "Is not present",
80
82
  "in": "In list",
81
83
  "nin": "Not in list",
82
84
  "between": "Between"
@@ -74,8 +74,10 @@
74
74
  "le": "Menor o igual",
75
75
  "co": "Contiene",
76
76
  "sw": "Empieza con",
77
+ "swAny": "Empieza con cualquiera",
77
78
  "ew": "Termina con",
78
- "pr": "Presente",
79
+ "pr": "Está presente",
80
+ "isNull": "No está presente",
79
81
  "in": "En lista",
80
82
  "nin": "No en lista",
81
83
  "between": "Entre"
@@ -74,8 +74,10 @@
74
74
  "le": "Inférieur ou égal",
75
75
  "co": "Contient",
76
76
  "sw": "Commence par",
77
+ "swAny": "Commence par l’un des préfixes",
77
78
  "ew": "Se termine par",
78
- "pr": "Présent",
79
+ "pr": "Est présent",
80
+ "isNull": "N’est pas présent",
79
81
  "in": "Dans la liste",
80
82
  "nin": "Pas dans la liste",
81
83
  "between": "Entre"
@@ -75,8 +75,10 @@
75
75
  "le": "קטן או שווה ל",
76
76
  "co": "מכיל",
77
77
  "sw": "מתחיל ב",
78
+ "swAny": "מתחיל באחד מ",
78
79
  "ew": "מסתיים ב",
79
80
  "pr": "קיים",
81
+ "isNull": "לא קיים",
80
82
  "in": "ברשימה",
81
83
  "nin": "לא ברשימה",
82
84
  "between": "בין"
@@ -74,8 +74,10 @@
74
74
  "le": "Minore o uguale",
75
75
  "co": "Contiene",
76
76
  "sw": "Inizia con",
77
+ "swAny": "Inizia con uno qualsiasi",
77
78
  "ew": "Finisce con",
78
- "pr": "Presente",
79
+ "pr": "È presente",
80
+ "isNull": "Non è presente",
79
81
  "in": "Nella lista",
80
82
  "nin": "Non nella lista",
81
83
  "between": "Tra"
@@ -74,8 +74,10 @@
74
74
  "le": "Меньше или равно",
75
75
  "co": "Содержит",
76
76
  "sw": "Начинается с",
77
+ "swAny": "Начинается с любого",
77
78
  "ew": "Заканчивается на",
78
79
  "pr": "Присутствует",
80
+ "isNull": "Отсутствует",
79
81
  "in": "В списке",
80
82
  "nin": "Не в списке",
81
83
  "between": "Между"
@@ -41,8 +41,10 @@ export type ComparisonOperator
41
41
  | 'le' // less than or equal
42
42
  | 'co' // contains
43
43
  | 'sw' // starts with
44
+ | 'sw_any' // starts with any value in a list
44
45
  | 'ew' // ends with
45
46
  | 'pr' // present (has value)
47
+ | 'is_null' // missing or explicitly null; serialized as `eq null`
46
48
  | 'in' // in list — native: field in ["a","b"] (scalar membership OR array overlap)
47
49
  | 'nin' // not in list — native: field nin ["a","b"]
48
50
  | 'between' // inclusive range — field between [lo, hi] (numbers or dates)
@@ -118,7 +120,9 @@ export function buildQuery<T extends Record<string, any>>(
118
120
  // Build the condition
119
121
  if (op === 'pr') {
120
122
  parts.push(`${field} pr`)
121
- } else if (op === 'in' || op === 'nin') {
123
+ } else if (op === 'is_null') {
124
+ parts.push(`${field} eq null`)
125
+ } else if (op === 'in' || op === 'nin' || op === 'sw_any') {
122
126
  // Native list membership / array-overlap: field in ["a","b"].
123
127
  const values = Array.isArray(value) ? value : (value != null ? [value] : [])
124
128
  if (values.length === 0) continue
@@ -206,6 +210,14 @@ export class QueryFilter<T extends Record<string, any>> {
206
210
  return this
207
211
  }
208
212
 
213
+ /**
214
+ * Starts with any listed prefix.
215
+ */
216
+ swAny<K extends DeepKeyOf<T>>(field: K, values: string[]): this {
217
+ this.parts.push(`${field as string} sw_any ${formatList(values)}`)
218
+ return this
219
+ }
220
+
209
221
  /**
210
222
  * Ends with comparison (for strings)
211
223
  */
@@ -451,8 +463,11 @@ export function evaluateQuery(query: string, data: Record<string, any>): boolean
451
463
  case 'le': match = value != null && fieldValue <= value; break
452
464
  case 'co': match = value != null && String(fieldValue).includes(String(value)); break
453
465
  case 'sw': match = value != null && String(fieldValue).startsWith(String(value)); break
466
+ case 'sw_any': match = fieldValue != null && Array.isArray(value)
467
+ && value.some(prefix => String(fieldValue).startsWith(String(prefix))); break
454
468
  case 'ew': match = value != null && String(fieldValue).endsWith(String(value)); break
455
469
  case 'pr': match = fieldValue != null; break
470
+ case 'is_null': match = fieldValue == null; break
456
471
  case 'in': match = matchesIn(fieldValue, value); break
457
472
  case 'nin': match = !matchesIn(fieldValue, value); break
458
473
  case 'between': match = matchesBetween(fieldValue, value); break
@@ -498,10 +513,10 @@ function matchesBetween(fieldValue: any, value: Primitive | Primitive[] | undefi
498
513
  return String(fieldValue) >= String(lo) && String(fieldValue) <= String(hi)
499
514
  }
500
515
 
501
- const OPERATORS: ComparisonOperator[] = ['eq', 'ne', 'gt', 'ge', 'lt', 'le', 'co', 'sw', 'ew', 'pr', 'in', 'nin', 'between']
516
+ const OPERATORS: ComparisonOperator[] = ['eq', 'ne', 'gt', 'ge', 'lt', 'le', 'co', 'sw', 'sw_any', 'ew', 'pr', 'in', 'nin', 'between']
502
517
  const CONNECTORS: LogicalOperator[] = ['and', 'or']
503
518
  /** Operators whose value is a bracket list literal. */
504
- const LIST_OPERATORS: ComparisonOperator[] = ['in', 'nin', 'between']
519
+ const LIST_OPERATORS: ComparisonOperator[] = ['in', 'nin', 'between', 'sw_any']
505
520
 
506
521
  /**
507
522
  * Parse a value string into a Primitive
@@ -651,7 +666,13 @@ function parseTokens<T extends Record<string, any>>(
651
666
  value = LIST_OPERATORS.includes(op) ? parseListLiteral(raw) : parseValue(raw)
652
667
  }
653
668
 
654
- const condition: FilterCondition<T> = { field, op, value }
669
+ const normalizedOp: ComparisonOperator
670
+ = op === 'eq' && value === null
671
+ ? 'is_null'
672
+ : op === 'ne' && value === null
673
+ ? 'pr'
674
+ : op
675
+ const condition: FilterCondition<T> = { field, op: normalizedOp, value }
655
676
  if (connector) condition.connector = connector
656
677
  conditions.push(condition)
657
678
  }