Skip to main content
Glama
Formly Agent Contracts

Formly Agent Contracts

by dills122

Formly Contract

Formly Contract は、Angular Formly のフィールド設定を、E2E テスト作成者やコーディングエージェントがフォームの構造を推測することなく理解できる、安定したバージョン管理された JSON に変換します。

FormlyFieldConfig[] が与えられると、アダプターは以下を記述します:

  • フォーム内のコントロール、表示コンテンツ、グループ、繰り返し可能なテンプレート

  • 各フィールドのモデルパス、Formly タイプ、ラベル、制約、選択肢

  • 既知の可視性、必須、読み取り専用、無効、動的オプションの動作

  • data-testid、data-test-id、data-cy などの正確な、またはアプリケーション由来のテストロケーター

  • 設定から直接得られたもの、制御された Formly ビルドで解決されたもの、まだ不明なもの

  • 安全に表現できない動作に対する安定した診断

結果は、厳密なランタイム検証、正規のシリアライゼーション、コンテンツハッシュを備えた決定論的な Form Contract です。このコントラクトは、Cypress/Playwright のテスト計画や将来のエージェントツールのための信頼できる入力となることを意図しています。Formly のライブランタイムオブジェクトのダンプではありません。

What exists today

このリポジトリは現在、スキーマ v0.3 と 2 つのワークスペースパッケージを提供しています:

Package

Purpose

@formly-contract/contract-schema

コントラクト DTO、ランタイム検証、正規 JSON、SHA-256 コンテンツハッシュ

@formly-contract/formly-adapter

Formly 6.1 向けの安全な宣言的抽出と信頼できるシナリオコンパイル

また、以下も含まれます:

  • 合成ゴールデンフォームを使用した決定論的 CLI デモ

  • 12 の合成 Formly フィクスチャを備えたブラウザレンダリング Angular テストアプリケーション

  • 固定された Angular 20.3.29 と Formly 6.1.8 の組み合わせに対する互換性カバレッジ

パーサーとコントラクトが現在の製品です。本番 MCP サーバー、自動 Playwright 生成、ブラウザ観測、アプリケーションソースの検出は将来のレイヤーであり、この MVP には含まれていません。

Related MCP server: dbt-assay

Use it in your own Angular/Formly codebase

このパッケージは、Angular アプリケーションの横でビルド/テストツールとして実行されます。アプリケーションのブラウザバンドルに追加する必要はありません。典型的な導入フローは次のとおりです:

application-owned Formly factories
              |
     generation script or CI job
              |
       versioned contract JSON
              |
 Playwright / Cypress / agent tooling

1. Add the packages

パッケージはまだ npm に公開されていません。最初のリリースまで、このリポジトリを利用側アプリケーションの隣にクローンし、2 つのパッケージをビルドしてください:

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 からリンクします(チェックアウトに合わせて相対パスを調整してください):

