/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */
/**
 * Read-only OAL listing — `GET /runtime/oal/*` introduced by SWIP-13.
 *
 * The actual implementation on the `swip-13-dsl-debugger` branch ships a
 * **per-source-dispatcher** listing, not a per-rule one — the OAL debugger
 * targets one source (e.g. `Endpoint`) and every metric routed off that
 * source captures together. So the listing is shaped to match: one row per
 * dispatcher, with the dispatcher's full metric set inline.
 *
 * File metadata is also intentionally minimal — `/files` returns just file
 * names; `/files/{name}` returns the raw `.oal` text as `text/plain`.
 *
 * Read-only by design. OAL hot-update is upstream-deferred; the path
 * prefix `/runtime/oal/*` is reserved for future write endpoints.
 */
import { RuntimeRuleApiError } from './types.js';
/**
 * Typed wrapper for the four read-only OAL endpoints.
 */
export class OalClient {
    fetchImpl;
    base;
    defaultHeaders;
    timeoutMs;
    constructor(options) {
        this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
        this.base = options.adminUrl.replace(/\/$/, '');
        this.defaultHeaders = options.headers ?? {};
        this.timeoutMs = options.timeoutMs ?? 0;
    }
    /** `GET /runtime/oal/files` — bare file-name listing. */
    async listFiles() {
        const url = `${this.base}/runtime/oal/files`;
        const res = await this.send(url, { method: 'GET' });
        if (!res.ok)
            throw await this.toError(res, url);
        return (await res.json());
    }
    /** `GET /runtime/oal/files/{name}` — returns raw `.oal` text
     *  (`text/plain`). Returns `null` when the file isn't loaded. */
    async getFileContent(name) {
        const url = `${this.base}/runtime/oal/files/${encodeURIComponent(name)}`;
        const res = await this.send(url, { method: 'GET' });
        if (res.status === 404)
            return null;
        if (!res.ok)
            throw await this.toError(res, url);
        return await res.text();
    }
    /** `GET /runtime/oal/rules` — per-dispatcher listing for the picker. */
    async listSources() {
        const url = `${this.base}/runtime/oal/rules`;
        const res = await this.send(url, { method: 'GET' });
        if (!res.ok)
            throw await this.toError(res, url);
        return (await res.json());
    }
    /** `GET /runtime/oal/rules/{source}` — single-source detail with
     *  holder-bound status. Path param is the source class name. Returns
     *  `null` when no dispatcher owns that source. */
    async getSource(source) {
        const url = `${this.base}/runtime/oal/rules/${encodeURIComponent(source)}`;
        const res = await this.send(url, { method: 'GET' });
        if (res.status === 404)
            return null;
        if (!res.ok)
            throw await this.toError(res, url);
        return (await res.json());
    }
    // ── private helpers ─────────────────────────────────────────────
    async send(url, init) {
        const headers = {
            Accept: 'application/json, text/plain',
            ...this.defaultHeaders,
            ...(init.headers ?? {}),
        };
        const finalInit = { ...init, headers };
        if (this.timeoutMs > 0) {
            const controller = new AbortController();
            const timer = setTimeout(() => controller.abort(), this.timeoutMs);
            try {
                return await this.fetchImpl(url, { ...finalInit, signal: controller.signal });
            }
            finally {
                clearTimeout(timer);
            }
        }
        return this.fetchImpl(url, finalInit);
    }
    async toError(res, url) {
        const text = await res.text();
        let parsed = text;
        try {
            const json = JSON.parse(text);
            if (typeof json.applyStatus === 'string' && typeof json.message === 'string') {
                parsed = json;
            }
            else if (json.status === 'error' &&
                typeof json.code === 'string' &&
                typeof json.message === 'string') {
                parsed = json;
            }
        }
        catch {
            // not JSON; keep the raw text.
        }
        return new RuntimeRuleApiError(res.status, parsed, url);
    }
}
//# sourceMappingURL=oal.js.map