@hesed/search 0.1.0 → 0.2.1

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/README.md CHANGED
@@ -20,7 +20,7 @@ $ npm install -g @hesed/search
20
20
  $ search COMMAND
21
21
  running command...
22
22
  $ search (--version)
23
- @hesed/search/0.1.0 darwin-arm64 node-v22.14.0
23
+ @hesed/search/0.2.1 linux-x64 node-v24.16.0
24
24
  $ search --help [COMMAND]
25
25
  USAGE
26
26
  $ search COMMAND
@@ -62,5 +62,5 @@ EXAMPLES
62
62
  $ search search "update jira" --details
63
63
  ```
64
64
 
65
- _See code: [src/commands/search.ts](https://github.com/hesedcasa/search/blob/v0.1.0/src/commands/search.ts)_
65
+ _See code: [src/commands/search.ts](https://github.com/hesedcasa/search/blob/v0.2.1/src/commands/search.ts)_
66
66
  <!-- commandsstop -->
@@ -18,7 +18,7 @@ export default class Search extends Command {
18
18
  async run() {
19
19
  const { args, flags } = await this.parse(Search);
20
20
  const allCommands = this.config.commands.filter((c) => !c.hidden && c.pluginName !== '@oclif/plugin-plugins');
21
- const scored = searchCommands(args.query, allCommands).slice(0, flags.limit);
21
+ const scored = (await searchCommands(args.query, allCommands)).slice(0, flags.limit);
22
22
  const results = scored.map((entry) => {
23
23
  const { cmd } = entry;
24
24
  const configuredId = toConfiguredId(cmd.id, this.config);
@@ -8,4 +8,5 @@ export type ScoredCommand<T extends SearchableCommand = SearchableCommand> = {
8
8
  cmd: T;
9
9
  score: number;
10
10
  };
11
- export declare function searchCommands<T extends SearchableCommand>(query: string, commands: T[]): Array<ScoredCommand<T>>;
11
+ export declare function searchCommands<T extends SearchableCommand>(query: string, commands: T[]): Promise<Array<ScoredCommand<T>>>;
12
+ export declare function searchCommandsLexically<T extends SearchableCommand>(query: string, commands: T[], haystack?: string[]): Array<ScoredCommand<T>>;
@@ -1,11 +1,18 @@
1
- import UFuzzy from '@leeoniya/ufuzzy';
2
- export function searchCommands(query, commands) {
3
- const uf = new UFuzzy({ intraIns: Infinity });
4
- const haystack = commands.map((c) => [c.id, c.summary ?? c.description ?? '', c.pluginName ?? ''].filter(Boolean).join(' '));
5
- const [idxs, , order] = uf.search(haystack, query, 0, Infinity);
6
- if (idxs && idxs.length > 0) {
7
- const ranked = order ?? idxs.map((_, i) => i);
8
- return ranked.map((oi, rank) => ({ cmd: commands[idxs[oi]], score: rank }));
1
+ import { createRequire } from 'node:module';
2
+ const require = createRequire(import.meta.url);
3
+ const { Index } = require('flexsearch');
4
+ export async function searchCommands(query, commands) {
5
+ const normalizedQuery = query.trim();
6
+ if (normalizedQuery.length === 0 || commands.length === 0)
7
+ return [];
8
+ const haystack = commands.map((command) => commandSearchText(command));
9
+ return searchCommandsLexically(normalizedQuery, commands, haystack);
10
+ }
11
+ export function searchCommandsLexically(query, commands, haystack = commands.map((command) => commandSearchText(command))) {
12
+ const index = createCommandSearchIndex(haystack);
13
+ const idxs = index.search(query, { limit: commands.length, suggest: true });
14
+ if (idxs.length > 0) {
15
+ return idxs.map((idx, rank) => ({ cmd: commands[Number(idx)], score: rank }));
9
16
  }
10
17
  // Multi-token fallback: score each command by how many individual query
11
18
  // tokens it matches. Handles queries containing unknown alias words (e.g.
@@ -15,13 +22,27 @@ export function searchCommands(query, commands) {
15
22
  return [];
16
23
  const hitCount = new Map();
17
24
  for (const token of tokens) {
18
- const [tIdxs] = uf.search(haystack, token, 0, Infinity);
19
- if (tIdxs) {
20
- for (const idx of tIdxs)
21
- hitCount.set(idx, (hitCount.get(idx) ?? 0) + 1);
25
+ const tIdxs = index.search(token, { limit: commands.length, suggest: true });
26
+ for (const idx of tIdxs) {
27
+ const commandIndex = Number(idx);
28
+ hitCount.set(commandIndex, (hitCount.get(commandIndex) ?? 0) + 1);
22
29
  }
23
30
  }
24
31
  return [...hitCount.entries()]
25
32
  .sort((a, b) => b[1] - a[1] || a[0] - b[0])
26
33
  .map(([idx, hits]) => ({ cmd: commands[idx], score: tokens.length - hits }));
27
34
  }
35
+ function createCommandSearchIndex(haystack) {
36
+ const index = new Index({ encoder: 'LatinAdvanced', resolution: 9, tokenize: 'forward' });
37
+ for (const [idx, text] of haystack.entries()) {
38
+ index.add(idx, text);
39
+ }
40
+ return index;
41
+ }
42
+ function commandSearchText(command) {
43
+ return [command.id, command.summary ?? command.description ?? '', command.pluginName ?? '']
44
+ .filter(Boolean)
45
+ .join(' ')
46
+ .replaceAll(/\s+/g, ' ')
47
+ .trim();
48
+ }
@@ -58,5 +58,5 @@
58
58
  ]
59
59
  }
60
60
  },
61
- "version": "0.1.0"
61
+ "version": "0.2.1"
62
62
  }
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@hesed/search",
3
3
  "description": "Intelligence search plugin",
4
- "version": "0.1.0",
4
+ "version": "0.2.1",
5
5
  "author": "Hesed",
6
6
  "bin": {
7
7
  "permission": "./bin/run.js"
8
8
  },
9
9
  "bugs": "https://github.com/hesedcasa/search/issues",
10
10
  "dependencies": {
11
- "@leeoniya/ufuzzy": "^1.0.19",
12
- "@oclif/core": "^4"
11
+ "@oclif/core": "^4",
12
+ "flexsearch": "^0.8.212"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@eslint/compat": "^1",