search-gold 0.9.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.
Files changed (3) hide show
  1. package/README.md +111 -0
  2. package/package.json +39 -0
  3. package/src/search.ts +66 -0
package/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # search-gold
2
+
3
+ ## Overview
4
+
5
+ Your page displays a list columnar information, based on the ubiquitous data stucture:
6
+
7
+ `array of objects`
8
+
9
+ For an improved UX, you want to add a search field so the user can filter the displayed records.
10
+
11
+ The `search-gold` package is easy to set up and use!
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ $ npm i search-gold
17
+ ```
18
+ OR
19
+ ```bash
20
+ $ bun i search-gold
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ The following should provide enough to get things up and running.
26
+
27
+ `my-data-list-component.ts`
28
+ ```javascript
29
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30
+ // 1. Import the two types and methods.
31
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32
+ import {
33
+ ConfigParams,
34
+ ItemRecord,
35
+ searchHandler,
36
+ searchSetConfig,
37
+ } from 'search-gold'
38
+
39
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
40
+ // 2. Create a method to set search configuration.
41
+ //
42
+ // The configuration will be custom for your data.
43
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
44
+ const initSearchConfig = () => {
45
+ const config: ConfigParams = {
46
+ // required: items must reference an array of
47
+ // objects.
48
+ //
49
+ // 'search-gold' package only reads this data.
50
+ // Results are returned in a separate array.
51
+ items: MyData,
52
+
53
+ // required: keys are the object properties you
54
+ // want to expose to search queries. List only
55
+ // the fields which make sense to you.
56
+ keys: ['productName', 'model', 'status'],
57
+
58
+ // optional: default is false
59
+ caseSensitive: true,
60
+ },
61
+
62
+ searchSetConfig(config)
63
+
64
+ // Passing an empty string effectively returns
65
+ // the unfiltered set, the same as MyData.
66
+ //
67
+ // How you set up your component to display
68
+ // the filtered data is up to you and how
69
+ // your component is structured. This is just
70
+ // an example.
71
+ myFilteredData = <ItemRecord[]>searchHandler('')
72
+ }
73
+
74
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
75
+ // 3. Initialize the search engine.
76
+ //
77
+ // This method (from step 2.) should be called when
78
+ // your module is initialized and your data is ready.
79
+ //
80
+ // Also, it should be called whenever your data
81
+ // changes via record create, edit, or delete.
82
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
83
+ initSearchConfig()
84
+
85
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
86
+ // 4. Perform search queries (finally!)
87
+ //
88
+ // Presumably, a method is binded to the UI's
89
+ // search field (in this example it's called
90
+ // 'searchMyData') and is called on every keystroke.
91
+ //
92
+ // You can optionally add a debounce in your component.
93
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
94
+ const searchMyData = (query: string) => {
95
+ myFilteredData = searchHandler(query)
96
+ }
97
+ ```
98
+
99
+ During development, I integrated this `search-gold` package
100
+ in a couple of apps I had built using Vue and its Composition API.
101
+
102
+ I tried to keep the Usage code examples framework
103
+ neutral.
104
+
105
+ # Credits
106
+
107
+ G. Gold
108
+
109
+ October 2025
110
+
111
+ Have fun!
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "search-gold",
3
+ "private": false,
4
+ "version": "0.9.0",
5
+ "description": "speedy search engine targeting an array of objects",
6
+ "author": "gold",
7
+ "main": "./src/search.ts",
8
+ "type": "module",
9
+ "files": [
10
+ "./src/search.ts"
11
+ ],
12
+ "scripts": {
13
+ "lint": "npx oxlint ./src/search.ts && npx eslint ."
14
+ },
15
+ "devDependencies": {
16
+ "@typescript-eslint/eslint-plugin": "^8.34.0",
17
+ "@typescript-eslint/parser": "^8.34.0",
18
+ "eslint": "9.33.0",
19
+ "eslint-plugin-oxlint": "1.12.0",
20
+ "globals": "^16.2.0",
21
+ "oxlint": "1.12.0",
22
+ "prettier": "^3.5.3",
23
+ "tslib": "^2.8.1",
24
+ "typescript-eslint": "8.39.1"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://codeberg.org/gold/search-gold.git"
29
+ },
30
+ "keywords": [
31
+ "data search",
32
+ "search",
33
+ "data filter",
34
+ "simple search",
35
+ "fast search"
36
+ ],
37
+ "license": "MIT",
38
+ "homepage": "https://codeberg.org/gold/search-gold"
39
+ }
package/src/search.ts ADDED
@@ -0,0 +1,66 @@
1
+ export type ItemRecord = Record<string, any>
2
+
3
+ export interface ConfigParams {
4
+ items: ItemRecord[]
5
+ keys: string[]
6
+ caseSensitive?: boolean
7
+ }
8
+
9
+ interface Config {
10
+ Items: ItemRecord[]
11
+ Keys: string[]
12
+ CaseSensitive: boolean
13
+ RegexFlag: 'i' | undefined
14
+ }
15
+
16
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
17
+ //
18
+ // RegExp.escape() is a relatively new addition to JS, thus for now
19
+ // we continue to use the following tried and true functionality.
20
+ const escapeRegExp = (str: string): string => {
21
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
22
+ }
23
+
24
+ const Conf: Config = {
25
+ Items: [],
26
+ Keys: [],
27
+ CaseSensitive: false,
28
+ RegexFlag: 'i',
29
+ }
30
+ export const searchSetConfig = (params: ConfigParams): void => {
31
+ const { items, keys, caseSensitive = false } = params
32
+ Conf.Items = items
33
+ Conf.Keys = keys
34
+ Conf.CaseSensitive = caseSensitive
35
+ Conf.RegexFlag = caseSensitive ? undefined : 'i'
36
+ }
37
+
38
+ // Since (if no debounce) every search input keystroke calls this
39
+ // method, performance has been optimized: there is no run-time type
40
+ // guarding and no try-catch blocks; regex escaping is still necessary
41
+ // though and has been encapsulated in this module.
42
+ //
43
+ // For a better run-time UX -- no errors and speedy execution -- we feel
44
+ // that it behooves the caller to this method be responsible for
45
+ // correctly setting the config for this module and to check that the
46
+ // search's query argument is a string.
47
+ export const searchHandler = (query: string): ItemRecord[] => {
48
+ const results: ItemRecord[] = []
49
+ const qTrimmed = escapeRegExp(query.trim())
50
+
51
+ if (qTrimmed === '') {
52
+ return Conf.Items
53
+ }
54
+
55
+ const rx = new RegExp(qTrimmed, Conf.RegexFlag)
56
+
57
+ // All the dirty work is done here...
58
+ for (const item of Conf.Items) {
59
+ const searchString = Conf.Keys.map((k) => item[k]).join('^')
60
+ if (rx.test(searchString)) {
61
+ results.push(item)
62
+ }
63
+ }
64
+
65
+ return results
66
+ }