nitro-web 0.0.158 → 0.0.160

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/client/index.ts CHANGED
@@ -26,7 +26,7 @@ export { Avatar } from '../components/partials/element/avatar'
26
26
  export { Button } from '../components/partials/element/button'
27
27
  export { Calendar, type CalendarProps } from '../components/partials/element/calendar'
28
28
  export { Dropdown, type DropdownProps, type DropdownOption } from '../components/partials/element/dropdown'
29
- export { Filters, type FiltersHandleType, type FilterType, usePushChangesToPath } from '../components/partials/element/filters'
29
+ export { Filters, type FilterType, usePushChangesToPath } from '../components/partials/element/filters'
30
30
  export { GithubLink } from '../components/partials/element/github-link'
31
31
  export { Initials } from '../components/partials/element/initials'
32
32
  export { Message } from '../components/partials/element/message'
@@ -1,7 +1,7 @@
1
1
  import { useRef, useMemo, useEffect } from 'react'
2
2
  import { isValid, format } from 'date-fns'
3
3
  import { TZDate } from '@date-fns/tz'
4
- import { twMerge } from 'nitro-web'
4
+ import { twMerge, date } from 'nitro-web'
5
5
  import { dayButtonClassName } from '../element/calendar'
6
6
 
7
7
  type Timestamp = null | number
@@ -10,12 +10,14 @@ export type TimePickerProps = {
10
10
  onChange?: (value: Timestamp) => void
11
11
  tz?: string
12
12
  value?: Timestamp
13
- /** "Reactive" reference timestamp to use for the time picker's date. Precedence order: `referenceTimestamp`, `value`, or today */
13
+ // eslint-disable-next-line max-len
14
+ /** "Reactive" reference timestamp to copy the date from. Precedence order: `referenceTimestamp`, `value`, or today. Warning: This value needs to be updated after, or at the same time as `value`, otherwise the useffect will use the previous `value` */
14
15
  referenceTimestamp?: Timestamp
16
+ /** Data for testing */
17
+ // _data?: { name: unknown }
15
18
  }
16
19
 
