@galaxy-tool-util/search 0.1.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.
- package/LICENSE +21 -0
- package/README.md +21 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +3 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/toolshed.d.ts +22 -0
- package/dist/client/toolshed.d.ts.map +1 -0
- package/dist/client/toolshed.js +68 -0
- package/dist/client/toolshed.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/models/toolshed-search.d.ts +59 -0
- package/dist/models/toolshed-search.d.ts.map +1 -0
- package/dist/models/toolshed-search.js +125 -0
- package/dist/models/toolshed-search.js.map +1 -0
- package/dist/tool-search.d.ts +68 -0
- package/dist/tool-search.d.ts.map +1 -0
- package/dist/tool-search.js +97 -0
- package/dist/tool-search.js.map +1 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 John Chilton
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# @galaxy-tool-util/search
|
|
2
|
+
|
|
3
|
+
Tool discovery for Galaxy: Tool Shed search client, result normalization, and ranking helpers.
|
|
4
|
+
|
|
5
|
+
Built on `@galaxy-tool-util/core` (`ParsedTool`, `ToolInfoService`, cache). Browser-safe — no Node-only imports in the universal entry.
|
|
6
|
+
|
|
7
|
+
## Status
|
|
8
|
+
|
|
9
|
+
`0.1.x` — wire types and a normalizer for Tool Shed `/api/tools?q=` responses. HTTP client, `ToolSearchService`, and ranking land in subsequent minor releases.
|
|
10
|
+
|
|
11
|
+
## Usage (Stage 1)
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { normalizeToolSearchResults } from "@galaxy-tool-util/search";
|
|
15
|
+
|
|
16
|
+
const raw: unknown = await fetch(
|
|
17
|
+
"https://toolshed.g2.bx.psu.edu/api/tools?q=fastqc",
|
|
18
|
+
).then((r) => r.json());
|
|
19
|
+
const results = normalizeToolSearchResults(raw);
|
|
20
|
+
// { totalResults, page, pageSize, hostname, hits: ToolSearchHit[] }
|
|
21
|
+
```
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ToolFetchError, getTRSToolVersions, getLatestTRSToolVersion, } from "@galaxy-tool-util/core";
|
|
2
|
+
export type { TRSToolVersion } from "@galaxy-tool-util/core";
|
|
3
|
+
export { searchTools, iterateToolSearchPages } from "./toolshed.js";
|
|
4
|
+
export type { SearchToolsOptions } from "./toolshed.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACpE,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { SearchResults, ToolSearchHit } from "../models/toolshed-search.js";
|
|
2
|
+
export interface SearchToolsOptions {
|
|
3
|
+
page?: number;
|
|
4
|
+
pageSize?: number;
|
|
5
|
+
fetcher?: typeof fetch;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Fetch a page of tool search results from a Tool Shed.
|
|
9
|
+
*
|
|
10
|
+
* Pass the query verbatim — the Tool Shed wraps it with `*term*` server-side,
|
|
11
|
+
* so any client-side rewrite would double-wrap and break scoring.
|
|
12
|
+
*
|
|
13
|
+
* An `ObjectNotFound` 404 (which some Tool Shed versions return when paging
|
|
14
|
+
* past the last page of hits) is treated as an empty page.
|
|
15
|
+
*/
|
|
16
|
+
export declare function searchTools(toolshedUrl: string, query: string, opts?: SearchToolsOptions): Promise<SearchResults<ToolSearchHit>>;
|
|
17
|
+
/**
|
|
18
|
+
* Iterate Tool Shed search results page-by-page. Stops when the server
|
|
19
|
+
* returns fewer hits than `pageSize` (or no hits at all).
|
|
20
|
+
*/
|
|
21
|
+
export declare function iterateToolSearchPages(toolshedUrl: string, query: string, opts?: SearchToolsOptions): AsyncGenerator<SearchResults<ToolSearchHit>, void, void>;
|
|
22
|
+
//# sourceMappingURL=toolshed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolshed.d.ts","sourceRoot":"","sources":["../../src/client/toolshed.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAKjF,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAkDvC;AAED;;;GAGG;AACH,wBAAuB,sBAAsB,CAC3C,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,kBAAuB,GAC5B,cAAc,CAAC,aAAa,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAS1D"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ToolFetchError } from "@galaxy-tool-util/core";
|
|
2
|
+
import { normalizeToolSearchResults } from "../models/toolshed-search.js";
|
|
3
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
4
|
+
/**
|
|
5
|
+
* Fetch a page of tool search results from a Tool Shed.
|
|
6
|
+
*
|
|
7
|
+
* Pass the query verbatim — the Tool Shed wraps it with `*term*` server-side,
|
|
8
|
+
* so any client-side rewrite would double-wrap and break scoring.
|
|
9
|
+
*
|
|
10
|
+
* An `ObjectNotFound` 404 (which some Tool Shed versions return when paging
|
|
11
|
+
* past the last page of hits) is treated as an empty page.
|
|
12
|
+
*/
|
|
13
|
+
export async function searchTools(toolshedUrl, query, opts = {}) {
|
|
14
|
+
const fetcher = opts.fetcher ?? globalThis.fetch;
|
|
15
|
+
const params = new URLSearchParams({ q: query });
|
|
16
|
+
if (opts.page !== undefined)
|
|
17
|
+
params.set("page", String(opts.page));
|
|
18
|
+
if (opts.pageSize !== undefined)
|
|
19
|
+
params.set("page_size", String(opts.pageSize));
|
|
20
|
+
const url = `${toolshedUrl}/api/tools?${params.toString()}`;
|
|
21
|
+
let response;
|
|
22
|
+
try {
|
|
23
|
+
response = await fetcher(url, {
|
|
24
|
+
headers: { Accept: "application/json" },
|
|
25
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
throw new ToolFetchError(`Tool Shed search request to ${url} failed: ${err.message}`, url);
|
|
30
|
+
}
|
|
31
|
+
if (response.status === 404) {
|
|
32
|
+
console.debug(`Tool Shed search: ${url} returned 404 — treating as empty page`);
|
|
33
|
+
return {
|
|
34
|
+
total_results: 0,
|
|
35
|
+
page: opts.page ?? 1,
|
|
36
|
+
page_size: opts.pageSize ?? 0,
|
|
37
|
+
hostname: toolshedUrl,
|
|
38
|
+
hits: [],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
const body = await response.text().catch(() => "");
|
|
43
|
+
throw new ToolFetchError(`Tool Shed search request to ${url} failed: ${response.status} ${body.slice(0, 200)}`, url, response.status);
|
|
44
|
+
}
|
|
45
|
+
const json = await response.json();
|
|
46
|
+
try {
|
|
47
|
+
return normalizeToolSearchResults(json);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
throw new ToolFetchError(`Tool Shed search response from ${url} was malformed: ${err.message}`, url, response.status);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Iterate Tool Shed search results page-by-page. Stops when the server
|
|
55
|
+
* returns fewer hits than `pageSize` (or no hits at all).
|
|
56
|
+
*/
|
|
57
|
+
export async function* iterateToolSearchPages(toolshedUrl, query, opts = {}) {
|
|
58
|
+
const pageSize = opts.pageSize ?? 10;
|
|
59
|
+
let page = opts.page ?? 1;
|
|
60
|
+
while (true) {
|
|
61
|
+
const results = await searchTools(toolshedUrl, query, { ...opts, page, pageSize });
|
|
62
|
+
yield results;
|
|
63
|
+
if (results.hits.length < pageSize)
|
|
64
|
+
return;
|
|
65
|
+
page += 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=toolshed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolshed.js","sourceRoot":"","sources":["../../src/client/toolshed.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @galaxy-tool-util/search
|
|
3
|
+
*
|
|
4
|
+
* Universal entry — safe to import in Node or browser. Tool discovery
|
|
5
|
+
* primitives: Tool Shed wire types, response normalization, HTTP client, and
|
|
6
|
+
* (in subsequent releases) `ToolSearchService` + ranking.
|
|
7
|
+
*/
|
|
8
|
+
export type { ToolSearchHit, SearchResults } from "./models/toolshed-search.js";
|
|
9
|
+
export { normalizeToolSearchResults } from "./models/toolshed-search.js";
|
|
10
|
+
export { searchTools, iterateToolSearchPages, getTRSToolVersions, getLatestTRSToolVersion, ToolFetchError, } from "./client/index.js";
|
|
11
|
+
export type { SearchToolsOptions, TRSToolVersion } from "./client/index.js";
|
|
12
|
+
export { ToolSearchService } from "./tool-search.js";
|
|
13
|
+
export type { NormalizedToolHit, ToolSearchServiceOptions, SearchToolsServiceOptions, } from "./tool-search.js";
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAEzE,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,kBAAkB,EAClB,uBAAuB,EACvB,cAAc,GACf,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,YAAY,EACV,iBAAiB,EACjB,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @galaxy-tool-util/search
|
|
3
|
+
*
|
|
4
|
+
* Universal entry — safe to import in Node or browser. Tool discovery
|
|
5
|
+
* primitives: Tool Shed wire types, response normalization, HTTP client, and
|
|
6
|
+
* (in subsequent releases) `ToolSearchService` + ranking.
|
|
7
|
+
*/
|
|
8
|
+
export { normalizeToolSearchResults } from "./models/toolshed-search.js";
|
|
9
|
+
export { searchTools, iterateToolSearchPages, getTRSToolVersions, getLatestTRSToolVersion, ToolFetchError, } from "./client/index.js";
|
|
10
|
+
export { ToolSearchService } from "./tool-search.js";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAEzE,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,kBAAkB,EAClB,uBAAuB,EACvB,cAAc,GACf,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plain TypeScript types for Tool Shed `/api/tools?q=` responses, plus a
|
|
3
|
+
* one-way normalizer that coerces the server's stringified pagination numbers
|
|
4
|
+
* and shape-checks the payload.
|
|
5
|
+
*
|
|
6
|
+
* Wire types stay snake_case to mirror the Tool Shed JSON. Downstream code
|
|
7
|
+
* (`ToolSearchService`, Stage 3) flattens these into a camelCase
|
|
8
|
+
* `NormalizedToolHit` model.
|
|
9
|
+
*
|
|
10
|
+
* Effect Schema is intentionally not used here: these payloads are one-way
|
|
11
|
+
* deserialized from a trusted peer and immediately flattened, so the
|
|
12
|
+
* bidirectional codec / diagnostics tree / composable transforms that justify
|
|
13
|
+
* Effect Schema for user-authored content do not apply.
|
|
14
|
+
*/
|
|
15
|
+
/** A single hit inside a Tool Shed tool search response. */
|
|
16
|
+
export interface ToolSearchHit {
|
|
17
|
+
tool: {
|
|
18
|
+
/** Tool id local to its repository (e.g. `fastqc`). Not the full Galaxy tool id. */
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
description: string | null;
|
|
22
|
+
repo_name: string;
|
|
23
|
+
repo_owner_username: string;
|
|
24
|
+
/**
|
|
25
|
+
* Tool version. Optional — current Tool Shed search payloads omit it.
|
|
26
|
+
* See `TS_SEARCH_OVERHAUL_ISSUE.md` (P0) for the upstream patch.
|
|
27
|
+
*/
|
|
28
|
+
version?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Mercurial changeset revision the hit was indexed from. Optional for
|
|
31
|
+
* the same reason as `version`.
|
|
32
|
+
*/
|
|
33
|
+
changeset_revision?: string;
|
|
34
|
+
};
|
|
35
|
+
/** Indexed-field → matched-term map (e.g. `{ name: "fastqc", help: "fastqc" }`). */
|
|
36
|
+
matched_terms: Record<string, string>;
|
|
37
|
+
/** Whoosh BM25 score. Higher is better. */
|
|
38
|
+
score: number;
|
|
39
|
+
}
|
|
40
|
+
/** Generic Tool Shed search response wrapper. */
|
|
41
|
+
export interface SearchResults<A> {
|
|
42
|
+
total_results: number;
|
|
43
|
+
page: number;
|
|
44
|
+
page_size: number;
|
|
45
|
+
/** Tool Shed base URL the response was served from. */
|
|
46
|
+
hostname: string;
|
|
47
|
+
hits: A[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Validate and normalize a raw Tool Shed `/api/tools?q=` response.
|
|
51
|
+
*
|
|
52
|
+
* Coerces stringified pagination numbers (`total_results`, `page`,
|
|
53
|
+
* `page_size`) into JS numbers and shape-checks each hit. Throws a
|
|
54
|
+
* descriptive `Error` identifying the offending field on malformed input;
|
|
55
|
+
* the HTTP client (Stage 2) is responsible for wrapping network/transport
|
|
56
|
+
* failures as `ToolFetchError`.
|
|
57
|
+
*/
|
|
58
|
+
export declare function normalizeToolSearchResults(raw: unknown): SearchResults<ToolSearchHit>;
|
|
59
|
+
//# sourceMappingURL=toolshed-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolshed-search.d.ts","sourceRoot":"","sources":["../../src/models/toolshed-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE;QACJ,oFAAoF;QACpF,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,SAAS,EAAE,MAAM,CAAC;QAClB,mBAAmB,EAAE,MAAM,CAAC;QAC5B;;;WAGG;QACH,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB;;;WAGG;QACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,oFAAoF;IACpF,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,iDAAiD;AACjD,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,CAAC,EAAE,CAAC;CACX;AAsGD;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAAC,aAAa,CAAC,CAiBrF"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plain TypeScript types for Tool Shed `/api/tools?q=` responses, plus a
|
|
3
|
+
* one-way normalizer that coerces the server's stringified pagination numbers
|
|
4
|
+
* and shape-checks the payload.
|
|
5
|
+
*
|
|
6
|
+
* Wire types stay snake_case to mirror the Tool Shed JSON. Downstream code
|
|
7
|
+
* (`ToolSearchService`, Stage 3) flattens these into a camelCase
|
|
8
|
+
* `NormalizedToolHit` model.
|
|
9
|
+
*
|
|
10
|
+
* Effect Schema is intentionally not used here: these payloads are one-way
|
|
11
|
+
* deserialized from a trusted peer and immediately flattened, so the
|
|
12
|
+
* bidirectional codec / diagnostics tree / composable transforms that justify
|
|
13
|
+
* Effect Schema for user-authored content do not apply.
|
|
14
|
+
*/
|
|
15
|
+
function isObject(value) {
|
|
16
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17
|
+
}
|
|
18
|
+
function coerceCount(value, field) {
|
|
19
|
+
if (typeof value === "number") {
|
|
20
|
+
if (!Number.isFinite(value)) {
|
|
21
|
+
throw new Error(`Tool Shed search response: \`${field}\` is not a finite number`);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
if (typeof value === "string") {
|
|
26
|
+
const n = Number(value);
|
|
27
|
+
if (!Number.isFinite(n)) {
|
|
28
|
+
throw new Error(`Tool Shed search response: \`${field}\` (${JSON.stringify(value)}) is not numeric`);
|
|
29
|
+
}
|
|
30
|
+
return n;
|
|
31
|
+
}
|
|
32
|
+
throw new Error(`Tool Shed search response: \`${field}\` must be a number or numeric string`);
|
|
33
|
+
}
|
|
34
|
+
function coerceMatchedTerms(value, hitIndex) {
|
|
35
|
+
if (!isObject(value)) {
|
|
36
|
+
throw new Error(`Tool Shed search response: hits[${hitIndex}].matched_terms must be an object`);
|
|
37
|
+
}
|
|
38
|
+
const out = {};
|
|
39
|
+
for (const [k, v] of Object.entries(value)) {
|
|
40
|
+
if (typeof v !== "string") {
|
|
41
|
+
throw new Error(`Tool Shed search response: hits[${hitIndex}].matched_terms[${JSON.stringify(k)}] must be a string`);
|
|
42
|
+
}
|
|
43
|
+
out[k] = v;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
function coerceToolHit(value, hitIndex) {
|
|
48
|
+
if (!isObject(value)) {
|
|
49
|
+
throw new Error(`Tool Shed search response: hits[${hitIndex}] must be an object`);
|
|
50
|
+
}
|
|
51
|
+
const tool = value.tool;
|
|
52
|
+
if (!isObject(tool)) {
|
|
53
|
+
throw new Error(`Tool Shed search response: hits[${hitIndex}].tool must be an object`);
|
|
54
|
+
}
|
|
55
|
+
const required = [
|
|
56
|
+
"id",
|
|
57
|
+
"name",
|
|
58
|
+
"repo_name",
|
|
59
|
+
"repo_owner_username",
|
|
60
|
+
];
|
|
61
|
+
for (const key of required) {
|
|
62
|
+
if (typeof tool[key] !== "string") {
|
|
63
|
+
throw new Error(`Tool Shed search response: hits[${hitIndex}].tool.${String(key)} must be a string`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const description = tool.description;
|
|
67
|
+
if (description !== null && typeof description !== "string" && description !== undefined) {
|
|
68
|
+
throw new Error(`Tool Shed search response: hits[${hitIndex}].tool.description must be a string or null`);
|
|
69
|
+
}
|
|
70
|
+
const version = tool.version;
|
|
71
|
+
if (version !== undefined && typeof version !== "string") {
|
|
72
|
+
throw new Error(`Tool Shed search response: hits[${hitIndex}].tool.version must be a string`);
|
|
73
|
+
}
|
|
74
|
+
const changeset = tool.changeset_revision;
|
|
75
|
+
if (changeset !== undefined && typeof changeset !== "string") {
|
|
76
|
+
throw new Error(`Tool Shed search response: hits[${hitIndex}].tool.changeset_revision must be a string`);
|
|
77
|
+
}
|
|
78
|
+
if (typeof value.score !== "number" || !Number.isFinite(value.score)) {
|
|
79
|
+
throw new Error(`Tool Shed search response: hits[${hitIndex}].score must be a finite number`);
|
|
80
|
+
}
|
|
81
|
+
const hit = {
|
|
82
|
+
tool: {
|
|
83
|
+
id: tool.id,
|
|
84
|
+
name: tool.name,
|
|
85
|
+
description: (description ?? null),
|
|
86
|
+
repo_name: tool.repo_name,
|
|
87
|
+
repo_owner_username: tool.repo_owner_username,
|
|
88
|
+
},
|
|
89
|
+
matched_terms: coerceMatchedTerms(value.matched_terms, hitIndex),
|
|
90
|
+
score: value.score,
|
|
91
|
+
};
|
|
92
|
+
if (version !== undefined)
|
|
93
|
+
hit.tool.version = version;
|
|
94
|
+
if (changeset !== undefined)
|
|
95
|
+
hit.tool.changeset_revision = changeset;
|
|
96
|
+
return hit;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Validate and normalize a raw Tool Shed `/api/tools?q=` response.
|
|
100
|
+
*
|
|
101
|
+
* Coerces stringified pagination numbers (`total_results`, `page`,
|
|
102
|
+
* `page_size`) into JS numbers and shape-checks each hit. Throws a
|
|
103
|
+
* descriptive `Error` identifying the offending field on malformed input;
|
|
104
|
+
* the HTTP client (Stage 2) is responsible for wrapping network/transport
|
|
105
|
+
* failures as `ToolFetchError`.
|
|
106
|
+
*/
|
|
107
|
+
export function normalizeToolSearchResults(raw) {
|
|
108
|
+
if (!isObject(raw)) {
|
|
109
|
+
throw new Error("Tool Shed search response: payload must be an object");
|
|
110
|
+
}
|
|
111
|
+
if (typeof raw.hostname !== "string") {
|
|
112
|
+
throw new Error("Tool Shed search response: `hostname` must be a string");
|
|
113
|
+
}
|
|
114
|
+
if (!Array.isArray(raw.hits)) {
|
|
115
|
+
throw new Error("Tool Shed search response: `hits` must be an array");
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
total_results: coerceCount(raw.total_results, "total_results"),
|
|
119
|
+
page: coerceCount(raw.page, "page"),
|
|
120
|
+
page_size: coerceCount(raw.page_size, "page_size"),
|
|
121
|
+
hostname: raw.hostname,
|
|
122
|
+
hits: raw.hits.map(coerceToolHit),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=toolshed-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolshed-search.js","sourceRoot":"","sources":["../../src/models/toolshed-search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ToolInfoService, ToolSource } from "@galaxy-tool-util/core";
|
|
2
|
+
import type { ParsedTool } from "@galaxy-tool-util/schema";
|
|
3
|
+
/**
|
|
4
|
+
* A Tool Shed search hit flattened into a neutral, camelCase shape suitable
|
|
5
|
+
* for UI consumption and cross-source dedup.
|
|
6
|
+
*
|
|
7
|
+
* Fields mirror the Tool Shed wire types but add two derived ids:
|
|
8
|
+
* - `trsToolId` — `<owner>~<repo>~<toolId>`, consumable by the TRS API and
|
|
9
|
+
* `ToolInfoService.getToolInfo`.
|
|
10
|
+
* - `fullToolId` — `<host>/repos/<owner>/<repo>/<toolId>[/<version>]`, the id
|
|
11
|
+
* Galaxy stores in workflows.
|
|
12
|
+
*/
|
|
13
|
+
export interface NormalizedToolHit {
|
|
14
|
+
/** Source the hit came from. */
|
|
15
|
+
source: ToolSource;
|
|
16
|
+
toolId: string;
|
|
17
|
+
toolName: string;
|
|
18
|
+
toolDescription: string | null;
|
|
19
|
+
repoName: string;
|
|
20
|
+
repoOwnerUsername: string;
|
|
21
|
+
score: number;
|
|
22
|
+
/** Tool version, when the server supplies one. Tool Shed currently omits this. */
|
|
23
|
+
version?: string;
|
|
24
|
+
/** Mercurial changeset revision, when the server supplies one. */
|
|
25
|
+
changesetRevision?: string;
|
|
26
|
+
/** `<owner>~<repo>~<toolId>` — TRS-style id. */
|
|
27
|
+
trsToolId: string;
|
|
28
|
+
/** `<host>/repos/<owner>/<repo>/<toolId>[/<version>]` — full Galaxy tool id. */
|
|
29
|
+
fullToolId: string;
|
|
30
|
+
/** Populated when `enrich: true` was requested and the fetch succeeded. */
|
|
31
|
+
parsedTool?: ParsedTool;
|
|
32
|
+
}
|
|
33
|
+
export interface SearchToolsServiceOptions {
|
|
34
|
+
/** Server-side page size. Defaults to 20. */
|
|
35
|
+
pageSize?: number;
|
|
36
|
+
/** Hard cap on hits returned (after dedup). Defaults to 50. */
|
|
37
|
+
maxResults?: number;
|
|
38
|
+
/** When true, resolve each hit's `ParsedTool` via the info service (and cache). */
|
|
39
|
+
enrich?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface ToolSearchServiceOptions {
|
|
42
|
+
/**
|
|
43
|
+
* Sources to query. Only `type: "toolshed"` sources are searched — Galaxy
|
|
44
|
+
* instances do not expose an equivalent tool-search endpoint.
|
|
45
|
+
*/
|
|
46
|
+
sources: ToolSource[];
|
|
47
|
+
/** Shared with `ToolInfoService` so enriched hits reuse its cache. */
|
|
48
|
+
info: ToolInfoService;
|
|
49
|
+
fetcher?: typeof fetch;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* High-level tool discovery service. Fans a query out across configured
|
|
53
|
+
* Tool Shed sources, dedupes hits that describe the same `(owner, repo,
|
|
54
|
+
* toolId)` across mirrors (first source wins), sorts by server score, and
|
|
55
|
+
* optionally enriches each hit with a full `ParsedTool`.
|
|
56
|
+
*/
|
|
57
|
+
export declare class ToolSearchService {
|
|
58
|
+
private readonly sources;
|
|
59
|
+
private readonly info;
|
|
60
|
+
private readonly fetcher;
|
|
61
|
+
constructor(opts: ToolSearchServiceOptions);
|
|
62
|
+
searchTools(query: string, opts?: SearchToolsServiceOptions): Promise<NormalizedToolHit[]>;
|
|
63
|
+
getToolVersions(toolshedUrl: string, trsToolId: string): Promise<string[]>;
|
|
64
|
+
getLatestVersionForToolId(toolshedUrl: string, trsToolId: string): Promise<string | null>;
|
|
65
|
+
private collectFromSource;
|
|
66
|
+
private enrich;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=tool-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-search.d.ts","sourceRoot":"","sources":["../src/tool-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAK3D;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,gCAAgC;IAChC,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACxC,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,sEAAsE;IACtE,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED;;;;;GAKG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkB;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;gBAE3B,IAAI,EAAE,wBAAwB;IAMpC,WAAW,CACf,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,yBAA8B,GACnC,OAAO,CAAC,iBAAiB,EAAE,CAAC;IA+BzB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAK1E,yBAAyB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;YAIjF,iBAAiB;YAmBjB,MAAM;CASrB"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { getLatestTRSToolVersion, getTRSToolVersions, toolIdFromTrs } from "@galaxy-tool-util/core";
|
|
2
|
+
import { iterateToolSearchPages } from "./client/toolshed.js";
|
|
3
|
+
/**
|
|
4
|
+
* High-level tool discovery service. Fans a query out across configured
|
|
5
|
+
* Tool Shed sources, dedupes hits that describe the same `(owner, repo,
|
|
6
|
+
* toolId)` across mirrors (first source wins), sorts by server score, and
|
|
7
|
+
* optionally enriches each hit with a full `ParsedTool`.
|
|
8
|
+
*/
|
|
9
|
+
export class ToolSearchService {
|
|
10
|
+
sources;
|
|
11
|
+
info;
|
|
12
|
+
fetcher;
|
|
13
|
+
constructor(opts) {
|
|
14
|
+
this.sources = opts.sources.filter((s) => s.type === "toolshed");
|
|
15
|
+
this.info = opts.info;
|
|
16
|
+
this.fetcher = opts.fetcher ?? globalThis.fetch;
|
|
17
|
+
}
|
|
18
|
+
async searchTools(query, opts = {}) {
|
|
19
|
+
const pageSize = opts.pageSize ?? 20;
|
|
20
|
+
const maxResults = opts.maxResults ?? 50;
|
|
21
|
+
const perSource = await Promise.all(this.sources.map((source) => this.collectFromSource(source, query, pageSize, maxResults).catch((err) => {
|
|
22
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
23
|
+
console.debug(`Tool Shed search failed for ${source.url}: ${msg}`);
|
|
24
|
+
return [];
|
|
25
|
+
})));
|
|
26
|
+
const dedupKey = (h) => `${h.repoOwnerUsername}~${h.repoName}~${h.toolId}`;
|
|
27
|
+
const seen = new Map();
|
|
28
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
29
|
+
for (const hit of perSource[i]) {
|
|
30
|
+
const k = dedupKey(hit);
|
|
31
|
+
if (!seen.has(k))
|
|
32
|
+
seen.set(k, hit);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const merged = Array.from(seen.values()).sort((a, b) => b.score - a.score);
|
|
36
|
+
const truncated = merged.slice(0, maxResults);
|
|
37
|
+
if (opts.enrich) {
|
|
38
|
+
await Promise.all(truncated.map((hit) => this.enrich(hit)));
|
|
39
|
+
}
|
|
40
|
+
return truncated;
|
|
41
|
+
}
|
|
42
|
+
async getToolVersions(toolshedUrl, trsToolId) {
|
|
43
|
+
const versions = await getTRSToolVersions(toolshedUrl, trsToolId, this.fetcher);
|
|
44
|
+
return versions.map((v) => v.id);
|
|
45
|
+
}
|
|
46
|
+
async getLatestVersionForToolId(toolshedUrl, trsToolId) {
|
|
47
|
+
return getLatestTRSToolVersion(toolshedUrl, trsToolId, this.fetcher);
|
|
48
|
+
}
|
|
49
|
+
async collectFromSource(source, query, pageSize, maxResults) {
|
|
50
|
+
const out = [];
|
|
51
|
+
for await (const page of iterateToolSearchPages(source.url, query, {
|
|
52
|
+
pageSize,
|
|
53
|
+
fetcher: this.fetcher,
|
|
54
|
+
})) {
|
|
55
|
+
for (const hit of page.hits) {
|
|
56
|
+
out.push(normalizeHit(hit, source));
|
|
57
|
+
}
|
|
58
|
+
if (out.length >= maxResults)
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
async enrich(hit) {
|
|
64
|
+
try {
|
|
65
|
+
const parsed = await this.info.getToolInfo(hit.trsToolId, hit.version ?? null);
|
|
66
|
+
if (parsed !== null)
|
|
67
|
+
hit.parsedTool = parsed;
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
71
|
+
console.debug(`Enrichment failed for ${hit.trsToolId}: ${msg}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function normalizeHit(hit, source) {
|
|
76
|
+
const { id, name, description, repo_name, repo_owner_username, version, changeset_revision } = hit.tool;
|
|
77
|
+
const trsToolId = `${repo_owner_username}~${repo_name}~${id}`;
|
|
78
|
+
const base = toolIdFromTrs(source.url, trsToolId);
|
|
79
|
+
const fullToolId = version ? `${base}/${version}` : base;
|
|
80
|
+
const normalized = {
|
|
81
|
+
source,
|
|
82
|
+
toolId: id,
|
|
83
|
+
toolName: name,
|
|
84
|
+
toolDescription: description,
|
|
85
|
+
repoName: repo_name,
|
|
86
|
+
repoOwnerUsername: repo_owner_username,
|
|
87
|
+
score: hit.score,
|
|
88
|
+
trsToolId,
|
|
89
|
+
fullToolId,
|
|
90
|
+
};
|
|
91
|
+
if (version !== undefined)
|
|
92
|
+
normalized.version = version;
|
|
93
|
+
if (changeset_revision !== undefined)
|
|
94
|
+
normalized.changesetRevision = changeset_revision;
|
|
95
|
+
return normalized;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=tool-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-search.js","sourceRoot":"","sources":["../src/tool-search.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@galaxy-tool-util/search",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Tool discovery — Tool Shed search client, result normalization, and ranking",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"browser": "./dist/index.js",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/jmchilton/galaxy-tool-util-ts",
|
|
23
|
+
"directory": "packages/search"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public",
|
|
27
|
+
"provenance": true
|
|
28
|
+
},
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^25.5.0"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@galaxy-tool-util/core": "0.3.0",
|
|
35
|
+
"@galaxy-tool-util/schema": "0.4.0"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"test": "tsc --noEmit && vitest run",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"lint": "eslint src/ test/",
|
|
43
|
+
"format": "prettier --check 'src/**/*.ts' 'test/**/*.ts'",
|
|
44
|
+
"format-fix": "prettier --write 'src/**/*.ts' 'test/**/*.ts'",
|
|
45
|
+
"lint:pkg": "publint",
|
|
46
|
+
"check:types": "attw --pack . --profile esm-only",
|
|
47
|
+
"check:browser": "node scripts/verify-browser-entry.mjs"
|
|
48
|
+
}
|
|
49
|
+
}
|