FTS в Node.js: CLI-утилита и HTTP boundary
На backend модель лучше компилировать при старте, а не на каждый запрос.
import { readFile } from "node:fs/promises"
import { compile, executeUtility, validate } from "@digitable/fts"
const source = await readFile(new URL("./discount.fts", import.meta.url), "utf8")
const document = compile(source)
const report = validate(document)
if (!report.valid) throw new Error(JSON.stringify(report.diagnostics))
export function calculateDiscount(input: Record<string, string | number | boolean | null>) {
return executeUtility(document, "Рассчитать скидку", input)
}
HTTP-обработчик
server.on("request", async (request, response) => {
const input = await readJson(request)
try {
const discount = calculateDiscount(input)
response.writeHead(200, { "content-type": "application/json" })
response.end(JSON.stringify({ discount }))
} catch (error) {
response.writeHead(422, { "content-type": "application/json" })
response.end(JSON.stringify({ error: String(error) }))
}
})
FTS не читает request, не пишет response и не знает про status code. Поэтому утилиту можно тестировать без поднятия сервера.
Сгенерированный вариант
Если runtime-интерпретация не нужна:
fts generate discount.fts --out src/generated
import { ftsUtilities } from "./generated/fts.utilities.js"
const calculate = ftsUtilities["Рассчитать скидку"]
const discount = calculate({ сумма: 20_000, "постоянный клиент": true })
Сгенерированный файл стоит считать build artifact: изменяйте .fts, затем
перегенерируйте и проверяйте drift в CI.
Ошибки, которых следует избежать
- Не компилируйте пользовательский FTS без модели доверия и лимитов.
- Не передавайте внутрь произвольные объекты с секретами: утилите нужны только объявленные скалярные поля.
- Не выполняйте платёж сразу по ответу агента; сначала получите проверенный результат или сертификат.
- Не создавайте две реализации правила — FTS и ручную TypeScript — без теста их эквивалентности.
Упражнение
Сделайте endpoint POST /discount. Загрузите модель один раз, верните 422 при
нарушении свойства и добавьте integration test только HTTP-границы. Саму
математику должен проверять fts test.