17
20
  export function TimePicker({ value, onChange, className, tz, referenceTimestamp }: TimePickerProps) {
18
- const [internallyChanged, setInternallyChanged] = useState<Timestamp>()
19
21
  const refs = {
20
22
  hour: useRef<HTMLDivElement>(null),
21
23
  minute: useRef<HTMLDivElement>(null),
@@ -34,31 +36,33 @@ export function TimePicker({ value, onChange, className, tz, referenceTimestamp
34
36
  // Convert the value to an valid* date
35
37
  const internalValue = useMemo(() => {
36
38
  if (!value || !isValid(value)) return undefined
37
- const date = new TZDate(value, tz)
38
-
39
- // Carry over the date from the referenceTimestamp, if provided
40
- if (referenceTimestamp && isValid(referenceTimestamp)) {
41
- const referenceDate = new TZDate(referenceTimestamp, tz)
42
- const originalTime = date.getTime()
43
- date.setDate(referenceDate.getDate())
44
- date.setMonth(referenceDate.getMonth())
45
- date.setFullYear(referenceDate.getFullYear())
46
- // If the time has changed, update the value
47
- if (originalTime !== date.getTime()) {
48
- setInternallyChanged(date.getTime())
49
- }
50
- }
51
-
52
- return date
39
+ const tzdate = new TZDate(value, tz)
40
+ return tzdate
53
41
  }, [value, tz, referenceTimestamp])
54
42
 
55
- // Update the value when the changedValue changes
43
+ // Carry over the date from the referenceTimestamp, if provided
56
44
  useEffect(() => {
57
- if (internallyChanged && isValid(internallyChanged)) {
58
- onChange?.(internallyChanged)
59
- setInternallyChanged(undefined)
60
- }
61
- }, [internallyChanged])
45
+ if (!referenceTimestamp || !isValid(referenceTimestamp) || !internalValue) return
46
+ const referenceDate = new TZDate(referenceTimestamp, tz)
47
+ const newInternalValue = new TZDate(internalValue.getTime(), tz)
48
+ const [day, month, year] = [newInternalValue.getDate(), newInternalValue.getMonth(), newInternalValue.getFullYear()]
49
+ const [refDay, refMonth, refYear] = [referenceDate.getDate(), referenceDate.getMonth(), referenceDate.getFullYear()]
50
+ // const old = date(newInternalValue.getTime(), 'dd hh:mm aa', 'Pacific/Auckland')
51
+
52
+ if (day === refDay && month === refMonth && year === refYear) return
53
+ newInternalValue.setDate(refDay)
54
+ newInternalValue.setMonth(refMonth)
55
+ newInternalValue.setFullYear(refYear)
56
+
57
+ // if (_data && _data?.name === 'endTime') {
58
+ // console.log({
59
+ // old: old,
60
+ // new: date(newInternalValue.getTime(), 'dd hh:mm aa', 'Pacific/Auckland'),
61
+ // })
62
+ // }
63
+
64
+ onChange?.(newInternalValue?.getTime())
65
+ }, [referenceTimestamp])
62
66
 
63
67
  // Get current values from date or use defaults
64
68
  const hour = useMemo(() => internalValue ? parseInt(format(internalValue, 'h')) : undefined, [internalValue])
@@ -108,8 +108,7 @@ export function FieldDate({
108
108
  switch (props.mode) {
109
109
  case 'single':
110
110
  case 'time': {
111
- const value = props?.value ?? props?.defaultValue
112
- return [value && isValid(value) ? new Date(value).getTime() : null]
111
+ return [getStringValue(props.value, props.defaultValue)]
113
112
  }
114
113
  case 'multiple':
115
114
  case 'range': {
@@ -127,6 +126,11 @@ export function FieldDate({
127
126
  }
128
127
  }
129
128
 
129
+ function getStringValue(value?: Timestamp, defaultValue?: Timestamp) {
130
+ const value2 = value ?? defaultValue
131
+ return value2 && isValid(value2) ? new Date(value2).getTime() : null
132
+ }
133
+
130
134
  function getInputValue(value: Timestamp[]) {
131
135
  return value.map(o => date(o, pattern, tz)).join(props.mode == 'range' ? ' - ' : ', ')
132
136
  }
@@ -173,7 +177,7 @@ export function FieldDate({
173
177
  function onNowClick() {
174
178
  // Use the browser's local time and parse it into the timezone
175
179
  const [hours, minutes, seconds] = [new Date().getHours(), new Date().getMinutes(), new Date().getSeconds()]
176
- const nowInTz = parseDateString(`${hours}:${minutes}:${seconds}`, 'hh:mm:ss', tz, referenceTimestamp)
180
+ const nowInTz = parseDateString(`${hours}:${minutes}:${seconds}`, 'HH:mm:ss', tz)
177
181
  onChange(nowInTz)
178
182
  }
179
183
 
@@ -203,9 +207,15 @@ export function FieldDate({
203
207
  }
204
208
  {
205
209
  (props.mode == 'time' || (!!showTime && props.mode == 'single')) &&
206
- <TimePicker value={internalValue?.[0]} onChange={onChange<Timestamp>}
210
+ <TimePicker
211
+ // The `value` needs to be updated before or at the same time as `referenceTimestamp`, otherwise the
212
+ // useffect in the timepicker will use the previous `value`
213
+ value={getStringValue(props.value, props.defaultValue)}
214
+ onChange={onChange<Timestamp>}
207
215
  className={`border-l border-gray-100 ${props.mode == 'single' ? 'min-h-[0]' : ''}`}
208
- referenceTimestamp={referenceTimestamp} tz={tz}
216
+ referenceTimestamp={referenceTimestamp}
217
+ tz={tz}
218
+ // _data={{ name: props.name }}
209
219
  />
210
220
  }
211
221
  </div>
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  Drop, Dropdown, Field, Select, Button as ButtonNitro, Checkbox, GithubLink, Modal, Calendar, injectedConfig, TimePicker,
3
- Filters, FiltersHandleType, FilterType, Table, TableColumn, usePushChangesToPath,
3
+ Filters, FilterType, Table, TableColumn, usePushChangesToPath,
4
4
  } from 'nitro-web'
5
- import { date, getCountryOptions, getCurrencyOptions, onChange, ucFirst } from 'nitro-web/util'
5
+ import { date, getCurrencyOptions, onChange, ucFirst } from 'nitro-web/util'
6
6
  import { Check, EllipsisVerticalIcon, FileEditIcon } from 'lucide-react'
7
+ import React from 'react'
7
8
 
8
9
  const perPage = 10
9
10
  const statusColors = function(status: string) {
@@ -237,7 +238,12 @@ export function Styleguide({ className, elements, children, currencies }: Styleg
237
238
  <h1 class="h1">{injectedConfig.isDemo ? 'Design System' : 'Style Guide'}</h1>
238
239
  <p class="mb-3">
239
240
  Components are styled using&nbsp;
240
- <a href="https://v3.tailwindcss.com/docs/configuration" class="underline" target="_blank" rel="noreferrer">TailwindCSS</a>. {injectedConfig.isDemo && <><a href="#" class="underline" onClick={indirectlyChangeTheState}>Click here</a> to indirectly change the state</>}
241
+ <a href="https://v3.tailwindcss.com/docs/configuration" class="underline" target="_blank" rel="noreferrer">TailwindCSS</a>.
242
+ {injectedConfig.isDemo &&
243
+ <React.Fragment>
244
+ <a href="#" class="underline" onClick={indirectlyChangeTheState}>Click here</a> to indirectly change the state
245
+ </React.Fragment>
246
+ }
241
247
  </p>
242
248
  </div>
243
249
 
@@ -266,7 +272,13 @@ export function Styleguide({ className, elements, children, currencies }: Styleg
266
272
  // menuIsOpen={true}
267
273
  dir="bottom-right"
268
274
  minWidth="330px"
269
- options={[{ label: <><b>New Customer</b> / Add <b>Bruce Lee</b></>, className: 'border-bottom-with-space' }, ...options]}
275
+ options={[
276
+ {
277
+ label: <React.Fragment><b>New Customer</b> / Add <b>Bruce Lee</b></React.Fragment>,
278
+ className: 'border-bottom-with-space',
279
+ },
280
+ ...options,
281
+ ]}
270
282
  >
271
283
  <Button color="white" IconRight="v" class="gap-x-3">Dropdown bottom-right</Button>
272
284
  </Dropdown>
@@ -429,10 +441,10 @@ export function Styleguide({ className, elements, children, currencies }: Styleg
429
441
  fixed: true,
430
442
  value: '0',
431
443
  label: (
432
- <>
444
+ <React.Fragment>
433
445
  <b>New Customer</b> (and clear select)
434
- {customerSearch ? <> / Add <b>{ucFirst(customerSearch)}</b></> : ''}
435
- </>
446
+ {customerSearch ? <React.Fragment> / Add <b>{ucFirst(customerSearch)}</b></React.Fragment> : ''}
447
+ </React.Fragment>
436
448
  ),
437
449
  },
438
450
  { value: '1', label: 'Iron Man Industries' },
@@ -446,7 +458,10 @@ export function Styleguide({ className, elements, children, currencies }: Styleg
446
458
  <Select
447
459
  name="currency"
448
460
  state={state}
449
- options={useMemo(() => (currencies ? getCurrencyOptions(currencies) : [{ value: 'nzd', label: 'New Zealand Dollar' }, { value: 'aud', label: 'Australian Dollar' }]), [])}
461
+ options={useMemo(() => (currencies ? getCurrencyOptions(currencies) : [
462
+ { value: 'nzd', label: 'New Zealand Dollar' },
463
+ { value: 'aud', label: 'Australian Dollar' },
464
+ ]), [])}
450
465
  onChange={(e) => onChange(e, setState)}
451
466
  />
452
467
  </div>
@@ -522,7 +537,7 @@ export function Styleguide({ className, elements, children, currencies }: Styleg
522
537
  // DropdownProps={{ menuIsOpen: true }}
523
538
  />
524
539
  </div>
525
- <div>
540
+ <div>
526
541
  <label for="dateRange">Date range (with prefix & disabled days)</label>
527
542
  <Field
528
543
  name="dateRange"
@@ -532,13 +547,14 @@ export function Styleguide({ className, elements, children, currencies }: Styleg
532
547
  state={state}
533
548
  onChange={(e) => onChange(e, setState)}
534
549
  DayPickerProps={{
535
- disabled: { after: new Date(Date.now() + 1000 * 60 * 60 * 24 * 45) }
550
+ disabled: { after: new Date(Date.now() + 1000 * 60 * 60 * 24 * 45) },
536
551
  }}
537
552
  />
538
553
  </div>
539
554
  <div>
540
555
  <label for="dateMultiple">Date multi-select (right aligned)</label>
541
- <Field name="dateMultiple" type="date" mode="multiple" state={state} onChange={(e) => onChange(e, setState)} dir="bottom-right" />
556
+ <Field name="dateMultiple" type="date" mode="multiple" state={state}
557
+ onChange={(e) => onChange(e, setState)} dir="bottom-right" />
542
558
  </div>
543
559
  <div>
544
560
  <label for="time">Time: {date(state.time, 'dd MMM hh:mmaa')}</label>
@@ -583,14 +599,14 @@ export function Styleguide({ className, elements, children, currencies }: Styleg
583
599
  <div>
584
600
  <label for="time">TimePicker</label>
585
601
  {/* <div className="mt-2.5 mb-6 mt-input-before mb-input-after pt-2"> */}
586
- <TimePicker
587
- value={state.time}
588
- className="min-h-[150] mt-2.5 mb-6 mt-input-before mb-input-after pt-2"
589
- onChange={(value) => onChange({ target: { name: 'time', value: value }}, setState)}
590
- // Testing timezone support:
591
- // tz="Pacific/Honolulu"
592
- // referenceTimestamp={new Date().getTime() + 1000 * 60 * 60 * 24 * 5}
593
- />
602
+ <TimePicker
603
+ value={state.time}
604
+ className="min-h-[150] mt-2.5 mb-6 mt-input-before mb-input-after pt-2"
605
+ onChange={(value) => onChange({ target: { name: 'time', value: value }}, setState)}
606
+ // Testing timezone support:
607
+ // tz="Pacific/Honolulu"
608
+ // referenceTimestamp={new Date().getTime() + 1000 * 60 * 60 * 24 * 5}
609
+ />
594
610
  {/* </div> */}
595
611
  </div>
596
612
  </div>
@@ -650,7 +666,7 @@ export function Styleguide({ className, elements, children, currencies }: Styleg
650
666
  )}
651
667
 
652
668
  {groups.includes('Modals') && (
653
- <>
669
+ <React.Fragment>
654
670
  <div>
655
671
  <h2 class="h3">Modals</h2>
656
672
  <div class="mb-6"><Button color="primary" onClick={() => setShowModal1(true)}>Modal (default)</Button></div>
@@ -673,13 +689,13 @@ export function Styleguide({ className, elements, children, currencies }: Styleg
673
689
  <Button color="primary" onClick={() => setShowModal1(false)}>Save</Button>
674
690
  </div>
675
691
  </Modal>
676
- </>
692
+ </React.Fragment>
677
693
  )}
678
694
 
679
695
  {groups.includes('Custom Components') && (
680
- <>
696
+ <React.Fragment>
681
697
  {children}
682
- </>
698
+ </React.Fragment>
683
699
  )}
684
700
 
685
701
  <GithubLink filename={__filename} />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nitro-web",
3
- "version": "0.0.158",
3
+ "version": "0.0.160",
4
4
  "repository": "github:boycce/nitro-web",
5
5
  "homepage": "https://boycce.github.io/nitro-web/",
6
6
  "description": "Nitro is a battle-tested, modular base project to turbocharge your projects, styled using Tailwind 🚀",
package/types/util.d.ts CHANGED
@@ -190,6 +190,22 @@ export function deepSetWithInfo<T>(_obj: T, path: string, value: unknown | Funct
190
190
  parent: T;
191
191
  fieldName: string;
192
192
  };
193
+ /**
194
+ * Recursively traverses arrays and plain objects and replaces
195
+ * every value strictly equal to "{VALUE}".
196
+ *
197
+ * - Does not mutate the original input
198
+ * - Only traverses plain objects and arrays
199
+ * - Throws if a circular reference is detected
200
+ *
201
+ * @template T
202
+ * @param {T} input
203
+ * @param {any} matchingValue
204
+ * @param {any} value
205
+ * @returns {T}
206
+ * @throws {Error}
207
+ */
208
+ export function deepSetWithMatch<T>(input: T, matchingValue: any, value: any): T;
193
209
  /**
194
210
  * Iterates over an object or array
195
211
  * @param {{[key: string]: any}|[]|null} obj
@@ -528,63 +544,86 @@ export function onChange<T>(eventOrPathValue: EventOrPathValue, setState: React.
528
544
  export function pad(num?: number, padLeft?: number, fixedRight?: number): string;
529
545
  /**
530
546
  * Validates req.query "filters" against a config object, and returns a MongoDB-compatible query object.
547
+ * - `{ rule: 'search' }` is only supported with supported $search operations (e.g. aggregate).
548
+ * - `{ rule: 'text', numberFields: string[] }` number fields require indexes to work.
531
549
  * @param {{ [key: string]: unknown }} query - req.query
532
- * E.g. {
533
- * location: '10-RS',
534
- * age: '33',
535
- * isDeleted: 'false',
536
- * search: 'John Doe',
537
- * createdAt: '1749038400000,1749729600000',
538
- * status: 'incomplete',
539
- * bookingDate: '14'
540
- * isActive: 'true',
541
- * customer.0: '69214ce7ab121fb3726965a1', // splayed array items
542
- * }
543
- * @param {{
544
- * [key: string]: 'string'|'number'|'boolean'|'search'|'dateRange'|EnumArray|{ rule: 'ids', parseId: parseId }
545
- * }} config - allowed filters and their rules
546
- * E.g. {
547
- * location: 'string',
548
- * age: 'number',
549
- * isDeleted: 'boolean',
550
- * search: 'search',
551
- * createdAt: 'dateRange',
552
- * status: ['incomplete', 'complete'], // EnumArray
553
- * bookingDate: [11, 14, 33], // EnumArray
554
- * isActive: [true, false], // EnumArray
555
- * customer: { rule: 'ids', ObjectId: ObjectIdConstructor },
556
- * }
557
- * @example returned object (using the examples above):
558
- * E.g. {
559
- * location: '10-RS',
560
- * age: 33,
561
- * isDeleted: false,
562
- * search: { $search: 'John' },
563
- * createdAt: { $gte: 1749038400000, $lte: 1749729600000 },
564
- * status: 'incomplete',
565
- * bookingDate: 14,
566
- * isActive: true,
567
- * customer: { $in: [new ObjectId('1234567890')] },
568
- * }
550
+ * @param {{ [key: string]: (
551
+ * 'string'
552
+ * | 'number'
553
+ * | 'boolean'
554
+ * | 'dateRange'
555
+ * | EnumArray
556
+ * | { rule: 'ids', parseId: parseId }
557
+ * | 'text'
558
+ * | { rule: 'text', numberFields: string[] }
559
+ * | { rule: 'search' } & SearchOperators
560
+ * )}} config - object of allowed filter/rule pairs.
561
+ *
562
+ * Examples:
563
+ *
564
+ * @example query = {
565
+ * location: '10-RS',
566
+ * age: '33',
567
+ * isDeleted: 'false',
568
+ * createdAt: '1749038400000,1749729600000',
569
+ * status: 'incomplete',
570
+ * customer.0: '69214ce7ab121fb3726965a1', // splayed array items
571
+ * text: 'John Doe',
572
+ * text: '15',
573
+ * search: 'John Doe',
574
+ * }
575
+ * @example config = {
576
+ * location: 'string',
577
+ * age: 'number',
578
+ * isDeleted: 'boolean',
579
+ * createdAt: 'dateRange',
580
+ * status: ['incomplete', 'complete'], // EnumArray [string|boolean|number]
581
+ * customer: { rule: 'ids', ObjectId: ObjectIdConstructor },
582
+ * text: 'text',
583
+ * text: { rule: 'text', numberFields: ['age'] }, // search with numeric fields
584
+ * search: { rule: 'search', text: { query: "{VALUE}", path: ['firstName'] } },
585
+ * }
586
+ * @example returned MongoDB query object = {
587
+ * location: '10-RS',
588
+ * age: 33,
589
+ * isDeleted: false,
590
+ * createdAt: { $gte: 1749038400000, $lte: 1749729600000 },
591
+ * status: 'incomplete',
592
+ * customer: { $in: [new ObjectId('1234567890')] },
593
+ * $text: { $search: 'John Doe' },
594
+ * $or: [{ $text: { $search: '15' }}, { age: 15 }],
595
+ * $search: { text: { query: "John Doe", path: ['firstName'] }},
596
+ * }
569
597
  */
570
598
  export function parseFilters(query: {
571
599
  [key: string]: unknown;
572
600
  }, config: {
573
- [key: string]: "string" | "number" | "boolean" | "search" | "dateRange" | EnumArray | {
601
+ [key: string]: ("string" | "number" | "boolean" | "dateRange" | EnumArray | {
574
602
  rule: "ids";
575
603
  parseId: parseId;
576
- };
604
+ } | "text" | {
605
+ rule: "text";
606
+ numberFields: string[];
607
+ } | ({
608
+ rule: "search";
609
+ } & SearchOperators));
577
610
  }): {
578
- [key: string]: string | number | boolean | {
579
- $search: string;
580
- } | {
611
+ [key: string]: string | number | boolean | SearchOperators | {
581
612
  $gte?: number;
582
613
  $gt?: number;
583
614
  $lte?: number;
584
615
  $lt?: number;
585
616
  } | {
586
617
  $in: ObjectId[];
587
- };
618
+ } | {
619
+ $search: string;
620
+ } | ({
621
+ $text: {
622
+ $search: string;
623
+ };
624
+ } | {
625
+ [key: string]: number;
626
+ })[];
588
627
  };
589
628
  /**
590
629
  * Parses req.query "pagination" and "sorting" fields and returns a monastery-compatible options object.
@@ -879,6 +918,12 @@ export type parseId = (value: string) => ObjectId;
879
918
  * - an array of strings, numbers or booleans
880
919
  */
881
920
  export type EnumArray = (string | number | boolean)[];
921
+ /**
922
+ * - https://mongodb.com/docs/atlas/atlas-search/operators-and-collectors/
923
+ */
924
+ export type SearchOperators = {
925
+ [key: string]: any;
926
+ };
882
927
  export type NitroError = {
883
928
  title: string;
884
929
  detail: string;
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../util.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../util.js"],"names":[],"mappings":"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"}
package/util.js CHANGED
@@ -29,6 +29,7 @@ export { TZDate } from '@date-fns/tz'
29
29
  /** @typedef {object} ObjectId */
30
30
  /** @typedef {(value: string) => ObjectId} parseId */
31
31
  /** @typedef {(string|number|boolean)[]} EnumArray - an array of strings, numbers or booleans */
32
+ /** @typedef {{ [key: string]: any }} SearchOperators - https://mongodb.com/docs/atlas/atlas-search/operators-and-collectors/ */
32
33
  /** @typedef {{ title: string, detail: string }} NitroError */
33
34
  /** @typedef {{ toJSON: () => { message: string } }} MongoError */
34
35
  /** @typedef {{ response: { data: { errors?: NitroError[], error?: string, error_description?: string } } }} AxiosWithErrors */
@@ -540,6 +541,56 @@ export function deepSetWithInfo(_obj, path, value) {
540
541
  }
541
542
  }
542
543
 
544
+ /**
545
+ * Recursively traverses arrays and plain objects and replaces
546
+ * every value strictly equal to "{VALUE}".
547
+ *
548
+ * - Does not mutate the original input
549
+ * - Only traverses plain objects and arrays
550
+ * - Throws if a circular reference is detected
551
+ *
552
+ * @template T
553
+ * @param {T} input
554
+ * @param {any} matchingValue
555
+ * @param {any} value
556
+ * @returns {T}
557
+ * @throws {Error}
558
+ */
559
+ export function deepSetWithMatch(input, matchingValue, value) {
560
+ const seen = new WeakMap()
561
+
562
+ /**
563
+ * @param {*} input2
564
+ * @returns {*}
565
+ */
566
+ function walk(input2) {
567
+ if (input2 === matchingValue) return value
568
+
569
+ if (Array.isArray(input2)) {
570
+ return input2.map(walk)
571
+ }
572
+
573
+ if (input2 && typeof input2 === 'object') {
574
+ const proto = Object.getPrototypeOf(input2)
575
+ if (proto !== Object.prototype && proto !== null) return input2
576
+ if (seen.has(input2)) throw new Error('Circular reference detected')
577
+
578
+ /** @type {{ [key: string]: any }} */
579
+ const result = {}
580
+ seen.set(input2, result)
581
+
582
+ for (const key of Object.keys(input2)) {
583
+ result[key] = (walk(input2[key]))
584
+ }
585
+ return result
586
+ }
587
+
588
+ return input2
589
+ }
590
+
591
+ return /** @type {T} */ (walk(input))
592
+ }
593
+
543
594
  /**
544
595
  * Iterates over an object or array
545
596
  * @param {{[key: string]: any}|[]|null} obj
@@ -1282,54 +1333,77 @@ export function pad (num=0, padLeft=0, fixedRight) {
1282
1333
 
1283
1334
  /**
1284
1335
  * Validates req.query "filters" against a config object, and returns a MongoDB-compatible query object.
1336
+ * - `{ rule: 'search' }` is only supported with supported $search operations (e.g. aggregate).
1337
+ * - `{ rule: 'text', numberFields: string[] }` number fields require indexes to work.
1285
1338
  * @param {{ [key: string]: unknown }} query - req.query
1286
- * E.g. {
1287
- * location: '10-RS',
1288
- * age: '33',
1289
- * isDeleted: 'false',
1290
- * search: 'John Doe',
1291
- * createdAt: '1749038400000,1749729600000',
1292
- * status: 'incomplete',
1293
- * bookingDate: '14'
1294
- * isActive: 'true',
1295
- * customer.0: '69214ce7ab121fb3726965a1', // splayed array items
1296
- * }
1297
- * @param {{
1298
- * [key: string]: 'string'|'number'|'boolean'|'search'|'dateRange'|EnumArray|{ rule: 'ids', parseId: parseId }
1299
- * }} config - allowed filters and their rules
1300
- * E.g. {
1301
- * location: 'string',
1302
- * age: 'number',
1303
- * isDeleted: 'boolean',
1304
- * search: 'search',
1305
- * createdAt: 'dateRange',
1306
- * status: ['incomplete', 'complete'], // EnumArray
1307
- * bookingDate: [11, 14, 33], // EnumArray
1308
- * isActive: [true, false], // EnumArray
1309
- * customer: { rule: 'ids', ObjectId: ObjectIdConstructor },
1310
- * }
1311
- * @example returned object (using the examples above):
1312
- * E.g. {
1313
- * location: '10-RS',
1314
- * age: 33,
1315
- * isDeleted: false,
1316
- * search: { $search: 'John' },
1317
- * createdAt: { $gte: 1749038400000, $lte: 1749729600000 },
1318
- * status: 'incomplete',
1319
- * bookingDate: 14,
1320
- * isActive: true,
1321
- * customer: { $in: [new ObjectId('1234567890')] },
1322
- * }
1339
+ * @param {{ [key: string]: (
1340
+ * 'string'
1341
+ * | 'number'
1342
+ * | 'boolean'
1343
+ * | 'dateRange'
1344
+ * | EnumArray
1345
+ * | { rule: 'ids', parseId: parseId }
1346
+ * | 'text'
1347
+ * | { rule: 'text', numberFields: string[] }
1348
+ * | { rule: 'search' } & SearchOperators
1349
+ * )}} config - object of allowed filter/rule pairs.
1350
+ *
1351
+ * Examples:
1352
+ *
1353
+ * @example query = {
1354
+ * location: '10-RS',
1355
+ * age: '33',
1356
+ * isDeleted: 'false',
1357
+ * createdAt: '1749038400000,1749729600000',
1358
+ * status: 'incomplete',
1359
+ * customer.0: '69214ce7ab121fb3726965a1', // splayed array items
1360
+ * text: 'John Doe',
1361
+ * text: '15',
1362
+ * search: 'John Doe',
1363
+ * }
1364
+ * @example config = {
1365
+ * location: 'string',
1366
+ * age: 'number',
1367
+ * isDeleted: 'boolean',
1368
+ * createdAt: 'dateRange',
1369
+ * status: ['incomplete', 'complete'], // EnumArray [string|boolean|number]
1370
+ * customer: { rule: 'ids', ObjectId: ObjectIdConstructor },
1371
+ * text: 'text',
1372
+ * text: { rule: 'text', numberFields: ['age'] }, // search with numeric fields
1373
+ * search: { rule: 'search', text: { query: "{VALUE}", path: ['firstName'] } },
1374
+ * }
1375
+ * @example returned MongoDB query object = {
1376
+ * location: '10-RS',
1377
+ * age: 33,
1378
+ * isDeleted: false,
1379
+ * createdAt: { $gte: 1749038400000, $lte: 1749729600000 },
1380
+ * status: 'incomplete',
1381
+ * customer: { $in: [new ObjectId('1234567890')] },
1382
+ * $text: { $search: 'John Doe' },
1383
+ * $or: [{ $text: { $search: '15' }}, { age: 15 }],
1384
+ * $search: { text: { query: "John Doe", path: ['firstName'] }},
1385
+ * }
1323
1386
  */
1324
1387
  export function parseFilters(query, config) {
1325
- /**
1388
+ /**
1326
1389
  * Should match the example returned object above
1327
1390
  * @type {{
1328
- * [key: string]: string|number|boolean|{ $search: string }|{ $gte?: number; $gt?: number; $lte?: number; $lt?: number; }|
1329
- * { $in: ObjectId[] } }} */
1330
- const mongoQuery = {}
1331
-
1332
- // Convert splayed array items into a unified array objects, e.g. 'customer.0' = '1' and 'customer.1' = '2' -> 'customer' = '1,2'
1391
+ * [key: string]: (
1392
+ * string
1393
+ * | number
1394
+ * | boolean
1395
+ * | { $gte?: number; $gt?: number; $lte?: number; $lt?: number; }
1396
+ * | (string|number|boolean)
1397
+ * | { $in: ObjectId[] }
1398
+ * | { $search: string } // key=$text
1399
+ * | ({ $text: { $search: string } } | { [key: string]: number })[] // key=$or
1400
+ * | SearchOperators // key=$search
1401
+ * )
1402
+ * }} */
1403
+ const returnedMongoQuery = {}
1404
+
1405
+ // Convert splayed array items into a unified array objects,
1406
+ // E.g. 'customer.0' = '1' and 'customer.1' = '2' -> 'customer' = '1,2'
1333
1407
  for (const key in query) {
1334
1408
  if (key.match(/\.\d+$/)) {
1335
1409
  const baseKey = key.replace(/\.\d+$/, '')
@@ -1341,7 +1415,7 @@ export function parseFilters(query, config) {
1341
1415
 
1342
1416
  for (const key in query) {
1343
1417
  if (typeof query[key] !== 'string') continue
1344
- const val = query[key]
1418
+ const val = query[key].trim()
1345
1419
  const rule = config[key]
1346
1420
 
1347
1421
  if (!rule) {
@@ -1349,24 +1423,26 @@ export function parseFilters(query, config) {
1349
1423
 
1350
1424
  } else if (rule === 'string') {
1351
1425
  if (typeof val !== 'string') throw new Error(`The "${key}" filter has an invalid value "${val}". Expected a string.`)
1352
- mongoQuery[key] = val
1426
+ returnedMongoQuery[key] = val
1353
1427
 
1354
1428
  } else if (rule === 'number') {
1355
1429
  const num = parseFloat(val)
1356
1430
  if (isNaN(num)) throw new Error(`The "${key}" filter should be a number, but received "${val}".`)
1357
- mongoQuery[key] = num
1431
+ returnedMongoQuery[key] = num
1358
1432
 
1359
1433
  } else if (rule === 'boolean') {
1360
1434
  const bool = val === 'true' ? true : val === 'false' ? false : undefined
1361
1435
  if (bool === undefined) throw new Error(`The "${key}" filter should be a boolean, but received "${val}".`)
1362
- mongoQuery[key] = bool
1436
+ returnedMongoQuery[key] = bool
1363
1437
 
1364
- } else if (rule === 'search') {
1365
- if (typeof val !== 'string') throw new Error(`The "${key}" filter has an invalid value "${val}". Expected a string.`)
1366
- mongoQuery['$text'] = { $search: '"' + val + '"' }
1438
+ } else if (rule === 'dateRange') {
1439
+ const [start, end] = val.split(',').map(Number)
1440
+ if (isNaN(start) && isNaN(end)) throw new Error(`The "${key}" filter has an invalid value "${val}". Expected a date range.`)
1441
+ else if (isNaN(start)) returnedMongoQuery[key] = { $gte: 0, $lte: end }
1442
+ else if (isNaN(end)) returnedMongoQuery[key] = { $gte: start }
1443
+ else returnedMongoQuery[key] = { $gte: start, $lte: end }
1367
1444
 
1368
- // Enums
1369
- } else if (Array.isArray(rule)) {
1445
+ } else if (Array.isArray(rule)) { // Enum
1370
1446
  // Detetect the entire array's type from the first item
1371
1447
  const type = typeof rule[0]
1372
1448
  if (!['string', 'number', 'boolean'].includes(type)) {
@@ -1377,11 +1453,10 @@ export function parseFilters(query, config) {
1377
1453
  let valParsed = /** @type {string|number|boolean|undefined} */(val)
1378
1454
  if (type === 'number') valParsed = parseFloat(val)
1379
1455
  else if (type === 'boolean') valParsed = val === 'true' ? true : val === 'false' ? false : undefined
1380
- if (valParsed === ruleItem) mongoQuery[key] = valParsed
1456
+ if (valParsed === ruleItem) returnedMongoQuery[key] = valParsed
1381
1457
  }
1382
1458
 
1383
- // Ids
1384
- } else if (typeof rule === 'object' && 'rule' in rule && rule.rule === 'ids') {
1459
+ } else if (typeof rule === 'object' && 'rule' in rule && rule.rule === 'ids') { // ids
1385
1460
  if (!rule.parseId) {
1386
1461
  throw new Error(`The "${key}" filter has an invalid rule. Expected a parseId function.`)
1387
1462
  }
@@ -1390,21 +1465,39 @@ export function parseFilters(query, config) {
1390
1465
  else return rule.parseId(id)
1391
1466
  })
1392
1467
  if (!ids.length) throw new Error(`Please pass at least one id to the "${key}" filter.`)
1393
- mongoQuery[key] = { $in: ids }
1468
+ returnedMongoQuery[key] = { $in: ids }
1394
1469
 
1395
- } else if (rule === 'dateRange') {
1396
- const [start, end] = val.split(',').map(Number)
1397
- if (isNaN(start) && isNaN(end)) throw new Error(`The "${key}" filter has an invalid value "${val}". Expected a date range.`)
1398
- else if (isNaN(start)) mongoQuery[key] = { $gte: 0, $lte: end }
1399
- else if (isNaN(end)) mongoQuery[key] = { $gte: start }
1400
- else mongoQuery[key] = { $gte: start, $lte: end }
1470
+ } else if (rule === 'text') {
1471
+ if (typeof val !== 'string') throw new Error(`The "${key}" filter has an invalid value "${val}". Expected a string.`)
1472
+ returnedMongoQuery['$text'] = { $search: '"' + val + '"' }
1401
1473
 
1474
+ } else if (typeof rule === 'object' && 'rule' in rule && rule.rule === 'text') {
1475
+ if (typeof val !== 'string') {
1476
+ throw new Error(`The "${key}" filter has an invalid value "${val}". Expected a string.`)
1477
+ } else if (!Array.isArray(rule.numberFields) || !rule.numberFields.length) {
1478
+ throw new Error(`The "${key}" filter has an invalid rule. Expected an numberFields to be a non-empty array.`)
1479
+ }
1480
+ const ors = []
1481
+ const num = parseFloat(val)
1482
+ ors.push({ $text: { $search: val } })
1483
+ if (!isNaN(num)) {
1484
+ for (const field of rule.numberFields) {
1485
+ ors.push({ [field]: num })
1486
+ }
1487
+ }
1488
+ returnedMongoQuery.$or = ors
1489
+
1490
+ } else if (typeof rule === 'object' && 'rule' in rule && rule.rule === 'search') {
1491
+ if (typeof val !== 'string') throw new Error(`The "${key}" filter has an invalid value "${val}". Expected a string.`)
1492
+ const output = /** @type {SearchOperators} */(deepSetWithMatch(rule, '{VALUE}', val)) // replace {VALUE} with the value
1493
+ delete output.rule // need to remove rule from the object
1494
+ returnedMongoQuery['$search'] = output
1402
1495
  } else {
1403
1496
  throw new Error(`Unknown filter type "${rule}" in the config.`)
1404
1497
  }
1405
1498
  }
1406
1499
 
1407
- return mongoQuery
1500
+ return returnedMongoQuery
1408
1501
  }
1409
1502
 
1410
1503
  /**