{
  "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. Select the forms to expose

アプリケーションソースの検出は意図的に自動化されていません。コントラクトジェネレーターに検査させたいフォームファクトリーのみをインポートする、アプリケーション所有の小さなレジストリを作成してください:

// 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. Generate contract artifacts

アプリケーションリポジトリにビルド時スクリプトを追加します:

// 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. Use a contract in Playwright

保存された JSON を信頼する前に検証し、必要なセマンティックノードを見つけ、その正確なロケーター候補の 1 つを使用します。標準の 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 ヘルパーに置きます。複合コントロールは複数のロケーターターゲットを公開できるため、ヘルパーは 1 つの Formly ノードが常に 1 つの DOM 要素にマップされると想定するのではなく、target で選択する必要があります。空のロケーター配列と診断は、欠落した証拠として処理する必要があり、発明されたセレクターで置き換えてはなりません。

5. Resolve dynamic behavior when needed

式が可視性、必須/読み取り専用状態、またはオプションリストを決定する場合は、合成シナリオを追加して compileFormContractScenario を呼び出します。その API を、アプリケーションの実際の Formly モジュールとカスタムタイプで構成された信頼できる Angular テスト/ビルド環境で実行します。意味のあるシナリオごとに 1 つのアーティファクトを生成し、合成モデルとフォーム状態データのみを使用します。

合成互換性ハーネス は、FormlyFormBuilder を取得するための完全な Angular TestBed セットアップを示しています。以下の詳細な API 例は、シナリオ呼び出しを示しています。

Why this is useful

大規模な Formly フォームは、ネストされたグループ、共有フラグメント、カスタムフィールドタイプ、式、動的選択肢、アプリケーションの規約から組み立てられることがよくあります。そのソースを繰り返し読むのは遅く、レンダリングされたページから推測すると脆いテストにつながります。

このプロジェクトは、小さく明確な境界を作成します:

Formly fields + synthetic scenario
                |
       safe contract projection
                |
   deterministic versioned JSON
                |
 E2E planning / agent inspection

コンシューマーは 1 つのコントラクトを検査して、次のような質問に答えることができます:

  • どのコントロールが存在し、どの順序か?

  • 各コントロールはどのモデル値を編集するか?

  • どの値と検証境界が既知か?

  • 選択リストは空、静的、動的、非同期のいずれか?

  • どのフィールドが非表示、必須、読み取り専用、無効になる可能性があるか?

  • どの data-*、role、label、placeholder、DOM-ID ロケーター候補が利用可能か?

  • どの事実が正確か、導出されたか、1 つのシナリオで解決されたか、まだ不明か?

Try this repository

前提条件:

  • Node.js 22.22.1

  • pnpm 10.23.0

pnpm install --frozen-lockfile
pnpm demo

pnpm demo はパッケージスライスをビルドし、1 つの正規 JSON コントラクトを出力します。完全なリポジトリゲートを実行するには:

pnpm check

そのコマンドは、lint、すべてのテスト、パッケージと Angular の本番ビルド、デモのスモークテスト、ドキュメントチェックを実行します。

Extract declared form structure

Formly 設定があり、コールバックを実行せずに検査したい場合は、extractFormContract を使用します:

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,
});

このパスは純粋で非変更的です。式関数を呼び出したり、Observable にサブスクライブしたり、バリデーターを実行したり、Angular コンポーネントをレンダリングしたりしません。認識されたコールバックは動的ルールメタデータになり、サポートされていない動作は明示的な診断になります。返されるノードは、安定した ID example.profile::path:s_profile.s_name、モデルパス ['profile', 'name']、必須制約、正確な data-testid ロケーターを持ちます。

Resolve a synthetic scenario

必須、読み取り専用、無効、非表示、オプション、またはロケーター属性が Formly 式コールバックに依存する場合は、compileFormContractScenario を使用します:

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 },
});

これは信頼できるビルド/CI API です。アプリケーションの構成済み FormlyFormBuilder を使用するため、アプリケーションと Formly のコールバックが実行される可能性があります。モデルとフォーム状態は構造化クローン可能である必要があります。フィールドファクトリーまたはビルダーが実行される前に、両方がクローンされます。

ビルドされたフィールドツリーは、宣言された抽出と同じ許可リストを通過します。たとえば、動的オプションは、アプリケーションオブジェクトから任意のプロパティをコピーするのではなく、公開された label/value/disabled レコードに縮小されます。

このコンパイラーを MCP やその他の信頼できないリクエストハンドラーから直接公開しないでください。クエリレイヤーは、以前に生成されたコントラクトアーティファクトを読み取る必要があります。

Test locators

すべてのノードには順序付けられた locators 配列があります。アダプターは props.attributes からこれらの一般的な属性を自動的に読み取ります:

  • data-testid

  • data-test-id

  • data-test

  • data-cy

  • data-pw

また、明示的な role、アクセシブルネーム、プレースホルダー、Formly フィールド ID の候補を保持することもできます。空の配列は信頼できるロケーターが見つからなかったことを意味します。アダプターは CSS や XPath を発明することはありません。

独自の命名規則を持つアプリケーションは、testIdAttributes を設定し、決定論的な deriveLocators コールバックを提供できます。コールバックは、ライブの Formly フィールドではなく、凍結された ID データのみを受け取ります。日付範囲などの複合ウィジェットに対して複数の名前付きターゲットを返すことができます。その出力は confidence: "derived" とマークされます。完全なコントラクトと例については、v0.3 ロケーター仕様 を参照してください。

Evidence model

コントラクトは 3 つの証拠レベルを分離して保持します:

Evidence

Meaning

Available now?

declared

