Formly Agent Contracts
Formly Contract
Formly Contract превращает конфигурацию полей Angular Formly в стабильный, версионируемый JSON, который автор E2E-тестов или кодирующий агент может понять без необходимости угадывать структуру формы.
По заданному FormlyFieldConfig[] адаптер описывает:
элементы управления, отображаемый контент, группы и повторяемые шаблоны в форме;
путь модели каждого поля, тип Formly, метку, ограничения и варианты выбора;
известное поведение видимости, обязательности, только для чтения, отключённости и динамических опций;
точные или производные от приложения локаторы для тестирования, такие как
data-testid,data-test-idиdata-cy;что пришло напрямую из конфигурации, что было разрешено контролируемой сборкой Formly, а что остаётся неизвестным; и
стабильную диагностику для поведения, которое невозможно безопасно представить.
Результатом является детерминированный Контракт формы со строгой проверкой во время выполнения, канонической сериализацией и хешем содержимого. Контракт предназначен для использования в качестве надёжного входного данных для планирования тестов Cypress/Playwright и будущих инструментов агентов. Это не дамп живых объектов времени выполнения Formly.
Что существует сегодня
В этом репозитории в настоящее время предоставляются схема v0.3 и два пакета рабочего пространства:
Пакет | Назначение |
| DTO контракта, проверка во время выполнения, канонический JSON и хеширование SHA-256 |
| Безопасное извлечение объявленного и доверенная компиляция сценариев для Formly 6.1 |
Также включает:
детерминированное демо CLI с использованием синтетической эталонной формы;
браузерное тестовое приложение Angular с двенадцатью синтетическими фикстурами Formly;
и покрытие совместимости для закреплённой комбинации Angular
20.3.29и Formly6.1.8.
Парсер и контракт являются текущим продуктом. Продакшн-сервер MCP, автоматическая генерация Playwright, браузерное наблюдение и обнаружение исходников приложения — это будущие слои и не поставляются в этой MVP.
Related MCP server: SpecBridge MCP
Использование в вашей собственной кодовой базе Angular/Formly
Пакет работает как инструмент сборки/тестирования рядом с вашим приложением Angular. Его не нужно добавлять в браузерный бандл приложения. Типичный процесс внедрения выглядит так:
application-owned Formly factories
|
generation script or CI job
|
versioned contract JSON
|
Playwright / Cypress / agent tooling1. Добавьте пакеты
Пакеты ещё не опубликованы в npm. До первого релиза клонируйте этот репозиторий рядом с потребляющим приложением и соберите два пакета:
git clone https://github.com/dills122/formly-contract.git
cd formly-contract
pnpm install --frozen-lockfile
pnpm --filter @formly-contract/contract-schema build
pnpm --filter @formly-contract/formly-adapter buildЗатем свяжите их из package.json потребляющего приложения (отрегулируйте
относительный путь для вашего checkout):
{
"devDependencies": {
"@formly-contract/contract-schema": "link:../formly-contract/packages/contract-schema",
"@formly-contract/formly-adapter": "link:../formly-contract/packages/formly-adapter"
}
}Запустите pnpm install в потребляющем приложении. Приложение уже должно
предоставлять совместимые одноранговые зависимости Angular и Formly; текущая
протестированная комбинация — Angular 20.3.29 с Formly 6.1.8. Как только пакеты
будут опубликованы, обычные версионированные зависимости pnpm add --save-dev заменят
эти локальные ссылки.
2. Выберите формы для отображения
Обнаружение исходников приложения намеренно не автоматическое. Создайте небольшой реестр, принадлежащий приложению, который импортирует только те фабрики форм, которые генератор контрактов должен проверять:
// tools/contract-forms.ts
import type { FormlyFieldConfig } from '@ngx-formly/core';
import { createClaimFields } from '../src/app/claims/claim.fields';
import { createCustomerFields } from '../src/app/customers/customer.fields';
export interface ContractFormTarget {
id: string;
createFields: () => FormlyFieldConfig[];
}
export const contractForms: ContractFormTarget[] = [
{ id: 'claims.create', createFields: () => createClaimFields() },
{ id: 'customers.edit', createFields: () => createCustomerFields() },
];Каждая фабрика должна возвращать свежее дерево полей. Если фабрике нужны входные данные приложения, оберните её в замыкание с синтетическими значениями, безопасными для использования в локальной разработке и CI.
3. Создайте артефакты контракта
Добавьте сценарий времени сборки в репозиторий приложения:
// tools/generate-form-contracts.ts
import { mkdir, writeFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import { canonicalStringify } from '@formly-contract/contract-schema';
import { extractFormContract } from '@formly-contract/formly-adapter';
import { contractForms } from './contract-forms';
const outputDirectory = resolve('artifacts/form-contracts');
await mkdir(outputDirectory, { recursive: true });
for (const target of contractForms) {
const { contract, diagnostics } = extractFormContract({
formId: target.id,
fields: target.createFields(),
});
await writeFile(
resolve(outputDirectory, `${target.id}.json`),
`${canonicalStringify(contract)}\n`,
);
console.log(
`${target.id}: ${contract.nodes.length} root nodes, ${diagnostics.length} diagnostics`,
);
}Запустите этот файл с помощью TypeScript-раннера, уже используемого потребляющим репозиторием, или скомпилируйте его как часть инструментального проекта, ориентированного на Node. Полученный JSON можно зафиксировать для проверки, загрузить как артефакт CI или прочитать инструментами для написания тестов. Поскольку он канонический и с хешем содержимого, неожиданное изменение контракта формы видно в системе контроля версий или CI.
Этот объявленный путь — лучшая отправная точка. Он фиксирует статическую структуру и записывает выражения обратных вызовов как динамические метаданные без выполнения произвольного кода приложения.
4. Использование контракта в Playwright
Проверьте сохранённый JSON перед доверием к нему, найдите нужный семантический узел и
используйте один из его точных кандидатов локатора. Для стандартного локатора data-testid:
import { readFile } from 'node:fs/promises';
import {
parseFormContract,
type ContractNode,
type ModelPathSegment,
} from '@formly-contract/contract-schema';
function findNodeByPath(
nodes: readonly ContractNode[],
modelPath: readonly ModelPathSegment[],
): ContractNode | undefined {
for (const node of nodes) {
if (
node.modelPath.length === modelPath.length &&
node.modelPath.every((segment, index) => segment === modelPath[index])
) {
return node;
}
const nested = findNodeByPath(
node.arrayTemplate
? [...node.children, node.arrayTemplate]
: node.children,
modelPath,
);
if (nested) return nested;
}
}
const contract = parseFormContract(
JSON.parse(
await readFile('artifacts/form-contracts/claims.create.json', 'utf8'),
),
);
const claimantName = findNodeByPath(contract.nodes, ['claimant', 'name']);
const testId = claimantName?.locators.find(
(locator) =>
locator.strategy === 'testId' && locator.attribute === 'data-testid',
);
if (!claimantName || !testId) {
throw new Error('claimant.name has no exact data-testid locator');
}
await page.getByTestId(testId.value).fill('Ada Lovelace');Реальные потребители обычно размещают рекурсивный поиск узлов и выбор локаторов в
общем помощнике Playwright или Cypress. Составные элементы управления могут предоставлять несколько
целей локаторов, поэтому помощники должны выбирать по target, а не предполагать, что один узел
Formly всегда соответствует одному элементу DOM. Пустые массивы локаторов и диагностика
должны обрабатываться как отсутствие доказательств, а не заменяться выдуманными селекторами.
5. Разрешение динамического поведения при необходимости
Если выражения определяют видимость, состояние обязательности/только для чтения или списки опций,
добавьте синтетические сценарии и вызовите compileFormContractScenario. Запускайте этот API в
доверенной среде тестирования/сборки Angular, настроенной с реальными модулями Formly
приложения и пользовательскими типами. Создавайте по одному артефакту на значимый сценарий,
используя только синтетические данные модели и состояния формы.
Синтетический харнесс совместимости
показывает полную настройку Angular TestBed для получения
FormlyFormBuilder. Подробный пример API ниже показывает вызов сценария.
Почему это полезно
Большие формы Formly часто собираются из вложенных групп, общих фрагментов, пользовательских типов полей, выражений, динамических вариантов и соглашений приложения. Чтение этого исходного кода многократно — медленно, а угадывание по отображаемой странице приводит к хрупким тестам.
Этот проект создаёт небольшую явную границу:
Formly fields + synthetic scenario
|
safe contract projection
|
deterministic versioned JSON
|
E2E planning / agent inspectionПотребители могут просмотреть один контракт, чтобы ответить на такие вопросы, как:
Какие элементы управления существуют и в каком порядке?
Какое значение модели редактирует каждый элемент управления?
Какие значения и границы проверки известны?
Является ли список вариантов пустым, статическим, динамическим или асинхронным?
Какие поля могут быть скрыты, обязательными, только для чтения или отключены?
Какие кандидаты локаторов
data-*, role, label, placeholder или DOM-ID доступны?Какие факты точны, производны, разрешены для одного сценария или ещё неизвестны?
Попробуйте этот репозиторий
Предварительные требования:
Node.js
22.22.1pnpm
10.23.0
pnpm install --frozen-lockfile
pnpm demopnpm demo собирает срез пакета и выводит один канонический JSON-контракт.
Запустите полный шлюз репозитория с помощью:
pnpm checkЭта команда запускает lint, все тесты, продакшн-сборки пакетов и Angular, демонстрационный смоук-тест и проверки документации.
Извлечение объявленной структуры формы
Используйте extractFormContract, когда у вас есть конфигурация Formly и вы хотите её
проверить без запуска обратных вызовов:
import { extractFormContract } from '@formly-contract/formly-adapter';
import type { FormlyFieldConfig } from '@ngx-formly/core';
const fields: FormlyFieldConfig[] = [
{
key: 'profile.name',
type: 'input',
props: {
label: 'Name',
required: true,
attributes: { 'data-testid': 'profile-name' },
},
},
];
const { contract, diagnostics } = extractFormContract({
formId: 'example.profile',
fields,
});Этот путь чистый и не мутирующий. Он не вызывает функции выражений, не подписывается
на Observables, не выполняет валидаторы и не рендерит компоненты Angular.
Распознанные обратные вызовы становятся метаданными динамических правил; неподдерживаемое поведение
превращается в явную диагностику. Возвращаемый узел имеет стабильный идентификатор
example.profile::path:s_profile.s_name, путь модели ['profile', 'name'], его
ограничение обязательности и точный локатор data-testid.
Разрешение синтетического сценария
Используйте compileFormContractScenario, когда обязательность, только для чтения, отключенность,
скрытость, опции или атрибуты локаторов зависят от выражений обратных вызовов Formly:
import { inject } from '@angular/core';
import { FormlyFormBuilder } from '@ngx-formly/core';
import { compileFormContractScenario } from '@formly-contract/formly-adapter';
const builder = inject(FormlyFormBuilder);
const { contract, diagnostics } = compileFormContractScenario({
formId: 'example.profile',
builder,
createFields: () => createProfileFields(),
model: { contactMethod: 'email' },
formState: { readonly: false },
});Это доверенный API для сборки/CI. Он использует сконфигурированный FormlyFormBuilder
приложения, поэтому обратные вызовы приложения и Formly могут выполняться. Модель и
состояние формы должны быть структурно клонируемыми; оба клонируются до запуска фабрики полей или билдера.
Построенное дерево полей по-прежнему проходит через тот же белый список, что и при объявленном
извлечении. Например, динамические опции сводятся к публичным записям
label/value/disabled, а не копируют произвольные свойства из объектов приложения.
Не предоставляйте этот компилятор напрямую из MCP или другого обработчика запросов без доверия. Слои запросов должны читать ранее созданные артефакты контракта.
Локаторы тестов
Каждый узел имеет упорядоченный массив locators. Адаптер автоматически считывает
эти общие атрибуты из props.attributes:
data-testiddata-test-iddata-testdata-cydata-pw
Он также может сохранять явные роли, доступные имена, плейсхолдеры и кандидаты идентификаторов полей Formly. Пустой массив означает, что надёжный локатор не найден; адаптер никогда не изобретает CSS или XPath.
Приложения с собственным соглашением об именах могут установить testIdAttributes и
предоставить детерминированный обратный вызов deriveLocators. Обратный вызов получает только
замороженные идентификационные данные, а не живое поле Formly. Он может вернуть несколько именованных
целей для составного виджета, такого как диапазон дат; его вывод помечается
confidence: "derived". См.
спецификацию локаторов v0.3 для полного
контракта и примеров.
Модель доказательств
Контракт разделяет три уровня доказательств:
Доказательство | Значение | Доступно сейчас? |
| Безопасно прочитано из предоставленной конфигурации Formly | Да |
| Прочитано из контролируемой сборки Formly для одного синтетического сценария | Да |
| Увиденное в реальном отрендеренном браузерном DOM | Готово для схемы; слой захвата не реализован |
Разрешённый локатор не молчаливо представляется как наблюдаемый в браузере. Аналогично, непрозрачное или асинхронное поведение сообщается, а не угадывается.
Поддерживаемая информация контракта
Схема v0.3 может представлять:
упорядоченные элементы управления, группы, узлы только для отображения и шаблоны массивов;
стабильные семантические идентификаторы узлов и кумулятивные пути моделей;
типы Formly и общие семантические типы элементов управления;
метки, описания, плейсхолдеры, безопасные для JSON значения по умолчанию и обёртки;
обязательность, min/max, длину, строковые шаблоны и именованные ограничения;
статические и разрешённые публичные опции, а также метаданные источников динамических/асинхронных опций;
строковые/логические условия и метаданные динамических правил обратных вызовов/асинхронных;
разрешённые состояния скрытости, только для чтения и отключённости;
точные и производные кандидаты локаторов, включая несколько именованных целей; и
детерминированную диагностику, канонический JSON и хеширование содержимого.
Преднамеренные ограничения
Формы должны быть явно предоставлены; адаптер не обнаруживает произвольные экспорты TypeScript или маршруты приложения.
Объявленное извлечение никогда не оценивает функции или исходный код функций.
Компилятор сценариев выполняет начальную контролируемую сборку Formly, но не ожидает удалённых опций или поведения, управляемого жизненным циклом браузера.
Шаблоны
RegExpFormly диагностируются; v0.3 представляет только строковые шаблоны.Пользовательские действия виджетов и кодеки значений ещё не моделируются.
Проект в настоящее время не генерирует и не выполняет тесты Cypress/Playwright.
Не включён продакшн-сервер MCP или слой браузерного наблюдения.
Совместимость доказана для Angular
20.3.29с Formly6.1.8, а не для каждой комбинации Angular/Formly.Публикация в npm и автоматизация релизов ещё не включены.
Синтетическое тестовое приложение
Тестовое приложение Angular содержит двенадцать вымышленных форм, покрывающих нативные и пользовательские поля, обёртки, валидаторы, расширения, пресеты, выражения, валидацию, повторители, непрозрачное поведение и устаревшие алиасы Formly v6.
pnpm app:serveОткройте http://127.0.0.1:4200/ и выберите фикстуру из каталога.
Рабочие формы и данные должны оставаться в приватном рабочем репозитории. Приватный
модуль фикстур может реализовать TestFormDefinition и зарегистрировать группу через
TEST_FORM_GROUPS без копирования рабочих меток, идентификаторов, опций или
правил в этот публичный проект.
Структура репозитория
packages/
contract-schema/ Versioned DTOs, validation, canonical JSON, and hashing
formly-adapter/ Declared extraction and trusted Formly scenario builds
fixtures/
synthetic-form/ Public golden form and real-builder compatibility fixture
apps/
demo-cli/ Prints the deterministic golden contract
formly-test-app/ Browser-rendered Angular/Formly fixture catalog
docs/ Specifications, ADRs, delivery plans, and evidenceДорожная карта
Предполагаемый путь поставки:
Form Contract packages (current)
|
read-only MCP queries
|
typed E2E intent
|
deterministic Playwright/Cypress drivers
|
browser observation and parity checksБудущие слои должны потреблять неизменяемые контракты. Они не должны переносить выполнение Angular, произвольную оценку обратных вызовов или изобретение селекторов в обычные запросы агента.
Участие и безопасность
Ваш вклад приветствуется. Перед участием прочитайте CONTRIBUTING.md и Кодекс поведения. О проблемах безопасности сообщайте через приватный процесс, описанный в SECURITY.md.
Этот проект доступен на условиях лицензии MIT.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Live React design-system APIs, patterns, and code validation so AI agents build real UI, not slop.
Machine-native capabilities with explicit contracts and machine-readable commerce.
Define, ship & query your analytics tracking from one source of truth, trusted by humans and agents.
API governance for AI agents. Detects breaking changes, scores blast radius, blocks unsafe calls.
Related MCP Servers
- AlicenseBqualityDmaintenanceExposes TypeScript Language Server Protocol functionality to AI agents, enabling them to query types at specific positions, find definitions and references, get diagnostics, run type tests, and type-check inline code just like in an IDE.91463MIT
- FlicenseAqualityDmaintenanceA clone-and-own MCP server that exposes OpenAPI/Huma contract intelligence to AI agents by turning API specifications into deterministic endpoint metadata, schemas, validation facts, and TypeScript declarations.6
- FlicenseAqualityDmaintenanceEnables AI agents to query component governance rules, validate component props, and generate development prompts for questionnaire editors.4
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to browse, read, compare, and validate OpenAPI contracts for providers and consumers.1MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/dills122/formly-contract'
If you have feedback or need assistance with the MCP directory API, please join our Discord server