Skip to main content
Glama
dills122

Formly Agent Contracts

by dills122

Formly Contract

Formly Contract превращает конфигурацию полей Angular Formly в стабильный, версионируемый JSON, который автор E2E-тестов или кодирующий агент может понять без необходимости угадывать структуру формы.

По заданному FormlyFieldConfig[] адаптер описывает:

  • элементы управления, отображаемый контент, группы и повторяемые шаблоны в форме;

  • путь модели каждого поля, тип Formly, метку, ограничения и варианты выбора;

  • известное поведение видимости, обязательности, только для чтения, отключённости и динамических опций;

  • точные или производные от приложения локаторы для тестирования, такие как data-testid, data-test-id и data-cy;

  • что пришло напрямую из конфигурации, что было разрешено контролируемой сборкой Formly, а что остаётся неизвестным; и

  • стабильную диагностику для поведения, которое невозможно безопасно представить.

Результатом является детерминированный Контракт формы со строгой проверкой во время выполнения, канонической сериализацией и хешем содержимого. Контракт предназначен для использования в качестве надёжного входного данных для планирования тестов Cypress/Playwright и будущих инструментов агентов. Это не дамп живых объектов времени выполнения Formly.

Что существует сегодня

В этом репозитории в настоящее время предоставляются схема v0.3 и два пакета рабочего пространства:

Пакет

Назначение

@formly-contract/contract-schema

DTO контракта, проверка во время выполнения, канонический JSON и хеширование SHA-256

@formly-contract/formly-adapter

Безопасное извлечение объявленного и доверенная компиляция сценариев для Formly 6.1

Также включает:

  • детерминированное демо CLI с использованием синтетической эталонной формы;

  • браузерное тестовое приложение Angular с двенадцатью синтетическими фикстурами Formly;

  • и покрытие совместимости для закреплённой комбинации Angular 20.3.29 и Formly 6.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 tooling

1. Добавьте пакеты

Пакеты ещё не опубликованы в 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.1

  • pnpm 10.23.0

pnpm install --frozen-lockfile
pnpm demo

pnpm 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-testid

  • data-test-id

  • data-test

  • data-cy

  • data-pw

Он также может сохранять явные роли, доступные имена, плейсхолдеры и кандидаты идентификаторов полей Formly. Пустой массив означает, что надёжный локатор не найден; адаптер никогда не изобретает CSS или XPath.

Приложения с собственным соглашением об именах могут установить testIdAttributes и предоставить детерминированный обратный вызов deriveLocators. Обратный вызов получает только замороженные идентификационные данные, а не живое поле Formly. Он может вернуть несколько именованных целей для составного виджета, такого как диапазон дат; его вывод помечается confidence: "derived". См. спецификацию локаторов v0.3 для полного контракта и примеров.

Модель доказательств

Контракт разделяет три уровня доказательств:

Доказательство

Значение

Доступно сейчас?

declared

Безопасно прочитано из предоставленной конфигурации Formly

Да

resolved

Прочитано из контролируемой сборки Formly для одного синтетического сценария

Да

observed

Увиденное в реальном отрендеренном браузерном DOM

Готово для схемы; слой захвата не реализован

Разрешённый локатор не молчаливо представляется как наблюдаемый в браузере. Аналогично, непрозрачное или асинхронное поведение сообщается, а не угадывается.

Поддерживаемая информация контракта

Схема v0.3 может представлять:

  • упорядоченные элементы управления, группы, узлы только для отображения и шаблоны массивов;

  • стабильные семантические идентификаторы узлов и кумулятивные пути моделей;

  • типы Formly и общие семантические типы элементов управления;

  • метки, описания, плейсхолдеры, безопасные для JSON значения по умолчанию и обёртки;

  • обязательность, min/max, длину, строковые шаблоны и именованные ограничения;

  • статические и разрешённые публичные опции, а также метаданные источников динамических/асинхронных опций;

  • строковые/логические условия и метаданные динамических правил обратных вызовов/асинхронных;

  • разрешённые состояния скрытости, только для чтения и отключённости;

  • точные и производные кандидаты локаторов, включая несколько именованных целей; и

  • детерминированную диагностику, канонический JSON и хеширование содержимого.

Преднамеренные ограничения

  • Формы должны быть явно предоставлены; адаптер не обнаруживает произвольные экспорты TypeScript или маршруты приложения.

  • Объявленное извлечение никогда не оценивает функции или исходный код функций.

  • Компилятор сценариев выполняет начальную контролируемую сборку Formly, но не ожидает удалённых опций или поведения, управляемого жизненным циклом браузера.

  • Шаблоны RegExp Formly диагностируются; v0.3 представляет только строковые шаблоны.

  • Пользовательские действия виджетов и кодеки значений ещё не моделируются.

  • Проект в настоящее время не генерирует и не выполняет тесты Cypress/Playwright.

  • Не включён продакшн-сервер MCP или слой браузерного наблюдения.

  • Совместимость доказана для Angular 20.3.29 с Formly 6.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.

Maintenance

ActivityMaintained
ResponsivenessResponsive

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

Related MCP Servers

Latest Blog Posts

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