提供された Formly 設定から安全に読み取る

はい

resolved

1 つの合成シナリオに対して制御された Formly ビルドから読み取る

はい

observed

実際にレンダリングされたブラウザ DOM で見られる

スキーマ対応; キャプチャレイヤーは未実装

解決されたロケーターは、ブラウザで観測されたものとして暗黙的に提示されることはありません。同様に、不透明または非同期の動作は推測ではなく報告されます。

Supported contract information

スキーマ v0.3 は以下を表現できます:

  • 順序付けられたコントロール、グループ、表示専用ノード、配列テンプレート

  • 安定したセマンティックノード ID と累積モデルパス

  • Formly および一般的なセマンティックコントロールタイプ

  • ラベル、説明、プレースホルダー、JSON 安全なデフォルト、ラッパー

  • 必須、最小/最大、長さ、文字列パターン、名前付き制約

  • 静的および解決された公開オプション、および動的/非同期オプションソースメタデータ

  • 文字列/ブール条件とコールバック/非同期動的ルールメタデータ

  • 解決された非表示、読み取り専用、無効状態

  • 複数の名前付きターゲットを含む、正確および導出されたロケーター候補

  • 決定論的な診断、正規 JSON、コンテンツハッシュ

Intentional limitations

  • フォームは明示的に提供する必要があります。アダプターは任意の TypeScript エクスポートやアプリケーションルートを検出しません。

  • 宣言された抽出は、関数や関数ソースを決して評価しません。

  • シナリオコンパイラーは初期の制御された Formly ビルドを実行しますが、リモートオプションやライフサイクル駆動のブラウザ動作を待機しません。

  • Formly の RegExp パターンは診断されます。v0.3 は文字列パターンのみを表現します。

  • カスタムウィジェットアクションとバリューコーデックはまだモデル化されていません。

  • このプロジェクトは現在、Cypress/Playwright テストを生成または実行しません。

  • 本番 MCP サーバーやブラウザ観測レイヤーは含まれていません。

  • 互換性は Angular 20.3.29 と Formly 6.1.8 の組み合わせで証明されており、すべての Angular/Formly の組み合わせではありません。

  • npm 公開とリリース自動化はまだ含まれていません。

Synthetic test application

Angular テストアプリケーションには、ネイティブおよびカスタムフィールド、ラッパー、バリデーター、拡張機能、プリセット、式、検証、リピーター、不透明な動作、レガシー Formly v6 エイリアスをカバーする 12 の発明されたフォームが含まれています。

pnpm app:serve

http://127.0.0.1:4200/ を開き、カタログからフィクスチャを選択します。

職場のフォームとデータは、プライベートな作業リポジトリに残す必要があります。プライベートフィクスチャモジュールは、TestFormDefinition を実装し、TEST_FORM_GROUPS を通じてグループを登録できます。職場のラベル、識別子、オプション、ルールをこの公開プロジェクトにコピーすることはありません。

Repository layout

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

Roadmap

意図された提供パスは次のとおりです:

Form Contract packages (current)
              |
      read-only MCP queries
              |
        typed E2E intent
              |
 deterministic Playwright/Cypress drivers
              |
 browser observation and parity checks

将来のレイヤーは不変のコントラクトを消費する必要があります。Angular の実行、任意のコールバック評価、セレクターの発明を日常的なエージェントリクエストに移すべきではありません。

Documentation

コントリビューションとセキュリティ

コントリビューションを歓迎します。参加する前にCONTRIBUTING.mdと 行動規範をお読みください。セキュリティの問題は SECURITY.mdに記載されている非公開のプロセスを通じて報告してください。

このプロジェクトはMITライセンスの下で提供されています。

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides AI coding agents with accurate OpenAPI contract details to prevent hallucinated API calls, supporting multi-version pinning, endpoint discovery, and request validation.
    42 npm
    Apache 2.0
  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables AI coding agents to query a live, type-aware, intent-aware cross-repo index of an organization's TypeScript services, answering semantic questions about functions, endpoints, types, and behavior across repositories. It also supports validation of cross-repo API mismatches and same-repo checks.
    7
    -
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables agents to discover offers, retrieve schemas, validate JSON blueprints, preview plugins, and compile deterministic plugin packages as portable artifacts without publishing or mutating third-party systems.
    -