@omnicross/contracts 0.2.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/LICENSE +21 -21
- package/README.md +15 -15
- package/dist/index.cjs +184 -2
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +173 -1
- package/dist/search-compat.cjs +130 -0
- package/dist/search-compat.d.cts +93 -0
- package/dist/search-compat.d.ts +93 -0
- package/dist/search-compat.js +100 -0
- package/dist/search-types.cjs +102 -0
- package/dist/search-types.d.cts +420 -0
- package/dist/search-types.d.ts +420 -0
- package/dist/search-types.js +74 -0
- package/dist/websearch-types.d.cts +20 -2
- package/dist/websearch-types.d.ts +20 -2
- package/package.json +13 -2
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-neutral search contracts — the Phase-1 target surface.
|
|
3
|
+
*
|
|
4
|
+
* This module is the vocabulary every later search stage implements against:
|
|
5
|
+
* HTTP providers, API providers, the registry/orchestrator, and the protocol
|
|
6
|
+
* frontends. It is deliberately pure — no Node builtins, no Electron, no HTTP
|
|
7
|
+
* client, no upstream SDK types — so it can be imported from a browser bundle,
|
|
8
|
+
* a daemon, or a host application alike. `AbortSignal` is used only as an
|
|
9
|
+
* ambient web-standard type.
|
|
10
|
+
*
|
|
11
|
+
* Two rules this module exists to enforce:
|
|
12
|
+
*
|
|
13
|
+
* 1. **Nothing is inferred from an identifier's spelling.** A provider's
|
|
14
|
+
* transport and trust boundary are declared explicitly on
|
|
15
|
+
* {@link SearchProviderContribution} (`kind` / `source`). No exported
|
|
16
|
+
* function here classifies a provider from its id, and none ever should —
|
|
17
|
+
* that is what the deprecated `isApiProvider`/`isLocalProvider` helpers in
|
|
18
|
+
* `./websearch-types` did.
|
|
19
|
+
* 2. **The provider id space is open.** {@link KnownSearchProviderId} lists
|
|
20
|
+
* only the providers Omnicross itself ships; host contributions and future
|
|
21
|
+
* providers arrive as plain strings without a contract change.
|
|
22
|
+
*
|
|
23
|
+
* Legacy `WebSearch*` shapes in `./websearch-types` stay where they are;
|
|
24
|
+
* `./search-compat` converts between the two while consumers migrate.
|
|
25
|
+
*/
|
|
26
|
+
/** A single search result item. Field names are the legacy ones, on purpose. */
|
|
27
|
+
interface SearchResult {
|
|
28
|
+
/** Result title. Non-empty for a usable result. */
|
|
29
|
+
title: string;
|
|
30
|
+
/** Direct result URL (not a SERP redirect). */
|
|
31
|
+
url: string;
|
|
32
|
+
/** Result snippet or page content. May be `''` when the source has none. */
|
|
33
|
+
content: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A completed search from ONE provider.
|
|
37
|
+
*
|
|
38
|
+
* There is no `success` flag and no error string: a response only exists for a
|
|
39
|
+
* search that produced results. Failures travel as {@link SearchProviderError}
|
|
40
|
+
* (thrown) or {@link SearchErrorShape} (serialized).
|
|
41
|
+
*/
|
|
42
|
+
interface SearchResponse {
|
|
43
|
+
/** The query that produced these results. */
|
|
44
|
+
query: string;
|
|
45
|
+
/** The provider that produced them. */
|
|
46
|
+
providerId: SearchProviderId;
|
|
47
|
+
/** Results in provider order, already deduplicated by the producer. */
|
|
48
|
+
results: SearchResult[];
|
|
49
|
+
}
|
|
50
|
+
/** Recency window for providers whose `supportsTimeRange` capability is true. */
|
|
51
|
+
type SearchTimeRange = 'day' | 'week' | 'month' | 'year';
|
|
52
|
+
/**
|
|
53
|
+
* Per-request options. The first four fields are the legacy
|
|
54
|
+
* `WebSearchOptions` set, unchanged, so legacy call sites assign without a
|
|
55
|
+
* mapping; the rest match the capability flags a provider declares.
|
|
56
|
+
*
|
|
57
|
+
* A provider MUST ignore an option it does not declare support for rather than
|
|
58
|
+
* failing the request.
|
|
59
|
+
*/
|
|
60
|
+
interface SearchOptions {
|
|
61
|
+
/** Maximum number of results to return. */
|
|
62
|
+
maxResults?: number;
|
|
63
|
+
/** Request timeout in milliseconds. */
|
|
64
|
+
timeout?: number;
|
|
65
|
+
/** Cancellation signal — the only cancellation channel in this contract. */
|
|
66
|
+
signal?: AbortSignal;
|
|
67
|
+
/** Fetch full page content instead of snippets, where the provider can. */
|
|
68
|
+
fetchPageContent?: boolean;
|
|
69
|
+
/** Region/market hint (provider-specific spelling, e.g. `us`, `zh-CN`). */
|
|
70
|
+
region?: string;
|
|
71
|
+
/** Result language hint. */
|
|
72
|
+
language?: string;
|
|
73
|
+
/** Recency window. */
|
|
74
|
+
timeRange?: SearchTimeRange;
|
|
75
|
+
}
|
|
76
|
+
/** A search to execute, optionally pinned to one provider. */
|
|
77
|
+
interface SearchRequest {
|
|
78
|
+
/** The user or tool supplied query. Untrusted input. */
|
|
79
|
+
query: string;
|
|
80
|
+
/** Pin the search to one provider; omit to let the runtime choose. */
|
|
81
|
+
provider?: SearchProviderId;
|
|
82
|
+
/** Per-request options. */
|
|
83
|
+
options?: SearchOptions;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The provider ids Omnicross itself ships in Phase 1.
|
|
87
|
+
*
|
|
88
|
+
* This list is NOT the set of valid ids — see {@link SearchProviderId}. Ids
|
|
89
|
+
* absent from it are absent by decision, not by oversight: `local-*` providers
|
|
90
|
+
* remain host (Elftia) contributions because they need a browser runtime, and
|
|
91
|
+
* `grok` / `claude` / `exa` / `bocha` are deliberately not registered in
|
|
92
|
+
* Phase 1.
|
|
93
|
+
*/
|
|
94
|
+
type KnownSearchProviderId = 'http-bing' | 'http-duckduckgo' | 'tavily' | 'jina' | 'searxng' | 'zhipu' | 'z.ai';
|
|
95
|
+
/**
|
|
96
|
+
* A provider identifier — open by construction.
|
|
97
|
+
*
|
|
98
|
+
* `(string & {})` keeps autocomplete for {@link KnownSearchProviderId} while
|
|
99
|
+
* accepting any other string, so host contributions and namespaced custom ids
|
|
100
|
+
* (`acme:internal-search`) are first-class without growing a union. Whether an
|
|
101
|
+
* id is *registered* is a registry question, not a type-level one.
|
|
102
|
+
*/
|
|
103
|
+
type SearchProviderId = KnownSearchProviderId | (string & {});
|
|
104
|
+
/**
|
|
105
|
+
* Narrow an id to {@link KnownSearchProviderId}.
|
|
106
|
+
*
|
|
107
|
+
* This is a membership test against the shipped list — it does NOT classify.
|
|
108
|
+
* A `false` result says "not one of the ids Omnicross ships", never "invalid",
|
|
109
|
+
* "untrusted", or "local".
|
|
110
|
+
*/
|
|
111
|
+
declare function isKnownSearchProviderId(id: SearchProviderId): id is KnownSearchProviderId;
|
|
112
|
+
/**
|
|
113
|
+
* What a provider can do, declared explicitly.
|
|
114
|
+
*
|
|
115
|
+
* Flat booleans plus one limit: the shape serializes as-is into capability
|
|
116
|
+
* discovery and doctor output, and extends additively.
|
|
117
|
+
*/
|
|
118
|
+
interface SearchProviderCapabilities {
|
|
119
|
+
/** Requires a configured API key to run at all. */
|
|
120
|
+
requiresApiKey: boolean;
|
|
121
|
+
/** Honors {@link SearchOptions.region}. */
|
|
122
|
+
supportsRegion: boolean;
|
|
123
|
+
/** Honors {@link SearchOptions.language}. */
|
|
124
|
+
supportsLanguage: boolean;
|
|
125
|
+
/** Honors {@link SearchOptions.timeRange}. */
|
|
126
|
+
supportsTimeRange: boolean;
|
|
127
|
+
/** Implements {@link SearchProvider.readUrl}. */
|
|
128
|
+
supportsUrlRead: boolean;
|
|
129
|
+
/** Honors {@link SearchOptions.signal}. */
|
|
130
|
+
supportsCancellation: boolean;
|
|
131
|
+
/** Upper bound on results per request, when the provider imposes one. */
|
|
132
|
+
maxResults?: number;
|
|
133
|
+
}
|
|
134
|
+
/** The result of reading one URL through a provider that supports it. */
|
|
135
|
+
interface SearchUrlReadResult {
|
|
136
|
+
/** The URL that was read (post-redirect, when the provider reports it). */
|
|
137
|
+
url: string;
|
|
138
|
+
/** Page title, when available. */
|
|
139
|
+
title?: string;
|
|
140
|
+
/** Extracted page content, when available. */
|
|
141
|
+
content?: string;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* A search provider.
|
|
145
|
+
*
|
|
146
|
+
* `search` returns bare results; assembling a {@link SearchResponse} is the
|
|
147
|
+
* runtime's job. Failures are thrown as {@link SearchProviderError} — there is
|
|
148
|
+
* no in-band failure channel, because a code-less error string cannot drive a
|
|
149
|
+
* fallback policy.
|
|
150
|
+
*/
|
|
151
|
+
interface SearchProvider {
|
|
152
|
+
/** Stable provider identifier, matching its contribution's `id`. */
|
|
153
|
+
readonly id: SearchProviderId;
|
|
154
|
+
/** Run a search. Throws {@link SearchProviderError} on failure. */
|
|
155
|
+
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
156
|
+
/** Read one URL, when `capabilities.supportsUrlRead` is true. */
|
|
157
|
+
readUrl?(url: string, options?: SearchOptions): Promise<SearchUrlReadResult>;
|
|
158
|
+
/** Report health without performing a real search. */
|
|
159
|
+
healthCheck?(): Promise<SearchProviderDiagnostic>;
|
|
160
|
+
}
|
|
161
|
+
/** Who supplied a provider: the Omnicross runtime, or the embedding host. */
|
|
162
|
+
type SearchProviderSource = 'builtin' | 'host';
|
|
163
|
+
/**
|
|
164
|
+
* How a provider reaches the network. This is the baseline's observed
|
|
165
|
+
* transport vocabulary, kept verbatim so behavior comparisons across the
|
|
166
|
+
* legacy and new runtimes speak one language.
|
|
167
|
+
*/
|
|
168
|
+
type SearchTransportKind = 'api' | 'http' | 'local-browser' | 'native';
|
|
169
|
+
/**
|
|
170
|
+
* A provider offered to the registry, with everything the runtime must never
|
|
171
|
+
* infer stated up front.
|
|
172
|
+
*
|
|
173
|
+
* `source` and `kind` together are the structural replacement for
|
|
174
|
+
* `id.startsWith('local-')`: eligibility, secret handling, and egress policy
|
|
175
|
+
* read these declared fields, never the id's spelling.
|
|
176
|
+
*/
|
|
177
|
+
interface SearchProviderContribution {
|
|
178
|
+
/** Stable id the provider registers under. */
|
|
179
|
+
id: SearchProviderId;
|
|
180
|
+
/** Explicit origin declaration. */
|
|
181
|
+
source: SearchProviderSource;
|
|
182
|
+
/** Explicit transport declaration. */
|
|
183
|
+
kind: SearchTransportKind;
|
|
184
|
+
/** The provider implementation. */
|
|
185
|
+
provider: SearchProvider;
|
|
186
|
+
/** Explicit capability declaration. */
|
|
187
|
+
capabilities: SearchProviderCapabilities;
|
|
188
|
+
/** Ordering hint for the registry; lower runs earlier. Ties keep registration order. */
|
|
189
|
+
priorityHint?: number;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* The stable search error taxonomy — exactly eight codes.
|
|
193
|
+
*
|
|
194
|
+
* These are the vocabulary a fallback policy decides on (`cancelled` and
|
|
195
|
+
* `policy_denied` must not be retried) and the vocabulary behavior comparisons
|
|
196
|
+
* are written in. `blocked` is deliberately absent: it is a *diagnostic
|
|
197
|
+
* status* ({@link SearchProviderHealthStatus}), not a failure code.
|
|
198
|
+
*/
|
|
199
|
+
type SearchErrorCode = 'config_missing' | 'auth_failed' | 'rate_limited' | 'timeout' | 'upstream_unavailable' | 'parse_failed' | 'cancelled' | 'policy_denied';
|
|
200
|
+
/**
|
|
201
|
+
* A search failure in serializable form — what crosses a wire, lands in a
|
|
202
|
+
* diagnostic, or gets logged.
|
|
203
|
+
*
|
|
204
|
+
* `message` and `details` values MUST be pre-sanitized by the producer: no API
|
|
205
|
+
* keys, no cookies, no raw headers, no proxy URLs. `details` is deliberately
|
|
206
|
+
* `Record<string, string>` so nothing nested can smuggle a credential object.
|
|
207
|
+
*/
|
|
208
|
+
interface SearchErrorShape {
|
|
209
|
+
/** Stable taxonomy code. */
|
|
210
|
+
code: SearchErrorCode;
|
|
211
|
+
/** Human-readable, pre-sanitized message. */
|
|
212
|
+
message: string;
|
|
213
|
+
/** The provider that failed, when known. */
|
|
214
|
+
providerId?: SearchProviderId;
|
|
215
|
+
/** Whether retrying the same provider could plausibly succeed. */
|
|
216
|
+
retryable?: boolean;
|
|
217
|
+
/** Pre-sanitized string-valued context. */
|
|
218
|
+
details?: Record<string, string>;
|
|
219
|
+
}
|
|
220
|
+
/** Optional fields for {@link SearchProviderError}. */
|
|
221
|
+
interface SearchProviderErrorInit {
|
|
222
|
+
/** The provider that failed. */
|
|
223
|
+
providerId?: SearchProviderId;
|
|
224
|
+
/** Whether retrying the same provider could plausibly succeed. */
|
|
225
|
+
retryable?: boolean;
|
|
226
|
+
/** Pre-sanitized string-valued context. */
|
|
227
|
+
details?: Record<string, string>;
|
|
228
|
+
/** Underlying error, for local diagnosis. Never serialized into the shape. */
|
|
229
|
+
cause?: unknown;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* The error providers throw.
|
|
233
|
+
*
|
|
234
|
+
* Carries the same fields as {@link SearchErrorShape} plus an optional
|
|
235
|
+
* `cause`, which stays local — {@link toSearchErrorShape} never serializes it.
|
|
236
|
+
*/
|
|
237
|
+
declare class SearchProviderError extends Error {
|
|
238
|
+
/** Stable taxonomy code. */
|
|
239
|
+
readonly code: SearchErrorCode;
|
|
240
|
+
/** The provider that failed, when known. */
|
|
241
|
+
readonly providerId?: SearchProviderId;
|
|
242
|
+
/** Whether retrying the same provider could plausibly succeed. */
|
|
243
|
+
readonly retryable?: boolean;
|
|
244
|
+
/** Pre-sanitized string-valued context. */
|
|
245
|
+
readonly details?: Record<string, string>;
|
|
246
|
+
constructor(code: SearchErrorCode, message: string, init?: SearchProviderErrorInit);
|
|
247
|
+
/** This error as its serializable {@link SearchErrorShape}. */
|
|
248
|
+
toShape(): SearchErrorShape;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Whether a value is a search error carrying a taxonomy code.
|
|
252
|
+
*
|
|
253
|
+
* Structural as well as `instanceof`, because `@omnicross/contracts` can be
|
|
254
|
+
* present twice in one process (ESM and CJS builds, or `src` under test beside
|
|
255
|
+
* `dist` at runtime) and a bare `instanceof` silently fails across those
|
|
256
|
+
* copies.
|
|
257
|
+
*/
|
|
258
|
+
declare function isSearchProviderError(value: unknown): value is SearchProviderError;
|
|
259
|
+
/**
|
|
260
|
+
* Convert any thrown value into a {@link SearchErrorShape}.
|
|
261
|
+
*
|
|
262
|
+
* A {@link SearchProviderError} keeps its code and fields. Anything else gets
|
|
263
|
+
* the documented default code `upstream_unavailable` — this function does NOT
|
|
264
|
+
* sniff messages; string-to-code translation of legacy error text lives in
|
|
265
|
+
* `./search-compat` so exactly one mapping exists.
|
|
266
|
+
*/
|
|
267
|
+
declare function toSearchErrorShape(value: unknown): SearchErrorShape;
|
|
268
|
+
/**
|
|
269
|
+
* The knobs a runtime uses to choose and order providers.
|
|
270
|
+
*
|
|
271
|
+
* These are plan §11.3's query-egress controls: a query reaches a provider only
|
|
272
|
+
* because policy allowed it, so "send this query to exactly one provider" is
|
|
273
|
+
* expressible (`fallbackEnabled: false`, or a one-id `allowed`) rather than
|
|
274
|
+
* implied. Every field is optional; the documented defaults are fallback on,
|
|
275
|
+
* every registered provider allowed, and an unbounded number of attempts.
|
|
276
|
+
*/
|
|
277
|
+
interface SearchPolicy {
|
|
278
|
+
/** Try this provider first when the request pins none. Unknown ids are skipped, not errors. */
|
|
279
|
+
preferred?: SearchProviderId;
|
|
280
|
+
/** Restrict candidates to these ids. Omit to allow every registered provider. */
|
|
281
|
+
allowed?: SearchProviderId[];
|
|
282
|
+
/** Whether a failed candidate may be followed by another. Omit for `true`. */
|
|
283
|
+
fallbackEnabled?: boolean;
|
|
284
|
+
/** Upper bound on candidates attempted for one search. Omit for unbounded. */
|
|
285
|
+
maxAttempts?: number;
|
|
286
|
+
}
|
|
287
|
+
/** How one provider attempt ended. Empty results are a `success`. */
|
|
288
|
+
type SearchAttemptOutcome = 'success' | 'failed';
|
|
289
|
+
/**
|
|
290
|
+
* One provider attempt, as recorded by the orchestrator.
|
|
291
|
+
*
|
|
292
|
+
* This is the structured record; it carries no query text and no result
|
|
293
|
+
* content, so it is safe to log or return to a caller verbatim.
|
|
294
|
+
*/
|
|
295
|
+
interface SearchAttempt {
|
|
296
|
+
/** The provider that was attempted. */
|
|
297
|
+
providerId: SearchProviderId;
|
|
298
|
+
/** How the attempt ended. */
|
|
299
|
+
outcome: SearchAttemptOutcome;
|
|
300
|
+
/** Taxonomy code for a `failed` attempt. */
|
|
301
|
+
errorCode?: SearchErrorCode;
|
|
302
|
+
/** Results the attempt produced; `0` is a legitimate success. */
|
|
303
|
+
resultCount?: number;
|
|
304
|
+
/** Wall-clock duration of the attempt in milliseconds. */
|
|
305
|
+
durationMs: number;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* A {@link SearchResponse} plus what the orchestrator did to produce it.
|
|
309
|
+
*
|
|
310
|
+
* `attempts` records provider EXECUTIONS in order; candidates that were never
|
|
311
|
+
* attempted (skipped by policy, cut off by `maxAttempts`) do not appear.
|
|
312
|
+
*/
|
|
313
|
+
interface OrchestratedSearchResponse extends SearchResponse {
|
|
314
|
+
/** Every provider attempt, in the order they ran. */
|
|
315
|
+
attempts: SearchAttempt[];
|
|
316
|
+
/** Attempts beyond the first — `attempts.length - 1`, never negative. */
|
|
317
|
+
fallbackCount: number;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* A registered provider as capability discovery sees it.
|
|
321
|
+
*
|
|
322
|
+
* Serializable by construction: the provider instance is deliberately absent,
|
|
323
|
+
* so a descriptor can cross a wire, land in doctor output, or reach a renderer
|
|
324
|
+
* without dragging an implementation (or its configuration) along.
|
|
325
|
+
*/
|
|
326
|
+
interface SearchProviderDescriptor {
|
|
327
|
+
/** The id the provider is registered under. */
|
|
328
|
+
id: SearchProviderId;
|
|
329
|
+
/** Declared origin. */
|
|
330
|
+
source: SearchProviderSource;
|
|
331
|
+
/** Declared transport. */
|
|
332
|
+
kind: SearchTransportKind;
|
|
333
|
+
/** Declared capabilities. */
|
|
334
|
+
capabilities: SearchProviderCapabilities;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Who is registering a contribution.
|
|
338
|
+
*
|
|
339
|
+
* Minimal on purpose: the parameter exists today so a Phase-2 host registering
|
|
340
|
+
* its `local-*` providers does not force a signature change, and so registry
|
|
341
|
+
* policy has somewhere to read a caller identity from when it needs one.
|
|
342
|
+
*/
|
|
343
|
+
interface SearchContributionContext {
|
|
344
|
+
/** Identifier of the embedding host making the registration. */
|
|
345
|
+
hostId?: string;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Fields every runtime event carries.
|
|
349
|
+
*
|
|
350
|
+
* `queryHash` — never the query — is the correlation key (plan §11.3). No
|
|
351
|
+
* variant of {@link SearchRuntimeEvent} declares a field for query text, a URL,
|
|
352
|
+
* or result content, and none ever should: these events are built to be logged.
|
|
353
|
+
*/
|
|
354
|
+
interface SearchRuntimeEventBase {
|
|
355
|
+
/** Correlates every event of one search. Random per search. */
|
|
356
|
+
requestId: string;
|
|
357
|
+
/** Short one-way hash of the query, computed by the runtime. */
|
|
358
|
+
queryHash: string;
|
|
359
|
+
/** Wall-clock duration in milliseconds. */
|
|
360
|
+
durationMs: number;
|
|
361
|
+
}
|
|
362
|
+
/** One provider attempt, as observed. */
|
|
363
|
+
interface SearchAttemptEvent extends SearchRuntimeEventBase {
|
|
364
|
+
type: 'search_attempt';
|
|
365
|
+
/** The provider attempted, or the one a policy skipped. */
|
|
366
|
+
providerId: SearchProviderId;
|
|
367
|
+
/** How the attempt ended. */
|
|
368
|
+
outcome: SearchAttemptOutcome;
|
|
369
|
+
/** Taxonomy code for a `failed` attempt. */
|
|
370
|
+
errorCode?: SearchErrorCode;
|
|
371
|
+
/** Results the attempt produced. */
|
|
372
|
+
resultCount?: number;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* The single terminal event of a search, emitted for success and failure alike.
|
|
376
|
+
*
|
|
377
|
+
* `providerId` is present exactly when a provider produced the response — its
|
|
378
|
+
* absence means no provider succeeded, which is what separates a failed search
|
|
379
|
+
* from a provider that authoritatively found nothing (present id, zero results).
|
|
380
|
+
*/
|
|
381
|
+
interface SearchCompleteEvent extends SearchRuntimeEventBase {
|
|
382
|
+
type: 'search_complete';
|
|
383
|
+
/** The provider that produced the response, when one did. */
|
|
384
|
+
providerId?: SearchProviderId;
|
|
385
|
+
/** Results returned to the caller; `0` for a failed search. */
|
|
386
|
+
resultCount: number;
|
|
387
|
+
/** Attempts beyond the first. */
|
|
388
|
+
fallbackCount: number;
|
|
389
|
+
}
|
|
390
|
+
/** Everything a runtime observability listener can receive. */
|
|
391
|
+
type SearchRuntimeEvent = SearchAttemptEvent | SearchCompleteEvent;
|
|
392
|
+
/**
|
|
393
|
+
* Provider health as reported by doctor/diagnostics surfaces.
|
|
394
|
+
*
|
|
395
|
+
* `blocked` means an egress or policy decision stopped the provider — the
|
|
396
|
+
* status that keeps `blocked` out of {@link SearchErrorCode}.
|
|
397
|
+
*/
|
|
398
|
+
type SearchProviderHealthStatus = 'healthy' | 'degraded' | 'unconfigured' | 'blocked' | 'failed';
|
|
399
|
+
/**
|
|
400
|
+
* One provider's health.
|
|
401
|
+
*
|
|
402
|
+
* This type has no field for an API key, cookie, token, or raw request or
|
|
403
|
+
* response header, and must never gain one. `reason` and any
|
|
404
|
+
* `error.details` values MUST be pre-sanitized by the producer before they
|
|
405
|
+
* reach this shape — diagnostics are displayed and logged.
|
|
406
|
+
*/
|
|
407
|
+
interface SearchProviderDiagnostic {
|
|
408
|
+
/** The provider this diagnostic describes. */
|
|
409
|
+
providerId: SearchProviderId;
|
|
410
|
+
/** Current status. */
|
|
411
|
+
status: SearchProviderHealthStatus;
|
|
412
|
+
/** ISO-8601 timestamp of the check. */
|
|
413
|
+
checkedAt?: string;
|
|
414
|
+
/** Short, pre-sanitized explanation of a non-healthy status. */
|
|
415
|
+
reason?: string;
|
|
416
|
+
/** The failure behind a `failed`/`degraded` status. */
|
|
417
|
+
error?: SearchErrorShape;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export { type KnownSearchProviderId, type OrchestratedSearchResponse, type SearchAttempt, type SearchAttemptEvent, type SearchAttemptOutcome, type SearchCompleteEvent, type SearchContributionContext, type SearchErrorCode, type SearchErrorShape, type SearchOptions, type SearchPolicy, type SearchProvider, type SearchProviderCapabilities, type SearchProviderContribution, type SearchProviderDescriptor, type SearchProviderDiagnostic, SearchProviderError, type SearchProviderErrorInit, type SearchProviderHealthStatus, type SearchProviderId, type SearchProviderSource, type SearchRequest, type SearchResponse, type SearchResult, type SearchRuntimeEvent, type SearchRuntimeEventBase, type SearchTimeRange, type SearchTransportKind, type SearchUrlReadResult, isKnownSearchProviderId, isSearchProviderError, toSearchErrorShape };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// src/search-types.ts
|
|
2
|
+
var KNOWN_SEARCH_PROVIDER_IDS = /* @__PURE__ */ new Set([
|
|
3
|
+
"http-bing",
|
|
4
|
+
"http-duckduckgo",
|
|
5
|
+
"tavily",
|
|
6
|
+
"jina",
|
|
7
|
+
"searxng",
|
|
8
|
+
"zhipu",
|
|
9
|
+
"z.ai"
|
|
10
|
+
]);
|
|
11
|
+
function isKnownSearchProviderId(id) {
|
|
12
|
+
return KNOWN_SEARCH_PROVIDER_IDS.has(id);
|
|
13
|
+
}
|
|
14
|
+
var SEARCH_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
15
|
+
"config_missing",
|
|
16
|
+
"auth_failed",
|
|
17
|
+
"rate_limited",
|
|
18
|
+
"timeout",
|
|
19
|
+
"upstream_unavailable",
|
|
20
|
+
"parse_failed",
|
|
21
|
+
"cancelled",
|
|
22
|
+
"policy_denied"
|
|
23
|
+
]);
|
|
24
|
+
var DEFAULT_SEARCH_ERROR_CODE = "upstream_unavailable";
|
|
25
|
+
var SearchProviderError = class _SearchProviderError extends Error {
|
|
26
|
+
/** Stable taxonomy code. */
|
|
27
|
+
code;
|
|
28
|
+
/** The provider that failed, when known. */
|
|
29
|
+
providerId;
|
|
30
|
+
/** Whether retrying the same provider could plausibly succeed. */
|
|
31
|
+
retryable;
|
|
32
|
+
/** Pre-sanitized string-valued context. */
|
|
33
|
+
details;
|
|
34
|
+
constructor(code, message, init = {}) {
|
|
35
|
+
super(message, init.cause === void 0 ? void 0 : { cause: init.cause });
|
|
36
|
+
this.name = "SearchProviderError";
|
|
37
|
+
this.code = code;
|
|
38
|
+
this.providerId = init.providerId;
|
|
39
|
+
this.retryable = init.retryable;
|
|
40
|
+
this.details = init.details;
|
|
41
|
+
Object.setPrototypeOf(this, _SearchProviderError.prototype);
|
|
42
|
+
}
|
|
43
|
+
/** This error as its serializable {@link SearchErrorShape}. */
|
|
44
|
+
toShape() {
|
|
45
|
+
return toSearchErrorShape(this);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
function isSearchProviderError(value) {
|
|
49
|
+
if (value instanceof SearchProviderError) return true;
|
|
50
|
+
if (!(value instanceof Error)) return false;
|
|
51
|
+
const code = value.code;
|
|
52
|
+
return typeof code === "string" && SEARCH_ERROR_CODES.has(code);
|
|
53
|
+
}
|
|
54
|
+
function toSearchErrorShape(value) {
|
|
55
|
+
if (isSearchProviderError(value)) {
|
|
56
|
+
const shape = { code: value.code, message: value.message };
|
|
57
|
+
if (value.providerId !== void 0) shape.providerId = value.providerId;
|
|
58
|
+
if (value.retryable !== void 0) shape.retryable = value.retryable;
|
|
59
|
+
if (value.details !== void 0) shape.details = value.details;
|
|
60
|
+
return shape;
|
|
61
|
+
}
|
|
62
|
+
return { code: DEFAULT_SEARCH_ERROR_CODE, message: describeUnknownError(value) };
|
|
63
|
+
}
|
|
64
|
+
function describeUnknownError(value) {
|
|
65
|
+
if (typeof value === "string") return value;
|
|
66
|
+
if (value instanceof Error && value.message) return value.message;
|
|
67
|
+
return "Unknown error";
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
SearchProviderError,
|
|
71
|
+
isKnownSearchProviderId,
|
|
72
|
+
isSearchProviderError,
|
|
73
|
+
toSearchErrorShape
|
|
74
|
+
};
|
|
@@ -68,9 +68,27 @@ interface JinaReaderResponse {
|
|
|
68
68
|
/** Error message if failed */
|
|
69
69
|
error?: string;
|
|
70
70
|
}
|
|
71
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* Check if provider is API type
|
|
73
|
+
*
|
|
74
|
+
* @deprecated Classifying a provider from its id's spelling is not supported.
|
|
75
|
+
* Declare the transport explicitly instead: `SearchProviderContribution.kind`
|
|
76
|
+
* (`'api' | 'http' | 'local-browser' | 'native'`) and
|
|
77
|
+
* `SearchProviderContribution.source` in `@omnicross/contracts/search-types`.
|
|
78
|
+
* This helper has no callers and is kept only so the exported symbol set stays
|
|
79
|
+
* byte-compatible for downstream re-export shims.
|
|
80
|
+
*/
|
|
72
81
|
declare function isApiProvider(id: WebSearchProviderId): boolean;
|
|
73
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Check if provider is local type
|
|
84
|
+
*
|
|
85
|
+
* @deprecated Classifying a provider from its id's spelling is not supported.
|
|
86
|
+
* Declare the origin explicitly instead: `SearchProviderContribution.source`
|
|
87
|
+
* (`'builtin' | 'host'`) and `SearchProviderContribution.kind` in
|
|
88
|
+
* `@omnicross/contracts/search-types`. This helper has no callers and is kept
|
|
89
|
+
* only so the exported symbol set stays byte-compatible for downstream
|
|
90
|
+
* re-export shims.
|
|
91
|
+
*/
|
|
74
92
|
declare function isLocalProvider(id: WebSearchProviderId): boolean;
|
|
75
93
|
|
|
76
94
|
export { type JinaReaderResponse, type WebSearchOptions, type WebSearchProviderConfig, type WebSearchProviderId, type WebSearchProviderType, type WebSearchResponse, type WebSearchResult, isApiProvider, isLocalProvider };
|
|
@@ -68,9 +68,27 @@ interface JinaReaderResponse {
|
|
|
68
68
|
/** Error message if failed */
|
|
69
69
|
error?: string;
|
|
70
70
|
}
|
|
71
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* Check if provider is API type
|
|
73
|
+
*
|
|
74
|
+
* @deprecated Classifying a provider from its id's spelling is not supported.
|
|
75
|
+
* Declare the transport explicitly instead: `SearchProviderContribution.kind`
|
|
76
|
+
* (`'api' | 'http' | 'local-browser' | 'native'`) and
|
|
77
|
+
* `SearchProviderContribution.source` in `@omnicross/contracts/search-types`.
|
|
78
|
+
* This helper has no callers and is kept only so the exported symbol set stays
|
|
79
|
+
* byte-compatible for downstream re-export shims.
|
|
80
|
+
*/
|
|
72
81
|
declare function isApiProvider(id: WebSearchProviderId): boolean;
|
|
73
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Check if provider is local type
|
|
84
|
+
*
|
|
85
|
+
* @deprecated Classifying a provider from its id's spelling is not supported.
|
|
86
|
+
* Declare the origin explicitly instead: `SearchProviderContribution.source`
|
|
87
|
+
* (`'builtin' | 'host'`) and `SearchProviderContribution.kind` in
|
|
88
|
+
* `@omnicross/contracts/search-types`. This helper has no callers and is kept
|
|
89
|
+
* only so the exported symbol set stays byte-compatible for downstream
|
|
90
|
+
* re-export shims.
|
|
91
|
+
*/
|
|
74
92
|
declare function isLocalProvider(id: WebSearchProviderId): boolean;
|
|
75
93
|
|
|
76
94
|
export { type JinaReaderResponse, type WebSearchOptions, type WebSearchProviderConfig, type WebSearchProviderId, type WebSearchProviderType, type WebSearchResponse, type WebSearchResult, isApiProvider, isLocalProvider };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnicross/contracts",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Dependency-light, host-agnostic contract types + runtime-value helpers shared by the @omnicross/* packages.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Sayo (https://github.com/Dumoedss)",
|
|
@@ -105,6 +105,16 @@
|
|
|
105
105
|
"import": "./dist/provider-presets/index.js",
|
|
106
106
|
"require": "./dist/provider-presets/index.cjs"
|
|
107
107
|
},
|
|
108
|
+
"./search-compat": {
|
|
109
|
+
"types": "./dist/search-compat.d.ts",
|
|
110
|
+
"import": "./dist/search-compat.js",
|
|
111
|
+
"require": "./dist/search-compat.cjs"
|
|
112
|
+
},
|
|
113
|
+
"./search-types": {
|
|
114
|
+
"types": "./dist/search-types.d.ts",
|
|
115
|
+
"import": "./dist/search-types.js",
|
|
116
|
+
"require": "./dist/search-types.cjs"
|
|
117
|
+
},
|
|
108
118
|
"./subscription-model-catalog": {
|
|
109
119
|
"types": "./dist/subscription-model-catalog.d.ts",
|
|
110
120
|
"import": "./dist/subscription-model-catalog.js",
|
|
@@ -152,7 +162,8 @@
|
|
|
152
162
|
"README.md"
|
|
153
163
|
],
|
|
154
164
|
"scripts": {
|
|
155
|
-
"build": "tsup",
|
|
165
|
+
"build": "tsup && node scripts/check-exports.mjs",
|
|
166
|
+
"check:exports": "node scripts/check-exports.mjs",
|
|
156
167
|
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit"
|
|
157
168
|
},
|
|
158
169
|
"dependencies": {
|