UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

133 lines • 5.06 kB
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= import { apiRequest, buildUrl } from "../api/client.js"; import { ApiError } from "../api/errors.js"; const TRACE_ID_RE = /^[0-9a-f]{16}$|^[0-9a-f]{32}$/i; // Jaeger's own query service self-instruments, so a `jaeger` service shows up // in listings alongside the ones a user actually sent. export class JaegerApiError extends ApiError { constructor(message, status = null) { super(message, status); this.name = 'JaegerApiError'; } } /** * Report whether a name is a syntactically valid Jaeger trace id. * * Checked before calling the API so a malformed id becomes ENOENT instead of * the API's 400 "invalid length for TraceID". */ export function isTraceId(value) { return TRACE_ID_RE.test(value); } // The parse comes before the status check, so a non-JSON error body reports // as invalid JSON rather than a bare status, matching the old transport. function errorOf(response, text) { let body; try { body = JSON.parse(text); } catch (err) { const msg = err instanceof Error ? err.message : String(err); return new JaegerApiError(`Jaeger API: invalid JSON: ${msg}`, response.status); } let message = `Jaeger API error: HTTP ${String(response.status)}`; if (body !== null && typeof body === 'object') { const errors = body.errors; if (Array.isArray(errors) && errors.length > 0) { const first = errors[0]; const msg = first?.msg; if (typeof msg === 'string' && msg !== '') message = msg; } } return new JaegerApiError(message, response.status); } export class HttpJaegerTransport { fetch = globalThis.fetch.bind(globalThis); host; timeout; constructor(opts = {}) { this.host = opts.host ?? 'http://localhost:16686'; this.timeout = opts.timeout; } async request(path, query = {}) { const text = (await apiRequest('GET', buildUrl(this.host, path, query), { fetchFn: this.fetch, headers: { Accept: 'application/json' }, timeout: this.timeout, read: 'text', errorOf: errorOf, })); try { return JSON.parse(text); } catch (err) { const msg = err instanceof Error ? err.message : String(err); throw new JaegerApiError(`Jaeger API: invalid JSON: ${msg}`); } } } function dataList(body) { if (body === null || typeof body !== 'object') return []; const data = body.data; return Array.isArray(data) ? data : []; } function micros(iso, fallback) { if (iso === undefined || iso === '') return fallback; const parsed = Date.parse(iso); if (Number.isNaN(parsed)) return fallback; return parsed * 1000; } export async function fetchServices(transport) { const body = await transport.request('/api/services'); return dataList(body).map((name) => String(name)); } /** * List operations recorded for a service. * * An unknown service yields an empty list rather than an error, so callers * that need existence semantics must check the service list first. */ export async function fetchOperations(transport, service) { const body = await transport.request('/api/operations', { service }); return dataList(body).filter((row) => row !== null && typeof row === 'object'); } /** * Search traces for a service within an explicit time window. * * `service` is required by the API and `lookback` is ignored, so the window is * always sent as explicit microsecond bounds. */ export async function fetchTraces(transport, service, opts = {}) { const body = await transport.request('/api/traces', { service, limit: opts.limit ?? 100, start: micros(opts.fromTimestamp, 0), end: micros(opts.toTimestamp, Date.now() * 1000), }); return dataList(body).filter((row) => row !== null && typeof row === 'object'); } export async function fetchTrace(transport, traceId) { const body = await transport.request(`/api/traces/${encodeURIComponent(traceId)}`); const traces = dataList(body).filter((row) => row !== null && typeof row === 'object'); const first = traces[0]; if (first === undefined) throw new JaegerApiError('trace not found', 404); return first; } //# sourceMappingURL=client.js.map