Интеграции и тестирование
HTTP-адаптер (кастомный fetch / edge-окружения)
Замените встроенный клиент на axios любой HTTP-реализацией:
const fetchAdapter = {
async request(config) {
const url = `${config.baseURL ?? ''}${config.url ?? ''}`
const res = await fetch(url, {
method: config.method ?? 'GET',
body: config.data ? JSON.stringify(config.data) : undefined,
headers: { 'Content-Type': 'application/json', ...config.headers },
signal: config.signal,
})
const data = await res.json()
return {
data,
status: res.status,
statusText: res.statusText,
headers: Object.fromEntries(res.headers.entries()),
}
},
}
const client = createRestClient({
baseURL: 'https://api.example.com',
adapter: fetchAdapter,
// auth, interceptors, sanitizeHeaders, metrics по-прежнему работают поверх адаптера
auth: { getToken: async () => token },
})type HttpAdapter = {
request<T = unknown>(config: RestRequestConfig & { baseURL?: string }): Promise<ApiResponse<T>>
}Когда задан adapter, createRestClient() никогда не вызывает axios.create() — встроенный экземпляр axios просто не создаётся, так что использование только с адаптером (например, в Cloudflare Workers / Deno) не несёт за это накладных расходов.
Интеграция с Vue
<script setup>
import {
PipelineOrchestrator,
usePipelineProgressVue,
usePipelineRunVue,
} from 'rest-pipeline-js/vue'
const orchestrator = new PipelineOrchestrator({
config: {
stages: [/* ... */],
},
})
const progress = usePipelineProgressVue(orchestrator)
const { run, running, result, error, abort, pause, resume, rerunStep } =
usePipelineRunVue(orchestrator)
</script>
<template>
<div>
<div>Current stage: {{ progress.currentStage }}</div>
<button @click="run()" :disabled="running">Start</button>
<button @click="abort()" :disabled="!running">Abort</button>
<button @click="pause()">Pause</button>
<button @click="resume()">Resume</button>
<div v-if="result">Done: {{ result }}</div>
<div v-if="error">Error: {{ error.message }}</div>
</div>
</template>Composables (импорт из rest-pipeline-js/vue):
| Composable | Возвращает | Описание |
|---|---|---|
usePipelineProgressVue(orchestrator) | Ref<PipelineProgress> | Реактивный прогресс |
usePipelineRunVue(orchestrator) | { run, running, result, error, stageResults, abort, pause, resume, rerunStep, clearStageResults } | Запуск пайплайна и получение реактивного состояния |
usePipelineStepEventVue(orchestrator, stepKey, eventType) | Ref<any> | Последняя полезная нагрузка конкретного события шага |
usePipelineLogsVue(orchestrator) | Ref<log[]> | Реактивные логи |
useRerunPipelineStepVue(orchestrator) | function | Связанный rerunStep |
useRestClientVue(config) | ComputedRef<RestClient> | Реактивный REST-клиент |
usePipelineStageResultVue(orchestrator, stepKey) | Ref<PipelineStepResult | null> | Реактивный результат одного этапа |
Интеграция с React
import { useRef } from 'react'
import {
PipelineOrchestrator,
usePipelineProgressReact,
usePipelineRunReact,
} from 'rest-pipeline-js/react'
const orchestrator = new PipelineOrchestrator({
config: {
stages: [/* ... */],
},
})
export function PipelineComponent() {
const progress = usePipelineProgressReact(orchestrator)
const [run, { running, result, error, abort, pause, resume, rerunStep, clearStageResults }] =
usePipelineRunReact(orchestrator)
return (
<div>
<div>Current stage: {progress.currentStage}</div>
<button onClick={() => run()} disabled={running}>
Start
</button>
<button onClick={() => abort()} disabled={!running}>
Abort
</button>
<button onClick={() => pause()}>Pause</button>
<button onClick={() => resume()}>Resume</button>
{result && <div>Done: {JSON.stringify(result)}</div>}
{error && <div>Error: {error.message}</div>}
</div>
)
}Хуки (импорт из rest-pipeline-js/react):
| Хук | Возвращает | Описание |
|---|---|---|
usePipelineProgressReact(orchestrator) | PipelineProgress | Реактивный прогресс |
usePipelineRunReact(orchestrator) | [run, { running, result, error, stageResults, abort, pause, resume, rerunStep, clearStageResults }] | Запуск пайплайна и получение состояния |
usePipelineStepEventReact(orchestrator, stepKey, eventType) | any | Последняя полезная нагрузка конкретного события шага |
usePipelineLogsReact(orchestrator) | log[] | Реактивные логи |
useRerunPipelineStepReact(orchestrator) | function | Связанный rerunStep |
useRestClientReact(config) | RestClient | Мемоизированный REST-клиент — пересоздаётся при новой ссылке на объект config; мемоизируйте его сами (useMemo/useState/константа модуля), чтобы не пересоздавать на каждом рендере |
usePipelineStageResultReact(orchestrator, stepKey) | PipelineStepResult | null | Результат одного этапа |
Тестирование
createMockAdapter() (из отдельной точки входа rest-pipeline-js/testing, поэтому он никогда не попадает в продакшен-бандл) заменяет сеть набором определений маршрутов — для тестирования кода, использующего createRestClient() или PipelineOrchestrator, без обращения к реальному backend:
import { createRestClient } from 'rest-pipeline-js'
import { createMockAdapter } from 'rest-pipeline-js/testing'
const adapter = createMockAdapter([
{ method: 'GET', url: '/users/1', respond: { data: { id: 1, name: 'Ada' } } },
// Динамический ответ — читает запрос, чтобы построить ответ
{
method: 'POST',
url: '/orders',
respond: (info) => ({ data: { id: 42, ...(info.data as object) }, status: 201 }),
},
// Последовательность ответов, по одному на каждый совпадающий вызов — проверка логики повторов:
// первые две попытки проваливаются, третья успешна. После исчерпания повторяет последнюю запись.
{
method: 'GET',
url: '/flaky',
respond: [{ error: true, status: 503 }, { error: true, status: 503 }, { data: { ok: true } }],
},
])
const client = createRestClient({ baseURL: 'https://api.example.com', adapter })
const user = await client.get('/users/1')
// adapter.calls — история всех обработанных запросов по порядку; проверяйте её в тестах
expect(adapter.calls).toHaveLength(1)urlсовпадает по подстроке (string) либо через.test()(RegExp) с относительным URL (config.url, например/users/1), а не с полным URL. Опуститеmethod, чтобы совпадать с любым методом.- Ответ со
status >= 400по умолчанию отклоняется (как это делают axios/fetch), выбрасывается какErrorс заданными.statusи.response.status/.response.data/.response.headers— этого достаточно, чтобы работалиretry.retriableStatus,circuitBreakerи перехватчики ошибок. Переопределяется явнымerror: true/falseв спецификации ответа. delayMsна ответе имитирует сетевую задержку через настоящийsetTimeout(нормально работает подvi.useFakeTimers()/аналогами).- Отсутствие маршрута, подходящего под запрос, немедленно бросает исключение с понятным сообщением, вместо зависания — тест с недостающей настройкой маршрута проваливается явно.
adapter.reset()очищаетcallsи перематывает любые массивные последовательностиrespondв начало, не трогая сами маршруты.
Более полный пример, включая использование с PipelineOrchestrator через httpConfig, см. в examples/mock-adapter.ts.