From ffe6f5c53b63b1624182f413e8cd040cc8bc795c Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Tue, 2 Jun 2026 10:36:24 +0100 Subject: [PATCH 01/14] feat: support a2a in apidom (PROVCON-5343) --- README.md | 6 + package-lock.json | 62 +++ packages/apidom-ls/package.json | 3 + .../src/config/a2a/a2a1/completion.ts | 359 +++++++++++++++ .../src/config/a2a/a2a1/documentation.ts | 47 ++ .../config/a2a/a2a1/lint/allowed-fields.ts | 36 ++ .../a2a/a2a1/lint/capabilities--type.ts | 19 + .../a2a1/lint/default-input-modes--type.ts | 19 + .../a2a1/lint/default-output-modes--type.ts | 19 + .../config/a2a/a2a1/lint/description--type.ts | 19 + .../a2a/a2a1/lint/documentation-url--type.ts | 19 + .../config/a2a/a2a1/lint/icon-url--type.ts | 19 + .../src/config/a2a/a2a1/lint/index.ts | 37 ++ .../src/config/a2a/a2a1/lint/name--type.ts | 19 + .../config/a2a/a2a1/lint/provider--type.ts | 19 + .../a2a1/lint/security-requirements--type.ts | 19 + .../a2a/a2a1/lint/security-schemes--type.ts | 19 + .../config/a2a/a2a1/lint/signatures--type.ts | 19 + .../src/config/a2a/a2a1/lint/skills--type.ts | 19 + .../a2a1/lint/supported-interfaces--type.ts | 19 + .../src/config/a2a/a2a1/lint/url--type.ts | 19 + .../src/config/a2a/a2a1/lint/version--type.ts | 19 + .../apidom-ls/src/config/a2a/a2a1/meta.ts | 12 + .../a2a/agent-capabilities/completion.ts | 64 +++ .../agent-capabilities/lint/allowed-fields.ts | 18 + .../lint/extended-agent-card--type.ts | 19 + .../lint/extensions--type.ts | 19 + .../a2a/agent-capabilities/lint/index.ts | 15 + .../lint/push-notifications--type.ts | 19 + .../lint/streaming--type.ts | 19 + .../src/config/a2a/agent-capabilities/meta.ts | 10 + .../a2a/agent-card-signature/completion.ts | 41 ++ .../lint/allowed-fields.ts | 18 + .../agent-card-signature/lint/header--type.ts | 19 + .../a2a/agent-card-signature/lint/index.ts | 8 + .../lint/protected--type.ts | 19 + .../lint/signature--type.ts | 19 + .../config/a2a/agent-card-signature/meta.ts | 10 + .../config/a2a/agent-interface/completion.ts | 64 +++ .../agent-interface/lint/allowed-fields.ts | 18 + .../config/a2a/agent-interface/lint/index.ts | 9 + .../lint/protocol-binding--type.ts | 19 + .../lint/protocol-version--type.ts | 19 + .../a2a/agent-interface/lint/tenant--type.ts | 19 + .../a2a/agent-interface/lint/url--type.ts | 19 + .../src/config/a2a/agent-interface/meta.ts | 10 + .../config/a2a/agent-provider/completion.ts | 34 ++ .../a2a/agent-provider/lint/allowed-fields.ts | 18 + .../config/a2a/agent-provider/lint/index.ts | 7 + .../agent-provider/lint/organization--type.ts | 19 + .../a2a/agent-provider/lint/url--type.ts | 19 + .../src/config/a2a/agent-provider/meta.ts | 10 + .../src/config/a2a/agent-skill/completion.ts | 50 +++ .../a2a/agent-skill/lint/allowed-fields.ts | 29 ++ .../a2a/agent-skill/lint/description--type.ts | 19 + .../a2a/agent-skill/lint/examples--type.ts | 19 + .../config/a2a/agent-skill/lint/id--type.ts | 19 + .../src/config/a2a/agent-skill/lint/index.ts | 23 + .../a2a/agent-skill/lint/input-modes--type.ts | 19 + .../config/a2a/agent-skill/lint/name--type.ts | 19 + .../agent-skill/lint/output-modes--type.ts | 19 + .../lint/security-requirements--type.ts | 19 + .../config/a2a/agent-skill/lint/tags--type.ts | 19 + .../src/config/a2a/agent-skill/meta.ts | 10 + .../completion.ts | 47 ++ .../lint/allowed-fields.ts | 18 + .../lint/authorization-url--type.ts | 19 + .../lint/index.ts | 17 + .../lint/pkce-required--type.ts | 19 + .../lint/refresh-url--type.ts | 19 + .../lint/scopes--type.ts | 19 + .../lint/token-url--type.ts | 19 + .../a2a/authorization-code-oauth-flow/meta.ts | 10 + .../completion.ts | 37 ++ .../lint/allowed-fields.ts | 18 + .../lint/index.ts | 8 + .../lint/refresh-url--type.ts | 19 + .../lint/scopes--type.ts | 19 + .../lint/token-url--type.ts | 19 + .../a2a/client-credentials-oauth-flow/meta.ts | 10 + packages/apidom-ls/src/config/a2a/config.ts | 44 ++ .../a2a/device-code-oauth-flow/completion.ts | 42 ++ .../lint/allowed-fields.ts | 18 + .../lint/device-authorization-url--type.ts | 19 + .../a2a/device-code-oauth-flow/lint/index.ts | 15 + .../lint/refresh-url--type.ts | 19 + .../lint/scopes--type.ts | 19 + .../lint/token-url--type.ts | 19 + .../config/a2a/device-code-oauth-flow/meta.ts | 10 + .../a2a/implicit-oauth-flow/completion.ts | 41 ++ .../lint/allowed-fields.ts | 18 + .../lint/authorization-url--type.ts | 19 + .../a2a/implicit-oauth-flow/lint/index.ts | 8 + .../lint/refresh-url--type.ts | 19 + .../implicit-oauth-flow/lint/scopes--type.ts | 19 + .../config/a2a/implicit-oauth-flow/meta.ts | 10 + .../src/config/a2a/oauth-flows/completion.ts | 27 ++ .../a2a/oauth-flows/lint/allowed-fields.ts | 20 + .../lint/authorization-code--type.ts | 19 + .../lint/client-credentials--type.ts | 19 + .../a2a/oauth-flows/lint/device-code--type.ts | 19 + .../a2a/oauth-flows/lint/implicit--type.ts | 19 + .../src/config/a2a/oauth-flows/lint/index.ts | 17 + .../a2a/oauth-flows/lint/password--type.ts | 19 + .../src/config/a2a/oauth-flows/meta.ts | 10 + .../a2a/password-oauth-flow/completion.ts | 37 ++ .../lint/allowed-fields.ts | 18 + .../a2a/password-oauth-flow/lint/index.ts | 8 + .../lint/refresh-url--type.ts | 19 + .../password-oauth-flow/lint/scopes--type.ts | 19 + .../lint/token-url--type.ts | 19 + .../config/a2a/password-oauth-flow/meta.ts | 10 + .../config/a2a/security-scheme/completion.ts | 30 ++ .../security-scheme/lint/allowed-fields.ts | 26 ++ .../lint/api-key-security-scheme--type.ts | 19 + .../lint/http-auth-security-scheme--type.ts | 19 + .../config/a2a/security-scheme/lint/index.ts | 17 + .../lint/mtls-security-scheme--type.ts | 19 + .../lint/oauth2-security-scheme--type.ts | 19 + .../open-id-connect-security-scheme--type.ts | 19 + .../src/config/a2a/security-scheme/meta.ts | 10 + .../apidom-ls/src/config/a2a/target-specs.ts | 3 + packages/apidom-ls/src/config/codes.ts | 92 ++++ packages/apidom-ls/src/config/config.ts | 2 + packages/apidom-ls/src/parser-factory.ts | 6 + packages/apidom-ls/src/utils/utils.ts | 27 ++ packages/apidom-ls/test/a2a.ts | 55 +++ .../test/fixtures/a2a/agent-card-valid.json | 44 ++ packages/apidom-ns-a2a-1/.eslintignore | 9 + packages/apidom-ns-a2a-1/.gitignore | 7 + packages/apidom-ns-a2a-1/.mocharc.json | 7 + packages/apidom-ns-a2a-1/.npmrc | 2 + packages/apidom-ns-a2a-1/CHANGELOG.md | 10 + packages/apidom-ns-a2a-1/README.md | 60 +++ .../config/api-extractor/api-extractor.json | 4 + packages/apidom-ns-a2a-1/package.json | 61 +++ .../src/elements/APIKeySecurityScheme.ts | 38 ++ .../src/elements/AgentCapabilities.ts | 52 +++ .../apidom-ns-a2a-1/src/elements/AgentCard.ts | 146 +++++++ .../src/elements/AgentCardSignature.ts | 38 ++ .../src/elements/AgentExtension.ts | 52 +++ .../src/elements/AgentInterface.ts | 46 ++ .../src/elements/AgentProvider.ts | 30 ++ .../src/elements/AgentSkill.ts | 76 ++++ .../elements/AuthorizationCodeOAuthFlow.ts | 61 +++ .../elements/ClientCredentialsOAuthFlow.ts | 39 ++ .../src/elements/DeviceCodeOAuthFlow.ts | 47 ++ .../src/elements/HTTPAuthSecurityScheme.ts | 38 ++ .../src/elements/ImplicitOAuthFlow.ts | 41 ++ .../src/elements/MutualTlsSecurityScheme.ts | 22 + .../src/elements/OAuth2SecurityScheme.ts | 40 ++ .../src/elements/OAuthFlows.ts | 63 +++ .../elements/OpenIdConnectSecurityScheme.ts | 30 ++ .../src/elements/PasswordOAuthFlow.ts | 41 ++ .../src/elements/SecurityRequirement.ts | 25 ++ .../src/elements/SecurityScheme.ts | 68 +++ .../src/elements/StringList.ts | 26 ++ .../src/elements/nces/Extensions.ts | 15 + .../src/elements/nces/SecurityRequirements.ts | 15 + .../src/elements/nces/SecuritySchemes.ts | 15 + .../src/elements/nces/Signatures.ts | 15 + .../src/elements/nces/Skills.ts | 15 + .../src/elements/nces/SupportedInterfaces.ts | 15 + packages/apidom-ns-a2a-1/src/index.ts | 225 ++++++++++ packages/apidom-ns-a2a-1/src/media-types.ts | 44 ++ packages/apidom-ns-a2a-1/src/namespace.ts | 58 +++ packages/apidom-ns-a2a-1/src/predicates.ts | 408 ++++++++++++++++++ .../src/refractor/canonicalize.ts | 87 ++++ .../apidom-ns-a2a-1/src/refractor/index.ts | 57 +++ .../src/refractor/predicates.ts | 23 + .../src/refractor/registration.ts | 194 +++++++++ .../src/refractor/specification.ts | 253 +++++++++++ .../apidom-ns-a2a-1/src/refractor/toolbox.ts | 18 + .../src/refractor/visitors/FallbackVisitor.ts | 24 ++ .../visitors/SpecificationExtensionVisitor.ts | 21 + .../visitors/SpecificationVisitor.ts | 79 ++++ .../src/refractor/visitors/Visitor.ts | 39 ++ .../visitors/a2a-1/ExtensionsVisitor.ts | 40 ++ .../SecurityRequirementSchemesVisitor.ts | 31 ++ .../a2a-1/SecurityRequirementsVisitor.ts | 40 ++ .../visitors/a2a-1/SecuritySchemesVisitor.ts | 29 ++ .../visitors/a2a-1/SignaturesVisitor.ts | 40 ++ .../refractor/visitors/a2a-1/SkillsVisitor.ts | 38 ++ .../a2a-1/SupportedInterfacesVisitor.ts | 40 ++ .../a2a-1/agent-capabilities/index.ts | 33 ++ .../a2a-1/agent-card-signature/index.ts | 33 ++ .../visitors/a2a-1/agent-card/index.ts | 33 ++ .../visitors/a2a-1/agent-extension/index.ts | 33 ++ .../visitors/a2a-1/agent-interface/index.ts | 33 ++ .../visitors/a2a-1/agent-provider/index.ts | 33 ++ .../visitors/a2a-1/agent-skill/index.ts | 33 ++ .../a2a-1/api-key-security-scheme/index.ts | 33 ++ .../authorization-code-oauth-flow/index.ts | 35 ++ .../client-credentials-oauth-flow/index.ts | 35 ++ .../a2a-1/device-code-oauth-flow/index.ts | 33 ++ .../a2a-1/http-auth-security-scheme/index.ts | 33 ++ .../a2a-1/implicit-oauth-flow/index.ts | 33 ++ .../a2a-1/mutual-tls-security-scheme/index.ts | 33 ++ .../visitors/a2a-1/oauth-flows/index.ts | 33 ++ .../a2a-1/oauth2-security-scheme/index.ts | 33 ++ .../open-id-connect-security-scheme/index.ts | 35 ++ .../a2a-1/password-oauth-flow/index.ts | 33 ++ .../a2a-1/security-requirement/index.ts | 33 ++ .../visitors/a2a-1/security-scheme/index.ts | 33 ++ .../visitors/a2a-1/string-list/index.ts | 33 ++ .../visitors/generics/FixedFieldsVisitor.ts | 96 +++++ .../refractor/visitors/generics/MapVisitor.ts | 25 ++ .../generics/PatternedFieldsVisitor.ts | 95 ++++ .../apidom-ns-a2a-1/src/traversal/visitor.ts | 44 ++ packages/apidom-ns-a2a-1/test/.eslintrc | 57 +++ .../fixtures/real-world-planner-agent.json | 32 ++ .../apidom-ns-a2a-1/test/mocha-bootstrap.ts | 11 + packages/apidom-ns-a2a-1/test/predicates.ts | 166 +++++++ .../test/refractor/canonicalize.ts | 102 +++++ .../__snapshots__/index.ts.snap | 14 + .../elements/APIKeySecurityScheme/index.ts | 20 + .../__snapshots__/index.ts.snap | 17 + .../elements/AgentCapabilities/index.ts | 21 + .../AgentCard/__snapshots__/index.ts.snap | 120 ++++++ .../refractor/elements/AgentCard/index.ts | 65 +++ .../__snapshots__/index.ts.snap | 17 + .../elements/AgentCardSignature/index.ts | 20 + .../__snapshots__/index.ts.snap | 14 + .../elements/AgentExtension/index.ts | 20 + .../__snapshots__/index.ts.snap | 14 + .../elements/AgentInterface/index.ts | 20 + .../AgentProvider/__snapshots__/index.ts.snap | 11 + .../refractor/elements/AgentProvider/index.ts | 19 + .../AgentSkill/__snapshots__/index.ts.snap | 30 ++ .../refractor/elements/AgentSkill/index.ts | 24 ++ .../__snapshots__/index.ts.snap | 20 + .../AuthorizationCodeOAuthFlow/index.ts | 21 + .../__snapshots__/index.ts.snap | 14 + .../ClientCredentialsOAuthFlow/index.ts | 19 + .../__snapshots__/index.ts.snap | 14 + .../elements/DeviceCodeOAuthFlow/index.ts | 20 + .../__snapshots__/index.ts.snap | 14 + .../elements/HTTPAuthSecurityScheme/index.ts | 20 + .../__snapshots__/index.ts.snap | 11 + .../elements/ImplicitOAuthFlow/index.ts | 19 + .../__snapshots__/index.ts.snap | 8 + .../elements/MutualTlsSecurityScheme/index.ts | 18 + .../__snapshots__/index.ts.snap | 14 + .../elements/OAuth2SecurityScheme/index.ts | 20 + .../OAuthFlows/__snapshots__/index.ts.snap | 14 + .../refractor/elements/OAuthFlows/index.ts | 21 + .../__snapshots__/index.ts.snap | 11 + .../OpenIdConnectSecurityScheme/index.ts | 19 + .../__snapshots__/index.ts.snap | 11 + .../elements/PasswordOAuthFlow/index.ts | 19 + .../__snapshots__/index.ts.snap | 15 + .../elements/SecurityRequirement/index.ts | 18 + .../__snapshots__/index.ts.snap | 14 + .../elements/SecurityScheme/index.ts | 18 + .../StringList/__snapshots__/index.ts.snap | 10 + .../refractor/elements/StringList/index.ts | 16 + .../test/refractor/real-world.ts | 77 ++++ packages/apidom-ns-a2a-1/test/tsconfig.json | 10 + .../apidom-ns-a2a-1/tsconfig.declaration.json | 9 + packages/apidom-ns-a2a-1/tsconfig.json | 6 + packages/apidom-ns-a2a-1/vite.config.ts | 8 + .../.eslintignore | 8 + .../.gitignore | 7 + .../.mocharc.json | 7 + .../apidom-parser-adapter-a2a-json-1/.npmrc | 2 + .../CHANGELOG.md | 10 + .../README.md | 27 ++ .../config/api-extractor/api-extractor.json | 5 + .../package.json | 58 +++ .../src/adapter.ts | 60 +++ .../src/media-types.ts | 11 + .../test/.eslintrc | 55 +++ .../test/__snapshots__/adapter.ts.snap | 108 +++++ .../test/adapter.ts | 83 ++++ .../test/fixtures/sample-agent-card.json | 44 ++ .../test/media-types.ts | 33 ++ .../test/mocha-bootstrap.ts | 11 + .../test/tsconfig.json | 10 + .../tsconfig.declaration.json | 9 + .../tsconfig.json | 6 + .../vite.config.ts | 8 + .../.eslintignore | 8 + .../.gitignore | 7 + .../.mocharc.json | 7 + .../apidom-parser-adapter-a2a-yaml-1/.npmrc | 2 + .../CHANGELOG.md | 10 + .../README.md | 27 ++ .../config/api-extractor/api-extractor.json | 5 + .../package.json | 58 +++ .../src/adapter.ts | 64 +++ .../src/media-types.ts | 11 + .../test/.eslintrc | 55 +++ .../test/__snapshots__/adapter.ts.snap | 108 +++++ .../test/adapter.ts | 71 +++ .../test/fixtures/sample-agent-card.yaml | 38 ++ .../test/media-types.ts | 23 + .../test/mocha-bootstrap.ts | 11 + .../test/tsconfig.json | 10 + .../tsconfig.declaration.json | 9 + .../tsconfig.json | 6 + .../vite.config.ts | 8 + packages/apidom-reference/package.json | 16 + .../src/configuration/saturated.ts | 4 + .../src/parse/parsers/a2a-json-1/index.ts | 60 +++ .../src/parse/parsers/a2a-yaml-1/index.ts | 58 +++ .../fixtures/sample-agent-card.json | 44 ++ .../test/parse/parsers/a2a-json-1/index.ts | 60 +++ .../fixtures/sample-agent-card.yaml | 38 ++ .../test/parse/parsers/a2a-yaml-1/index.ts | 60 +++ 309 files changed, 9872 insertions(+) create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/capabilities--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/default-input-modes--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/default-output-modes--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/description--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/documentation-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/icon-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/name--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/provider--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/security-requirements--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/security-schemes--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/signatures--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/skills--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/supported-interfaces--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/version--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-capabilities/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-capabilities/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-capabilities/lint/extended-agent-card--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-capabilities/lint/extensions--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-capabilities/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-capabilities/lint/push-notifications--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-capabilities/lint/streaming--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-capabilities/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-card-signature/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-card-signature/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-card-signature/lint/header--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-card-signature/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-card-signature/lint/protected--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-card-signature/lint/signature--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-card-signature/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-interface/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-interface/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-interface/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-binding--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-version--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-interface/lint/tenant--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-interface/lint/url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-interface/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-provider/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-provider/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-provider/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-provider/lint/organization--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-provider/lint/url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-provider/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/description--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/examples--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/id--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/input-modes--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/name--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/output-modes--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/security-requirements--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/tags--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/authorization-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/pkce-required--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/refresh-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/scopes--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/token-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/refresh-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/scopes--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/token-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/config.ts create mode 100644 packages/apidom-ls/src/config/a2a/device-code-oauth-flow/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/device-authorization-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/refresh-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/scopes--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/token-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/device-code-oauth-flow/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/refresh-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/scopes--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth-flows/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth-flows/lint/authorization-code--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth-flows/lint/client-credentials--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth-flows/lint/device-code--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth-flows/lint/implicit--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth-flows/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth-flows/lint/password--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth-flows/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/refresh-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/scopes--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/security-scheme/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/security-scheme/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/security-scheme/lint/api-key-security-scheme--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/security-scheme/lint/http-auth-security-scheme--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/security-scheme/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/security-scheme/lint/mtls-security-scheme--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/security-scheme/lint/oauth2-security-scheme--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/security-scheme/lint/open-id-connect-security-scheme--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/security-scheme/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/target-specs.ts create mode 100644 packages/apidom-ls/test/a2a.ts create mode 100644 packages/apidom-ls/test/fixtures/a2a/agent-card-valid.json create mode 100644 packages/apidom-ns-a2a-1/.eslintignore create mode 100644 packages/apidom-ns-a2a-1/.gitignore create mode 100644 packages/apidom-ns-a2a-1/.mocharc.json create mode 100644 packages/apidom-ns-a2a-1/.npmrc create mode 100644 packages/apidom-ns-a2a-1/CHANGELOG.md create mode 100644 packages/apidom-ns-a2a-1/README.md create mode 100644 packages/apidom-ns-a2a-1/config/api-extractor/api-extractor.json create mode 100644 packages/apidom-ns-a2a-1/package.json create mode 100644 packages/apidom-ns-a2a-1/src/elements/APIKeySecurityScheme.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/AgentCapabilities.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/AgentCard.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/AgentCardSignature.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/AgentExtension.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/AgentInterface.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/AgentProvider.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/AgentSkill.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/AuthorizationCodeOAuthFlow.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/ClientCredentialsOAuthFlow.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/DeviceCodeOAuthFlow.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/HTTPAuthSecurityScheme.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/ImplicitOAuthFlow.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/MutualTlsSecurityScheme.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/OAuth2SecurityScheme.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/OAuthFlows.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/OpenIdConnectSecurityScheme.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/PasswordOAuthFlow.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/SecurityRequirement.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/SecurityScheme.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/StringList.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/nces/Extensions.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/nces/SecurityRequirements.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/nces/SecuritySchemes.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/nces/Signatures.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/nces/Skills.ts create mode 100644 packages/apidom-ns-a2a-1/src/elements/nces/SupportedInterfaces.ts create mode 100644 packages/apidom-ns-a2a-1/src/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/media-types.ts create mode 100644 packages/apidom-ns-a2a-1/src/namespace.ts create mode 100644 packages/apidom-ns-a2a-1/src/predicates.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/canonicalize.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/predicates.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/registration.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/specification.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/toolbox.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/FallbackVisitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/SpecificationExtensionVisitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/SpecificationVisitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/Visitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/ExtensionsVisitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementSchemesVisitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementsVisitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecuritySchemesVisitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SignaturesVisitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SkillsVisitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SupportedInterfacesVisitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-capabilities/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-card-signature/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-card/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-extension/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-interface/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-provider/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-skill/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/api-key-security-scheme/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/authorization-code-oauth-flow/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/client-credentials-oauth-flow/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/device-code-oauth-flow/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/http-auth-security-scheme/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/implicit-oauth-flow/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/mutual-tls-security-scheme/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/oauth-flows/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/oauth2-security-scheme/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/open-id-connect-security-scheme/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/password-oauth-flow/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/security-requirement/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/security-scheme/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/string-list/index.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/generics/FixedFieldsVisitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/generics/MapVisitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/generics/PatternedFieldsVisitor.ts create mode 100644 packages/apidom-ns-a2a-1/src/traversal/visitor.ts create mode 100644 packages/apidom-ns-a2a-1/test/.eslintrc create mode 100644 packages/apidom-ns-a2a-1/test/fixtures/real-world-planner-agent.json create mode 100644 packages/apidom-ns-a2a-1/test/mocha-bootstrap.ts create mode 100644 packages/apidom-ns-a2a-1/test/predicates.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/canonicalize.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/APIKeySecurityScheme/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/APIKeySecurityScheme/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentCapabilities/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentCapabilities/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentCardSignature/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentCardSignature/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentProvider/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentProvider/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/HTTPAuthSecurityScheme/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/HTTPAuthSecurityScheme/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/MutualTlsSecurityScheme/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/MutualTlsSecurityScheme/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/OAuth2SecurityScheme/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/OAuth2SecurityScheme/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/OpenIdConnectSecurityScheme/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/OpenIdConnectSecurityScheme/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/SecurityRequirement/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/SecurityRequirement/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/StringList/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/StringList/index.ts create mode 100644 packages/apidom-ns-a2a-1/test/refractor/real-world.ts create mode 100644 packages/apidom-ns-a2a-1/test/tsconfig.json create mode 100644 packages/apidom-ns-a2a-1/tsconfig.declaration.json create mode 100644 packages/apidom-ns-a2a-1/tsconfig.json create mode 100644 packages/apidom-ns-a2a-1/vite.config.ts create mode 100644 packages/apidom-parser-adapter-a2a-json-1/.eslintignore create mode 100644 packages/apidom-parser-adapter-a2a-json-1/.gitignore create mode 100644 packages/apidom-parser-adapter-a2a-json-1/.mocharc.json create mode 100644 packages/apidom-parser-adapter-a2a-json-1/.npmrc create mode 100644 packages/apidom-parser-adapter-a2a-json-1/CHANGELOG.md create mode 100644 packages/apidom-parser-adapter-a2a-json-1/README.md create mode 100644 packages/apidom-parser-adapter-a2a-json-1/config/api-extractor/api-extractor.json create mode 100644 packages/apidom-parser-adapter-a2a-json-1/package.json create mode 100644 packages/apidom-parser-adapter-a2a-json-1/src/adapter.ts create mode 100644 packages/apidom-parser-adapter-a2a-json-1/src/media-types.ts create mode 100644 packages/apidom-parser-adapter-a2a-json-1/test/.eslintrc create mode 100644 packages/apidom-parser-adapter-a2a-json-1/test/__snapshots__/adapter.ts.snap create mode 100644 packages/apidom-parser-adapter-a2a-json-1/test/adapter.ts create mode 100644 packages/apidom-parser-adapter-a2a-json-1/test/fixtures/sample-agent-card.json create mode 100644 packages/apidom-parser-adapter-a2a-json-1/test/media-types.ts create mode 100644 packages/apidom-parser-adapter-a2a-json-1/test/mocha-bootstrap.ts create mode 100644 packages/apidom-parser-adapter-a2a-json-1/test/tsconfig.json create mode 100644 packages/apidom-parser-adapter-a2a-json-1/tsconfig.declaration.json create mode 100644 packages/apidom-parser-adapter-a2a-json-1/tsconfig.json create mode 100644 packages/apidom-parser-adapter-a2a-json-1/vite.config.ts create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/.eslintignore create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/.gitignore create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/.mocharc.json create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/.npmrc create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/CHANGELOG.md create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/README.md create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/config/api-extractor/api-extractor.json create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/package.json create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/src/adapter.ts create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/src/media-types.ts create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/test/.eslintrc create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/test/__snapshots__/adapter.ts.snap create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/test/adapter.ts create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/test/fixtures/sample-agent-card.yaml create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/test/media-types.ts create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/test/mocha-bootstrap.ts create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/test/tsconfig.json create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/tsconfig.declaration.json create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/tsconfig.json create mode 100644 packages/apidom-parser-adapter-a2a-yaml-1/vite.config.ts create mode 100644 packages/apidom-reference/src/parse/parsers/a2a-json-1/index.ts create mode 100644 packages/apidom-reference/src/parse/parsers/a2a-yaml-1/index.ts create mode 100644 packages/apidom-reference/test/parse/parsers/a2a-json-1/fixtures/sample-agent-card.json create mode 100644 packages/apidom-reference/test/parse/parsers/a2a-json-1/index.ts create mode 100644 packages/apidom-reference/test/parse/parsers/a2a-yaml-1/fixtures/sample-agent-card.yaml create mode 100644 packages/apidom-reference/test/parse/parsers/a2a-yaml-1/index.ts diff --git a/README.md b/README.md index 3bd74b8d1f..2898bcd0e1 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ You can install ApiDOM packages using [npm CLI](https://docs.npmjs.com/cli): $ npm install @swagger-api/apidom-json-pointer-relative $ npm install @swagger-api/apidom-logging $ npm install @swagger-api/apidom-ls + $ npm install @swagger-api/apidom-ns-a2a-1 $ npm install @swagger-api/apidom-ns-api-design-systems $ npm install @swagger-api/apidom-ns-arazzo-1 $ npm install @swagger-api/apidom-ns-asyncapi-2 @@ -93,6 +94,8 @@ You can install ApiDOM packages using [npm CLI](https://docs.npmjs.com/cli): $ npm install @swagger-api/apidom-ns-openapi-3-0 $ npm install @swagger-api/apidom-ns-openapi-3-1 $ npm install @swagger-api/apidom-parser + $ npm install @swagger-api/apidom-parser-adapter-a2a-json-1 + $ npm install @swagger-api/apidom-parser-adapter-a2a-yaml-1 $ npm install @swagger-api/apidom-parser-adapter-api-design-systems-json $ npm install @swagger-api/apidom-parser-adapter-api-design-systems-yaml $ npm install @swagger-api/apidom-parser-adapter-arazzo-json-1 @@ -628,6 +631,7 @@ The namespace packages provide ApiDOM namespaces for specifications, consisiting Available namespaces: +- [A2A 1.0.0](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-a2a-1) - [API Design Systems](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-api-design-systems) - [Arazzo 1.0.1](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-arazzo-1) - [AsyncAPI 2.x.y](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-asyncapi-2) @@ -648,6 +652,7 @@ The base parser adapters support [JSON](https://github.com/swagger-api/apidom/tr Available parser adapters for ApiDOM namespaces: +- A2A 1.0.0 - [JSON](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-a2a-json-1) / [YAML](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-a2a-yaml-1) - API Design Systems - [JSON](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-api-design-systems-json) / [YAML](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-api-design-systems-yaml) - Arazzo 1.0.1 - [JSON](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-arazzo-json-1) / [YAML](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-arazzo-yaml-1) - AsyncAPI 2.x.y - [JSON](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-asyncapi-json-2) / [YAML](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-asyncapi-yaml-2) @@ -671,6 +676,7 @@ Available parse strategies: - YAML 1.2 format - Binary format - ApiDOM - JSON +- A2A 1.0.0 - JSON / YAML - API Design Systems - JSON / YAML - Arazzo 1.0.1 - JSON / YAML - AsyncAPI 2.x.y - JSON / YAML diff --git a/package-lock.json b/package-lock.json index 3f2cf78c22..8326f883cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7075,6 +7075,10 @@ "resolved": "packages/apidom-ls", "link": true }, + "node_modules/@swagger-api/apidom-ns-a2a-1": { + "resolved": "packages/apidom-ns-a2a-1", + "link": true + }, "node_modules/@swagger-api/apidom-ns-api-design-systems": { "resolved": "packages/apidom-ns-api-design-systems", "link": true @@ -7131,6 +7135,14 @@ "resolved": "packages/apidom-parser", "link": true }, + "node_modules/@swagger-api/apidom-parser-adapter-a2a-json-1": { + "resolved": "packages/apidom-parser-adapter-a2a-json-1", + "link": true + }, + "node_modules/@swagger-api/apidom-parser-adapter-a2a-yaml-1": { + "resolved": "packages/apidom-parser-adapter-a2a-yaml-1", + "link": true + }, "node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-json": { "resolved": "packages/apidom-parser-adapter-api-design-systems-json", "link": true @@ -25186,6 +25198,7 @@ "@swagger-api/apidom-core": "^1.11.1", "@swagger-api/apidom-json-path": "^1.11.1", "@swagger-api/apidom-json-pointer": "^1.11.1", + "@swagger-api/apidom-ns-a2a-1": "^1.11.1", "@swagger-api/apidom-ns-api-design-systems": "^1.11.1", "@swagger-api/apidom-ns-asyncapi-2": "^1.11.1", "@swagger-api/apidom-ns-openapi-2": "^1.11.1", @@ -25193,6 +25206,8 @@ "@swagger-api/apidom-ns-openapi-3-1": "^1.11.1", "@swagger-api/apidom-ns-openapi-3-2": "^1.11.1", "@swagger-api/apidom-parser": "^1.11.1", + "@swagger-api/apidom-parser-adapter-a2a-json-1": "^1.11.1", + "@swagger-api/apidom-parser-adapter-a2a-yaml-1": "^1.11.1", "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^1.11.1", "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^1.11.1", "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^1.11.1", @@ -25243,6 +25258,19 @@ } } }, + "packages/apidom-ns-a2a-1": { + "name": "@swagger-api/apidom-ns-a2a-1", + "version": "1.11.1", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.11.1", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" + } + }, "packages/apidom-ns-api-design-systems": { "name": "@swagger-api/apidom-ns-api-design-systems", "version": "1.11.1", @@ -25452,6 +25480,34 @@ "ramda-adjunct": "^5.0.0" } }, + "packages/apidom-parser-adapter-a2a-json-1": { + "name": "@swagger-api/apidom-parser-adapter-a2a-json-1", + "version": "1.11.1", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.11.1", + "@swagger-api/apidom-ns-a2a-1": "^1.11.1", + "@swagger-api/apidom-parser-adapter-json": "^1.11.1", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "packages/apidom-parser-adapter-a2a-yaml-1": { + "name": "@swagger-api/apidom-parser-adapter-a2a-yaml-1", + "version": "1.11.1", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.11.1", + "@swagger-api/apidom-ns-a2a-1": "^1.11.1", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.11.1", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, "packages/apidom-parser-adapter-api-design-systems-json": { "name": "@swagger-api/apidom-parser-adapter-api-design-systems-json", "version": "1.11.1", @@ -25851,12 +25907,15 @@ }, "devDependencies": { "@swagger-api/apidom-json-pointer": "*", + "@swagger-api/apidom-ns-a2a-1": "*", "@swagger-api/apidom-ns-arazzo-1": "*", "@swagger-api/apidom-ns-asyncapi-2": "*", "@swagger-api/apidom-ns-openapi-2": "*", "@swagger-api/apidom-ns-openapi-3-0": "*", "@swagger-api/apidom-ns-openapi-3-1": "*", "@swagger-api/apidom-ns-openapi-3-2": "*", + "@swagger-api/apidom-parser-adapter-a2a-json-1": "*", + "@swagger-api/apidom-parser-adapter-a2a-yaml-1": "*", "@swagger-api/apidom-parser-adapter-api-design-systems-json": "*", "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "*", "@swagger-api/apidom-parser-adapter-arazzo-json-1": "*", @@ -25879,12 +25938,15 @@ }, "optionalDependencies": { "@swagger-api/apidom-json-pointer": "^1.11.1", + "@swagger-api/apidom-ns-a2a-1": "^1.11.1", "@swagger-api/apidom-ns-arazzo-1": "^1.11.1", "@swagger-api/apidom-ns-asyncapi-2": "^1.11.1", "@swagger-api/apidom-ns-openapi-2": "^1.11.1", "@swagger-api/apidom-ns-openapi-3-0": "^1.11.1", "@swagger-api/apidom-ns-openapi-3-1": "^1.11.1", "@swagger-api/apidom-ns-openapi-3-2": "^1.11.1", + "@swagger-api/apidom-parser-adapter-a2a-json-1": "^1.11.1", + "@swagger-api/apidom-parser-adapter-a2a-yaml-1": "^1.11.1", "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^1.11.1", "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^1.11.1", "@swagger-api/apidom-parser-adapter-arazzo-json-1": "^1.11.1", diff --git a/packages/apidom-ls/package.json b/packages/apidom-ls/package.json index 5ba10fa6f3..537dd0fa4e 100644 --- a/packages/apidom-ls/package.json +++ b/packages/apidom-ls/package.json @@ -101,6 +101,7 @@ "@swagger-api/apidom-core": "^1.11.1", "@swagger-api/apidom-json-path": "^1.11.1", "@swagger-api/apidom-json-pointer": "^1.11.1", + "@swagger-api/apidom-ns-a2a-1": "^1.11.1", "@swagger-api/apidom-ns-api-design-systems": "^1.11.1", "@swagger-api/apidom-ns-asyncapi-2": "^1.11.1", "@swagger-api/apidom-ns-openapi-2": "^1.11.1", @@ -108,6 +109,8 @@ "@swagger-api/apidom-ns-openapi-3-1": "^1.11.1", "@swagger-api/apidom-ns-openapi-3-2": "^1.11.1", "@swagger-api/apidom-parser": "^1.11.1", + "@swagger-api/apidom-parser-adapter-a2a-json-1": "^1.11.1", + "@swagger-api/apidom-parser-adapter-a2a-yaml-1": "^1.11.1", "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^1.11.1", "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^1.11.1", "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^1.11.1", diff --git a/packages/apidom-ls/src/config/a2a/a2a1/completion.ts b/packages/apidom-ls/src/config/a2a/a2a1/completion.ts new file mode 100644 index 0000000000..2278afcb29 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/completion.ts @@ -0,0 +1,359 @@ +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; + +const completion: ApidomCompletionItem[] = [ + { + label: 'name', + insertText: 'name', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[Agent Card name](https://a2a-protocol.org/latest/definitions/#agent-card)\n\\\n\\\nA human-readable name for the agent. Example: `"Recipe Agent"`.', + }, + targetSpecs: A2A1, + }, + { + label: 'description', + insertText: 'description', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'A human-readable description of the agent, helping users and other agents understand its purpose.', + }, + targetSpecs: A2A1, + }, + { + label: 'url', + insertText: 'url', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: "The agent's primary endpoint URL. Must be a valid absolute HTTPS URL in production.", + }, + targetSpecs: A2A1, + }, + { + label: 'version', + insertText: 'version', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'The agent\'s deployment version. Example: `"1.0.0"`.', + }, + targetSpecs: A2A1, + }, + { + label: 'iconUrl', + insertText: 'iconUrl', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'Optional URL to an icon for the agent.', + }, + targetSpecs: A2A1, + }, + { + label: 'documentationUrl', + insertText: 'documentationUrl', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'A URL providing additional documentation about the agent.', + }, + targetSpecs: A2A1, + }, + { + label: 'provider', + insertText: 'provider', + kind: 14, + format: CompletionFormat.OBJECT, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[Agent Provider Object](https://a2a-protocol.org/latest/definitions/#agent-provider)\n\\\n\\\nThe service provider of the agent (organization, URL).', + }, + targetSpecs: A2A1, + }, + { + label: 'capabilities', + insertText: 'capabilities', + kind: 14, + format: CompletionFormat.OBJECT, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[Agent Capabilities Object](https://a2a-protocol.org/latest/definitions/#agent-capabilities)\n\\\n\\\nOptional capabilities supported by the agent (streaming, push notifications, extensions, extended agent card).', + }, + targetSpecs: A2A1, + }, + { + label: 'defaultInputModes', + insertText: 'defaultInputModes', + kind: 14, + format: CompletionFormat.ARRAY, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'Media types the agent accepts as input across all skills. May be overridden per skill.', + }, + targetSpecs: A2A1, + }, + { + label: 'defaultOutputModes', + insertText: 'defaultOutputModes', + kind: 14, + format: CompletionFormat.ARRAY, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'Media types the agent can produce as output across all skills.', + }, + targetSpecs: A2A1, + }, + { + label: 'supportedInterfaces', + insertText: 'supportedInterfaces', + kind: 14, + format: CompletionFormat.ARRAY, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'Ordered list of supported interfaces (URL + protocol binding + protocol version). The first entry is preferred.', + }, + targetSpecs: A2A1, + }, + { + label: 'skills', + insertText: 'skills', + kind: 14, + format: CompletionFormat.ARRAY, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[Agent Skill Objects](https://a2a-protocol.org/latest/definitions/#agent-skill)\n\\\n\\\nDistinct capabilities the agent can perform.', + }, + targetSpecs: A2A1, + }, + { + label: 'securitySchemes', + insertText: 'securitySchemes', + kind: 14, + format: CompletionFormat.OBJECT, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'A map of named security schemes (`apiKeySecurityScheme`, `httpAuthSecurityScheme`, `oauth2SecurityScheme`, `openIdConnectSecurityScheme`, `mtlsSecurityScheme`).', + }, + targetSpecs: A2A1, + }, + { + label: 'securityRequirements', + insertText: 'securityRequirements', + kind: 14, + format: CompletionFormat.ARRAY, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'Security requirements that clients must satisfy to contact the agent.', + }, + targetSpecs: A2A1, + }, + { + label: 'signatures', + insertText: 'signatures', + kind: 14, + format: CompletionFormat.ARRAY, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'JSON Web Signatures computed for this Agent Card (RFC 7515 JWS).', + }, + targetSpecs: A2A1, + }, + // Nested: AgentCapabilities properties + { + label: 'streaming', + insertText: 'streaming', + kind: 14, + format: CompletionFormat.UNQUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'Indicates if the agent supports streaming responses.', + }, + conditions: [{ function: 'isInsideAgentCapabilities' }], + targetSpecs: A2A1, + }, + { + label: 'pushNotifications', + insertText: 'pushNotifications', + kind: 14, + format: CompletionFormat.UNQUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'Indicates if the agent supports sending push notifications for asynchronous task updates.', + }, + conditions: [{ function: 'isInsideAgentCapabilities' }], + targetSpecs: A2A1, + }, + // Nested: AgentSkill properties + { + label: 'id', + insertText: 'id', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'Unique identifier for the skill.', + }, + conditions: [{ function: 'isInsideAgentSkill' }], + targetSpecs: A2A1, + }, + { + label: 'tags', + insertText: 'tags', + kind: 14, + format: CompletionFormat.ARRAY, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: "Keywords describing the skill's capabilities.", + }, + conditions: [{ function: 'isInsideAgentSkill' }], + targetSpecs: A2A1, + }, + { + label: 'examples', + insertText: 'examples', + kind: 14, + format: CompletionFormat.ARRAY, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'Example prompts or scenarios this skill can handle.', + }, + conditions: [{ function: 'isInsideAgentSkill' }], + targetSpecs: A2A1, + }, + // Nested: SecurityScheme subtype fields + { + label: 'apiKeySecurityScheme', + insertText: 'apiKeySecurityScheme', + kind: 14, + format: CompletionFormat.OBJECT, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'API key authentication scheme. Set this OR one of the other security scheme subfields.', + }, + conditions: [{ function: 'isInsideSecurityScheme' }], + targetSpecs: A2A1, + }, + { + label: 'httpAuthSecurityScheme', + insertText: 'httpAuthSecurityScheme', + kind: 14, + format: CompletionFormat.OBJECT, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'HTTP authentication scheme (Basic, Bearer, etc., per RFC 7235).', + }, + conditions: [{ function: 'isInsideSecurityScheme' }], + targetSpecs: A2A1, + }, + { + label: 'oauth2SecurityScheme', + insertText: 'oauth2SecurityScheme', + kind: 14, + format: CompletionFormat.OBJECT, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'OAuth 2.0 authentication scheme.', + }, + conditions: [{ function: 'isInsideSecurityScheme' }], + targetSpecs: A2A1, + }, + { + label: 'openIdConnectSecurityScheme', + insertText: 'openIdConnectSecurityScheme', + kind: 14, + format: CompletionFormat.OBJECT, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'OpenID Connect authentication scheme.', + }, + conditions: [{ function: 'isInsideSecurityScheme' }], + targetSpecs: A2A1, + }, + { + label: 'mtlsSecurityScheme', + insertText: 'mtlsSecurityScheme', + kind: 14, + format: CompletionFormat.OBJECT, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'Mutual TLS authentication scheme.', + }, + conditions: [{ function: 'isInsideSecurityScheme' }], + targetSpecs: A2A1, + }, +]; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/documentation.ts b/packages/apidom-ls/src/config/a2a/a2a1/documentation.ts new file mode 100644 index 0000000000..10ad9ac687 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/documentation.ts @@ -0,0 +1,47 @@ +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 AgentCard fields. Only fields whose type + * isn't obvious from the value (objects, arrays of typed elements, maps) get + * entries — primitive string/boolean fields are self-documenting via the + * spec's element types. + */ +const documentation = [ + { + target: 'provider', + docs: '[Agent Provider Object](https://a2a-protocol.org/latest/definitions/#agent-provider) — the service provider of the agent (organization name and URL).', + targetSpecs: A2A1, + }, + { + target: 'capabilities', + docs: '[Agent Capabilities Object](https://a2a-protocol.org/latest/definitions/#agent-capabilities) — optional capabilities supported by the agent (streaming, push notifications, extensions, extended agent card).', + targetSpecs: A2A1, + }, + { + target: 'supportedInterfaces', + docs: 'Ordered list of [Agent Interface Objects](https://a2a-protocol.org/latest/definitions/#agent-interface). Each entry combines a URL, protocol binding (`JSONRPC`, `GRPC`, `HTTP+JSON`), and the A2A protocol version exposed at that endpoint. The first entry is preferred.', + targetSpecs: A2A1, + }, + { + target: 'skills', + docs: 'Array of [Agent Skill Objects](https://a2a-protocol.org/latest/definitions/#agent-skill) representing distinct capabilities the agent can perform.', + targetSpecs: A2A1, + }, + { + target: 'securitySchemes', + docs: 'Map of named [Security Scheme Objects](https://a2a-protocol.org/latest/definitions/#security-scheme). Each value is a wrapper with one of: `apiKeySecurityScheme`, `httpAuthSecurityScheme`, `oauth2SecurityScheme`, `openIdConnectSecurityScheme`, or `mtlsSecurityScheme`.', + targetSpecs: A2A1, + }, + { + target: 'securityRequirements', + docs: 'Array of [Security Requirement Objects](https://a2a-protocol.org/latest/definitions/#security-requirement) describing which security schemes clients must satisfy to contact the agent.', + targetSpecs: A2A1, + }, + { + target: 'signatures', + docs: 'Array of [Agent Card Signature Objects](https://a2a-protocol.org/latest/definitions/#agent-card-signature) — JSON Web Signatures (JWS, RFC 7515) computed for this Agent Card.', + targetSpecs: A2A1, + }, +]; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/allowed-fields.ts new file mode 100644 index 0000000000..14a3397237 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/allowed-fields.ts @@ -0,0 +1,36 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [ + [ + 'name', + 'description', + 'url', + 'version', + 'iconUrl', + 'documentationUrl', + 'provider', + 'capabilities', + 'defaultInputModes', + 'defaultOutputModes', + 'supportedInterfaces', + 'skills', + 'securitySchemes', + 'securityRequirements', + 'signatures', + ], + ], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/capabilities--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/capabilities--type.ts new file mode 100644 index 0000000000..d4b6fb7f6c --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/capabilities--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_CAPABILITIES_TYPE, + source: 'apilint', + message: "'capabilities' must be an Agent Capabilities Object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['agentCapabilities'], + marker: 'value', + target: 'capabilities', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/default-input-modes--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/default-input-modes--type.ts new file mode 100644 index 0000000000..03a1e5b068 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/default-input-modes--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_DEFAULT_INPUT_MODES_TYPE, + source: 'apilint', + message: "'defaultInputModes' must be an array", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['array'], + marker: 'value', + target: 'defaultInputModes', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/default-output-modes--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/default-output-modes--type.ts new file mode 100644 index 0000000000..3a5d3f13ce --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/default-output-modes--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_DEFAULT_OUTPUT_MODES_TYPE, + source: 'apilint', + message: "'defaultOutputModes' must be an array", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['array'], + marker: 'value', + target: 'defaultOutputModes', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/description--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/description--type.ts new file mode 100644 index 0000000000..ab89f3515c --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/description--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_DESCRIPTION_TYPE, + source: 'apilint', + message: "'description' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'description', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/documentation-url--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/documentation-url--type.ts new file mode 100644 index 0000000000..5779550543 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/documentation-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_DOCUMENTATION_URL_TYPE, + source: 'apilint', + message: "'documentationUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'documentationUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/icon-url--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/icon-url--type.ts new file mode 100644 index 0000000000..9611915059 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/icon-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_ICON_URL_TYPE, + source: 'apilint', + message: "'iconUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'iconUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/index.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/index.ts new file mode 100644 index 0000000000..505d194a88 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/index.ts @@ -0,0 +1,37 @@ +import allowedFieldsLint from './allowed-fields.ts'; +import nameTypeLint from './name--type.ts'; +import descriptionTypeLint from './description--type.ts'; +import urlTypeLint from './url--type.ts'; +import versionTypeLint from './version--type.ts'; +import iconUrlTypeLint from './icon-url--type.ts'; +import documentationUrlTypeLint from './documentation-url--type.ts'; +import providerTypeLint from './provider--type.ts'; +import capabilitiesTypeLint from './capabilities--type.ts'; +import defaultInputModesTypeLint from './default-input-modes--type.ts'; +import defaultOutputModesTypeLint from './default-output-modes--type.ts'; +import supportedInterfacesTypeLint from './supported-interfaces--type.ts'; +import skillsTypeLint from './skills--type.ts'; +import securitySchemesTypeLint from './security-schemes--type.ts'; +import securityRequirementsTypeLint from './security-requirements--type.ts'; +import signaturesTypeLint from './signatures--type.ts'; + +const lints = [ + allowedFieldsLint, + nameTypeLint, + descriptionTypeLint, + urlTypeLint, + versionTypeLint, + iconUrlTypeLint, + documentationUrlTypeLint, + providerTypeLint, + capabilitiesTypeLint, + defaultInputModesTypeLint, + defaultOutputModesTypeLint, + supportedInterfacesTypeLint, + skillsTypeLint, + securitySchemesTypeLint, + securityRequirementsTypeLint, + signaturesTypeLint, +]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/name--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/name--type.ts new file mode 100644 index 0000000000..873c569f4c --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/name--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_NAME_TYPE, + source: 'apilint', + message: "'name' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'name', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/provider--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/provider--type.ts new file mode 100644 index 0000000000..a36338a6ad --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/provider--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_PROVIDER_TYPE, + source: 'apilint', + message: "'provider' must be an Agent Provider Object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['agentProvider'], + marker: 'value', + target: 'provider', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/security-requirements--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/security-requirements--type.ts new file mode 100644 index 0000000000..59c4452287 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/security-requirements--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_SECURITY_REQUIREMENTS_TYPE, + source: 'apilint', + message: "'securityRequirements' must be an array of Security Requirement Objects", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintArrayOfElementsOrClasses', + linterParams: [['securityRequirement']], + marker: 'value', + target: 'securityRequirements', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/security-schemes--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/security-schemes--type.ts new file mode 100644 index 0000000000..10d325c58a --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/security-schemes--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_SECURITY_SCHEMES_TYPE, + source: 'apilint', + message: "'securitySchemes' must be a map of Security Scheme Objects", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintChildrenOfElementsOrClasses', + linterParams: [['securityScheme']], + marker: 'value', + target: 'securitySchemes', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/signatures--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/signatures--type.ts new file mode 100644 index 0000000000..f5f8ca885e --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/signatures--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_SIGNATURES_TYPE, + source: 'apilint', + message: "'signatures' must be an array of Agent Card Signature Objects", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintArrayOfElementsOrClasses', + linterParams: [['agentCardSignature']], + marker: 'value', + target: 'signatures', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/skills--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/skills--type.ts new file mode 100644 index 0000000000..8b2e6e7b44 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/skills--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_SKILLS_TYPE, + source: 'apilint', + message: "'skills' must be an array of Agent Skill Objects", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintArrayOfElementsOrClasses', + linterParams: [['agentSkill']], + marker: 'value', + target: 'skills', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/supported-interfaces--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/supported-interfaces--type.ts new file mode 100644 index 0000000000..9a5f8ec77e --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/supported-interfaces--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_SUPPORTED_INTERFACES_TYPE, + source: 'apilint', + message: "'supportedInterfaces' must be an array of Agent Interface Objects", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintArrayOfElementsOrClasses', + linterParams: [['agentInterface']], + marker: 'value', + target: 'supportedInterfaces', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/url--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/url--type.ts new file mode 100644 index 0000000000..6a1258a814 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_URL_TYPE, + source: 'apilint', + message: "'url' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'url', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/version--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/version--type.ts new file mode 100644 index 0000000000..77ee2c7485 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/version--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_VERSION_TYPE, + source: 'apilint', + message: "'version' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'version', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/meta.ts b/packages/apidom-ls/src/config/a2a/a2a1/meta.ts new file mode 100644 index 0000000000..6e1bbd7f60 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/meta.ts @@ -0,0 +1,12 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; +import documentation from './documentation.ts'; + +const meta: FormatMeta = { + lint, + completion, + documentation, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/agent-capabilities/completion.ts b/packages/apidom-ls/src/config/a2a/agent-capabilities/completion.ts new file mode 100644 index 0000000000..e3ea93a14b --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-capabilities/completion.ts @@ -0,0 +1,64 @@ +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; + +const completion: ApidomCompletionItem[] = [ + { + label: 'streaming', + insertText: 'streaming', + kind: 14, + format: CompletionFormat.UNQUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'Indicates if the agent supports streaming responses.', + }, + targetSpecs: A2A1, + }, + { + label: 'pushNotifications', + insertText: 'pushNotifications', + kind: 14, + format: CompletionFormat.UNQUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'Indicates if the agent supports sending push notifications for asynchronous task updates.', + }, + targetSpecs: A2A1, + }, + { + label: 'extendedAgentCard', + insertText: 'extendedAgentCard', + kind: 14, + format: CompletionFormat.UNQUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'Indicates if the agent supports providing an extended agent card when authenticated.', + }, + targetSpecs: A2A1, + }, + { + label: 'extensions', + insertText: 'extensions', + kind: 14, + format: CompletionFormat.ARRAY, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'A list of protocol extensions supported by the agent.', + }, + targetSpecs: A2A1, + }, +]; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/allowed-fields.ts new file mode 100644 index 0000000000..90cf32bf16 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['streaming', 'pushNotifications', 'extendedAgentCard', 'extensions']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/extended-agent-card--type.ts b/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/extended-agent-card--type.ts new file mode 100644 index 0000000000..38d96ca1ee --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/extended-agent-card--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CAPABILITIES_FIELD_EXTENDED_AGENT_CARD_TYPE, + source: 'apilint', + message: "'extendedAgentCard' must be a boolean", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['boolean'], + marker: 'value', + target: 'extendedAgentCard', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/extensions--type.ts b/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/extensions--type.ts new file mode 100644 index 0000000000..0fa7b0185b --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/extensions--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CAPABILITIES_FIELD_EXTENSIONS_TYPE, + source: 'apilint', + message: "'extensions' must be an array of Agent Extension Objects", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintArrayOfElementsOrClasses', + linterParams: [['agentExtension']], + marker: 'value', + target: 'extensions', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/index.ts b/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/index.ts new file mode 100644 index 0000000000..e95ed9732d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/index.ts @@ -0,0 +1,15 @@ +import allowedFieldsLint from './allowed-fields.ts'; +import streamingLint from './streaming--type.ts'; +import pushNotificationsLint from './push-notifications--type.ts'; +import extendedAgentCardLint from './extended-agent-card--type.ts'; +import extensionsLint from './extensions--type.ts'; + +const lints = [ + allowedFieldsLint, + streamingLint, + pushNotificationsLint, + extendedAgentCardLint, + extensionsLint, +]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/push-notifications--type.ts b/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/push-notifications--type.ts new file mode 100644 index 0000000000..562801f1ed --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/push-notifications--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CAPABILITIES_FIELD_PUSH_NOTIFICATIONS_TYPE, + source: 'apilint', + message: "'pushNotifications' must be a boolean", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['boolean'], + marker: 'value', + target: 'pushNotifications', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/streaming--type.ts b/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/streaming--type.ts new file mode 100644 index 0000000000..114f29fc70 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-capabilities/lint/streaming--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CAPABILITIES_FIELD_STREAMING_TYPE, + source: 'apilint', + message: "'streaming' must be a boolean", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['boolean'], + marker: 'value', + target: 'streaming', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-capabilities/meta.ts b/packages/apidom-ls/src/config/a2a/agent-capabilities/meta.ts new file mode 100644 index 0000000000..4c36077b61 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-capabilities/meta.ts @@ -0,0 +1,10 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; + +const meta: FormatMeta = { + lint, + completion, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/completion.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/completion.ts new file mode 100644 index 0000000000..550082a1a5 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/completion.ts @@ -0,0 +1,41 @@ +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; + +const signatureField = ( + label: string, + format: CompletionFormat, + docs: string, +): ApidomCompletionItem => ({ + label, + insertText: label, + kind: 14, + format, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { kind: 'markdown', value: docs }, + targetSpecs: A2A1, +}); + +const completion: ApidomCompletionItem[] = [ + signatureField( + 'protected', + CompletionFormat.QUOTED, + 'Base64url-encoded JWS Protected Header (JOSE).', + ), + signatureField( + 'signature', + CompletionFormat.QUOTED, + 'Base64url-encoded signature value over the AgentCard payload.', + ), + signatureField( + 'header', + CompletionFormat.OBJECT, + 'Unprotected JWS header parameters (JOSE).', + ), +]; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/allowed-fields.ts new file mode 100644 index 0000000000..a8733bb92b --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['protected', 'signature', 'header']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/header--type.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/header--type.ts new file mode 100644 index 0000000000..16684d7de0 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/header--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_SIGNATURE_FIELD_HEADER_TYPE, + source: 'apilint', + message: "'header' must be an object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['object'], + marker: 'value', + target: 'header', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/index.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/index.ts new file mode 100644 index 0000000000..db3ba74823 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/index.ts @@ -0,0 +1,8 @@ +import allowedFieldsLint from './allowed-fields.ts'; +import protectedLint from './protected--type.ts'; +import signatureLint from './signature--type.ts'; +import headerLint from './header--type.ts'; + +const lints = [allowedFieldsLint, protectedLint, signatureLint, headerLint]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/protected--type.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/protected--type.ts new file mode 100644 index 0000000000..413b949005 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/protected--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_SIGNATURE_FIELD_PROTECTED_TYPE, + source: 'apilint', + message: "'protected' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'protected', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/signature--type.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/signature--type.ts new file mode 100644 index 0000000000..a2be7d397c --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/signature--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_SIGNATURE_FIELD_SIGNATURE_TYPE, + source: 'apilint', + message: "'signature' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'signature', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/meta.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/meta.ts new file mode 100644 index 0000000000..4c36077b61 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/meta.ts @@ -0,0 +1,10 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; + +const meta: FormatMeta = { + lint, + completion, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/completion.ts b/packages/apidom-ls/src/config/a2a/agent-interface/completion.ts new file mode 100644 index 0000000000..974f5bb014 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-interface/completion.ts @@ -0,0 +1,64 @@ +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; + +const completion: ApidomCompletionItem[] = [ + { + label: 'url', + insertText: 'url', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'The URL where this interface is available. Must be a valid absolute HTTPS URL in production.', + }, + targetSpecs: A2A1, + }, + { + label: 'protocolBinding', + insertText: 'protocolBinding', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'The protocol binding (e.g. `JSONRPC`, `GRPC`, `HTTP+JSON`).', + }, + targetSpecs: A2A1, + }, + { + label: 'protocolVersion', + insertText: 'protocolVersion', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'The version of the A2A protocol this interface exposes (e.g. `"1.0"`).', + }, + targetSpecs: A2A1, + }, + { + label: 'tenant', + insertText: 'tenant', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'Optional opaque tenant identifier for multi-tenant A2A endpoints.', + }, + targetSpecs: A2A1, + }, +]; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/agent-interface/lint/allowed-fields.ts new file mode 100644 index 0000000000..4653ef954d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-interface/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['url', 'protocolBinding', 'protocolVersion', 'tenant']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/lint/index.ts b/packages/apidom-ls/src/config/a2a/agent-interface/lint/index.ts new file mode 100644 index 0000000000..3ec5efe036 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-interface/lint/index.ts @@ -0,0 +1,9 @@ +import allowedFieldsLint from './allowed-fields.ts'; +import urlLint from './url--type.ts'; +import protocolBindingLint from './protocol-binding--type.ts'; +import protocolVersionLint from './protocol-version--type.ts'; +import tenantLint from './tenant--type.ts'; + +const lints = [allowedFieldsLint, urlLint, protocolBindingLint, protocolVersionLint, tenantLint]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-binding--type.ts b/packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-binding--type.ts new file mode 100644 index 0000000000..becd419feb --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-binding--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_INTERFACE_FIELD_PROTOCOL_BINDING_TYPE, + source: 'apilint', + message: "'protocolBinding' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'protocolBinding', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-version--type.ts b/packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-version--type.ts new file mode 100644 index 0000000000..afff3a5880 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-version--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_INTERFACE_FIELD_PROTOCOL_VERSION_TYPE, + source: 'apilint', + message: "'protocolVersion' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'protocolVersion', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/lint/tenant--type.ts b/packages/apidom-ls/src/config/a2a/agent-interface/lint/tenant--type.ts new file mode 100644 index 0000000000..37bdc8522d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-interface/lint/tenant--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_INTERFACE_FIELD_TENANT_TYPE, + source: 'apilint', + message: "'tenant' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'tenant', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/lint/url--type.ts b/packages/apidom-ls/src/config/a2a/agent-interface/lint/url--type.ts new file mode 100644 index 0000000000..cf164a0252 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-interface/lint/url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_INTERFACE_FIELD_URL_TYPE, + source: 'apilint', + message: "'url' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'url', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/meta.ts b/packages/apidom-ls/src/config/a2a/agent-interface/meta.ts new file mode 100644 index 0000000000..4c36077b61 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-interface/meta.ts @@ -0,0 +1,10 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; + +const meta: FormatMeta = { + lint, + completion, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/agent-provider/completion.ts b/packages/apidom-ls/src/config/a2a/agent-provider/completion.ts new file mode 100644 index 0000000000..c861604c23 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-provider/completion.ts @@ -0,0 +1,34 @@ +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; + +const completion: ApidomCompletionItem[] = [ + { + label: 'organization', + insertText: 'organization', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { kind: 'markdown', value: "The name of the agent provider's organization." }, + targetSpecs: A2A1, + }, + { + label: 'url', + insertText: 'url', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: "A URL for the provider's website or documentation.", + }, + targetSpecs: A2A1, + }, +]; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/agent-provider/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/agent-provider/lint/allowed-fields.ts new file mode 100644 index 0000000000..1529bd6b0d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-provider/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['organization', 'url']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-provider/lint/index.ts b/packages/apidom-ls/src/config/a2a/agent-provider/lint/index.ts new file mode 100644 index 0000000000..63e828e8af --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-provider/lint/index.ts @@ -0,0 +1,7 @@ +import allowedFieldsLint from './allowed-fields.ts'; +import organizationLint from './organization--type.ts'; +import urlLint from './url--type.ts'; + +const lints = [allowedFieldsLint, organizationLint, urlLint]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/agent-provider/lint/organization--type.ts b/packages/apidom-ls/src/config/a2a/agent-provider/lint/organization--type.ts new file mode 100644 index 0000000000..1701d6e8a7 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-provider/lint/organization--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_PROVIDER_FIELD_ORGANIZATION_TYPE, + source: 'apilint', + message: "'organization' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'organization', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-provider/lint/url--type.ts b/packages/apidom-ls/src/config/a2a/agent-provider/lint/url--type.ts new file mode 100644 index 0000000000..00c8261cc6 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-provider/lint/url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_PROVIDER_FIELD_URL_TYPE, + source: 'apilint', + message: "'url' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'url', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-provider/meta.ts b/packages/apidom-ls/src/config/a2a/agent-provider/meta.ts new file mode 100644 index 0000000000..4c36077b61 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-provider/meta.ts @@ -0,0 +1,10 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; + +const meta: FormatMeta = { + lint, + completion, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/completion.ts b/packages/apidom-ls/src/config/a2a/agent-skill/completion.ts new file mode 100644 index 0000000000..848f887a40 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/completion.ts @@ -0,0 +1,50 @@ +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; + +const skillField = ( + label: string, + format: CompletionFormat, + docs: string, +): ApidomCompletionItem => ({ + label, + insertText: label, + kind: 14, + format, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { kind: 'markdown', value: docs }, + targetSpecs: A2A1, +}); + +const completion: ApidomCompletionItem[] = [ + skillField('id', CompletionFormat.QUOTED, 'Unique identifier for the skill.'), + skillField('name', CompletionFormat.QUOTED, 'Human-readable name for the skill.'), + skillField('description', CompletionFormat.QUOTED, 'Detailed description of the skill.'), + skillField('tags', CompletionFormat.ARRAY, "Keywords describing the skill's capabilities."), + skillField( + 'examples', + CompletionFormat.ARRAY, + 'Example prompts or scenarios this skill can handle.', + ), + skillField( + 'inputModes', + CompletionFormat.ARRAY, + "Supported input media types for this skill, overriding the agent's defaults.", + ), + skillField( + 'outputModes', + CompletionFormat.ARRAY, + "Supported output media types for this skill, overriding the agent's defaults.", + ), + skillField( + 'securityRequirements', + CompletionFormat.ARRAY, + 'Security schemes necessary for this skill.', + ), +]; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/allowed-fields.ts new file mode 100644 index 0000000000..7ae0201d8a --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/allowed-fields.ts @@ -0,0 +1,29 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [ + [ + 'id', + 'name', + 'description', + 'tags', + 'examples', + 'inputModes', + 'outputModes', + 'securityRequirements', + ], + ], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/description--type.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/description--type.ts new file mode 100644 index 0000000000..acb87cde9c --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/description--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_SKILL_FIELD_DESCRIPTION_TYPE, + source: 'apilint', + message: "'description' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'description', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/examples--type.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/examples--type.ts new file mode 100644 index 0000000000..76078571bf --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/examples--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_SKILL_FIELD_EXAMPLES_TYPE, + source: 'apilint', + message: "'examples' must be an array", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['array'], + marker: 'value', + target: 'examples', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/id--type.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/id--type.ts new file mode 100644 index 0000000000..6f1d30c64e --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/id--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_SKILL_FIELD_ID_TYPE, + source: 'apilint', + message: "'id' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'id', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/index.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/index.ts new file mode 100644 index 0000000000..e9edd8a3d6 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/index.ts @@ -0,0 +1,23 @@ +import allowedFieldsLint from './allowed-fields.ts'; +import idLint from './id--type.ts'; +import nameLint from './name--type.ts'; +import descriptionLint from './description--type.ts'; +import tagsLint from './tags--type.ts'; +import examplesLint from './examples--type.ts'; +import inputModesLint from './input-modes--type.ts'; +import outputModesLint from './output-modes--type.ts'; +import securityRequirementsLint from './security-requirements--type.ts'; + +const lints = [ + allowedFieldsLint, + idLint, + nameLint, + descriptionLint, + tagsLint, + examplesLint, + inputModesLint, + outputModesLint, + securityRequirementsLint, +]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/input-modes--type.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/input-modes--type.ts new file mode 100644 index 0000000000..a85580a3b2 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/input-modes--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_SKILL_FIELD_INPUT_MODES_TYPE, + source: 'apilint', + message: "'inputModes' must be an array", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['array'], + marker: 'value', + target: 'inputModes', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/name--type.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/name--type.ts new file mode 100644 index 0000000000..4e9882772d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/name--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_SKILL_FIELD_NAME_TYPE, + source: 'apilint', + message: "'name' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'name', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/output-modes--type.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/output-modes--type.ts new file mode 100644 index 0000000000..cc7edc37fb --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/output-modes--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_SKILL_FIELD_OUTPUT_MODES_TYPE, + source: 'apilint', + message: "'outputModes' must be an array", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['array'], + marker: 'value', + target: 'outputModes', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/security-requirements--type.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/security-requirements--type.ts new file mode 100644 index 0000000000..1c3a0f7a5a --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/security-requirements--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_SKILL_FIELD_SECURITY_REQUIREMENTS_TYPE, + source: 'apilint', + message: "'securityRequirements' must be an array of Security Requirement Objects", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintArrayOfElementsOrClasses', + linterParams: [['securityRequirement']], + marker: 'value', + target: 'securityRequirements', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/tags--type.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/tags--type.ts new file mode 100644 index 0000000000..abaf291762 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/tags--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_SKILL_FIELD_TAGS_TYPE, + source: 'apilint', + message: "'tags' must be an array", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['array'], + marker: 'value', + target: 'tags', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/meta.ts b/packages/apidom-ls/src/config/a2a/agent-skill/meta.ts new file mode 100644 index 0000000000..4c36077b61 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/meta.ts @@ -0,0 +1,10 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; + +const meta: FormatMeta = { + lint, + completion, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/completion.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/completion.ts new file mode 100644 index 0000000000..870ddb3840 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/completion.ts @@ -0,0 +1,47 @@ +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; + +const flowField = ( + label: string, + format: CompletionFormat, + docs: string, +): ApidomCompletionItem => ({ + label, + insertText: label, + kind: 14, + format, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { kind: 'markdown', value: docs }, + targetSpecs: A2A1, +}); + +const completion: ApidomCompletionItem[] = [ + flowField( + 'authorizationUrl', + CompletionFormat.QUOTED, + 'The authorization URL to be used for this flow.', + ), + flowField('tokenUrl', CompletionFormat.QUOTED, 'The token URL to be used for this flow.'), + flowField( + 'refreshUrl', + CompletionFormat.QUOTED, + 'The URL to be used for obtaining refresh tokens.', + ), + flowField( + 'pkceRequired', + CompletionFormat.UNQUOTED, + 'Whether PKCE (Proof Key for Code Exchange) is required for this flow.', + ), + flowField( + 'scopes', + CompletionFormat.OBJECT, + 'Available scopes for the OAuth2 security scheme. A map of scope name to short description.', + ), +]; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/allowed-fields.ts new file mode 100644 index 0000000000..cf7a9a3d69 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['authorizationUrl', 'tokenUrl', 'refreshUrl', 'pkceRequired', 'scopes']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/authorization-url--type.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/authorization-url--type.ts new file mode 100644 index 0000000000..82b4af4f70 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/authorization-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_TYPE, + source: 'apilint', + message: "'authorizationUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'authorizationUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/index.ts new file mode 100644 index 0000000000..fec8910cf7 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/index.ts @@ -0,0 +1,17 @@ +import allowedFieldsLint from './allowed-fields.ts'; +import authorizationUrlLint from './authorization-url--type.ts'; +import tokenUrlLint from './token-url--type.ts'; +import refreshUrlLint from './refresh-url--type.ts'; +import pkceRequiredLint from './pkce-required--type.ts'; +import scopesLint from './scopes--type.ts'; + +const lints = [ + allowedFieldsLint, + authorizationUrlLint, + tokenUrlLint, + refreshUrlLint, + pkceRequiredLint, + scopesLint, +]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/pkce-required--type.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/pkce-required--type.ts new file mode 100644 index 0000000000..28ef1144bd --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/pkce-required--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_PKCE_REQUIRED_TYPE, + source: 'apilint', + message: "'pkceRequired' must be a boolean", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['boolean'], + marker: 'value', + target: 'pkceRequired', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/refresh-url--type.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/refresh-url--type.ts new file mode 100644 index 0000000000..ce06ccac07 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/refresh-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE, + source: 'apilint', + message: "'refreshUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'refreshUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/scopes--type.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/scopes--type.ts new file mode 100644 index 0000000000..0df14bdf5b --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/scopes--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_SCOPES_TYPE, + source: 'apilint', + message: "'scopes' must be an object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['object'], + marker: 'value', + target: 'scopes', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/token-url--type.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/token-url--type.ts new file mode 100644 index 0000000000..29a1b4c43d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/token-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE, + source: 'apilint', + message: "'tokenUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'tokenUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/meta.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/meta.ts new file mode 100644 index 0000000000..4c36077b61 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/meta.ts @@ -0,0 +1,10 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; + +const meta: FormatMeta = { + lint, + completion, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/completion.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/completion.ts new file mode 100644 index 0000000000..cd69f1880d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/completion.ts @@ -0,0 +1,37 @@ +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; + +const flowField = ( + label: string, + format: CompletionFormat, + docs: string, +): ApidomCompletionItem => ({ + label, + insertText: label, + kind: 14, + format, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { kind: 'markdown', value: docs }, + targetSpecs: A2A1, +}); + +const completion: ApidomCompletionItem[] = [ + flowField('tokenUrl', CompletionFormat.QUOTED, 'The token URL to be used for this flow.'), + flowField( + 'refreshUrl', + CompletionFormat.QUOTED, + 'The URL to be used for obtaining refresh tokens.', + ), + flowField( + 'scopes', + CompletionFormat.OBJECT, + 'Available scopes for the OAuth2 security scheme. A map of scope name to short description.', + ), +]; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/allowed-fields.ts new file mode 100644 index 0000000000..2c7a29e77f --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['tokenUrl', 'refreshUrl', 'scopes']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/index.ts new file mode 100644 index 0000000000..4813c9dd15 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/index.ts @@ -0,0 +1,8 @@ +import allowedFieldsLint from './allowed-fields.ts'; +import tokenUrlLint from './token-url--type.ts'; +import refreshUrlLint from './refresh-url--type.ts'; +import scopesLint from './scopes--type.ts'; + +const lints = [allowedFieldsLint, tokenUrlLint, refreshUrlLint, scopesLint]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/refresh-url--type.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/refresh-url--type.ts new file mode 100644 index 0000000000..6ee21f83ca --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/refresh-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE, + source: 'apilint', + message: "'refreshUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'refreshUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/scopes--type.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/scopes--type.ts new file mode 100644 index 0000000000..9d62e2924e --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/scopes--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_SCOPES_TYPE, + source: 'apilint', + message: "'scopes' must be an object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['object'], + marker: 'value', + target: 'scopes', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/token-url--type.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/token-url--type.ts new file mode 100644 index 0000000000..0e5d7fb9b9 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/token-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE, + source: 'apilint', + message: "'tokenUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'tokenUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/meta.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/meta.ts new file mode 100644 index 0000000000..4c36077b61 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/meta.ts @@ -0,0 +1,10 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; + +const meta: FormatMeta = { + lint, + completion, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/config.ts b/packages/apidom-ls/src/config/a2a/config.ts new file mode 100644 index 0000000000..a29feff634 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/config.ts @@ -0,0 +1,44 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import a2a1Meta from './a2a1/meta.ts'; +import agentCapabilitiesMeta from './agent-capabilities/meta.ts'; +import agentSkillMeta from './agent-skill/meta.ts'; +import agentProviderMeta from './agent-provider/meta.ts'; +import agentInterfaceMeta from './agent-interface/meta.ts'; +import securitySchemeMeta from './security-scheme/meta.ts'; +import agentCardSignatureMeta from './agent-card-signature/meta.ts'; +import oauthFlowsMeta from './oauth-flows/meta.ts'; +import authorizationCodeOAuthFlowMeta from './authorization-code-oauth-flow/meta.ts'; +import clientCredentialsOAuthFlowMeta from './client-credentials-oauth-flow/meta.ts'; +import deviceCodeOAuthFlowMeta from './device-code-oauth-flow/meta.ts'; +import implicitOAuthFlowMeta from './implicit-oauth-flow/meta.ts'; +import passwordOAuthFlowMeta from './password-oauth-flow/meta.ts'; +import ApilintCodes from '../codes.ts'; + +export default { + '*': { + lint: [ + { + code: ApilintCodes.DUPLICATE_KEYS, + source: 'apilint', + message: 'an object cannot contain duplicate keys', + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintNoDuplicateKeys', + marker: 'key', + }, + ], + }, + agentCard: a2a1Meta, + agentCapabilities: agentCapabilitiesMeta, + agentSkill: agentSkillMeta, + agentProvider: agentProviderMeta, + agentInterface: agentInterfaceMeta, + securityScheme: securitySchemeMeta, + agentCardSignature: agentCardSignatureMeta, + oauthFlows: oauthFlowsMeta, + authorizationCodeOAuthFlow: authorizationCodeOAuthFlowMeta, + clientCredentialsOAuthFlow: clientCredentialsOAuthFlowMeta, + deviceCodeOAuthFlow: deviceCodeOAuthFlowMeta, + implicitOAuthFlow: implicitOAuthFlowMeta, + passwordOAuthFlow: passwordOAuthFlowMeta, +}; diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/completion.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/completion.ts new file mode 100644 index 0000000000..dd7304dd3f --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/completion.ts @@ -0,0 +1,42 @@ +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; + +const flowField = ( + label: string, + format: CompletionFormat, + docs: string, +): ApidomCompletionItem => ({ + label, + insertText: label, + kind: 14, + format, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { kind: 'markdown', value: docs }, + targetSpecs: A2A1, +}); + +const completion: ApidomCompletionItem[] = [ + flowField( + 'deviceAuthorizationUrl', + CompletionFormat.QUOTED, + 'The device authorization endpoint used to obtain a device verification code.', + ), + flowField('tokenUrl', CompletionFormat.QUOTED, 'The token URL to be used for this flow.'), + flowField( + 'refreshUrl', + CompletionFormat.QUOTED, + 'The URL to be used for obtaining refresh tokens.', + ), + flowField( + 'scopes', + CompletionFormat.OBJECT, + 'Available scopes for the OAuth2 security scheme. A map of scope name to short description.', + ), +]; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/allowed-fields.ts new file mode 100644 index 0000000000..eaae4d306b --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['deviceAuthorizationUrl', 'tokenUrl', 'refreshUrl', 'scopes']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/device-authorization-url--type.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/device-authorization-url--type.ts new file mode 100644 index 0000000000..f9941a693f --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/device-authorization-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_DEVICE_AUTHORIZATION_URL_TYPE, + source: 'apilint', + message: "'deviceAuthorizationUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'deviceAuthorizationUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/index.ts new file mode 100644 index 0000000000..47bd172b8c --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/index.ts @@ -0,0 +1,15 @@ +import allowedFieldsLint from './allowed-fields.ts'; +import deviceAuthorizationUrlLint from './device-authorization-url--type.ts'; +import tokenUrlLint from './token-url--type.ts'; +import refreshUrlLint from './refresh-url--type.ts'; +import scopesLint from './scopes--type.ts'; + +const lints = [ + allowedFieldsLint, + deviceAuthorizationUrlLint, + tokenUrlLint, + refreshUrlLint, + scopesLint, +]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/refresh-url--type.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/refresh-url--type.ts new file mode 100644 index 0000000000..f02c813388 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/refresh-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE, + source: 'apilint', + message: "'refreshUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'refreshUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/scopes--type.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/scopes--type.ts new file mode 100644 index 0000000000..e3a0f189a4 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/scopes--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_SCOPES_TYPE, + source: 'apilint', + message: "'scopes' must be an object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['object'], + marker: 'value', + target: 'scopes', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/token-url--type.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/token-url--type.ts new file mode 100644 index 0000000000..e7ec5af320 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/token-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE, + source: 'apilint', + message: "'tokenUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'tokenUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/meta.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/meta.ts new file mode 100644 index 0000000000..4c36077b61 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/meta.ts @@ -0,0 +1,10 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; + +const meta: FormatMeta = { + lint, + completion, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/completion.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/completion.ts new file mode 100644 index 0000000000..2465c3da40 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/completion.ts @@ -0,0 +1,41 @@ +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; + +const flowField = ( + label: string, + format: CompletionFormat, + docs: string, +): ApidomCompletionItem => ({ + label, + insertText: label, + kind: 14, + format, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { kind: 'markdown', value: docs }, + targetSpecs: A2A1, +}); + +const completion: ApidomCompletionItem[] = [ + flowField( + 'authorizationUrl', + CompletionFormat.QUOTED, + 'The authorization URL to be used for this flow.', + ), + flowField( + 'refreshUrl', + CompletionFormat.QUOTED, + 'The URL to be used for obtaining refresh tokens.', + ), + flowField( + 'scopes', + CompletionFormat.OBJECT, + 'Available scopes for the OAuth2 security scheme. A map of scope name to short description.', + ), +]; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/allowed-fields.ts new file mode 100644 index 0000000000..11920d5f11 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['authorizationUrl', 'refreshUrl', 'scopes']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--type.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--type.ts new file mode 100644 index 0000000000..46af92ce53 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_IMPLICIT_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_TYPE, + source: 'apilint', + message: "'authorizationUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'authorizationUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/index.ts new file mode 100644 index 0000000000..c0e25a4987 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/index.ts @@ -0,0 +1,8 @@ +import allowedFieldsLint from './allowed-fields.ts'; +import authorizationUrlLint from './authorization-url--type.ts'; +import refreshUrlLint from './refresh-url--type.ts'; +import scopesLint from './scopes--type.ts'; + +const lints = [allowedFieldsLint, authorizationUrlLint, refreshUrlLint, scopesLint]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/refresh-url--type.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/refresh-url--type.ts new file mode 100644 index 0000000000..3f0211eedc --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/refresh-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_IMPLICIT_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE, + source: 'apilint', + message: "'refreshUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'refreshUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/scopes--type.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/scopes--type.ts new file mode 100644 index 0000000000..5297d696af --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/scopes--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_IMPLICIT_OAUTH_FLOW_FIELD_SCOPES_TYPE, + source: 'apilint', + message: "'scopes' must be an object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['object'], + marker: 'value', + target: 'scopes', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/meta.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/meta.ts new file mode 100644 index 0000000000..4c36077b61 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/meta.ts @@ -0,0 +1,10 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; + +const meta: FormatMeta = { + lint, + completion, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/completion.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/completion.ts new file mode 100644 index 0000000000..415b7c397c --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/completion.ts @@ -0,0 +1,27 @@ +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; + +const flowField = (label: string, docs: string): ApidomCompletionItem => ({ + label, + insertText: label, + kind: 14, + format: CompletionFormat.OBJECT, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { kind: 'markdown', value: docs }, + targetSpecs: A2A1, +}); + +const completion: ApidomCompletionItem[] = [ + flowField('authorizationCode', 'OAuth 2.0 Authorization Code flow configuration.'), + flowField('clientCredentials', 'OAuth 2.0 Client Credentials flow configuration.'), + flowField('deviceCode', 'OAuth 2.0 Device Authorization Grant flow configuration.'), + flowField('implicit', 'OAuth 2.0 Implicit flow configuration.'), + flowField('password', 'OAuth 2.0 Resource Owner Password flow configuration.'), +]; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts new file mode 100644 index 0000000000..583565eb88 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [ + ['authorizationCode', 'clientCredentials', 'deviceCode', 'implicit', 'password'], + ], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/authorization-code--type.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/authorization-code--type.ts new file mode 100644 index 0000000000..35afe89544 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/authorization-code--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_OAUTH_FLOWS_FIELD_AUTHORIZATION_CODE_TYPE, + source: 'apilint', + message: "'authorizationCode' must be an Authorization Code OAuth Flow Object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['authorizationCodeOAuthFlow'], + marker: 'value', + target: 'authorizationCode', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/client-credentials--type.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/client-credentials--type.ts new file mode 100644 index 0000000000..4eaeb15e85 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/client-credentials--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_OAUTH_FLOWS_FIELD_CLIENT_CREDENTIALS_TYPE, + source: 'apilint', + message: "'clientCredentials' must be a Client Credentials OAuth Flow Object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['clientCredentialsOAuthFlow'], + marker: 'value', + target: 'clientCredentials', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/device-code--type.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/device-code--type.ts new file mode 100644 index 0000000000..1c7eae00e9 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/device-code--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_OAUTH_FLOWS_FIELD_DEVICE_CODE_TYPE, + source: 'apilint', + message: "'deviceCode' must be a Device Code OAuth Flow Object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['deviceCodeOAuthFlow'], + marker: 'value', + target: 'deviceCode', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/implicit--type.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/implicit--type.ts new file mode 100644 index 0000000000..630f8a5187 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/implicit--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_OAUTH_FLOWS_FIELD_IMPLICIT_TYPE, + source: 'apilint', + message: "'implicit' must be an Implicit OAuth Flow Object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['implicitOAuthFlow'], + marker: 'value', + target: 'implicit', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/index.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/index.ts new file mode 100644 index 0000000000..d3d8dc4983 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/index.ts @@ -0,0 +1,17 @@ +import allowedFieldsLint from './allowed-fields.ts'; +import authorizationCodeLint from './authorization-code--type.ts'; +import clientCredentialsLint from './client-credentials--type.ts'; +import deviceCodeLint from './device-code--type.ts'; +import implicitLint from './implicit--type.ts'; +import passwordLint from './password--type.ts'; + +const lints = [ + allowedFieldsLint, + authorizationCodeLint, + clientCredentialsLint, + deviceCodeLint, + implicitLint, + passwordLint, +]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/password--type.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/password--type.ts new file mode 100644 index 0000000000..79d8485194 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/password--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_OAUTH_FLOWS_FIELD_PASSWORD_TYPE, + source: 'apilint', + message: "'password' must be a Password OAuth Flow Object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['passwordOAuthFlow'], + marker: 'value', + target: 'password', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/meta.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/meta.ts new file mode 100644 index 0000000000..4c36077b61 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/meta.ts @@ -0,0 +1,10 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; + +const meta: FormatMeta = { + lint, + completion, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/completion.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/completion.ts new file mode 100644 index 0000000000..cd69f1880d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/password-oauth-flow/completion.ts @@ -0,0 +1,37 @@ +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; + +const flowField = ( + label: string, + format: CompletionFormat, + docs: string, +): ApidomCompletionItem => ({ + label, + insertText: label, + kind: 14, + format, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { kind: 'markdown', value: docs }, + targetSpecs: A2A1, +}); + +const completion: ApidomCompletionItem[] = [ + flowField('tokenUrl', CompletionFormat.QUOTED, 'The token URL to be used for this flow.'), + flowField( + 'refreshUrl', + CompletionFormat.QUOTED, + 'The URL to be used for obtaining refresh tokens.', + ), + flowField( + 'scopes', + CompletionFormat.OBJECT, + 'Available scopes for the OAuth2 security scheme. A map of scope name to short description.', + ), +]; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/allowed-fields.ts new file mode 100644 index 0000000000..2c7a29e77f --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['tokenUrl', 'refreshUrl', 'scopes']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/index.ts new file mode 100644 index 0000000000..4813c9dd15 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/index.ts @@ -0,0 +1,8 @@ +import allowedFieldsLint from './allowed-fields.ts'; +import tokenUrlLint from './token-url--type.ts'; +import refreshUrlLint from './refresh-url--type.ts'; +import scopesLint from './scopes--type.ts'; + +const lints = [allowedFieldsLint, tokenUrlLint, refreshUrlLint, scopesLint]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/refresh-url--type.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/refresh-url--type.ts new file mode 100644 index 0000000000..90a55c86a9 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/refresh-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_PASSWORD_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE, + source: 'apilint', + message: "'refreshUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'refreshUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/scopes--type.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/scopes--type.ts new file mode 100644 index 0000000000..99d0f42b64 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/scopes--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_PASSWORD_OAUTH_FLOW_FIELD_SCOPES_TYPE, + source: 'apilint', + message: "'scopes' must be an object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['object'], + marker: 'value', + target: 'scopes', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--type.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--type.ts new file mode 100644 index 0000000000..d781310197 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_PASSWORD_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE, + source: 'apilint', + message: "'tokenUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'tokenUrl', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/meta.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/meta.ts new file mode 100644 index 0000000000..4c36077b61 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/password-oauth-flow/meta.ts @@ -0,0 +1,10 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; + +const meta: FormatMeta = { + lint, + completion, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/security-scheme/completion.ts b/packages/apidom-ls/src/config/a2a/security-scheme/completion.ts new file mode 100644 index 0000000000..bb015c666f --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/security-scheme/completion.ts @@ -0,0 +1,30 @@ +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; + +const schemeWrapperField = (label: string, docs: string): ApidomCompletionItem => ({ + label, + insertText: label, + kind: 14, + format: CompletionFormat.OBJECT, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { kind: 'markdown', value: docs }, + targetSpecs: A2A1, +}); + +const completion: ApidomCompletionItem[] = [ + schemeWrapperField('apiKeySecurityScheme', 'API key authentication scheme.'), + schemeWrapperField( + 'httpAuthSecurityScheme', + 'HTTP authentication scheme (Basic, Bearer, etc., per RFC 7235).', + ), + schemeWrapperField('mtlsSecurityScheme', 'Mutual TLS authentication scheme.'), + schemeWrapperField('oauth2SecurityScheme', 'OAuth 2.0 authentication scheme.'), + schemeWrapperField('openIdConnectSecurityScheme', 'OpenID Connect authentication scheme.'), +]; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/security-scheme/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/security-scheme/lint/allowed-fields.ts new file mode 100644 index 0000000000..a8743c8140 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/security-scheme/lint/allowed-fields.ts @@ -0,0 +1,26 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [ + [ + 'apiKeySecurityScheme', + 'httpAuthSecurityScheme', + 'mtlsSecurityScheme', + 'oauth2SecurityScheme', + 'openIdConnectSecurityScheme', + ], + ], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/security-scheme/lint/api-key-security-scheme--type.ts b/packages/apidom-ls/src/config/a2a/security-scheme/lint/api-key-security-scheme--type.ts new file mode 100644 index 0000000000..27ebf2fe5c --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/security-scheme/lint/api-key-security-scheme--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_SECURITY_SCHEME_FIELD_API_KEY_TYPE, + source: 'apilint', + message: "'apiKeySecurityScheme' must be an API Key Security Scheme Object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['apiKeySecurityScheme'], + marker: 'value', + target: 'apiKeySecurityScheme', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/security-scheme/lint/http-auth-security-scheme--type.ts b/packages/apidom-ls/src/config/a2a/security-scheme/lint/http-auth-security-scheme--type.ts new file mode 100644 index 0000000000..b144c7f4d0 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/security-scheme/lint/http-auth-security-scheme--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_SECURITY_SCHEME_FIELD_HTTP_AUTH_TYPE, + source: 'apilint', + message: "'httpAuthSecurityScheme' must be an HTTP Auth Security Scheme Object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['httpAuthSecurityScheme'], + marker: 'value', + target: 'httpAuthSecurityScheme', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/security-scheme/lint/index.ts new file mode 100644 index 0000000000..ae758fcc4f --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/security-scheme/lint/index.ts @@ -0,0 +1,17 @@ +import allowedFieldsLint from './allowed-fields.ts'; +import apiKeySecuritySchemeLint from './api-key-security-scheme--type.ts'; +import httpAuthSecuritySchemeLint from './http-auth-security-scheme--type.ts'; +import mtlsSecuritySchemeLint from './mtls-security-scheme--type.ts'; +import oauth2SecuritySchemeLint from './oauth2-security-scheme--type.ts'; +import openIdConnectSecuritySchemeLint from './open-id-connect-security-scheme--type.ts'; + +const lints = [ + allowedFieldsLint, + apiKeySecuritySchemeLint, + httpAuthSecuritySchemeLint, + mtlsSecuritySchemeLint, + oauth2SecuritySchemeLint, + openIdConnectSecuritySchemeLint, +]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/security-scheme/lint/mtls-security-scheme--type.ts b/packages/apidom-ls/src/config/a2a/security-scheme/lint/mtls-security-scheme--type.ts new file mode 100644 index 0000000000..5af77666e2 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/security-scheme/lint/mtls-security-scheme--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_SECURITY_SCHEME_FIELD_MTLS_TYPE, + source: 'apilint', + message: "'mtlsSecurityScheme' must be a Mutual TLS Security Scheme Object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['mutualTlsSecurityScheme'], + marker: 'value', + target: 'mtlsSecurityScheme', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/security-scheme/lint/oauth2-security-scheme--type.ts b/packages/apidom-ls/src/config/a2a/security-scheme/lint/oauth2-security-scheme--type.ts new file mode 100644 index 0000000000..640a10b9e6 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/security-scheme/lint/oauth2-security-scheme--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_SECURITY_SCHEME_FIELD_OAUTH2_TYPE, + source: 'apilint', + message: "'oauth2SecurityScheme' must be an OAuth 2.0 Security Scheme Object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['oauth2SecurityScheme'], + marker: 'value', + target: 'oauth2SecurityScheme', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/security-scheme/lint/open-id-connect-security-scheme--type.ts b/packages/apidom-ls/src/config/a2a/security-scheme/lint/open-id-connect-security-scheme--type.ts new file mode 100644 index 0000000000..79b64dc221 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/security-scheme/lint/open-id-connect-security-scheme--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const lint: LinterMeta = { + code: ApilintCodes.A2A1_SECURITY_SCHEME_FIELD_OPEN_ID_CONNECT_TYPE, + source: 'apilint', + message: "'openIdConnectSecurityScheme' must be an OpenID Connect Security Scheme Object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['openIdConnectSecurityScheme'], + marker: 'value', + target: 'openIdConnectSecurityScheme', + targetSpecs: A2A1, +}; + +export default lint; diff --git a/packages/apidom-ls/src/config/a2a/security-scheme/meta.ts b/packages/apidom-ls/src/config/a2a/security-scheme/meta.ts new file mode 100644 index 0000000000..4c36077b61 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/security-scheme/meta.ts @@ -0,0 +1,10 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; + +const meta: FormatMeta = { + lint, + completion, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/target-specs.ts b/packages/apidom-ls/src/config/a2a/target-specs.ts new file mode 100644 index 0000000000..51c5e666fb --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/target-specs.ts @@ -0,0 +1,3 @@ +export const A2A100 = [{ namespace: 'a2a', version: '1.0.0' }]; + +export const A2A1 = [...A2A100]; diff --git a/packages/apidom-ls/src/config/codes.ts b/packages/apidom-ls/src/config/codes.ts index d0e8cafbf9..c57299e09a 100644 --- a/packages/apidom-ls/src/config/codes.ts +++ b/packages/apidom-ls/src/config/codes.ts @@ -1435,6 +1435,98 @@ enum ApilintCodes { JSON_SCHEMA_2020_12_KEYWORD_$SCHEMA_FORMAT_URI = 8030200, JSON_SCHEMA_2020_12_KEYWORD_$REF_FORMAT_URI = 8030300, JSON_SCHEMA_2020_12_KEYWORD_$COMMENT_TYPE = 8030400, + + A2A1 = 9000000, + + A2A1_AGENT_CARD = 9010000, + A2A1_AGENT_CARD_FIELD_NAME_TYPE = 9010100, + A2A1_AGENT_CARD_FIELD_DESCRIPTION_TYPE = 9010200, + A2A1_AGENT_CARD_FIELD_URL_TYPE = 9010300, + A2A1_AGENT_CARD_FIELD_VERSION_TYPE = 9010400, + A2A1_AGENT_CARD_FIELD_ICON_URL_TYPE = 9010500, + A2A1_AGENT_CARD_FIELD_DOCUMENTATION_URL_TYPE = 9010600, + A2A1_AGENT_CARD_FIELD_PROVIDER_TYPE = 9010700, + A2A1_AGENT_CARD_FIELD_CAPABILITIES_TYPE = 9010800, + A2A1_AGENT_CARD_FIELD_DEFAULT_INPUT_MODES_TYPE = 9010900, + A2A1_AGENT_CARD_FIELD_DEFAULT_OUTPUT_MODES_TYPE = 9011000, + A2A1_AGENT_CARD_FIELD_SUPPORTED_INTERFACES_TYPE = 9011100, + A2A1_AGENT_CARD_FIELD_SKILLS_TYPE = 9011200, + A2A1_AGENT_CARD_FIELD_SECURITY_SCHEMES_TYPE = 9011300, + A2A1_AGENT_CARD_FIELD_SECURITY_REQUIREMENTS_TYPE = 9011400, + A2A1_AGENT_CARD_FIELD_SIGNATURES_TYPE = 9011500, + + A2A1_AGENT_CAPABILITIES = 9020000, + A2A1_AGENT_CAPABILITIES_FIELD_STREAMING_TYPE = 9020100, + A2A1_AGENT_CAPABILITIES_FIELD_PUSH_NOTIFICATIONS_TYPE = 9020200, + A2A1_AGENT_CAPABILITIES_FIELD_EXTENDED_AGENT_CARD_TYPE = 9020300, + A2A1_AGENT_CAPABILITIES_FIELD_EXTENSIONS_TYPE = 9020400, + + A2A1_AGENT_SKILL = 9030000, + A2A1_AGENT_SKILL_FIELD_ID_TYPE = 9030100, + A2A1_AGENT_SKILL_FIELD_NAME_TYPE = 9030200, + A2A1_AGENT_SKILL_FIELD_DESCRIPTION_TYPE = 9030300, + A2A1_AGENT_SKILL_FIELD_TAGS_TYPE = 9030400, + A2A1_AGENT_SKILL_FIELD_EXAMPLES_TYPE = 9030500, + A2A1_AGENT_SKILL_FIELD_INPUT_MODES_TYPE = 9030600, + A2A1_AGENT_SKILL_FIELD_OUTPUT_MODES_TYPE = 9030700, + A2A1_AGENT_SKILL_FIELD_SECURITY_REQUIREMENTS_TYPE = 9030800, + + A2A1_AGENT_PROVIDER = 9040000, + A2A1_AGENT_PROVIDER_FIELD_ORGANIZATION_TYPE = 9040100, + A2A1_AGENT_PROVIDER_FIELD_URL_TYPE = 9040200, + + A2A1_AGENT_INTERFACE = 9050000, + A2A1_AGENT_INTERFACE_FIELD_URL_TYPE = 9050100, + A2A1_AGENT_INTERFACE_FIELD_PROTOCOL_BINDING_TYPE = 9050200, + A2A1_AGENT_INTERFACE_FIELD_PROTOCOL_VERSION_TYPE = 9050300, + A2A1_AGENT_INTERFACE_FIELD_TENANT_TYPE = 9050400, + + A2A1_SECURITY_SCHEME = 9060000, + A2A1_SECURITY_SCHEME_FIELD_API_KEY_TYPE = 9060100, + A2A1_SECURITY_SCHEME_FIELD_HTTP_AUTH_TYPE = 9060200, + A2A1_SECURITY_SCHEME_FIELD_MTLS_TYPE = 9060300, + A2A1_SECURITY_SCHEME_FIELD_OAUTH2_TYPE = 9060400, + A2A1_SECURITY_SCHEME_FIELD_OPEN_ID_CONNECT_TYPE = 9060500, + + A2A1_AGENT_CARD_SIGNATURE = 9070000, + A2A1_AGENT_CARD_SIGNATURE_FIELD_PROTECTED_TYPE = 9070100, + A2A1_AGENT_CARD_SIGNATURE_FIELD_SIGNATURE_TYPE = 9070200, + A2A1_AGENT_CARD_SIGNATURE_FIELD_HEADER_TYPE = 9070300, + + A2A1_OAUTH_FLOWS = 9080000, + A2A1_OAUTH_FLOWS_FIELD_AUTHORIZATION_CODE_TYPE = 9080100, + A2A1_OAUTH_FLOWS_FIELD_CLIENT_CREDENTIALS_TYPE = 9080200, + A2A1_OAUTH_FLOWS_FIELD_DEVICE_CODE_TYPE = 9080300, + A2A1_OAUTH_FLOWS_FIELD_IMPLICIT_TYPE = 9080400, + A2A1_OAUTH_FLOWS_FIELD_PASSWORD_TYPE = 9080500, + + A2A1_AUTHORIZATION_CODE_OAUTH_FLOW = 9090000, + A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_TYPE = 9090100, + A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE = 9090200, + A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE = 9090300, + A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_PKCE_REQUIRED_TYPE = 9090400, + A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9090500, + + A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW = 9100000, + A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE = 9100100, + A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE = 9100200, + A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9100300, + + A2A1_DEVICE_CODE_OAUTH_FLOW = 9110000, + A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_DEVICE_AUTHORIZATION_URL_TYPE = 9110100, + A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE = 9110200, + A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE = 9110300, + A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9110400, + + A2A1_IMPLICIT_OAUTH_FLOW = 9120000, + A2A1_IMPLICIT_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_TYPE = 9120100, + A2A1_IMPLICIT_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE = 9120200, + A2A1_IMPLICIT_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9120300, + + A2A1_PASSWORD_OAUTH_FLOW = 9130000, + A2A1_PASSWORD_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE = 9130100, + A2A1_PASSWORD_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE = 9130200, + A2A1_PASSWORD_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9130300, } export default ApilintCodes; diff --git a/packages/apidom-ls/src/config/config.ts b/packages/apidom-ls/src/config/config.ts index 2da7cfbcee..4a5921036a 100644 --- a/packages/apidom-ls/src/config/config.ts +++ b/packages/apidom-ls/src/config/config.ts @@ -1,6 +1,7 @@ import configAsyncAPI from './asyncapi/config.ts'; import configOpenAPI from './openapi/config.ts'; import configADS from './ads/config.ts'; +import configA2A from './a2a/config.ts'; import configJSONSchema202012 from './json-schema/2020-12/config.ts'; import asyncapiReferenceMeta from './asyncapi/reference/meta.ts'; import openapiReferenceMeta from './openapi/reference/meta.ts'; @@ -18,6 +19,7 @@ export function config(): Metadata { openapi: configOpenAPI, asyncapi: configAsyncAPI, ads: configADS, + a2a: configA2A, 'json-schema-2020-12': configJSONSchema202012, }, rules: { diff --git a/packages/apidom-ls/src/parser-factory.ts b/packages/apidom-ls/src/parser-factory.ts index 53fcdd172d..e1667e388d 100644 --- a/packages/apidom-ls/src/parser-factory.ts +++ b/packages/apidom-ls/src/parser-factory.ts @@ -10,6 +10,8 @@ import * as asyncapi2AdapterJson from '@swagger-api/apidom-parser-adapter-asynca import * as asyncapi2AdapterYaml from '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2'; import * as asyncapi3AdapterJson from '@swagger-api/apidom-parser-adapter-asyncapi-json-3'; import * as asyncapi3AdapterYaml from '@swagger-api/apidom-parser-adapter-asyncapi-yaml-3'; +import * as a2a1AdapterJson from '@swagger-api/apidom-parser-adapter-a2a-json-1'; +import * as a2a1AdapterYaml from '@swagger-api/apidom-parser-adapter-a2a-yaml-1'; import * as adsAdapterJson from '@swagger-api/apidom-parser-adapter-api-design-systems-json'; import * as adsAdapterYaml from '@swagger-api/apidom-parser-adapter-api-design-systems-yaml'; import * as adapterJson from '@swagger-api/apidom-parser-adapter-json'; @@ -219,6 +221,10 @@ export async function parse( }; result = await openapi3_2AdapterYaml.parse(text, options); + } else if (contentLanguage.namespace === 'a2a' && contentLanguage.format === 'JSON') { + result = await a2a1AdapterJson.parse(text, { sourceMap: true }); + } else if (contentLanguage.namespace === 'a2a' && contentLanguage.format === 'YAML') { + result = await a2a1AdapterYaml.parse(text, { sourceMap: true }); } else if (contentLanguage.namespace === 'ads' && contentLanguage.format === 'JSON') { result = await adsAdapterJson.parse(text, { sourceMap: true }); } else if (contentLanguage.namespace === 'ads' && contentLanguage.format === 'YAML') { diff --git a/packages/apidom-ls/src/utils/utils.ts b/packages/apidom-ls/src/utils/utils.ts index b0bf5ac11b..89c6a848c3 100644 --- a/packages/apidom-ls/src/utils/utils.ts +++ b/packages/apidom-ls/src/utils/utils.ts @@ -10,6 +10,8 @@ import * as asyncapi2AdapterJson from '@swagger-api/apidom-parser-adapter-asynca import * as asyncapi2AdapterYaml from '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2'; import * as asyncapi3AdapterJson from '@swagger-api/apidom-parser-adapter-asyncapi-json-3'; import * as asyncapi3AdapterYaml from '@swagger-api/apidom-parser-adapter-asyncapi-yaml-3'; +import * as a2a1AdapterJson from '@swagger-api/apidom-parser-adapter-a2a-json-1'; +import * as a2a1AdapterYaml from '@swagger-api/apidom-parser-adapter-a2a-yaml-1'; import * as adsAdapterJson from '@swagger-api/apidom-parser-adapter-api-design-systems-json'; import * as adsAdapterYaml from '@swagger-api/apidom-parser-adapter-api-design-systems-yaml'; import * as adapterJson from '@swagger-api/apidom-parser-adapter-json'; @@ -974,6 +976,31 @@ export async function findNamespace( }; } + if (await a2a1AdapterJson.detect(text)) { + // A2A AgentCard documents have no version discriminator field, so we + // pin the LS namespace identification to A2A v1 (the only published + // protocol version at time of writing). + const version = '1.0.0'; + + return { + namespace: 'a2a', + version, + format: 'JSON', + mediaType: a2a1AdapterJson.mediaTypes.findBy(version, 'json'), + }; + } + + if (await a2a1AdapterYaml.detect(text)) { + const version = '1.0.0'; + + return { + namespace: 'a2a', + version, + format: 'YAML', + mediaType: a2a1AdapterYaml.mediaTypes.findBy(version, 'yaml'), + }; + } + if (await adsAdapterJson.detect(text)) { const adsJsonMatch = text.match(adsAdapterJson.detectionRegExp)!; const groups = adsJsonMatch.groups!; diff --git a/packages/apidom-ls/test/a2a.ts b/packages/apidom-ls/test/a2a.ts new file mode 100644 index 0000000000..7effc03bf3 --- /dev/null +++ b/packages/apidom-ls/test/a2a.ts @@ -0,0 +1,55 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { assert } from 'chai'; +import { TextDocument } from 'vscode-languageserver-textdocument'; +import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver-types'; +import { fileURLToPath } from 'node:url'; + +import getLanguageService from '../src/apidom-language-service.ts'; +import { + LanguageService, + LanguageServiceContext, + ValidationContext, +} from '../src/apidom-language-types.ts'; +import { metadata } from './metadata.ts'; +import { logPerformance, logLevel } from './test-utils.ts'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const agentCardValid = fs + .readFileSync(path.join(__dirname, 'fixtures', 'a2a', 'agent-card-valid.json')) + .toString(); + +describe('apidom-ls-a2a', function () { + const context: LanguageServiceContext = { + metadata: metadata(), + performanceLogs: logPerformance, + logLevel, + }; + + const languageService: LanguageService = getLanguageService(context); + + after(function () { + languageService.terminate(); + }); + + it('validates a well-formed AgentCard with no diagnostics', async function () { + this.timeout(10000); + + const validationContext: ValidationContext = { + comments: DiagnosticSeverity.Error, + maxNumberOfProblems: 100, + relatedInformation: false, + }; + + const doc: TextDocument = TextDocument.create( + 'foo://bar/agent-card.json', + 'json', + 0, + agentCardValid, + ); + + const result = await languageService.doValidation(doc, validationContext); + assert.deepEqual(result, [] as Diagnostic[]); + }); +}); diff --git a/packages/apidom-ls/test/fixtures/a2a/agent-card-valid.json b/packages/apidom-ls/test/fixtures/a2a/agent-card-valid.json new file mode 100644 index 0000000000..2c4d27a55e --- /dev/null +++ b/packages/apidom-ls/test/fixtures/a2a/agent-card-valid.json @@ -0,0 +1,44 @@ +{ + "name": "Planner Agent", + "description": "Breaks down a request into actionable tasks.", + "url": "https://agent.example.com/", + "version": "1.0.0", + "iconUrl": "https://agent.example.com/icon.png", + "documentationUrl": "https://agent.example.com/docs", + "provider": { + "organization": "Example Co.", + "url": "https://example.com/" + }, + "capabilities": { + "streaming": true, + "pushNotifications": true, + "extendedAgentCard": false + }, + "defaultInputModes": ["text", "text/plain"], + "defaultOutputModes": ["text", "text/plain"], + "skills": [ + { + "id": "planner", + "name": "Task Planner", + "description": "Plans tasks based on user goals.", + "tags": ["planner"], + "examples": ["Plan a business trip"] + } + ], + "securitySchemes": { + "bearer": { + "httpAuthSecurityScheme": { + "description": "Bearer token authentication.", + "scheme": "bearer", + "bearerFormat": "JWT" + } + } + }, + "securityRequirements": [ + { + "schemes": { + "bearer": [] + } + } + ] +} diff --git a/packages/apidom-ns-a2a-1/.eslintignore b/packages/apidom-ns-a2a-1/.eslintignore new file mode 100644 index 0000000000..5637a4f9c6 --- /dev/null +++ b/packages/apidom-ns-a2a-1/.eslintignore @@ -0,0 +1,9 @@ +/**/*.js +/**/*.mjs +/**/*.cjs +/dist +/config +/types +/.eslintrc.js +/.nyc_output +/node_modules diff --git a/packages/apidom-ns-a2a-1/.gitignore b/packages/apidom-ns-a2a-1/.gitignore new file mode 100644 index 0000000000..9026be80f3 --- /dev/null +++ b/packages/apidom-ns-a2a-1/.gitignore @@ -0,0 +1,7 @@ +/src/**/*.mjs +/src/**/*.cjs +/test/**/*.mjs +/dist +/types +/NOTICE +/swagger-api-apidom-ns-a2a-1-*.tgz diff --git a/packages/apidom-ns-a2a-1/.mocharc.json b/packages/apidom-ns-a2a-1/.mocharc.json new file mode 100644 index 0000000000..38ee8de7ec --- /dev/null +++ b/packages/apidom-ns-a2a-1/.mocharc.json @@ -0,0 +1,7 @@ +{ + "extensions": ["ts"], + "loader": "ts-node/esm", + "recursive": true, + "spec": "test/**/*.ts", + "file": ["test/mocha-bootstrap.ts"] +} diff --git a/packages/apidom-ns-a2a-1/.npmrc b/packages/apidom-ns-a2a-1/.npmrc new file mode 100644 index 0000000000..4b82d2e7bb --- /dev/null +++ b/packages/apidom-ns-a2a-1/.npmrc @@ -0,0 +1,2 @@ +save-prefix="=" +save=false diff --git a/packages/apidom-ns-a2a-1/CHANGELOG.md b/packages/apidom-ns-a2a-1/CHANGELOG.md new file mode 100644 index 0000000000..7dd6dc3ad1 --- /dev/null +++ b/packages/apidom-ns-a2a-1/CHANGELOG.md @@ -0,0 +1,10 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## 1.11.1 (2026-05-27) + +### Features + +- **a2a:** initial namespace package for A2A (Agent-to-Agent) Protocol v1.0. Models the AgentCard document with 21 element classes (AgentCard, AgentCapabilities, AgentExtension, AgentProvider, AgentInterface, AgentSkill, AgentCardSignature, SecurityRequirement, SecurityScheme + 5 concrete subtypes, OAuthFlows + 5 flow subtypes, StringList). diff --git a/packages/apidom-ns-a2a-1/README.md b/packages/apidom-ns-a2a-1/README.md new file mode 100644 index 0000000000..ddb3461528 --- /dev/null +++ b/packages/apidom-ns-a2a-1/README.md @@ -0,0 +1,60 @@ +# @swagger-api/apidom-ns-a2a-1 + +`apidom-ns-a2a-1` contains the ApiDOM namespace for the [A2A (Agent-to-Agent) Protocol v1.0](https://a2a-protocol.org/latest/definitions/). It models the **Agent Card** document type — the `/.well-known/agent.json` manifest that describes an agent's identity, capabilities, skills, supported interfaces, and security requirements. + +## Installation + +```sh +npm install --save @swagger-api/apidom-ns-a2a-1 +``` + +## Usage + +```ts +import { AgentCardElement, isAgentCardElement } from '@swagger-api/apidom-ns-a2a-1'; + +const card = AgentCardElement.refract({ + name: 'Recipe Agent', + description: 'Helps users find and follow recipes', + url: 'https://recipes.example.com/a2a', + version: '1.0.0', + capabilities: { + streaming: true, + pushNotifications: false, + }, + defaultInputModes: ['text/plain'], + defaultOutputModes: ['application/json'], + skills: [ + { + id: 'find-recipe', + name: 'Find Recipe', + description: 'Locate recipes matching ingredients or cuisine.', + tags: ['recipes', 'cooking'], + }, + ], +}); + +isAgentCardElement(card); // true +card.name?.toValue(); // "Recipe Agent" +card.capabilities?.streaming?.toValue(); // true +``` + +## Supported elements + +`AgentCard`, `AgentCapabilities`, `AgentExtension`, `AgentProvider`, `AgentInterface`, `AgentSkill`, `AgentCardSignature`, `SecurityRequirement`, `SecurityScheme` (wrapper) and its five concrete subtypes (`APIKey`, `HTTPAuth`, `MutualTls`, `OAuth2`, `OpenIdConnect`), `OAuthFlows` and its five flow subtypes (`AuthorizationCode`, `ClientCredentials`, `DeviceCode`, `Implicit`, `Password`), and `StringList`. + +## Implementation notes + +- **Source of truth.** A2A's normative spec is the [Protocol Buffers definition](https://github.com/a2aproject/A2A). The [JSON Schema bundle](https://a2a-protocol.org/latest/spec/a2a.json) used here is non-normative and machine-generated from the `.proto` files. Use the `.proto` to resolve ambiguities. + +- **camelCase canonicalisation.** A2A's JSON encoding allows both camelCase and snake_case property names (a protobuf JSON convention). Element classes expose camelCase getters/setters. Snake_case keys for the 28 dual-named fields in the A2A schema are canonicalised to camelCase by `refractor/canonicalize.ts` before refraction, so both spellings refract to the same tree. + +- **SecurityScheme is a wrapper.** The A2A schema models `SecurityScheme` as a protobuf `oneof` — a wrapper object with five named optional subfields (`apiKeySecurityScheme`, `httpAuthSecurityScheme`, `mtlsSecurityScheme`, `oauth2SecurityScheme`, `openIdConnectSecurityScheme`). It is not `type`-discriminated like OpenAPI's SecurityScheme. + +- **Scope.** This namespace models the AgentCard *document*. Wire-protocol messages (JSON-RPC requests, responses, errors; Task, Message, Artifact types) live in the same A2A schema but are not modelled here. + +- **Media types.** A2A has no IANA-registered media type. This namespace uses a `application/vnd.a2a;version=1.0.0` convention; revisit when/if A2A registers an official one. + +## License + +Apache-2.0 diff --git a/packages/apidom-ns-a2a-1/config/api-extractor/api-extractor.json b/packages/apidom-ns-a2a-1/config/api-extractor/api-extractor.json new file mode 100644 index 0000000000..7de0d99447 --- /dev/null +++ b/packages/apidom-ns-a2a-1/config/api-extractor/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "../../../../api-extractor.json" +} diff --git a/packages/apidom-ns-a2a-1/package.json b/packages/apidom-ns-a2a-1/package.json new file mode 100644 index 0000000000..699e3c0de3 --- /dev/null +++ b/packages/apidom-ns-a2a-1/package.json @@ -0,0 +1,61 @@ +{ + "name": "@swagger-api/apidom-ns-a2a-1", + "version": "1.11.1", + "description": "A2A (Agent-to-Agent) Protocol v1 namespace for ApiDOM.", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "type": "module", + "sideEffects": [ + "./src/refractor/registration.mjs", + "./src/refractor/registration.cjs" + ], + "unpkg": "./dist/apidom-ns-a2a-1.browser.min.js", + "main": "./src/index.cjs", + "exports": { + "types": "./types/apidom-ns-a2a-1.d.ts", + "import": "./src/index.mjs", + "require": "./src/index.cjs" + }, + "types": "./types/apidom-ns-a2a-1.d.ts", + "scripts": { + "build": "npm run clean && run-p --max-parallel ${CPU_CORES:-6} typescript:declaration build:es build:cjs build:umd:browser", + "build:es": "cross-env BABEL_ENV=es babel src --out-dir src --extensions '.ts' --out-file-extension '.mjs' --root-mode 'upward'", + "build:cjs": "cross-env BABEL_ENV=cjs babel src --out-dir src --extensions '.ts' --out-file-extension '.cjs' --root-mode 'upward'", + "build:umd:browser": "vite build", + "lint": "eslint ./", + "lint:fix": "eslint ./ --fix", + "clean": "rimraf --glob 'src/**/*.mjs' 'test/**/*.mjs' ./dist ./types", + "test": "NODE_ENV=test ts-mocha --exit", + "test:update-snapshots": "cross-env UPDATE_SNAPSHOT=1 BABEL_ENV=cjs mocha", + "typescript:check-types": "tsc --noEmit && tsc -p ./test/tsconfig.json --noEmit", + "typescript:declaration": "tsc -p tsconfig.declaration.json && api-extractor run -l -c ./config/api-extractor/api-extractor.json 2>&1 | shx grep -v 'Visitor_base'", + "prepack": "copyfiles -u 3 ../../LICENSES/* LICENSES && copyfiles -u 2 ../../NOTICE .", + "postpack": "rimraf NOTICE LICENSES" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/swagger-api/apidom.git" + }, + "author": "SmartBear", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.11.1", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" + }, + "files": [ + "src/**/*.mjs", + "src/**/*.cjs", + "dist/", + "types/apidom-ns-a2a-1.d.ts", + "LICENSES", + "NOTICE", + "README.md", + "CHANGELOG.md" + ] +} diff --git a/packages/apidom-ns-a2a-1/src/elements/APIKeySecurityScheme.ts b/packages/apidom-ns-a2a-1/src/elements/APIKeySecurityScheme.ts new file mode 100644 index 0000000000..65248b4b1e --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/APIKeySecurityScheme.ts @@ -0,0 +1,38 @@ +import { StringElement, ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class APIKeySecurityScheme extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'apiKeySecurityScheme'; + this.classes.push('api-key-security-scheme'); + } + + get description(): StringElement | undefined { + return this.get('description'); + } + + set description(description: StringElement | undefined) { + this.set('description', description); + } + + get name(): StringElement | undefined { + return this.get('name'); + } + + set name(name: StringElement | undefined) { + this.set('name', name); + } + + get location(): StringElement | undefined { + return this.get('location'); + } + + set location(location: StringElement | undefined) { + this.set('location', location); + } +} + +export default APIKeySecurityScheme; diff --git a/packages/apidom-ns-a2a-1/src/elements/AgentCapabilities.ts b/packages/apidom-ns-a2a-1/src/elements/AgentCapabilities.ts new file mode 100644 index 0000000000..3eee845068 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/AgentCapabilities.ts @@ -0,0 +1,52 @@ +import { + ArrayElement, + BooleanElement, + ObjectElement, + Attributes, + Meta, +} from '@swagger-api/apidom-core'; + +/** + * @public + */ +class AgentCapabilities extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'agentCapabilities'; + this.classes.push('agent-capabilities'); + } + + get streaming(): BooleanElement | undefined { + return this.get('streaming'); + } + + set streaming(streaming: BooleanElement | undefined) { + this.set('streaming', streaming); + } + + get pushNotifications(): BooleanElement | undefined { + return this.get('pushNotifications'); + } + + set pushNotifications(pushNotifications: BooleanElement | undefined) { + this.set('pushNotifications', pushNotifications); + } + + get extendedAgentCard(): BooleanElement | undefined { + return this.get('extendedAgentCard'); + } + + set extendedAgentCard(extendedAgentCard: BooleanElement | undefined) { + this.set('extendedAgentCard', extendedAgentCard); + } + + get extensions(): ArrayElement | undefined { + return this.get('extensions'); + } + + set extensions(extensions: ArrayElement | undefined) { + this.set('extensions', extensions); + } +} + +export default AgentCapabilities; diff --git a/packages/apidom-ns-a2a-1/src/elements/AgentCard.ts b/packages/apidom-ns-a2a-1/src/elements/AgentCard.ts new file mode 100644 index 0000000000..4bd9be2dae --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/AgentCard.ts @@ -0,0 +1,146 @@ +import { + ArrayElement, + ObjectElement, + StringElement, + Attributes, + Meta, +} from '@swagger-api/apidom-core'; + +import AgentCapabilitiesElement from './AgentCapabilities.ts'; +import AgentProviderElement from './AgentProvider.ts'; + +/** + * @public + * + * Root element for an A2A Agent Card document. + */ +class AgentCard extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'agentCard'; + this.classes.push('api'); + this.classes.push('agent-card'); + } + + get name(): StringElement | undefined { + return this.get('name'); + } + + set name(name: StringElement | undefined) { + this.set('name', name); + } + + get description(): StringElement | undefined { + return this.get('description'); + } + + set description(description: StringElement | undefined) { + this.set('description', description); + } + + get url(): StringElement | undefined { + return this.get('url'); + } + + set url(url: StringElement | undefined) { + this.set('url', url); + } + + get version(): StringElement | undefined { + return this.get('version'); + } + + set version(version: StringElement | undefined) { + this.set('version', version); + } + + get iconUrl(): StringElement | undefined { + return this.get('iconUrl'); + } + + set iconUrl(iconUrl: StringElement | undefined) { + this.set('iconUrl', iconUrl); + } + + get documentationUrl(): StringElement | undefined { + return this.get('documentationUrl'); + } + + set documentationUrl(documentationUrl: StringElement | undefined) { + this.set('documentationUrl', documentationUrl); + } + + get provider(): AgentProviderElement | undefined { + return this.get('provider'); + } + + set provider(provider: AgentProviderElement | undefined) { + this.set('provider', provider); + } + + get capabilities(): AgentCapabilitiesElement | undefined { + return this.get('capabilities'); + } + + set capabilities(capabilities: AgentCapabilitiesElement | undefined) { + this.set('capabilities', capabilities); + } + + get defaultInputModes(): ArrayElement | undefined { + return this.get('defaultInputModes'); + } + + set defaultInputModes(defaultInputModes: ArrayElement | undefined) { + this.set('defaultInputModes', defaultInputModes); + } + + get defaultOutputModes(): ArrayElement | undefined { + return this.get('defaultOutputModes'); + } + + set defaultOutputModes(defaultOutputModes: ArrayElement | undefined) { + this.set('defaultOutputModes', defaultOutputModes); + } + + get supportedInterfaces(): ArrayElement | undefined { + return this.get('supportedInterfaces'); + } + + set supportedInterfaces(supportedInterfaces: ArrayElement | undefined) { + this.set('supportedInterfaces', supportedInterfaces); + } + + get skills(): ArrayElement | undefined { + return this.get('skills'); + } + + set skills(skills: ArrayElement | undefined) { + this.set('skills', skills); + } + + get securitySchemes(): ObjectElement | undefined { + return this.get('securitySchemes'); + } + + set securitySchemes(securitySchemes: ObjectElement | undefined) { + this.set('securitySchemes', securitySchemes); + } + + get securityRequirements(): ArrayElement | undefined { + return this.get('securityRequirements'); + } + + set securityRequirements(securityRequirements: ArrayElement | undefined) { + this.set('securityRequirements', securityRequirements); + } + + get signatures(): ArrayElement | undefined { + return this.get('signatures'); + } + + set signatures(signatures: ArrayElement | undefined) { + this.set('signatures', signatures); + } +} + +export default AgentCard; diff --git a/packages/apidom-ns-a2a-1/src/elements/AgentCardSignature.ts b/packages/apidom-ns-a2a-1/src/elements/AgentCardSignature.ts new file mode 100644 index 0000000000..bfe62571f0 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/AgentCardSignature.ts @@ -0,0 +1,38 @@ +import { StringElement, ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class AgentCardSignature extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'agentCardSignature'; + this.classes.push('agent-card-signature'); + } + + get protected(): StringElement | undefined { + return this.get('protected'); + } + + set protected(protectedValue: StringElement | undefined) { + this.set('protected', protectedValue); + } + + get signature(): StringElement | undefined { + return this.get('signature'); + } + + set signature(signature: StringElement | undefined) { + this.set('signature', signature); + } + + get header(): ObjectElement | undefined { + return this.get('header'); + } + + set header(header: ObjectElement | undefined) { + this.set('header', header); + } +} + +export default AgentCardSignature; diff --git a/packages/apidom-ns-a2a-1/src/elements/AgentExtension.ts b/packages/apidom-ns-a2a-1/src/elements/AgentExtension.ts new file mode 100644 index 0000000000..314affaf66 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/AgentExtension.ts @@ -0,0 +1,52 @@ +import { + StringElement, + BooleanElement, + ObjectElement, + Attributes, + Meta, +} from '@swagger-api/apidom-core'; + +/** + * @public + */ +class AgentExtension extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'agentExtension'; + this.classes.push('agent-extension'); + } + + get uri(): StringElement | undefined { + return this.get('uri'); + } + + set uri(uri: StringElement | undefined) { + this.set('uri', uri); + } + + get description(): StringElement | undefined { + return this.get('description'); + } + + set description(description: StringElement | undefined) { + this.set('description', description); + } + + get required(): BooleanElement | undefined { + return this.get('required'); + } + + set required(required: BooleanElement | undefined) { + this.set('required', required); + } + + get params(): ObjectElement | undefined { + return this.get('params'); + } + + set params(params: ObjectElement | undefined) { + this.set('params', params); + } +} + +export default AgentExtension; diff --git a/packages/apidom-ns-a2a-1/src/elements/AgentInterface.ts b/packages/apidom-ns-a2a-1/src/elements/AgentInterface.ts new file mode 100644 index 0000000000..1ec7f95880 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/AgentInterface.ts @@ -0,0 +1,46 @@ +import { StringElement, ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class AgentInterface extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'agentInterface'; + this.classes.push('agent-interface'); + } + + get url(): StringElement | undefined { + return this.get('url'); + } + + set url(url: StringElement | undefined) { + this.set('url', url); + } + + get protocolBinding(): StringElement | undefined { + return this.get('protocolBinding'); + } + + set protocolBinding(protocolBinding: StringElement | undefined) { + this.set('protocolBinding', protocolBinding); + } + + get protocolVersion(): StringElement | undefined { + return this.get('protocolVersion'); + } + + set protocolVersion(protocolVersion: StringElement | undefined) { + this.set('protocolVersion', protocolVersion); + } + + get tenant(): StringElement | undefined { + return this.get('tenant'); + } + + set tenant(tenant: StringElement | undefined) { + this.set('tenant', tenant); + } +} + +export default AgentInterface; diff --git a/packages/apidom-ns-a2a-1/src/elements/AgentProvider.ts b/packages/apidom-ns-a2a-1/src/elements/AgentProvider.ts new file mode 100644 index 0000000000..5289809f7e --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/AgentProvider.ts @@ -0,0 +1,30 @@ +import { StringElement, ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class AgentProvider extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'agentProvider'; + this.classes.push('agent-provider'); + } + + get organization(): StringElement | undefined { + return this.get('organization'); + } + + set organization(organization: StringElement | undefined) { + this.set('organization', organization); + } + + get url(): StringElement | undefined { + return this.get('url'); + } + + set url(url: StringElement | undefined) { + this.set('url', url); + } +} + +export default AgentProvider; diff --git a/packages/apidom-ns-a2a-1/src/elements/AgentSkill.ts b/packages/apidom-ns-a2a-1/src/elements/AgentSkill.ts new file mode 100644 index 0000000000..745b55156c --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/AgentSkill.ts @@ -0,0 +1,76 @@ +import { + ArrayElement, + ObjectElement, + StringElement, + Attributes, + Meta, +} from '@swagger-api/apidom-core'; + +/** + * @public + */ +class AgentSkill extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'agentSkill'; + this.classes.push('agent-skill'); + } + + get name(): StringElement | undefined { + return this.get('name'); + } + + set name(name: StringElement | undefined) { + this.set('name', name); + } + + get description(): StringElement | undefined { + return this.get('description'); + } + + set description(description: StringElement | undefined) { + this.set('description', description); + } + + get tags(): ArrayElement | undefined { + return this.get('tags'); + } + + set tags(tags: ArrayElement | undefined) { + this.set('tags', tags); + } + + get examples(): ArrayElement | undefined { + return this.get('examples'); + } + + set examples(examples: ArrayElement | undefined) { + this.set('examples', examples); + } + + get inputModes(): ArrayElement | undefined { + return this.get('inputModes'); + } + + set inputModes(inputModes: ArrayElement | undefined) { + this.set('inputModes', inputModes); + } + + get outputModes(): ArrayElement | undefined { + return this.get('outputModes'); + } + + set outputModes(outputModes: ArrayElement | undefined) { + this.set('outputModes', outputModes); + } + + get securityRequirements(): ArrayElement | undefined { + return this.get('securityRequirements'); + } + + set securityRequirements(securityRequirements: ArrayElement | undefined) { + this.set('securityRequirements', securityRequirements); + } +} + +export default AgentSkill; diff --git a/packages/apidom-ns-a2a-1/src/elements/AuthorizationCodeOAuthFlow.ts b/packages/apidom-ns-a2a-1/src/elements/AuthorizationCodeOAuthFlow.ts new file mode 100644 index 0000000000..d95463b19c --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/AuthorizationCodeOAuthFlow.ts @@ -0,0 +1,61 @@ +import { + BooleanElement, + ObjectElement, + StringElement, + Attributes, + Meta, +} from '@swagger-api/apidom-core'; + +/** + * @public + */ +class AuthorizationCodeOAuthFlow extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'authorizationCodeOAuthFlow'; + this.classes.push('authorization-code-oauth-flow'); + this.classes.push('oauth-flow'); + } + + get authorizationUrl(): StringElement | undefined { + return this.get('authorizationUrl'); + } + + set authorizationUrl(authorizationUrl: StringElement | undefined) { + this.set('authorizationUrl', authorizationUrl); + } + + get tokenUrl(): StringElement | undefined { + return this.get('tokenUrl'); + } + + set tokenUrl(tokenUrl: StringElement | undefined) { + this.set('tokenUrl', tokenUrl); + } + + get refreshUrl(): StringElement | undefined { + return this.get('refreshUrl'); + } + + set refreshUrl(refreshUrl: StringElement | undefined) { + this.set('refreshUrl', refreshUrl); + } + + get pkceRequired(): BooleanElement | undefined { + return this.get('pkceRequired'); + } + + set pkceRequired(pkceRequired: BooleanElement | undefined) { + this.set('pkceRequired', pkceRequired); + } + + get scopes(): ObjectElement | undefined { + return this.get('scopes'); + } + + set scopes(scopes: ObjectElement | undefined) { + this.set('scopes', scopes); + } +} + +export default AuthorizationCodeOAuthFlow; diff --git a/packages/apidom-ns-a2a-1/src/elements/ClientCredentialsOAuthFlow.ts b/packages/apidom-ns-a2a-1/src/elements/ClientCredentialsOAuthFlow.ts new file mode 100644 index 0000000000..d120168020 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/ClientCredentialsOAuthFlow.ts @@ -0,0 +1,39 @@ +import { ObjectElement, StringElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class ClientCredentialsOAuthFlow extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'clientCredentialsOAuthFlow'; + this.classes.push('client-credentials-oauth-flow'); + this.classes.push('oauth-flow'); + } + + get tokenUrl(): StringElement | undefined { + return this.get('tokenUrl'); + } + + set tokenUrl(tokenUrl: StringElement | undefined) { + this.set('tokenUrl', tokenUrl); + } + + get refreshUrl(): StringElement | undefined { + return this.get('refreshUrl'); + } + + set refreshUrl(refreshUrl: StringElement | undefined) { + this.set('refreshUrl', refreshUrl); + } + + get scopes(): ObjectElement | undefined { + return this.get('scopes'); + } + + set scopes(scopes: ObjectElement | undefined) { + this.set('scopes', scopes); + } +} + +export default ClientCredentialsOAuthFlow; diff --git a/packages/apidom-ns-a2a-1/src/elements/DeviceCodeOAuthFlow.ts b/packages/apidom-ns-a2a-1/src/elements/DeviceCodeOAuthFlow.ts new file mode 100644 index 0000000000..12b2e99675 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/DeviceCodeOAuthFlow.ts @@ -0,0 +1,47 @@ +import { ObjectElement, StringElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class DeviceCodeOAuthFlow extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'deviceCodeOAuthFlow'; + this.classes.push('device-code-oauth-flow'); + this.classes.push('oauth-flow'); + } + + get deviceAuthorizationUrl(): StringElement | undefined { + return this.get('deviceAuthorizationUrl'); + } + + set deviceAuthorizationUrl(deviceAuthorizationUrl: StringElement | undefined) { + this.set('deviceAuthorizationUrl', deviceAuthorizationUrl); + } + + get tokenUrl(): StringElement | undefined { + return this.get('tokenUrl'); + } + + set tokenUrl(tokenUrl: StringElement | undefined) { + this.set('tokenUrl', tokenUrl); + } + + get refreshUrl(): StringElement | undefined { + return this.get('refreshUrl'); + } + + set refreshUrl(refreshUrl: StringElement | undefined) { + this.set('refreshUrl', refreshUrl); + } + + get scopes(): ObjectElement | undefined { + return this.get('scopes'); + } + + set scopes(scopes: ObjectElement | undefined) { + this.set('scopes', scopes); + } +} + +export default DeviceCodeOAuthFlow; diff --git a/packages/apidom-ns-a2a-1/src/elements/HTTPAuthSecurityScheme.ts b/packages/apidom-ns-a2a-1/src/elements/HTTPAuthSecurityScheme.ts new file mode 100644 index 0000000000..5f6ea3a05b --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/HTTPAuthSecurityScheme.ts @@ -0,0 +1,38 @@ +import { StringElement, ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class HTTPAuthSecurityScheme extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'httpAuthSecurityScheme'; + this.classes.push('http-auth-security-scheme'); + } + + get description(): StringElement | undefined { + return this.get('description'); + } + + set description(description: StringElement | undefined) { + this.set('description', description); + } + + get scheme(): StringElement | undefined { + return this.get('scheme'); + } + + set scheme(scheme: StringElement | undefined) { + this.set('scheme', scheme); + } + + get bearerFormat(): StringElement | undefined { + return this.get('bearerFormat'); + } + + set bearerFormat(bearerFormat: StringElement | undefined) { + this.set('bearerFormat', bearerFormat); + } +} + +export default HTTPAuthSecurityScheme; diff --git a/packages/apidom-ns-a2a-1/src/elements/ImplicitOAuthFlow.ts b/packages/apidom-ns-a2a-1/src/elements/ImplicitOAuthFlow.ts new file mode 100644 index 0000000000..1a2bb22969 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/ImplicitOAuthFlow.ts @@ -0,0 +1,41 @@ +import { ObjectElement, StringElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + * + * Deprecated by A2A spec; use Authorization Code + PKCE instead. + */ +class ImplicitOAuthFlow extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'implicitOAuthFlow'; + this.classes.push('implicit-oauth-flow'); + this.classes.push('oauth-flow'); + } + + get authorizationUrl(): StringElement | undefined { + return this.get('authorizationUrl'); + } + + set authorizationUrl(authorizationUrl: StringElement | undefined) { + this.set('authorizationUrl', authorizationUrl); + } + + get refreshUrl(): StringElement | undefined { + return this.get('refreshUrl'); + } + + set refreshUrl(refreshUrl: StringElement | undefined) { + this.set('refreshUrl', refreshUrl); + } + + get scopes(): ObjectElement | undefined { + return this.get('scopes'); + } + + set scopes(scopes: ObjectElement | undefined) { + this.set('scopes', scopes); + } +} + +export default ImplicitOAuthFlow; diff --git a/packages/apidom-ns-a2a-1/src/elements/MutualTlsSecurityScheme.ts b/packages/apidom-ns-a2a-1/src/elements/MutualTlsSecurityScheme.ts new file mode 100644 index 0000000000..e94e437d69 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/MutualTlsSecurityScheme.ts @@ -0,0 +1,22 @@ +import { StringElement, ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class MutualTlsSecurityScheme extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'mutualTlsSecurityScheme'; + this.classes.push('mutual-tls-security-scheme'); + } + + get description(): StringElement | undefined { + return this.get('description'); + } + + set description(description: StringElement | undefined) { + this.set('description', description); + } +} + +export default MutualTlsSecurityScheme; diff --git a/packages/apidom-ns-a2a-1/src/elements/OAuth2SecurityScheme.ts b/packages/apidom-ns-a2a-1/src/elements/OAuth2SecurityScheme.ts new file mode 100644 index 0000000000..c39e4b1dd7 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/OAuth2SecurityScheme.ts @@ -0,0 +1,40 @@ +import { StringElement, ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +import OAuthFlowsElement from './OAuthFlows.ts'; + +/** + * @public + */ +class OAuth2SecurityScheme extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'oauth2SecurityScheme'; + this.classes.push('oauth2-security-scheme'); + } + + get description(): StringElement | undefined { + return this.get('description'); + } + + set description(description: StringElement | undefined) { + this.set('description', description); + } + + get flows(): OAuthFlowsElement | undefined { + return this.get('flows'); + } + + set flows(flows: OAuthFlowsElement | undefined) { + this.set('flows', flows); + } + + get oauth2MetadataUrl(): StringElement | undefined { + return this.get('oauth2MetadataUrl'); + } + + set oauth2MetadataUrl(oauth2MetadataUrl: StringElement | undefined) { + this.set('oauth2MetadataUrl', oauth2MetadataUrl); + } +} + +export default OAuth2SecurityScheme; diff --git a/packages/apidom-ns-a2a-1/src/elements/OAuthFlows.ts b/packages/apidom-ns-a2a-1/src/elements/OAuthFlows.ts new file mode 100644 index 0000000000..64ebe7e345 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/OAuthFlows.ts @@ -0,0 +1,63 @@ +import { ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +import AuthorizationCodeOAuthFlowElement from './AuthorizationCodeOAuthFlow.ts'; +import ClientCredentialsOAuthFlowElement from './ClientCredentialsOAuthFlow.ts'; +import DeviceCodeOAuthFlowElement from './DeviceCodeOAuthFlow.ts'; +import ImplicitOAuthFlowElement from './ImplicitOAuthFlow.ts'; +import PasswordOAuthFlowElement from './PasswordOAuthFlow.ts'; + +/** + * @public + * + * Container for the five OAuth 2.0 flow configurations. Any subset may be + * populated at once (this is not a discriminated union). + */ +class OAuthFlows extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'oauthFlows'; + this.classes.push('oauth-flows'); + } + + get authorizationCode(): AuthorizationCodeOAuthFlowElement | undefined { + return this.get('authorizationCode'); + } + + set authorizationCode(authorizationCode: AuthorizationCodeOAuthFlowElement | undefined) { + this.set('authorizationCode', authorizationCode); + } + + get clientCredentials(): ClientCredentialsOAuthFlowElement | undefined { + return this.get('clientCredentials'); + } + + set clientCredentials(clientCredentials: ClientCredentialsOAuthFlowElement | undefined) { + this.set('clientCredentials', clientCredentials); + } + + get deviceCode(): DeviceCodeOAuthFlowElement | undefined { + return this.get('deviceCode'); + } + + set deviceCode(deviceCode: DeviceCodeOAuthFlowElement | undefined) { + this.set('deviceCode', deviceCode); + } + + get implicit(): ImplicitOAuthFlowElement | undefined { + return this.get('implicit'); + } + + set implicit(implicit: ImplicitOAuthFlowElement | undefined) { + this.set('implicit', implicit); + } + + get password(): PasswordOAuthFlowElement | undefined { + return this.get('password'); + } + + set password(password: PasswordOAuthFlowElement | undefined) { + this.set('password', password); + } +} + +export default OAuthFlows; diff --git a/packages/apidom-ns-a2a-1/src/elements/OpenIdConnectSecurityScheme.ts b/packages/apidom-ns-a2a-1/src/elements/OpenIdConnectSecurityScheme.ts new file mode 100644 index 0000000000..437fbf1181 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/OpenIdConnectSecurityScheme.ts @@ -0,0 +1,30 @@ +import { StringElement, ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class OpenIdConnectSecurityScheme extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'openIdConnectSecurityScheme'; + this.classes.push('open-id-connect-security-scheme'); + } + + get description(): StringElement | undefined { + return this.get('description'); + } + + set description(description: StringElement | undefined) { + this.set('description', description); + } + + get openIdConnectUrl(): StringElement | undefined { + return this.get('openIdConnectUrl'); + } + + set openIdConnectUrl(openIdConnectUrl: StringElement | undefined) { + this.set('openIdConnectUrl', openIdConnectUrl); + } +} + +export default OpenIdConnectSecurityScheme; diff --git a/packages/apidom-ns-a2a-1/src/elements/PasswordOAuthFlow.ts b/packages/apidom-ns-a2a-1/src/elements/PasswordOAuthFlow.ts new file mode 100644 index 0000000000..044067ae44 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/PasswordOAuthFlow.ts @@ -0,0 +1,41 @@ +import { ObjectElement, StringElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + * + * Deprecated by A2A spec; use Authorization Code + PKCE or Device Code. + */ +class PasswordOAuthFlow extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'passwordOAuthFlow'; + this.classes.push('password-oauth-flow'); + this.classes.push('oauth-flow'); + } + + get tokenUrl(): StringElement | undefined { + return this.get('tokenUrl'); + } + + set tokenUrl(tokenUrl: StringElement | undefined) { + this.set('tokenUrl', tokenUrl); + } + + get refreshUrl(): StringElement | undefined { + return this.get('refreshUrl'); + } + + set refreshUrl(refreshUrl: StringElement | undefined) { + this.set('refreshUrl', refreshUrl); + } + + get scopes(): ObjectElement | undefined { + return this.get('scopes'); + } + + set scopes(scopes: ObjectElement | undefined) { + this.set('scopes', scopes); + } +} + +export default PasswordOAuthFlow; diff --git a/packages/apidom-ns-a2a-1/src/elements/SecurityRequirement.ts b/packages/apidom-ns-a2a-1/src/elements/SecurityRequirement.ts new file mode 100644 index 0000000000..c9b94fb4d4 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/SecurityRequirement.ts @@ -0,0 +1,25 @@ +import { ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + * + * The `schemes` map's keys are security scheme names; its values are + * StringList elements containing required scopes. + */ +class SecurityRequirement extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'securityRequirement'; + this.classes.push('security-requirement'); + } + + get schemes(): ObjectElement | undefined { + return this.get('schemes'); + } + + set schemes(schemes: ObjectElement | undefined) { + this.set('schemes', schemes); + } +} + +export default SecurityRequirement; diff --git a/packages/apidom-ns-a2a-1/src/elements/SecurityScheme.ts b/packages/apidom-ns-a2a-1/src/elements/SecurityScheme.ts new file mode 100644 index 0000000000..6307068b4d --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/SecurityScheme.ts @@ -0,0 +1,68 @@ +import { ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +import APIKeySecuritySchemeElement from './APIKeySecurityScheme.ts'; +import HTTPAuthSecuritySchemeElement from './HTTPAuthSecurityScheme.ts'; +import MutualTlsSecuritySchemeElement from './MutualTlsSecurityScheme.ts'; +import OAuth2SecuritySchemeElement from './OAuth2SecurityScheme.ts'; +import OpenIdConnectSecuritySchemeElement from './OpenIdConnectSecurityScheme.ts'; + +/** + * @public + * + * Wrapper object for one of five concrete security scheme types. The A2A + * schema models this as a protobuf `oneof` rather than a `type`-discriminated + * union: exactly one of the five subobject fields is expected to be present. + * No conditional visitor is needed — each field refracts to its specific + * concrete element type. + */ +class SecurityScheme extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'securityScheme'; + this.classes.push('security-scheme'); + } + + get apiKeySecurityScheme(): APIKeySecuritySchemeElement | undefined { + return this.get('apiKeySecurityScheme'); + } + + set apiKeySecurityScheme(apiKeySecurityScheme: APIKeySecuritySchemeElement | undefined) { + this.set('apiKeySecurityScheme', apiKeySecurityScheme); + } + + get httpAuthSecurityScheme(): HTTPAuthSecuritySchemeElement | undefined { + return this.get('httpAuthSecurityScheme'); + } + + set httpAuthSecurityScheme(httpAuthSecurityScheme: HTTPAuthSecuritySchemeElement | undefined) { + this.set('httpAuthSecurityScheme', httpAuthSecurityScheme); + } + + get mtlsSecurityScheme(): MutualTlsSecuritySchemeElement | undefined { + return this.get('mtlsSecurityScheme'); + } + + set mtlsSecurityScheme(mtlsSecurityScheme: MutualTlsSecuritySchemeElement | undefined) { + this.set('mtlsSecurityScheme', mtlsSecurityScheme); + } + + get oauth2SecurityScheme(): OAuth2SecuritySchemeElement | undefined { + return this.get('oauth2SecurityScheme'); + } + + set oauth2SecurityScheme(oauth2SecurityScheme: OAuth2SecuritySchemeElement | undefined) { + this.set('oauth2SecurityScheme', oauth2SecurityScheme); + } + + get openIdConnectSecurityScheme(): OpenIdConnectSecuritySchemeElement | undefined { + return this.get('openIdConnectSecurityScheme'); + } + + set openIdConnectSecurityScheme( + openIdConnectSecurityScheme: OpenIdConnectSecuritySchemeElement | undefined, + ) { + this.set('openIdConnectSecurityScheme', openIdConnectSecurityScheme); + } +} + +export default SecurityScheme; diff --git a/packages/apidom-ns-a2a-1/src/elements/StringList.ts b/packages/apidom-ns-a2a-1/src/elements/StringList.ts new file mode 100644 index 0000000000..6bf02f9a0b --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/StringList.ts @@ -0,0 +1,26 @@ +import { ArrayElement, ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + * + * A2A's StringList is a wrapper object with a single `list` array property + * (modelled this way in the schema to satisfy protobuf's repeated-string + * encoding inside a map value). + */ +class StringList extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'stringList'; + this.classes.push('string-list'); + } + + get list(): ArrayElement | undefined { + return this.get('list'); + } + + set list(list: ArrayElement | undefined) { + this.set('list', list); + } +} + +export default StringList; diff --git a/packages/apidom-ns-a2a-1/src/elements/nces/Extensions.ts b/packages/apidom-ns-a2a-1/src/elements/nces/Extensions.ts new file mode 100644 index 0000000000..9bab3bc33b --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/nces/Extensions.ts @@ -0,0 +1,15 @@ +import { ArrayElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class Extensions extends ArrayElement { + static primaryClass = 'extensions'; + + constructor(content?: Array, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.classes.push(Extensions.primaryClass); + } +} + +export default Extensions; diff --git a/packages/apidom-ns-a2a-1/src/elements/nces/SecurityRequirements.ts b/packages/apidom-ns-a2a-1/src/elements/nces/SecurityRequirements.ts new file mode 100644 index 0000000000..284d82104c --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/nces/SecurityRequirements.ts @@ -0,0 +1,15 @@ +import { ArrayElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class SecurityRequirements extends ArrayElement { + static primaryClass = 'security-requirements'; + + constructor(content?: Array, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.classes.push(SecurityRequirements.primaryClass); + } +} + +export default SecurityRequirements; diff --git a/packages/apidom-ns-a2a-1/src/elements/nces/SecuritySchemes.ts b/packages/apidom-ns-a2a-1/src/elements/nces/SecuritySchemes.ts new file mode 100644 index 0000000000..4b28cf0028 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/nces/SecuritySchemes.ts @@ -0,0 +1,15 @@ +import { ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class SecuritySchemes extends ObjectElement { + static primaryClass = 'security-schemes'; + + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.classes.push(SecuritySchemes.primaryClass); + } +} + +export default SecuritySchemes; diff --git a/packages/apidom-ns-a2a-1/src/elements/nces/Signatures.ts b/packages/apidom-ns-a2a-1/src/elements/nces/Signatures.ts new file mode 100644 index 0000000000..1814f76112 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/nces/Signatures.ts @@ -0,0 +1,15 @@ +import { ArrayElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class Signatures extends ArrayElement { + static primaryClass = 'signatures'; + + constructor(content?: Array, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.classes.push(Signatures.primaryClass); + } +} + +export default Signatures; diff --git a/packages/apidom-ns-a2a-1/src/elements/nces/Skills.ts b/packages/apidom-ns-a2a-1/src/elements/nces/Skills.ts new file mode 100644 index 0000000000..df22759736 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/nces/Skills.ts @@ -0,0 +1,15 @@ +import { ArrayElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class Skills extends ArrayElement { + static primaryClass = 'skills'; + + constructor(content?: Array, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.classes.push(Skills.primaryClass); + } +} + +export default Skills; diff --git a/packages/apidom-ns-a2a-1/src/elements/nces/SupportedInterfaces.ts b/packages/apidom-ns-a2a-1/src/elements/nces/SupportedInterfaces.ts new file mode 100644 index 0000000000..eb56f6afd4 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/elements/nces/SupportedInterfaces.ts @@ -0,0 +1,15 @@ +import { ArrayElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +/** + * @public + */ +class SupportedInterfaces extends ArrayElement { + static primaryClass = 'supported-interfaces'; + + constructor(content?: Array, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.classes.push(SupportedInterfaces.primaryClass); + } +} + +export default SupportedInterfaces; diff --git a/packages/apidom-ns-a2a-1/src/index.ts b/packages/apidom-ns-a2a-1/src/index.ts new file mode 100644 index 0000000000..fd5a412d0b --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/index.ts @@ -0,0 +1,225 @@ +export { + isRefElement, + isLinkElement as isLinkPrimitiveElement, + isMemberElement, + isObjectElement, + isArrayElement, + isBooleanElement, + isNullElement, + isElement, + isNumberElement, + isStringElement, +} from '@swagger-api/apidom-core'; + +export { default as mediaTypes, A2AMediaTypes } from './media-types.ts'; +export type { Format } from './media-types.ts'; + +// eslint-disable-next-line no-restricted-exports +export { default } from './namespace.ts'; + +export { default as refract, createRefractor } from './refractor/index.ts'; +export { default as specificationObj } from './refractor/specification.ts'; + +// Generic visitor base classes +export { default as FixedFieldsVisitor } from './refractor/visitors/generics/FixedFieldsVisitor.ts'; +export type { + FixedFieldsVisitorOptions, + SpecPath, +} from './refractor/visitors/generics/FixedFieldsVisitor.ts'; +export { default as MapVisitor } from './refractor/visitors/generics/MapVisitor.ts'; +export type { MapVisitorOptions } from './refractor/visitors/generics/MapVisitor.ts'; +export { default as PatternedFieldsVisitor } from './refractor/visitors/generics/PatternedFieldsVisitor.ts'; +export type { PatternedFieldsVisitorOptions } from './refractor/visitors/generics/PatternedFieldsVisitor.ts'; +export { default as FallbackVisitor } from './refractor/visitors/FallbackVisitor.ts'; +export type { FallbackVisitorOptions } from './refractor/visitors/FallbackVisitor.ts'; +export { default as SpecificationExtensionVisitor } from './refractor/visitors/SpecificationExtensionVisitor.ts'; +export type { SpecificationExtensionVisitorOptions } from './refractor/visitors/SpecificationExtensionVisitor.ts'; +export { default as SpecificationVisitor } from './refractor/visitors/SpecificationVisitor.ts'; +export type { SpecificationVisitorOptions } from './refractor/visitors/SpecificationVisitor.ts'; +export { default as Visitor } from './refractor/visitors/Visitor.ts'; +export type { VisitorOptions } from './refractor/visitors/Visitor.ts'; + +// A2A v1 element visitors (per-element FixedFields visitors) +export type { + default as AgentCardVisitor, + AgentCardVisitorOptions, +} from './refractor/visitors/a2a-1/agent-card/index.ts'; +export type { + default as AgentCapabilitiesVisitor, + AgentCapabilitiesVisitorOptions, +} from './refractor/visitors/a2a-1/agent-capabilities/index.ts'; +export type { + default as AgentExtensionVisitor, + AgentExtensionVisitorOptions, +} from './refractor/visitors/a2a-1/agent-extension/index.ts'; +export type { + default as AgentProviderVisitor, + AgentProviderVisitorOptions, +} from './refractor/visitors/a2a-1/agent-provider/index.ts'; +export type { + default as AgentInterfaceVisitor, + AgentInterfaceVisitorOptions, +} from './refractor/visitors/a2a-1/agent-interface/index.ts'; +export type { + default as AgentSkillVisitor, + AgentSkillVisitorOptions, +} from './refractor/visitors/a2a-1/agent-skill/index.ts'; +export type { + default as AgentCardSignatureVisitor, + AgentCardSignatureVisitorOptions, +} from './refractor/visitors/a2a-1/agent-card-signature/index.ts'; +export type { + default as SecurityRequirementVisitor, + SecurityRequirementVisitorOptions, +} from './refractor/visitors/a2a-1/security-requirement/index.ts'; +export type { + default as SecuritySchemeVisitor, + SecuritySchemeVisitorOptions, +} from './refractor/visitors/a2a-1/security-scheme/index.ts'; +export type { + default as APIKeySecuritySchemeVisitor, + APIKeySecuritySchemeVisitorOptions, +} from './refractor/visitors/a2a-1/api-key-security-scheme/index.ts'; +export type { + default as HTTPAuthSecuritySchemeVisitor, + HTTPAuthSecuritySchemeVisitorOptions, +} from './refractor/visitors/a2a-1/http-auth-security-scheme/index.ts'; +export type { + default as MutualTlsSecuritySchemeVisitor, + MutualTlsSecuritySchemeVisitorOptions, +} from './refractor/visitors/a2a-1/mutual-tls-security-scheme/index.ts'; +export type { + default as OAuth2SecuritySchemeVisitor, + OAuth2SecuritySchemeVisitorOptions, +} from './refractor/visitors/a2a-1/oauth2-security-scheme/index.ts'; +export type { + default as OpenIdConnectSecuritySchemeVisitor, + OpenIdConnectSecuritySchemeVisitorOptions, +} from './refractor/visitors/a2a-1/open-id-connect-security-scheme/index.ts'; +export type { + default as OAuthFlowsVisitor, + OAuthFlowsVisitorOptions, +} from './refractor/visitors/a2a-1/oauth-flows/index.ts'; +export type { + default as AuthorizationCodeOAuthFlowVisitor, + AuthorizationCodeOAuthFlowVisitorOptions, +} from './refractor/visitors/a2a-1/authorization-code-oauth-flow/index.ts'; +export type { + default as ClientCredentialsOAuthFlowVisitor, + ClientCredentialsOAuthFlowVisitorOptions, +} from './refractor/visitors/a2a-1/client-credentials-oauth-flow/index.ts'; +export type { + default as DeviceCodeOAuthFlowVisitor, + DeviceCodeOAuthFlowVisitorOptions, +} from './refractor/visitors/a2a-1/device-code-oauth-flow/index.ts'; +export type { + default as ImplicitOAuthFlowVisitor, + ImplicitOAuthFlowVisitorOptions, +} from './refractor/visitors/a2a-1/implicit-oauth-flow/index.ts'; +export type { + default as PasswordOAuthFlowVisitor, + PasswordOAuthFlowVisitorOptions, +} from './refractor/visitors/a2a-1/password-oauth-flow/index.ts'; +export type { + default as StringListVisitor, + StringListVisitorOptions, +} from './refractor/visitors/a2a-1/string-list/index.ts'; + +// Array/Map visitors +export type { + default as SkillsVisitor, + SkillsVisitorOptions, +} from './refractor/visitors/a2a-1/SkillsVisitor.ts'; +export type { + default as SignaturesVisitor, + SignaturesVisitorOptions, +} from './refractor/visitors/a2a-1/SignaturesVisitor.ts'; +export type { + default as SupportedInterfacesVisitor, + SupportedInterfacesVisitorOptions, +} from './refractor/visitors/a2a-1/SupportedInterfacesVisitor.ts'; +export type { + default as SecurityRequirementsVisitor, + SecurityRequirementsVisitorOptions, +} from './refractor/visitors/a2a-1/SecurityRequirementsVisitor.ts'; +export type { + default as ExtensionsVisitor, + ExtensionsVisitorOptions, +} from './refractor/visitors/a2a-1/ExtensionsVisitor.ts'; +export type { + default as SecuritySchemesVisitor, + SecuritySchemesVisitorOptions, +} from './refractor/visitors/a2a-1/SecuritySchemesVisitor.ts'; +export type { + default as SecurityRequirementSchemesVisitor, + SecurityRequirementSchemesVisitorOptions, +} from './refractor/visitors/a2a-1/SecurityRequirementSchemesVisitor.ts'; + +// Predicates +export { + isAgentCardElement, + isAgentCapabilitiesElement, + isAgentExtensionElement, + isAgentProviderElement, + isAgentInterfaceElement, + isAgentSkillElement, + isAgentCardSignatureElement, + isSecurityRequirementElement, + isSecuritySchemeElement, + isAPIKeySecuritySchemeElement, + isHTTPAuthSecuritySchemeElement, + isMutualTlsSecuritySchemeElement, + isOAuth2SecuritySchemeElement, + isOpenIdConnectSecuritySchemeElement, + isOAuthFlowsElement, + isAuthorizationCodeOAuthFlowElement, + isClientCredentialsOAuthFlowElement, + isDeviceCodeOAuthFlowElement, + isImplicitOAuthFlowElement, + isPasswordOAuthFlowElement, + isStringListElement, + isSkillsElement, + isSignaturesElement, + isSupportedInterfacesElement, + isSecurityRequirementsElement, + isExtensionsElement, + isSecuritySchemesElement, +} from './predicates.ts'; + +export { isA2ASpecificationExtension } from './refractor/predicates.ts'; + +export { keyMap, getNodeType } from './traversal/visitor.ts'; + +// NCE element classes (Named Collection Elements: ArrayElement / ObjectElement +// subclasses with distinct CSS classes for typed predicates) +export { default as SkillsElement } from './elements/nces/Skills.ts'; +export { default as SignaturesElement } from './elements/nces/Signatures.ts'; +export { default as SupportedInterfacesElement } from './elements/nces/SupportedInterfaces.ts'; +export { default as SecurityRequirementsElement } from './elements/nces/SecurityRequirements.ts'; +export { default as ExtensionsElement } from './elements/nces/Extensions.ts'; +export { default as SecuritySchemesElement } from './elements/nces/SecuritySchemes.ts'; + +// A2A v1 elements (with .refract attached via registration) +export { + AgentCardElement, + AgentCapabilitiesElement, + AgentExtensionElement, + AgentProviderElement, + AgentInterfaceElement, + AgentSkillElement, + AgentCardSignatureElement, + SecurityRequirementElement, + SecuritySchemeElement, + APIKeySecuritySchemeElement, + HTTPAuthSecuritySchemeElement, + MutualTlsSecuritySchemeElement, + OAuth2SecuritySchemeElement, + OpenIdConnectSecuritySchemeElement, + OAuthFlowsElement, + AuthorizationCodeOAuthFlowElement, + ClientCredentialsOAuthFlowElement, + DeviceCodeOAuthFlowElement, + ImplicitOAuthFlowElement, + PasswordOAuthFlowElement, + StringListElement, +} from './refractor/registration.ts'; diff --git a/packages/apidom-ns-a2a-1/src/media-types.ts b/packages/apidom-ns-a2a-1/src/media-types.ts new file mode 100644 index 0000000000..4ea3e92746 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/media-types.ts @@ -0,0 +1,44 @@ +import { last } from 'ramda'; +import { MediaTypes } from '@swagger-api/apidom-core'; + +/** + * @public + */ +export type Format = 'generic' | 'json' | 'yaml'; + +/** + * @public + * + * A2A has no IANA-registered media type. The values below follow the + * `application/vnd.{vendor}.{type};version={ver}` convention used by other + * ApiDOM namespaces, with `a2a` as the vendor tag. + */ +export class A2AMediaTypes extends MediaTypes { + filterByFormat(format: Format = 'generic') { + const effectiveFormat = format === 'generic' ? 'a2a;version' : format; + return this.filter((mediaType) => mediaType.includes(effectiveFormat)); + } + + findBy(version = '1.0.0', format: Format = 'generic') { + const search = + format === 'generic' ? `vnd.a2a;version=${version}` : `vnd.a2a+${format};version=${version}`; + const found = this.find((mediaType) => mediaType.includes(search)); + + return found || this.unknownMediaType; + } + + latest(format: Format = 'generic') { + return last(this.filterByFormat(format)) as string; + } +} + +/** + * @public + */ +const mediaTypes = new A2AMediaTypes( + 'application/vnd.a2a;version=1.0.0', + 'application/vnd.a2a+json;version=1.0.0', + 'application/vnd.a2a+yaml;version=1.0.0', +); + +export default mediaTypes; diff --git a/packages/apidom-ns-a2a-1/src/namespace.ts b/packages/apidom-ns-a2a-1/src/namespace.ts new file mode 100644 index 0000000000..db92e0610d --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/namespace.ts @@ -0,0 +1,58 @@ +import { NamespacePluginOptions } from '@swagger-api/apidom-core'; + +import AgentCardElement from './elements/AgentCard.ts'; +import AgentCapabilitiesElement from './elements/AgentCapabilities.ts'; +import AgentExtensionElement from './elements/AgentExtension.ts'; +import AgentProviderElement from './elements/AgentProvider.ts'; +import AgentInterfaceElement from './elements/AgentInterface.ts'; +import AgentSkillElement from './elements/AgentSkill.ts'; +import AgentCardSignatureElement from './elements/AgentCardSignature.ts'; +import SecurityRequirementElement from './elements/SecurityRequirement.ts'; +import SecuritySchemeElement from './elements/SecurityScheme.ts'; +import APIKeySecuritySchemeElement from './elements/APIKeySecurityScheme.ts'; +import HTTPAuthSecuritySchemeElement from './elements/HTTPAuthSecurityScheme.ts'; +import MutualTlsSecuritySchemeElement from './elements/MutualTlsSecurityScheme.ts'; +import OAuth2SecuritySchemeElement from './elements/OAuth2SecurityScheme.ts'; +import OpenIdConnectSecuritySchemeElement from './elements/OpenIdConnectSecurityScheme.ts'; +import OAuthFlowsElement from './elements/OAuthFlows.ts'; +import AuthorizationCodeOAuthFlowElement from './elements/AuthorizationCodeOAuthFlow.ts'; +import ClientCredentialsOAuthFlowElement from './elements/ClientCredentialsOAuthFlow.ts'; +import DeviceCodeOAuthFlowElement from './elements/DeviceCodeOAuthFlow.ts'; +import ImplicitOAuthFlowElement from './elements/ImplicitOAuthFlow.ts'; +import PasswordOAuthFlowElement from './elements/PasswordOAuthFlow.ts'; +import StringListElement from './elements/StringList.ts'; + +/** + * @public + */ +const a2a1 = { + namespace: (options: NamespacePluginOptions) => { + const { base } = options; + + base.register('agentCard', AgentCardElement); + base.register('agentCapabilities', AgentCapabilitiesElement); + base.register('agentExtension', AgentExtensionElement); + base.register('agentProvider', AgentProviderElement); + base.register('agentInterface', AgentInterfaceElement); + base.register('agentSkill', AgentSkillElement); + base.register('agentCardSignature', AgentCardSignatureElement); + base.register('securityRequirement', SecurityRequirementElement); + base.register('securityScheme', SecuritySchemeElement); + base.register('apiKeySecurityScheme', APIKeySecuritySchemeElement); + base.register('httpAuthSecurityScheme', HTTPAuthSecuritySchemeElement); + base.register('mutualTlsSecurityScheme', MutualTlsSecuritySchemeElement); + base.register('oauth2SecurityScheme', OAuth2SecuritySchemeElement); + base.register('openIdConnectSecurityScheme', OpenIdConnectSecuritySchemeElement); + base.register('oauthFlows', OAuthFlowsElement); + base.register('authorizationCodeOAuthFlow', AuthorizationCodeOAuthFlowElement); + base.register('clientCredentialsOAuthFlow', ClientCredentialsOAuthFlowElement); + base.register('deviceCodeOAuthFlow', DeviceCodeOAuthFlowElement); + base.register('implicitOAuthFlow', ImplicitOAuthFlowElement); + base.register('passwordOAuthFlow', PasswordOAuthFlowElement); + base.register('stringList', StringListElement); + + return base; + }, +}; + +export default a2a1; diff --git a/packages/apidom-ns-a2a-1/src/predicates.ts b/packages/apidom-ns-a2a-1/src/predicates.ts new file mode 100644 index 0000000000..b0f4176025 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/predicates.ts @@ -0,0 +1,408 @@ +import { createPredicate } from '@swagger-api/apidom-core'; + +import AgentCardElement from './elements/AgentCard.ts'; +import AgentCapabilitiesElement from './elements/AgentCapabilities.ts'; +import AgentExtensionElement from './elements/AgentExtension.ts'; +import AgentProviderElement from './elements/AgentProvider.ts'; +import AgentInterfaceElement from './elements/AgentInterface.ts'; +import AgentSkillElement from './elements/AgentSkill.ts'; +import AgentCardSignatureElement from './elements/AgentCardSignature.ts'; +import SecurityRequirementElement from './elements/SecurityRequirement.ts'; +import SecuritySchemeElement from './elements/SecurityScheme.ts'; +import APIKeySecuritySchemeElement from './elements/APIKeySecurityScheme.ts'; +import HTTPAuthSecuritySchemeElement from './elements/HTTPAuthSecurityScheme.ts'; +import MutualTlsSecuritySchemeElement from './elements/MutualTlsSecurityScheme.ts'; +import OAuth2SecuritySchemeElement from './elements/OAuth2SecurityScheme.ts'; +import OpenIdConnectSecuritySchemeElement from './elements/OpenIdConnectSecurityScheme.ts'; +import OAuthFlowsElement from './elements/OAuthFlows.ts'; +import AuthorizationCodeOAuthFlowElement from './elements/AuthorizationCodeOAuthFlow.ts'; +import ClientCredentialsOAuthFlowElement from './elements/ClientCredentialsOAuthFlow.ts'; +import DeviceCodeOAuthFlowElement from './elements/DeviceCodeOAuthFlow.ts'; +import ImplicitOAuthFlowElement from './elements/ImplicitOAuthFlow.ts'; +import PasswordOAuthFlowElement from './elements/PasswordOAuthFlow.ts'; +import StringListElement from './elements/StringList.ts'; +import SkillsElement from './elements/nces/Skills.ts'; +import SignaturesElement from './elements/nces/Signatures.ts'; +import SupportedInterfacesElement from './elements/nces/SupportedInterfaces.ts'; +import SecurityRequirementsElement from './elements/nces/SecurityRequirements.ts'; +import ExtensionsElement from './elements/nces/Extensions.ts'; +import SecuritySchemesElement from './elements/nces/SecuritySchemes.ts'; + +/** + * @public + */ +export const isAgentCardElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is AgentCardElement => + element instanceof AgentCardElement || + (hasBasicElementProps(element) && + isElementType('agentCard', element) && + primitiveEq('object', element) && + hasClass('api', element) && + hasClass('agent-card', element)); + }, +); + +/** + * @public + */ +export const isAgentCapabilitiesElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is AgentCapabilitiesElement => + element instanceof AgentCapabilitiesElement || + (hasBasicElementProps(element) && + isElementType('agentCapabilities', element) && + primitiveEq('object', element) && + hasClass('agent-capabilities', element)); + }, +); + +/** + * @public + */ +export const isAgentExtensionElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is AgentExtensionElement => + element instanceof AgentExtensionElement || + (hasBasicElementProps(element) && + isElementType('agentExtension', element) && + primitiveEq('object', element) && + hasClass('agent-extension', element)); + }, +); + +/** + * @public + */ +export const isAgentProviderElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is AgentProviderElement => + element instanceof AgentProviderElement || + (hasBasicElementProps(element) && + isElementType('agentProvider', element) && + primitiveEq('object', element) && + hasClass('agent-provider', element)); + }, +); + +/** + * @public + */ +export const isAgentInterfaceElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is AgentInterfaceElement => + element instanceof AgentInterfaceElement || + (hasBasicElementProps(element) && + isElementType('agentInterface', element) && + primitiveEq('object', element) && + hasClass('agent-interface', element)); + }, +); + +/** + * @public + */ +export const isAgentSkillElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is AgentSkillElement => + element instanceof AgentSkillElement || + (hasBasicElementProps(element) && + isElementType('agentSkill', element) && + primitiveEq('object', element) && + hasClass('agent-skill', element)); + }, +); + +/** + * @public + */ +export const isAgentCardSignatureElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is AgentCardSignatureElement => + element instanceof AgentCardSignatureElement || + (hasBasicElementProps(element) && + isElementType('agentCardSignature', element) && + primitiveEq('object', element) && + hasClass('agent-card-signature', element)); + }, +); + +/** + * @public + */ +export const isSecurityRequirementElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is SecurityRequirementElement => + element instanceof SecurityRequirementElement || + (hasBasicElementProps(element) && + isElementType('securityRequirement', element) && + primitiveEq('object', element) && + hasClass('security-requirement', element)); + }, +); + +/** + * @public + */ +export const isSecuritySchemeElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is SecuritySchemeElement => + element instanceof SecuritySchemeElement || + (hasBasicElementProps(element) && + isElementType('securityScheme', element) && + primitiveEq('object', element) && + hasClass('security-scheme', element)); + }, +); + +/** + * @public + */ +export const isAPIKeySecuritySchemeElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is APIKeySecuritySchemeElement => + element instanceof APIKeySecuritySchemeElement || + (hasBasicElementProps(element) && + isElementType('apiKeySecurityScheme', element) && + primitiveEq('object', element) && + hasClass('api-key-security-scheme', element)); + }, +); + +/** + * @public + */ +export const isHTTPAuthSecuritySchemeElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is HTTPAuthSecuritySchemeElement => + element instanceof HTTPAuthSecuritySchemeElement || + (hasBasicElementProps(element) && + isElementType('httpAuthSecurityScheme', element) && + primitiveEq('object', element) && + hasClass('http-auth-security-scheme', element)); + }, +); + +/** + * @public + */ +export const isMutualTlsSecuritySchemeElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is MutualTlsSecuritySchemeElement => + element instanceof MutualTlsSecuritySchemeElement || + (hasBasicElementProps(element) && + isElementType('mutualTlsSecurityScheme', element) && + primitiveEq('object', element) && + hasClass('mutual-tls-security-scheme', element)); + }, +); + +/** + * @public + */ +export const isOAuth2SecuritySchemeElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is OAuth2SecuritySchemeElement => + element instanceof OAuth2SecuritySchemeElement || + (hasBasicElementProps(element) && + isElementType('oauth2SecurityScheme', element) && + primitiveEq('object', element) && + hasClass('oauth2-security-scheme', element)); + }, +); + +/** + * @public + */ +export const isOpenIdConnectSecuritySchemeElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is OpenIdConnectSecuritySchemeElement => + element instanceof OpenIdConnectSecuritySchemeElement || + (hasBasicElementProps(element) && + isElementType('openIdConnectSecurityScheme', element) && + primitiveEq('object', element) && + hasClass('open-id-connect-security-scheme', element)); + }, +); + +/** + * @public + */ +export const isOAuthFlowsElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is OAuthFlowsElement => + element instanceof OAuthFlowsElement || + (hasBasicElementProps(element) && + isElementType('oauthFlows', element) && + primitiveEq('object', element) && + hasClass('oauth-flows', element)); + }, +); + +/** + * @public + */ +export const isAuthorizationCodeOAuthFlowElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is AuthorizationCodeOAuthFlowElement => + element instanceof AuthorizationCodeOAuthFlowElement || + (hasBasicElementProps(element) && + isElementType('authorizationCodeOAuthFlow', element) && + primitiveEq('object', element) && + hasClass('authorization-code-oauth-flow', element)); + }, +); + +/** + * @public + */ +export const isClientCredentialsOAuthFlowElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is ClientCredentialsOAuthFlowElement => + element instanceof ClientCredentialsOAuthFlowElement || + (hasBasicElementProps(element) && + isElementType('clientCredentialsOAuthFlow', element) && + primitiveEq('object', element) && + hasClass('client-credentials-oauth-flow', element)); + }, +); + +/** + * @public + */ +export const isDeviceCodeOAuthFlowElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is DeviceCodeOAuthFlowElement => + element instanceof DeviceCodeOAuthFlowElement || + (hasBasicElementProps(element) && + isElementType('deviceCodeOAuthFlow', element) && + primitiveEq('object', element) && + hasClass('device-code-oauth-flow', element)); + }, +); + +/** + * @public + */ +export const isImplicitOAuthFlowElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is ImplicitOAuthFlowElement => + element instanceof ImplicitOAuthFlowElement || + (hasBasicElementProps(element) && + isElementType('implicitOAuthFlow', element) && + primitiveEq('object', element) && + hasClass('implicit-oauth-flow', element)); + }, +); + +/** + * @public + */ +export const isPasswordOAuthFlowElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is PasswordOAuthFlowElement => + element instanceof PasswordOAuthFlowElement || + (hasBasicElementProps(element) && + isElementType('passwordOAuthFlow', element) && + primitiveEq('object', element) && + hasClass('password-oauth-flow', element)); + }, +); + +/** + * @public + */ +export const isStringListElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is StringListElement => + element instanceof StringListElement || + (hasBasicElementProps(element) && + isElementType('stringList', element) && + primitiveEq('object', element) && + hasClass('string-list', element)); + }, +); + +/** + * @public + */ +export const isSkillsElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is SkillsElement => + element instanceof SkillsElement || + (hasBasicElementProps(element) && + isElementType('array', element) && + primitiveEq('array', element) && + hasClass('skills', element)); + }, +); + +/** + * @public + */ +export const isSignaturesElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is SignaturesElement => + element instanceof SignaturesElement || + (hasBasicElementProps(element) && + isElementType('array', element) && + primitiveEq('array', element) && + hasClass('signatures', element)); + }, +); + +/** + * @public + */ +export const isSupportedInterfacesElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is SupportedInterfacesElement => + element instanceof SupportedInterfacesElement || + (hasBasicElementProps(element) && + isElementType('array', element) && + primitiveEq('array', element) && + hasClass('supported-interfaces', element)); + }, +); + +/** + * @public + */ +export const isSecurityRequirementsElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is SecurityRequirementsElement => + element instanceof SecurityRequirementsElement || + (hasBasicElementProps(element) && + isElementType('array', element) && + primitiveEq('array', element) && + hasClass('security-requirements', element)); + }, +); + +/** + * @public + */ +export const isExtensionsElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is ExtensionsElement => + element instanceof ExtensionsElement || + (hasBasicElementProps(element) && + isElementType('array', element) && + primitiveEq('array', element) && + hasClass('extensions', element)); + }, +); + +/** + * @public + */ +export const isSecuritySchemesElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { + return (element: unknown): element is SecuritySchemesElement => + element instanceof SecuritySchemesElement || + (hasBasicElementProps(element) && + isElementType('object', element) && + primitiveEq('object', element) && + hasClass('security-schemes', element)); + }, +); diff --git a/packages/apidom-ns-a2a-1/src/refractor/canonicalize.ts b/packages/apidom-ns-a2a-1/src/refractor/canonicalize.ts new file mode 100644 index 0000000000..e4e67584a9 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/canonicalize.ts @@ -0,0 +1,87 @@ +/** + * Snake_case → camelCase canonicalisation for A2A documents. + * + * A2A's JSON encoding allows both camelCase and snake_case keys because the + * spec is generated from protobuf, where the canonical form is snake_case + * but protobuf's JSON encoding canonicalises to camelCase. Tooling on either + * side of the conversion may emit either form. + * + * This module exposes a single function `canonicalizeKeys` that recursively + * rewrites known snake_case keys to their camelCase equivalents. It is + * applied to the raw input value *before* refraction, so the visitor pipeline + * downstream only ever sees camelCase keys. + * + * Note: the mapping is global rather than element-scoped. This works because + * every snake_case key listed below appears in only one logical position in + * the schema (e.g. `default_input_modes` appears only inside AgentCard / + * AgentSkill, never in unrelated contexts). If A2A ever introduces an + * ambiguous snake_case key, this module will need a contextual variant. + * + * @public + */ + +const SNAKE_TO_CAMEL: Record = { + // AgentCard + default_input_modes: 'defaultInputModes', + default_output_modes: 'defaultOutputModes', + documentation_url: 'documentationUrl', + icon_url: 'iconUrl', + security_requirements: 'securityRequirements', + security_schemes: 'securitySchemes', + supported_interfaces: 'supportedInterfaces', + // AgentCapabilities + extended_agent_card: 'extendedAgentCard', + push_notifications: 'pushNotifications', + // AgentInterface + protocol_binding: 'protocolBinding', + protocol_version: 'protocolVersion', + // AgentSkill + input_modes: 'inputModes', + output_modes: 'outputModes', + // HTTPAuthSecurityScheme + bearer_format: 'bearerFormat', + // OAuth2SecurityScheme + oauth2_metadata_url: 'oauth2MetadataUrl', + // OpenIdConnectSecurityScheme + open_id_connect_url: 'openIdConnectUrl', + // OAuth flow types (Authorization, ClientCredentials, DeviceCode, Implicit, Password) + authorization_url: 'authorizationUrl', + token_url: 'tokenUrl', + refresh_url: 'refreshUrl', + device_authorization_url: 'deviceAuthorizationUrl', + pkce_required: 'pkceRequired', + // OAuthFlows wrapper + authorization_code: 'authorizationCode', + client_credentials: 'clientCredentials', + device_code: 'deviceCode', + // SecurityScheme wrapper (protobuf oneof subfields) + api_key_security_scheme: 'apiKeySecurityScheme', + http_auth_security_scheme: 'httpAuthSecurityScheme', + mtls_security_scheme: 'mtlsSecurityScheme', + oauth2_security_scheme: 'oauth2SecurityScheme', + open_id_connect_security_scheme: 'openIdConnectSecurityScheme', +}; + +/** + * Recursively rewrite known snake_case keys to camelCase in a plain JS value. + * Pure: returns a new structure; the input is not mutated. No-op for keys + * not in the mapping table. + * + * @public + */ +export const canonicalizeKeys = (value: unknown): unknown => { + if (Array.isArray(value)) { + return value.map(canonicalizeKeys); + } + if (value !== null && typeof value === 'object') { + const result: Record = {}; + for (const [key, val] of Object.entries(value)) { + const canonicalKey = SNAKE_TO_CAMEL[key] ?? key; + result[canonicalKey] = canonicalizeKeys(val); + } + return result; + } + return value; +}; + +export default canonicalizeKeys; diff --git a/packages/apidom-ns-a2a-1/src/refractor/index.ts b/packages/apidom-ns-a2a-1/src/refractor/index.ts new file mode 100644 index 0000000000..7d8fcf92b6 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/index.ts @@ -0,0 +1,57 @@ +import { + visit, + Element, + dereference, + refract as baseRefract, + dispatchRefractorPlugins, + isElement, +} from '@swagger-api/apidom-core'; +import { path } from 'ramda'; + +import type VisitorClass from './visitors/Visitor.ts'; +import specification from './specification.ts'; +import { keyMap, getNodeType } from '../traversal/visitor.ts'; +import createToolbox from './toolbox.ts'; +import canonicalizeKeys from './canonicalize.ts'; + +/** + * @public + */ +const refract = ( + value: unknown, + { specPath = ['visitors', 'document', 'objects', 'AgentCard', '$visitor'], plugins = [] } = {}, +): T => { + // Canonicalise snake_case keys → camelCase on raw input. Skip if the value + // is already an ApiDOM Element (caller passed a generic-refracted tree). + const canonicalised = isElement(value) ? value : canonicalizeKeys(value); + const element = baseRefract(canonicalised); + const resolvedSpec = dereference(specification); + + /** + * This is where generic ApiDOM becomes semantic (namespace applied). + * We don't allow consumers to hook into this translation. + * Though we allow consumers to define their own plugins on already transformed ApiDOM. + */ + const RootVisitorClass = path(specPath, resolvedSpec) as typeof VisitorClass; + const rootVisitor = new RootVisitorClass({ specObj: resolvedSpec }); + + visit(element, rootVisitor); + + /** + * Running plugins visitors means extra single traversal === performance hit. + */ + return dispatchRefractorPlugins(rootVisitor.element, plugins, { + toolboxCreator: createToolbox, + visitorOptions: { keyMap, nodeTypeGetter: getNodeType }, + }) as T; +}; + +/** + * @public + */ +export const createRefractor = + (specPath: string[]) => + (value: unknown, options = {}) => + refract(value, { specPath, ...options }); + +export default refract; diff --git a/packages/apidom-ns-a2a-1/src/refractor/predicates.ts b/packages/apidom-ns-a2a-1/src/refractor/predicates.ts new file mode 100644 index 0000000000..ad183bcd8e --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/predicates.ts @@ -0,0 +1,23 @@ +import { startsWith } from 'ramda'; +import { + MemberElement, + ObjectElement, + isStringElement, + toValue, + isObjectElement, +} from '@swagger-api/apidom-core'; + +export interface ReferenceLikeElement extends ObjectElement { + hasKey: (value: '$ref') => true; +} + +/** + * @public + */ +export const isA2ASpecificationExtension = (element: MemberElement): boolean => { + return isStringElement(element.key) && startsWith('x-', toValue(element.key)); +}; + +export const isReferenceLikeElement = (element: unknown): element is ReferenceLikeElement => { + return isObjectElement(element) && element.hasKey('$ref'); +}; diff --git a/packages/apidom-ns-a2a-1/src/refractor/registration.ts b/packages/apidom-ns-a2a-1/src/refractor/registration.ts new file mode 100644 index 0000000000..cb9ccce3d0 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/registration.ts @@ -0,0 +1,194 @@ +import AgentCardElement from '../elements/AgentCard.ts'; +import AgentCapabilitiesElement from '../elements/AgentCapabilities.ts'; +import AgentExtensionElement from '../elements/AgentExtension.ts'; +import AgentProviderElement from '../elements/AgentProvider.ts'; +import AgentInterfaceElement from '../elements/AgentInterface.ts'; +import AgentSkillElement from '../elements/AgentSkill.ts'; +import AgentCardSignatureElement from '../elements/AgentCardSignature.ts'; +import SecurityRequirementElement from '../elements/SecurityRequirement.ts'; +import SecuritySchemeElement from '../elements/SecurityScheme.ts'; +import APIKeySecuritySchemeElement from '../elements/APIKeySecurityScheme.ts'; +import HTTPAuthSecuritySchemeElement from '../elements/HTTPAuthSecurityScheme.ts'; +import MutualTlsSecuritySchemeElement from '../elements/MutualTlsSecurityScheme.ts'; +import OAuth2SecuritySchemeElement from '../elements/OAuth2SecurityScheme.ts'; +import OpenIdConnectSecuritySchemeElement from '../elements/OpenIdConnectSecurityScheme.ts'; +import OAuthFlowsElement from '../elements/OAuthFlows.ts'; +import AuthorizationCodeOAuthFlowElement from '../elements/AuthorizationCodeOAuthFlow.ts'; +import ClientCredentialsOAuthFlowElement from '../elements/ClientCredentialsOAuthFlow.ts'; +import DeviceCodeOAuthFlowElement from '../elements/DeviceCodeOAuthFlow.ts'; +import ImplicitOAuthFlowElement from '../elements/ImplicitOAuthFlow.ts'; +import PasswordOAuthFlowElement from '../elements/PasswordOAuthFlow.ts'; +import StringListElement from '../elements/StringList.ts'; +import { createRefractor } from './index.ts'; + +AgentCardElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'AgentCard', + '$visitor', +]); +AgentCapabilitiesElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'AgentCapabilities', + '$visitor', +]); +AgentExtensionElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'AgentExtension', + '$visitor', +]); +AgentProviderElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'AgentProvider', + '$visitor', +]); +AgentInterfaceElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'AgentInterface', + '$visitor', +]); +AgentSkillElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'AgentSkill', + '$visitor', +]); +AgentCardSignatureElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'AgentCardSignature', + '$visitor', +]); +SecurityRequirementElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'SecurityRequirement', + '$visitor', +]); +SecuritySchemeElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'SecurityScheme', + '$visitor', +]); +APIKeySecuritySchemeElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'APIKeySecurityScheme', + '$visitor', +]); +HTTPAuthSecuritySchemeElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'HTTPAuthSecurityScheme', + '$visitor', +]); +MutualTlsSecuritySchemeElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'MutualTlsSecurityScheme', + '$visitor', +]); +OAuth2SecuritySchemeElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'OAuth2SecurityScheme', + '$visitor', +]); +OpenIdConnectSecuritySchemeElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'OpenIdConnectSecurityScheme', + '$visitor', +]); +OAuthFlowsElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'OAuthFlows', + '$visitor', +]); +AuthorizationCodeOAuthFlowElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'AuthorizationCodeOAuthFlow', + '$visitor', +]); +ClientCredentialsOAuthFlowElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'ClientCredentialsOAuthFlow', + '$visitor', +]); +DeviceCodeOAuthFlowElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'DeviceCodeOAuthFlow', + '$visitor', +]); +ImplicitOAuthFlowElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'ImplicitOAuthFlow', + '$visitor', +]); +PasswordOAuthFlowElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'PasswordOAuthFlow', + '$visitor', +]); +StringListElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'StringList', + '$visitor', +]); + +export { + AgentCardElement, + AgentCapabilitiesElement, + AgentExtensionElement, + AgentProviderElement, + AgentInterfaceElement, + AgentSkillElement, + AgentCardSignatureElement, + SecurityRequirementElement, + SecuritySchemeElement, + APIKeySecuritySchemeElement, + HTTPAuthSecuritySchemeElement, + MutualTlsSecuritySchemeElement, + OAuth2SecuritySchemeElement, + OpenIdConnectSecuritySchemeElement, + OAuthFlowsElement, + AuthorizationCodeOAuthFlowElement, + ClientCredentialsOAuthFlowElement, + DeviceCodeOAuthFlowElement, + ImplicitOAuthFlowElement, + PasswordOAuthFlowElement, + StringListElement, +}; diff --git a/packages/apidom-ns-a2a-1/src/refractor/specification.ts b/packages/apidom-ns-a2a-1/src/refractor/specification.ts new file mode 100644 index 0000000000..dfadfe5f93 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/specification.ts @@ -0,0 +1,253 @@ +import AgentCardVisitor from './visitors/a2a-1/agent-card/index.ts'; +import AgentCapabilitiesVisitor from './visitors/a2a-1/agent-capabilities/index.ts'; +import AgentExtensionVisitor from './visitors/a2a-1/agent-extension/index.ts'; +import AgentProviderVisitor from './visitors/a2a-1/agent-provider/index.ts'; +import AgentInterfaceVisitor from './visitors/a2a-1/agent-interface/index.ts'; +import AgentSkillVisitor from './visitors/a2a-1/agent-skill/index.ts'; +import AgentCardSignatureVisitor from './visitors/a2a-1/agent-card-signature/index.ts'; +import SecurityRequirementVisitor from './visitors/a2a-1/security-requirement/index.ts'; +import SecuritySchemeVisitor from './visitors/a2a-1/security-scheme/index.ts'; +import APIKeySecuritySchemeVisitor from './visitors/a2a-1/api-key-security-scheme/index.ts'; +import HTTPAuthSecuritySchemeVisitor from './visitors/a2a-1/http-auth-security-scheme/index.ts'; +import MutualTlsSecuritySchemeVisitor from './visitors/a2a-1/mutual-tls-security-scheme/index.ts'; +import OAuth2SecuritySchemeVisitor from './visitors/a2a-1/oauth2-security-scheme/index.ts'; +import OpenIdConnectSecuritySchemeVisitor from './visitors/a2a-1/open-id-connect-security-scheme/index.ts'; +import OAuthFlowsVisitor from './visitors/a2a-1/oauth-flows/index.ts'; +import AuthorizationCodeOAuthFlowVisitor from './visitors/a2a-1/authorization-code-oauth-flow/index.ts'; +import ClientCredentialsOAuthFlowVisitor from './visitors/a2a-1/client-credentials-oauth-flow/index.ts'; +import DeviceCodeOAuthFlowVisitor from './visitors/a2a-1/device-code-oauth-flow/index.ts'; +import ImplicitOAuthFlowVisitor from './visitors/a2a-1/implicit-oauth-flow/index.ts'; +import PasswordOAuthFlowVisitor from './visitors/a2a-1/password-oauth-flow/index.ts'; +import StringListVisitor from './visitors/a2a-1/string-list/index.ts'; +import SkillsVisitor from './visitors/a2a-1/SkillsVisitor.ts'; +import SignaturesVisitor from './visitors/a2a-1/SignaturesVisitor.ts'; +import SupportedInterfacesVisitor from './visitors/a2a-1/SupportedInterfacesVisitor.ts'; +import SecurityRequirementsVisitor from './visitors/a2a-1/SecurityRequirementsVisitor.ts'; +import ExtensionsVisitor from './visitors/a2a-1/ExtensionsVisitor.ts'; +import SecuritySchemesVisitor from './visitors/a2a-1/SecuritySchemesVisitor.ts'; +import SecurityRequirementSchemesVisitor from './visitors/a2a-1/SecurityRequirementSchemesVisitor.ts'; +import FallbackVisitor from './visitors/FallbackVisitor.ts'; + +/** + * Specification object allows us to have complete control over visitors + * when traversing the ApiDOM. + * Specification also allows us to create amended refractors from + * existing ones by manipulating it. + * + * Note: Specification object allows to use absolute internal JSON pointers. + * + * @public + * + * A2A's JSON encoding allows both camelCase and snake_case property names + * (a protobuf JSON convention). Snake_case canonicalisation is handled by + * `refractor/canonicalize.ts`, which runs before refraction — by the time + * the visitors here see the input, all known snake_case keys have been + * rewritten to camelCase. The fixed-field map below only needs camelCase + * entries. + */ +const specification = { + visitors: { + value: FallbackVisitor, + document: { + objects: { + AgentCard: { + $visitor: AgentCardVisitor, + fixedFields: { + name: { $ref: '#/visitors/value' }, + description: { $ref: '#/visitors/value' }, + url: { $ref: '#/visitors/value' }, + version: { $ref: '#/visitors/value' }, + iconUrl: { $ref: '#/visitors/value' }, + documentationUrl: { $ref: '#/visitors/value' }, + provider: { $ref: '#/visitors/document/objects/AgentProvider' }, + capabilities: { $ref: '#/visitors/document/objects/AgentCapabilities' }, + defaultInputModes: { $ref: '#/visitors/value' }, + defaultOutputModes: { $ref: '#/visitors/value' }, + supportedInterfaces: SupportedInterfacesVisitor, + skills: SkillsVisitor, + securitySchemes: SecuritySchemesVisitor, + securityRequirements: SecurityRequirementsVisitor, + signatures: SignaturesVisitor, + }, + }, + AgentCapabilities: { + $visitor: AgentCapabilitiesVisitor, + fixedFields: { + streaming: { $ref: '#/visitors/value' }, + pushNotifications: { $ref: '#/visitors/value' }, + extendedAgentCard: { $ref: '#/visitors/value' }, + extensions: ExtensionsVisitor, + }, + }, + AgentExtension: { + $visitor: AgentExtensionVisitor, + fixedFields: { + uri: { $ref: '#/visitors/value' }, + description: { $ref: '#/visitors/value' }, + required: { $ref: '#/visitors/value' }, + params: { $ref: '#/visitors/value' }, + }, + }, + AgentProvider: { + $visitor: AgentProviderVisitor, + fixedFields: { + organization: { $ref: '#/visitors/value' }, + url: { $ref: '#/visitors/value' }, + }, + }, + AgentInterface: { + $visitor: AgentInterfaceVisitor, + fixedFields: { + url: { $ref: '#/visitors/value' }, + protocolBinding: { $ref: '#/visitors/value' }, + protocolVersion: { $ref: '#/visitors/value' }, + tenant: { $ref: '#/visitors/value' }, + }, + }, + AgentSkill: { + $visitor: AgentSkillVisitor, + fixedFields: { + id: { $ref: '#/visitors/value' }, + name: { $ref: '#/visitors/value' }, + description: { $ref: '#/visitors/value' }, + tags: { $ref: '#/visitors/value' }, + examples: { $ref: '#/visitors/value' }, + inputModes: { $ref: '#/visitors/value' }, + outputModes: { $ref: '#/visitors/value' }, + securityRequirements: SecurityRequirementsVisitor, + }, + }, + AgentCardSignature: { + $visitor: AgentCardSignatureVisitor, + fixedFields: { + protected: { $ref: '#/visitors/value' }, + signature: { $ref: '#/visitors/value' }, + header: { $ref: '#/visitors/value' }, + }, + }, + SecurityRequirement: { + $visitor: SecurityRequirementVisitor, + fixedFields: { + schemes: SecurityRequirementSchemesVisitor, + }, + }, + SecurityScheme: { + $visitor: SecuritySchemeVisitor, + fixedFields: { + apiKeySecurityScheme: { $ref: '#/visitors/document/objects/APIKeySecurityScheme' }, + httpAuthSecurityScheme: { $ref: '#/visitors/document/objects/HTTPAuthSecurityScheme' }, + mtlsSecurityScheme: { $ref: '#/visitors/document/objects/MutualTlsSecurityScheme' }, + oauth2SecurityScheme: { $ref: '#/visitors/document/objects/OAuth2SecurityScheme' }, + openIdConnectSecurityScheme: { + $ref: '#/visitors/document/objects/OpenIdConnectSecurityScheme', + }, + }, + }, + APIKeySecurityScheme: { + $visitor: APIKeySecuritySchemeVisitor, + fixedFields: { + description: { $ref: '#/visitors/value' }, + name: { $ref: '#/visitors/value' }, + location: { $ref: '#/visitors/value' }, + }, + }, + HTTPAuthSecurityScheme: { + $visitor: HTTPAuthSecuritySchemeVisitor, + fixedFields: { + description: { $ref: '#/visitors/value' }, + scheme: { $ref: '#/visitors/value' }, + bearerFormat: { $ref: '#/visitors/value' }, + }, + }, + MutualTlsSecurityScheme: { + $visitor: MutualTlsSecuritySchemeVisitor, + fixedFields: { + description: { $ref: '#/visitors/value' }, + }, + }, + OAuth2SecurityScheme: { + $visitor: OAuth2SecuritySchemeVisitor, + fixedFields: { + description: { $ref: '#/visitors/value' }, + flows: { $ref: '#/visitors/document/objects/OAuthFlows' }, + oauth2MetadataUrl: { $ref: '#/visitors/value' }, + }, + }, + OpenIdConnectSecurityScheme: { + $visitor: OpenIdConnectSecuritySchemeVisitor, + fixedFields: { + description: { $ref: '#/visitors/value' }, + openIdConnectUrl: { $ref: '#/visitors/value' }, + }, + }, + OAuthFlows: { + $visitor: OAuthFlowsVisitor, + fixedFields: { + authorizationCode: { + $ref: '#/visitors/document/objects/AuthorizationCodeOAuthFlow', + }, + clientCredentials: { + $ref: '#/visitors/document/objects/ClientCredentialsOAuthFlow', + }, + deviceCode: { $ref: '#/visitors/document/objects/DeviceCodeOAuthFlow' }, + implicit: { $ref: '#/visitors/document/objects/ImplicitOAuthFlow' }, + password: { $ref: '#/visitors/document/objects/PasswordOAuthFlow' }, + }, + }, + AuthorizationCodeOAuthFlow: { + $visitor: AuthorizationCodeOAuthFlowVisitor, + fixedFields: { + authorizationUrl: { $ref: '#/visitors/value' }, + tokenUrl: { $ref: '#/visitors/value' }, + refreshUrl: { $ref: '#/visitors/value' }, + pkceRequired: { $ref: '#/visitors/value' }, + scopes: { $ref: '#/visitors/value' }, + }, + }, + ClientCredentialsOAuthFlow: { + $visitor: ClientCredentialsOAuthFlowVisitor, + fixedFields: { + tokenUrl: { $ref: '#/visitors/value' }, + refreshUrl: { $ref: '#/visitors/value' }, + scopes: { $ref: '#/visitors/value' }, + }, + }, + DeviceCodeOAuthFlow: { + $visitor: DeviceCodeOAuthFlowVisitor, + fixedFields: { + deviceAuthorizationUrl: { $ref: '#/visitors/value' }, + tokenUrl: { $ref: '#/visitors/value' }, + refreshUrl: { $ref: '#/visitors/value' }, + scopes: { $ref: '#/visitors/value' }, + }, + }, + ImplicitOAuthFlow: { + $visitor: ImplicitOAuthFlowVisitor, + fixedFields: { + authorizationUrl: { $ref: '#/visitors/value' }, + refreshUrl: { $ref: '#/visitors/value' }, + scopes: { $ref: '#/visitors/value' }, + }, + }, + PasswordOAuthFlow: { + $visitor: PasswordOAuthFlowVisitor, + fixedFields: { + tokenUrl: { $ref: '#/visitors/value' }, + refreshUrl: { $ref: '#/visitors/value' }, + scopes: { $ref: '#/visitors/value' }, + }, + }, + StringList: { + $visitor: StringListVisitor, + fixedFields: { + list: { $ref: '#/visitors/value' }, + }, + }, + }, + // A2A does not support `x-*` specification extensions (every object + // declares `additionalProperties: false`). No extension visitor block. + }, + }, +} as const; + +export default specification; diff --git a/packages/apidom-ns-a2a-1/src/refractor/toolbox.ts b/packages/apidom-ns-a2a-1/src/refractor/toolbox.ts new file mode 100644 index 0000000000..c5033ec026 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/toolbox.ts @@ -0,0 +1,18 @@ +import { createNamespace, isStringElement } from '@swagger-api/apidom-core'; + +import * as a2aPredicates from '../predicates.ts'; +import * as refractorPredicates from './predicates.ts'; +import a2aNamespace from '../namespace.ts'; + +const createToolbox = () => { + const namespace = createNamespace(a2aNamespace); + const predicates = { + ...refractorPredicates, + ...a2aPredicates, + isStringElement, + }; + + return { predicates, namespace }; +}; + +export default createToolbox; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/FallbackVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/FallbackVisitor.ts new file mode 100644 index 0000000000..2b3f9ab46c --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/FallbackVisitor.ts @@ -0,0 +1,24 @@ +import { Element, BREAK, cloneDeep } from '@swagger-api/apidom-core'; + +import Visitor, { VisitorOptions } from './Visitor.ts'; + +/** + * This visitor is responsible for falling back to current traversed element. + * Given ArazzoSpecificationVisitor expects ObjectElement to be traversed. If + * different Element is provided FallBackVisitor is responsible to assigning + * this Element as current element. + */ + +export type { VisitorOptions as FallbackVisitorOptions }; + +/** + * @public + */ +class FallbackVisitor extends Visitor { + enter(element: Element) { + this.element = cloneDeep(element); + return BREAK; + } +} + +export default FallbackVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/SpecificationExtensionVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/SpecificationExtensionVisitor.ts new file mode 100644 index 0000000000..c00bdefc09 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/SpecificationExtensionVisitor.ts @@ -0,0 +1,21 @@ +import { MemberElement, BREAK, cloneDeep } from '@swagger-api/apidom-core'; + +import SpecificationVisitor, { SpecificationVisitorOptions } from './SpecificationVisitor.ts'; + +export type { SpecificationVisitorOptions as SpecificationExtensionVisitorOptions }; + +/** + * @public + */ +class SpecificationExtensionVisitor extends SpecificationVisitor { + declare public element: MemberElement; + + MemberElement(memberElement: MemberElement) { + this.element = cloneDeep(memberElement); + this.element.classes.push('specification-extension'); + + return BREAK; + } +} + +export default SpecificationExtensionVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/SpecificationVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/SpecificationVisitor.ts new file mode 100644 index 0000000000..f089eebfdd --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/SpecificationVisitor.ts @@ -0,0 +1,79 @@ +import { pathSatisfies, path, pick } from 'ramda'; +import { isFunction } from 'ramda-adjunct'; +import { visit, cloneDeep } from '@swagger-api/apidom-core'; + +import Visitor, { VisitorOptions } from './Visitor.ts'; +import FallbackVisitor from './FallbackVisitor.ts'; +import type specification from '../specification.ts'; + +/** + * This is a base Type for every visitor that does + * internal look-ups to retrieve other child visitors. + * @public + */ +export interface SpecificationVisitorOptions extends VisitorOptions { + readonly specObj: typeof specification; +} + +/** + * @public + */ +class SpecificationVisitor extends Visitor { + protected readonly specObj: typeof specification; + + protected readonly passingOptionsNames = ['specObj']; + + constructor({ specObj, ...rest }: SpecificationVisitorOptions) { + super({ ...rest }); + this.specObj = specObj; + } + + retrievePassingOptions() { + return pick(this.passingOptionsNames as (keyof this)[], this); + } + + retrieveFixedFields(specPath: string[]) { + const fixedFields = path(['visitors', ...specPath, 'fixedFields'], this.specObj); + if (typeof fixedFields === 'object' && fixedFields !== null) { + return Object.keys(fixedFields); + } + return []; + } + + retrieveVisitor(specPath: string[]) { + if (pathSatisfies(isFunction, ['visitors', ...specPath], this.specObj)) { + return path(['visitors', ...specPath], this.specObj); + } + + return path(['visitors', ...specPath, '$visitor'], this.specObj); + } + + retrieveVisitorInstance(specPath: string[], options = {}): Visitor { + const passingOpts = this.retrievePassingOptions(); + const VisitorClz = this.retrieveVisitor(specPath) as typeof Visitor; + const visitorOpts = { ...passingOpts, ...options }; + + return new VisitorClz(visitorOpts); + } + + toRefractedElement(specPath: string[], element: any, options = {}) { + /** + * This is `Visitor shortcut`: mechanism for short-circuiting the traversal and replacing + * it by basic node cloning. + * + * Visiting the element is equivalent to cloning it if the prototype of a visitor + * is the same as the prototype of FallbackVisitor. If that's the case, we can avoid + * bootstrapping the traversal cycle for fields that don't require any special visiting. + */ + const visitor = this.retrieveVisitorInstance(specPath, options); + + if (visitor instanceof FallbackVisitor && visitor?.constructor === FallbackVisitor) { + return cloneDeep(element); + } + + visit(element, visitor, options); + return visitor.element; + } +} + +export default SpecificationVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/Visitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/Visitor.ts new file mode 100644 index 0000000000..31375ad09a --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/Visitor.ts @@ -0,0 +1,39 @@ +import { + Element, + ObjectElement, + assignSourceMap, + deepmerge, + hasElementSourceMap, +} from '@swagger-api/apidom-core'; + +/** + * @public + */ +export interface VisitorOptions {} + +/** + * @public + */ +class Visitor { + public element!: Element; + + constructor(options: VisitorOptions = {}) { + Object.assign(this, options); + } + + /* eslint-disable class-methods-use-this, no-param-reassign */ + public copyMetaAndAttributes(from: Element, to: Element) { + if (from.meta.length > 0 || to.meta.length > 0) { + to.meta = deepmerge(to.meta, from.meta) as ObjectElement; + } + if (hasElementSourceMap(from)) { + assignSourceMap(to, from); + } + if (from.attributes.length > 0 || from.meta.length > 0) { + to.attributes = deepmerge(to.attributes, from.attributes) as ObjectElement; // eslint-disable-line no-param-reassign + } + } + /* eslint-enable class-methods-use-this, no-param-reassign */ +} + +export default Visitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/ExtensionsVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/ExtensionsVisitor.ts new file mode 100644 index 0000000000..ec0fc8195b --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/ExtensionsVisitor.ts @@ -0,0 +1,40 @@ +import { Mixin } from 'ts-mixer'; +import { ArrayElement, Element, BREAK } from '@swagger-api/apidom-core'; +import ExtensionsElement from '../../../elements/nces/Extensions.ts'; + +import SpecificationVisitor, { SpecificationVisitorOptions } from '../SpecificationVisitor.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; + +/** + * @public + */ +export interface ExtensionsVisitorOptions + extends SpecificationVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class ExtensionsVisitor extends Mixin(SpecificationVisitor, FallbackVisitor) { + public readonly element: ExtensionsElement; + + constructor(options: ExtensionsVisitorOptions) { + super(options); + this.element = new ExtensionsElement(); + } + + ArrayElement(arrayElement: ArrayElement) { + arrayElement.forEach((item: Element): void => { + const specPath = ['document', 'objects', 'AgentExtension']; + const element = this.toRefractedElement(specPath, item); + + this.element.push(element); + }); + + this.copyMetaAndAttributes(arrayElement, this.element); + + return BREAK; + } +} + +export default ExtensionsVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementSchemesVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementSchemesVisitor.ts new file mode 100644 index 0000000000..a566c49102 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementSchemesVisitor.ts @@ -0,0 +1,31 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; +import { ObjectElement } from '@swagger-api/apidom-core'; + +import MapVisitor, { MapVisitorOptions, SpecPath } from '../generics/MapVisitor.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; + +/** + * @public + */ +export interface SecurityRequirementSchemesVisitorOptions + extends MapVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class SecurityRequirementSchemesVisitor extends Mixin(MapVisitor, FallbackVisitor) { + public readonly element: ObjectElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'StringList']>; + + constructor(options: SecurityRequirementSchemesVisitorOptions) { + super(options); + this.element = new ObjectElement(); + this.element.classes.push('security-requirement-schemes'); + this.specPath = always(['document', 'objects', 'StringList']); + } +} + +export default SecurityRequirementSchemesVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementsVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementsVisitor.ts new file mode 100644 index 0000000000..e163db0a3e --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementsVisitor.ts @@ -0,0 +1,40 @@ +import { Mixin } from 'ts-mixer'; +import { ArrayElement, Element, BREAK } from '@swagger-api/apidom-core'; +import SecurityRequirementsElement from '../../../elements/nces/SecurityRequirements.ts'; + +import SpecificationVisitor, { SpecificationVisitorOptions } from '../SpecificationVisitor.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; + +/** + * @public + */ +export interface SecurityRequirementsVisitorOptions + extends SpecificationVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class SecurityRequirementsVisitor extends Mixin(SpecificationVisitor, FallbackVisitor) { + public readonly element: SecurityRequirementsElement; + + constructor(options: SecurityRequirementsVisitorOptions) { + super(options); + this.element = new SecurityRequirementsElement(); + } + + ArrayElement(arrayElement: ArrayElement) { + arrayElement.forEach((item: Element): void => { + const specPath = ['document', 'objects', 'SecurityRequirement']; + const element = this.toRefractedElement(specPath, item); + + this.element.push(element); + }); + + this.copyMetaAndAttributes(arrayElement, this.element); + + return BREAK; + } +} + +export default SecurityRequirementsVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecuritySchemesVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecuritySchemesVisitor.ts new file mode 100644 index 0000000000..c612361a61 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecuritySchemesVisitor.ts @@ -0,0 +1,29 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; +import { ObjectElement } from '@swagger-api/apidom-core'; +import SecuritySchemesElement from '../../../elements/nces/SecuritySchemes.ts'; + +import MapVisitor, { MapVisitorOptions, SpecPath } from '../generics/MapVisitor.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; + +/** + * @public + */ +export interface SecuritySchemesVisitorOptions extends MapVisitorOptions, FallbackVisitorOptions {} + +/** + * @public + */ +class SecuritySchemesVisitor extends Mixin(MapVisitor, FallbackVisitor) { + public readonly element: SecuritySchemesElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'SecurityScheme']>; + + constructor(options: SecuritySchemesVisitorOptions) { + super(options); + this.element = new SecuritySchemesElement(); + this.specPath = always(['document', 'objects', 'SecurityScheme']); + } +} + +export default SecuritySchemesVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SignaturesVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SignaturesVisitor.ts new file mode 100644 index 0000000000..814129fa82 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SignaturesVisitor.ts @@ -0,0 +1,40 @@ +import { Mixin } from 'ts-mixer'; +import { ArrayElement, Element, BREAK } from '@swagger-api/apidom-core'; +import SignaturesElement from '../../../elements/nces/Signatures.ts'; + +import SpecificationVisitor, { SpecificationVisitorOptions } from '../SpecificationVisitor.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; + +/** + * @public + */ +export interface SignaturesVisitorOptions + extends SpecificationVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class SignaturesVisitor extends Mixin(SpecificationVisitor, FallbackVisitor) { + public readonly element: SignaturesElement; + + constructor(options: SignaturesVisitorOptions) { + super(options); + this.element = new SignaturesElement(); + } + + ArrayElement(arrayElement: ArrayElement) { + arrayElement.forEach((item: Element): void => { + const specPath = ['document', 'objects', 'AgentCardSignature']; + const element = this.toRefractedElement(specPath, item); + + this.element.push(element); + }); + + this.copyMetaAndAttributes(arrayElement, this.element); + + return BREAK; + } +} + +export default SignaturesVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SkillsVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SkillsVisitor.ts new file mode 100644 index 0000000000..6e39821af9 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SkillsVisitor.ts @@ -0,0 +1,38 @@ +import { Mixin } from 'ts-mixer'; +import { ArrayElement, Element, BREAK } from '@swagger-api/apidom-core'; +import SkillsElement from '../../../elements/nces/Skills.ts'; + +import SpecificationVisitor, { SpecificationVisitorOptions } from '../SpecificationVisitor.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; + +/** + * @public + */ +export interface SkillsVisitorOptions extends SpecificationVisitorOptions, FallbackVisitorOptions {} + +/** + * @public + */ +class SkillsVisitor extends Mixin(SpecificationVisitor, FallbackVisitor) { + public readonly element: SkillsElement; + + constructor(options: SkillsVisitorOptions) { + super(options); + this.element = new SkillsElement(); + } + + ArrayElement(arrayElement: ArrayElement) { + arrayElement.forEach((item: Element): void => { + const specPath = ['document', 'objects', 'AgentSkill']; + const element = this.toRefractedElement(specPath, item); + + this.element.push(element); + }); + + this.copyMetaAndAttributes(arrayElement, this.element); + + return BREAK; + } +} + +export default SkillsVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SupportedInterfacesVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SupportedInterfacesVisitor.ts new file mode 100644 index 0000000000..119c27743e --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SupportedInterfacesVisitor.ts @@ -0,0 +1,40 @@ +import { Mixin } from 'ts-mixer'; +import { ArrayElement, Element, BREAK } from '@swagger-api/apidom-core'; +import SupportedInterfacesElement from '../../../elements/nces/SupportedInterfaces.ts'; + +import SpecificationVisitor, { SpecificationVisitorOptions } from '../SpecificationVisitor.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; + +/** + * @public + */ +export interface SupportedInterfacesVisitorOptions + extends SpecificationVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class SupportedInterfacesVisitor extends Mixin(SpecificationVisitor, FallbackVisitor) { + public readonly element: SupportedInterfacesElement; + + constructor(options: SupportedInterfacesVisitorOptions) { + super(options); + this.element = new SupportedInterfacesElement(); + } + + ArrayElement(arrayElement: ArrayElement) { + arrayElement.forEach((item: Element): void => { + const specPath = ['document', 'objects', 'AgentInterface']; + const element = this.toRefractedElement(specPath, item); + + this.element.push(element); + }); + + this.copyMetaAndAttributes(arrayElement, this.element); + + return BREAK; + } +} + +export default SupportedInterfacesVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-capabilities/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-capabilities/index.ts new file mode 100644 index 0000000000..fb181c0ffa --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-capabilities/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import AgentCapabilitiesElement from '../../../../elements/AgentCapabilities.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface AgentCapabilitiesVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class AgentCapabilitiesVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: AgentCapabilitiesElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'AgentCapabilities']>; + + constructor(options: AgentCapabilitiesVisitorOptions) { + super(options); + this.element = new AgentCapabilitiesElement(); + this.specPath = always(['document', 'objects', 'AgentCapabilities']); + } +} + +export default AgentCapabilitiesVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-card-signature/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-card-signature/index.ts new file mode 100644 index 0000000000..d77223b25d --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-card-signature/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import AgentCardSignatureElement from '../../../../elements/AgentCardSignature.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface AgentCardSignatureVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class AgentCardSignatureVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: AgentCardSignatureElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'AgentCardSignature']>; + + constructor(options: AgentCardSignatureVisitorOptions) { + super(options); + this.element = new AgentCardSignatureElement(); + this.specPath = always(['document', 'objects', 'AgentCardSignature']); + } +} + +export default AgentCardSignatureVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-card/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-card/index.ts new file mode 100644 index 0000000000..2c08eb5358 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-card/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import AgentCardElement from '../../../../elements/AgentCard.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface AgentCardVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class AgentCardVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: AgentCardElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'AgentCard']>; + + constructor(options: AgentCardVisitorOptions) { + super(options); + this.element = new AgentCardElement(); + this.specPath = always(['document', 'objects', 'AgentCard']); + } +} + +export default AgentCardVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-extension/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-extension/index.ts new file mode 100644 index 0000000000..9200e210f9 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-extension/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import AgentExtensionElement from '../../../../elements/AgentExtension.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface AgentExtensionVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class AgentExtensionVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: AgentExtensionElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'AgentExtension']>; + + constructor(options: AgentExtensionVisitorOptions) { + super(options); + this.element = new AgentExtensionElement(); + this.specPath = always(['document', 'objects', 'AgentExtension']); + } +} + +export default AgentExtensionVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-interface/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-interface/index.ts new file mode 100644 index 0000000000..c2a6297b54 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-interface/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import AgentInterfaceElement from '../../../../elements/AgentInterface.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface AgentInterfaceVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class AgentInterfaceVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: AgentInterfaceElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'AgentInterface']>; + + constructor(options: AgentInterfaceVisitorOptions) { + super(options); + this.element = new AgentInterfaceElement(); + this.specPath = always(['document', 'objects', 'AgentInterface']); + } +} + +export default AgentInterfaceVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-provider/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-provider/index.ts new file mode 100644 index 0000000000..7e291076a0 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-provider/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import AgentProviderElement from '../../../../elements/AgentProvider.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface AgentProviderVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class AgentProviderVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: AgentProviderElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'AgentProvider']>; + + constructor(options: AgentProviderVisitorOptions) { + super(options); + this.element = new AgentProviderElement(); + this.specPath = always(['document', 'objects', 'AgentProvider']); + } +} + +export default AgentProviderVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-skill/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-skill/index.ts new file mode 100644 index 0000000000..09597647a3 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/agent-skill/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import AgentSkillElement from '../../../../elements/AgentSkill.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface AgentSkillVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class AgentSkillVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: AgentSkillElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'AgentSkill']>; + + constructor(options: AgentSkillVisitorOptions) { + super(options); + this.element = new AgentSkillElement(); + this.specPath = always(['document', 'objects', 'AgentSkill']); + } +} + +export default AgentSkillVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/api-key-security-scheme/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/api-key-security-scheme/index.ts new file mode 100644 index 0000000000..9cb7d74fbd --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/api-key-security-scheme/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import APIKeySecuritySchemeElement from '../../../../elements/APIKeySecurityScheme.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface APIKeySecuritySchemeVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class APIKeySecuritySchemeVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: APIKeySecuritySchemeElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'APIKeySecurityScheme']>; + + constructor(options: APIKeySecuritySchemeVisitorOptions) { + super(options); + this.element = new APIKeySecuritySchemeElement(); + this.specPath = always(['document', 'objects', 'APIKeySecurityScheme']); + } +} + +export default APIKeySecuritySchemeVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/authorization-code-oauth-flow/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/authorization-code-oauth-flow/index.ts new file mode 100644 index 0000000000..0787d13d37 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/authorization-code-oauth-flow/index.ts @@ -0,0 +1,35 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import AuthorizationCodeOAuthFlowElement from '../../../../elements/AuthorizationCodeOAuthFlow.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface AuthorizationCodeOAuthFlowVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class AuthorizationCodeOAuthFlowVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: AuthorizationCodeOAuthFlowElement; + + declare protected readonly specPath: SpecPath< + ['document', 'objects', 'AuthorizationCodeOAuthFlow'] + >; + + constructor(options: AuthorizationCodeOAuthFlowVisitorOptions) { + super(options); + this.element = new AuthorizationCodeOAuthFlowElement(); + this.specPath = always(['document', 'objects', 'AuthorizationCodeOAuthFlow']); + } +} + +export default AuthorizationCodeOAuthFlowVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/client-credentials-oauth-flow/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/client-credentials-oauth-flow/index.ts new file mode 100644 index 0000000000..9a784fb3d5 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/client-credentials-oauth-flow/index.ts @@ -0,0 +1,35 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import ClientCredentialsOAuthFlowElement from '../../../../elements/ClientCredentialsOAuthFlow.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface ClientCredentialsOAuthFlowVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class ClientCredentialsOAuthFlowVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: ClientCredentialsOAuthFlowElement; + + declare protected readonly specPath: SpecPath< + ['document', 'objects', 'ClientCredentialsOAuthFlow'] + >; + + constructor(options: ClientCredentialsOAuthFlowVisitorOptions) { + super(options); + this.element = new ClientCredentialsOAuthFlowElement(); + this.specPath = always(['document', 'objects', 'ClientCredentialsOAuthFlow']); + } +} + +export default ClientCredentialsOAuthFlowVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/device-code-oauth-flow/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/device-code-oauth-flow/index.ts new file mode 100644 index 0000000000..7ec4ee78f2 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/device-code-oauth-flow/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import DeviceCodeOAuthFlowElement from '../../../../elements/DeviceCodeOAuthFlow.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface DeviceCodeOAuthFlowVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class DeviceCodeOAuthFlowVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: DeviceCodeOAuthFlowElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'DeviceCodeOAuthFlow']>; + + constructor(options: DeviceCodeOAuthFlowVisitorOptions) { + super(options); + this.element = new DeviceCodeOAuthFlowElement(); + this.specPath = always(['document', 'objects', 'DeviceCodeOAuthFlow']); + } +} + +export default DeviceCodeOAuthFlowVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/http-auth-security-scheme/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/http-auth-security-scheme/index.ts new file mode 100644 index 0000000000..8dedc45989 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/http-auth-security-scheme/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import HTTPAuthSecuritySchemeElement from '../../../../elements/HTTPAuthSecurityScheme.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface HTTPAuthSecuritySchemeVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class HTTPAuthSecuritySchemeVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: HTTPAuthSecuritySchemeElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'HTTPAuthSecurityScheme']>; + + constructor(options: HTTPAuthSecuritySchemeVisitorOptions) { + super(options); + this.element = new HTTPAuthSecuritySchemeElement(); + this.specPath = always(['document', 'objects', 'HTTPAuthSecurityScheme']); + } +} + +export default HTTPAuthSecuritySchemeVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/implicit-oauth-flow/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/implicit-oauth-flow/index.ts new file mode 100644 index 0000000000..b3ff9847f1 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/implicit-oauth-flow/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import ImplicitOAuthFlowElement from '../../../../elements/ImplicitOAuthFlow.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface ImplicitOAuthFlowVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class ImplicitOAuthFlowVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: ImplicitOAuthFlowElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'ImplicitOAuthFlow']>; + + constructor(options: ImplicitOAuthFlowVisitorOptions) { + super(options); + this.element = new ImplicitOAuthFlowElement(); + this.specPath = always(['document', 'objects', 'ImplicitOAuthFlow']); + } +} + +export default ImplicitOAuthFlowVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/mutual-tls-security-scheme/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/mutual-tls-security-scheme/index.ts new file mode 100644 index 0000000000..50fd8838a4 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/mutual-tls-security-scheme/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import MutualTlsSecuritySchemeElement from '../../../../elements/MutualTlsSecurityScheme.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface MutualTlsSecuritySchemeVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class MutualTlsSecuritySchemeVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: MutualTlsSecuritySchemeElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'MutualTlsSecurityScheme']>; + + constructor(options: MutualTlsSecuritySchemeVisitorOptions) { + super(options); + this.element = new MutualTlsSecuritySchemeElement(); + this.specPath = always(['document', 'objects', 'MutualTlsSecurityScheme']); + } +} + +export default MutualTlsSecuritySchemeVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/oauth-flows/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/oauth-flows/index.ts new file mode 100644 index 0000000000..ee231587fa --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/oauth-flows/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import OAuthFlowsElement from '../../../../elements/OAuthFlows.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface OAuthFlowsVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class OAuthFlowsVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: OAuthFlowsElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'OAuthFlows']>; + + constructor(options: OAuthFlowsVisitorOptions) { + super(options); + this.element = new OAuthFlowsElement(); + this.specPath = always(['document', 'objects', 'OAuthFlows']); + } +} + +export default OAuthFlowsVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/oauth2-security-scheme/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/oauth2-security-scheme/index.ts new file mode 100644 index 0000000000..21861c083b --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/oauth2-security-scheme/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import OAuth2SecuritySchemeElement from '../../../../elements/OAuth2SecurityScheme.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface OAuth2SecuritySchemeVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class OAuth2SecuritySchemeVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: OAuth2SecuritySchemeElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'OAuth2SecurityScheme']>; + + constructor(options: OAuth2SecuritySchemeVisitorOptions) { + super(options); + this.element = new OAuth2SecuritySchemeElement(); + this.specPath = always(['document', 'objects', 'OAuth2SecurityScheme']); + } +} + +export default OAuth2SecuritySchemeVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/open-id-connect-security-scheme/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/open-id-connect-security-scheme/index.ts new file mode 100644 index 0000000000..b5f381aca9 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/open-id-connect-security-scheme/index.ts @@ -0,0 +1,35 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import OpenIdConnectSecuritySchemeElement from '../../../../elements/OpenIdConnectSecurityScheme.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface OpenIdConnectSecuritySchemeVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class OpenIdConnectSecuritySchemeVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: OpenIdConnectSecuritySchemeElement; + + declare protected readonly specPath: SpecPath< + ['document', 'objects', 'OpenIdConnectSecurityScheme'] + >; + + constructor(options: OpenIdConnectSecuritySchemeVisitorOptions) { + super(options); + this.element = new OpenIdConnectSecuritySchemeElement(); + this.specPath = always(['document', 'objects', 'OpenIdConnectSecurityScheme']); + } +} + +export default OpenIdConnectSecuritySchemeVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/password-oauth-flow/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/password-oauth-flow/index.ts new file mode 100644 index 0000000000..84a7698347 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/password-oauth-flow/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import PasswordOAuthFlowElement from '../../../../elements/PasswordOAuthFlow.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface PasswordOAuthFlowVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class PasswordOAuthFlowVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: PasswordOAuthFlowElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'PasswordOAuthFlow']>; + + constructor(options: PasswordOAuthFlowVisitorOptions) { + super(options); + this.element = new PasswordOAuthFlowElement(); + this.specPath = always(['document', 'objects', 'PasswordOAuthFlow']); + } +} + +export default PasswordOAuthFlowVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/security-requirement/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/security-requirement/index.ts new file mode 100644 index 0000000000..43f5c7b7d3 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/security-requirement/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import SecurityRequirementElement from '../../../../elements/SecurityRequirement.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface SecurityRequirementVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class SecurityRequirementVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: SecurityRequirementElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'SecurityRequirement']>; + + constructor(options: SecurityRequirementVisitorOptions) { + super(options); + this.element = new SecurityRequirementElement(); + this.specPath = always(['document', 'objects', 'SecurityRequirement']); + } +} + +export default SecurityRequirementVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/security-scheme/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/security-scheme/index.ts new file mode 100644 index 0000000000..b7b6057c55 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/security-scheme/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import SecuritySchemeElement from '../../../../elements/SecurityScheme.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface SecuritySchemeVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class SecuritySchemeVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: SecuritySchemeElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'SecurityScheme']>; + + constructor(options: SecuritySchemeVisitorOptions) { + super(options); + this.element = new SecuritySchemeElement(); + this.specPath = always(['document', 'objects', 'SecurityScheme']); + } +} + +export default SecuritySchemeVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/string-list/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/string-list/index.ts new file mode 100644 index 0000000000..70ce846007 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/string-list/index.ts @@ -0,0 +1,33 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; + +import StringListElement from '../../../../elements/StringList.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; +import FixedFieldsVisitor, { + FixedFieldsVisitorOptions, + SpecPath, +} from '../../generics/FixedFieldsVisitor.ts'; + +/** + * @public + */ +export interface StringListVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class StringListVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: StringListElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'StringList']>; + + constructor(options: StringListVisitorOptions) { + super(options); + this.element = new StringListElement(); + this.specPath = always(['document', 'objects', 'StringList']); + } +} + +export default StringListVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/generics/FixedFieldsVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/generics/FixedFieldsVisitor.ts new file mode 100644 index 0000000000..eb6785b4cc --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/generics/FixedFieldsVisitor.ts @@ -0,0 +1,96 @@ +import { + isStringElement, + MemberElement, + Element, + BREAK, + cloneDeep, + toValue, + ObjectElement, +} from '@swagger-api/apidom-core'; + +import SpecificationVisitor, { SpecificationVisitorOptions } from '../SpecificationVisitor.ts'; +import { isA2ASpecificationExtension } from '../../predicates.ts'; + +/** + * @public + */ +export type SpecPath = (element: unknown) => T; + +/** + * @public + */ +export interface FixedFieldsVisitorOptions extends SpecificationVisitorOptions { + readonly specPath: SpecPath; + readonly ignoredFields?: string[]; + readonly canSupportSpecificationExtensions?: boolean; + readonly specificationExtensionPredicate?: typeof isA2ASpecificationExtension; +} + +/** + * @public + */ +class FixedFieldsVisitor extends SpecificationVisitor { + protected specPath: SpecPath; + + protected ignoredFields: string[] = []; + + protected canSupportSpecificationExtensions: boolean = false; + + protected specificationExtensionPredicate = isA2ASpecificationExtension; + + constructor({ + specPath, + ignoredFields, + canSupportSpecificationExtensions, + specificationExtensionPredicate, + ...rest + }: FixedFieldsVisitorOptions) { + super({ ...rest }); + this.specPath = specPath; + this.ignoredFields = ignoredFields || []; + + if (typeof canSupportSpecificationExtensions === 'boolean') { + this.canSupportSpecificationExtensions = canSupportSpecificationExtensions; + } + if (typeof specificationExtensionPredicate === 'function') { + this.specificationExtensionPredicate = specificationExtensionPredicate; + } + } + + ObjectElement(objectElement: ObjectElement) { + const specPath = this.specPath(objectElement); + const fields = this.retrieveFixedFields(specPath); + + // @ts-ignore + objectElement.forEach((value: Element, key: Element, memberElement: MemberElement) => { + if ( + isStringElement(key) && + fields.includes(toValue(key)) && + !this.ignoredFields.includes(toValue(key)) + ) { + const fixedFieldElement = this.toRefractedElement( + [...specPath, 'fixedFields', toValue(key)], + value, + ); + const newMemberElement = new MemberElement(cloneDeep(key), fixedFieldElement); + this.copyMetaAndAttributes(memberElement, newMemberElement); + newMemberElement.classes.push('fixed-field'); + this.element.content.push(newMemberElement); + } else if ( + this.canSupportSpecificationExtensions && + this.specificationExtensionPredicate(memberElement) + ) { + const extensionElement = this.toRefractedElement(['document', 'extension'], memberElement); + this.element.content.push(extensionElement); + } else if (!this.ignoredFields.includes(toValue(key))) { + this.element.content.push(cloneDeep(memberElement)); + } + }); + + this.copyMetaAndAttributes(objectElement, this.element); + + return BREAK; + } +} + +export default FixedFieldsVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/generics/MapVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/generics/MapVisitor.ts new file mode 100644 index 0000000000..ef8f8f7398 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/generics/MapVisitor.ts @@ -0,0 +1,25 @@ +import { isNonEmptyString } from 'ramda-adjunct'; + +import PatternedFieldsVisitor, { + SpecPath, + PatternedFieldsVisitorOptions, +} from './PatternedFieldsVisitor.ts'; + +export type { SpecPath }; + +/** + * @public + */ +export interface MapVisitorOptions extends PatternedFieldsVisitorOptions {} + +/** + * @public + */ +class MapVisitor extends PatternedFieldsVisitor { + constructor(options: MapVisitorOptions) { + super(options); + this.fieldPatternPredicate = isNonEmptyString; + } +} + +export default MapVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/generics/PatternedFieldsVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/generics/PatternedFieldsVisitor.ts new file mode 100644 index 0000000000..27d84c9e40 --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/generics/PatternedFieldsVisitor.ts @@ -0,0 +1,95 @@ +import { F as stubFalse } from 'ramda'; +import { + ObjectElement, + Element, + MemberElement, + BREAK, + cloneDeep, + toValue, +} from '@swagger-api/apidom-core'; + +import type { SpecPath } from './FixedFieldsVisitor.ts'; +import SpecificationVisitor, { SpecificationVisitorOptions } from '../SpecificationVisitor.ts'; +import { isA2ASpecificationExtension } from '../../predicates.ts'; + +export type { SpecPath }; + +/** + * @public + */ +export interface PatternedFieldsVisitorOptions extends SpecificationVisitorOptions { + readonly specPath: SpecPath; + readonly ignoredFields?: string[]; + readonly fieldPatternPredicate?: (...args: unknown[]) => boolean; + readonly canSupportSpecificationExtensions?: boolean; + readonly specificationExtensionPredicate?: typeof isA2ASpecificationExtension; +} + +/** + * @public + */ +class PatternedFieldsVisitor extends SpecificationVisitor { + protected specPath: SpecPath; + + protected ignoredFields: string[]; + + protected fieldPatternPredicate: (value: unknown) => boolean = stubFalse; + + protected canSupportSpecificationExtensions: boolean = false; + + protected specificationExtensionPredicate = isA2ASpecificationExtension; + + constructor({ + specPath, + ignoredFields, + fieldPatternPredicate, + canSupportSpecificationExtensions, + specificationExtensionPredicate, + ...rest + }: PatternedFieldsVisitorOptions) { + super({ ...rest }); + this.specPath = specPath; + this.ignoredFields = ignoredFields || []; + + if (typeof fieldPatternPredicate === 'function') { + this.fieldPatternPredicate = fieldPatternPredicate; + } + if (typeof canSupportSpecificationExtensions === 'boolean') { + this.canSupportSpecificationExtensions = canSupportSpecificationExtensions; + } + if (typeof specificationExtensionPredicate === 'function') { + this.specificationExtensionPredicate = specificationExtensionPredicate; + } + } + + ObjectElement(objectElement: ObjectElement) { + // @ts-ignore + objectElement.forEach((value: Element, key: Element, memberElement: MemberElement) => { + if ( + this.canSupportSpecificationExtensions && + this.specificationExtensionPredicate(memberElement) + ) { + const extensionElement = this.toRefractedElement(['document', 'extension'], memberElement); + this.element.content.push(extensionElement); + } else if ( + !this.ignoredFields.includes(toValue(key)) && + this.fieldPatternPredicate(toValue(key)) + ) { + const specPath = this.specPath(value); + const patternedFieldElement = this.toRefractedElement(specPath, value); + const newMemberElement = new MemberElement(cloneDeep(key), patternedFieldElement); + this.copyMetaAndAttributes(memberElement, newMemberElement); + newMemberElement.classes.push('patterned-field'); + this.element.content.push(newMemberElement); + } else if (!this.ignoredFields.includes(toValue(key))) { + this.element.content.push(cloneDeep(memberElement)); + } + }); + + this.copyMetaAndAttributes(objectElement, this.element); + + return BREAK; + } +} + +export default PatternedFieldsVisitor; diff --git a/packages/apidom-ns-a2a-1/src/traversal/visitor.ts b/packages/apidom-ns-a2a-1/src/traversal/visitor.ts new file mode 100644 index 0000000000..f6d93c6b3b --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/traversal/visitor.ts @@ -0,0 +1,44 @@ +import { keyMap as keyMapBase, isElement, Element } from '@swagger-api/apidom-core'; + +/** + * @public + */ +export const getNodeType = (element: T): string | undefined => { + if (!isElement(element)) { + return undefined; + } + return `${element.element.charAt(0).toUpperCase() + element.element.slice(1)}Element`; +}; + +/** + * A2A Protocol v1.0 + * @public + */ +export const keyMap = { + AgentCardElement: ['content'], + AgentCapabilitiesElement: ['content'], + AgentExtensionElement: ['content'], + AgentProviderElement: ['content'], + AgentInterfaceElement: ['content'], + AgentSkillElement: ['content'], + AgentCardSignatureElement: ['content'], + SecurityRequirementElement: ['content'], + SecuritySchemeElement: ['content'], + // Note: keyMap keys must match the output of getNodeType (above), which + // upper-cases only the first character of `element.element`. For names that + // begin with an acronym (API, HTTP, OAuth) this differs from the natural + // PascalCase class name — keep the keyMap key matching getNodeType. + ApiKeySecuritySchemeElement: ['content'], + HttpAuthSecuritySchemeElement: ['content'], + MutualTlsSecuritySchemeElement: ['content'], + Oauth2SecuritySchemeElement: ['content'], + OpenIdConnectSecuritySchemeElement: ['content'], + OauthFlowsElement: ['content'], + AuthorizationCodeOAuthFlowElement: ['content'], + ClientCredentialsOAuthFlowElement: ['content'], + DeviceCodeOAuthFlowElement: ['content'], + ImplicitOAuthFlowElement: ['content'], + PasswordOAuthFlowElement: ['content'], + StringListElement: ['content'], + ...keyMapBase, +}; diff --git a/packages/apidom-ns-a2a-1/test/.eslintrc b/packages/apidom-ns-a2a-1/test/.eslintrc new file mode 100644 index 0000000000..5cc99718c6 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/.eslintrc @@ -0,0 +1,57 @@ +{ + "env": { + "mocha": true + }, + "globals": { + "document": true + }, + "plugins": [ + "mocha" + ], + "rules": { + "no-void": 0, + "no-underscore-dangle": 0, + "func-names": 0, + "prefer-arrow-callback": 0, + "no-array-constructor": 0, + "prefer-rest-params": 0, + "no-new-wrappers": 0, + "mocha/no-skipped-tests": 2, + "mocha/handle-done-callback": 2, + "mocha/valid-suite-description": 2, + "mocha/no-mocha-arrows": 2, + "mocha/no-hooks-for-single-case": 2, + "mocha/no-sibling-hooks": 2, + "mocha/no-top-level-hooks": 2, + "mocha/no-identical-title": 2, + "mocha/no-nested-tests": 2, + "mocha/no-exclusive-tests": 2, + "max-classes-per-file": 0, + "@typescript-eslint/no-unused-expressions": 0, + "import/no-relative-packages": 0, + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "variable", + "format": ["camelCase", "PascalCase", "UPPER_CASE"], + "leadingUnderscore": "forbid" + }, + { + "selector": "variable", + "format": null, + "filter": { + "regex": "^__dirname$", + "match": true + } + }, + { + "selector": "variable", + "format": null, + "filter": { + "regex": "^__filename$", + "match": true + } + } + ] + } +} diff --git a/packages/apidom-ns-a2a-1/test/fixtures/real-world-planner-agent.json b/packages/apidom-ns-a2a-1/test/fixtures/real-world-planner-agent.json new file mode 100644 index 0000000000..4df8eadde3 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/fixtures/real-world-planner-agent.json @@ -0,0 +1,32 @@ +{ + "name": "Langraph Planner Agent", + "description": "Helps breakdown a request in to actionable tasks", + "url": "http://localhost:10102/", + "version": "1.0.0", + "capabilities": { + "streaming": true, + "pushNotifications": true, + "stateTransitionHistory": false + }, + "defaultInputModes": [ + "text", + "text/plain" + ], + "defaultOutputModes": [ + "text", + "text/plain" + ], + "skills": [ + { + "id": "planner", + "name": "Task Planner", + "description": "Helps breakdown a request in to actionable tasks", + "tags": [ + "planner" + ], + "examples": [ + "Plan my business trip from San Francisco to London, submit an expense report" + ] + } + ] +} diff --git a/packages/apidom-ns-a2a-1/test/mocha-bootstrap.ts b/packages/apidom-ns-a2a-1/test/mocha-bootstrap.ts new file mode 100644 index 0000000000..aec560d03f --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/mocha-bootstrap.ts @@ -0,0 +1,11 @@ +import * as chai from 'chai'; +import { jestSnapshotPlugin, addSerializer } from 'mocha-chai-jest-snapshot'; + +// @ts-ignore +import * as jestApiDOMSerializer from '../../../scripts/jest-serializer-apidom.mjs'; +// @ts-ignore +import * as jestStringSerializer from '../../../scripts/jest-serializer-string.mjs'; + +chai.use(jestSnapshotPlugin()); +addSerializer(jestApiDOMSerializer); +addSerializer(jestStringSerializer); diff --git a/packages/apidom-ns-a2a-1/test/predicates.ts b/packages/apidom-ns-a2a-1/test/predicates.ts new file mode 100644 index 0000000000..3d883b6d3d --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/predicates.ts @@ -0,0 +1,166 @@ +import { assert } from 'chai'; + +import { + isAgentCardElement, + isAgentCapabilitiesElement, + isAgentExtensionElement, + isAgentProviderElement, + isAgentInterfaceElement, + isAgentSkillElement, + isAgentCardSignatureElement, + isSecurityRequirementElement, + isSecuritySchemeElement, + isAPIKeySecuritySchemeElement, + isHTTPAuthSecuritySchemeElement, + isMutualTlsSecuritySchemeElement, + isOAuth2SecuritySchemeElement, + isOpenIdConnectSecuritySchemeElement, + isOAuthFlowsElement, + isAuthorizationCodeOAuthFlowElement, + isClientCredentialsOAuthFlowElement, + isDeviceCodeOAuthFlowElement, + isImplicitOAuthFlowElement, + isPasswordOAuthFlowElement, + isStringListElement, + AgentCardElement, + AgentCapabilitiesElement, + AgentExtensionElement, + AgentProviderElement, + AgentInterfaceElement, + AgentSkillElement, + AgentCardSignatureElement, + SecurityRequirementElement, + SecuritySchemeElement, + APIKeySecuritySchemeElement, + HTTPAuthSecuritySchemeElement, + MutualTlsSecuritySchemeElement, + OAuth2SecuritySchemeElement, + OpenIdConnectSecuritySchemeElement, + OAuthFlowsElement, + AuthorizationCodeOAuthFlowElement, + ClientCredentialsOAuthFlowElement, + DeviceCodeOAuthFlowElement, + ImplicitOAuthFlowElement, + PasswordOAuthFlowElement, + StringListElement, +} from '../src/index.ts'; + +interface PredicateCase { + name: string; + predicate: (e: unknown) => boolean; + Cls: new () => unknown; +} + +const cases: PredicateCase[] = [ + { name: 'isAgentCardElement', predicate: isAgentCardElement, Cls: AgentCardElement }, + { + name: 'isAgentCapabilitiesElement', + predicate: isAgentCapabilitiesElement, + Cls: AgentCapabilitiesElement, + }, + { + name: 'isAgentExtensionElement', + predicate: isAgentExtensionElement, + Cls: AgentExtensionElement, + }, + { name: 'isAgentProviderElement', predicate: isAgentProviderElement, Cls: AgentProviderElement }, + { + name: 'isAgentInterfaceElement', + predicate: isAgentInterfaceElement, + Cls: AgentInterfaceElement, + }, + { name: 'isAgentSkillElement', predicate: isAgentSkillElement, Cls: AgentSkillElement }, + { + name: 'isAgentCardSignatureElement', + predicate: isAgentCardSignatureElement, + Cls: AgentCardSignatureElement, + }, + { + name: 'isSecurityRequirementElement', + predicate: isSecurityRequirementElement, + Cls: SecurityRequirementElement, + }, + { + name: 'isSecuritySchemeElement', + predicate: isSecuritySchemeElement, + Cls: SecuritySchemeElement, + }, + { + name: 'isAPIKeySecuritySchemeElement', + predicate: isAPIKeySecuritySchemeElement, + Cls: APIKeySecuritySchemeElement, + }, + { + name: 'isHTTPAuthSecuritySchemeElement', + predicate: isHTTPAuthSecuritySchemeElement, + Cls: HTTPAuthSecuritySchemeElement, + }, + { + name: 'isMutualTlsSecuritySchemeElement', + predicate: isMutualTlsSecuritySchemeElement, + Cls: MutualTlsSecuritySchemeElement, + }, + { + name: 'isOAuth2SecuritySchemeElement', + predicate: isOAuth2SecuritySchemeElement, + Cls: OAuth2SecuritySchemeElement, + }, + { + name: 'isOpenIdConnectSecuritySchemeElement', + predicate: isOpenIdConnectSecuritySchemeElement, + Cls: OpenIdConnectSecuritySchemeElement, + }, + { name: 'isOAuthFlowsElement', predicate: isOAuthFlowsElement, Cls: OAuthFlowsElement }, + { + name: 'isAuthorizationCodeOAuthFlowElement', + predicate: isAuthorizationCodeOAuthFlowElement, + Cls: AuthorizationCodeOAuthFlowElement, + }, + { + name: 'isClientCredentialsOAuthFlowElement', + predicate: isClientCredentialsOAuthFlowElement, + Cls: ClientCredentialsOAuthFlowElement, + }, + { + name: 'isDeviceCodeOAuthFlowElement', + predicate: isDeviceCodeOAuthFlowElement, + Cls: DeviceCodeOAuthFlowElement, + }, + { + name: 'isImplicitOAuthFlowElement', + predicate: isImplicitOAuthFlowElement, + Cls: ImplicitOAuthFlowElement, + }, + { + name: 'isPasswordOAuthFlowElement', + predicate: isPasswordOAuthFlowElement, + Cls: PasswordOAuthFlowElement, + }, + { name: 'isStringListElement', predicate: isStringListElement, Cls: StringListElement }, +]; + +describe('predicates', function () { + cases.forEach(({ name, predicate, Cls }) => { + context(name, function () { + context(`given matching ${Cls.name} instance`, function () { + specify('should return true', function () { + assert.isTrue(predicate(new Cls())); + }); + }); + + context('given non-matching values', function () { + specify('should return false for plain object', function () { + assert.isFalse(predicate({})); + }); + + specify('should return false for null', function () { + assert.isFalse(predicate(null)); + }); + + specify('should return false for undefined', function () { + assert.isFalse(predicate(undefined)); + }); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/canonicalize.ts b/packages/apidom-ns-a2a-1/test/refractor/canonicalize.ts new file mode 100644 index 0000000000..88f7b8ff56 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/canonicalize.ts @@ -0,0 +1,102 @@ +import { assert, expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { AgentCardElement, isAgentCardElement } from '../../src/index.ts'; + +describe('refractor', function () { + context('snake_case canonicalisation', function () { + specify('should refract snake_case keys identically to camelCase', function () { + const camelCase = AgentCardElement.refract({ + name: 'Demo', + url: 'https://demo.example', + version: '1.0.0', + iconUrl: 'https://demo.example/icon.png', + documentationUrl: 'https://demo.example/docs', + defaultInputModes: ['text/plain'], + defaultOutputModes: ['application/json'], + capabilities: { + streaming: true, + pushNotifications: false, + extendedAgentCard: false, + }, + supportedInterfaces: [ + { url: 'https://demo.example/grpc', protocolBinding: 'GRPC', protocolVersion: '1.0' }, + ], + skills: [ + { + id: 'skill-1', + name: 'Skill 1', + description: 'A skill', + inputModes: ['text/plain'], + outputModes: ['application/json'], + }, + ], + }); + + const snakeCase = AgentCardElement.refract({ + name: 'Demo', + url: 'https://demo.example', + version: '1.0.0', + icon_url: 'https://demo.example/icon.png', + documentation_url: 'https://demo.example/docs', + default_input_modes: ['text/plain'], + default_output_modes: ['application/json'], + capabilities: { + streaming: true, + push_notifications: false, + extended_agent_card: false, + }, + supported_interfaces: [ + { url: 'https://demo.example/grpc', protocol_binding: 'GRPC', protocol_version: '1.0' }, + ], + skills: [ + { + id: 'skill-1', + name: 'Skill 1', + description: 'A skill', + input_modes: ['text/plain'], + output_modes: ['application/json'], + }, + ], + }); + + assert.isTrue(isAgentCardElement(camelCase)); + assert.isTrue(isAgentCardElement(snakeCase)); + // sexprs is structural; both should produce the same semantic tree + expect(sexprs(snakeCase)).to.equal(sexprs(camelCase)); + }); + + specify('should canonicalise security scheme oneof subfields', function () { + const snake = AgentCardElement.refract({ + name: 'a', + url: 'https://x', + version: '1.0.0', + capabilities: {}, + defaultInputModes: [], + defaultOutputModes: [], + skills: [], + security_schemes: { + apiKey: { + api_key_security_scheme: { name: 'X-API-Key', location: 'header' }, + }, + }, + }); + const camel = AgentCardElement.refract({ + name: 'a', + url: 'https://x', + version: '1.0.0', + capabilities: {}, + defaultInputModes: [], + defaultOutputModes: [], + skills: [], + securitySchemes: { + apiKey: { + apiKeySecurityScheme: { name: 'X-API-Key', location: 'header' }, + }, + }, + }); + + expect(sexprs(snake)).to.equal(sexprs(camel)); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/APIKeySecurityScheme/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/APIKeySecurityScheme/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..9457d09a68 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/APIKeySecurityScheme/__snapshots__/index.ts.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements APIKeySecuritySchemeElement should refract to semantic ApiDOM tree 1`] = ` +(ApiKeySecuritySchemeElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/APIKeySecurityScheme/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/APIKeySecurityScheme/index.ts new file mode 100644 index 0000000000..5ffedc517c --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/APIKeySecurityScheme/index.ts @@ -0,0 +1,20 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { APIKeySecuritySchemeElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('APIKeySecuritySchemeElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = APIKeySecuritySchemeElement.refract({ + description: 'API key', + name: 'X-API-Key', + location: 'header', + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCapabilities/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCapabilities/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..7a7d3638e8 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCapabilities/__snapshots__/index.ts.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements AgentCapabilitiesElement should refract to semantic ApiDOM tree 1`] = ` +(AgentCapabilitiesElement + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (ArrayElement))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCapabilities/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCapabilities/index.ts new file mode 100644 index 0000000000..0e5520ac69 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCapabilities/index.ts @@ -0,0 +1,21 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { AgentCapabilitiesElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('AgentCapabilitiesElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = AgentCapabilitiesElement.refract({ + streaming: true, + pushNotifications: false, + extendedAgentCard: false, + extensions: [], + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..c7d3cb46f9 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/__snapshots__/index.ts.snap @@ -0,0 +1,120 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements AgentCardElement should refract a minimal agent card 1`] = ` +(AgentCardElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (AgentCapabilitiesElement + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (AgentSkillElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))))))) +`; + +exports[`refractor elements AgentCardElement should refract an agent card with security schemes and a signature 1`] = ` +(AgentCardElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (AgentCapabilitiesElement)) + (MemberElement + (StringElement) + (ArrayElement)) + (MemberElement + (StringElement) + (ArrayElement)) + (MemberElement + (StringElement) + (ArrayElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (SecuritySchemeElement + (MemberElement + (StringElement) + (ApiKeySecuritySchemeElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))))))) + (MemberElement + (StringElement) + (ArrayElement + (SecurityRequirementElement + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringListElement + (MemberElement + (StringElement) + (ArrayElement))))))))) + (MemberElement + (StringElement) + (ArrayElement + (AgentCardSignatureElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringElement)))))))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/index.ts new file mode 100644 index 0000000000..aa181c4354 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/index.ts @@ -0,0 +1,65 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { AgentCardElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('AgentCardElement', function () { + specify('should refract a minimal agent card', function () { + const element = AgentCardElement.refract({ + name: 'Demo Agent', + description: 'A demo A2A agent', + url: 'https://demo.example/a2a', + version: '1.0.0', + capabilities: { + streaming: true, + pushNotifications: false, + }, + defaultInputModes: ['text/plain'], + defaultOutputModes: ['application/json'], + skills: [ + { + id: 'lookup', + name: 'Lookup', + description: 'Looks up data', + tags: ['search'], + }, + ], + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + + specify('should refract an agent card with security schemes and a signature', function () { + const element = AgentCardElement.refract({ + name: 'Secure Agent', + version: '1.0.0', + url: 'https://secure.example/a2a', + capabilities: {}, + defaultInputModes: [], + defaultOutputModes: [], + skills: [], + securitySchemes: { + apiKeyAuth: { + apiKeySecurityScheme: { + name: 'X-API-Key', + location: 'header', + }, + }, + }, + securityRequirements: [{ schemes: { apiKeyAuth: { list: [] } } }], + signatures: [ + { + protected: 'eyJhbGciOiJFUzI1NiJ9', + signature: 'base64url-sig', + header: { kid: 'key-1' }, + }, + ], + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCardSignature/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCardSignature/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..7dd46761ee --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCardSignature/__snapshots__/index.ts.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements AgentCardSignatureElement should refract to semantic ApiDOM tree 1`] = ` +(AgentCardSignatureElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringElement))))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCardSignature/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCardSignature/index.ts new file mode 100644 index 0000000000..f206ad18bf --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCardSignature/index.ts @@ -0,0 +1,20 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { AgentCardSignatureElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('AgentCardSignatureElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = AgentCardSignatureElement.refract({ + protected: 'eyJ...', + signature: 'sig...', + header: { alg: 'ES256' }, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..c691590c25 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/__snapshots__/index.ts.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements AgentExtensionElement should refract to semantic ApiDOM tree 1`] = ` +(AgentExtensionElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (BooleanElement))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/index.ts new file mode 100644 index 0000000000..29a1537cfb --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/index.ts @@ -0,0 +1,20 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { AgentExtensionElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('AgentExtensionElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = AgentExtensionElement.refract({ + uri: 'urn:example:ext', + description: 'demo', + required: true, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..b427a2dbeb --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/__snapshots__/index.ts.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements AgentInterfaceElement should refract to semantic ApiDOM tree 1`] = ` +(AgentInterfaceElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/index.ts new file mode 100644 index 0000000000..847ed66694 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/index.ts @@ -0,0 +1,20 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { AgentInterfaceElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('AgentInterfaceElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = AgentInterfaceElement.refract({ + url: 'https://example.com/a2a', + protocolBinding: 'JSONRPC', + protocolVersion: '1.0', + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentProvider/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentProvider/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..0eee7e4ff2 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentProvider/__snapshots__/index.ts.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements AgentProviderElement should refract to semantic ApiDOM tree 1`] = ` +(AgentProviderElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentProvider/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentProvider/index.ts new file mode 100644 index 0000000000..9b95f4bcb4 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentProvider/index.ts @@ -0,0 +1,19 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { AgentProviderElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('AgentProviderElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = AgentProviderElement.refract({ + organization: 'Acme', + url: 'https://acme.example', + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..e6ec5d00c5 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/__snapshots__/index.ts.snap @@ -0,0 +1,30 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements AgentSkillElement should refract to semantic ApiDOM tree 1`] = ` +(AgentSkillElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement)))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/index.ts new file mode 100644 index 0000000000..d6223fda24 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/index.ts @@ -0,0 +1,24 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { AgentSkillElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('AgentSkillElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = AgentSkillElement.refract({ + id: 'sk-1', + name: 'Lookup', + description: 'Looks up data', + tags: ['data'], + examples: ['Find X'], + inputModes: ['text/plain'], + outputModes: ['application/json'], + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..ac0c4579aa --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/__snapshots__/index.ts.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements AuthorizationCodeOAuthFlowElement should refract to semantic ApiDOM tree 1`] = ` +(AuthorizationCodeOAuthFlowElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringElement))))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/index.ts new file mode 100644 index 0000000000..782b99c3c3 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/index.ts @@ -0,0 +1,21 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { AuthorizationCodeOAuthFlowElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('AuthorizationCodeOAuthFlowElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = AuthorizationCodeOAuthFlowElement.refract({ + authorizationUrl: 'https://idp.example/authorize', + tokenUrl: 'https://idp.example/token', + pkceRequired: true, + scopes: { read: 'Read access' }, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..79b0cda2ae --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/__snapshots__/index.ts.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements ClientCredentialsOAuthFlowElement should refract to semantic ApiDOM tree 1`] = ` +(ClientCredentialsOAuthFlowElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringElement))))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/index.ts new file mode 100644 index 0000000000..b162aca964 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/index.ts @@ -0,0 +1,19 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { ClientCredentialsOAuthFlowElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('ClientCredentialsOAuthFlowElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = ClientCredentialsOAuthFlowElement.refract({ + tokenUrl: 'https://idp.example/token', + scopes: { admin: 'Admin' }, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..7bde0d9a92 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/__snapshots__/index.ts.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements DeviceCodeOAuthFlowElement should refract to semantic ApiDOM tree 1`] = ` +(DeviceCodeOAuthFlowElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/index.ts new file mode 100644 index 0000000000..b215c51e10 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/index.ts @@ -0,0 +1,20 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { DeviceCodeOAuthFlowElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('DeviceCodeOAuthFlowElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = DeviceCodeOAuthFlowElement.refract({ + deviceAuthorizationUrl: 'https://idp.example/device', + tokenUrl: 'https://idp.example/token', + scopes: {}, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/HTTPAuthSecurityScheme/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/HTTPAuthSecurityScheme/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..07a39181e0 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/HTTPAuthSecurityScheme/__snapshots__/index.ts.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements HTTPAuthSecuritySchemeElement should refract to semantic ApiDOM tree 1`] = ` +(HttpAuthSecuritySchemeElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/HTTPAuthSecurityScheme/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/HTTPAuthSecurityScheme/index.ts new file mode 100644 index 0000000000..d625863837 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/HTTPAuthSecurityScheme/index.ts @@ -0,0 +1,20 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { HTTPAuthSecuritySchemeElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('HTTPAuthSecuritySchemeElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = HTTPAuthSecuritySchemeElement.refract({ + description: 'Bearer', + scheme: 'bearer', + bearerFormat: 'JWT', + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..9b4e98d552 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/__snapshots__/index.ts.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements ImplicitOAuthFlowElement should refract to semantic ApiDOM tree 1`] = ` +(ImplicitOAuthFlowElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/index.ts new file mode 100644 index 0000000000..9b6a8387fa --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/index.ts @@ -0,0 +1,19 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { ImplicitOAuthFlowElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('ImplicitOAuthFlowElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = ImplicitOAuthFlowElement.refract({ + authorizationUrl: 'https://idp.example/authorize', + scopes: {}, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/MutualTlsSecurityScheme/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/MutualTlsSecurityScheme/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..dcf01e9f06 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/MutualTlsSecurityScheme/__snapshots__/index.ts.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements MutualTlsSecuritySchemeElement should refract to semantic ApiDOM tree 1`] = ` +(MutualTlsSecuritySchemeElement + (MemberElement + (StringElement) + (StringElement))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/MutualTlsSecurityScheme/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/MutualTlsSecurityScheme/index.ts new file mode 100644 index 0000000000..88239b1916 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/MutualTlsSecurityScheme/index.ts @@ -0,0 +1,18 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { MutualTlsSecuritySchemeElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('MutualTlsSecuritySchemeElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = MutualTlsSecuritySchemeElement.refract({ + description: 'Client cert required', + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/OAuth2SecurityScheme/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuth2SecurityScheme/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..f46414c262 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuth2SecurityScheme/__snapshots__/index.ts.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements OAuth2SecuritySchemeElement should refract to semantic ApiDOM tree 1`] = ` +(Oauth2SecuritySchemeElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (OauthFlowsElement)) + (MemberElement + (StringElement) + (StringElement))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/OAuth2SecurityScheme/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuth2SecurityScheme/index.ts new file mode 100644 index 0000000000..95e224f0b8 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuth2SecurityScheme/index.ts @@ -0,0 +1,20 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { OAuth2SecuritySchemeElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('OAuth2SecuritySchemeElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = OAuth2SecuritySchemeElement.refract({ + description: 'OAuth2', + flows: {}, + oauth2MetadataUrl: 'https://idp.example/.well-known/oauth-authorization-server', + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..3bbdf988b0 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/__snapshots__/index.ts.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements OAuthFlowsElement should refract to semantic ApiDOM tree 1`] = ` +(OauthFlowsElement + (MemberElement + (StringElement) + (AuthorizationCodeOAuthFlowElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/index.ts new file mode 100644 index 0000000000..e5305b84d3 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/index.ts @@ -0,0 +1,21 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { OAuthFlowsElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('OAuthFlowsElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = OAuthFlowsElement.refract({ + authorizationCode: { + authorizationUrl: 'https://x.example/a', + tokenUrl: 'https://x.example/t', + }, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/OpenIdConnectSecurityScheme/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/OpenIdConnectSecurityScheme/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..ac0e40fae7 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/OpenIdConnectSecurityScheme/__snapshots__/index.ts.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements OpenIdConnectSecuritySchemeElement should refract to semantic ApiDOM tree 1`] = ` +(OpenIdConnectSecuritySchemeElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/OpenIdConnectSecurityScheme/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/OpenIdConnectSecurityScheme/index.ts new file mode 100644 index 0000000000..0c043f6938 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/OpenIdConnectSecurityScheme/index.ts @@ -0,0 +1,19 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { OpenIdConnectSecuritySchemeElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('OpenIdConnectSecuritySchemeElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = OpenIdConnectSecuritySchemeElement.refract({ + description: 'OIDC', + openIdConnectUrl: 'https://idp.example/.well-known/openid-configuration', + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..aa9278b9b0 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/__snapshots__/index.ts.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements PasswordOAuthFlowElement should refract to semantic ApiDOM tree 1`] = ` +(PasswordOAuthFlowElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/index.ts new file mode 100644 index 0000000000..bb76350941 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/index.ts @@ -0,0 +1,19 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { PasswordOAuthFlowElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('PasswordOAuthFlowElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = PasswordOAuthFlowElement.refract({ + tokenUrl: 'https://idp.example/token', + scopes: {}, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityRequirement/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityRequirement/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..67a298db3e --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityRequirement/__snapshots__/index.ts.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements SecurityRequirementElement should refract to semantic ApiDOM tree 1`] = ` +(SecurityRequirementElement + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringListElement + (MemberElement + (StringElement) + (ArrayElement + (StringElement)))))))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityRequirement/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityRequirement/index.ts new file mode 100644 index 0000000000..355b63f87f --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityRequirement/index.ts @@ -0,0 +1,18 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { SecurityRequirementElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('SecurityRequirementElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = SecurityRequirementElement.refract({ + schemes: { 'oauth-scheme': { list: ['read'] } }, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..6ebb68ad0a --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/__snapshots__/index.ts.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements SecuritySchemeElement should refract to semantic ApiDOM tree 1`] = ` +(SecuritySchemeElement + (MemberElement + (StringElement) + (ApiKeySecuritySchemeElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/index.ts new file mode 100644 index 0000000000..1430e62fad --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/index.ts @@ -0,0 +1,18 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { SecuritySchemeElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('SecuritySchemeElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = SecuritySchemeElement.refract({ + apiKeySecurityScheme: { name: 'X-API-Key', location: 'header' }, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/StringList/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/StringList/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..ef344fc919 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/StringList/__snapshots__/index.ts.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements StringListElement should refract to semantic ApiDOM tree 1`] = ` +(StringListElement + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (StringElement)))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/StringList/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/StringList/index.ts new file mode 100644 index 0000000000..19c9503021 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/StringList/index.ts @@ -0,0 +1,16 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { StringListElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('StringListElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const element = StringListElement.refract({ list: ['read', 'write'] }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/real-world.ts b/packages/apidom-ns-a2a-1/test/refractor/real-world.ts new file mode 100644 index 0000000000..1a8d17014e --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/real-world.ts @@ -0,0 +1,77 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { assert } from 'chai'; +import { toValue } from '@swagger-api/apidom-core'; + +import { + AgentCardElement, + isAgentCardElement, + isAgentCapabilitiesElement, + isSkillsElement, + isAgentSkillElement, +} from '../../src/index.ts'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +/** + * Round-trip a real published Agent Card (sourced from a2aproject/a2a-samples) + * through the namespace refractor and verify typed elements at each level. + * This exercises the foundation end-to-end on input that wasn't authored for + * the test suite. + */ +describe('refractor', function () { + context('real-world AgentCard', function () { + const source = fs.readFileSync( + path.join(__dirname, '..', 'fixtures', 'real-world-planner-agent.json'), + 'utf-8', + ); + const raw = JSON.parse(source); + + specify('should refract to AgentCardElement', function () { + const element = AgentCardElement.refract(raw); + assert.isTrue(isAgentCardElement(element)); + }); + + specify('should produce typed scalar accessors', function () { + const element = AgentCardElement.refract(raw) as AgentCardElement; + assert.strictEqual(element.name?.toValue(), 'Langraph Planner Agent'); + assert.strictEqual(element.url?.toValue(), 'http://localhost:10102/'); + assert.strictEqual(element.version?.toValue(), '1.0.0'); + }); + + specify('should refract capabilities into an AgentCapabilitiesElement', function () { + const element = AgentCardElement.refract(raw) as AgentCardElement; + assert.isTrue(isAgentCapabilitiesElement(element.capabilities)); + assert.strictEqual(element.capabilities?.streaming?.toValue(), true); + assert.strictEqual(element.capabilities?.pushNotifications?.toValue(), true); + }); + + specify('should refract skills into a SkillsElement of AgentSkillElements', function () { + const element = AgentCardElement.refract(raw) as AgentCardElement; + assert.isTrue(isSkillsElement(element.skills)); + const firstSkill = element.skills?.get(0); + assert.isTrue(isAgentSkillElement(firstSkill)); + assert.strictEqual(toValue(firstSkill?.get('id')), 'planner'); + assert.strictEqual(toValue(firstSkill?.get('name')), 'Task Planner'); + }); + + specify( + "should clone-through unknown fields (e.g. 'stateTransitionHistory' not in spec)", + function () { + // The real sample uses a stateTransitionHistory field that isn't in + // A2A v1's AgentCapabilities schema. The refractor should retain it + // as an untyped MemberElement rather than dropping or crashing. + const element = AgentCardElement.refract(raw) as AgentCardElement; + const stateTransition = element.capabilities?.get('stateTransitionHistory'); + assert.strictEqual(toValue(stateTransition), false); + }, + ); + + specify('should keep defaultInputModes / defaultOutputModes as arrays', function () { + const element = AgentCardElement.refract(raw) as AgentCardElement; + assert.deepEqual(toValue(element.defaultInputModes), ['text', 'text/plain']); + assert.deepEqual(toValue(element.defaultOutputModes), ['text', 'text/plain']); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/tsconfig.json b/packages/apidom-ns-a2a-1/test/tsconfig.json new file mode 100644 index 0000000000..405aae2d2f --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "esnext", + "moduleResolution": "node" + }, + "include": [ + "." + ] +} diff --git a/packages/apidom-ns-a2a-1/tsconfig.declaration.json b/packages/apidom-ns-a2a-1/tsconfig.declaration.json new file mode 100644 index 0000000000..82d128fa80 --- /dev/null +++ b/packages/apidom-ns-a2a-1/tsconfig.declaration.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationDir": "types", + "noEmit": false, + "emitDeclarationOnly": true + } +} diff --git a/packages/apidom-ns-a2a-1/tsconfig.json b/packages/apidom-ns-a2a-1/tsconfig.json new file mode 100644 index 0000000000..5cc50cd885 --- /dev/null +++ b/packages/apidom-ns-a2a-1/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src/**/*" + ] +} diff --git a/packages/apidom-ns-a2a-1/vite.config.ts b/packages/apidom-ns-a2a-1/vite.config.ts new file mode 100644 index 0000000000..c950f6a368 --- /dev/null +++ b/packages/apidom-ns-a2a-1/vite.config.ts @@ -0,0 +1,8 @@ +import { createViteConfig } from '../../config/vite/vite.config.base.ts'; + +export default createViteConfig({ + packageName: 'apidom-ns-a2a-1', + libraryName: 'apidomNsA2A1', + entry: './src/index.ts', + chunkSizeWarningLimit: 1100, +}); diff --git a/packages/apidom-parser-adapter-a2a-json-1/.eslintignore b/packages/apidom-parser-adapter-a2a-json-1/.eslintignore new file mode 100644 index 0000000000..23853b909a --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/.eslintignore @@ -0,0 +1,8 @@ +/**/*.js +/**/*.mjs +/**/*.cjs +/dist +/types +/config +/.nyc_output +/node_modules diff --git a/packages/apidom-parser-adapter-a2a-json-1/.gitignore b/packages/apidom-parser-adapter-a2a-json-1/.gitignore new file mode 100644 index 0000000000..dd0ac50adc --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/.gitignore @@ -0,0 +1,7 @@ +/src/**/*.mjs +/src/**/*.cjs +/test/**/*.mjs +/dist +/types +/NOTICE +/swagger-api-apidom-parser-adapter-a2a-json-1-*.tgz diff --git a/packages/apidom-parser-adapter-a2a-json-1/.mocharc.json b/packages/apidom-parser-adapter-a2a-json-1/.mocharc.json new file mode 100644 index 0000000000..38ee8de7ec --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/.mocharc.json @@ -0,0 +1,7 @@ +{ + "extensions": ["ts"], + "loader": "ts-node/esm", + "recursive": true, + "spec": "test/**/*.ts", + "file": ["test/mocha-bootstrap.ts"] +} diff --git a/packages/apidom-parser-adapter-a2a-json-1/.npmrc b/packages/apidom-parser-adapter-a2a-json-1/.npmrc new file mode 100644 index 0000000000..4b82d2e7bb --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/.npmrc @@ -0,0 +1,2 @@ +save-prefix="=" +save=false diff --git a/packages/apidom-parser-adapter-a2a-json-1/CHANGELOG.md b/packages/apidom-parser-adapter-a2a-json-1/CHANGELOG.md new file mode 100644 index 0000000000..b8d6b03dd9 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/CHANGELOG.md @@ -0,0 +1,10 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## 1.11.1 (2026-05-27) + +### Features + +- **a2a:** initial parser adapter for A2A v1 AgentCard documents in JSON format. Uses structural detection (`capabilities` + `skills` co-presence) since A2A has no version discriminator field. diff --git a/packages/apidom-parser-adapter-a2a-json-1/README.md b/packages/apidom-parser-adapter-a2a-json-1/README.md new file mode 100644 index 0000000000..5c9926cda0 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/README.md @@ -0,0 +1,27 @@ +# @swagger-api/apidom-parser-adapter-a2a-json-1 + +Parser adapter for [A2A (Agent-to-Agent) Protocol v1.0](https://a2a-protocol.org/latest/definitions/) AgentCard documents in JSON format. Refracts into [`@swagger-api/apidom-ns-a2a-1`](../apidom-ns-a2a-1). + +## Installation + +```sh +npm install --save @swagger-api/apidom-parser-adapter-a2a-json-1 +``` + +## Usage + +```ts +import * as a2aJsonAdapter from '@swagger-api/apidom-parser-adapter-a2a-json-1'; +import ApiDOMParser from '@swagger-api/apidom-parser'; + +const parser = new ApiDOMParser().use(a2aJsonAdapter); +const result = await parser.parse(jsonSource); +``` + +## Detection + +A2A AgentCard documents have **no version discriminator field** (unlike OpenAPI's `"openapi": "3.1.0"`). This adapter uses **structural detection**: a JSON document is treated as an A2A AgentCard when it contains both a `capabilities` object and a `skills` array. False positives are possible — set the `mediaType` on the `File` explicitly when known. + +## License + +Apache-2.0 diff --git a/packages/apidom-parser-adapter-a2a-json-1/config/api-extractor/api-extractor.json b/packages/apidom-parser-adapter-a2a-json-1/config/api-extractor/api-extractor.json new file mode 100644 index 0000000000..40bee5b261 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/config/api-extractor/api-extractor.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "../../../../api-extractor.json", + "mainEntryPointFilePath": "../../types/adapter.d.ts" +} diff --git a/packages/apidom-parser-adapter-a2a-json-1/package.json b/packages/apidom-parser-adapter-a2a-json-1/package.json new file mode 100644 index 0000000000..9ea50b5137 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/package.json @@ -0,0 +1,58 @@ +{ + "name": "@swagger-api/apidom-parser-adapter-a2a-json-1", + "version": "1.11.1", + "description": "Parser adapter for parsing JSON documents into A2A v1 namespace.", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "type": "module", + "sideEffects": false, + "unpkg": "./dist/apidom-parser-adapter-a2a-json-1.browser.min.js", + "main": "./src/adapter.cjs", + "exports": { + "types": "./types/apidom-parser-adapter-a2a-json-1.d.ts", + "import": "./src/adapter.mjs", + "require": "./src/adapter.cjs" + }, + "types": "./types/apidom-parser-adapter-a2a-json-1.d.ts", + "scripts": { + "build": "npm run clean && run-p --max-parallel ${CPU_CORES:-6} typescript:declaration build:es build:cjs build:umd:browser", + "build:es": "cross-env BABEL_ENV=es babel src --out-dir src --extensions '.ts' --out-file-extension '.mjs' --root-mode 'upward'", + "build:cjs": "cross-env BABEL_ENV=cjs babel src --out-dir src --extensions '.ts' --out-file-extension '.cjs' --root-mode 'upward'", + "build:umd:browser": "vite build", + "lint": "eslint ./", + "lint:fix": "eslint ./ --fix", + "clean": "rimraf --glob 'src/**/*.mjs' 'src/**/*.cjs' ./dist ./types", + "typescript:check-types": "tsc --noEmit && tsc -p ./test/tsconfig.json --noEmit", + "typescript:declaration": "tsc -p tsconfig.declaration.json && api-extractor run -l -c ./config/api-extractor/api-extractor.json", + "test": "NODE_ENV=test ts-mocha --exit", + "prepack": "copyfiles -u 3 ../../LICENSES/* LICENSES && copyfiles -u 2 ../../NOTICE .", + "postpack": "rimraf NOTICE LICENSES" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/swagger-api/apidom.git" + }, + "author": "SmartBear", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.11.1", + "@swagger-api/apidom-ns-a2a-1": "^1.11.1", + "@swagger-api/apidom-parser-adapter-json": "^1.11.1", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + }, + "files": [ + "src/**/*.mjs", + "src/**/*.cjs", + "dist/", + "types/apidom-parser-adapter-a2a-json-1.d.ts", + "LICENSES", + "NOTICE", + "README.md", + "CHANGELOG.md" + ] +} diff --git a/packages/apidom-parser-adapter-a2a-json-1/src/adapter.ts b/packages/apidom-parser-adapter-a2a-json-1/src/adapter.ts new file mode 100644 index 0000000000..8d2a389f2a --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/src/adapter.ts @@ -0,0 +1,60 @@ +import { propOr, omit } from 'ramda'; +import { isNotUndefined } from 'ramda-adjunct'; +import { ParseResultElement, createNamespace } from '@swagger-api/apidom-core'; +import { parse as parseJSON, detect as detectJSON } from '@swagger-api/apidom-parser-adapter-json'; +import a2aNamespace, { AgentCardElement } from '@swagger-api/apidom-ns-a2a-1'; + +export { default as mediaTypes } from './media-types.ts'; + +/** + * A2A AgentCard documents do not contain a version discriminator field + * (unlike `"openapi": "3.1.0"` or `"arazzo": "1.0.1"`). Detection is + * therefore *structural*: a JSON document is treated as an A2A AgentCard + * when it contains both a `capabilities` object and a `skills` array. These + * two markers together are distinctive enough to discriminate AgentCard + * documents from other JSON documents in practice. False positives are + * possible; use the `mediaType` field on `File` to override when known. + * + * The exported `detectionRegExp` matches either marker individually (it + * exists for parity with other adapters and is used as a cheap pre-filter). + * The `detect` function performs the full AND check. + * + * @public + */ +export const detectionRegExp = /"capabilities"\s*:\s*\{|"skills"\s*:\s*\[/; + +/** + * @public + */ +export const detect = async (source: string): Promise => { + if (!(await detectJSON(source))) return false; + const hasCapabilities = /"capabilities"\s*:\s*\{/.test(source); + const hasSkills = /"skills"\s*:\s*\[/.test(source); + return hasCapabilities && hasSkills; +}; + +/** + * @public + */ +export const parse = async ( + source: string, + options: Record = {}, +): Promise => { + const refractorOpts: Record = propOr({}, 'refractorOpts', options); + const parserOpts = omit(['refractorOpts'], options); + const parseResultElement = await parseJSON(source, parserOpts); + const { result } = parseResultElement; + + if (isNotUndefined(result)) { + const agentCardElement = AgentCardElement.refract(result, refractorOpts); + agentCardElement.classes.push('result'); + parseResultElement.replaceResult(agentCardElement); + } + + return parseResultElement; +}; + +/** + * @public + */ +export const namespace = createNamespace(a2aNamespace); diff --git a/packages/apidom-parser-adapter-a2a-json-1/src/media-types.ts b/packages/apidom-parser-adapter-a2a-json-1/src/media-types.ts new file mode 100644 index 0000000000..ec497c3dc3 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/src/media-types.ts @@ -0,0 +1,11 @@ +import { mediaTypes, A2AMediaTypes } from '@swagger-api/apidom-ns-a2a-1'; + +/** + * @public + */ +const jsonMediaTypes = new A2AMediaTypes( + ...mediaTypes.filterByFormat('generic'), + ...mediaTypes.filterByFormat('json'), +); + +export default jsonMediaTypes; diff --git a/packages/apidom-parser-adapter-a2a-json-1/test/.eslintrc b/packages/apidom-parser-adapter-a2a-json-1/test/.eslintrc new file mode 100644 index 0000000000..c47eea4f48 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/test/.eslintrc @@ -0,0 +1,55 @@ +{ + "env": { + "mocha": true + }, + "globals": { + "document": true + }, + "plugins": [ + "mocha" + ], + "rules": { + "no-void": 0, + "func-names": 0, + "prefer-arrow-callback": 0, + "no-array-constructor": 0, + "prefer-rest-params": 0, + "no-new-wrappers": 0, + "mocha/no-skipped-tests": 2, + "mocha/handle-done-callback": 2, + "mocha/valid-suite-description": 2, + "mocha/no-mocha-arrows": 2, + "mocha/no-hooks-for-single-case": 2, + "mocha/no-sibling-hooks": 2, + "mocha/no-top-level-hooks": 2, + "mocha/no-identical-title": 2, + "mocha/no-nested-tests": 2, + "mocha/no-exclusive-tests": 2, + "no-underscore-dangle": 0, + "import/no-relative-packages": 0, + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "variable", + "format": ["camelCase", "PascalCase", "UPPER_CASE"], + "leadingUnderscore": "forbid" + }, + { + "selector": "variable", + "format": null, + "filter": { + "regex": "^__dirname$", + "match": true + } + }, + { + "selector": "variable", + "format": null, + "filter": { + "regex": "^__filename$", + "match": true + } + } + ] + } +} diff --git a/packages/apidom-parser-adapter-a2a-json-1/test/__snapshots__/adapter.ts.snap b/packages/apidom-parser-adapter-a2a-json-1/test/__snapshots__/adapter.ts.snap new file mode 100644 index 0000000000..24f9b34fbd --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/test/__snapshots__/adapter.ts.snap @@ -0,0 +1,108 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`adapter should parse 1`] = ` +(ParseResultElement + (AgentCardElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (AgentProviderElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (AgentCapabilitiesElement + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (AgentSkillElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (SecuritySchemeElement + (MemberElement + (StringElement) + (ApiKeySecuritySchemeElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))))))) + (MemberElement + (StringElement) + (ArrayElement + (SecurityRequirementElement + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringListElement + (MemberElement + (StringElement) + (ArrayElement))))))))))) +`; diff --git a/packages/apidom-parser-adapter-a2a-json-1/test/adapter.ts b/packages/apidom-parser-adapter-a2a-json-1/test/adapter.ts new file mode 100644 index 0000000000..2a3af5ce2e --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/test/adapter.ts @@ -0,0 +1,83 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { assert, expect } from 'chai'; +import { isParseResultElement, sexprs } from '@swagger-api/apidom-core'; +import { isAgentCardElement } from '@swagger-api/apidom-ns-a2a-1'; + +import * as adapter from '../src/adapter.ts'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const jsonSpec = fs + .readFileSync(path.join(__dirname, 'fixtures', 'sample-agent-card.json')) + .toString(); + +describe('adapter', function () { + context('given AgentCard definition in JSON format', function () { + specify('should detect as A2A AgentCard', async function () { + assert.isTrue(await adapter.detect(jsonSpec)); + }); + + specify('should detect when both capabilities and skills are present', async function () { + assert.isTrue(await adapter.detect('{"capabilities": {}, "skills": []}')); + }); + + specify('should NOT detect when only capabilities is present', async function () { + assert.isFalse(await adapter.detect('{"capabilities": {}}')); + }); + + specify('should NOT detect when only skills is present', async function () { + assert.isFalse(await adapter.detect('{"skills": []}')); + }); + + specify('should NOT detect an OpenAPI document', async function () { + assert.isFalse(await adapter.detect('{"openapi": "3.1.0", "info": {}, "paths": {}}')); + }); + }); + + it('should parse', async function () { + const parseResult = await adapter.parse(jsonSpec, { sourceMap: true }); + + assert.isTrue(isParseResultElement(parseResult)); + assert.isTrue(isAgentCardElement(parseResult.api)); + expect(sexprs(parseResult)).toMatchSnapshot(); + }); + + context('given zero byte empty file', function () { + specify('should return empty parse result', async function () { + const parseResult = await adapter.parse('', { sourceMap: true }); + + assert.isTrue(parseResult.isEmpty); + }); + }); + + context('given non-zero byte empty file', function () { + specify('should return empty parser result', async function () { + const parseResult = await adapter.parse(' ', { sourceMap: true }); + + assert.isTrue(parseResult.isEmpty); + }); + }); + + context('given invalid json file', function () { + specify('should return empty parser result', async function () { + const parseResult = await adapter.parse(' a ', { sourceMap: true }); + + assert.isTrue(parseResult.isEmpty); + }); + }); + + context('detectionRegExp', function () { + specify('should match a document with capabilities object', function () { + assert.isTrue(adapter.detectionRegExp.test('{"capabilities": {}}')); + }); + + specify('should match a document with skills array', function () { + assert.isTrue(adapter.detectionRegExp.test('{"skills": []}')); + }); + + specify('should not match unrelated content', function () { + assert.isFalse(adapter.detectionRegExp.test('{"foo": "bar"}')); + }); + }); +}); diff --git a/packages/apidom-parser-adapter-a2a-json-1/test/fixtures/sample-agent-card.json b/packages/apidom-parser-adapter-a2a-json-1/test/fixtures/sample-agent-card.json new file mode 100644 index 0000000000..20cf2b55f0 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/test/fixtures/sample-agent-card.json @@ -0,0 +1,44 @@ +{ + "name": "Recipe Agent", + "description": "Helps users find and follow recipes", + "url": "https://recipes.example.com/a2a", + "version": "1.0.0", + "provider": { + "organization": "Example Foods", + "url": "https://example.com" + }, + "capabilities": { + "streaming": true, + "pushNotifications": false, + "extendedAgentCard": false + }, + "defaultInputModes": ["text/plain"], + "defaultOutputModes": ["application/json"], + "skills": [ + { + "id": "find-recipe", + "name": "Find Recipe", + "description": "Locate recipes matching ingredients or cuisine.", + "tags": ["recipes", "search"], + "examples": ["Find a vegan pasta recipe"], + "inputModes": ["text/plain"], + "outputModes": ["application/json"] + } + ], + "securitySchemes": { + "apiKey": { + "apiKeySecurityScheme": { + "name": "X-API-Key", + "location": "header", + "description": "API key authentication" + } + } + }, + "securityRequirements": [ + { + "schemes": { + "apiKey": { "list": [] } + } + } + ] +} diff --git a/packages/apidom-parser-adapter-a2a-json-1/test/media-types.ts b/packages/apidom-parser-adapter-a2a-json-1/test/media-types.ts new file mode 100644 index 0000000000..7c38162825 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/test/media-types.ts @@ -0,0 +1,33 @@ +import { assert } from 'chai'; +import ApiDOMParser from '@swagger-api/apidom-parser'; + +import * as a2aJsonAdapter from '../src/adapter.ts'; + +describe('given adapter is used in parser', function () { + const parser = new ApiDOMParser().use(a2aJsonAdapter); + + /** + * Note: A2A has no version-discriminator field in the document, so the + * detection regex has no `version_json` / `version_yaml` named groups. As a + * result `ApiDOMParser.findMediaType` resolves to the generic media type + * rather than the format-specific one. Consumers that need format-specific + * routing should set the `mediaType` on the `File` explicitly. + */ + context('given A2A AgentCard in JSON format', function () { + specify('should resolve to the generic A2A media type', async function () { + const mediaType = await parser.findMediaType( + '{"capabilities": {"streaming": true}, "skills": [], "name": "x", "url": "https://x", "version": "1.0.0"}', + ); + + assert.strictEqual(mediaType, 'application/vnd.a2a;version=1.0.0'); + }); + }); + + context('given a non-A2A JSON document', function () { + specify('should not match A2A media type', async function () { + const mediaType = await parser.findMediaType('{"foo": "bar"}'); + + assert.notStrictEqual(mediaType, 'application/vnd.a2a;version=1.0.0'); + }); + }); +}); diff --git a/packages/apidom-parser-adapter-a2a-json-1/test/mocha-bootstrap.ts b/packages/apidom-parser-adapter-a2a-json-1/test/mocha-bootstrap.ts new file mode 100644 index 0000000000..aec560d03f --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/test/mocha-bootstrap.ts @@ -0,0 +1,11 @@ +import * as chai from 'chai'; +import { jestSnapshotPlugin, addSerializer } from 'mocha-chai-jest-snapshot'; + +// @ts-ignore +import * as jestApiDOMSerializer from '../../../scripts/jest-serializer-apidom.mjs'; +// @ts-ignore +import * as jestStringSerializer from '../../../scripts/jest-serializer-string.mjs'; + +chai.use(jestSnapshotPlugin()); +addSerializer(jestApiDOMSerializer); +addSerializer(jestStringSerializer); diff --git a/packages/apidom-parser-adapter-a2a-json-1/test/tsconfig.json b/packages/apidom-parser-adapter-a2a-json-1/test/tsconfig.json new file mode 100644 index 0000000000..405aae2d2f --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/test/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "esnext", + "moduleResolution": "node" + }, + "include": [ + "." + ] +} diff --git a/packages/apidom-parser-adapter-a2a-json-1/tsconfig.declaration.json b/packages/apidom-parser-adapter-a2a-json-1/tsconfig.declaration.json new file mode 100644 index 0000000000..82d128fa80 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/tsconfig.declaration.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationDir": "types", + "noEmit": false, + "emitDeclarationOnly": true + } +} diff --git a/packages/apidom-parser-adapter-a2a-json-1/tsconfig.json b/packages/apidom-parser-adapter-a2a-json-1/tsconfig.json new file mode 100644 index 0000000000..5cc50cd885 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src/**/*" + ] +} diff --git a/packages/apidom-parser-adapter-a2a-json-1/vite.config.ts b/packages/apidom-parser-adapter-a2a-json-1/vite.config.ts new file mode 100644 index 0000000000..e1e5c92b1e --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-json-1/vite.config.ts @@ -0,0 +1,8 @@ +import { createViteConfig } from '../../config/vite/vite.config.base.ts'; + +export default createViteConfig({ + packageName: 'apidom-parser-adapter-a2a-json-1', + libraryName: 'apidomParserAdapterA2AJson1', + entry: './src/adapter.ts', + chunkSizeWarningLimit: 1100, +}); diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/.eslintignore b/packages/apidom-parser-adapter-a2a-yaml-1/.eslintignore new file mode 100644 index 0000000000..23853b909a --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/.eslintignore @@ -0,0 +1,8 @@ +/**/*.js +/**/*.mjs +/**/*.cjs +/dist +/types +/config +/.nyc_output +/node_modules diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/.gitignore b/packages/apidom-parser-adapter-a2a-yaml-1/.gitignore new file mode 100644 index 0000000000..5bab635f3a --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/.gitignore @@ -0,0 +1,7 @@ +/src/**/*.mjs +/src/**/*.cjs +/test/**/*.mjs +/dist +/types +/NOTICE +/swagger-api-apidom-parser-adapter-a2a-yaml-1-*.tgz diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/.mocharc.json b/packages/apidom-parser-adapter-a2a-yaml-1/.mocharc.json new file mode 100644 index 0000000000..38ee8de7ec --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/.mocharc.json @@ -0,0 +1,7 @@ +{ + "extensions": ["ts"], + "loader": "ts-node/esm", + "recursive": true, + "spec": "test/**/*.ts", + "file": ["test/mocha-bootstrap.ts"] +} diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/.npmrc b/packages/apidom-parser-adapter-a2a-yaml-1/.npmrc new file mode 100644 index 0000000000..4b82d2e7bb --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/.npmrc @@ -0,0 +1,2 @@ +save-prefix="=" +save=false diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/CHANGELOG.md b/packages/apidom-parser-adapter-a2a-yaml-1/CHANGELOG.md new file mode 100644 index 0000000000..d844d4cc11 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/CHANGELOG.md @@ -0,0 +1,10 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## 1.11.1 (2026-05-27) + +### Features + +- **a2a:** initial parser adapter for A2A v1 AgentCard documents in YAML 1.2 format. Uses structural detection (`capabilities` + `skills` co-presence) since A2A has no version discriminator field. diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/README.md b/packages/apidom-parser-adapter-a2a-yaml-1/README.md new file mode 100644 index 0000000000..5b0b9eacfd --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/README.md @@ -0,0 +1,27 @@ +# @swagger-api/apidom-parser-adapter-a2a-yaml-1 + +Parser adapter for [A2A (Agent-to-Agent) Protocol v1.0](https://a2a-protocol.org/latest/definitions/) AgentCard documents in YAML 1.2 format. Refracts into [`@swagger-api/apidom-ns-a2a-1`](../apidom-ns-a2a-1). + +## Installation + +```sh +npm install --save @swagger-api/apidom-parser-adapter-a2a-yaml-1 +``` + +## Usage + +```ts +import * as a2aYamlAdapter from '@swagger-api/apidom-parser-adapter-a2a-yaml-1'; +import ApiDOMParser from '@swagger-api/apidom-parser'; + +const parser = new ApiDOMParser().use(a2aYamlAdapter); +const result = await parser.parse(yamlSource); +``` + +## Detection + +A2A AgentCard documents have **no version discriminator field**. This adapter uses **structural detection**: a YAML or JSON document is treated as an A2A AgentCard when it contains both a `capabilities` mapping and a `skills` sequence. False positives are possible — set the `mediaType` on the `File` explicitly when known. + +## License + +Apache-2.0 diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/config/api-extractor/api-extractor.json b/packages/apidom-parser-adapter-a2a-yaml-1/config/api-extractor/api-extractor.json new file mode 100644 index 0000000000..40bee5b261 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/config/api-extractor/api-extractor.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "../../../../api-extractor.json", + "mainEntryPointFilePath": "../../types/adapter.d.ts" +} diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/package.json b/packages/apidom-parser-adapter-a2a-yaml-1/package.json new file mode 100644 index 0000000000..5ca2052369 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/package.json @@ -0,0 +1,58 @@ +{ + "name": "@swagger-api/apidom-parser-adapter-a2a-yaml-1", + "version": "1.11.1", + "description": "Parser adapter for parsing YAML documents into A2A v1 namespace.", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "type": "module", + "sideEffects": false, + "unpkg": "./dist/apidom-parser-adapter-a2a-yaml-1.browser.min.js", + "main": "./src/adapter.cjs", + "exports": { + "types": "./types/apidom-parser-adapter-a2a-yaml-1.d.ts", + "import": "./src/adapter.mjs", + "require": "./src/adapter.cjs" + }, + "types": "./types/apidom-parser-adapter-a2a-yaml-1.d.ts", + "scripts": { + "build": "npm run clean && run-p --max-parallel ${CPU_CORES:-6} typescript:declaration build:es build:cjs build:umd:browser", + "build:es": "cross-env BABEL_ENV=es babel src --out-dir src --extensions '.ts' --out-file-extension '.mjs' --root-mode 'upward'", + "build:cjs": "cross-env BABEL_ENV=cjs babel src --out-dir src --extensions '.ts' --out-file-extension '.cjs' --root-mode 'upward'", + "build:umd:browser": "vite build", + "lint": "eslint ./", + "lint:fix": "eslint ./ --fix", + "clean": "rimraf --glob 'src/**/*.mjs' 'src/**/*.cjs' ./dist ./types", + "typescript:check-types": "tsc --noEmit && tsc -p ./test/tsconfig.json --noEmit", + "typescript:declaration": "tsc -p tsconfig.declaration.json && api-extractor run -l -c ./config/api-extractor/api-extractor.json", + "test": "NODE_ENV=test ts-mocha --exit", + "prepack": "copyfiles -u 3 ../../LICENSES/* LICENSES && copyfiles -u 2 ../../NOTICE .", + "postpack": "rimraf NOTICE LICENSES" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/swagger-api/apidom.git" + }, + "author": "SmartBear", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.11.1", + "@swagger-api/apidom-ns-a2a-1": "^1.11.1", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.11.1", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + }, + "files": [ + "src/**/*.mjs", + "src/**/*.cjs", + "dist/", + "types/apidom-parser-adapter-a2a-yaml-1.d.ts", + "LICENSES", + "NOTICE", + "README.md", + "CHANGELOG.md" + ] +} diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/src/adapter.ts b/packages/apidom-parser-adapter-a2a-yaml-1/src/adapter.ts new file mode 100644 index 0000000000..0c41a0c810 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/src/adapter.ts @@ -0,0 +1,64 @@ +import { propOr, omit } from 'ramda'; +import { isNotUndefined } from 'ramda-adjunct'; +import { ParseResultElement, createNamespace } from '@swagger-api/apidom-core'; +import { + parse as parseYAML, + detect as detectYAML, +} from '@swagger-api/apidom-parser-adapter-yaml-1-2'; +import a2aNamespace, { AgentCardElement } from '@swagger-api/apidom-ns-a2a-1'; + +export { default as mediaTypes } from './media-types.ts'; + +/** + * A2A AgentCard documents do not contain a version discriminator field + * (unlike `openapi: 3.1.0` or `arazzo: 1.0.1`). Detection is therefore + * *structural*: a YAML or JSON document is treated as an A2A AgentCard when + * it contains both a `capabilities` mapping and a `skills` sequence. These + * two markers together are distinctive enough to discriminate AgentCard + * documents from other YAML/JSON documents in practice. False positives are + * possible; use the `mediaType` field on `File` to override when known. + * + * The exported `detectionRegExp` matches either marker individually (used as + * a cheap pre-filter). The `detect` function performs the full AND check. + * + * @public + */ +export const detectionRegExp = + /(?^(["']?)(?:capabilities|skills)\2\s*:)|(?"capabilities"\s*:\s*\{|"skills"\s*:\s*\[)/m; + +/** + * @public + */ +export const detect = async (source: string): Promise => { + if (!(await detectYAML(source))) return false; + const hasCapabilities = + /^(["']?)capabilities\1\s*:/m.test(source) || /"capabilities"\s*:\s*\{/.test(source); + const hasSkills = /^(["']?)skills\1\s*:/m.test(source) || /"skills"\s*:\s*\[/.test(source); + return hasCapabilities && hasSkills; +}; + +/** + * @public + */ +export const parse = async ( + source: string, + options: Record = {}, +): Promise => { + const refractorOpts: Record = propOr({}, 'refractorOpts', options); + const parserOpts = omit(['refractorOpts'], options); + const parseResultElement = await parseYAML(source, parserOpts); + const { result } = parseResultElement; + + if (isNotUndefined(result)) { + const agentCardElement = AgentCardElement.refract(result, refractorOpts); + agentCardElement.classes.push('result'); + parseResultElement.replaceResult(agentCardElement); + } + + return parseResultElement; +}; + +/** + * @public + */ +export const namespace = createNamespace(a2aNamespace); diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/src/media-types.ts b/packages/apidom-parser-adapter-a2a-yaml-1/src/media-types.ts new file mode 100644 index 0000000000..eb0ff09b78 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/src/media-types.ts @@ -0,0 +1,11 @@ +import { mediaTypes, A2AMediaTypes } from '@swagger-api/apidom-ns-a2a-1'; + +/** + * @public + */ +const yamlMediaTypes = new A2AMediaTypes( + ...mediaTypes.filterByFormat('generic'), + ...mediaTypes.filterByFormat('yaml'), +); + +export default yamlMediaTypes; diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/test/.eslintrc b/packages/apidom-parser-adapter-a2a-yaml-1/test/.eslintrc new file mode 100644 index 0000000000..c47eea4f48 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/test/.eslintrc @@ -0,0 +1,55 @@ +{ + "env": { + "mocha": true + }, + "globals": { + "document": true + }, + "plugins": [ + "mocha" + ], + "rules": { + "no-void": 0, + "func-names": 0, + "prefer-arrow-callback": 0, + "no-array-constructor": 0, + "prefer-rest-params": 0, + "no-new-wrappers": 0, + "mocha/no-skipped-tests": 2, + "mocha/handle-done-callback": 2, + "mocha/valid-suite-description": 2, + "mocha/no-mocha-arrows": 2, + "mocha/no-hooks-for-single-case": 2, + "mocha/no-sibling-hooks": 2, + "mocha/no-top-level-hooks": 2, + "mocha/no-identical-title": 2, + "mocha/no-nested-tests": 2, + "mocha/no-exclusive-tests": 2, + "no-underscore-dangle": 0, + "import/no-relative-packages": 0, + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "variable", + "format": ["camelCase", "PascalCase", "UPPER_CASE"], + "leadingUnderscore": "forbid" + }, + { + "selector": "variable", + "format": null, + "filter": { + "regex": "^__dirname$", + "match": true + } + }, + { + "selector": "variable", + "format": null, + "filter": { + "regex": "^__filename$", + "match": true + } + } + ] + } +} diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/test/__snapshots__/adapter.ts.snap b/packages/apidom-parser-adapter-a2a-yaml-1/test/__snapshots__/adapter.ts.snap new file mode 100644 index 0000000000..24f9b34fbd --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/test/__snapshots__/adapter.ts.snap @@ -0,0 +1,108 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`adapter should parse 1`] = ` +(ParseResultElement + (AgentCardElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (AgentProviderElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (AgentCapabilitiesElement + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (AgentSkillElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (SecuritySchemeElement + (MemberElement + (StringElement) + (ApiKeySecuritySchemeElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))))))) + (MemberElement + (StringElement) + (ArrayElement + (SecurityRequirementElement + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringListElement + (MemberElement + (StringElement) + (ArrayElement))))))))))) +`; diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/test/adapter.ts b/packages/apidom-parser-adapter-a2a-yaml-1/test/adapter.ts new file mode 100644 index 0000000000..a72240825b --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/test/adapter.ts @@ -0,0 +1,71 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { assert, expect } from 'chai'; +import { isParseResultElement, sexprs } from '@swagger-api/apidom-core'; +import { isAgentCardElement } from '@swagger-api/apidom-ns-a2a-1'; + +import * as adapter from '../src/adapter.ts'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const yamlSpec = fs + .readFileSync(path.join(__dirname, 'fixtures', 'sample-agent-card.yaml')) + .toString(); + +describe('adapter', function () { + context('given AgentCard definition in YAML 1.2 format', function () { + specify('should detect as A2A AgentCard', async function () { + assert.isTrue(await adapter.detect(yamlSpec)); + }); + + specify('should detect minimal YAML with both keys', async function () { + assert.isTrue(await adapter.detect('capabilities: {}\nskills: []\n')); + }); + + specify('should NOT detect when only capabilities is present', async function () { + assert.isFalse(await adapter.detect('capabilities: {}\n')); + }); + + specify('should NOT detect an Arazzo workflow', async function () { + assert.isFalse(await adapter.detect('arazzo: 1.0.1\ninfo:\n title: x\n version: 1.0\n')); + }); + }); + + context('given AgentCard definition in JSON format (YAML 1.2 is a superset)', function () { + specify('should also detect JSON', async function () { + const jsonSrc = + '{"capabilities": {"streaming": true}, "skills": [], "name": "test", "url": "https://x", "version": "1.0.0"}'; + assert.isTrue(await adapter.detect(jsonSrc)); + }); + }); + + it('should parse', async function () { + const parseResult = await adapter.parse(yamlSpec, { sourceMap: true }); + + assert.isTrue(isParseResultElement(parseResult)); + assert.isTrue(isAgentCardElement(parseResult.api)); + expect(sexprs(parseResult)).toMatchSnapshot(); + }); + + context('given zero byte empty file', function () { + specify('should return empty parse result', async function () { + const parseResult = await adapter.parse('', { sourceMap: true }); + + assert.isTrue(parseResult.isEmpty); + }); + }); + + context('detectionRegExp', function () { + specify('should match a YAML document with capabilities key', function () { + assert.isTrue(adapter.detectionRegExp.test('capabilities:\n streaming: true\n')); + }); + + specify('should match a YAML document with skills key', function () { + assert.isTrue(adapter.detectionRegExp.test('skills:\n - id: a\n')); + }); + + specify('should match a JSON document with capabilities object', function () { + assert.isTrue(adapter.detectionRegExp.test('{"capabilities": {}}')); + }); + }); +}); diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/test/fixtures/sample-agent-card.yaml b/packages/apidom-parser-adapter-a2a-yaml-1/test/fixtures/sample-agent-card.yaml new file mode 100644 index 0000000000..c0551caa35 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/test/fixtures/sample-agent-card.yaml @@ -0,0 +1,38 @@ +name: Recipe Agent +description: Helps users find and follow recipes +url: https://recipes.example.com/a2a +version: 1.0.0 +provider: + organization: Example Foods + url: https://example.com +capabilities: + streaming: true + pushNotifications: false + extendedAgentCard: false +defaultInputModes: + - text/plain +defaultOutputModes: + - application/json +skills: + - id: find-recipe + name: Find Recipe + description: Locate recipes matching ingredients or cuisine. + tags: + - recipes + - search + examples: + - Find a vegan pasta recipe + inputModes: + - text/plain + outputModes: + - application/json +securitySchemes: + apiKey: + apiKeySecurityScheme: + name: X-API-Key + location: header + description: API key authentication +securityRequirements: + - schemes: + apiKey: + list: [] diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/test/media-types.ts b/packages/apidom-parser-adapter-a2a-yaml-1/test/media-types.ts new file mode 100644 index 0000000000..333984ed7a --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/test/media-types.ts @@ -0,0 +1,23 @@ +import { assert } from 'chai'; +import ApiDOMParser from '@swagger-api/apidom-parser'; + +import * as a2aYamlAdapter from '../src/adapter.ts'; + +describe('given adapter is used in parser', function () { + const parser = new ApiDOMParser().use(a2aYamlAdapter); + + /** + * Note: A2A has no version-discriminator field. See the JSON adapter's + * test/media-types.ts for the rationale; the generic media type is the + * expected resolution. + */ + context('given A2A AgentCard in YAML format', function () { + specify('should resolve to the generic A2A media type', async function () { + const mediaType = await parser.findMediaType( + 'capabilities:\n streaming: true\nskills: []\nname: x\nurl: https://x\nversion: 1.0.0\n', + ); + + assert.strictEqual(mediaType, 'application/vnd.a2a;version=1.0.0'); + }); + }); +}); diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/test/mocha-bootstrap.ts b/packages/apidom-parser-adapter-a2a-yaml-1/test/mocha-bootstrap.ts new file mode 100644 index 0000000000..aec560d03f --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/test/mocha-bootstrap.ts @@ -0,0 +1,11 @@ +import * as chai from 'chai'; +import { jestSnapshotPlugin, addSerializer } from 'mocha-chai-jest-snapshot'; + +// @ts-ignore +import * as jestApiDOMSerializer from '../../../scripts/jest-serializer-apidom.mjs'; +// @ts-ignore +import * as jestStringSerializer from '../../../scripts/jest-serializer-string.mjs'; + +chai.use(jestSnapshotPlugin()); +addSerializer(jestApiDOMSerializer); +addSerializer(jestStringSerializer); diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/test/tsconfig.json b/packages/apidom-parser-adapter-a2a-yaml-1/test/tsconfig.json new file mode 100644 index 0000000000..405aae2d2f --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/test/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "esnext", + "moduleResolution": "node" + }, + "include": [ + "." + ] +} diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/tsconfig.declaration.json b/packages/apidom-parser-adapter-a2a-yaml-1/tsconfig.declaration.json new file mode 100644 index 0000000000..82d128fa80 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/tsconfig.declaration.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationDir": "types", + "noEmit": false, + "emitDeclarationOnly": true + } +} diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/tsconfig.json b/packages/apidom-parser-adapter-a2a-yaml-1/tsconfig.json new file mode 100644 index 0000000000..5cc50cd885 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src/**/*" + ] +} diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/vite.config.ts b/packages/apidom-parser-adapter-a2a-yaml-1/vite.config.ts new file mode 100644 index 0000000000..df396112a7 --- /dev/null +++ b/packages/apidom-parser-adapter-a2a-yaml-1/vite.config.ts @@ -0,0 +1,8 @@ +import { createViteConfig } from '../../config/vite/vite.config.base.ts'; + +export default createViteConfig({ + packageName: 'apidom-parser-adapter-a2a-yaml-1', + libraryName: 'apidomParserAdapterA2AYaml1', + entry: './src/adapter.ts', + chunkSizeWarningLimit: 1100, +}); diff --git a/packages/apidom-reference/package.json b/packages/apidom-reference/package.json index 841fbf119d..b0e353ecd8 100644 --- a/packages/apidom-reference/package.json +++ b/packages/apidom-reference/package.json @@ -123,6 +123,16 @@ "require": "./src/parse/parsers/arazzo-yaml-1/index.cjs", "types": "./types/parse/parsers/arazzo-yaml-1/index.d.ts" }, + "./parse/parsers/a2a-json-1": { + "import": "./src/parse/parsers/a2a-json-1/index.mjs", + "require": "./src/parse/parsers/a2a-json-1/index.cjs", + "types": "./types/parse/parsers/a2a-json-1/index.d.ts" + }, + "./parse/parsers/a2a-yaml-1": { + "import": "./src/parse/parsers/a2a-yaml-1/index.mjs", + "require": "./src/parse/parsers/a2a-yaml-1/index.cjs", + "types": "./types/parse/parsers/a2a-yaml-1/index.d.ts" + }, "./parse/parsers/apidom-json": { "import": "./src/parse/parsers/apidom-json/index.mjs", "require": "./src/parse/parsers/apidom-json/index.cjs", @@ -313,6 +323,7 @@ }, "optionalDependencies": { "@swagger-api/apidom-json-pointer": "^1.11.1", + "@swagger-api/apidom-ns-a2a-1": "^1.11.1", "@swagger-api/apidom-ns-arazzo-1": "^1.11.1", "@swagger-api/apidom-ns-asyncapi-2": "^1.11.1", "@swagger-api/apidom-ns-openapi-2": "^1.11.1", @@ -321,6 +332,8 @@ "@swagger-api/apidom-ns-openapi-3-2": "^1.11.1", "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^1.11.1", "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^1.11.1", + "@swagger-api/apidom-parser-adapter-a2a-json-1": "^1.11.1", + "@swagger-api/apidom-parser-adapter-a2a-yaml-1": "^1.11.1", "@swagger-api/apidom-parser-adapter-arazzo-json-1": "^1.11.1", "@swagger-api/apidom-parser-adapter-arazzo-yaml-1": "^1.11.1", "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^1.11.1", @@ -340,6 +353,7 @@ }, "devDependencies": { "@swagger-api/apidom-json-pointer": "*", + "@swagger-api/apidom-ns-a2a-1": "*", "@swagger-api/apidom-ns-arazzo-1": "*", "@swagger-api/apidom-ns-asyncapi-2": "*", "@swagger-api/apidom-ns-openapi-2": "*", @@ -348,6 +362,8 @@ "@swagger-api/apidom-ns-openapi-3-2": "*", "@swagger-api/apidom-parser-adapter-api-design-systems-json": "*", "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "*", + "@swagger-api/apidom-parser-adapter-a2a-json-1": "*", + "@swagger-api/apidom-parser-adapter-a2a-yaml-1": "*", "@swagger-api/apidom-parser-adapter-arazzo-json-1": "*", "@swagger-api/apidom-parser-adapter-arazzo-yaml-1": "*", "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "*", diff --git a/packages/apidom-reference/src/configuration/saturated.ts b/packages/apidom-reference/src/configuration/saturated.ts index 76cf3e237f..b6c4ad32fd 100644 --- a/packages/apidom-reference/src/configuration/saturated.ts +++ b/packages/apidom-reference/src/configuration/saturated.ts @@ -22,6 +22,8 @@ import AsyncAPIYAML2Parser from '../parse/parsers/asyncapi-yaml-2/index.ts'; import AsyncAPIYAML3Parser from '../parse/parsers/asyncapi-yaml-3/index.ts'; import ArazzoJSON1Parser from '../parse/parsers/arazzo-json-1/index.ts'; import ArazzoYAML1Parser from '../parse/parsers/arazzo-yaml-1/index.ts'; +import A2AJSON1Parser from '../parse/parsers/a2a-json-1/index.ts'; +import A2AYAML1Parser from '../parse/parsers/a2a-yaml-1/index.ts'; import APIDOMJSONParser from '../parse/parsers/apidom-json/index.ts'; import JSONParser from '../parse/parsers/json/index.ts'; import YAMLParser from '../parse/parsers/yaml-1-2/index.ts'; @@ -52,6 +54,8 @@ options.parse.parsers = [ new AsyncAPIYAML3Parser({ allowEmpty: true, sourceMap: false }), new ArazzoJSON1Parser({ allowEmpty: true, sourceMap: false }), new ArazzoYAML1Parser({ allowEmpty: true, sourceMap: false }), + new A2AJSON1Parser({ allowEmpty: true, sourceMap: false }), + new A2AYAML1Parser({ allowEmpty: true, sourceMap: false }), new APIDesignSystemsJSONParser({ allowEmpty: true, sourceMap: false }), new APIDesignSystemsYAMLParser({ allowEmpty: true, sourceMap: false }), new APIDOMJSONParser({ allowEmpty: true, sourceMap: false }), diff --git a/packages/apidom-reference/src/parse/parsers/a2a-json-1/index.ts b/packages/apidom-reference/src/parse/parsers/a2a-json-1/index.ts new file mode 100644 index 0000000000..fbe02178ae --- /dev/null +++ b/packages/apidom-reference/src/parse/parsers/a2a-json-1/index.ts @@ -0,0 +1,60 @@ +import { pick } from 'ramda'; +import { ParseResultElement } from '@swagger-api/apidom-core'; +import { + parse, + mediaTypes as A2A1JsonMediaTypes, + detect, +} from '@swagger-api/apidom-parser-adapter-a2a-json-1'; + +import ParserError from '../../../errors/ParserError.ts'; +import Parser, { ParserOptions } from '../Parser.ts'; +import File from '../../../File.ts'; + +export type { default as Parser, ParserOptions } from '../Parser.ts'; +export type { default as File, FileOptions } from '../../../File.ts'; + +/** + * @public + */ +export interface A2AJSON1ParserOptions extends Omit {} + +/** + * @public + */ +class A2AJSON1Parser extends Parser { + public syntacticAnalysis?: 'direct' | 'indirect'; + + public refractorOpts!: object; + + constructor(options?: A2AJSON1ParserOptions) { + const { fileExtensions = [], mediaTypes = A2A1JsonMediaTypes, ...rest } = options ?? {}; + + super({ ...rest, name: 'a2a-json-1', fileExtensions, mediaTypes }); + } + + async canParse(file: File): Promise { + const hasSupportedFileExtension = + this.fileExtensions.length === 0 ? true : this.fileExtensions.includes(file.extension); + const hasSupportedMediaType = this.mediaTypes.includes(file.mediaType); + + if (!hasSupportedFileExtension) return false; + if (hasSupportedMediaType) return true; + if (!hasSupportedMediaType) { + return detect(file.toString()); + } + return false; + } + + async parse(file: File): Promise { + const source = file.toString(); + + try { + const parserOpts = pick(['sourceMap', 'syntacticAnalysis', 'refractorOpts'], this); + return await parse(source, parserOpts); + } catch (error: unknown) { + throw new ParserError(`Error parsing "${file.uri}"`, { cause: error }); + } + } +} + +export default A2AJSON1Parser; diff --git a/packages/apidom-reference/src/parse/parsers/a2a-yaml-1/index.ts b/packages/apidom-reference/src/parse/parsers/a2a-yaml-1/index.ts new file mode 100644 index 0000000000..f4b8d98ac3 --- /dev/null +++ b/packages/apidom-reference/src/parse/parsers/a2a-yaml-1/index.ts @@ -0,0 +1,58 @@ +import { pick } from 'ramda'; +import { ParseResultElement } from '@swagger-api/apidom-core'; +import { + parse, + mediaTypes as A2A1YamlMediaTypes, + detect, +} from '@swagger-api/apidom-parser-adapter-a2a-yaml-1'; + +import ParserError from '../../../errors/ParserError.ts'; +import Parser, { ParserOptions } from '../Parser.ts'; +import File from '../../../File.ts'; + +export type { default as Parser, ParserOptions } from '../Parser.ts'; +export type { default as File, FileOptions } from '../../../File.ts'; + +/** + * @public + */ +export interface A2AYAML1ParserOptions extends Omit {} + +/** + * @public + */ +class A2AYAML1Parser extends Parser { + public refractorOpts!: object; + + constructor(options?: A2AYAML1ParserOptions) { + const { fileExtensions = [], mediaTypes = A2A1YamlMediaTypes, ...rest } = options ?? {}; + + super({ ...rest, name: 'a2a-yaml-1', fileExtensions, mediaTypes }); + } + + async canParse(file: File): Promise { + const hasSupportedFileExtension = + this.fileExtensions.length === 0 ? true : this.fileExtensions.includes(file.extension); + const hasSupportedMediaType = this.mediaTypes.includes(file.mediaType); + + if (!hasSupportedFileExtension) return false; + if (hasSupportedMediaType) return true; + if (!hasSupportedMediaType) { + return detect(file.toString()); + } + return false; + } + + async parse(file: File): Promise { + const source = file.toString(); + + try { + const parserOpts = pick(['sourceMap', 'refractorOpts'], this); + return await parse(source, parserOpts); + } catch (error: unknown) { + throw new ParserError(`Error parsing "${file.uri}"`, { cause: error }); + } + } +} + +export default A2AYAML1Parser; diff --git a/packages/apidom-reference/test/parse/parsers/a2a-json-1/fixtures/sample-agent-card.json b/packages/apidom-reference/test/parse/parsers/a2a-json-1/fixtures/sample-agent-card.json new file mode 100644 index 0000000000..20cf2b55f0 --- /dev/null +++ b/packages/apidom-reference/test/parse/parsers/a2a-json-1/fixtures/sample-agent-card.json @@ -0,0 +1,44 @@ +{ + "name": "Recipe Agent", + "description": "Helps users find and follow recipes", + "url": "https://recipes.example.com/a2a", + "version": "1.0.0", + "provider": { + "organization": "Example Foods", + "url": "https://example.com" + }, + "capabilities": { + "streaming": true, + "pushNotifications": false, + "extendedAgentCard": false + }, + "defaultInputModes": ["text/plain"], + "defaultOutputModes": ["application/json"], + "skills": [ + { + "id": "find-recipe", + "name": "Find Recipe", + "description": "Locate recipes matching ingredients or cuisine.", + "tags": ["recipes", "search"], + "examples": ["Find a vegan pasta recipe"], + "inputModes": ["text/plain"], + "outputModes": ["application/json"] + } + ], + "securitySchemes": { + "apiKey": { + "apiKeySecurityScheme": { + "name": "X-API-Key", + "location": "header", + "description": "API key authentication" + } + } + }, + "securityRequirements": [ + { + "schemes": { + "apiKey": { "list": [] } + } + } + ] +} diff --git a/packages/apidom-reference/test/parse/parsers/a2a-json-1/index.ts b/packages/apidom-reference/test/parse/parsers/a2a-json-1/index.ts new file mode 100644 index 0000000000..27af7538a8 --- /dev/null +++ b/packages/apidom-reference/test/parse/parsers/a2a-json-1/index.ts @@ -0,0 +1,60 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { assert } from 'chai'; +import { mediaTypes, isAgentCardElement } from '@swagger-api/apidom-ns-a2a-1'; + +import A2AJSON1Parser from '../../../../src/parse/parsers/a2a-json-1/index.ts'; +import File from '../../../../src/File.ts'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('parsers', function () { + context('A2AJSON1Parser', function () { + context('canParse', function () { + context('given file with .json extension', function () { + context('and with proper media type', function () { + specify('should return true', async function () { + const file = new File({ + uri: '/path/to/agent.json', + mediaType: mediaTypes.latest('json'), + }); + const parser = new A2AJSON1Parser(); + + assert.isTrue(await parser.canParse(file)); + }); + }); + + context('and with unknown media type but detectable content', function () { + specify('should return true', async function () { + const url = path.join(__dirname, 'fixtures', 'sample-agent-card.json'); + const file = new File({ uri: url, data: fs.readFileSync(url) }); + const parser = new A2AJSON1Parser(); + + assert.isTrue(await parser.canParse(file)); + }); + }); + }); + + context('given file with unsupported extension', function () { + specify('should return false', async function () { + const file = new File({ uri: '/path/to/agent.yaml' }); + const parser = new A2AJSON1Parser({ fileExtensions: ['.json'] }); + + assert.isFalse(await parser.canParse(file)); + }); + }); + }); + + context('parse', function () { + specify('should return a parse result containing an AgentCardElement', async function () { + const url = path.join(__dirname, 'fixtures', 'sample-agent-card.json'); + const file = new File({ uri: url, data: fs.readFileSync(url) }); + const parser = new A2AJSON1Parser(); + const result = await parser.parse(file); + + assert.isTrue(isAgentCardElement(result.api)); + }); + }); + }); +}); diff --git a/packages/apidom-reference/test/parse/parsers/a2a-yaml-1/fixtures/sample-agent-card.yaml b/packages/apidom-reference/test/parse/parsers/a2a-yaml-1/fixtures/sample-agent-card.yaml new file mode 100644 index 0000000000..c0551caa35 --- /dev/null +++ b/packages/apidom-reference/test/parse/parsers/a2a-yaml-1/fixtures/sample-agent-card.yaml @@ -0,0 +1,38 @@ +name: Recipe Agent +description: Helps users find and follow recipes +url: https://recipes.example.com/a2a +version: 1.0.0 +provider: + organization: Example Foods + url: https://example.com +capabilities: + streaming: true + pushNotifications: false + extendedAgentCard: false +defaultInputModes: + - text/plain +defaultOutputModes: + - application/json +skills: + - id: find-recipe + name: Find Recipe + description: Locate recipes matching ingredients or cuisine. + tags: + - recipes + - search + examples: + - Find a vegan pasta recipe + inputModes: + - text/plain + outputModes: + - application/json +securitySchemes: + apiKey: + apiKeySecurityScheme: + name: X-API-Key + location: header + description: API key authentication +securityRequirements: + - schemes: + apiKey: + list: [] diff --git a/packages/apidom-reference/test/parse/parsers/a2a-yaml-1/index.ts b/packages/apidom-reference/test/parse/parsers/a2a-yaml-1/index.ts new file mode 100644 index 0000000000..c12bd52f54 --- /dev/null +++ b/packages/apidom-reference/test/parse/parsers/a2a-yaml-1/index.ts @@ -0,0 +1,60 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { assert } from 'chai'; +import { mediaTypes, isAgentCardElement } from '@swagger-api/apidom-ns-a2a-1'; + +import A2AYAML1Parser from '../../../../src/parse/parsers/a2a-yaml-1/index.ts'; +import File from '../../../../src/File.ts'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('parsers', function () { + context('A2AYAML1Parser', function () { + context('canParse', function () { + context('given file with .yaml extension', function () { + context('and with proper media type', function () { + specify('should return true', async function () { + const file = new File({ + uri: '/path/to/agent.yaml', + mediaType: mediaTypes.latest('yaml'), + }); + const parser = new A2AYAML1Parser(); + + assert.isTrue(await parser.canParse(file)); + }); + }); + + context('and with unknown media type but detectable content', function () { + specify('should return true', async function () { + const url = path.join(__dirname, 'fixtures', 'sample-agent-card.yaml'); + const file = new File({ uri: url, data: fs.readFileSync(url) }); + const parser = new A2AYAML1Parser(); + + assert.isTrue(await parser.canParse(file)); + }); + }); + }); + + context('given file with unsupported extension', function () { + specify('should return false', async function () { + const file = new File({ uri: '/path/to/agent.txt' }); + const parser = new A2AYAML1Parser({ fileExtensions: ['.yaml', '.yml'] }); + + assert.isFalse(await parser.canParse(file)); + }); + }); + }); + + context('parse', function () { + specify('should return a parse result containing an AgentCardElement', async function () { + const url = path.join(__dirname, 'fixtures', 'sample-agent-card.yaml'); + const file = new File({ uri: url, data: fs.readFileSync(url) }); + const parser = new A2AYAML1Parser(); + const result = await parser.parse(file); + + assert.isTrue(isAgentCardElement(result.api)); + }); + }); + }); +}); From 379cb9a1d05d7db037d0d15b7491f2b55dbc4a8e Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Wed, 3 Jun 2026 16:22:59 +0100 Subject: [PATCH 02/14] feat: lint (PROVCON-5343) --- packages/apidom-ns-a2a-1/src/refractor/index.ts | 2 +- .../src/refractor/visitors/SpecificationVisitor.ts | 4 ++-- .../src/refractor/visitors/a2a-1/ExtensionsVisitor.ts | 2 +- .../refractor/visitors/a2a-1/SecurityRequirementsVisitor.ts | 2 +- .../src/refractor/visitors/a2a-1/SecuritySchemesVisitor.ts | 3 +-- .../src/refractor/visitors/a2a-1/SignaturesVisitor.ts | 2 +- .../src/refractor/visitors/a2a-1/SkillsVisitor.ts | 2 +- .../refractor/visitors/a2a-1/SupportedInterfacesVisitor.ts | 2 +- 8 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/apidom-ns-a2a-1/src/refractor/index.ts b/packages/apidom-ns-a2a-1/src/refractor/index.ts index 7d8fcf92b6..d625f44b0e 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/index.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/index.ts @@ -12,7 +12,7 @@ import type VisitorClass from './visitors/Visitor.ts'; import specification from './specification.ts'; import { keyMap, getNodeType } from '../traversal/visitor.ts'; import createToolbox from './toolbox.ts'; -import canonicalizeKeys from './canonicalize.ts'; +import { canonicalizeKeys } from './canonicalize.ts'; /** * @public diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/SpecificationVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/SpecificationVisitor.ts index f089eebfdd..80942b32af 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/visitors/SpecificationVisitor.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/SpecificationVisitor.ts @@ -1,6 +1,6 @@ import { pathSatisfies, path, pick } from 'ramda'; import { isFunction } from 'ramda-adjunct'; -import { visit, cloneDeep } from '@swagger-api/apidom-core'; +import { visit, cloneDeep, Element } from '@swagger-api/apidom-core'; import Visitor, { VisitorOptions } from './Visitor.ts'; import FallbackVisitor from './FallbackVisitor.ts'; @@ -56,7 +56,7 @@ class SpecificationVisitor extends Visitor { return new VisitorClz(visitorOpts); } - toRefractedElement(specPath: string[], element: any, options = {}) { + toRefractedElement(specPath: string[], element: Element, options = {}) { /** * This is `Visitor shortcut`: mechanism for short-circuiting the traversal and replacing * it by basic node cloning. diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/ExtensionsVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/ExtensionsVisitor.ts index ec0fc8195b..76217a4212 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/ExtensionsVisitor.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/ExtensionsVisitor.ts @@ -1,7 +1,7 @@ import { Mixin } from 'ts-mixer'; import { ArrayElement, Element, BREAK } from '@swagger-api/apidom-core'; -import ExtensionsElement from '../../../elements/nces/Extensions.ts'; +import ExtensionsElement from '../../../elements/nces/Extensions.ts'; import SpecificationVisitor, { SpecificationVisitorOptions } from '../SpecificationVisitor.ts'; import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementsVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementsVisitor.ts index e163db0a3e..6738224b90 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementsVisitor.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementsVisitor.ts @@ -1,7 +1,7 @@ import { Mixin } from 'ts-mixer'; import { ArrayElement, Element, BREAK } from '@swagger-api/apidom-core'; -import SecurityRequirementsElement from '../../../elements/nces/SecurityRequirements.ts'; +import SecurityRequirementsElement from '../../../elements/nces/SecurityRequirements.ts'; import SpecificationVisitor, { SpecificationVisitorOptions } from '../SpecificationVisitor.ts'; import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecuritySchemesVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecuritySchemesVisitor.ts index c612361a61..c429c82a8b 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecuritySchemesVisitor.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecuritySchemesVisitor.ts @@ -1,8 +1,7 @@ import { Mixin } from 'ts-mixer'; import { always } from 'ramda'; -import { ObjectElement } from '@swagger-api/apidom-core'; -import SecuritySchemesElement from '../../../elements/nces/SecuritySchemes.ts'; +import SecuritySchemesElement from '../../../elements/nces/SecuritySchemes.ts'; import MapVisitor, { MapVisitorOptions, SpecPath } from '../generics/MapVisitor.ts'; import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SignaturesVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SignaturesVisitor.ts index 814129fa82..511e3639e7 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SignaturesVisitor.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SignaturesVisitor.ts @@ -1,7 +1,7 @@ import { Mixin } from 'ts-mixer'; import { ArrayElement, Element, BREAK } from '@swagger-api/apidom-core'; -import SignaturesElement from '../../../elements/nces/Signatures.ts'; +import SignaturesElement from '../../../elements/nces/Signatures.ts'; import SpecificationVisitor, { SpecificationVisitorOptions } from '../SpecificationVisitor.ts'; import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SkillsVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SkillsVisitor.ts index 6e39821af9..7fcdbe0a9f 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SkillsVisitor.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SkillsVisitor.ts @@ -1,7 +1,7 @@ import { Mixin } from 'ts-mixer'; import { ArrayElement, Element, BREAK } from '@swagger-api/apidom-core'; -import SkillsElement from '../../../elements/nces/Skills.ts'; +import SkillsElement from '../../../elements/nces/Skills.ts'; import SpecificationVisitor, { SpecificationVisitorOptions } from '../SpecificationVisitor.ts'; import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SupportedInterfacesVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SupportedInterfacesVisitor.ts index 119c27743e..6267823108 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SupportedInterfacesVisitor.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SupportedInterfacesVisitor.ts @@ -1,7 +1,7 @@ import { Mixin } from 'ts-mixer'; import { ArrayElement, Element, BREAK } from '@swagger-api/apidom-core'; -import SupportedInterfacesElement from '../../../elements/nces/SupportedInterfaces.ts'; +import SupportedInterfacesElement from '../../../elements/nces/SupportedInterfaces.ts'; import SpecificationVisitor, { SpecificationVisitorOptions } from '../SpecificationVisitor.ts'; import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; From d80a5b1629cf954a81b2b5a108b023e4771a5413 Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Thu, 4 Jun 2026 10:11:00 +0100 Subject: [PATCH 03/14] fix(a2a): address namespace review feedback (PROVCON-5343) Rename root element agentCard -> a2aAgentCard1 to match spec+version convention used by other namespaces (openApi3_0, arazzoSpecification1), incl. namespace registration, predicate, keyMap and snapshots. Remove non-spec `url` field from AgentCard (in A2A v1 the agent URL lives in AgentInterface.url under supportedInterfaces). Fix snake_case canonicalisation: rewrite canonicalizeKeys to walk the generic ApiDOM element tree post-baseRefract instead of the raw JS value, so the parser-adapter path (which passes an already-refracted Element) also gets snake_case -> camelCase normalisation. Previously the isElement guard skipped canonicalisation for every parsed document. Add refractor replace-empty-element plugin (+ export and tests) so empty YAML values are compensated with the appropriate semantic element. Move SecurityRequirementSchemesVisitor into the security-requirement/ folder. Extend element tests to cover previously untested fixed fields (refreshUrl across all OAuth flows, all OAuthFlows/SecurityScheme variants, AgentCard provider/iconUrl/documentationUrl/supportedInterfaces, AgentSkill securityRequirements, AgentInterface tenant, AgentExtension params). Co-Authored-By: Claude Opus 4.8 --- .../apidom-ns-a2a-1/src/elements/AgentCard.ts | 10 +- packages/apidom-ns-a2a-1/src/index.ts | 3 +- packages/apidom-ns-a2a-1/src/namespace.ts | 2 +- packages/apidom-ns-a2a-1/src/predicates.ts | 2 +- .../src/refractor/canonicalize.ts | 64 ++-- .../apidom-ns-a2a-1/src/refractor/index.ts | 11 +- .../plugins/replace-empty-element.ts | 295 ++++++++++++++++++ .../src/refractor/specification.ts | 3 +- .../SchemesVisitor.ts} | 4 +- .../apidom-ns-a2a-1/src/traversal/visitor.ts | 2 +- .../AgentCard/__snapshots__/index.ts.snap | 32 +- .../refractor/elements/AgentCard/index.ts | 15 +- .../__snapshots__/index.ts.snap | 8 +- .../elements/AgentExtension/index.ts | 1 + .../__snapshots__/index.ts.snap | 3 + .../elements/AgentInterface/index.ts | 1 + .../AgentSkill/__snapshots__/index.ts.snap | 13 +- .../refractor/elements/AgentSkill/index.ts | 1 + .../__snapshots__/index.ts.snap | 3 + .../AuthorizationCodeOAuthFlow/index.ts | 1 + .../__snapshots__/index.ts.snap | 3 + .../ClientCredentialsOAuthFlow/index.ts | 1 + .../__snapshots__/index.ts.snap | 3 + .../elements/DeviceCodeOAuthFlow/index.ts | 1 + .../__snapshots__/index.ts.snap | 3 + .../elements/ImplicitOAuthFlow/index.ts | 1 + .../OAuthFlows/__snapshots__/index.ts.snap | 27 ++ .../refractor/elements/OAuthFlows/index.ts | 13 + .../__snapshots__/index.ts.snap | 3 + .../elements/PasswordOAuthFlow/index.ts | 1 + .../__snapshots__/index.ts.snap | 54 +++- .../elements/SecurityScheme/index.ts | 39 ++- .../__snapshots__/index.ts.snap | 54 ++++ .../plugins/replace-empty-element/index.ts | 84 +++++ .../test/refractor/real-world.ts | 1 - 35 files changed, 710 insertions(+), 52 deletions(-) create mode 100644 packages/apidom-ns-a2a-1/src/refractor/plugins/replace-empty-element.ts rename packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/{SecurityRequirementSchemesVisitor.ts => security-requirement/SchemesVisitor.ts} (82%) create mode 100644 packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/__snapshots__/index.ts.snap create mode 100644 packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/index.ts diff --git a/packages/apidom-ns-a2a-1/src/elements/AgentCard.ts b/packages/apidom-ns-a2a-1/src/elements/AgentCard.ts index 4bd9be2dae..2057f74f70 100644 --- a/packages/apidom-ns-a2a-1/src/elements/AgentCard.ts +++ b/packages/apidom-ns-a2a-1/src/elements/AgentCard.ts @@ -17,7 +17,7 @@ import AgentProviderElement from './AgentProvider.ts'; class AgentCard extends ObjectElement { constructor(content?: Record, meta?: Meta, attributes?: Attributes) { super(content, meta, attributes); - this.element = 'agentCard'; + this.element = 'a2aAgentCard1'; this.classes.push('api'); this.classes.push('agent-card'); } @@ -38,14 +38,6 @@ class AgentCard extends ObjectElement { this.set('description', description); } - get url(): StringElement | undefined { - return this.get('url'); - } - - set url(url: StringElement | undefined) { - this.set('url', url); - } - get version(): StringElement | undefined { return this.get('version'); } diff --git a/packages/apidom-ns-a2a-1/src/index.ts b/packages/apidom-ns-a2a-1/src/index.ts index fd5a412d0b..5aeaf51460 100644 --- a/packages/apidom-ns-a2a-1/src/index.ts +++ b/packages/apidom-ns-a2a-1/src/index.ts @@ -19,6 +19,7 @@ export { default } from './namespace.ts'; export { default as refract, createRefractor } from './refractor/index.ts'; export { default as specificationObj } from './refractor/specification.ts'; +export { default as refractorPluginReplaceEmptyElement } from './refractor/plugins/replace-empty-element.ts'; // Generic visitor base classes export { default as FixedFieldsVisitor } from './refractor/visitors/generics/FixedFieldsVisitor.ts'; @@ -153,7 +154,7 @@ export type { export type { default as SecurityRequirementSchemesVisitor, SecurityRequirementSchemesVisitorOptions, -} from './refractor/visitors/a2a-1/SecurityRequirementSchemesVisitor.ts'; +} from './refractor/visitors/a2a-1/security-requirement/SchemesVisitor.ts'; // Predicates export { diff --git a/packages/apidom-ns-a2a-1/src/namespace.ts b/packages/apidom-ns-a2a-1/src/namespace.ts index db92e0610d..435ff71a2c 100644 --- a/packages/apidom-ns-a2a-1/src/namespace.ts +++ b/packages/apidom-ns-a2a-1/src/namespace.ts @@ -29,7 +29,7 @@ const a2a1 = { namespace: (options: NamespacePluginOptions) => { const { base } = options; - base.register('agentCard', AgentCardElement); + base.register('a2aAgentCard1', AgentCardElement); base.register('agentCapabilities', AgentCapabilitiesElement); base.register('agentExtension', AgentExtensionElement); base.register('agentProvider', AgentProviderElement); diff --git a/packages/apidom-ns-a2a-1/src/predicates.ts b/packages/apidom-ns-a2a-1/src/predicates.ts index b0f4176025..2af0e3a262 100644 --- a/packages/apidom-ns-a2a-1/src/predicates.ts +++ b/packages/apidom-ns-a2a-1/src/predicates.ts @@ -36,7 +36,7 @@ export const isAgentCardElement = createPredicate( return (element: unknown): element is AgentCardElement => element instanceof AgentCardElement || (hasBasicElementProps(element) && - isElementType('agentCard', element) && + isElementType('a2aAgentCard1', element) && primitiveEq('object', element) && hasClass('api', element) && hasClass('agent-card', element)); diff --git a/packages/apidom-ns-a2a-1/src/refractor/canonicalize.ts b/packages/apidom-ns-a2a-1/src/refractor/canonicalize.ts index e4e67584a9..87722534bb 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/canonicalize.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/canonicalize.ts @@ -7,9 +7,16 @@ * side of the conversion may emit either form. * * This module exposes a single function `canonicalizeKeys` that recursively - * rewrites known snake_case keys to their camelCase equivalents. It is - * applied to the raw input value *before* refraction, so the visitor pipeline - * downstream only ever sees camelCase keys. + * rewrites known snake_case keys to their camelCase equivalents *on an ApiDOM + * element tree*. It runs against the generic ApiDOM produced by `baseRefract`, + * before the namespace visitor pipeline, so downstream visitors only ever see + * camelCase keys. + * + * Operating on the element tree (rather than a plain JS value) is required + * because documents reach the refractor through two paths: callers may pass a + * plain JS value, but the parser adapters pass the already-refracted generic + * Element from `parseJSON` / `parseYAML`. Both converge on a generic Element + * after `baseRefract`, so canonicalising there covers every entry point. * * Note: the mapping is global rather than element-scoped. This works because * every snake_case key listed below appears in only one logical position in @@ -19,6 +26,15 @@ * * @public */ +import { + Element, + MemberElement, + toValue, + isElement, + isObjectElement, + isArrayElement, + isStringElement, +} from '@swagger-api/apidom-core'; const SNAKE_TO_CAMEL: Record = { // AgentCard @@ -63,25 +79,37 @@ const SNAKE_TO_CAMEL: Record = { }; /** - * Recursively rewrite known snake_case keys to camelCase in a plain JS value. - * Pure: returns a new structure; the input is not mutated. No-op for keys - * not in the mapping table. + * Recursively rewrite known snake_case member keys to camelCase in an ApiDOM + * element tree. Mutates `element` in place (the generic tree is freshly built + * by `baseRefract`, so in-place rewriting is safe) and returns it. No-op for + * keys not in the mapping table. Key `meta`/`attributes` (e.g. source maps) + * are preserved because only the key's primitive content is rewritten. * * @public */ -export const canonicalizeKeys = (value: unknown): unknown => { - if (Array.isArray(value)) { - return value.map(canonicalizeKeys); - } - if (value !== null && typeof value === 'object') { - const result: Record = {}; - for (const [key, val] of Object.entries(value)) { - const canonicalKey = SNAKE_TO_CAMEL[key] ?? key; - result[canonicalKey] = canonicalizeKeys(val); - } - return result; +export const canonicalizeKeys = (element: Element): Element => { + if (isObjectElement(element)) { + (element.content as unknown as MemberElement[]).forEach((member) => { + const { key, value } = member; + if (isStringElement(key)) { + const canonicalKey = SNAKE_TO_CAMEL[toValue(key) as string]; + if (typeof canonicalKey === 'string') { + // minim's base typing declares `content` as `Array`, but a + // StringElement's content is the string primitive — assign through a + // narrowed target so the rewrite type-checks. + (key as unknown as { content: string }).content = canonicalKey; + } + } + if (isElement(value)) { + canonicalizeKeys(value); + } + }); + } else if (isArrayElement(element)) { + (element.content as unknown as Element[]).forEach((item) => { + canonicalizeKeys(item); + }); } - return value; + return element; }; export default canonicalizeKeys; diff --git a/packages/apidom-ns-a2a-1/src/refractor/index.ts b/packages/apidom-ns-a2a-1/src/refractor/index.ts index d625f44b0e..c20af106f8 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/index.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/index.ts @@ -4,7 +4,6 @@ import { dereference, refract as baseRefract, dispatchRefractorPlugins, - isElement, } from '@swagger-api/apidom-core'; import { path } from 'ramda'; @@ -21,10 +20,12 @@ const refract = ( value: unknown, { specPath = ['visitors', 'document', 'objects', 'AgentCard', '$visitor'], plugins = [] } = {}, ): T => { - // Canonicalise snake_case keys → camelCase on raw input. Skip if the value - // is already an ApiDOM Element (caller passed a generic-refracted tree). - const canonicalised = isElement(value) ? value : canonicalizeKeys(value); - const element = baseRefract(canonicalised); + // Canonicalise snake_case keys → camelCase. Both entry points (a plain JS + // value and the generic Element passed by the parser adapters) converge on + // a generic Element after `baseRefract`, so canonicalising the element tree + // here covers every path before the namespace visitors run. + const element = baseRefract(value); + canonicalizeKeys(element); const resolvedSpec = dereference(specification); /** diff --git a/packages/apidom-ns-a2a-1/src/refractor/plugins/replace-empty-element.ts b/packages/apidom-ns-a2a-1/src/refractor/plugins/replace-empty-element.ts new file mode 100644 index 0000000000..fbf9e0d5eb --- /dev/null +++ b/packages/apidom-ns-a2a-1/src/refractor/plugins/replace-empty-element.ts @@ -0,0 +1,295 @@ +import { + ArrayElement, + ObjectElement, + StringElement, + isStringElement, + isArrayElement, + isElement, + isMemberElement, + includesClasses, + cloneDeep, + toValue, + assignSourceMap, +} from '@swagger-api/apidom-core'; + +/** + * A2A v1 specification elements. + */ +import AgentProviderElement from '../../elements/AgentProvider.ts'; +import AgentCapabilitiesElement from '../../elements/AgentCapabilities.ts'; +import AgentExtensionElement from '../../elements/AgentExtension.ts'; +import AgentInterfaceElement from '../../elements/AgentInterface.ts'; +import AgentSkillElement from '../../elements/AgentSkill.ts'; +import AgentCardSignatureElement from '../../elements/AgentCardSignature.ts'; +import SecurityRequirementElement from '../../elements/SecurityRequirement.ts'; +import SecuritySchemeElement from '../../elements/SecurityScheme.ts'; +import APIKeySecuritySchemeElement from '../../elements/APIKeySecurityScheme.ts'; +import HTTPAuthSecuritySchemeElement from '../../elements/HTTPAuthSecurityScheme.ts'; +import MutualTlsSecuritySchemeElement from '../../elements/MutualTlsSecurityScheme.ts'; +import OAuth2SecuritySchemeElement from '../../elements/OAuth2SecurityScheme.ts'; +import OpenIdConnectSecuritySchemeElement from '../../elements/OpenIdConnectSecurityScheme.ts'; +import OAuthFlowsElement from '../../elements/OAuthFlows.ts'; +import AuthorizationCodeOAuthFlowElement from '../../elements/AuthorizationCodeOAuthFlow.ts'; +import ClientCredentialsOAuthFlowElement from '../../elements/ClientCredentialsOAuthFlow.ts'; +import DeviceCodeOAuthFlowElement from '../../elements/DeviceCodeOAuthFlow.ts'; +import ImplicitOAuthFlowElement from '../../elements/ImplicitOAuthFlow.ts'; +import PasswordOAuthFlowElement from '../../elements/PasswordOAuthFlow.ts'; +// non-concrete Elements (NCEs) +import SkillsElement from '../../elements/nces/Skills.ts'; +import SignaturesElement from '../../elements/nces/Signatures.ts'; +import SupportedInterfacesElement from '../../elements/nces/SupportedInterfaces.ts'; +import SecurityRequirementsElement from '../../elements/nces/SecurityRequirements.ts'; +import ExtensionsElement from '../../elements/nces/Extensions.ts'; +import SecuritySchemesElement from '../../elements/nces/SecuritySchemes.ts'; +import { getNodeType } from '../../traversal/visitor.ts'; + +/** + * This plugin is specific to YAML 1.2 format, which allows defining key-value pairs + * with empty key, empty value, or both. If the value is not provided in YAML format, + * this plugin compensates for this missing value with the most appropriate semantic element type. + * + * https://yaml.org/spec/1.2.2/#72-empty-nodes + * + * @example + * + * ```yaml + * name: 'agent example' + * capabilities: + * ``` + * Refracting result without this plugin: + * + * (A2aAgentCard1Element + * (MemberElement + * (StringElement) + * (StringElement)) + * (MemberElement + * (StringElement) + * (StringElement)) + * + * Refracting result with this plugin: + * + * (A2aAgentCard1Element + * (MemberElement + * (StringElement) + * (StringElement)) + * (MemberElement + * (StringElement) + * (AgentCapabilitiesElement)) + */ + +const isEmptyElement = (element: unknown) => + isStringElement(element) && includesClasses(['yaml-e-node', 'yaml-e-scalar'], element); + +const schema = { + // concrete types handling (CTs) + A2aAgentCard1Element: { + provider(...args: any[]) { + return new AgentProviderElement(...args); + }, + capabilities(...args: any[]) { + return new AgentCapabilitiesElement(...args); + }, + defaultInputModes(...args: any[]) { + return new ArrayElement(...args); + }, + defaultOutputModes(...args: any[]) { + return new ArrayElement(...args); + }, + supportedInterfaces(...args: any[]) { + return new SupportedInterfacesElement(...args); + }, + skills(...args: any[]) { + return new SkillsElement(...args); + }, + securitySchemes(...args: any[]) { + return new SecuritySchemesElement(...args); + }, + securityRequirements(...args: any[]) { + return new SecurityRequirementsElement(...args); + }, + signatures(...args: any[]) { + return new SignaturesElement(...args); + }, + }, + AgentCapabilitiesElement: { + extensions(...args: any[]) { + return new ExtensionsElement(...args); + }, + }, + AgentExtensionElement: { + params(...args: any[]) { + return new ObjectElement(...args); + }, + }, + AgentSkillElement: { + tags(...args: any[]) { + return new ArrayElement(...args); + }, + examples(...args: any[]) { + return new ArrayElement(...args); + }, + inputModes(...args: any[]) { + return new ArrayElement(...args); + }, + outputModes(...args: any[]) { + return new ArrayElement(...args); + }, + securityRequirements(...args: any[]) { + return new SecurityRequirementsElement(...args); + }, + }, + SecuritySchemeElement: { + apiKeySecurityScheme(...args: any[]) { + return new APIKeySecuritySchemeElement(...args); + }, + httpAuthSecurityScheme(...args: any[]) { + return new HTTPAuthSecuritySchemeElement(...args); + }, + mtlsSecurityScheme(...args: any[]) { + return new MutualTlsSecuritySchemeElement(...args); + }, + oauth2SecurityScheme(...args: any[]) { + return new OAuth2SecuritySchemeElement(...args); + }, + openIdConnectSecurityScheme(...args: any[]) { + return new OpenIdConnectSecuritySchemeElement(...args); + }, + }, + Oauth2SecuritySchemeElement: { + flows(...args: any[]) { + return new OAuthFlowsElement(...args); + }, + }, + OauthFlowsElement: { + authorizationCode(...args: any[]) { + return new AuthorizationCodeOAuthFlowElement(...args); + }, + clientCredentials(...args: any[]) { + return new ClientCredentialsOAuthFlowElement(...args); + }, + deviceCode(...args: any[]) { + return new DeviceCodeOAuthFlowElement(...args); + }, + implicit(...args: any[]) { + return new ImplicitOAuthFlowElement(...args); + }, + password(...args: any[]) { + return new PasswordOAuthFlowElement(...args); + }, + }, + AuthorizationCodeOAuthFlowElement: { + scopes(...args: any[]) { + return new ObjectElement(...args); + }, + }, + ClientCredentialsOAuthFlowElement: { + scopes(...args: any[]) { + return new ObjectElement(...args); + }, + }, + DeviceCodeOAuthFlowElement: { + scopes(...args: any[]) { + return new ObjectElement(...args); + }, + }, + ImplicitOAuthFlowElement: { + scopes(...args: any[]) { + return new ObjectElement(...args); + }, + }, + PasswordOAuthFlowElement: { + scopes(...args: any[]) { + return new ObjectElement(...args); + }, + }, + SecurityRequirementElement: { + schemes(...args: any[]) { + return new ObjectElement(...args); + }, + }, + // non-concrete types handling (NCEs) + [SkillsElement.primaryClass]: { + '<*>': function asterisk(...args: any[]) { + return new AgentSkillElement(...args); + }, + }, + [SignaturesElement.primaryClass]: { + '<*>': function asterisk(...args: any[]) { + return new AgentCardSignatureElement(...args); + }, + }, + [SupportedInterfacesElement.primaryClass]: { + '<*>': function asterisk(...args: any[]) { + return new AgentInterfaceElement(...args); + }, + }, + [SecurityRequirementsElement.primaryClass]: { + '<*>': function asterisk(...args: any[]) { + return new SecurityRequirementElement(...args); + }, + }, + [ExtensionsElement.primaryClass]: { + '<*>': function asterisk(...args: any[]) { + return new AgentExtensionElement(...args); + }, + }, + [SecuritySchemesElement.primaryClass]: { + '[key: *]': function key(...args: any[]) { + return new SecuritySchemeElement(...args); + }, + }, + 'security-requirement-schemes': { + '[key: *]': function key(...args: any[]) { + return new ArrayElement(...args); + }, + }, +}; + +const findElementFactory = (ancestor: any, keyName: string) => { + const elementType = getNodeType(ancestor); // @ts-ignore + const keyMapping = schema[elementType] || schema[toValue(ancestor.classes.first)]; + + return typeof keyMapping === 'undefined' + ? undefined + : Object.prototype.hasOwnProperty.call(keyMapping, '[key: *]') + ? keyMapping['[key: *]'] + : keyMapping[keyName]; +}; + +/** + * @public + */ +const plugin = () => () => ({ + visitor: { + StringElement(element: StringElement, key: any, parent: any, path: any, ancestors: any[]) { + if (!isEmptyElement(element)) return undefined; + + const lineage = [...ancestors, parent].filter(isElement); + const parentElement = lineage[lineage.length - 1]; // @TODO(vladimir.gorej@gmail.com): can be replaced by Array.prototype.at in future + let elementFactory; + let context; + + if (isArrayElement(parentElement)) { + context = element; + elementFactory = findElementFactory(parentElement, '<*>'); + } else if (isMemberElement(parentElement)) { + context = lineage[lineage.length - 2]; // @TODO(vladimir.gorej@gmail.com): can be replaced by Array.prototype.at in future + elementFactory = findElementFactory(context, toValue(parentElement.key)); + } + + // no element factory found + if (typeof elementFactory !== 'function') return undefined; + + const result = elementFactory.call( + { context }, + undefined, + cloneDeep(element.meta), + cloneDeep(element.attributes), + ); + + return assignSourceMap(result, element); + }, + }, +}); + +export default plugin; diff --git a/packages/apidom-ns-a2a-1/src/refractor/specification.ts b/packages/apidom-ns-a2a-1/src/refractor/specification.ts index dfadfe5f93..a616c64216 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/specification.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/specification.ts @@ -25,7 +25,7 @@ import SupportedInterfacesVisitor from './visitors/a2a-1/SupportedInterfacesVisi import SecurityRequirementsVisitor from './visitors/a2a-1/SecurityRequirementsVisitor.ts'; import ExtensionsVisitor from './visitors/a2a-1/ExtensionsVisitor.ts'; import SecuritySchemesVisitor from './visitors/a2a-1/SecuritySchemesVisitor.ts'; -import SecurityRequirementSchemesVisitor from './visitors/a2a-1/SecurityRequirementSchemesVisitor.ts'; +import SecurityRequirementSchemesVisitor from './visitors/a2a-1/security-requirement/SchemesVisitor.ts'; import FallbackVisitor from './visitors/FallbackVisitor.ts'; /** @@ -55,7 +55,6 @@ const specification = { fixedFields: { name: { $ref: '#/visitors/value' }, description: { $ref: '#/visitors/value' }, - url: { $ref: '#/visitors/value' }, version: { $ref: '#/visitors/value' }, iconUrl: { $ref: '#/visitors/value' }, documentationUrl: { $ref: '#/visitors/value' }, diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementSchemesVisitor.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/security-requirement/SchemesVisitor.ts similarity index 82% rename from packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementSchemesVisitor.ts rename to packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/security-requirement/SchemesVisitor.ts index a566c49102..46118a1f6d 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/SecurityRequirementSchemesVisitor.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/security-requirement/SchemesVisitor.ts @@ -2,8 +2,8 @@ import { Mixin } from 'ts-mixer'; import { always } from 'ramda'; import { ObjectElement } from '@swagger-api/apidom-core'; -import MapVisitor, { MapVisitorOptions, SpecPath } from '../generics/MapVisitor.ts'; -import FallbackVisitor, { FallbackVisitorOptions } from '../FallbackVisitor.ts'; +import MapVisitor, { MapVisitorOptions, SpecPath } from '../../generics/MapVisitor.ts'; +import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; /** * @public diff --git a/packages/apidom-ns-a2a-1/src/traversal/visitor.ts b/packages/apidom-ns-a2a-1/src/traversal/visitor.ts index f6d93c6b3b..f044152382 100644 --- a/packages/apidom-ns-a2a-1/src/traversal/visitor.ts +++ b/packages/apidom-ns-a2a-1/src/traversal/visitor.ts @@ -15,7 +15,7 @@ export const getNodeType = (element: T): string | undefined = * @public */ export const keyMap = { - AgentCardElement: ['content'], + A2aAgentCard1Element: ['content'], AgentCapabilitiesElement: ['content'], AgentExtensionElement: ['content'], AgentProviderElement: ['content'], diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/__snapshots__/index.ts.snap index c7d3cb46f9..dc05d72dc5 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/__snapshots__/index.ts.snap +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/__snapshots__/index.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`refractor elements AgentCardElement should refract a minimal agent card 1`] = ` -(AgentCardElement +(A2aAgentCard1Element (MemberElement (StringElement) (StringElement)) @@ -14,6 +14,18 @@ exports[`refractor elements AgentCardElement should refract a minimal agent card (MemberElement (StringElement) (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (AgentProviderElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) (MemberElement (StringElement) (AgentCapabilitiesElement @@ -31,6 +43,19 @@ exports[`refractor elements AgentCardElement should refract a minimal agent card (StringElement) (ArrayElement (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (AgentInterfaceElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))))) (MemberElement (StringElement) (ArrayElement @@ -51,10 +76,7 @@ exports[`refractor elements AgentCardElement should refract a minimal agent card `; exports[`refractor elements AgentCardElement should refract an agent card with security schemes and a signature 1`] = ` -(AgentCardElement - (MemberElement - (StringElement) - (StringElement)) +(A2aAgentCard1Element (MemberElement (StringElement) (StringElement)) diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/index.ts index aa181c4354..93a84078c6 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/index.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentCard/index.ts @@ -10,14 +10,26 @@ describe('refractor', function () { const element = AgentCardElement.refract({ name: 'Demo Agent', description: 'A demo A2A agent', - url: 'https://demo.example/a2a', version: '1.0.0', + iconUrl: 'https://demo.example/icon.png', + documentationUrl: 'https://demo.example/docs', + provider: { + organization: 'Demo Inc', + url: 'https://demo.example', + }, capabilities: { streaming: true, pushNotifications: false, }, defaultInputModes: ['text/plain'], defaultOutputModes: ['application/json'], + supportedInterfaces: [ + { + url: 'https://demo.example/a2a', + protocolBinding: 'JSONRPC', + protocolVersion: '1.0', + }, + ], skills: [ { id: 'lookup', @@ -35,7 +47,6 @@ describe('refractor', function () { const element = AgentCardElement.refract({ name: 'Secure Agent', version: '1.0.0', - url: 'https://secure.example/a2a', capabilities: {}, defaultInputModes: [], defaultOutputModes: [], diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/__snapshots__/index.ts.snap index c691590c25..8e705dd743 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/__snapshots__/index.ts.snap +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/__snapshots__/index.ts.snap @@ -10,5 +10,11 @@ exports[`refractor elements AgentExtensionElement should refract to semantic Api (StringElement)) (MemberElement (StringElement) - (BooleanElement))) + (BooleanElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringElement))))) `; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/index.ts index 29a1537cfb..3b4131773f 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/index.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentExtension/index.ts @@ -11,6 +11,7 @@ describe('refractor', function () { uri: 'urn:example:ext', description: 'demo', required: true, + params: { foo: 'bar' }, }); expect(sexprs(element)).toMatchSnapshot(); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/__snapshots__/index.ts.snap index b427a2dbeb..03a6cf7e27 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/__snapshots__/index.ts.snap +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/__snapshots__/index.ts.snap @@ -8,6 +8,9 @@ exports[`refractor elements AgentInterfaceElement should refract to semantic Api (MemberElement (StringElement) (StringElement)) + (MemberElement + (StringElement) + (StringElement)) (MemberElement (StringElement) (StringElement))) diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/index.ts index 847ed66694..c34a0d991a 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/index.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentInterface/index.ts @@ -11,6 +11,7 @@ describe('refractor', function () { url: 'https://example.com/a2a', protocolBinding: 'JSONRPC', protocolVersion: '1.0', + tenant: 'tenant-a', }); expect(sexprs(element)).toMatchSnapshot(); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/__snapshots__/index.ts.snap index e6ec5d00c5..a747b0ee0f 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/__snapshots__/index.ts.snap +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/__snapshots__/index.ts.snap @@ -26,5 +26,16 @@ exports[`refractor elements AgentSkillElement should refract to semantic ApiDOM (MemberElement (StringElement) (ArrayElement - (StringElement)))) + (StringElement))) + (MemberElement + (StringElement) + (ArrayElement + (SecurityRequirementElement + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ArrayElement + (StringElement))))))))) `; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/index.ts index d6223fda24..71a3585acc 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/index.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AgentSkill/index.ts @@ -15,6 +15,7 @@ describe('refractor', function () { examples: ['Find X'], inputModes: ['text/plain'], outputModes: ['application/json'], + securityRequirements: [{ schemes: { oauth: ['read'] } }], }); expect(sexprs(element)).toMatchSnapshot(); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/__snapshots__/index.ts.snap index ac0c4579aa..22b2322f73 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/__snapshots__/index.ts.snap +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/__snapshots__/index.ts.snap @@ -8,6 +8,9 @@ exports[`refractor elements AuthorizationCodeOAuthFlowElement should refract to (MemberElement (StringElement) (StringElement)) + (MemberElement + (StringElement) + (StringElement)) (MemberElement (StringElement) (BooleanElement)) diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/index.ts index 782b99c3c3..d73d8a3f96 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/index.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/AuthorizationCodeOAuthFlow/index.ts @@ -10,6 +10,7 @@ describe('refractor', function () { const element = AuthorizationCodeOAuthFlowElement.refract({ authorizationUrl: 'https://idp.example/authorize', tokenUrl: 'https://idp.example/token', + refreshUrl: 'https://idp.example/refresh', pkceRequired: true, scopes: { read: 'Read access' }, }); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/__snapshots__/index.ts.snap index 79b0cda2ae..10b4408895 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/__snapshots__/index.ts.snap +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/__snapshots__/index.ts.snap @@ -5,6 +5,9 @@ exports[`refractor elements ClientCredentialsOAuthFlowElement should refract to (MemberElement (StringElement) (StringElement)) + (MemberElement + (StringElement) + (StringElement)) (MemberElement (StringElement) (ObjectElement diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/index.ts index b162aca964..090efbca2e 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/index.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/ClientCredentialsOAuthFlow/index.ts @@ -9,6 +9,7 @@ describe('refractor', function () { specify('should refract to semantic ApiDOM tree', function () { const element = ClientCredentialsOAuthFlowElement.refract({ tokenUrl: 'https://idp.example/token', + refreshUrl: 'https://idp.example/refresh', scopes: { admin: 'Admin' }, }); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/__snapshots__/index.ts.snap index 7bde0d9a92..764ba2fc02 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/__snapshots__/index.ts.snap +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/__snapshots__/index.ts.snap @@ -8,6 +8,9 @@ exports[`refractor elements DeviceCodeOAuthFlowElement should refract to semanti (MemberElement (StringElement) (StringElement)) + (MemberElement + (StringElement) + (StringElement)) (MemberElement (StringElement) (ObjectElement))) diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/index.ts index b215c51e10..b40c2dc3ee 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/index.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/DeviceCodeOAuthFlow/index.ts @@ -10,6 +10,7 @@ describe('refractor', function () { const element = DeviceCodeOAuthFlowElement.refract({ deviceAuthorizationUrl: 'https://idp.example/device', tokenUrl: 'https://idp.example/token', + refreshUrl: 'https://idp.example/refresh', scopes: {}, }); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/__snapshots__/index.ts.snap index 9b4e98d552..5f69646e26 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/__snapshots__/index.ts.snap +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/__snapshots__/index.ts.snap @@ -5,6 +5,9 @@ exports[`refractor elements ImplicitOAuthFlowElement should refract to semantic (MemberElement (StringElement) (StringElement)) + (MemberElement + (StringElement) + (StringElement)) (MemberElement (StringElement) (ObjectElement))) diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/index.ts index 9b6a8387fa..fd3fe35935 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/index.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/index.ts @@ -9,6 +9,7 @@ describe('refractor', function () { specify('should refract to semantic ApiDOM tree', function () { const element = ImplicitOAuthFlowElement.refract({ authorizationUrl: 'https://idp.example/authorize', + refreshUrl: 'https://idp.example/refresh', scopes: {}, }); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/__snapshots__/index.ts.snap index 3bbdf988b0..0f3d8fc340 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/__snapshots__/index.ts.snap +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/__snapshots__/index.ts.snap @@ -8,6 +8,33 @@ exports[`refractor elements OAuthFlowsElement should refract to semantic ApiDOM (MemberElement (StringElement) (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ClientCredentialsOAuthFlowElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (DeviceCodeOAuthFlowElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ImplicitOAuthFlowElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (PasswordOAuthFlowElement (MemberElement (StringElement) (StringElement))))) diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/index.ts index e5305b84d3..58fb087ba6 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/index.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/index.ts @@ -12,6 +12,19 @@ describe('refractor', function () { authorizationUrl: 'https://x.example/a', tokenUrl: 'https://x.example/t', }, + clientCredentials: { + tokenUrl: 'https://x.example/t', + }, + deviceCode: { + deviceAuthorizationUrl: 'https://x.example/d', + tokenUrl: 'https://x.example/t', + }, + implicit: { + authorizationUrl: 'https://x.example/a', + }, + password: { + tokenUrl: 'https://x.example/t', + }, }); expect(sexprs(element)).toMatchSnapshot(); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/__snapshots__/index.ts.snap index aa9278b9b0..b0370502c0 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/__snapshots__/index.ts.snap +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/__snapshots__/index.ts.snap @@ -5,6 +5,9 @@ exports[`refractor elements PasswordOAuthFlowElement should refract to semantic (MemberElement (StringElement) (StringElement)) + (MemberElement + (StringElement) + (StringElement)) (MemberElement (StringElement) (ObjectElement))) diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/index.ts index bb76350941..55b6c26280 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/index.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/index.ts @@ -9,6 +9,7 @@ describe('refractor', function () { specify('should refract to semantic ApiDOM tree', function () { const element = PasswordOAuthFlowElement.refract({ tokenUrl: 'https://idp.example/token', + refreshUrl: 'https://idp.example/refresh', scopes: {}, }); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/__snapshots__/index.ts.snap index 6ebb68ad0a..47028072b4 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/__snapshots__/index.ts.snap +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/__snapshots__/index.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`refractor elements SecuritySchemeElement should refract to semantic ApiDOM tree 1`] = ` +exports[`refractor elements SecuritySchemeElement should refract apiKeySecurityScheme variant 1`] = ` (SecuritySchemeElement (MemberElement (StringElement) @@ -12,3 +12,55 @@ exports[`refractor elements SecuritySchemeElement should refract to semantic Api (StringElement) (StringElement))))) `; + +exports[`refractor elements SecuritySchemeElement should refract httpAuthSecurityScheme variant 1`] = ` +(SecuritySchemeElement + (MemberElement + (StringElement) + (HttpAuthSecuritySchemeElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))))) +`; + +exports[`refractor elements SecuritySchemeElement should refract mtlsSecurityScheme variant 1`] = ` +(SecuritySchemeElement + (MemberElement + (StringElement) + (MutualTlsSecuritySchemeElement + (MemberElement + (StringElement) + (StringElement))))) +`; + +exports[`refractor elements SecuritySchemeElement should refract oauth2SecurityScheme variant 1`] = ` +(SecuritySchemeElement + (MemberElement + (StringElement) + (Oauth2SecuritySchemeElement + (MemberElement + (StringElement) + (OauthFlowsElement + (MemberElement + (StringElement) + (ClientCredentialsOAuthFlowElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (StringElement))))) +`; + +exports[`refractor elements SecuritySchemeElement should refract openIdConnectSecurityScheme variant 1`] = ` +(SecuritySchemeElement + (MemberElement + (StringElement) + (OpenIdConnectSecuritySchemeElement + (MemberElement + (StringElement) + (StringElement))))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/index.ts index 1430e62fad..7942635840 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/index.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/SecurityScheme/index.ts @@ -6,13 +6,50 @@ import { SecuritySchemeElement } from '../../../../src/index.ts'; describe('refractor', function () { context('elements', function () { context('SecuritySchemeElement', function () { - specify('should refract to semantic ApiDOM tree', function () { + specify('should refract apiKeySecurityScheme variant', function () { const element = SecuritySchemeElement.refract({ apiKeySecurityScheme: { name: 'X-API-Key', location: 'header' }, }); expect(sexprs(element)).toMatchSnapshot(); }); + + specify('should refract httpAuthSecurityScheme variant', function () { + const element = SecuritySchemeElement.refract({ + httpAuthSecurityScheme: { scheme: 'bearer', bearerFormat: 'JWT' }, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + + specify('should refract mtlsSecurityScheme variant', function () { + const element = SecuritySchemeElement.refract({ + mtlsSecurityScheme: { description: 'mTLS' }, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + + specify('should refract oauth2SecurityScheme variant', function () { + const element = SecuritySchemeElement.refract({ + oauth2SecurityScheme: { + flows: { clientCredentials: { tokenUrl: 'https://idp.example/token' } }, + oauth2MetadataUrl: 'https://idp.example/.well-known', + }, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); + + specify('should refract openIdConnectSecurityScheme variant', function () { + const element = SecuritySchemeElement.refract({ + openIdConnectSecurityScheme: { + openIdConnectUrl: 'https://idp.example/.well-known/openid-configuration', + }, + }); + + expect(sexprs(element)).toMatchSnapshot(); + }); }); }); }); diff --git a/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..5c952c5c0a --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/__snapshots__/index.ts.snap @@ -0,0 +1,54 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor plugins replace-empty-element should leave non-empty values untouched 1`] = ` +(A2aAgentCard1Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (AgentCapabilitiesElement + (MemberElement + (StringElement) + (BooleanElement))))) +`; + +exports[`refractor plugins replace-empty-element should replace empty capabilities with an AgentCapabilitiesElement 1`] = ` +(A2aAgentCard1Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (AgentCapabilitiesElement))) +`; + +exports[`refractor plugins replace-empty-element should replace empty provider with an AgentProviderElement 1`] = ` +(A2aAgentCard1Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (AgentProviderElement))) +`; + +exports[`refractor plugins replace-empty-element should replace empty securitySchemes with a SecuritySchemesElement 1`] = ` +(A2aAgentCard1Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement))) +`; + +exports[`refractor plugins replace-empty-element should replace empty skills with a SkillsElement 1`] = ` +(A2aAgentCard1Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement))) +`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/index.ts b/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/index.ts new file mode 100644 index 0000000000..d75c8e0ed2 --- /dev/null +++ b/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/index.ts @@ -0,0 +1,84 @@ +import { expect } from 'chai'; +import dedent from 'dedent'; +import { sexprs } from '@swagger-api/apidom-core'; +import { parse } from '@swagger-api/apidom-parser-adapter-yaml-1-2'; + +import { refractorPluginReplaceEmptyElement, AgentCardElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('plugins', function () { + context('replace-empty-element', function () { + specify( + 'should replace empty capabilities with an AgentCapabilitiesElement', + async function () { + const yamlDefinition = dedent` + name: agent example + capabilities: + `; + const apiDOM = await parse(yamlDefinition); + const agentCardElement = AgentCardElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(agentCardElement)).toMatchSnapshot(); + }, + ); + + specify('should replace empty provider with an AgentProviderElement', async function () { + const yamlDefinition = dedent` + name: agent example + provider: + `; + const apiDOM = await parse(yamlDefinition); + const agentCardElement = AgentCardElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(agentCardElement)).toMatchSnapshot(); + }); + + specify('should replace empty skills with a SkillsElement', async function () { + const yamlDefinition = dedent` + name: agent example + skills: + `; + const apiDOM = await parse(yamlDefinition); + const agentCardElement = AgentCardElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(agentCardElement)).toMatchSnapshot(); + }); + + specify( + 'should replace empty securitySchemes with a SecuritySchemesElement', + async function () { + const yamlDefinition = dedent` + name: agent example + securitySchemes: + `; + const apiDOM = await parse(yamlDefinition); + const agentCardElement = AgentCardElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(agentCardElement)).toMatchSnapshot(); + }, + ); + + specify('should leave non-empty values untouched', async function () { + const yamlDefinition = dedent` + name: agent example + capabilities: + streaming: true + `; + const apiDOM = await parse(yamlDefinition); + const agentCardElement = AgentCardElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(agentCardElement)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/real-world.ts b/packages/apidom-ns-a2a-1/test/refractor/real-world.ts index 1a8d17014e..c21faacfd9 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/real-world.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/real-world.ts @@ -36,7 +36,6 @@ describe('refractor', function () { specify('should produce typed scalar accessors', function () { const element = AgentCardElement.refract(raw) as AgentCardElement; assert.strictEqual(element.name?.toValue(), 'Langraph Planner Agent'); - assert.strictEqual(element.url?.toValue(), 'http://localhost:10102/'); assert.strictEqual(element.version?.toValue(), '1.0.0'); }); From c6e3c731bf61b5499ddf3a44628c5a5b253cee32 Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Thu, 4 Jun 2026 10:11:07 +0100 Subject: [PATCH 04/14] test(a2a): align parser adapters and reference parser tests (PROVCON-5343) Rewrite the a2a-json-1 and a2a-yaml-1 parser-adapter READMEs to match the format used by the other adapters (mediaTypes/detect/namespace/parse/usage sections), keeping the structural-detection note specific to A2A. Expand the apidom-reference a2a-json-1 and a2a-yaml-1 parser tests to mirror the coverage of the other parsers (canParse media-type/extension variants, parse from string and buffer, empty file, sourceMap enabled/disabled). Update adapter snapshots for the a2aAgentCard1 element rename. Co-Authored-By: Claude Opus 4.8 --- .../README.md | 81 ++++++++-- .../test/__snapshots__/adapter.ts.snap | 2 +- .../README.md | 81 ++++++++-- .../test/__snapshots__/adapter.ts.snap | 2 +- .../test/parse/parsers/a2a-json-1/index.ts | 142 ++++++++++++++++-- .../test/parse/parsers/a2a-yaml-1/index.ts | 142 ++++++++++++++++-- 6 files changed, 398 insertions(+), 52 deletions(-) diff --git a/packages/apidom-parser-adapter-a2a-json-1/README.md b/packages/apidom-parser-adapter-a2a-json-1/README.md index 5c9926cda0..655cb6d216 100644 --- a/packages/apidom-parser-adapter-a2a-json-1/README.md +++ b/packages/apidom-parser-adapter-a2a-json-1/README.md @@ -1,26 +1,89 @@ # @swagger-api/apidom-parser-adapter-a2a-json-1 -Parser adapter for [A2A (Agent-to-Agent) Protocol v1.0](https://a2a-protocol.org/latest/definitions/) AgentCard documents in JSON format. Refracts into [`@swagger-api/apidom-ns-a2a-1`](../apidom-ns-a2a-1). +`@swagger-api/apidom-parser-adapter-a2a-json-1` is a parser adapter for [A2A (Agent-to-Agent) Protocol v1.0](https://a2a-protocol.org/latest/specification/) AgentCard documents in [JSON format](https://www.json.org/json-en.html). +Under the hood this adapter uses [apidom-parser-adapter-json](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-json) +to parse a source string into generic ApiDOM in [base ApiDOM namespace](https://github.com/swagger-api/apidom/tree/main/packages/apidom-core#base-namespace) +which is then refracted with [A2A 1.x.y Refractors](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-a2a-1#refractors). ## Installation +After [prerequisites](https://github.com/swagger-api/apidom/blob/main/README.md#prerequisites) for installing this package are satisfied, you can install it +via [npm CLI](https://docs.npmjs.com/cli) by running the following command: + ```sh -npm install --save @swagger-api/apidom-parser-adapter-a2a-json-1 + $ npm install @swagger-api/apidom-parser-adapter-a2a-json-1 +``` + +## Parser adapter API + +This parser adapter is fully compatible with parser adapter interface required by [@swagger-api/apidom-parser](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser#mounting-parser-adapters) +and implements all required properties. + +### mediaTypes + +Defines list of media types that this parser adapter recognizes. + +```js +[ + 'application/vnd.a2a;version=1.0.0', + 'application/vnd.a2a+json;version=1.0.0', +] ``` +### detect + +A2A AgentCard documents have **no version discriminator field** (unlike OpenAPI's `"openapi": "3.1.0"` or Arazzo's `"arazzo": "1.0.1"`). [Detection](https://github.com/swagger-api/apidom/blob/main/packages/apidom-parser-adapter-a2a-json-1/src/adapter.ts) is therefore **structural**: a JSON document is treated as an A2A AgentCard when it parses as JSON and contains both a `capabilities` object and a `skills` array. False positives are possible — set the `mediaType` on the `File` explicitly when the type is known. + +### namespace + +This adapter exposes an instance of [A2A 1.x.y ApiDOM namespace](https://github.com/swagger-api/apidom/blob/main/packages/apidom-ns-a2a-1/README.md). + +### parse + +`parse` function consumes various options as a second argument. Here is a list of these options: + +Option | Type | Default | Description +--- | --- | --- | --- +`specObj` | `Object` | [Specification Object](https://github.com/swagger-api/apidom/blob/main/packages/apidom-ns-a2a-1/src/refractor/specification.ts) | This specification object drives the JSON AST transformation to A2A 1.x.y ApiDOM namespace. +`sourceMap` | `Boolean` | `false` | Indicate whether to generate source maps. +`refractorOpts` | `Object` | `{}` | Refractor options are passed to refractors during refracting phase. + +All unrecognized arbitrary options will be ignored. + ## Usage -```ts -import * as a2aJsonAdapter from '@swagger-api/apidom-parser-adapter-a2a-json-1'; -import ApiDOMParser from '@swagger-api/apidom-parser'; +This parser adapter can be used directly or indirectly via [@swagger-api/apidom-parser](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser). + +### Direct usage + +During direct usage you don't need to provide `mediaType` as the `parse` function is already pre-bound +with [supported media types](#mediatypes). + +```js +import { parse, detect } from '@swagger-api/apidom-parser-adapter-a2a-json-1'; + +// detecting +await detect('{ "capabilities": {}, "skills": [] }'); // => true +await detect('test'); // => false -const parser = new ApiDOMParser().use(a2aJsonAdapter); -const result = await parser.parse(jsonSource); +// parsing +const parseResult = await parse('{ "capabilities": {}, "skills": [] }', { sourceMap: true }); ``` -## Detection +### Indirect usage -A2A AgentCard documents have **no version discriminator field** (unlike OpenAPI's `"openapi": "3.1.0"`). This adapter uses **structural detection**: a JSON document is treated as an A2A AgentCard when it contains both a `capabilities` object and a `skills` array. False positives are possible — set the `mediaType` on the `File` explicitly when known. +You can omit the `mediaType` option here, but please read [Word on detect vs mediaTypes](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser#word-on-detect-vs-mediatypes) before you do so. + +```js +import ApiDOMParser from '@swagger-api/apidom-parser'; +import * as a2aJsonAdapter from '@swagger-api/apidom-parser-adapter-a2a-json-1'; + +const parser = new ApiDOMParser(); + +parser.use(a2aJsonAdapter); + +const parseResult = await parser.parse(jsonSource, { mediaType: a2aJsonAdapter.mediaTypes.latest('json') }); +``` ## License diff --git a/packages/apidom-parser-adapter-a2a-json-1/test/__snapshots__/adapter.ts.snap b/packages/apidom-parser-adapter-a2a-json-1/test/__snapshots__/adapter.ts.snap index 24f9b34fbd..6750117e0b 100644 --- a/packages/apidom-parser-adapter-a2a-json-1/test/__snapshots__/adapter.ts.snap +++ b/packages/apidom-parser-adapter-a2a-json-1/test/__snapshots__/adapter.ts.snap @@ -2,7 +2,7 @@ exports[`adapter should parse 1`] = ` (ParseResultElement - (AgentCardElement + (A2aAgentCard1Element (MemberElement (StringElement) (StringElement)) diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/README.md b/packages/apidom-parser-adapter-a2a-yaml-1/README.md index 5b0b9eacfd..3f21bc1f6a 100644 --- a/packages/apidom-parser-adapter-a2a-yaml-1/README.md +++ b/packages/apidom-parser-adapter-a2a-yaml-1/README.md @@ -1,26 +1,89 @@ # @swagger-api/apidom-parser-adapter-a2a-yaml-1 -Parser adapter for [A2A (Agent-to-Agent) Protocol v1.0](https://a2a-protocol.org/latest/definitions/) AgentCard documents in YAML 1.2 format. Refracts into [`@swagger-api/apidom-ns-a2a-1`](../apidom-ns-a2a-1). +`@swagger-api/apidom-parser-adapter-a2a-yaml-1` is a parser adapter for [A2A (Agent-to-Agent) Protocol v1.0](https://a2a-protocol.org/latest/specification/) AgentCard documents in [YAML format](https://yaml.org/spec/1.2/spec.html). +Under the hood this adapter uses [apidom-parser-adapter-yaml-1-2](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-yaml-1-2) +to parse a source string into generic ApiDOM in [base ApiDOM namespace](https://github.com/swagger-api/apidom/tree/main/packages/apidom-core#base-namespace) +which is then refracted with [A2A 1.x.y Refractors](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-a2a-1#refractors). ## Installation +After [prerequisites](https://github.com/swagger-api/apidom/blob/main/README.md#prerequisites) for installing this package are satisfied, you can install it +via [npm CLI](https://docs.npmjs.com/cli) by running the following command: + ```sh -npm install --save @swagger-api/apidom-parser-adapter-a2a-yaml-1 + $ npm install @swagger-api/apidom-parser-adapter-a2a-yaml-1 +``` + +## Parser adapter API + +This parser adapter is fully compatible with parser adapter interface required by [@swagger-api/apidom-parser](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser#mounting-parser-adapters) +and implements all required properties. + +### mediaTypes + +Defines list of media types that this parser adapter recognizes. + +```js +[ + 'application/vnd.a2a;version=1.0.0', + 'application/vnd.a2a+yaml;version=1.0.0', +] ``` +### detect + +A2A AgentCard documents have **no version discriminator field** (unlike OpenAPI's `"openapi": "3.1.0"` or Arazzo's `"arazzo": "1.0.1"`). [Detection](https://github.com/swagger-api/apidom/blob/main/packages/apidom-parser-adapter-a2a-yaml-1/src/adapter.ts) is therefore **structural**: a YAML document is treated as an A2A AgentCard when it parses as YAML and contains both a `capabilities` mapping and a `skills` sequence. False positives are possible — set the `mediaType` on the `File` explicitly when the type is known. + +### namespace + +This adapter exposes an instance of [A2A 1.x.y ApiDOM namespace](https://github.com/swagger-api/apidom/blob/main/packages/apidom-ns-a2a-1/README.md). + +### parse + +`parse` function consumes various options as a second argument. Here is a list of these options: + +Option | Type | Default | Description +--- | --- | --- | --- +`specObj` | `Object` | [Specification Object](https://github.com/swagger-api/apidom/blob/main/packages/apidom-ns-a2a-1/src/refractor/specification.ts) | This specification object drives the YAML AST transformation to A2A 1.x.y ApiDOM namespace. +`sourceMap` | `Boolean` | `false` | Indicate whether to generate source maps. +`refractorOpts` | `Object` | `{}` | Refractor options are passed to refractors during refracting phase. + +All unrecognized arbitrary options will be ignored. + ## Usage -```ts -import * as a2aYamlAdapter from '@swagger-api/apidom-parser-adapter-a2a-yaml-1'; -import ApiDOMParser from '@swagger-api/apidom-parser'; +This parser adapter can be used directly or indirectly via [@swagger-api/apidom-parser](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser). + +### Direct usage + +During direct usage you don't need to provide `mediaType` as the `parse` function is already pre-bound +with [supported media types](#mediatypes). + +```js +import { parse, detect } from '@swagger-api/apidom-parser-adapter-a2a-yaml-1'; + +// detecting +await detect('capabilities:\n streaming: true\nskills: []'); // => true +await detect('test'); // => false -const parser = new ApiDOMParser().use(a2aYamlAdapter); -const result = await parser.parse(yamlSource); +// parsing +const parseResult = await parse('capabilities:\n streaming: true\nskills: []', { sourceMap: true }); ``` -## Detection +### Indirect usage -A2A AgentCard documents have **no version discriminator field**. This adapter uses **structural detection**: a YAML or JSON document is treated as an A2A AgentCard when it contains both a `capabilities` mapping and a `skills` sequence. False positives are possible — set the `mediaType` on the `File` explicitly when known. +You can omit the `mediaType` option here, but please read [Word on detect vs mediaTypes](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser#word-on-detect-vs-mediatypes) before you do so. + +```js +import ApiDOMParser from '@swagger-api/apidom-parser'; +import * as a2aYamlAdapter from '@swagger-api/apidom-parser-adapter-a2a-yaml-1'; + +const parser = new ApiDOMParser(); + +parser.use(a2aYamlAdapter); + +const parseResult = await parser.parse(yamlSource, { mediaType: a2aYamlAdapter.mediaTypes.latest('yaml') }); +``` ## License diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/test/__snapshots__/adapter.ts.snap b/packages/apidom-parser-adapter-a2a-yaml-1/test/__snapshots__/adapter.ts.snap index 24f9b34fbd..6750117e0b 100644 --- a/packages/apidom-parser-adapter-a2a-yaml-1/test/__snapshots__/adapter.ts.snap +++ b/packages/apidom-parser-adapter-a2a-yaml-1/test/__snapshots__/adapter.ts.snap @@ -2,7 +2,7 @@ exports[`adapter should parse 1`] = ` (ParseResultElement - (AgentCardElement + (A2aAgentCard1Element (MemberElement (StringElement) (StringElement)) diff --git a/packages/apidom-reference/test/parse/parsers/a2a-json-1/index.ts b/packages/apidom-reference/test/parse/parsers/a2a-json-1/index.ts index 27af7538a8..00a5611eba 100644 --- a/packages/apidom-reference/test/parse/parsers/a2a-json-1/index.ts +++ b/packages/apidom-reference/test/parse/parsers/a2a-json-1/index.ts @@ -2,7 +2,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { assert } from 'chai'; -import { mediaTypes, isAgentCardElement } from '@swagger-api/apidom-ns-a2a-1'; +import { isParseResultElement, hasElementSourceMap } from '@swagger-api/apidom-core'; +import { isAgentCardElement } from '@swagger-api/apidom-ns-a2a-1'; +import { mediaTypes } from '@swagger-api/apidom-parser-adapter-a2a-json-1'; import A2AJSON1Parser from '../../../../src/parse/parsers/a2a-json-1/index.ts'; import File from '../../../../src/File.ts'; @@ -15,45 +17,153 @@ describe('parsers', function () { context('given file with .json extension', function () { context('and with proper media type', function () { specify('should return true', async function () { - const file = new File({ + const file1 = new File({ + uri: '/path/to/agent.json', + mediaType: mediaTypes.latest('generic'), + }); + const file2 = new File({ uri: '/path/to/agent.json', mediaType: mediaTypes.latest('json'), }); const parser = new A2AJSON1Parser(); - assert.isTrue(await parser.canParse(file)); + assert.isTrue(await parser.canParse(file1)); + assert.isTrue(await parser.canParse(file2)); }); }); - context('and with unknown media type but detectable content', function () { - specify('should return true', async function () { - const url = path.join(__dirname, 'fixtures', 'sample-agent-card.json'); - const file = new File({ uri: url, data: fs.readFileSync(url) }); + context('and with improper media type', function () { + specify('should return false', async function () { + const file = new File({ + uri: '/path/to/agent.json', + mediaType: 'application/vnd.aai.asyncapi+json;version=2.6.0', + }); const parser = new A2AJSON1Parser(); - assert.isTrue(await parser.canParse(file)); + assert.isFalse(await parser.canParse(file)); }); }); }); - context('given file with unsupported extension', function () { + context('given file with unknown extension', function () { + specify('should return false', async function () { + const file = new File({ + uri: '/path/to/agent.yaml', + mediaType: mediaTypes.latest('json'), + }); + const parser = new A2AJSON1Parser({ fileExtensions: ['.json'] }); + + assert.isFalse(await parser.canParse(file)); + }); + }); + + context('given file with no extension', function () { specify('should return false', async function () { - const file = new File({ uri: '/path/to/agent.yaml' }); + const file = new File({ + uri: '/path/to/agent', + mediaType: mediaTypes.latest('json'), + }); const parser = new A2AJSON1Parser({ fileExtensions: ['.json'] }); assert.isFalse(await parser.canParse(file)); }); }); + + context('given file with supported extension', function () { + context('and file data is buffer and can be detected as A2A AgentCard', function () { + specify('should return true', async function () { + const uri = path.join(__dirname, 'fixtures', 'sample-agent-card.json'); + const file = new File({ + uri: '/path/to/agent.json', + data: fs.readFileSync(uri), + }); + const parser = new A2AJSON1Parser(); + + assert.isTrue(await parser.canParse(file)); + }); + }); + + context('and file data is string and can be detected as A2A AgentCard', function () { + specify('should return true', async function () { + const uri = path.join(__dirname, 'fixtures', 'sample-agent-card.json'); + const file = new File({ + uri: '/path/to/agent.json', + data: fs.readFileSync(uri).toString(), + }); + const parser = new A2AJSON1Parser(); + + assert.isTrue(await parser.canParse(file)); + }); + }); + }); }); context('parse', function () { - specify('should return a parse result containing an AgentCardElement', async function () { - const url = path.join(__dirname, 'fixtures', 'sample-agent-card.json'); - const file = new File({ uri: url, data: fs.readFileSync(url) }); - const parser = new A2AJSON1Parser(); - const result = await parser.parse(file); + context('given A2A AgentCard JSON data', function () { + specify('should return parse result containing an AgentCardElement', async function () { + const uri = path.join(__dirname, 'fixtures', 'sample-agent-card.json'); + const data = fs.readFileSync(uri).toString(); + const file = new File({ uri, data, mediaType: mediaTypes.latest('json') }); + const parser = new A2AJSON1Parser(); + const parseResult = await parser.parse(file); + + assert.isTrue(isParseResultElement(parseResult)); + assert.isTrue(isAgentCardElement(parseResult.api)); + }); + }); - assert.isTrue(isAgentCardElement(result.api)); + context('given A2A AgentCard JSON data as buffer', function () { + specify('should return parse result', async function () { + const uri = path.join(__dirname, 'fixtures', 'sample-agent-card.json'); + const data = fs.readFileSync(uri); + const file = new File({ uri, data, mediaType: mediaTypes.latest('json') }); + const parser = new A2AJSON1Parser(); + const parseResult = await parser.parse(file); + + assert.isTrue(isParseResultElement(parseResult)); + assert.isTrue(isAgentCardElement(parseResult.api)); + }); + }); + + context('given empty file', function () { + specify('should return empty parse result', async function () { + const file = new File({ + uri: '/path/to/file.json', + data: '', + mediaType: mediaTypes.latest('json'), + }); + const parser = new A2AJSON1Parser(); + const parseResult = await parser.parse(file); + + assert.isTrue(isParseResultElement(parseResult)); + assert.isTrue(parseResult.isEmpty); + }); + }); + + context('sourceMap', function () { + context('given sourceMap enabled', function () { + specify('should decorate ApiDOM with source maps', async function () { + const uri = path.join(__dirname, 'fixtures', 'sample-agent-card.json'); + const data = fs.readFileSync(uri).toString(); + const file = new File({ uri, data, mediaType: mediaTypes.latest('json') }); + const parser = new A2AJSON1Parser({ sourceMap: true }); + const parseResult = await parser.parse(file); + + assert.isTrue(hasElementSourceMap(parseResult.api)); + }); + }); + + context('given sourceMap disabled', function () { + specify('should not decorate ApiDOM with source maps', async function () { + const uri = path.join(__dirname, 'fixtures', 'sample-agent-card.json'); + const data = fs.readFileSync(uri).toString(); + const file = new File({ uri, data, mediaType: mediaTypes.latest('json') }); + const parser = new A2AJSON1Parser(); + const parseResult = await parser.parse(file); + + assert.isUndefined(parseResult.api?.meta.get('sourceMap')); + }); + }); }); }); }); diff --git a/packages/apidom-reference/test/parse/parsers/a2a-yaml-1/index.ts b/packages/apidom-reference/test/parse/parsers/a2a-yaml-1/index.ts index c12bd52f54..f07f9a7135 100644 --- a/packages/apidom-reference/test/parse/parsers/a2a-yaml-1/index.ts +++ b/packages/apidom-reference/test/parse/parsers/a2a-yaml-1/index.ts @@ -2,7 +2,9 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { assert } from 'chai'; -import { mediaTypes, isAgentCardElement } from '@swagger-api/apidom-ns-a2a-1'; +import { isParseResultElement, hasElementSourceMap } from '@swagger-api/apidom-core'; +import { isAgentCardElement } from '@swagger-api/apidom-ns-a2a-1'; +import { mediaTypes } from '@swagger-api/apidom-parser-adapter-a2a-yaml-1'; import A2AYAML1Parser from '../../../../src/parse/parsers/a2a-yaml-1/index.ts'; import File from '../../../../src/File.ts'; @@ -15,45 +17,153 @@ describe('parsers', function () { context('given file with .yaml extension', function () { context('and with proper media type', function () { specify('should return true', async function () { - const file = new File({ + const file1 = new File({ + uri: '/path/to/agent.yaml', + mediaType: mediaTypes.latest('generic'), + }); + const file2 = new File({ uri: '/path/to/agent.yaml', mediaType: mediaTypes.latest('yaml'), }); const parser = new A2AYAML1Parser(); - assert.isTrue(await parser.canParse(file)); + assert.isTrue(await parser.canParse(file1)); + assert.isTrue(await parser.canParse(file2)); }); }); - context('and with unknown media type but detectable content', function () { - specify('should return true', async function () { - const url = path.join(__dirname, 'fixtures', 'sample-agent-card.yaml'); - const file = new File({ uri: url, data: fs.readFileSync(url) }); + context('and with improper media type', function () { + specify('should return false', async function () { + const file = new File({ + uri: '/path/to/agent.yaml', + mediaType: 'application/vnd.aai.asyncapi;version=2.6.0', + }); const parser = new A2AYAML1Parser(); - assert.isTrue(await parser.canParse(file)); + assert.isFalse(await parser.canParse(file)); }); }); }); - context('given file with unsupported extension', function () { + context('given file with unknown extension', function () { + specify('should return false', async function () { + const file = new File({ + uri: '/path/to/agent.json', + mediaType: mediaTypes.latest('yaml'), + }); + const parser = new A2AYAML1Parser({ fileExtensions: ['.yaml', '.yml'] }); + + assert.isFalse(await parser.canParse(file)); + }); + }); + + context('given file with no extension', function () { specify('should return false', async function () { - const file = new File({ uri: '/path/to/agent.txt' }); + const file = new File({ + uri: '/path/to/agent', + mediaType: mediaTypes.latest('yaml'), + }); const parser = new A2AYAML1Parser({ fileExtensions: ['.yaml', '.yml'] }); assert.isFalse(await parser.canParse(file)); }); }); + + context('given file with supported extension', function () { + context('and file data is buffer and can be detected as A2A AgentCard', function () { + specify('should return true', async function () { + const uri = path.join(__dirname, 'fixtures', 'sample-agent-card.yaml'); + const file = new File({ + uri: '/path/to/agent.yaml', + data: fs.readFileSync(uri), + }); + const parser = new A2AYAML1Parser(); + + assert.isTrue(await parser.canParse(file)); + }); + }); + + context('and file data is string and can be detected as A2A AgentCard', function () { + specify('should return true', async function () { + const uri = path.join(__dirname, 'fixtures', 'sample-agent-card.yaml'); + const file = new File({ + uri: '/path/to/agent.yaml', + data: fs.readFileSync(uri).toString(), + }); + const parser = new A2AYAML1Parser(); + + assert.isTrue(await parser.canParse(file)); + }); + }); + }); }); context('parse', function () { - specify('should return a parse result containing an AgentCardElement', async function () { - const url = path.join(__dirname, 'fixtures', 'sample-agent-card.yaml'); - const file = new File({ uri: url, data: fs.readFileSync(url) }); - const parser = new A2AYAML1Parser(); - const result = await parser.parse(file); + context('given A2A AgentCard YAML data', function () { + specify('should return parse result containing an AgentCardElement', async function () { + const uri = path.join(__dirname, 'fixtures', 'sample-agent-card.yaml'); + const data = fs.readFileSync(uri).toString(); + const file = new File({ uri, data, mediaType: mediaTypes.latest('yaml') }); + const parser = new A2AYAML1Parser(); + const parseResult = await parser.parse(file); + + assert.isTrue(isParseResultElement(parseResult)); + assert.isTrue(isAgentCardElement(parseResult.api)); + }); + }); - assert.isTrue(isAgentCardElement(result.api)); + context('given A2A AgentCard YAML data as buffer', function () { + specify('should return parse result', async function () { + const uri = path.join(__dirname, 'fixtures', 'sample-agent-card.yaml'); + const data = fs.readFileSync(uri); + const file = new File({ uri, data, mediaType: mediaTypes.latest('yaml') }); + const parser = new A2AYAML1Parser(); + const parseResult = await parser.parse(file); + + assert.isTrue(isParseResultElement(parseResult)); + assert.isTrue(isAgentCardElement(parseResult.api)); + }); + }); + + context('given empty file', function () { + specify('should return empty parse result', async function () { + const file = new File({ + uri: '/path/to/file.yaml', + data: '', + mediaType: mediaTypes.latest('yaml'), + }); + const parser = new A2AYAML1Parser(); + const parseResult = await parser.parse(file); + + assert.isTrue(isParseResultElement(parseResult)); + assert.isTrue(parseResult.isEmpty); + }); + }); + + context('sourceMap', function () { + context('given sourceMap enabled', function () { + specify('should decorate ApiDOM with source maps', async function () { + const uri = path.join(__dirname, 'fixtures', 'sample-agent-card.yaml'); + const data = fs.readFileSync(uri).toString(); + const file = new File({ uri, data, mediaType: mediaTypes.latest('yaml') }); + const parser = new A2AYAML1Parser({ sourceMap: true }); + const parseResult = await parser.parse(file); + + assert.isTrue(hasElementSourceMap(parseResult.api)); + }); + }); + + context('given sourceMap disabled', function () { + specify('should not decorate ApiDOM with source maps', async function () { + const uri = path.join(__dirname, 'fixtures', 'sample-agent-card.yaml'); + const data = fs.readFileSync(uri).toString(); + const file = new File({ uri, data, mediaType: mediaTypes.latest('yaml') }); + const parser = new A2AYAML1Parser(); + const parseResult = await parser.parse(file); + + assert.isUndefined(parseResult.api?.meta.get('sourceMap')); + }); + }); }); }); }); From a070a8d330cc7c53ca8110cfaa93b2f7e08a0d72 Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Thu, 4 Jun 2026 10:11:15 +0100 Subject: [PATCH 05/14] feat(ls): add A2A required-field lints and element docs (PROVCON-5343) Add required-field lint rules (hasRequiredField) for A2A elements per the spec: AgentCard, AgentSkill, AgentInterface, AgentProvider, AgentCardSignature and all OAuth flow types, with new codes and quick fixes. Add documentation for the A2A element configs that lacked it and wire it into each element's meta. Remove the obsolete AgentCard `url` lint/code/completion entry (the field was removed from the spec) and update the root element config key to a2aAgentCard1. Apply the replace-empty-element plugin on the A2A YAML parsing path in the parser factory. Co-Authored-By: Claude Opus 4.8 --- .../src/config/a2a/a2a1/completion.ts | 13 -- .../config/a2a/a2a1/lint/allowed-fields.ts | 1 - .../a2a/a2a1/lint/capabilities--required.ts | 28 +++ .../lint/default-input-modes--required.ts | 28 +++ .../lint/default-output-modes--required.ts | 28 +++ .../a2a/a2a1/lint/description--required.ts | 28 +++ .../src/config/a2a/a2a1/lint/index.ts | 18 +- .../config/a2a/a2a1/lint/name--required.ts | 28 +++ .../config/a2a/a2a1/lint/skills--required.ts | 28 +++ .../lint/supported-interfaces--required.ts | 28 +++ .../src/config/a2a/a2a1/lint/url--type.ts | 19 -- .../config/a2a/a2a1/lint/version--required.ts | 28 +++ .../a2a/agent-capabilities/documentation.ts | 30 +++ .../src/config/a2a/agent-capabilities/meta.ts | 2 + .../a2a/agent-card-signature/completion.ts | 6 +- .../a2a/agent-card-signature/documentation.ts | 25 +++ .../a2a/agent-card-signature/lint/index.ts | 11 +- .../lint/protected--required.ts | 28 +++ .../lint/signature--required.ts | 28 +++ .../config/a2a/agent-card-signature/meta.ts | 2 + .../a2a/agent-interface/documentation.ts | 30 +++ .../config/a2a/agent-interface/lint/index.ts | 14 +- .../lint/protocol-binding--required.ts | 28 +++ .../lint/protocol-version--required.ts | 28 +++ .../a2a/agent-interface/lint/url--required.ts | 28 +++ .../src/config/a2a/agent-interface/meta.ts | 2 + .../a2a/agent-provider/documentation.ts | 20 ++ .../config/a2a/agent-provider/lint/index.ts | 10 +- .../lint/organization--required.ts | 28 +++ .../a2a/agent-provider/lint/url--required.ts | 28 +++ .../src/config/a2a/agent-provider/meta.ts | 2 + .../config/a2a/agent-skill/documentation.ts | 50 +++++ .../agent-skill/lint/description--required.ts | 28 +++ .../a2a/agent-skill/lint/id--required.ts | 28 +++ .../src/config/a2a/agent-skill/lint/index.ts | 8 + .../a2a/agent-skill/lint/name--required.ts | 28 +++ .../a2a/agent-skill/lint/tags--required.ts | 28 +++ .../src/config/a2a/agent-skill/meta.ts | 2 + .../documentation.ts | 35 ++++ .../lint/authorization-url--required.ts | 28 +++ .../lint/index.ts | 4 + .../lint/token-url--required.ts | 28 +++ .../a2a/authorization-code-oauth-flow/meta.ts | 2 + .../documentation.ts | 25 +++ .../lint/index.ts | 3 +- .../lint/token-url--required.ts | 28 +++ .../a2a/client-credentials-oauth-flow/meta.ts | 2 + packages/apidom-ls/src/config/a2a/config.ts | 2 +- .../device-code-oauth-flow/documentation.ts | 30 +++ .../device-authorization-url--required.ts | 28 +++ .../a2a/device-code-oauth-flow/lint/index.ts | 4 + .../lint/token-url--required.ts | 28 +++ .../config/a2a/device-code-oauth-flow/meta.ts | 2 + .../a2a/implicit-oauth-flow/documentation.ts | 25 +++ .../lint/authorization-url--required.ts | 28 +++ .../a2a/implicit-oauth-flow/lint/index.ts | 9 +- .../config/a2a/implicit-oauth-flow/meta.ts | 2 + .../config/a2a/oauth-flows/documentation.ts | 36 ++++ .../a2a/oauth-flows/lint/allowed-fields.ts | 4 +- .../src/config/a2a/oauth-flows/meta.ts | 2 + .../a2a/password-oauth-flow/documentation.ts | 25 +++ .../a2a/password-oauth-flow/lint/index.ts | 3 +- .../lint/token-url--required.ts | 28 +++ .../config/a2a/password-oauth-flow/meta.ts | 2 + .../a2a/security-scheme/documentation.ts | 36 ++++ .../src/config/a2a/security-scheme/meta.ts | 2 + packages/apidom-ls/src/config/codes.ts | 27 ++- packages/apidom-ls/src/parser-factory.ts | 22 +- packages/apidom-ls/test/a2a.ts | 194 ++++++++++++++++++ .../a2a/agent-card-missing-required.json | 11 + .../test/fixtures/a2a/agent-card-valid.json | 8 +- 71 files changed, 1456 insertions(+), 54 deletions(-) create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/capabilities--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/default-input-modes--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/default-output-modes--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/description--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/name--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/skills--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/supported-interfaces--required.ts delete mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/a2a1/lint/version--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-capabilities/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-card-signature/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-card-signature/lint/protected--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-card-signature/lint/signature--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-interface/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-binding--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-version--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-interface/lint/url--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-provider/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-provider/lint/organization--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-provider/lint/url--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/description--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/id--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/name--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-skill/lint/tags--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/authorization-url--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/token-url--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/token-url--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/device-code-oauth-flow/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/device-authorization-url--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/token-url--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth-flows/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/security-scheme/documentation.ts create mode 100644 packages/apidom-ls/test/fixtures/a2a/agent-card-missing-required.json diff --git a/packages/apidom-ls/src/config/a2a/a2a1/completion.ts b/packages/apidom-ls/src/config/a2a/a2a1/completion.ts index 2278afcb29..cc5e0804e5 100644 --- a/packages/apidom-ls/src/config/a2a/a2a1/completion.ts +++ b/packages/apidom-ls/src/config/a2a/a2a1/completion.ts @@ -34,19 +34,6 @@ const completion: ApidomCompletionItem[] = [ }, targetSpecs: A2A1, }, - { - label: 'url', - insertText: 'url', - kind: 14, - format: CompletionFormat.QUOTED, - type: CompletionType.PROPERTY, - insertTextFormat: 2, - documentation: { - kind: 'markdown', - value: "The agent's primary endpoint URL. Must be a valid absolute HTTPS URL in production.", - }, - targetSpecs: A2A1, - }, { label: 'version', insertText: 'version', diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/allowed-fields.ts index 14a3397237..54d6c8577c 100644 --- a/packages/apidom-ls/src/config/a2a/a2a1/lint/allowed-fields.ts +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/allowed-fields.ts @@ -14,7 +14,6 @@ const allowedFieldsLint: LinterMeta = { [ 'name', 'description', - 'url', 'version', 'iconUrl', 'documentationUrl', diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/capabilities--required.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/capabilities--required.ts new file mode 100644 index 0000000000..3284024935 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/capabilities--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const capabilitiesRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_CAPABILITIES_REQUIRED, + source: 'apilint', + message: "should always have a 'capabilities' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['capabilities'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'capabilities' field", + action: 'addChild', + snippetYaml: 'capabilities:\n \n', + snippetJson: '"capabilities": {},\n', + }, + ], + }, +}; + +export default capabilitiesRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/default-input-modes--required.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/default-input-modes--required.ts new file mode 100644 index 0000000000..ba4de7d4ad --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/default-input-modes--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const defaultInputModesRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_DEFAULT_INPUT_MODES_REQUIRED, + source: 'apilint', + message: "should always have a 'defaultInputModes' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['defaultInputModes'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'defaultInputModes' field", + action: 'addChild', + snippetYaml: 'defaultInputModes:\n - \n', + snippetJson: '"defaultInputModes": [],\n', + }, + ], + }, +}; + +export default defaultInputModesRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/default-output-modes--required.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/default-output-modes--required.ts new file mode 100644 index 0000000000..9bf85fb67f --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/default-output-modes--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const defaultOutputModesRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_DEFAULT_OUTPUT_MODES_REQUIRED, + source: 'apilint', + message: "should always have a 'defaultOutputModes' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['defaultOutputModes'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'defaultOutputModes' field", + action: 'addChild', + snippetYaml: 'defaultOutputModes:\n - \n', + snippetJson: '"defaultOutputModes": [],\n', + }, + ], + }, +}; + +export default defaultOutputModesRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/description--required.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/description--required.ts new file mode 100644 index 0000000000..98b9a18b72 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/description--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const descriptionRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_DESCRIPTION_REQUIRED, + source: 'apilint', + message: "should always have a 'description' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['description'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'description' field", + action: 'addChild', + snippetYaml: "description: ''\n", + snippetJson: '"description": "",\n', + }, + ], + }, +}; + +export default descriptionRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/index.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/index.ts index 505d194a88..e368a88698 100644 --- a/packages/apidom-ls/src/config/a2a/a2a1/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/index.ts @@ -1,7 +1,14 @@ import allowedFieldsLint from './allowed-fields.ts'; +import nameRequiredLint from './name--required.ts'; +import descriptionRequiredLint from './description--required.ts'; +import supportedInterfacesRequiredLint from './supported-interfaces--required.ts'; +import versionRequiredLint from './version--required.ts'; +import capabilitiesRequiredLint from './capabilities--required.ts'; +import defaultInputModesRequiredLint from './default-input-modes--required.ts'; +import defaultOutputModesRequiredLint from './default-output-modes--required.ts'; +import skillsRequiredLint from './skills--required.ts'; import nameTypeLint from './name--type.ts'; import descriptionTypeLint from './description--type.ts'; -import urlTypeLint from './url--type.ts'; import versionTypeLint from './version--type.ts'; import iconUrlTypeLint from './icon-url--type.ts'; import documentationUrlTypeLint from './documentation-url--type.ts'; @@ -17,9 +24,16 @@ import signaturesTypeLint from './signatures--type.ts'; const lints = [ allowedFieldsLint, + nameRequiredLint, + descriptionRequiredLint, + supportedInterfacesRequiredLint, + versionRequiredLint, + capabilitiesRequiredLint, + defaultInputModesRequiredLint, + defaultOutputModesRequiredLint, + skillsRequiredLint, nameTypeLint, descriptionTypeLint, - urlTypeLint, versionTypeLint, iconUrlTypeLint, documentationUrlTypeLint, diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/name--required.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/name--required.ts new file mode 100644 index 0000000000..3c5fd679ea --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/name--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const nameRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_NAME_REQUIRED, + source: 'apilint', + message: "should always have a 'name' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['name'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'name' field", + action: 'addChild', + snippetYaml: "name: ''\n", + snippetJson: '"name": "",\n', + }, + ], + }, +}; + +export default nameRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/skills--required.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/skills--required.ts new file mode 100644 index 0000000000..89cf9605cd --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/skills--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const skillsRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_SKILLS_REQUIRED, + source: 'apilint', + message: "should always have a 'skills' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['skills'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'skills' field", + action: 'addChild', + snippetYaml: 'skills:\n - \n', + snippetJson: '"skills": [],\n', + }, + ], + }, +}; + +export default skillsRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/supported-interfaces--required.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/supported-interfaces--required.ts new file mode 100644 index 0000000000..583e1d2881 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/supported-interfaces--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const supportedInterfacesRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_SUPPORTED_INTERFACES_REQUIRED, + source: 'apilint', + message: "should always have a 'supportedInterfaces' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['supportedInterfaces'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'supportedInterfaces' field", + action: 'addChild', + snippetYaml: 'supportedInterfaces:\n - \n', + snippetJson: '"supportedInterfaces": [],\n', + }, + ], + }, +}; + +export default supportedInterfacesRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/url--type.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/url--type.ts deleted file mode 100644 index 6a1258a814..0000000000 --- a/packages/apidom-ls/src/config/a2a/a2a1/lint/url--type.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { DiagnosticSeverity } from 'vscode-languageserver-types'; - -import ApilintCodes from '../../../codes.ts'; -import { LinterMeta } from '../../../../apidom-language-types.ts'; -import { A2A1 } from '../../target-specs.ts'; - -const lint: LinterMeta = { - code: ApilintCodes.A2A1_AGENT_CARD_FIELD_URL_TYPE, - source: 'apilint', - message: "'url' must be a string", - severity: DiagnosticSeverity.Error, - linterFunction: 'apilintType', - linterParams: ['string'], - marker: 'value', - target: 'url', - targetSpecs: A2A1, -}; - -export default lint; diff --git a/packages/apidom-ls/src/config/a2a/a2a1/lint/version--required.ts b/packages/apidom-ls/src/config/a2a/a2a1/lint/version--required.ts new file mode 100644 index 0000000000..c4bbd41d31 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/a2a1/lint/version--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const versionRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_FIELD_VERSION_REQUIRED, + source: 'apilint', + message: "should always have a 'version' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['version'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'version' field", + action: 'addChild', + snippetYaml: "version: ''\n", + snippetJson: '"version": "",\n', + }, + ], + }, +}; + +export default versionRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-capabilities/documentation.ts b/packages/apidom-ls/src/config/a2a/agent-capabilities/documentation.ts new file mode 100644 index 0000000000..a24eb3235d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-capabilities/documentation.ts @@ -0,0 +1,30 @@ +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 AgentCapabilities fields. + * See [AgentCapabilities](https://a2a-protocol.org/latest/specification/#agentcapabilities). + */ +const documentation = [ + { + target: 'streaming', + docs: 'Indicates if the agent supports streaming responses (boolean).', + targetSpecs: A2A1, + }, + { + target: 'pushNotifications', + docs: 'Indicates if the agent supports sending push notifications for asynchronous task updates (boolean).', + targetSpecs: A2A1, + }, + { + target: 'extensions', + docs: 'Array of [Agent Extension Objects](https://a2a-protocol.org/latest/specification/#agentextension) — protocol extensions supported by the agent.', + targetSpecs: A2A1, + }, + { + target: 'extendedAgentCard', + docs: 'Indicates if the agent supports providing an extended agent card when authenticated (boolean).', + targetSpecs: A2A1, + }, +]; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/agent-capabilities/meta.ts b/packages/apidom-ls/src/config/a2a/agent-capabilities/meta.ts index 4c36077b61..6e1bbd7f60 100644 --- a/packages/apidom-ls/src/config/a2a/agent-capabilities/meta.ts +++ b/packages/apidom-ls/src/config/a2a/agent-capabilities/meta.ts @@ -1,10 +1,12 @@ import { FormatMeta } from '../../../apidom-language-types.ts'; import lint from './lint/index.ts'; import completion from './completion.ts'; +import documentation from './documentation.ts'; const meta: FormatMeta = { lint, completion, + documentation, }; export default meta; diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/completion.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/completion.ts index 550082a1a5..78d7468769 100644 --- a/packages/apidom-ls/src/config/a2a/agent-card-signature/completion.ts +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/completion.ts @@ -31,11 +31,7 @@ const completion: ApidomCompletionItem[] = [ CompletionFormat.QUOTED, 'Base64url-encoded signature value over the AgentCard payload.', ), - signatureField( - 'header', - CompletionFormat.OBJECT, - 'Unprotected JWS header parameters (JOSE).', - ), + signatureField('header', CompletionFormat.OBJECT, 'Unprotected JWS header parameters (JOSE).'), ]; export default completion; diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/documentation.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/documentation.ts new file mode 100644 index 0000000000..ab4cde9ff8 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/documentation.ts @@ -0,0 +1,25 @@ +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 AgentCardSignature fields (RFC 7515 JWS). + * See [AgentCardSignature](https://a2a-protocol.org/latest/specification/#agentcardsignature). + */ +const documentation = [ + { + target: 'protected', + docs: 'The protected JWS header for the signature (string, required). A base64url-encoded JSON object.', + targetSpecs: A2A1, + }, + { + target: 'signature', + docs: 'The computed signature, base64url-encoded (string, required).', + targetSpecs: A2A1, + }, + { + target: 'header', + docs: 'The unprotected JWS header values (object).', + targetSpecs: A2A1, + }, +]; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/index.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/index.ts index db3ba74823..efe8b613a8 100644 --- a/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/index.ts @@ -1,8 +1,17 @@ import allowedFieldsLint from './allowed-fields.ts'; +import protectedRequiredLint from './protected--required.ts'; +import signatureRequiredLint from './signature--required.ts'; import protectedLint from './protected--type.ts'; import signatureLint from './signature--type.ts'; import headerLint from './header--type.ts'; -const lints = [allowedFieldsLint, protectedLint, signatureLint, headerLint]; +const lints = [ + allowedFieldsLint, + protectedRequiredLint, + signatureRequiredLint, + protectedLint, + signatureLint, + headerLint, +]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/protected--required.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/protected--required.ts new file mode 100644 index 0000000000..8eb6c41b3b --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/protected--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const protectedRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_SIGNATURE_FIELD_PROTECTED_REQUIRED, + source: 'apilint', + message: "should always have a 'protected' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['protected'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'protected' field", + action: 'addChild', + snippetYaml: "protected: ''\n", + snippetJson: '"protected": "",\n', + }, + ], + }, +}; + +export default protectedRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/signature--required.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/signature--required.ts new file mode 100644 index 0000000000..d08f6614a8 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/lint/signature--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const signatureRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_CARD_SIGNATURE_FIELD_SIGNATURE_REQUIRED, + source: 'apilint', + message: "should always have a 'signature' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['signature'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'signature' field", + action: 'addChild', + snippetYaml: "signature: ''\n", + snippetJson: '"signature": "",\n', + }, + ], + }, +}; + +export default signatureRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/meta.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/meta.ts index 4c36077b61..6e1bbd7f60 100644 --- a/packages/apidom-ls/src/config/a2a/agent-card-signature/meta.ts +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/meta.ts @@ -1,10 +1,12 @@ import { FormatMeta } from '../../../apidom-language-types.ts'; import lint from './lint/index.ts'; import completion from './completion.ts'; +import documentation from './documentation.ts'; const meta: FormatMeta = { lint, completion, + documentation, }; export default meta; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/documentation.ts b/packages/apidom-ls/src/config/a2a/agent-interface/documentation.ts new file mode 100644 index 0000000000..7e2517ff70 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-interface/documentation.ts @@ -0,0 +1,30 @@ +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 AgentInterface fields. + * See [AgentInterface](https://a2a-protocol.org/latest/specification/#agentinterface). + */ +const documentation = [ + { + target: 'url', + docs: 'The URL where this interface is available (string, required). Must be a valid absolute HTTPS URL in production.', + targetSpecs: A2A1, + }, + { + target: 'protocolBinding', + docs: 'The protocol binding supported at this URL (string, required). Officially supported values: `JSONRPC`, `GRPC`, `HTTP+JSON`. Custom bindings SHOULD use a URI.', + targetSpecs: A2A1, + }, + { + target: 'protocolVersion', + docs: 'The version of the A2A protocol this interface exposes (string, required). Examples: `"0.3"`, `"1.0"`.', + targetSpecs: A2A1, + }, + { + target: 'tenant', + docs: 'Optional opaque routing identifier for a specific agent or tenant served behind a single A2A endpoint (string).', + targetSpecs: A2A1, + }, +]; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/lint/index.ts b/packages/apidom-ls/src/config/a2a/agent-interface/lint/index.ts index 3ec5efe036..6c2d8463b1 100644 --- a/packages/apidom-ls/src/config/a2a/agent-interface/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/agent-interface/lint/index.ts @@ -1,9 +1,21 @@ import allowedFieldsLint from './allowed-fields.ts'; +import urlRequiredLint from './url--required.ts'; +import protocolBindingRequiredLint from './protocol-binding--required.ts'; +import protocolVersionRequiredLint from './protocol-version--required.ts'; import urlLint from './url--type.ts'; import protocolBindingLint from './protocol-binding--type.ts'; import protocolVersionLint from './protocol-version--type.ts'; import tenantLint from './tenant--type.ts'; -const lints = [allowedFieldsLint, urlLint, protocolBindingLint, protocolVersionLint, tenantLint]; +const lints = [ + allowedFieldsLint, + urlRequiredLint, + protocolBindingRequiredLint, + protocolVersionRequiredLint, + urlLint, + protocolBindingLint, + protocolVersionLint, + tenantLint, +]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-binding--required.ts b/packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-binding--required.ts new file mode 100644 index 0000000000..15f74c22f2 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-binding--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const protocolBindingRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_INTERFACE_FIELD_PROTOCOL_BINDING_REQUIRED, + source: 'apilint', + message: "should always have a 'protocolBinding' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['protocolBinding'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'protocolBinding' field", + action: 'addChild', + snippetYaml: "protocolBinding: ''\n", + snippetJson: '"protocolBinding": "",\n', + }, + ], + }, +}; + +export default protocolBindingRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-version--required.ts b/packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-version--required.ts new file mode 100644 index 0000000000..ff291479f2 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-interface/lint/protocol-version--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const protocolVersionRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_INTERFACE_FIELD_PROTOCOL_VERSION_REQUIRED, + source: 'apilint', + message: "should always have a 'protocolVersion' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['protocolVersion'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'protocolVersion' field", + action: 'addChild', + snippetYaml: "protocolVersion: ''\n", + snippetJson: '"protocolVersion": "",\n', + }, + ], + }, +}; + +export default protocolVersionRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/lint/url--required.ts b/packages/apidom-ls/src/config/a2a/agent-interface/lint/url--required.ts new file mode 100644 index 0000000000..7e1a323753 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-interface/lint/url--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const urlRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_INTERFACE_FIELD_URL_REQUIRED, + source: 'apilint', + message: "should always have a 'url' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['url'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'url' field", + action: 'addChild', + snippetYaml: "url: ''\n", + snippetJson: '"url": "",\n', + }, + ], + }, +}; + +export default urlRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/meta.ts b/packages/apidom-ls/src/config/a2a/agent-interface/meta.ts index 4c36077b61..6e1bbd7f60 100644 --- a/packages/apidom-ls/src/config/a2a/agent-interface/meta.ts +++ b/packages/apidom-ls/src/config/a2a/agent-interface/meta.ts @@ -1,10 +1,12 @@ import { FormatMeta } from '../../../apidom-language-types.ts'; import lint from './lint/index.ts'; import completion from './completion.ts'; +import documentation from './documentation.ts'; const meta: FormatMeta = { lint, completion, + documentation, }; export default meta; diff --git a/packages/apidom-ls/src/config/a2a/agent-provider/documentation.ts b/packages/apidom-ls/src/config/a2a/agent-provider/documentation.ts new file mode 100644 index 0000000000..da7a2d3ad7 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-provider/documentation.ts @@ -0,0 +1,20 @@ +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 AgentProvider fields. + * See [AgentProvider](https://a2a-protocol.org/latest/specification/#agentprovider). + */ +const documentation = [ + { + target: 'organization', + docs: 'The name of the agent provider\'s organization (string, required). Example: `"Google"`.', + targetSpecs: A2A1, + }, + { + target: 'url', + docs: "A URL for the agent provider's website or relevant documentation (string, required).", + targetSpecs: A2A1, + }, +]; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/agent-provider/lint/index.ts b/packages/apidom-ls/src/config/a2a/agent-provider/lint/index.ts index 63e828e8af..7f6b43a225 100644 --- a/packages/apidom-ls/src/config/a2a/agent-provider/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/agent-provider/lint/index.ts @@ -1,7 +1,15 @@ import allowedFieldsLint from './allowed-fields.ts'; +import organizationRequiredLint from './organization--required.ts'; +import urlRequiredLint from './url--required.ts'; import organizationLint from './organization--type.ts'; import urlLint from './url--type.ts'; -const lints = [allowedFieldsLint, organizationLint, urlLint]; +const lints = [ + allowedFieldsLint, + organizationRequiredLint, + urlRequiredLint, + organizationLint, + urlLint, +]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/agent-provider/lint/organization--required.ts b/packages/apidom-ls/src/config/a2a/agent-provider/lint/organization--required.ts new file mode 100644 index 0000000000..7fca9e938f --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-provider/lint/organization--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const organizationRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_PROVIDER_FIELD_ORGANIZATION_REQUIRED, + source: 'apilint', + message: "should always have an 'organization' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['organization'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'organization' field", + action: 'addChild', + snippetYaml: "organization: ''\n", + snippetJson: '"organization": "",\n', + }, + ], + }, +}; + +export default organizationRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-provider/lint/url--required.ts b/packages/apidom-ls/src/config/a2a/agent-provider/lint/url--required.ts new file mode 100644 index 0000000000..62f7ba7e8e --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-provider/lint/url--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const urlRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_PROVIDER_FIELD_URL_REQUIRED, + source: 'apilint', + message: "should always have a 'url' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['url'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'url' field", + action: 'addChild', + snippetYaml: "url: ''\n", + snippetJson: '"url": "",\n', + }, + ], + }, +}; + +export default urlRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-provider/meta.ts b/packages/apidom-ls/src/config/a2a/agent-provider/meta.ts index 4c36077b61..6e1bbd7f60 100644 --- a/packages/apidom-ls/src/config/a2a/agent-provider/meta.ts +++ b/packages/apidom-ls/src/config/a2a/agent-provider/meta.ts @@ -1,10 +1,12 @@ import { FormatMeta } from '../../../apidom-language-types.ts'; import lint from './lint/index.ts'; import completion from './completion.ts'; +import documentation from './documentation.ts'; const meta: FormatMeta = { lint, completion, + documentation, }; export default meta; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/documentation.ts b/packages/apidom-ls/src/config/a2a/agent-skill/documentation.ts new file mode 100644 index 0000000000..5c23585dd5 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/documentation.ts @@ -0,0 +1,50 @@ +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 AgentSkill fields. + * See [AgentSkill](https://a2a-protocol.org/latest/specification/#agentskill). + */ +const documentation = [ + { + target: 'id', + docs: 'A unique identifier for the skill (string, required).', + targetSpecs: A2A1, + }, + { + target: 'name', + docs: 'A human-readable name for the skill (string, required).', + targetSpecs: A2A1, + }, + { + target: 'description', + docs: 'A detailed description of the skill (string, required).', + targetSpecs: A2A1, + }, + { + target: 'tags', + docs: "Array of keywords describing the skill's capabilities (array of strings, required).", + targetSpecs: A2A1, + }, + { + target: 'examples', + docs: 'Example prompts or scenarios this skill can handle (array of strings).', + targetSpecs: A2A1, + }, + { + target: 'inputModes', + docs: "Supported input media types for this skill, overriding the agent's defaults (array of strings).", + targetSpecs: A2A1, + }, + { + target: 'outputModes', + docs: "Supported output media types for this skill, overriding the agent's defaults (array of strings).", + targetSpecs: A2A1, + }, + { + target: 'securityRequirements', + docs: 'Array of [Security Requirement Objects](https://a2a-protocol.org/latest/specification/#securityrequirement) necessary for this skill.', + targetSpecs: A2A1, + }, +]; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/description--required.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/description--required.ts new file mode 100644 index 0000000000..8d9c4f1315 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/description--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const descriptionRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_SKILL_FIELD_DESCRIPTION_REQUIRED, + source: 'apilint', + message: "should always have a 'description' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['description'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'description' field", + action: 'addChild', + snippetYaml: "description: ''\n", + snippetJson: '"description": "",\n', + }, + ], + }, +}; + +export default descriptionRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/id--required.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/id--required.ts new file mode 100644 index 0000000000..e356d341b1 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/id--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const idRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_SKILL_FIELD_ID_REQUIRED, + source: 'apilint', + message: "should always have an 'id' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['id'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'id' field", + action: 'addChild', + snippetYaml: "id: ''\n", + snippetJson: '"id": "",\n', + }, + ], + }, +}; + +export default idRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/index.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/index.ts index e9edd8a3d6..62f68589c0 100644 --- a/packages/apidom-ls/src/config/a2a/agent-skill/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/index.ts @@ -1,4 +1,8 @@ import allowedFieldsLint from './allowed-fields.ts'; +import idRequiredLint from './id--required.ts'; +import nameRequiredLint from './name--required.ts'; +import descriptionRequiredLint from './description--required.ts'; +import tagsRequiredLint from './tags--required.ts'; import idLint from './id--type.ts'; import nameLint from './name--type.ts'; import descriptionLint from './description--type.ts'; @@ -10,6 +14,10 @@ import securityRequirementsLint from './security-requirements--type.ts'; const lints = [ allowedFieldsLint, + idRequiredLint, + nameRequiredLint, + descriptionRequiredLint, + tagsRequiredLint, idLint, nameLint, descriptionLint, diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/name--required.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/name--required.ts new file mode 100644 index 0000000000..d1aca9eaee --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/name--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const nameRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_SKILL_FIELD_NAME_REQUIRED, + source: 'apilint', + message: "should always have a 'name' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['name'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'name' field", + action: 'addChild', + snippetYaml: "name: ''\n", + snippetJson: '"name": "",\n', + }, + ], + }, +}; + +export default nameRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/lint/tags--required.ts b/packages/apidom-ls/src/config/a2a/agent-skill/lint/tags--required.ts new file mode 100644 index 0000000000..0f7cc5adfc --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-skill/lint/tags--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const tagsRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_SKILL_FIELD_TAGS_REQUIRED, + source: 'apilint', + message: "should always have a 'tags' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['tags'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'tags' field", + action: 'addChild', + snippetYaml: 'tags:\n - \n', + snippetJson: '"tags": [],\n', + }, + ], + }, +}; + +export default tagsRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/meta.ts b/packages/apidom-ls/src/config/a2a/agent-skill/meta.ts index 4c36077b61..6e1bbd7f60 100644 --- a/packages/apidom-ls/src/config/a2a/agent-skill/meta.ts +++ b/packages/apidom-ls/src/config/a2a/agent-skill/meta.ts @@ -1,10 +1,12 @@ import { FormatMeta } from '../../../apidom-language-types.ts'; import lint from './lint/index.ts'; import completion from './completion.ts'; +import documentation from './documentation.ts'; const meta: FormatMeta = { lint, completion, + documentation, }; export default meta; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/documentation.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/documentation.ts new file mode 100644 index 0000000000..cdb2ba976b --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/documentation.ts @@ -0,0 +1,35 @@ +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 AuthorizationCodeOAuthFlow fields. + * See [AuthorizationCodeOAuthFlow](https://a2a-protocol.org/latest/specification/#authorizationcodeoauthflow). + */ +const documentation = [ + { + target: 'authorizationUrl', + docs: 'The authorization URL to be used for this flow (string, required).', + targetSpecs: A2A1, + }, + { + target: 'tokenUrl', + docs: 'The token URL to be used for this flow (string, required).', + targetSpecs: A2A1, + }, + { + target: 'refreshUrl', + docs: 'The URL to be used for obtaining refresh tokens (string).', + targetSpecs: A2A1, + }, + { + target: 'scopes', + docs: 'A map of scope name to description available for the OAuth2 security scheme (required).', + targetSpecs: A2A1, + }, + { + target: 'pkceRequired', + docs: 'Indicates if PKCE (RFC 7636) is required for this flow (boolean).', + targetSpecs: A2A1, + }, +]; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/authorization-url--required.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/authorization-url--required.ts new file mode 100644 index 0000000000..75cc5cadec --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/authorization-url--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const authorizationUrlRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_REQUIRED, + source: 'apilint', + message: "should always have an 'authorizationUrl' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['authorizationUrl'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'authorizationUrl' field", + action: 'addChild', + snippetYaml: "authorizationUrl: ''\n", + snippetJson: '"authorizationUrl": "",\n', + }, + ], + }, +}; + +export default authorizationUrlRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/index.ts index fec8910cf7..be1dcd1063 100644 --- a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/index.ts @@ -1,4 +1,6 @@ import allowedFieldsLint from './allowed-fields.ts'; +import authorizationUrlRequiredLint from './authorization-url--required.ts'; +import tokenUrlRequiredLint from './token-url--required.ts'; import authorizationUrlLint from './authorization-url--type.ts'; import tokenUrlLint from './token-url--type.ts'; import refreshUrlLint from './refresh-url--type.ts'; @@ -7,6 +9,8 @@ import scopesLint from './scopes--type.ts'; const lints = [ allowedFieldsLint, + authorizationUrlRequiredLint, + tokenUrlRequiredLint, authorizationUrlLint, tokenUrlLint, refreshUrlLint, diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/token-url--required.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/token-url--required.ts new file mode 100644 index 0000000000..b0bfeb0975 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/token-url--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const tokenUrlRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED, + source: 'apilint', + message: "should always have a 'tokenUrl' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['tokenUrl'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'tokenUrl' field", + action: 'addChild', + snippetYaml: "tokenUrl: ''\n", + snippetJson: '"tokenUrl": "",\n', + }, + ], + }, +}; + +export default tokenUrlRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/meta.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/meta.ts index 4c36077b61..6e1bbd7f60 100644 --- a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/meta.ts +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/meta.ts @@ -1,10 +1,12 @@ import { FormatMeta } from '../../../apidom-language-types.ts'; import lint from './lint/index.ts'; import completion from './completion.ts'; +import documentation from './documentation.ts'; const meta: FormatMeta = { lint, completion, + documentation, }; export default meta; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/documentation.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/documentation.ts new file mode 100644 index 0000000000..db2c6de3bd --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/documentation.ts @@ -0,0 +1,25 @@ +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 ClientCredentialsOAuthFlow fields. + * See [ClientCredentialsOAuthFlow](https://a2a-protocol.org/latest/specification/#clientcredentialsoauthflow). + */ +const documentation = [ + { + target: 'tokenUrl', + docs: 'The token URL to be used for this flow (string, required).', + targetSpecs: A2A1, + }, + { + target: 'refreshUrl', + docs: 'The URL to be used for obtaining refresh tokens (string).', + targetSpecs: A2A1, + }, + { + target: 'scopes', + docs: 'A map of scope name to description available for the OAuth2 security scheme (required).', + targetSpecs: A2A1, + }, +]; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/index.ts index 4813c9dd15..3e388d4fb3 100644 --- a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/index.ts @@ -1,8 +1,9 @@ import allowedFieldsLint from './allowed-fields.ts'; +import tokenUrlRequiredLint from './token-url--required.ts'; import tokenUrlLint from './token-url--type.ts'; import refreshUrlLint from './refresh-url--type.ts'; import scopesLint from './scopes--type.ts'; -const lints = [allowedFieldsLint, tokenUrlLint, refreshUrlLint, scopesLint]; +const lints = [allowedFieldsLint, tokenUrlRequiredLint, tokenUrlLint, refreshUrlLint, scopesLint]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/token-url--required.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/token-url--required.ts new file mode 100644 index 0000000000..aceacc9639 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/token-url--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const tokenUrlRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED, + source: 'apilint', + message: "should always have a 'tokenUrl' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['tokenUrl'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'tokenUrl' field", + action: 'addChild', + snippetYaml: "tokenUrl: ''\n", + snippetJson: '"tokenUrl": "",\n', + }, + ], + }, +}; + +export default tokenUrlRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/meta.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/meta.ts index 4c36077b61..6e1bbd7f60 100644 --- a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/meta.ts +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/meta.ts @@ -1,10 +1,12 @@ import { FormatMeta } from '../../../apidom-language-types.ts'; import lint from './lint/index.ts'; import completion from './completion.ts'; +import documentation from './documentation.ts'; const meta: FormatMeta = { lint, completion, + documentation, }; export default meta; diff --git a/packages/apidom-ls/src/config/a2a/config.ts b/packages/apidom-ls/src/config/a2a/config.ts index a29feff634..a501a3d0b5 100644 --- a/packages/apidom-ls/src/config/a2a/config.ts +++ b/packages/apidom-ls/src/config/a2a/config.ts @@ -28,7 +28,7 @@ export default { }, ], }, - agentCard: a2a1Meta, + a2aAgentCard1: a2a1Meta, agentCapabilities: agentCapabilitiesMeta, agentSkill: agentSkillMeta, agentProvider: agentProviderMeta, diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/documentation.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/documentation.ts new file mode 100644 index 0000000000..1757c966ec --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/documentation.ts @@ -0,0 +1,30 @@ +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 DeviceCodeOAuthFlow fields (RFC 8628). + * See [DeviceCodeOAuthFlow](https://a2a-protocol.org/latest/specification/#devicecodeoauthflow). + */ +const documentation = [ + { + target: 'deviceAuthorizationUrl', + docs: 'The device authorization endpoint URL (string, required).', + targetSpecs: A2A1, + }, + { + target: 'tokenUrl', + docs: 'The token URL to be used for this flow (string, required).', + targetSpecs: A2A1, + }, + { + target: 'refreshUrl', + docs: 'The URL to be used for obtaining refresh tokens (string).', + targetSpecs: A2A1, + }, + { + target: 'scopes', + docs: 'A map of scope name to description available for the OAuth2 security scheme (required).', + targetSpecs: A2A1, + }, +]; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/device-authorization-url--required.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/device-authorization-url--required.ts new file mode 100644 index 0000000000..397b987a25 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/device-authorization-url--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const deviceAuthorizationUrlRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_DEVICE_AUTHORIZATION_URL_REQUIRED, + source: 'apilint', + message: "should always have a 'deviceAuthorizationUrl' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['deviceAuthorizationUrl'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'deviceAuthorizationUrl' field", + action: 'addChild', + snippetYaml: "deviceAuthorizationUrl: ''\n", + snippetJson: '"deviceAuthorizationUrl": "",\n', + }, + ], + }, +}; + +export default deviceAuthorizationUrlRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/index.ts index 47bd172b8c..72f0500db8 100644 --- a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/index.ts @@ -1,4 +1,6 @@ import allowedFieldsLint from './allowed-fields.ts'; +import deviceAuthorizationUrlRequiredLint from './device-authorization-url--required.ts'; +import tokenUrlRequiredLint from './token-url--required.ts'; import deviceAuthorizationUrlLint from './device-authorization-url--type.ts'; import tokenUrlLint from './token-url--type.ts'; import refreshUrlLint from './refresh-url--type.ts'; @@ -6,6 +8,8 @@ import scopesLint from './scopes--type.ts'; const lints = [ allowedFieldsLint, + deviceAuthorizationUrlRequiredLint, + tokenUrlRequiredLint, deviceAuthorizationUrlLint, tokenUrlLint, refreshUrlLint, diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/token-url--required.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/token-url--required.ts new file mode 100644 index 0000000000..b5dd95c755 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/token-url--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const tokenUrlRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED, + source: 'apilint', + message: "should always have a 'tokenUrl' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['tokenUrl'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'tokenUrl' field", + action: 'addChild', + snippetYaml: "tokenUrl: ''\n", + snippetJson: '"tokenUrl": "",\n', + }, + ], + }, +}; + +export default tokenUrlRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/meta.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/meta.ts index 4c36077b61..6e1bbd7f60 100644 --- a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/meta.ts +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/meta.ts @@ -1,10 +1,12 @@ import { FormatMeta } from '../../../apidom-language-types.ts'; import lint from './lint/index.ts'; import completion from './completion.ts'; +import documentation from './documentation.ts'; const meta: FormatMeta = { lint, completion, + documentation, }; export default meta; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/documentation.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/documentation.ts new file mode 100644 index 0000000000..42c4afa91a --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/documentation.ts @@ -0,0 +1,25 @@ +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 ImplicitOAuthFlow fields (deprecated flow). + * See [ImplicitOAuthFlow](https://a2a-protocol.org/latest/specification/#implicitoauthflow). + */ +const documentation = [ + { + target: 'authorizationUrl', + docs: 'The authorization URL to be used for this flow (string, required).', + targetSpecs: A2A1, + }, + { + target: 'refreshUrl', + docs: 'The URL to be used for obtaining refresh tokens (string).', + targetSpecs: A2A1, + }, + { + target: 'scopes', + docs: 'A map of scope name to description available for the OAuth2 security scheme (required).', + targetSpecs: A2A1, + }, +]; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--required.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--required.ts new file mode 100644 index 0000000000..502df5bad4 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const authorizationUrlRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_IMPLICIT_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_REQUIRED, + source: 'apilint', + message: "should always have an 'authorizationUrl' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['authorizationUrl'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'authorizationUrl' field", + action: 'addChild', + snippetYaml: "authorizationUrl: ''\n", + snippetJson: '"authorizationUrl": "",\n', + }, + ], + }, +}; + +export default authorizationUrlRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/index.ts index c0e25a4987..cb98dff2dc 100644 --- a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/index.ts @@ -1,8 +1,15 @@ import allowedFieldsLint from './allowed-fields.ts'; +import authorizationUrlRequiredLint from './authorization-url--required.ts'; import authorizationUrlLint from './authorization-url--type.ts'; import refreshUrlLint from './refresh-url--type.ts'; import scopesLint from './scopes--type.ts'; -const lints = [allowedFieldsLint, authorizationUrlLint, refreshUrlLint, scopesLint]; +const lints = [ + allowedFieldsLint, + authorizationUrlRequiredLint, + authorizationUrlLint, + refreshUrlLint, + scopesLint, +]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/meta.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/meta.ts index 4c36077b61..6e1bbd7f60 100644 --- a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/meta.ts +++ b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/meta.ts @@ -1,10 +1,12 @@ import { FormatMeta } from '../../../apidom-language-types.ts'; import lint from './lint/index.ts'; import completion from './completion.ts'; +import documentation from './documentation.ts'; const meta: FormatMeta = { lint, completion, + documentation, }; export default meta; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/documentation.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/documentation.ts new file mode 100644 index 0000000000..bdf21f2d7a --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/documentation.ts @@ -0,0 +1,36 @@ +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 OAuthFlows fields. OAuthFlows must contain + * exactly one of the flow configurations below. + * See [OAuthFlows](https://a2a-protocol.org/latest/specification/#oauthflows). + */ +const documentation = [ + { + target: 'authorizationCode', + docs: '[Authorization Code OAuth Flow](https://a2a-protocol.org/latest/specification/#authorizationcodeoauthflow) — configuration for the OAuth Authorization Code flow.', + targetSpecs: A2A1, + }, + { + target: 'clientCredentials', + docs: '[Client Credentials OAuth Flow](https://a2a-protocol.org/latest/specification/#clientcredentialsoauthflow) — configuration for the OAuth Client Credentials flow.', + targetSpecs: A2A1, + }, + { + target: 'deviceCode', + docs: '[Device Code OAuth Flow](https://a2a-protocol.org/latest/specification/#devicecodeoauthflow) — configuration for the OAuth Device Code flow (RFC 8628).', + targetSpecs: A2A1, + }, + { + target: 'implicit', + docs: '[Implicit OAuth Flow](https://a2a-protocol.org/latest/specification/#implicitoauthflow) — deprecated; use Authorization Code + PKCE instead.', + targetSpecs: A2A1, + }, + { + target: 'password', + docs: '[Password OAuth Flow](https://a2a-protocol.org/latest/specification/#passwordoauthflow) — deprecated; use Authorization Code + PKCE or Device Code.', + targetSpecs: A2A1, + }, +]; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts index 583565eb88..6b4e0a9173 100644 --- a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts @@ -10,9 +10,7 @@ const allowedFieldsLint: LinterMeta = { message: 'Object includes not allowed fields', severity: DiagnosticSeverity.Error, linterFunction: 'allowedFields', - linterParams: [ - ['authorizationCode', 'clientCredentials', 'deviceCode', 'implicit', 'password'], - ], + linterParams: [['authorizationCode', 'clientCredentials', 'deviceCode', 'implicit', 'password']], marker: 'key', targetSpecs: A2A1, }; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/meta.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/meta.ts index 4c36077b61..6e1bbd7f60 100644 --- a/packages/apidom-ls/src/config/a2a/oauth-flows/meta.ts +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/meta.ts @@ -1,10 +1,12 @@ import { FormatMeta } from '../../../apidom-language-types.ts'; import lint from './lint/index.ts'; import completion from './completion.ts'; +import documentation from './documentation.ts'; const meta: FormatMeta = { lint, completion, + documentation, }; export default meta; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/documentation.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/documentation.ts new file mode 100644 index 0000000000..61634d2863 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/password-oauth-flow/documentation.ts @@ -0,0 +1,25 @@ +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 PasswordOAuthFlow fields (deprecated flow). + * See [PasswordOAuthFlow](https://a2a-protocol.org/latest/specification/#passwordoauthflow). + */ +const documentation = [ + { + target: 'tokenUrl', + docs: 'The token URL to be used for this flow (string, required).', + targetSpecs: A2A1, + }, + { + target: 'refreshUrl', + docs: 'The URL to be used for obtaining refresh tokens (string).', + targetSpecs: A2A1, + }, + { + target: 'scopes', + docs: 'A map of scope name to description available for the OAuth2 security scheme (required).', + targetSpecs: A2A1, + }, +]; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/index.ts index 4813c9dd15..3e388d4fb3 100644 --- a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/index.ts @@ -1,8 +1,9 @@ import allowedFieldsLint from './allowed-fields.ts'; +import tokenUrlRequiredLint from './token-url--required.ts'; import tokenUrlLint from './token-url--type.ts'; import refreshUrlLint from './refresh-url--type.ts'; import scopesLint from './scopes--type.ts'; -const lints = [allowedFieldsLint, tokenUrlLint, refreshUrlLint, scopesLint]; +const lints = [allowedFieldsLint, tokenUrlRequiredLint, tokenUrlLint, refreshUrlLint, scopesLint]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--required.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--required.ts new file mode 100644 index 0000000000..026c8894f6 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const tokenUrlRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_PASSWORD_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED, + source: 'apilint', + message: "should always have a 'tokenUrl' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['tokenUrl'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'tokenUrl' field", + action: 'addChild', + snippetYaml: "tokenUrl: ''\n", + snippetJson: '"tokenUrl": "",\n', + }, + ], + }, +}; + +export default tokenUrlRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/meta.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/meta.ts index 4c36077b61..6e1bbd7f60 100644 --- a/packages/apidom-ls/src/config/a2a/password-oauth-flow/meta.ts +++ b/packages/apidom-ls/src/config/a2a/password-oauth-flow/meta.ts @@ -1,10 +1,12 @@ import { FormatMeta } from '../../../apidom-language-types.ts'; import lint from './lint/index.ts'; import completion from './completion.ts'; +import documentation from './documentation.ts'; const meta: FormatMeta = { lint, completion, + documentation, }; export default meta; diff --git a/packages/apidom-ls/src/config/a2a/security-scheme/documentation.ts b/packages/apidom-ls/src/config/a2a/security-scheme/documentation.ts new file mode 100644 index 0000000000..180bfb1481 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/security-scheme/documentation.ts @@ -0,0 +1,36 @@ +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 SecurityScheme fields. SecurityScheme is a + * discriminated union — exactly one of the subtype fields must be set. + * See [SecurityScheme](https://a2a-protocol.org/latest/specification/#securityscheme). + */ +const documentation = [ + { + target: 'apiKeySecurityScheme', + docs: '[API Key Security Scheme](https://a2a-protocol.org/latest/specification/#apikeysecurityscheme) — API key-based authentication. Set this OR exactly one other subtype.', + targetSpecs: A2A1, + }, + { + target: 'httpAuthSecurityScheme', + docs: '[HTTP Auth Security Scheme](https://a2a-protocol.org/latest/specification/#httpauthsecurityscheme) — HTTP authentication (Basic, Bearer, etc.). Set this OR exactly one other subtype.', + targetSpecs: A2A1, + }, + { + target: 'oauth2SecurityScheme', + docs: '[OAuth2 Security Scheme](https://a2a-protocol.org/latest/specification/#oauth2securityscheme) — OAuth 2.0 authentication. Set this OR exactly one other subtype.', + targetSpecs: A2A1, + }, + { + target: 'openIdConnectSecurityScheme', + docs: '[OpenID Connect Security Scheme](https://a2a-protocol.org/latest/specification/#openidconnectsecurityscheme) — OpenID Connect authentication. Set this OR exactly one other subtype.', + targetSpecs: A2A1, + }, + { + target: 'mtlsSecurityScheme', + docs: '[Mutual TLS Security Scheme](https://a2a-protocol.org/latest/specification/#mutualtlssecurityscheme) — mutual TLS authentication. Set this OR exactly one other subtype.', + targetSpecs: A2A1, + }, +]; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/security-scheme/meta.ts b/packages/apidom-ls/src/config/a2a/security-scheme/meta.ts index 4c36077b61..6e1bbd7f60 100644 --- a/packages/apidom-ls/src/config/a2a/security-scheme/meta.ts +++ b/packages/apidom-ls/src/config/a2a/security-scheme/meta.ts @@ -1,10 +1,12 @@ import { FormatMeta } from '../../../apidom-language-types.ts'; import lint from './lint/index.ts'; import completion from './completion.ts'; +import documentation from './documentation.ts'; const meta: FormatMeta = { lint, completion, + documentation, }; export default meta; diff --git a/packages/apidom-ls/src/config/codes.ts b/packages/apidom-ls/src/config/codes.ts index c57299e09a..fdeecd0035 100644 --- a/packages/apidom-ls/src/config/codes.ts +++ b/packages/apidom-ls/src/config/codes.ts @@ -1441,7 +1441,6 @@ enum ApilintCodes { A2A1_AGENT_CARD = 9010000, A2A1_AGENT_CARD_FIELD_NAME_TYPE = 9010100, A2A1_AGENT_CARD_FIELD_DESCRIPTION_TYPE = 9010200, - A2A1_AGENT_CARD_FIELD_URL_TYPE = 9010300, A2A1_AGENT_CARD_FIELD_VERSION_TYPE = 9010400, A2A1_AGENT_CARD_FIELD_ICON_URL_TYPE = 9010500, A2A1_AGENT_CARD_FIELD_DOCUMENTATION_URL_TYPE = 9010600, @@ -1454,6 +1453,14 @@ enum ApilintCodes { A2A1_AGENT_CARD_FIELD_SECURITY_SCHEMES_TYPE = 9011300, A2A1_AGENT_CARD_FIELD_SECURITY_REQUIREMENTS_TYPE = 9011400, A2A1_AGENT_CARD_FIELD_SIGNATURES_TYPE = 9011500, + A2A1_AGENT_CARD_FIELD_NAME_REQUIRED = 9011600, + A2A1_AGENT_CARD_FIELD_DESCRIPTION_REQUIRED = 9011700, + A2A1_AGENT_CARD_FIELD_SUPPORTED_INTERFACES_REQUIRED = 9011800, + A2A1_AGENT_CARD_FIELD_VERSION_REQUIRED = 9011900, + A2A1_AGENT_CARD_FIELD_CAPABILITIES_REQUIRED = 9012000, + A2A1_AGENT_CARD_FIELD_DEFAULT_INPUT_MODES_REQUIRED = 9012100, + A2A1_AGENT_CARD_FIELD_DEFAULT_OUTPUT_MODES_REQUIRED = 9012200, + A2A1_AGENT_CARD_FIELD_SKILLS_REQUIRED = 9012300, A2A1_AGENT_CAPABILITIES = 9020000, A2A1_AGENT_CAPABILITIES_FIELD_STREAMING_TYPE = 9020100, @@ -1470,16 +1477,25 @@ enum ApilintCodes { A2A1_AGENT_SKILL_FIELD_INPUT_MODES_TYPE = 9030600, A2A1_AGENT_SKILL_FIELD_OUTPUT_MODES_TYPE = 9030700, A2A1_AGENT_SKILL_FIELD_SECURITY_REQUIREMENTS_TYPE = 9030800, + A2A1_AGENT_SKILL_FIELD_ID_REQUIRED = 9030900, + A2A1_AGENT_SKILL_FIELD_NAME_REQUIRED = 9031000, + A2A1_AGENT_SKILL_FIELD_DESCRIPTION_REQUIRED = 9031100, + A2A1_AGENT_SKILL_FIELD_TAGS_REQUIRED = 9031200, A2A1_AGENT_PROVIDER = 9040000, A2A1_AGENT_PROVIDER_FIELD_ORGANIZATION_TYPE = 9040100, A2A1_AGENT_PROVIDER_FIELD_URL_TYPE = 9040200, + A2A1_AGENT_PROVIDER_FIELD_ORGANIZATION_REQUIRED = 9040300, + A2A1_AGENT_PROVIDER_FIELD_URL_REQUIRED = 9040400, A2A1_AGENT_INTERFACE = 9050000, A2A1_AGENT_INTERFACE_FIELD_URL_TYPE = 9050100, A2A1_AGENT_INTERFACE_FIELD_PROTOCOL_BINDING_TYPE = 9050200, A2A1_AGENT_INTERFACE_FIELD_PROTOCOL_VERSION_TYPE = 9050300, A2A1_AGENT_INTERFACE_FIELD_TENANT_TYPE = 9050400, + A2A1_AGENT_INTERFACE_FIELD_URL_REQUIRED = 9050500, + A2A1_AGENT_INTERFACE_FIELD_PROTOCOL_BINDING_REQUIRED = 9050600, + A2A1_AGENT_INTERFACE_FIELD_PROTOCOL_VERSION_REQUIRED = 9050700, A2A1_SECURITY_SCHEME = 9060000, A2A1_SECURITY_SCHEME_FIELD_API_KEY_TYPE = 9060100, @@ -1492,6 +1508,8 @@ enum ApilintCodes { A2A1_AGENT_CARD_SIGNATURE_FIELD_PROTECTED_TYPE = 9070100, A2A1_AGENT_CARD_SIGNATURE_FIELD_SIGNATURE_TYPE = 9070200, A2A1_AGENT_CARD_SIGNATURE_FIELD_HEADER_TYPE = 9070300, + A2A1_AGENT_CARD_SIGNATURE_FIELD_PROTECTED_REQUIRED = 9070400, + A2A1_AGENT_CARD_SIGNATURE_FIELD_SIGNATURE_REQUIRED = 9070500, A2A1_OAUTH_FLOWS = 9080000, A2A1_OAUTH_FLOWS_FIELD_AUTHORIZATION_CODE_TYPE = 9080100, @@ -1506,27 +1524,34 @@ enum ApilintCodes { A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE = 9090300, A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_PKCE_REQUIRED_TYPE = 9090400, A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9090500, + A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_REQUIRED = 9090600, + A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED = 9090700, A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW = 9100000, A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE = 9100100, A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE = 9100200, A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9100300, + A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED = 9100400, A2A1_DEVICE_CODE_OAUTH_FLOW = 9110000, A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_DEVICE_AUTHORIZATION_URL_TYPE = 9110100, A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE = 9110200, A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE = 9110300, A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9110400, + A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_DEVICE_AUTHORIZATION_URL_REQUIRED = 9110500, + A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED = 9110600, A2A1_IMPLICIT_OAUTH_FLOW = 9120000, A2A1_IMPLICIT_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_TYPE = 9120100, A2A1_IMPLICIT_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE = 9120200, A2A1_IMPLICIT_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9120300, + A2A1_IMPLICIT_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_REQUIRED = 9120400, A2A1_PASSWORD_OAUTH_FLOW = 9130000, A2A1_PASSWORD_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE = 9130100, A2A1_PASSWORD_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE = 9130200, A2A1_PASSWORD_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9130300, + A2A1_PASSWORD_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED = 9130400, } export default ApilintCodes; diff --git a/packages/apidom-ls/src/parser-factory.ts b/packages/apidom-ls/src/parser-factory.ts index e1667e388d..2160b29a81 100644 --- a/packages/apidom-ls/src/parser-factory.ts +++ b/packages/apidom-ls/src/parser-factory.ts @@ -22,6 +22,7 @@ import { refractorPluginReplaceEmptyElement as refractorPluginReplaceEmptyElemen import { refractorPluginReplaceEmptyElement as refractorPluginReplaceEmptyElementOpenAPI3_0 } from '@swagger-api/apidom-ns-openapi-3-0'; import { refractorPluginReplaceEmptyElement as refractorPluginReplaceEmptyElementOpenAPI3_1 } from '@swagger-api/apidom-ns-openapi-3-1'; import { refractorPluginReplaceEmptyElement as refractorPluginReplaceEmptyElementOpenAPI3_2 } from '@swagger-api/apidom-ns-openapi-3-2'; +import { refractorPluginReplaceEmptyElement as refractorPluginReplaceEmptyElementA2A1 } from '@swagger-api/apidom-ns-a2a-1'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { ParseResultElement } from '@swagger-api/apidom-core'; @@ -222,9 +223,26 @@ export async function parse( result = await openapi3_2AdapterYaml.parse(text, options); } else if (contentLanguage.namespace === 'a2a' && contentLanguage.format === 'JSON') { - result = await a2a1AdapterJson.parse(text, { sourceMap: true }); + const options: Record = { + sourceMap: true, + refractorOpts: { + plugins: [...(refractorPlugins?.['a2a-1'] || [])], + }, + }; + + result = await a2a1AdapterJson.parse(text, options); } else if (contentLanguage.namespace === 'a2a' && contentLanguage.format === 'YAML') { - result = await a2a1AdapterYaml.parse(text, { sourceMap: true }); + const options: Record = { + sourceMap: true, + refractorOpts: { + plugins: [ + registerPlugins && refractorPluginReplaceEmptyElementA2A1(), + ...(refractorPlugins?.['a2a-1'] || []), + ].filter(Boolean), + }, + }; + + result = await a2a1AdapterYaml.parse(text, options); } else if (contentLanguage.namespace === 'ads' && contentLanguage.format === 'JSON') { result = await adsAdapterJson.parse(text, { sourceMap: true }); } else if (contentLanguage.namespace === 'ads' && contentLanguage.format === 'YAML') { diff --git a/packages/apidom-ls/test/a2a.ts b/packages/apidom-ls/test/a2a.ts index 7effc03bf3..8be60d600b 100644 --- a/packages/apidom-ls/test/a2a.ts +++ b/packages/apidom-ls/test/a2a.ts @@ -20,11 +20,23 @@ const agentCardValid = fs .readFileSync(path.join(__dirname, 'fixtures', 'a2a', 'agent-card-valid.json')) .toString(); +const agentCardMissingRequired = fs + .readFileSync(path.join(__dirname, 'fixtures', 'a2a', 'agent-card-missing-required.json')) + .toString(); + describe('apidom-ls-a2a', function () { const context: LanguageServiceContext = { metadata: metadata(), performanceLogs: logPerformance, logLevel, + // A2A AgentCard documents carry no version discriminator field, so the + // spec version used for lint-rule targeting is pinned to A2A v1. + defaultContentLanguage: { + namespace: 'a2a', + version: '1.0.0', + format: 'JSON', + mediaType: 'application/vnd.a2a+json;version=1.0.0', + }, }; const languageService: LanguageService = getLanguageService(context); @@ -52,4 +64,186 @@ describe('apidom-ls-a2a', function () { const result = await languageService.doValidation(doc, validationContext); assert.deepEqual(result, [] as Diagnostic[]); }); + + it('reports missing required fields on AgentCard and nested AgentSkill', async function () { + this.timeout(10000); + + const validationContext: ValidationContext = { + comments: DiagnosticSeverity.Error, + maxNumberOfProblems: 100, + relatedInformation: false, + }; + + const doc: TextDocument = TextDocument.create( + 'foo://bar/agent-card-missing.json', + 'json', + 0, + agentCardMissingRequired, + ); + + const result = await languageService.doValidation(doc, validationContext); + const expected: Diagnostic[] = [ + { + range: { start: { line: 0, character: 0 }, end: { line: 1, character: 3 } }, + message: "should always have a 'description' field", + severity: 1, + code: 9011700, + source: 'apilint', + data: { + quickFix: [ + { + message: "add 'description' field", + action: 'addChild', + snippetYaml: "description: ''\n", + snippetJson: '"description": "",\n', + }, + ], + }, + }, + { + range: { start: { line: 0, character: 0 }, end: { line: 1, character: 3 } }, + message: "should always have a 'supportedInterfaces' field", + severity: 1, + code: 9011800, + source: 'apilint', + data: { + quickFix: [ + { + message: "add 'supportedInterfaces' field", + action: 'addChild', + snippetYaml: 'supportedInterfaces:\n - \n', + snippetJson: '"supportedInterfaces": [],\n', + }, + ], + }, + }, + { + range: { start: { line: 0, character: 0 }, end: { line: 1, character: 3 } }, + message: "should always have a 'version' field", + severity: 1, + code: 9011900, + source: 'apilint', + data: { + quickFix: [ + { + message: "add 'version' field", + action: 'addChild', + snippetYaml: "version: ''\n", + snippetJson: '"version": "",\n', + }, + ], + }, + }, + { + range: { start: { line: 0, character: 0 }, end: { line: 1, character: 3 } }, + message: "should always have a 'defaultInputModes' field", + severity: 1, + code: 9012100, + source: 'apilint', + data: { + quickFix: [ + { + message: "add 'defaultInputModes' field", + action: 'addChild', + snippetYaml: 'defaultInputModes:\n - \n', + snippetJson: '"defaultInputModes": [],\n', + }, + ], + }, + }, + { + range: { start: { line: 0, character: 0 }, end: { line: 1, character: 3 } }, + message: "should always have a 'defaultOutputModes' field", + severity: 1, + code: 9012200, + source: 'apilint', + data: { + quickFix: [ + { + message: "add 'defaultOutputModes' field", + action: 'addChild', + snippetYaml: 'defaultOutputModes:\n - \n', + snippetJson: '"defaultOutputModes": [],\n', + }, + ], + }, + }, + { + range: { start: { line: 1, character: 10 }, end: { line: 1, character: 13 } }, + message: "'name' must be a string", + severity: 1, + code: 9010100, + source: 'apilint', + }, + { + range: { start: { line: 6, character: 4 }, end: { line: 8, character: 5 } }, + message: "should always have an 'id' field", + severity: 1, + code: 9030900, + source: 'apilint', + data: { + quickFix: [ + { + message: "add 'id' field", + action: 'addChild', + snippetYaml: "id: ''\n", + snippetJson: '"id": "",\n', + }, + ], + }, + }, + { + range: { start: { line: 6, character: 4 }, end: { line: 8, character: 5 } }, + message: "should always have a 'name' field", + severity: 1, + code: 9031000, + source: 'apilint', + data: { + quickFix: [ + { + message: "add 'name' field", + action: 'addChild', + snippetYaml: "name: ''\n", + snippetJson: '"name": "",\n', + }, + ], + }, + }, + { + range: { start: { line: 6, character: 4 }, end: { line: 8, character: 5 } }, + message: "should always have a 'description' field", + severity: 1, + code: 9031100, + source: 'apilint', + data: { + quickFix: [ + { + message: "add 'description' field", + action: 'addChild', + snippetYaml: "description: ''\n", + snippetJson: '"description": "",\n', + }, + ], + }, + }, + { + range: { start: { line: 6, character: 4 }, end: { line: 8, character: 5 } }, + message: "should always have a 'tags' field", + severity: 1, + code: 9031200, + source: 'apilint', + data: { + quickFix: [ + { + message: "add 'tags' field", + action: 'addChild', + snippetYaml: 'tags:\n - \n', + snippetJson: '"tags": [],\n', + }, + ], + }, + }, + ]; + assert.deepEqual(result, expected); + }); }); diff --git a/packages/apidom-ls/test/fixtures/a2a/agent-card-missing-required.json b/packages/apidom-ls/test/fixtures/a2a/agent-card-missing-required.json new file mode 100644 index 0000000000..4460a73e14 --- /dev/null +++ b/packages/apidom-ls/test/fixtures/a2a/agent-card-missing-required.json @@ -0,0 +1,11 @@ +{ + "name": 123, + "capabilities": { + "streaming": true + }, + "skills": [ + { + "examples": ["do a thing"] + } + ] +} diff --git a/packages/apidom-ls/test/fixtures/a2a/agent-card-valid.json b/packages/apidom-ls/test/fixtures/a2a/agent-card-valid.json index 2c4d27a55e..4536e6a75e 100644 --- a/packages/apidom-ls/test/fixtures/a2a/agent-card-valid.json +++ b/packages/apidom-ls/test/fixtures/a2a/agent-card-valid.json @@ -1,7 +1,6 @@ { "name": "Planner Agent", "description": "Breaks down a request into actionable tasks.", - "url": "https://agent.example.com/", "version": "1.0.0", "iconUrl": "https://agent.example.com/icon.png", "documentationUrl": "https://agent.example.com/docs", @@ -14,6 +13,13 @@ "pushNotifications": true, "extendedAgentCard": false }, + "supportedInterfaces": [ + { + "url": "https://agent.example.com/a2a/v1", + "protocolBinding": "JSONRPC", + "protocolVersion": "1.0" + } + ], "defaultInputModes": ["text", "text/plain"], "defaultOutputModes": ["text", "text/plain"], "skills": [ From 2b7282b6982b5671fbb4ea505546cddcbd8eef00 Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Mon, 8 Jun 2026 17:02:26 +0100 Subject: [PATCH 06/14] feat: review feedback and 1.0.1 upgrade (PROVCON-5343) --- .../a2a/agent-capabilities/documentation.ts | 2 +- .../config/a2a/agent-extension/completion.ts | 5 + .../a2a/agent-extension/documentation.ts | 3 + .../agent-extension/lint/allowed-fields.ts | 18 ++ .../config/a2a/agent-extension/lint/index.ts | 5 + .../src/config/a2a/agent-extension/meta.ts | 12 ++ .../a2a/api-key-security-scheme/completion.ts | 5 + .../api-key-security-scheme/documentation.ts | 3 + .../lint/allowed-fields.ts | 18 ++ .../a2a/api-key-security-scheme/lint/index.ts | 5 + .../a2a/api-key-security-scheme/meta.ts | 12 ++ .../lint/index.ts | 2 + .../lint/scopes--required.ts | 28 +++ .../lint/index.ts | 10 +- .../lint/scopes--required.ts | 28 +++ packages/apidom-ls/src/config/a2a/config.ts | 12 ++ .../a2a/device-code-oauth-flow/lint/index.ts | 2 + .../lint/scopes--required.ts | 28 +++ .../http-auth-security-scheme/completion.ts | 5 + .../documentation.ts | 3 + .../lint/allowed-fields.ts | 18 ++ .../http-auth-security-scheme/lint/index.ts | 5 + .../a2a/http-auth-security-scheme/meta.ts | 12 ++ .../mutual-tls-security-scheme/completion.ts | 5 + .../documentation.ts | 3 + .../lint/allowed-fields.ts | 18 ++ .../mutual-tls-security-scheme/lint/index.ts | 5 + .../a2a/mutual-tls-security-scheme/meta.ts | 12 ++ .../a2a/oauth2-security-scheme/completion.ts | 5 + .../oauth2-security-scheme/documentation.ts | 3 + .../lint/allowed-fields.ts | 18 ++ .../a2a/oauth2-security-scheme/lint/index.ts | 5 + .../config/a2a/oauth2-security-scheme/meta.ts | 12 ++ .../completion.ts | 5 + .../documentation.ts | 3 + .../lint/allowed-fields.ts | 18 ++ .../lint/index.ts | 5 + .../open-id-connect-security-scheme/meta.ts | 12 ++ .../apidom-ls/src/config/a2a/target-specs.ts | 3 +- packages/apidom-ls/src/config/codes.ts | 3 + packages/apidom-ls/src/utils/utils.ts | 8 +- packages/apidom-ls/test/a2a.ts | 160 +++++++++++++++++- ...ard-client-credentials-missing-scopes.json | 35 ++++ ...agent-card-device-code-missing-scopes.json | 36 ++++ .../a2a/agent-card-oauth-missing-scopes.json | 36 ++++ .../fixtures/a2a/agent-card-oauth-valid.json | 37 ++++ packages/apidom-ns-a2a-1/src/media-types.ts | 3 + 47 files changed, 682 insertions(+), 9 deletions(-) create mode 100644 packages/apidom-ls/src/config/a2a/agent-extension/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-extension/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-extension/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-extension/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-extension/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/api-key-security-scheme/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/api-key-security-scheme/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/api-key-security-scheme/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/scopes--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/scopes--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/scopes--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/http-auth-security-scheme/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/http-auth-security-scheme/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/http-auth-security-scheme/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth2-security-scheme/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth2-security-scheme/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth2-security-scheme/meta.ts create mode 100644 packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/completion.ts create mode 100644 packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/documentation.ts create mode 100644 packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/allowed-fields.ts create mode 100644 packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts create mode 100644 packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/meta.ts create mode 100644 packages/apidom-ls/test/fixtures/a2a/agent-card-client-credentials-missing-scopes.json create mode 100644 packages/apidom-ls/test/fixtures/a2a/agent-card-device-code-missing-scopes.json create mode 100644 packages/apidom-ls/test/fixtures/a2a/agent-card-oauth-missing-scopes.json create mode 100644 packages/apidom-ls/test/fixtures/a2a/agent-card-oauth-valid.json diff --git a/packages/apidom-ls/src/config/a2a/agent-capabilities/documentation.ts b/packages/apidom-ls/src/config/a2a/agent-capabilities/documentation.ts index a24eb3235d..d4e5e47d50 100644 --- a/packages/apidom-ls/src/config/a2a/agent-capabilities/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/agent-capabilities/documentation.ts @@ -17,7 +17,7 @@ const documentation = [ }, { target: 'extensions', - docs: 'Array of [Agent Extension Objects](https://a2a-protocol.org/latest/specification/#agentextension) — protocol extensions supported by the agent.', + docs: 'Array of [Agent Extension Objects](https://a2a-protocol.org/latest/specification/#444-agentextension) — protocol extensions supported by the agent.', targetSpecs: A2A1, }, { diff --git a/packages/apidom-ls/src/config/a2a/agent-extension/completion.ts b/packages/apidom-ls/src/config/a2a/agent-extension/completion.ts new file mode 100644 index 0000000000..f97c84e35d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-extension/completion.ts @@ -0,0 +1,5 @@ +import { ApidomCompletionItem } from '../../../apidom-language-types.ts'; + +const completion: ApidomCompletionItem[] = []; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/agent-extension/documentation.ts b/packages/apidom-ls/src/config/a2a/agent-extension/documentation.ts new file mode 100644 index 0000000000..6a524bbcb3 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-extension/documentation.ts @@ -0,0 +1,3 @@ +const documentation: never[] = []; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/agent-extension/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/agent-extension/lint/allowed-fields.ts new file mode 100644 index 0000000000..d2f1fb2262 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-extension/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['uri', 'description', 'required', 'params']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-extension/lint/index.ts b/packages/apidom-ls/src/config/a2a/agent-extension/lint/index.ts new file mode 100644 index 0000000000..47f7d48761 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-extension/lint/index.ts @@ -0,0 +1,5 @@ +import allowedFieldsLint from './allowed-fields.ts'; + +const lints = [allowedFieldsLint]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/agent-extension/meta.ts b/packages/apidom-ls/src/config/a2a/agent-extension/meta.ts new file mode 100644 index 0000000000..6e1bbd7f60 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-extension/meta.ts @@ -0,0 +1,12 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; +import documentation from './documentation.ts'; + +const meta: FormatMeta = { + lint, + completion, + documentation, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/completion.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/completion.ts new file mode 100644 index 0000000000..f97c84e35d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/completion.ts @@ -0,0 +1,5 @@ +import { ApidomCompletionItem } from '../../../apidom-language-types.ts'; + +const completion: ApidomCompletionItem[] = []; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/documentation.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/documentation.ts new file mode 100644 index 0000000000..6a524bbcb3 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/documentation.ts @@ -0,0 +1,3 @@ +const documentation: never[] = []; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/allowed-fields.ts new file mode 100644 index 0000000000..9b9fbd5856 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['description', 'name', 'location']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/index.ts new file mode 100644 index 0000000000..47f7d48761 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/index.ts @@ -0,0 +1,5 @@ +import allowedFieldsLint from './allowed-fields.ts'; + +const lints = [allowedFieldsLint]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/meta.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/meta.ts new file mode 100644 index 0000000000..6e1bbd7f60 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/meta.ts @@ -0,0 +1,12 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; +import documentation from './documentation.ts'; + +const meta: FormatMeta = { + lint, + completion, + documentation, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/index.ts index be1dcd1063..d8ab9ea671 100644 --- a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/index.ts @@ -1,6 +1,7 @@ import allowedFieldsLint from './allowed-fields.ts'; import authorizationUrlRequiredLint from './authorization-url--required.ts'; import tokenUrlRequiredLint from './token-url--required.ts'; +import scopesRequiredLint from './scopes--required.ts'; import authorizationUrlLint from './authorization-url--type.ts'; import tokenUrlLint from './token-url--type.ts'; import refreshUrlLint from './refresh-url--type.ts'; @@ -11,6 +12,7 @@ const lints = [ allowedFieldsLint, authorizationUrlRequiredLint, tokenUrlRequiredLint, + scopesRequiredLint, authorizationUrlLint, tokenUrlLint, refreshUrlLint, diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/scopes--required.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/scopes--required.ts new file mode 100644 index 0000000000..512ab6c4e0 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/lint/scopes--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const scopesRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_SCOPES_REQUIRED, + source: 'apilint', + message: "should always have a 'scopes' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['scopes'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'scopes' field", + action: 'addChild', + snippetYaml: 'scopes: \n \n', + snippetJson: '"scopes": {\n \n },\n', + }, + ], + }, +}; + +export default scopesRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/index.ts index 3e388d4fb3..094482622e 100644 --- a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/index.ts @@ -1,9 +1,17 @@ import allowedFieldsLint from './allowed-fields.ts'; import tokenUrlRequiredLint from './token-url--required.ts'; +import scopesRequiredLint from './scopes--required.ts'; import tokenUrlLint from './token-url--type.ts'; import refreshUrlLint from './refresh-url--type.ts'; import scopesLint from './scopes--type.ts'; -const lints = [allowedFieldsLint, tokenUrlRequiredLint, tokenUrlLint, refreshUrlLint, scopesLint]; +const lints = [ + allowedFieldsLint, + tokenUrlRequiredLint, + scopesRequiredLint, + tokenUrlLint, + refreshUrlLint, + scopesLint, +]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/scopes--required.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/scopes--required.ts new file mode 100644 index 0000000000..d00c7c8dc9 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/lint/scopes--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const scopesRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_SCOPES_REQUIRED, + source: 'apilint', + message: "should always have a 'scopes' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['scopes'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'scopes' field", + action: 'addChild', + snippetYaml: 'scopes: \n \n', + snippetJson: '"scopes": {\n \n },\n', + }, + ], + }, +}; + +export default scopesRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/config.ts b/packages/apidom-ls/src/config/a2a/config.ts index a501a3d0b5..920cbb5be0 100644 --- a/packages/apidom-ls/src/config/a2a/config.ts +++ b/packages/apidom-ls/src/config/a2a/config.ts @@ -13,6 +13,12 @@ import clientCredentialsOAuthFlowMeta from './client-credentials-oauth-flow/meta import deviceCodeOAuthFlowMeta from './device-code-oauth-flow/meta.ts'; import implicitOAuthFlowMeta from './implicit-oauth-flow/meta.ts'; import passwordOAuthFlowMeta from './password-oauth-flow/meta.ts'; +import agentExtensionMeta from './agent-extension/meta.ts'; +import apiKeySecuritySchemeMeta from './api-key-security-scheme/meta.ts'; +import httpAuthSecuritySchemeMeta from './http-auth-security-scheme/meta.ts'; +import oauth2SecuritySchemeMeta from './oauth2-security-scheme/meta.ts'; +import openIdConnectSecuritySchemeMeta from './open-id-connect-security-scheme/meta.ts'; +import mutualTlsSecuritySchemeMeta from './mutual-tls-security-scheme/meta.ts'; import ApilintCodes from '../codes.ts'; export default { @@ -41,4 +47,10 @@ export default { deviceCodeOAuthFlow: deviceCodeOAuthFlowMeta, implicitOAuthFlow: implicitOAuthFlowMeta, passwordOAuthFlow: passwordOAuthFlowMeta, + agentExtension: agentExtensionMeta, + apiKeySecurityScheme: apiKeySecuritySchemeMeta, + httpAuthSecurityScheme: httpAuthSecuritySchemeMeta, + oauth2SecurityScheme: oauth2SecuritySchemeMeta, + openIdConnectSecurityScheme: openIdConnectSecuritySchemeMeta, + mutualTlsSecurityScheme: mutualTlsSecuritySchemeMeta, }; diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/index.ts index 72f0500db8..82ba2f2785 100644 --- a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/index.ts @@ -1,6 +1,7 @@ import allowedFieldsLint from './allowed-fields.ts'; import deviceAuthorizationUrlRequiredLint from './device-authorization-url--required.ts'; import tokenUrlRequiredLint from './token-url--required.ts'; +import scopesRequiredLint from './scopes--required.ts'; import deviceAuthorizationUrlLint from './device-authorization-url--type.ts'; import tokenUrlLint from './token-url--type.ts'; import refreshUrlLint from './refresh-url--type.ts'; @@ -10,6 +11,7 @@ const lints = [ allowedFieldsLint, deviceAuthorizationUrlRequiredLint, tokenUrlRequiredLint, + scopesRequiredLint, deviceAuthorizationUrlLint, tokenUrlLint, refreshUrlLint, diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/scopes--required.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/scopes--required.ts new file mode 100644 index 0000000000..a4ab0c5a8f --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/lint/scopes--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const scopesRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_SCOPES_REQUIRED, + source: 'apilint', + message: "should always have a 'scopes' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['scopes'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'scopes' field", + action: 'addChild', + snippetYaml: 'scopes: \n \n', + snippetJson: '"scopes": {\n \n },\n', + }, + ], + }, +}; + +export default scopesRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/completion.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/completion.ts new file mode 100644 index 0000000000..f97c84e35d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/completion.ts @@ -0,0 +1,5 @@ +import { ApidomCompletionItem } from '../../../apidom-language-types.ts'; + +const completion: ApidomCompletionItem[] = []; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/documentation.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/documentation.ts new file mode 100644 index 0000000000..6a524bbcb3 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/documentation.ts @@ -0,0 +1,3 @@ +const documentation: never[] = []; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/allowed-fields.ts new file mode 100644 index 0000000000..21986d0cb3 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['description', 'scheme', 'bearerFormat']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts new file mode 100644 index 0000000000..47f7d48761 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts @@ -0,0 +1,5 @@ +import allowedFieldsLint from './allowed-fields.ts'; + +const lints = [allowedFieldsLint]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/meta.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/meta.ts new file mode 100644 index 0000000000..6e1bbd7f60 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/meta.ts @@ -0,0 +1,12 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; +import documentation from './documentation.ts'; + +const meta: FormatMeta = { + lint, + completion, + documentation, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/completion.ts b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/completion.ts new file mode 100644 index 0000000000..f97c84e35d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/completion.ts @@ -0,0 +1,5 @@ +import { ApidomCompletionItem } from '../../../apidom-language-types.ts'; + +const completion: ApidomCompletionItem[] = []; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/documentation.ts b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/documentation.ts new file mode 100644 index 0000000000..6a524bbcb3 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/documentation.ts @@ -0,0 +1,3 @@ +const documentation: never[] = []; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/allowed-fields.ts new file mode 100644 index 0000000000..b8015bba09 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['description']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/index.ts new file mode 100644 index 0000000000..47f7d48761 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/index.ts @@ -0,0 +1,5 @@ +import allowedFieldsLint from './allowed-fields.ts'; + +const lints = [allowedFieldsLint]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/meta.ts b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/meta.ts new file mode 100644 index 0000000000..6e1bbd7f60 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/meta.ts @@ -0,0 +1,12 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; +import documentation from './documentation.ts'; + +const meta: FormatMeta = { + lint, + completion, + documentation, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/completion.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/completion.ts new file mode 100644 index 0000000000..f97c84e35d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/completion.ts @@ -0,0 +1,5 @@ +import { ApidomCompletionItem } from '../../../apidom-language-types.ts'; + +const completion: ApidomCompletionItem[] = []; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/documentation.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/documentation.ts new file mode 100644 index 0000000000..6a524bbcb3 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/documentation.ts @@ -0,0 +1,3 @@ +const documentation: never[] = []; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/allowed-fields.ts new file mode 100644 index 0000000000..485cc21b1a --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['description', 'flows', 'oauth2MetadataUrl']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts new file mode 100644 index 0000000000..47f7d48761 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts @@ -0,0 +1,5 @@ +import allowedFieldsLint from './allowed-fields.ts'; + +const lints = [allowedFieldsLint]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/meta.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/meta.ts new file mode 100644 index 0000000000..6e1bbd7f60 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/meta.ts @@ -0,0 +1,12 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; +import documentation from './documentation.ts'; + +const meta: FormatMeta = { + lint, + completion, + documentation, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/completion.ts b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/completion.ts new file mode 100644 index 0000000000..f97c84e35d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/completion.ts @@ -0,0 +1,5 @@ +import { ApidomCompletionItem } from '../../../apidom-language-types.ts'; + +const completion: ApidomCompletionItem[] = []; + +export default completion; diff --git a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/documentation.ts b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/documentation.ts new file mode 100644 index 0000000000..6a524bbcb3 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/documentation.ts @@ -0,0 +1,3 @@ +const documentation: never[] = []; + +export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/allowed-fields.ts new file mode 100644 index 0000000000..7a0cde7bd9 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/allowed-fields.ts @@ -0,0 +1,18 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const allowedFieldsLint: LinterMeta = { + code: ApilintCodes.NOT_ALLOWED_FIELDS, + source: 'apilint', + message: 'Object includes not allowed fields', + severity: DiagnosticSeverity.Error, + linterFunction: 'allowedFields', + linterParams: [['description', 'openIdConnectUrl']], + marker: 'key', + targetSpecs: A2A1, +}; + +export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts new file mode 100644 index 0000000000..47f7d48761 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts @@ -0,0 +1,5 @@ +import allowedFieldsLint from './allowed-fields.ts'; + +const lints = [allowedFieldsLint]; + +export default lints; diff --git a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/meta.ts b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/meta.ts new file mode 100644 index 0000000000..6e1bbd7f60 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/meta.ts @@ -0,0 +1,12 @@ +import { FormatMeta } from '../../../apidom-language-types.ts'; +import lint from './lint/index.ts'; +import completion from './completion.ts'; +import documentation from './documentation.ts'; + +const meta: FormatMeta = { + lint, + completion, + documentation, +}; + +export default meta; diff --git a/packages/apidom-ls/src/config/a2a/target-specs.ts b/packages/apidom-ls/src/config/a2a/target-specs.ts index 51c5e666fb..95b35b8aa4 100644 --- a/packages/apidom-ls/src/config/a2a/target-specs.ts +++ b/packages/apidom-ls/src/config/a2a/target-specs.ts @@ -1,3 +1,4 @@ export const A2A100 = [{ namespace: 'a2a', version: '1.0.0' }]; +export const A2A101 = [{ namespace: 'a2a', version: '1.0.1' }]; -export const A2A1 = [...A2A100]; +export const A2A1 = [...A2A100, ...A2A101]; diff --git a/packages/apidom-ls/src/config/codes.ts b/packages/apidom-ls/src/config/codes.ts index fdeecd0035..05c68e3b57 100644 --- a/packages/apidom-ls/src/config/codes.ts +++ b/packages/apidom-ls/src/config/codes.ts @@ -1526,12 +1526,14 @@ enum ApilintCodes { A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9090500, A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_REQUIRED = 9090600, A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED = 9090700, + A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_SCOPES_REQUIRED = 9090800, A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW = 9100000, A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE = 9100100, A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE = 9100200, A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9100300, A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED = 9100400, + A2A1_CLIENT_CREDENTIALS_OAUTH_FLOW_FIELD_SCOPES_REQUIRED = 9100500, A2A1_DEVICE_CODE_OAUTH_FLOW = 9110000, A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_DEVICE_AUTHORIZATION_URL_TYPE = 9110100, @@ -1540,6 +1542,7 @@ enum ApilintCodes { A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9110400, A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_DEVICE_AUTHORIZATION_URL_REQUIRED = 9110500, A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED = 9110600, + A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_SCOPES_REQUIRED = 9110700, A2A1_IMPLICIT_OAUTH_FLOW = 9120000, A2A1_IMPLICIT_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_TYPE = 9120100, diff --git a/packages/apidom-ls/src/utils/utils.ts b/packages/apidom-ls/src/utils/utils.ts index 89c6a848c3..74cb9c6b10 100644 --- a/packages/apidom-ls/src/utils/utils.ts +++ b/packages/apidom-ls/src/utils/utils.ts @@ -978,9 +978,9 @@ export async function findNamespace( if (await a2a1AdapterJson.detect(text)) { // A2A AgentCard documents have no version discriminator field, so we - // pin the LS namespace identification to A2A v1 (the only published - // protocol version at time of writing). - const version = '1.0.0'; + // pin the LS namespace identification to A2A v1 (matches the registered + // media types in the adapter). + const version = '1.0.1'; return { namespace: 'a2a', @@ -991,7 +991,7 @@ export async function findNamespace( } if (await a2a1AdapterYaml.detect(text)) { - const version = '1.0.0'; + const version = '1.0.1'; return { namespace: 'a2a', diff --git a/packages/apidom-ls/test/a2a.ts b/packages/apidom-ls/test/a2a.ts index 8be60d600b..0668ca344b 100644 --- a/packages/apidom-ls/test/a2a.ts +++ b/packages/apidom-ls/test/a2a.ts @@ -24,6 +24,31 @@ const agentCardMissingRequired = fs .readFileSync(path.join(__dirname, 'fixtures', 'a2a', 'agent-card-missing-required.json')) .toString(); +const agentCardOAuthValid = fs + .readFileSync(path.join(__dirname, 'fixtures', 'a2a', 'agent-card-oauth-valid.json')) + .toString(); + +const agentCardOAuthMissingScopes = fs + .readFileSync(path.join(__dirname, 'fixtures', 'a2a', 'agent-card-oauth-missing-scopes.json')) + .toString(); + +const agentCardClientCredentialsMissingScopes = fs + .readFileSync( + path.join( + __dirname, + 'fixtures', + 'a2a', + 'agent-card-client-credentials-missing-scopes.json', + ), + ) + .toString(); + +const agentCardDeviceCodeMissingScopes = fs + .readFileSync( + path.join(__dirname, 'fixtures', 'a2a', 'agent-card-device-code-missing-scopes.json'), + ) + .toString(); + describe('apidom-ls-a2a', function () { const context: LanguageServiceContext = { metadata: metadata(), @@ -33,9 +58,9 @@ describe('apidom-ls-a2a', function () { // spec version used for lint-rule targeting is pinned to A2A v1. defaultContentLanguage: { namespace: 'a2a', - version: '1.0.0', + version: '1.0.1', format: 'JSON', - mediaType: 'application/vnd.a2a+json;version=1.0.0', + mediaType: 'application/vnd.a2a+json;version=1.0.1', }, }; @@ -246,4 +271,135 @@ describe('apidom-ls-a2a', function () { ]; assert.deepEqual(result, expected); }); + + it('validates an AgentCard with oauth2 authorization code flow and scopes with no diagnostics', async function () { + this.timeout(10000); + + const validationContext: ValidationContext = { + comments: DiagnosticSeverity.Error, + maxNumberOfProblems: 100, + relatedInformation: false, + }; + + const doc = TextDocument.create('foo://bar/oauth-valid.json', 'json', 0, agentCardOAuthValid); + const result = await languageService.doValidation(doc, validationContext); + assert.deepEqual(result, [] as Diagnostic[]); + }); + + it('reports missing scopes on AuthorizationCodeOAuthFlow', async function () { + this.timeout(10000); + + const validationContext: ValidationContext = { + comments: DiagnosticSeverity.Error, + maxNumberOfProblems: 100, + relatedInformation: false, + }; + + const doc = TextDocument.create( + 'foo://bar/oauth-missing-scopes.json', + 'json', + 0, + agentCardOAuthMissingScopes, + ); + + const result = await languageService.doValidation(doc, validationContext); + const expected: Diagnostic[] = [ + { + range: { start: { line: 26, character: 10 }, end: { line: 26, character: 29 } }, + message: "should always have a 'scopes' field", + severity: 1, + code: 9090800, + source: 'apilint', + data: { + quickFix: [ + { + message: "add 'scopes' field", + action: 'addChild', + snippetYaml: 'scopes: \n \n', + snippetJson: '"scopes": {\n \n },\n', + }, + ], + }, + }, + ]; + assert.deepEqual(result, expected); + }); + + it('reports missing scopes on ClientCredentialsOAuthFlow', async function () { + this.timeout(10000); + + const validationContext: ValidationContext = { + comments: DiagnosticSeverity.Error, + maxNumberOfProblems: 100, + relatedInformation: false, + }; + + const doc = TextDocument.create( + 'foo://bar/cc-missing-scopes.json', + 'json', + 0, + agentCardClientCredentialsMissingScopes, + ); + + const result = await languageService.doValidation(doc, validationContext); + const expected: Diagnostic[] = [ + { + range: { start: { line: 26, character: 10 }, end: { line: 26, character: 29 } }, + message: "should always have a 'scopes' field", + severity: 1, + code: 9100500, + source: 'apilint', + data: { + quickFix: [ + { + message: "add 'scopes' field", + action: 'addChild', + snippetYaml: 'scopes: \n \n', + snippetJson: '"scopes": {\n \n },\n', + }, + ], + }, + }, + ]; + assert.deepEqual(result, expected); + }); + + it('reports missing scopes on DeviceCodeOAuthFlow', async function () { + this.timeout(10000); + + const validationContext: ValidationContext = { + comments: DiagnosticSeverity.Error, + maxNumberOfProblems: 100, + relatedInformation: false, + }; + + const doc = TextDocument.create( + 'foo://bar/dc-missing-scopes.json', + 'json', + 0, + agentCardDeviceCodeMissingScopes, + ); + + const result = await languageService.doValidation(doc, validationContext); + const expected: Diagnostic[] = [ + { + range: { start: { line: 26, character: 10 }, end: { line: 26, character: 22 } }, + message: "should always have a 'scopes' field", + severity: 1, + code: 9110700, + source: 'apilint', + data: { + quickFix: [ + { + message: "add 'scopes' field", + action: 'addChild', + snippetYaml: 'scopes: \n \n', + snippetJson: '"scopes": {\n \n },\n', + }, + ], + }, + }, + ]; + assert.deepEqual(result, expected); + }); }); diff --git a/packages/apidom-ls/test/fixtures/a2a/agent-card-client-credentials-missing-scopes.json b/packages/apidom-ls/test/fixtures/a2a/agent-card-client-credentials-missing-scopes.json new file mode 100644 index 0000000000..167df078d5 --- /dev/null +++ b/packages/apidom-ls/test/fixtures/a2a/agent-card-client-credentials-missing-scopes.json @@ -0,0 +1,35 @@ +{ + "name": "OAuth Agent", + "description": "Agent with OAuth2 client credentials flow missing scopes.", + "version": "1.0.0", + "supportedInterfaces": [ + { + "url": "https://agent.example.com/a2a/v1", + "protocolBinding": "JSONRPC", + "protocolVersion": "1.0" + } + ], + "capabilities": { "streaming": false }, + "defaultInputModes": ["text"], + "defaultOutputModes": ["text"], + "skills": [ + { + "id": "skill1", + "name": "Skill One", + "description": "A skill.", + "tags": ["test"] + } + ], + "securitySchemes": { + "oauth": { + "oauth2SecurityScheme": { + "flows": { + "clientCredentials": { + "tokenUrl": "https://example.com/token" + } + } + } + } + }, + "securityRequirements": [{ "schemes": { "oauth": [] } }] +} diff --git a/packages/apidom-ls/test/fixtures/a2a/agent-card-device-code-missing-scopes.json b/packages/apidom-ls/test/fixtures/a2a/agent-card-device-code-missing-scopes.json new file mode 100644 index 0000000000..8a9a7d0880 --- /dev/null +++ b/packages/apidom-ls/test/fixtures/a2a/agent-card-device-code-missing-scopes.json @@ -0,0 +1,36 @@ +{ + "name": "OAuth Agent", + "description": "Agent with OAuth2 device code flow missing scopes.", + "version": "1.0.0", + "supportedInterfaces": [ + { + "url": "https://agent.example.com/a2a/v1", + "protocolBinding": "JSONRPC", + "protocolVersion": "1.0" + } + ], + "capabilities": { "streaming": false }, + "defaultInputModes": ["text"], + "defaultOutputModes": ["text"], + "skills": [ + { + "id": "skill1", + "name": "Skill One", + "description": "A skill.", + "tags": ["test"] + } + ], + "securitySchemes": { + "oauth": { + "oauth2SecurityScheme": { + "flows": { + "deviceCode": { + "deviceAuthorizationUrl": "https://example.com/device", + "tokenUrl": "https://example.com/token" + } + } + } + } + }, + "securityRequirements": [{ "schemes": { "oauth": [] } }] +} diff --git a/packages/apidom-ls/test/fixtures/a2a/agent-card-oauth-missing-scopes.json b/packages/apidom-ls/test/fixtures/a2a/agent-card-oauth-missing-scopes.json new file mode 100644 index 0000000000..d4ecdad907 --- /dev/null +++ b/packages/apidom-ls/test/fixtures/a2a/agent-card-oauth-missing-scopes.json @@ -0,0 +1,36 @@ +{ + "name": "OAuth Agent", + "description": "Agent with OAuth2 authorization code flow missing scopes.", + "version": "1.0.0", + "supportedInterfaces": [ + { + "url": "https://agent.example.com/a2a/v1", + "protocolBinding": "JSONRPC", + "protocolVersion": "1.0" + } + ], + "capabilities": { "streaming": false }, + "defaultInputModes": ["text"], + "defaultOutputModes": ["text"], + "skills": [ + { + "id": "skill1", + "name": "Skill One", + "description": "A skill.", + "tags": ["test"] + } + ], + "securitySchemes": { + "oauth": { + "oauth2SecurityScheme": { + "flows": { + "authorizationCode": { + "authorizationUrl": "https://example.com/auth", + "tokenUrl": "https://example.com/token" + } + } + } + } + }, + "securityRequirements": [{ "schemes": { "oauth": [] } }] +} diff --git a/packages/apidom-ls/test/fixtures/a2a/agent-card-oauth-valid.json b/packages/apidom-ls/test/fixtures/a2a/agent-card-oauth-valid.json new file mode 100644 index 0000000000..7345690336 --- /dev/null +++ b/packages/apidom-ls/test/fixtures/a2a/agent-card-oauth-valid.json @@ -0,0 +1,37 @@ +{ + "name": "OAuth Agent", + "description": "Agent with OAuth2 authorization code flow.", + "version": "1.0.0", + "supportedInterfaces": [ + { + "url": "https://agent.example.com/a2a/v1", + "protocolBinding": "JSONRPC", + "protocolVersion": "1.0" + } + ], + "capabilities": { "streaming": false }, + "defaultInputModes": ["text"], + "defaultOutputModes": ["text"], + "skills": [ + { + "id": "skill1", + "name": "Skill One", + "description": "A skill.", + "tags": ["test"] + } + ], + "securitySchemes": { + "oauth": { + "oauth2SecurityScheme": { + "flows": { + "authorizationCode": { + "authorizationUrl": "https://example.com/auth", + "tokenUrl": "https://example.com/token", + "scopes": { "read": "Read access" } + } + } + } + } + }, + "securityRequirements": [{ "schemes": { "oauth": ["read"] } }] +} diff --git a/packages/apidom-ns-a2a-1/src/media-types.ts b/packages/apidom-ns-a2a-1/src/media-types.ts index 4ea3e92746..cf3be84d12 100644 --- a/packages/apidom-ns-a2a-1/src/media-types.ts +++ b/packages/apidom-ns-a2a-1/src/media-types.ts @@ -39,6 +39,9 @@ const mediaTypes = new A2AMediaTypes( 'application/vnd.a2a;version=1.0.0', 'application/vnd.a2a+json;version=1.0.0', 'application/vnd.a2a+yaml;version=1.0.0', + 'application/vnd.a2a;version=1.0.1', + 'application/vnd.a2a+json;version=1.0.1', + 'application/vnd.a2a+yaml;version=1.0.1', ); export default mediaTypes; From ef717946d8405d4a3cdb15fb56a78656960cf647 Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Tue, 9 Jun 2026 10:44:38 +0100 Subject: [PATCH 07/14] feat: lint (PROVCON-5343) --- packages/apidom-ls/test/a2a.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/apidom-ls/test/a2a.ts b/packages/apidom-ls/test/a2a.ts index 0668ca344b..4830d232ad 100644 --- a/packages/apidom-ls/test/a2a.ts +++ b/packages/apidom-ls/test/a2a.ts @@ -34,12 +34,7 @@ const agentCardOAuthMissingScopes = fs const agentCardClientCredentialsMissingScopes = fs .readFileSync( - path.join( - __dirname, - 'fixtures', - 'a2a', - 'agent-card-client-credentials-missing-scopes.json', - ), + path.join(__dirname, 'fixtures', 'a2a', 'agent-card-client-credentials-missing-scopes.json'), ) .toString(); From 2c7a92fb815b059e13d9a1dda73bbf62f949350b Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Tue, 9 Jun 2026 10:58:17 +0100 Subject: [PATCH 08/14] feat: update test for a2a 1.0.1 (PROVCON-5343) --- packages/apidom-parser-adapter-a2a-json-1/test/media-types.ts | 2 +- packages/apidom-parser-adapter-a2a-yaml-1/test/media-types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/apidom-parser-adapter-a2a-json-1/test/media-types.ts b/packages/apidom-parser-adapter-a2a-json-1/test/media-types.ts index 7c38162825..622bfc64cc 100644 --- a/packages/apidom-parser-adapter-a2a-json-1/test/media-types.ts +++ b/packages/apidom-parser-adapter-a2a-json-1/test/media-types.ts @@ -19,7 +19,7 @@ describe('given adapter is used in parser', function () { '{"capabilities": {"streaming": true}, "skills": [], "name": "x", "url": "https://x", "version": "1.0.0"}', ); - assert.strictEqual(mediaType, 'application/vnd.a2a;version=1.0.0'); + assert.strictEqual(mediaType, 'application/vnd.a2a;version=1.0.1'); }); }); diff --git a/packages/apidom-parser-adapter-a2a-yaml-1/test/media-types.ts b/packages/apidom-parser-adapter-a2a-yaml-1/test/media-types.ts index 333984ed7a..f13b0ba8a9 100644 --- a/packages/apidom-parser-adapter-a2a-yaml-1/test/media-types.ts +++ b/packages/apidom-parser-adapter-a2a-yaml-1/test/media-types.ts @@ -17,7 +17,7 @@ describe('given adapter is used in parser', function () { 'capabilities:\n streaming: true\nskills: []\nname: x\nurl: https://x\nversion: 1.0.0\n', ); - assert.strictEqual(mediaType, 'application/vnd.a2a;version=1.0.0'); + assert.strictEqual(mediaType, 'application/vnd.a2a;version=1.0.1'); }); }); }); From cb635b0fbed348a90bc784ac9affc1e441663a12 Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Tue, 9 Jun 2026 15:35:02 +0100 Subject: [PATCH 09/14] feat: review feedback (PROVCON-5343) --- .../a2a/agent-capabilities/documentation.ts | 2 +- .../a2a/agent-extension/documentation.ts | 29 ++++++++++++- .../config/a2a/agent-skill/documentation.ts | 2 +- .../api-key-security-scheme/documentation.ts | 24 ++++++++++- .../a2a/api-key-security-scheme/lint/index.ts | 4 +- .../lint/location--required.ts} | 16 ++++---- .../lint/name--required.ts} | 16 ++++---- .../documentation.ts | 2 +- packages/apidom-ls/src/config/a2a/config.ts | 4 -- .../documentation.ts | 24 ++++++++++- .../http-auth-security-scheme/lint/index.ts | 3 +- .../lint/scheme--required.ts | 28 +++++++++++++ .../a2a/implicit-oauth-flow/completion.ts | 41 ------------------- .../a2a/implicit-oauth-flow/documentation.ts | 25 ----------- .../lint/allowed-fields.ts | 18 -------- .../lint/authorization-url--type.ts | 19 --------- .../a2a/implicit-oauth-flow/lint/index.ts | 15 ------- .../lint/refresh-url--type.ts | 19 --------- .../implicit-oauth-flow/lint/scopes--type.ts | 19 --------- .../config/a2a/implicit-oauth-flow/meta.ts | 12 ------ .../documentation.ts | 14 ++++++- .../src/config/a2a/oauth-flows/completion.ts | 2 - .../config/a2a/oauth-flows/documentation.ts | 18 ++------ .../a2a/oauth-flows/lint/allowed-fields.ts | 2 +- .../a2a/oauth-flows/lint/implicit--type.ts | 19 --------- .../src/config/a2a/oauth-flows/lint/index.ts | 11 +---- .../a2a/oauth-flows/lint/password--type.ts | 19 --------- .../oauth2-security-scheme/documentation.ts | 24 ++++++++++- .../lint/flows--required.ts | 28 +++++++++++++ .../a2a/oauth2-security-scheme/lint/index.ts | 3 +- .../documentation.ts | 19 ++++++++- .../lint/index.ts | 3 +- .../lint/open-id-connect-url--required.ts | 28 +++++++++++++ .../a2a/password-oauth-flow/completion.ts | 37 ----------------- .../a2a/password-oauth-flow/documentation.ts | 25 ----------- .../lint/allowed-fields.ts | 18 -------- .../a2a/password-oauth-flow/lint/index.ts | 9 ---- .../lint/refresh-url--type.ts | 19 --------- .../password-oauth-flow/lint/scopes--type.ts | 19 --------- .../lint/token-url--type.ts | 19 --------- .../config/a2a/password-oauth-flow/meta.ts | 12 ------ .../a2a/security-scheme/documentation.ts | 12 +++--- packages/apidom-ls/src/config/codes.ts | 25 ++++++----- .../src/elements/ImplicitOAuthFlow.ts | 41 ------------------- .../src/elements/OAuthFlows.ts | 20 +-------- .../src/elements/PasswordOAuthFlow.ts | 41 ------------------- packages/apidom-ns-a2a-1/src/index.ts | 12 ------ packages/apidom-ns-a2a-1/src/namespace.ts | 4 -- packages/apidom-ns-a2a-1/src/predicates.ts | 30 -------------- .../src/refractor/canonicalize.ts | 2 +- .../plugins/replace-empty-element.ts | 18 -------- .../src/refractor/registration.ts | 18 -------- .../src/refractor/specification.ts | 20 --------- .../a2a-1/implicit-oauth-flow/index.ts | 33 --------------- .../a2a-1/password-oauth-flow/index.ts | 33 --------------- .../apidom-ns-a2a-1/src/traversal/visitor.ts | 2 - packages/apidom-ns-a2a-1/test/predicates.ts | 14 ------- .../__snapshots__/index.ts.snap | 14 ------- .../elements/ImplicitOAuthFlow/index.ts | 20 --------- .../OAuthFlows/__snapshots__/index.ts.snap | 12 ------ .../refractor/elements/OAuthFlows/index.ts | 6 --- .../__snapshots__/index.ts.snap | 14 ------- .../elements/PasswordOAuthFlow/index.ts | 20 --------- 63 files changed, 266 insertions(+), 815 deletions(-) rename packages/apidom-ls/src/config/a2a/{implicit-oauth-flow/lint/authorization-url--required.ts => api-key-security-scheme/lint/location--required.ts} (51%) rename packages/apidom-ls/src/config/a2a/{password-oauth-flow/lint/token-url--required.ts => api-key-security-scheme/lint/name--required.ts} (55%) create mode 100644 packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/scheme--required.ts delete mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/completion.ts delete mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/documentation.ts delete mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/allowed-fields.ts delete mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--type.ts delete mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/index.ts delete mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/refresh-url--type.ts delete mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/scopes--type.ts delete mode 100644 packages/apidom-ls/src/config/a2a/implicit-oauth-flow/meta.ts delete mode 100644 packages/apidom-ls/src/config/a2a/oauth-flows/lint/implicit--type.ts delete mode 100644 packages/apidom-ls/src/config/a2a/oauth-flows/lint/password--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/flows--required.ts create mode 100644 packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/open-id-connect-url--required.ts delete mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/completion.ts delete mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/documentation.ts delete mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/allowed-fields.ts delete mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/index.ts delete mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/refresh-url--type.ts delete mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/scopes--type.ts delete mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--type.ts delete mode 100644 packages/apidom-ls/src/config/a2a/password-oauth-flow/meta.ts delete mode 100644 packages/apidom-ns-a2a-1/src/elements/ImplicitOAuthFlow.ts delete mode 100644 packages/apidom-ns-a2a-1/src/elements/PasswordOAuthFlow.ts delete mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/implicit-oauth-flow/index.ts delete mode 100644 packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/password-oauth-flow/index.ts delete mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/__snapshots__/index.ts.snap delete mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/index.ts delete mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/__snapshots__/index.ts.snap delete mode 100644 packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/index.ts diff --git a/packages/apidom-ls/src/config/a2a/agent-capabilities/documentation.ts b/packages/apidom-ls/src/config/a2a/agent-capabilities/documentation.ts index d4e5e47d50..fb3ed9d563 100644 --- a/packages/apidom-ls/src/config/a2a/agent-capabilities/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/agent-capabilities/documentation.ts @@ -2,7 +2,7 @@ import { A2A1 } from '../target-specs.ts'; /** * Hover documentation for A2A v1 AgentCapabilities fields. - * See [AgentCapabilities](https://a2a-protocol.org/latest/specification/#agentcapabilities). + * See [AgentCapabilities](https://a2a-protocol.org/latest/specification/#443-agentcapabilities). */ const documentation = [ { diff --git a/packages/apidom-ls/src/config/a2a/agent-extension/documentation.ts b/packages/apidom-ls/src/config/a2a/agent-extension/documentation.ts index 6a524bbcb3..f74d13aed6 100644 --- a/packages/apidom-ls/src/config/a2a/agent-extension/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/agent-extension/documentation.ts @@ -1,3 +1,30 @@ -const documentation: never[] = []; +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 AgentExtension fields. + * See [AgentExtension](https://a2a-protocol.org/latest/specification/#444-agentextension). + */ +const documentation = [ + { + target: 'uri', + docs: 'The unique URI identifying the extension (string).', + targetSpecs: A2A1, + }, + { + target: 'description', + docs: 'A human-readable description of how this agent uses the extension (string).', + targetSpecs: A2A1, + }, + { + target: 'required', + docs: "If true, the client must understand and comply with the extension's requirements (boolean).", + targetSpecs: A2A1, + }, + { + target: 'params', + docs: 'Optional extension-specific configuration parameters (object).', + targetSpecs: A2A1, + }, +]; export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/documentation.ts b/packages/apidom-ls/src/config/a2a/agent-skill/documentation.ts index 5c23585dd5..e33cbb3894 100644 --- a/packages/apidom-ls/src/config/a2a/agent-skill/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/agent-skill/documentation.ts @@ -42,7 +42,7 @@ const documentation = [ }, { target: 'securityRequirements', - docs: 'Array of [Security Requirement Objects](https://a2a-protocol.org/latest/specification/#securityrequirement) necessary for this skill.', + docs: 'Array of [Security Requirement Objects](https://a2a-protocol.org/latest/specification/#451-securityscheme) necessary for this skill.', targetSpecs: A2A1, }, ]; diff --git a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/documentation.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/documentation.ts index 6a524bbcb3..2519345444 100644 --- a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/documentation.ts @@ -1,3 +1,25 @@ -const documentation: never[] = []; +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 APIKeySecurityScheme fields. + * See [APIKeySecurityScheme](https://a2a-protocol.org/latest/specification/#452-apikeysecurityscheme). + */ +const documentation = [ + { + target: 'description', + docs: 'An optional description for the security scheme (string).', + targetSpecs: A2A1, + }, + { + target: 'location', + docs: 'The location of the API key. Valid values are "query", "header", or "cookie" (string, required).', + targetSpecs: A2A1, + }, + { + target: 'name', + docs: 'The name of the header, query, or cookie parameter to be used (string, required).', + targetSpecs: A2A1, + }, +]; export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/index.ts index 47f7d48761..80998fb078 100644 --- a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/index.ts @@ -1,5 +1,7 @@ import allowedFieldsLint from './allowed-fields.ts'; +import locationRequiredLint from './location--required.ts'; +import nameRequiredLint from './name--required.ts'; -const lints = [allowedFieldsLint]; +const lints = [allowedFieldsLint, locationRequiredLint, nameRequiredLint]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--required.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/location--required.ts similarity index 51% rename from packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--required.ts rename to packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/location--required.ts index 502df5bad4..9b05edc0ce 100644 --- a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--required.ts +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/location--required.ts @@ -4,25 +4,25 @@ import ApilintCodes from '../../../codes.ts'; import { LinterMeta } from '../../../../apidom-language-types.ts'; import { A2A1 } from '../../target-specs.ts'; -const authorizationUrlRequiredLint: LinterMeta = { - code: ApilintCodes.A2A1_IMPLICIT_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_REQUIRED, +const locationRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_API_KEY_SECURITY_SCHEME_FIELD_LOCATION_REQUIRED, source: 'apilint', - message: "should always have an 'authorizationUrl' field", + message: "should always have a 'location' field", severity: DiagnosticSeverity.Error, linterFunction: 'hasRequiredField', - linterParams: ['authorizationUrl'], + linterParams: ['location'], marker: 'key', targetSpecs: A2A1, data: { quickFix: [ { - message: "add 'authorizationUrl' field", + message: "add 'location' field", action: 'addChild', - snippetYaml: "authorizationUrl: ''\n", - snippetJson: '"authorizationUrl": "",\n', + snippetYaml: "location: ''\n", + snippetJson: '"location": "",\n', }, ], }, }; -export default authorizationUrlRequiredLint; +export default locationRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--required.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/name--required.ts similarity index 55% rename from packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--required.ts rename to packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/name--required.ts index 026c8894f6..cf2a765c49 100644 --- a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--required.ts +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/name--required.ts @@ -4,25 +4,25 @@ import ApilintCodes from '../../../codes.ts'; import { LinterMeta } from '../../../../apidom-language-types.ts'; import { A2A1 } from '../../target-specs.ts'; -const tokenUrlRequiredLint: LinterMeta = { - code: ApilintCodes.A2A1_PASSWORD_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED, +const nameRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_API_KEY_SECURITY_SCHEME_FIELD_NAME_REQUIRED, source: 'apilint', - message: "should always have a 'tokenUrl' field", + message: "should always have a 'name' field", severity: DiagnosticSeverity.Error, linterFunction: 'hasRequiredField', - linterParams: ['tokenUrl'], + linterParams: ['name'], marker: 'key', targetSpecs: A2A1, data: { quickFix: [ { - message: "add 'tokenUrl' field", + message: "add 'name' field", action: 'addChild', - snippetYaml: "tokenUrl: ''\n", - snippetJson: '"tokenUrl": "",\n', + snippetYaml: "name: ''\n", + snippetJson: '"name": "",\n', }, ], }, }; -export default tokenUrlRequiredLint; +export default nameRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/documentation.ts b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/documentation.ts index cdb2ba976b..b36e7c3961 100644 --- a/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/authorization-code-oauth-flow/documentation.ts @@ -2,7 +2,7 @@ import { A2A1 } from '../target-specs.ts'; /** * Hover documentation for A2A v1 AuthorizationCodeOAuthFlow fields. - * See [AuthorizationCodeOAuthFlow](https://a2a-protocol.org/latest/specification/#authorizationcodeoauthflow). + * See [AuthorizationCodeOAuthFlow](https://a2a-protocol.org/latest/specification/#458-authorizationcodeoauthflow). */ const documentation = [ { diff --git a/packages/apidom-ls/src/config/a2a/config.ts b/packages/apidom-ls/src/config/a2a/config.ts index 920cbb5be0..4a2fca7e9d 100644 --- a/packages/apidom-ls/src/config/a2a/config.ts +++ b/packages/apidom-ls/src/config/a2a/config.ts @@ -11,8 +11,6 @@ import oauthFlowsMeta from './oauth-flows/meta.ts'; import authorizationCodeOAuthFlowMeta from './authorization-code-oauth-flow/meta.ts'; import clientCredentialsOAuthFlowMeta from './client-credentials-oauth-flow/meta.ts'; import deviceCodeOAuthFlowMeta from './device-code-oauth-flow/meta.ts'; -import implicitOAuthFlowMeta from './implicit-oauth-flow/meta.ts'; -import passwordOAuthFlowMeta from './password-oauth-flow/meta.ts'; import agentExtensionMeta from './agent-extension/meta.ts'; import apiKeySecuritySchemeMeta from './api-key-security-scheme/meta.ts'; import httpAuthSecuritySchemeMeta from './http-auth-security-scheme/meta.ts'; @@ -45,8 +43,6 @@ export default { authorizationCodeOAuthFlow: authorizationCodeOAuthFlowMeta, clientCredentialsOAuthFlow: clientCredentialsOAuthFlowMeta, deviceCodeOAuthFlow: deviceCodeOAuthFlowMeta, - implicitOAuthFlow: implicitOAuthFlowMeta, - passwordOAuthFlow: passwordOAuthFlowMeta, agentExtension: agentExtensionMeta, apiKeySecurityScheme: apiKeySecuritySchemeMeta, httpAuthSecurityScheme: httpAuthSecuritySchemeMeta, diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/documentation.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/documentation.ts index 6a524bbcb3..d423229bd2 100644 --- a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/documentation.ts @@ -1,3 +1,25 @@ -const documentation: never[] = []; +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 HTTPAuthSecurityScheme fields. + * See [HTTPAuthSecurityScheme](https://a2a-protocol.org/latest/specification/#453-httpauthsecurityscheme). + */ +const documentation = [ + { + target: 'description', + docs: 'An optional description for the security scheme (string).', + targetSpecs: A2A1, + }, + { + target: 'scheme', + docs: 'The name of the HTTP Authentication scheme to be used in the Authorization header, as defined in RFC 7235 (e.g. "Bearer") (string, required).', + targetSpecs: A2A1, + }, + { + target: 'bearerFormat', + docs: 'A hint to the client to identify how the bearer token is formatted (e.g. "JWT") (string).', + targetSpecs: A2A1, + }, +]; export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts index 47f7d48761..b78c6c9b54 100644 --- a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts @@ -1,5 +1,6 @@ import allowedFieldsLint from './allowed-fields.ts'; +import schemeRequiredLint from './scheme--required.ts'; -const lints = [allowedFieldsLint]; +const lints = [allowedFieldsLint, schemeRequiredLint]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/scheme--required.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/scheme--required.ts new file mode 100644 index 0000000000..1d794bd9ca --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/scheme--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const schemeRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_HTTP_AUTH_SECURITY_SCHEME_FIELD_SCHEME_REQUIRED, + source: 'apilint', + message: "should always have a 'scheme' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['scheme'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'scheme' field", + action: 'addChild', + snippetYaml: "scheme: ''\n", + snippetJson: '"scheme": "",\n', + }, + ], + }, +}; + +export default schemeRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/completion.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/completion.ts deleted file mode 100644 index 2465c3da40..0000000000 --- a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/completion.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - ApidomCompletionItem, - CompletionFormat, - CompletionType, -} from '../../../apidom-language-types.ts'; -import { A2A1 } from '../target-specs.ts'; - -const flowField = ( - label: string, - format: CompletionFormat, - docs: string, -): ApidomCompletionItem => ({ - label, - insertText: label, - kind: 14, - format, - type: CompletionType.PROPERTY, - insertTextFormat: 2, - documentation: { kind: 'markdown', value: docs }, - targetSpecs: A2A1, -}); - -const completion: ApidomCompletionItem[] = [ - flowField( - 'authorizationUrl', - CompletionFormat.QUOTED, - 'The authorization URL to be used for this flow.', - ), - flowField( - 'refreshUrl', - CompletionFormat.QUOTED, - 'The URL to be used for obtaining refresh tokens.', - ), - flowField( - 'scopes', - CompletionFormat.OBJECT, - 'Available scopes for the OAuth2 security scheme. A map of scope name to short description.', - ), -]; - -export default completion; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/documentation.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/documentation.ts deleted file mode 100644 index 42c4afa91a..0000000000 --- a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/documentation.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { A2A1 } from '../target-specs.ts'; - -/** - * Hover documentation for A2A v1 ImplicitOAuthFlow fields (deprecated flow). - * See [ImplicitOAuthFlow](https://a2a-protocol.org/latest/specification/#implicitoauthflow). - */ -const documentation = [ - { - target: 'authorizationUrl', - docs: 'The authorization URL to be used for this flow (string, required).', - targetSpecs: A2A1, - }, - { - target: 'refreshUrl', - docs: 'The URL to be used for obtaining refresh tokens (string).', - targetSpecs: A2A1, - }, - { - target: 'scopes', - docs: 'A map of scope name to description available for the OAuth2 security scheme (required).', - targetSpecs: A2A1, - }, -]; - -export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/allowed-fields.ts deleted file mode 100644 index 11920d5f11..0000000000 --- a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/allowed-fields.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DiagnosticSeverity } from 'vscode-languageserver-types'; - -import ApilintCodes from '../../../codes.ts'; -import { LinterMeta } from '../../../../apidom-language-types.ts'; -import { A2A1 } from '../../target-specs.ts'; - -const allowedFieldsLint: LinterMeta = { - code: ApilintCodes.NOT_ALLOWED_FIELDS, - source: 'apilint', - message: 'Object includes not allowed fields', - severity: DiagnosticSeverity.Error, - linterFunction: 'allowedFields', - linterParams: [['authorizationUrl', 'refreshUrl', 'scopes']], - marker: 'key', - targetSpecs: A2A1, -}; - -export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--type.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--type.ts deleted file mode 100644 index 46af92ce53..0000000000 --- a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/authorization-url--type.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { DiagnosticSeverity } from 'vscode-languageserver-types'; - -import ApilintCodes from '../../../codes.ts'; -import { LinterMeta } from '../../../../apidom-language-types.ts'; -import { A2A1 } from '../../target-specs.ts'; - -const lint: LinterMeta = { - code: ApilintCodes.A2A1_IMPLICIT_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_TYPE, - source: 'apilint', - message: "'authorizationUrl' must be a string", - severity: DiagnosticSeverity.Error, - linterFunction: 'apilintType', - linterParams: ['string'], - marker: 'value', - target: 'authorizationUrl', - targetSpecs: A2A1, -}; - -export default lint; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/index.ts deleted file mode 100644 index cb98dff2dc..0000000000 --- a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import allowedFieldsLint from './allowed-fields.ts'; -import authorizationUrlRequiredLint from './authorization-url--required.ts'; -import authorizationUrlLint from './authorization-url--type.ts'; -import refreshUrlLint from './refresh-url--type.ts'; -import scopesLint from './scopes--type.ts'; - -const lints = [ - allowedFieldsLint, - authorizationUrlRequiredLint, - authorizationUrlLint, - refreshUrlLint, - scopesLint, -]; - -export default lints; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/refresh-url--type.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/refresh-url--type.ts deleted file mode 100644 index 3f0211eedc..0000000000 --- a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/refresh-url--type.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { DiagnosticSeverity } from 'vscode-languageserver-types'; - -import ApilintCodes from '../../../codes.ts'; -import { LinterMeta } from '../../../../apidom-language-types.ts'; -import { A2A1 } from '../../target-specs.ts'; - -const lint: LinterMeta = { - code: ApilintCodes.A2A1_IMPLICIT_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE, - source: 'apilint', - message: "'refreshUrl' must be a string", - severity: DiagnosticSeverity.Error, - linterFunction: 'apilintType', - linterParams: ['string'], - marker: 'value', - target: 'refreshUrl', - targetSpecs: A2A1, -}; - -export default lint; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/scopes--type.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/scopes--type.ts deleted file mode 100644 index 5297d696af..0000000000 --- a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/lint/scopes--type.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { DiagnosticSeverity } from 'vscode-languageserver-types'; - -import ApilintCodes from '../../../codes.ts'; -import { LinterMeta } from '../../../../apidom-language-types.ts'; -import { A2A1 } from '../../target-specs.ts'; - -const lint: LinterMeta = { - code: ApilintCodes.A2A1_IMPLICIT_OAUTH_FLOW_FIELD_SCOPES_TYPE, - source: 'apilint', - message: "'scopes' must be an object", - severity: DiagnosticSeverity.Error, - linterFunction: 'apilintType', - linterParams: ['object'], - marker: 'value', - target: 'scopes', - targetSpecs: A2A1, -}; - -export default lint; diff --git a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/meta.ts b/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/meta.ts deleted file mode 100644 index 6e1bbd7f60..0000000000 --- a/packages/apidom-ls/src/config/a2a/implicit-oauth-flow/meta.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { FormatMeta } from '../../../apidom-language-types.ts'; -import lint from './lint/index.ts'; -import completion from './completion.ts'; -import documentation from './documentation.ts'; - -const meta: FormatMeta = { - lint, - completion, - documentation, -}; - -export default meta; diff --git a/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/documentation.ts b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/documentation.ts index 6a524bbcb3..e8543e74fe 100644 --- a/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/documentation.ts @@ -1,3 +1,15 @@ -const documentation: never[] = []; +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 MutualTlsSecurityScheme fields. + * See [MutualTlsSecurityScheme](https://a2a-protocol.org/latest/specification/#456-mutualtlssecurityscheme). + */ +const documentation = [ + { + target: 'description', + docs: 'An optional description for the security scheme (string).', + targetSpecs: A2A1, + }, +]; export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/completion.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/completion.ts index 415b7c397c..0cd1da1f20 100644 --- a/packages/apidom-ls/src/config/a2a/oauth-flows/completion.ts +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/completion.ts @@ -20,8 +20,6 @@ const completion: ApidomCompletionItem[] = [ flowField('authorizationCode', 'OAuth 2.0 Authorization Code flow configuration.'), flowField('clientCredentials', 'OAuth 2.0 Client Credentials flow configuration.'), flowField('deviceCode', 'OAuth 2.0 Device Authorization Grant flow configuration.'), - flowField('implicit', 'OAuth 2.0 Implicit flow configuration.'), - flowField('password', 'OAuth 2.0 Resource Owner Password flow configuration.'), ]; export default completion; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/documentation.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/documentation.ts index bdf21f2d7a..fa01754834 100644 --- a/packages/apidom-ls/src/config/a2a/oauth-flows/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/documentation.ts @@ -3,32 +3,22 @@ import { A2A1 } from '../target-specs.ts'; /** * Hover documentation for A2A v1 OAuthFlows fields. OAuthFlows must contain * exactly one of the flow configurations below. - * See [OAuthFlows](https://a2a-protocol.org/latest/specification/#oauthflows). + * See [OAuthFlows](https://a2a-protocol.org/latest/specification/#457-oauthflows). */ const documentation = [ { target: 'authorizationCode', - docs: '[Authorization Code OAuth Flow](https://a2a-protocol.org/latest/specification/#authorizationcodeoauthflow) — configuration for the OAuth Authorization Code flow.', + docs: '[Authorization Code OAuth Flow](https://a2a-protocol.org/latest/specification/#458-authorizationcodeoauthflow) — configuration for the OAuth Authorization Code flow.', targetSpecs: A2A1, }, { target: 'clientCredentials', - docs: '[Client Credentials OAuth Flow](https://a2a-protocol.org/latest/specification/#clientcredentialsoauthflow) — configuration for the OAuth Client Credentials flow.', + docs: '[Client Credentials OAuth Flow](https://a2a-protocol.org/latest/specification/#459-clientcredentialsoauthflow) — configuration for the OAuth Client Credentials flow.', targetSpecs: A2A1, }, { target: 'deviceCode', - docs: '[Device Code OAuth Flow](https://a2a-protocol.org/latest/specification/#devicecodeoauthflow) — configuration for the OAuth Device Code flow (RFC 8628).', - targetSpecs: A2A1, - }, - { - target: 'implicit', - docs: '[Implicit OAuth Flow](https://a2a-protocol.org/latest/specification/#implicitoauthflow) — deprecated; use Authorization Code + PKCE instead.', - targetSpecs: A2A1, - }, - { - target: 'password', - docs: '[Password OAuth Flow](https://a2a-protocol.org/latest/specification/#passwordoauthflow) — deprecated; use Authorization Code + PKCE or Device Code.', + docs: '[Device Code OAuth Flow](https://a2a-protocol.org/latest/specification/#4510-devicecodeoauthflow) — configuration for the OAuth Device Code flow (RFC 8628).', targetSpecs: A2A1, }, ]; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts index 6b4e0a9173..e2f4878d5d 100644 --- a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts @@ -10,7 +10,7 @@ const allowedFieldsLint: LinterMeta = { message: 'Object includes not allowed fields', severity: DiagnosticSeverity.Error, linterFunction: 'allowedFields', - linterParams: [['authorizationCode', 'clientCredentials', 'deviceCode', 'implicit', 'password']], + linterParams: [['authorizationCode', 'clientCredentials', 'deviceCode']], marker: 'key', targetSpecs: A2A1, }; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/implicit--type.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/implicit--type.ts deleted file mode 100644 index 630f8a5187..0000000000 --- a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/implicit--type.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { DiagnosticSeverity } from 'vscode-languageserver-types'; - -import ApilintCodes from '../../../codes.ts'; -import { LinterMeta } from '../../../../apidom-language-types.ts'; -import { A2A1 } from '../../target-specs.ts'; - -const lint: LinterMeta = { - code: ApilintCodes.A2A1_OAUTH_FLOWS_FIELD_IMPLICIT_TYPE, - source: 'apilint', - message: "'implicit' must be an Implicit OAuth Flow Object", - severity: DiagnosticSeverity.Error, - linterFunction: 'apilintElementOrClass', - linterParams: ['implicitOAuthFlow'], - marker: 'value', - target: 'implicit', - targetSpecs: A2A1, -}; - -export default lint; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/index.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/index.ts index d3d8dc4983..bd71828554 100644 --- a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/index.ts @@ -2,16 +2,7 @@ import allowedFieldsLint from './allowed-fields.ts'; import authorizationCodeLint from './authorization-code--type.ts'; import clientCredentialsLint from './client-credentials--type.ts'; import deviceCodeLint from './device-code--type.ts'; -import implicitLint from './implicit--type.ts'; -import passwordLint from './password--type.ts'; -const lints = [ - allowedFieldsLint, - authorizationCodeLint, - clientCredentialsLint, - deviceCodeLint, - implicitLint, - passwordLint, -]; +const lints = [allowedFieldsLint, authorizationCodeLint, clientCredentialsLint, deviceCodeLint]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/password--type.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/password--type.ts deleted file mode 100644 index 79d8485194..0000000000 --- a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/password--type.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { DiagnosticSeverity } from 'vscode-languageserver-types'; - -import ApilintCodes from '../../../codes.ts'; -import { LinterMeta } from '../../../../apidom-language-types.ts'; -import { A2A1 } from '../../target-specs.ts'; - -const lint: LinterMeta = { - code: ApilintCodes.A2A1_OAUTH_FLOWS_FIELD_PASSWORD_TYPE, - source: 'apilint', - message: "'password' must be a Password OAuth Flow Object", - severity: DiagnosticSeverity.Error, - linterFunction: 'apilintElementOrClass', - linterParams: ['passwordOAuthFlow'], - marker: 'value', - target: 'password', - targetSpecs: A2A1, -}; - -export default lint; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/documentation.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/documentation.ts index 6a524bbcb3..2bb5761b81 100644 --- a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/documentation.ts @@ -1,3 +1,25 @@ -const documentation: never[] = []; +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 OAuth2SecurityScheme fields. + * See [OAuth2SecurityScheme](https://a2a-protocol.org/latest/specification/#454-oauth2securityscheme). + */ +const documentation = [ + { + target: 'description', + docs: 'An optional description for the security scheme (string).', + targetSpecs: A2A1, + }, + { + target: 'flows', + docs: 'An object containing configuration information for the supported OAuth 2.0 flows (required).', + targetSpecs: A2A1, + }, + { + target: 'oauth2MetadataUrl', + docs: 'URL to the OAuth2 authorization server metadata (RFC 8414). TLS is required (string).', + targetSpecs: A2A1, + }, +]; export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/flows--required.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/flows--required.ts new file mode 100644 index 0000000000..2cd6b633d5 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/flows--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const flowsRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_OAUTH2_SECURITY_SCHEME_FIELD_FLOWS_REQUIRED, + source: 'apilint', + message: "should always have a 'flows' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['flows'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'flows' field", + action: 'addChild', + snippetYaml: 'flows: \n \n', + snippetJson: '"flows": {\n \n },\n', + }, + ], + }, +}; + +export default flowsRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts index 47f7d48761..fd26b6a59a 100644 --- a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts @@ -1,5 +1,6 @@ import allowedFieldsLint from './allowed-fields.ts'; +import flowsRequiredLint from './flows--required.ts'; -const lints = [allowedFieldsLint]; +const lints = [allowedFieldsLint, flowsRequiredLint]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/documentation.ts b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/documentation.ts index 6a524bbcb3..1e36d231d4 100644 --- a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/documentation.ts @@ -1,3 +1,20 @@ -const documentation: never[] = []; +import { A2A1 } from '../target-specs.ts'; + +/** + * Hover documentation for A2A v1 OpenIdConnectSecurityScheme fields. + * See [OpenIdConnectSecurityScheme](https://a2a-protocol.org/latest/specification/#455-openidconnectsecurityscheme). + */ +const documentation = [ + { + target: 'description', + docs: 'An optional description for the security scheme (string).', + targetSpecs: A2A1, + }, + { + target: 'openIdConnectUrl', + docs: "The OpenID Connect Discovery URL for the OIDC provider's metadata (string, required).", + targetSpecs: A2A1, + }, +]; export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts index 47f7d48761..ebd0eb7a6c 100644 --- a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts @@ -1,5 +1,6 @@ import allowedFieldsLint from './allowed-fields.ts'; +import openIdConnectUrlRequiredLint from './open-id-connect-url--required.ts'; -const lints = [allowedFieldsLint]; +const lints = [allowedFieldsLint, openIdConnectUrlRequiredLint]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/open-id-connect-url--required.ts b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/open-id-connect-url--required.ts new file mode 100644 index 0000000000..20677692e0 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/open-id-connect-url--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const openIdConnectUrlRequiredLint: LinterMeta = { + code: ApilintCodes.A2A1_OPEN_ID_CONNECT_SECURITY_SCHEME_FIELD_OPEN_ID_CONNECT_URL_REQUIRED, + source: 'apilint', + message: "should always have an 'openIdConnectUrl' field", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['openIdConnectUrl'], + marker: 'key', + targetSpecs: A2A1, + data: { + quickFix: [ + { + message: "add 'openIdConnectUrl' field", + action: 'addChild', + snippetYaml: "openIdConnectUrl: ''\n", + snippetJson: '"openIdConnectUrl": "",\n', + }, + ], + }, +}; + +export default openIdConnectUrlRequiredLint; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/completion.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/completion.ts deleted file mode 100644 index cd69f1880d..0000000000 --- a/packages/apidom-ls/src/config/a2a/password-oauth-flow/completion.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - ApidomCompletionItem, - CompletionFormat, - CompletionType, -} from '../../../apidom-language-types.ts'; -import { A2A1 } from '../target-specs.ts'; - -const flowField = ( - label: string, - format: CompletionFormat, - docs: string, -): ApidomCompletionItem => ({ - label, - insertText: label, - kind: 14, - format, - type: CompletionType.PROPERTY, - insertTextFormat: 2, - documentation: { kind: 'markdown', value: docs }, - targetSpecs: A2A1, -}); - -const completion: ApidomCompletionItem[] = [ - flowField('tokenUrl', CompletionFormat.QUOTED, 'The token URL to be used for this flow.'), - flowField( - 'refreshUrl', - CompletionFormat.QUOTED, - 'The URL to be used for obtaining refresh tokens.', - ), - flowField( - 'scopes', - CompletionFormat.OBJECT, - 'Available scopes for the OAuth2 security scheme. A map of scope name to short description.', - ), -]; - -export default completion; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/documentation.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/documentation.ts deleted file mode 100644 index 61634d2863..0000000000 --- a/packages/apidom-ls/src/config/a2a/password-oauth-flow/documentation.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { A2A1 } from '../target-specs.ts'; - -/** - * Hover documentation for A2A v1 PasswordOAuthFlow fields (deprecated flow). - * See [PasswordOAuthFlow](https://a2a-protocol.org/latest/specification/#passwordoauthflow). - */ -const documentation = [ - { - target: 'tokenUrl', - docs: 'The token URL to be used for this flow (string, required).', - targetSpecs: A2A1, - }, - { - target: 'refreshUrl', - docs: 'The URL to be used for obtaining refresh tokens (string).', - targetSpecs: A2A1, - }, - { - target: 'scopes', - docs: 'A map of scope name to description available for the OAuth2 security scheme (required).', - targetSpecs: A2A1, - }, -]; - -export default documentation; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/allowed-fields.ts deleted file mode 100644 index 2c7a29e77f..0000000000 --- a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/allowed-fields.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DiagnosticSeverity } from 'vscode-languageserver-types'; - -import ApilintCodes from '../../../codes.ts'; -import { LinterMeta } from '../../../../apidom-language-types.ts'; -import { A2A1 } from '../../target-specs.ts'; - -const allowedFieldsLint: LinterMeta = { - code: ApilintCodes.NOT_ALLOWED_FIELDS, - source: 'apilint', - message: 'Object includes not allowed fields', - severity: DiagnosticSeverity.Error, - linterFunction: 'allowedFields', - linterParams: [['tokenUrl', 'refreshUrl', 'scopes']], - marker: 'key', - targetSpecs: A2A1, -}; - -export default allowedFieldsLint; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/index.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/index.ts deleted file mode 100644 index 3e388d4fb3..0000000000 --- a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import allowedFieldsLint from './allowed-fields.ts'; -import tokenUrlRequiredLint from './token-url--required.ts'; -import tokenUrlLint from './token-url--type.ts'; -import refreshUrlLint from './refresh-url--type.ts'; -import scopesLint from './scopes--type.ts'; - -const lints = [allowedFieldsLint, tokenUrlRequiredLint, tokenUrlLint, refreshUrlLint, scopesLint]; - -export default lints; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/refresh-url--type.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/refresh-url--type.ts deleted file mode 100644 index 90a55c86a9..0000000000 --- a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/refresh-url--type.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { DiagnosticSeverity } from 'vscode-languageserver-types'; - -import ApilintCodes from '../../../codes.ts'; -import { LinterMeta } from '../../../../apidom-language-types.ts'; -import { A2A1 } from '../../target-specs.ts'; - -const lint: LinterMeta = { - code: ApilintCodes.A2A1_PASSWORD_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE, - source: 'apilint', - message: "'refreshUrl' must be a string", - severity: DiagnosticSeverity.Error, - linterFunction: 'apilintType', - linterParams: ['string'], - marker: 'value', - target: 'refreshUrl', - targetSpecs: A2A1, -}; - -export default lint; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/scopes--type.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/scopes--type.ts deleted file mode 100644 index 99d0f42b64..0000000000 --- a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/scopes--type.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { DiagnosticSeverity } from 'vscode-languageserver-types'; - -import ApilintCodes from '../../../codes.ts'; -import { LinterMeta } from '../../../../apidom-language-types.ts'; -import { A2A1 } from '../../target-specs.ts'; - -const lint: LinterMeta = { - code: ApilintCodes.A2A1_PASSWORD_OAUTH_FLOW_FIELD_SCOPES_TYPE, - source: 'apilint', - message: "'scopes' must be an object", - severity: DiagnosticSeverity.Error, - linterFunction: 'apilintType', - linterParams: ['object'], - marker: 'value', - target: 'scopes', - targetSpecs: A2A1, -}; - -export default lint; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--type.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--type.ts deleted file mode 100644 index d781310197..0000000000 --- a/packages/apidom-ls/src/config/a2a/password-oauth-flow/lint/token-url--type.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { DiagnosticSeverity } from 'vscode-languageserver-types'; - -import ApilintCodes from '../../../codes.ts'; -import { LinterMeta } from '../../../../apidom-language-types.ts'; -import { A2A1 } from '../../target-specs.ts'; - -const lint: LinterMeta = { - code: ApilintCodes.A2A1_PASSWORD_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE, - source: 'apilint', - message: "'tokenUrl' must be a string", - severity: DiagnosticSeverity.Error, - linterFunction: 'apilintType', - linterParams: ['string'], - marker: 'value', - target: 'tokenUrl', - targetSpecs: A2A1, -}; - -export default lint; diff --git a/packages/apidom-ls/src/config/a2a/password-oauth-flow/meta.ts b/packages/apidom-ls/src/config/a2a/password-oauth-flow/meta.ts deleted file mode 100644 index 6e1bbd7f60..0000000000 --- a/packages/apidom-ls/src/config/a2a/password-oauth-flow/meta.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { FormatMeta } from '../../../apidom-language-types.ts'; -import lint from './lint/index.ts'; -import completion from './completion.ts'; -import documentation from './documentation.ts'; - -const meta: FormatMeta = { - lint, - completion, - documentation, -}; - -export default meta; diff --git a/packages/apidom-ls/src/config/a2a/security-scheme/documentation.ts b/packages/apidom-ls/src/config/a2a/security-scheme/documentation.ts index 180bfb1481..aafc1b75af 100644 --- a/packages/apidom-ls/src/config/a2a/security-scheme/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/security-scheme/documentation.ts @@ -3,32 +3,32 @@ import { A2A1 } from '../target-specs.ts'; /** * Hover documentation for A2A v1 SecurityScheme fields. SecurityScheme is a * discriminated union — exactly one of the subtype fields must be set. - * See [SecurityScheme](https://a2a-protocol.org/latest/specification/#securityscheme). + * See [SecurityScheme](https://a2a-protocol.org/latest/specification/#451-securityscheme). */ const documentation = [ { target: 'apiKeySecurityScheme', - docs: '[API Key Security Scheme](https://a2a-protocol.org/latest/specification/#apikeysecurityscheme) — API key-based authentication. Set this OR exactly one other subtype.', + docs: '[API Key Security Scheme](https://a2a-protocol.org/latest/specification/#452-apikeysecurityscheme) — API key-based authentication. Set this OR exactly one other subtype.', targetSpecs: A2A1, }, { target: 'httpAuthSecurityScheme', - docs: '[HTTP Auth Security Scheme](https://a2a-protocol.org/latest/specification/#httpauthsecurityscheme) — HTTP authentication (Basic, Bearer, etc.). Set this OR exactly one other subtype.', + docs: '[HTTP Auth Security Scheme](https://a2a-protocol.org/latest/specification/#453-httpauthsecurityscheme) — HTTP authentication (Basic, Bearer, etc.). Set this OR exactly one other subtype.', targetSpecs: A2A1, }, { target: 'oauth2SecurityScheme', - docs: '[OAuth2 Security Scheme](https://a2a-protocol.org/latest/specification/#oauth2securityscheme) — OAuth 2.0 authentication. Set this OR exactly one other subtype.', + docs: '[OAuth2 Security Scheme](https://a2a-protocol.org/latest/specification/#454-oauth2securityscheme) — OAuth 2.0 authentication. Set this OR exactly one other subtype.', targetSpecs: A2A1, }, { target: 'openIdConnectSecurityScheme', - docs: '[OpenID Connect Security Scheme](https://a2a-protocol.org/latest/specification/#openidconnectsecurityscheme) — OpenID Connect authentication. Set this OR exactly one other subtype.', + docs: '[OpenID Connect Security Scheme](https://a2a-protocol.org/latest/specification/#455-openidconnectsecurityscheme) — OpenID Connect authentication. Set this OR exactly one other subtype.', targetSpecs: A2A1, }, { target: 'mtlsSecurityScheme', - docs: '[Mutual TLS Security Scheme](https://a2a-protocol.org/latest/specification/#mutualtlssecurityscheme) — mutual TLS authentication. Set this OR exactly one other subtype.', + docs: '[Mutual TLS Security Scheme](https://a2a-protocol.org/latest/specification/#456-mutualtlssecurityscheme) — mutual TLS authentication. Set this OR exactly one other subtype.', targetSpecs: A2A1, }, ]; diff --git a/packages/apidom-ls/src/config/codes.ts b/packages/apidom-ls/src/config/codes.ts index 05c68e3b57..3df8173e9d 100644 --- a/packages/apidom-ls/src/config/codes.ts +++ b/packages/apidom-ls/src/config/codes.ts @@ -1515,8 +1515,6 @@ enum ApilintCodes { A2A1_OAUTH_FLOWS_FIELD_AUTHORIZATION_CODE_TYPE = 9080100, A2A1_OAUTH_FLOWS_FIELD_CLIENT_CREDENTIALS_TYPE = 9080200, A2A1_OAUTH_FLOWS_FIELD_DEVICE_CODE_TYPE = 9080300, - A2A1_OAUTH_FLOWS_FIELD_IMPLICIT_TYPE = 9080400, - A2A1_OAUTH_FLOWS_FIELD_PASSWORD_TYPE = 9080500, A2A1_AUTHORIZATION_CODE_OAUTH_FLOW = 9090000, A2A1_AUTHORIZATION_CODE_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_TYPE = 9090100, @@ -1544,17 +1542,18 @@ enum ApilintCodes { A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED = 9110600, A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_SCOPES_REQUIRED = 9110700, - A2A1_IMPLICIT_OAUTH_FLOW = 9120000, - A2A1_IMPLICIT_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_TYPE = 9120100, - A2A1_IMPLICIT_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE = 9120200, - A2A1_IMPLICIT_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9120300, - A2A1_IMPLICIT_OAUTH_FLOW_FIELD_AUTHORIZATION_URL_REQUIRED = 9120400, - - A2A1_PASSWORD_OAUTH_FLOW = 9130000, - A2A1_PASSWORD_OAUTH_FLOW_FIELD_TOKEN_URL_TYPE = 9130100, - A2A1_PASSWORD_OAUTH_FLOW_FIELD_REFRESH_URL_TYPE = 9130200, - A2A1_PASSWORD_OAUTH_FLOW_FIELD_SCOPES_TYPE = 9130300, - A2A1_PASSWORD_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED = 9130400, + A2A1_API_KEY_SECURITY_SCHEME = 9140000, + A2A1_API_KEY_SECURITY_SCHEME_FIELD_LOCATION_REQUIRED = 9140100, + A2A1_API_KEY_SECURITY_SCHEME_FIELD_NAME_REQUIRED = 9140200, + + A2A1_HTTP_AUTH_SECURITY_SCHEME = 9150000, + A2A1_HTTP_AUTH_SECURITY_SCHEME_FIELD_SCHEME_REQUIRED = 9150100, + + A2A1_OAUTH2_SECURITY_SCHEME = 9160000, + A2A1_OAUTH2_SECURITY_SCHEME_FIELD_FLOWS_REQUIRED = 9160100, + + A2A1_OPEN_ID_CONNECT_SECURITY_SCHEME = 9170000, + A2A1_OPEN_ID_CONNECT_SECURITY_SCHEME_FIELD_OPEN_ID_CONNECT_URL_REQUIRED = 9170100, } export default ApilintCodes; diff --git a/packages/apidom-ns-a2a-1/src/elements/ImplicitOAuthFlow.ts b/packages/apidom-ns-a2a-1/src/elements/ImplicitOAuthFlow.ts deleted file mode 100644 index 1a2bb22969..0000000000 --- a/packages/apidom-ns-a2a-1/src/elements/ImplicitOAuthFlow.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { ObjectElement, StringElement, Attributes, Meta } from '@swagger-api/apidom-core'; - -/** - * @public - * - * Deprecated by A2A spec; use Authorization Code + PKCE instead. - */ -class ImplicitOAuthFlow extends ObjectElement { - constructor(content?: Record, meta?: Meta, attributes?: Attributes) { - super(content, meta, attributes); - this.element = 'implicitOAuthFlow'; - this.classes.push('implicit-oauth-flow'); - this.classes.push('oauth-flow'); - } - - get authorizationUrl(): StringElement | undefined { - return this.get('authorizationUrl'); - } - - set authorizationUrl(authorizationUrl: StringElement | undefined) { - this.set('authorizationUrl', authorizationUrl); - } - - get refreshUrl(): StringElement | undefined { - return this.get('refreshUrl'); - } - - set refreshUrl(refreshUrl: StringElement | undefined) { - this.set('refreshUrl', refreshUrl); - } - - get scopes(): ObjectElement | undefined { - return this.get('scopes'); - } - - set scopes(scopes: ObjectElement | undefined) { - this.set('scopes', scopes); - } -} - -export default ImplicitOAuthFlow; diff --git a/packages/apidom-ns-a2a-1/src/elements/OAuthFlows.ts b/packages/apidom-ns-a2a-1/src/elements/OAuthFlows.ts index 64ebe7e345..c48e623d06 100644 --- a/packages/apidom-ns-a2a-1/src/elements/OAuthFlows.ts +++ b/packages/apidom-ns-a2a-1/src/elements/OAuthFlows.ts @@ -3,13 +3,11 @@ import { ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; import AuthorizationCodeOAuthFlowElement from './AuthorizationCodeOAuthFlow.ts'; import ClientCredentialsOAuthFlowElement from './ClientCredentialsOAuthFlow.ts'; import DeviceCodeOAuthFlowElement from './DeviceCodeOAuthFlow.ts'; -import ImplicitOAuthFlowElement from './ImplicitOAuthFlow.ts'; -import PasswordOAuthFlowElement from './PasswordOAuthFlow.ts'; /** * @public * - * Container for the five OAuth 2.0 flow configurations. Any subset may be + * Container for the three OAuth 2.0 flow configurations. Any subset may be * populated at once (this is not a discriminated union). */ class OAuthFlows extends ObjectElement { @@ -42,22 +40,6 @@ class OAuthFlows extends ObjectElement { set deviceCode(deviceCode: DeviceCodeOAuthFlowElement | undefined) { this.set('deviceCode', deviceCode); } - - get implicit(): ImplicitOAuthFlowElement | undefined { - return this.get('implicit'); - } - - set implicit(implicit: ImplicitOAuthFlowElement | undefined) { - this.set('implicit', implicit); - } - - get password(): PasswordOAuthFlowElement | undefined { - return this.get('password'); - } - - set password(password: PasswordOAuthFlowElement | undefined) { - this.set('password', password); - } } export default OAuthFlows; diff --git a/packages/apidom-ns-a2a-1/src/elements/PasswordOAuthFlow.ts b/packages/apidom-ns-a2a-1/src/elements/PasswordOAuthFlow.ts deleted file mode 100644 index 044067ae44..0000000000 --- a/packages/apidom-ns-a2a-1/src/elements/PasswordOAuthFlow.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { ObjectElement, StringElement, Attributes, Meta } from '@swagger-api/apidom-core'; - -/** - * @public - * - * Deprecated by A2A spec; use Authorization Code + PKCE or Device Code. - */ -class PasswordOAuthFlow extends ObjectElement { - constructor(content?: Record, meta?: Meta, attributes?: Attributes) { - super(content, meta, attributes); - this.element = 'passwordOAuthFlow'; - this.classes.push('password-oauth-flow'); - this.classes.push('oauth-flow'); - } - - get tokenUrl(): StringElement | undefined { - return this.get('tokenUrl'); - } - - set tokenUrl(tokenUrl: StringElement | undefined) { - this.set('tokenUrl', tokenUrl); - } - - get refreshUrl(): StringElement | undefined { - return this.get('refreshUrl'); - } - - set refreshUrl(refreshUrl: StringElement | undefined) { - this.set('refreshUrl', refreshUrl); - } - - get scopes(): ObjectElement | undefined { - return this.get('scopes'); - } - - set scopes(scopes: ObjectElement | undefined) { - this.set('scopes', scopes); - } -} - -export default PasswordOAuthFlow; diff --git a/packages/apidom-ns-a2a-1/src/index.ts b/packages/apidom-ns-a2a-1/src/index.ts index 5aeaf51460..3092c4ac00 100644 --- a/packages/apidom-ns-a2a-1/src/index.ts +++ b/packages/apidom-ns-a2a-1/src/index.ts @@ -113,14 +113,6 @@ export type { default as DeviceCodeOAuthFlowVisitor, DeviceCodeOAuthFlowVisitorOptions, } from './refractor/visitors/a2a-1/device-code-oauth-flow/index.ts'; -export type { - default as ImplicitOAuthFlowVisitor, - ImplicitOAuthFlowVisitorOptions, -} from './refractor/visitors/a2a-1/implicit-oauth-flow/index.ts'; -export type { - default as PasswordOAuthFlowVisitor, - PasswordOAuthFlowVisitorOptions, -} from './refractor/visitors/a2a-1/password-oauth-flow/index.ts'; export type { default as StringListVisitor, StringListVisitorOptions, @@ -176,8 +168,6 @@ export { isAuthorizationCodeOAuthFlowElement, isClientCredentialsOAuthFlowElement, isDeviceCodeOAuthFlowElement, - isImplicitOAuthFlowElement, - isPasswordOAuthFlowElement, isStringListElement, isSkillsElement, isSignaturesElement, @@ -220,7 +210,5 @@ export { AuthorizationCodeOAuthFlowElement, ClientCredentialsOAuthFlowElement, DeviceCodeOAuthFlowElement, - ImplicitOAuthFlowElement, - PasswordOAuthFlowElement, StringListElement, } from './refractor/registration.ts'; diff --git a/packages/apidom-ns-a2a-1/src/namespace.ts b/packages/apidom-ns-a2a-1/src/namespace.ts index 435ff71a2c..71be989beb 100644 --- a/packages/apidom-ns-a2a-1/src/namespace.ts +++ b/packages/apidom-ns-a2a-1/src/namespace.ts @@ -18,8 +18,6 @@ import OAuthFlowsElement from './elements/OAuthFlows.ts'; import AuthorizationCodeOAuthFlowElement from './elements/AuthorizationCodeOAuthFlow.ts'; import ClientCredentialsOAuthFlowElement from './elements/ClientCredentialsOAuthFlow.ts'; import DeviceCodeOAuthFlowElement from './elements/DeviceCodeOAuthFlow.ts'; -import ImplicitOAuthFlowElement from './elements/ImplicitOAuthFlow.ts'; -import PasswordOAuthFlowElement from './elements/PasswordOAuthFlow.ts'; import StringListElement from './elements/StringList.ts'; /** @@ -47,8 +45,6 @@ const a2a1 = { base.register('authorizationCodeOAuthFlow', AuthorizationCodeOAuthFlowElement); base.register('clientCredentialsOAuthFlow', ClientCredentialsOAuthFlowElement); base.register('deviceCodeOAuthFlow', DeviceCodeOAuthFlowElement); - base.register('implicitOAuthFlow', ImplicitOAuthFlowElement); - base.register('passwordOAuthFlow', PasswordOAuthFlowElement); base.register('stringList', StringListElement); return base; diff --git a/packages/apidom-ns-a2a-1/src/predicates.ts b/packages/apidom-ns-a2a-1/src/predicates.ts index 2af0e3a262..2c08923d01 100644 --- a/packages/apidom-ns-a2a-1/src/predicates.ts +++ b/packages/apidom-ns-a2a-1/src/predicates.ts @@ -18,8 +18,6 @@ import OAuthFlowsElement from './elements/OAuthFlows.ts'; import AuthorizationCodeOAuthFlowElement from './elements/AuthorizationCodeOAuthFlow.ts'; import ClientCredentialsOAuthFlowElement from './elements/ClientCredentialsOAuthFlow.ts'; import DeviceCodeOAuthFlowElement from './elements/DeviceCodeOAuthFlow.ts'; -import ImplicitOAuthFlowElement from './elements/ImplicitOAuthFlow.ts'; -import PasswordOAuthFlowElement from './elements/PasswordOAuthFlow.ts'; import StringListElement from './elements/StringList.ts'; import SkillsElement from './elements/nces/Skills.ts'; import SignaturesElement from './elements/nces/Signatures.ts'; @@ -281,34 +279,6 @@ export const isDeviceCodeOAuthFlowElement = createPredicate( }, ); -/** - * @public - */ -export const isImplicitOAuthFlowElement = createPredicate( - ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { - return (element: unknown): element is ImplicitOAuthFlowElement => - element instanceof ImplicitOAuthFlowElement || - (hasBasicElementProps(element) && - isElementType('implicitOAuthFlow', element) && - primitiveEq('object', element) && - hasClass('implicit-oauth-flow', element)); - }, -); - -/** - * @public - */ -export const isPasswordOAuthFlowElement = createPredicate( - ({ hasBasicElementProps, isElementType, primitiveEq, hasClass }) => { - return (element: unknown): element is PasswordOAuthFlowElement => - element instanceof PasswordOAuthFlowElement || - (hasBasicElementProps(element) && - isElementType('passwordOAuthFlow', element) && - primitiveEq('object', element) && - hasClass('password-oauth-flow', element)); - }, -); - /** * @public */ diff --git a/packages/apidom-ns-a2a-1/src/refractor/canonicalize.ts b/packages/apidom-ns-a2a-1/src/refractor/canonicalize.ts index 87722534bb..982509237f 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/canonicalize.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/canonicalize.ts @@ -60,7 +60,7 @@ const SNAKE_TO_CAMEL: Record = { oauth2_metadata_url: 'oauth2MetadataUrl', // OpenIdConnectSecurityScheme open_id_connect_url: 'openIdConnectUrl', - // OAuth flow types (Authorization, ClientCredentials, DeviceCode, Implicit, Password) + // OAuth flow types (Authorization, ClientCredentials, DeviceCode) authorization_url: 'authorizationUrl', token_url: 'tokenUrl', refresh_url: 'refreshUrl', diff --git a/packages/apidom-ns-a2a-1/src/refractor/plugins/replace-empty-element.ts b/packages/apidom-ns-a2a-1/src/refractor/plugins/replace-empty-element.ts index fbf9e0d5eb..ddd79507f3 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/plugins/replace-empty-element.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/plugins/replace-empty-element.ts @@ -32,8 +32,6 @@ import OAuthFlowsElement from '../../elements/OAuthFlows.ts'; import AuthorizationCodeOAuthFlowElement from '../../elements/AuthorizationCodeOAuthFlow.ts'; import ClientCredentialsOAuthFlowElement from '../../elements/ClientCredentialsOAuthFlow.ts'; import DeviceCodeOAuthFlowElement from '../../elements/DeviceCodeOAuthFlow.ts'; -import ImplicitOAuthFlowElement from '../../elements/ImplicitOAuthFlow.ts'; -import PasswordOAuthFlowElement from '../../elements/PasswordOAuthFlow.ts'; // non-concrete Elements (NCEs) import SkillsElement from '../../elements/nces/Skills.ts'; import SignaturesElement from '../../elements/nces/Signatures.ts'; @@ -170,12 +168,6 @@ const schema = { deviceCode(...args: any[]) { return new DeviceCodeOAuthFlowElement(...args); }, - implicit(...args: any[]) { - return new ImplicitOAuthFlowElement(...args); - }, - password(...args: any[]) { - return new PasswordOAuthFlowElement(...args); - }, }, AuthorizationCodeOAuthFlowElement: { scopes(...args: any[]) { @@ -192,16 +184,6 @@ const schema = { return new ObjectElement(...args); }, }, - ImplicitOAuthFlowElement: { - scopes(...args: any[]) { - return new ObjectElement(...args); - }, - }, - PasswordOAuthFlowElement: { - scopes(...args: any[]) { - return new ObjectElement(...args); - }, - }, SecurityRequirementElement: { schemes(...args: any[]) { return new ObjectElement(...args); diff --git a/packages/apidom-ns-a2a-1/src/refractor/registration.ts b/packages/apidom-ns-a2a-1/src/refractor/registration.ts index cb9ccce3d0..9a8acea549 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/registration.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/registration.ts @@ -16,8 +16,6 @@ import OAuthFlowsElement from '../elements/OAuthFlows.ts'; import AuthorizationCodeOAuthFlowElement from '../elements/AuthorizationCodeOAuthFlow.ts'; import ClientCredentialsOAuthFlowElement from '../elements/ClientCredentialsOAuthFlow.ts'; import DeviceCodeOAuthFlowElement from '../elements/DeviceCodeOAuthFlow.ts'; -import ImplicitOAuthFlowElement from '../elements/ImplicitOAuthFlow.ts'; -import PasswordOAuthFlowElement from '../elements/PasswordOAuthFlow.ts'; import StringListElement from '../elements/StringList.ts'; import { createRefractor } from './index.ts'; @@ -147,20 +145,6 @@ DeviceCodeOAuthFlowElement.refract = createRefractor([ 'DeviceCodeOAuthFlow', '$visitor', ]); -ImplicitOAuthFlowElement.refract = createRefractor([ - 'visitors', - 'document', - 'objects', - 'ImplicitOAuthFlow', - '$visitor', -]); -PasswordOAuthFlowElement.refract = createRefractor([ - 'visitors', - 'document', - 'objects', - 'PasswordOAuthFlow', - '$visitor', -]); StringListElement.refract = createRefractor([ 'visitors', 'document', @@ -188,7 +172,5 @@ export { AuthorizationCodeOAuthFlowElement, ClientCredentialsOAuthFlowElement, DeviceCodeOAuthFlowElement, - ImplicitOAuthFlowElement, - PasswordOAuthFlowElement, StringListElement, }; diff --git a/packages/apidom-ns-a2a-1/src/refractor/specification.ts b/packages/apidom-ns-a2a-1/src/refractor/specification.ts index a616c64216..647c6c3194 100644 --- a/packages/apidom-ns-a2a-1/src/refractor/specification.ts +++ b/packages/apidom-ns-a2a-1/src/refractor/specification.ts @@ -16,8 +16,6 @@ import OAuthFlowsVisitor from './visitors/a2a-1/oauth-flows/index.ts'; import AuthorizationCodeOAuthFlowVisitor from './visitors/a2a-1/authorization-code-oauth-flow/index.ts'; import ClientCredentialsOAuthFlowVisitor from './visitors/a2a-1/client-credentials-oauth-flow/index.ts'; import DeviceCodeOAuthFlowVisitor from './visitors/a2a-1/device-code-oauth-flow/index.ts'; -import ImplicitOAuthFlowVisitor from './visitors/a2a-1/implicit-oauth-flow/index.ts'; -import PasswordOAuthFlowVisitor from './visitors/a2a-1/password-oauth-flow/index.ts'; import StringListVisitor from './visitors/a2a-1/string-list/index.ts'; import SkillsVisitor from './visitors/a2a-1/SkillsVisitor.ts'; import SignaturesVisitor from './visitors/a2a-1/SignaturesVisitor.ts'; @@ -189,8 +187,6 @@ const specification = { $ref: '#/visitors/document/objects/ClientCredentialsOAuthFlow', }, deviceCode: { $ref: '#/visitors/document/objects/DeviceCodeOAuthFlow' }, - implicit: { $ref: '#/visitors/document/objects/ImplicitOAuthFlow' }, - password: { $ref: '#/visitors/document/objects/PasswordOAuthFlow' }, }, }, AuthorizationCodeOAuthFlow: { @@ -220,22 +216,6 @@ const specification = { scopes: { $ref: '#/visitors/value' }, }, }, - ImplicitOAuthFlow: { - $visitor: ImplicitOAuthFlowVisitor, - fixedFields: { - authorizationUrl: { $ref: '#/visitors/value' }, - refreshUrl: { $ref: '#/visitors/value' }, - scopes: { $ref: '#/visitors/value' }, - }, - }, - PasswordOAuthFlow: { - $visitor: PasswordOAuthFlowVisitor, - fixedFields: { - tokenUrl: { $ref: '#/visitors/value' }, - refreshUrl: { $ref: '#/visitors/value' }, - scopes: { $ref: '#/visitors/value' }, - }, - }, StringList: { $visitor: StringListVisitor, fixedFields: { diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/implicit-oauth-flow/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/implicit-oauth-flow/index.ts deleted file mode 100644 index b3ff9847f1..0000000000 --- a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/implicit-oauth-flow/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Mixin } from 'ts-mixer'; -import { always } from 'ramda'; - -import ImplicitOAuthFlowElement from '../../../../elements/ImplicitOAuthFlow.ts'; -import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; -import FixedFieldsVisitor, { - FixedFieldsVisitorOptions, - SpecPath, -} from '../../generics/FixedFieldsVisitor.ts'; - -/** - * @public - */ -export interface ImplicitOAuthFlowVisitorOptions - extends FixedFieldsVisitorOptions, - FallbackVisitorOptions {} - -/** - * @public - */ -class ImplicitOAuthFlowVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { - declare public readonly element: ImplicitOAuthFlowElement; - - declare protected readonly specPath: SpecPath<['document', 'objects', 'ImplicitOAuthFlow']>; - - constructor(options: ImplicitOAuthFlowVisitorOptions) { - super(options); - this.element = new ImplicitOAuthFlowElement(); - this.specPath = always(['document', 'objects', 'ImplicitOAuthFlow']); - } -} - -export default ImplicitOAuthFlowVisitor; diff --git a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/password-oauth-flow/index.ts b/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/password-oauth-flow/index.ts deleted file mode 100644 index 84a7698347..0000000000 --- a/packages/apidom-ns-a2a-1/src/refractor/visitors/a2a-1/password-oauth-flow/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Mixin } from 'ts-mixer'; -import { always } from 'ramda'; - -import PasswordOAuthFlowElement from '../../../../elements/PasswordOAuthFlow.ts'; -import FallbackVisitor, { FallbackVisitorOptions } from '../../FallbackVisitor.ts'; -import FixedFieldsVisitor, { - FixedFieldsVisitorOptions, - SpecPath, -} from '../../generics/FixedFieldsVisitor.ts'; - -/** - * @public - */ -export interface PasswordOAuthFlowVisitorOptions - extends FixedFieldsVisitorOptions, - FallbackVisitorOptions {} - -/** - * @public - */ -class PasswordOAuthFlowVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { - declare public readonly element: PasswordOAuthFlowElement; - - declare protected readonly specPath: SpecPath<['document', 'objects', 'PasswordOAuthFlow']>; - - constructor(options: PasswordOAuthFlowVisitorOptions) { - super(options); - this.element = new PasswordOAuthFlowElement(); - this.specPath = always(['document', 'objects', 'PasswordOAuthFlow']); - } -} - -export default PasswordOAuthFlowVisitor; diff --git a/packages/apidom-ns-a2a-1/src/traversal/visitor.ts b/packages/apidom-ns-a2a-1/src/traversal/visitor.ts index f044152382..3151046a37 100644 --- a/packages/apidom-ns-a2a-1/src/traversal/visitor.ts +++ b/packages/apidom-ns-a2a-1/src/traversal/visitor.ts @@ -37,8 +37,6 @@ export const keyMap = { AuthorizationCodeOAuthFlowElement: ['content'], ClientCredentialsOAuthFlowElement: ['content'], DeviceCodeOAuthFlowElement: ['content'], - ImplicitOAuthFlowElement: ['content'], - PasswordOAuthFlowElement: ['content'], StringListElement: ['content'], ...keyMapBase, }; diff --git a/packages/apidom-ns-a2a-1/test/predicates.ts b/packages/apidom-ns-a2a-1/test/predicates.ts index 3d883b6d3d..ac70e2a59c 100644 --- a/packages/apidom-ns-a2a-1/test/predicates.ts +++ b/packages/apidom-ns-a2a-1/test/predicates.ts @@ -19,8 +19,6 @@ import { isAuthorizationCodeOAuthFlowElement, isClientCredentialsOAuthFlowElement, isDeviceCodeOAuthFlowElement, - isImplicitOAuthFlowElement, - isPasswordOAuthFlowElement, isStringListElement, AgentCardElement, AgentCapabilitiesElement, @@ -40,8 +38,6 @@ import { AuthorizationCodeOAuthFlowElement, ClientCredentialsOAuthFlowElement, DeviceCodeOAuthFlowElement, - ImplicitOAuthFlowElement, - PasswordOAuthFlowElement, StringListElement, } from '../src/index.ts'; @@ -126,16 +122,6 @@ const cases: PredicateCase[] = [ predicate: isDeviceCodeOAuthFlowElement, Cls: DeviceCodeOAuthFlowElement, }, - { - name: 'isImplicitOAuthFlowElement', - predicate: isImplicitOAuthFlowElement, - Cls: ImplicitOAuthFlowElement, - }, - { - name: 'isPasswordOAuthFlowElement', - predicate: isPasswordOAuthFlowElement, - Cls: PasswordOAuthFlowElement, - }, { name: 'isStringListElement', predicate: isStringListElement, Cls: StringListElement }, ]; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/__snapshots__/index.ts.snap deleted file mode 100644 index 5f69646e26..0000000000 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/__snapshots__/index.ts.snap +++ /dev/null @@ -1,14 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`refractor elements ImplicitOAuthFlowElement should refract to semantic ApiDOM tree 1`] = ` -(ImplicitOAuthFlowElement - (MemberElement - (StringElement) - (StringElement)) - (MemberElement - (StringElement) - (StringElement)) - (MemberElement - (StringElement) - (ObjectElement))) -`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/index.ts deleted file mode 100644 index fd3fe35935..0000000000 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/ImplicitOAuthFlow/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { expect } from 'chai'; -import { sexprs } from '@swagger-api/apidom-core'; - -import { ImplicitOAuthFlowElement } from '../../../../src/index.ts'; - -describe('refractor', function () { - context('elements', function () { - context('ImplicitOAuthFlowElement', function () { - specify('should refract to semantic ApiDOM tree', function () { - const element = ImplicitOAuthFlowElement.refract({ - authorizationUrl: 'https://idp.example/authorize', - refreshUrl: 'https://idp.example/refresh', - scopes: {}, - }); - - expect(sexprs(element)).toMatchSnapshot(); - }); - }); - }); -}); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/__snapshots__/index.ts.snap index 0f3d8fc340..463897facc 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/__snapshots__/index.ts.snap +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/__snapshots__/index.ts.snap @@ -23,18 +23,6 @@ exports[`refractor elements OAuthFlowsElement should refract to semantic ApiDOM (MemberElement (StringElement) (StringElement)) - (MemberElement - (StringElement) - (StringElement)))) - (MemberElement - (StringElement) - (ImplicitOAuthFlowElement - (MemberElement - (StringElement) - (StringElement)))) - (MemberElement - (StringElement) - (PasswordOAuthFlowElement (MemberElement (StringElement) (StringElement))))) diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/index.ts index 58fb087ba6..e480a6ad09 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/index.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/elements/OAuthFlows/index.ts @@ -19,12 +19,6 @@ describe('refractor', function () { deviceAuthorizationUrl: 'https://x.example/d', tokenUrl: 'https://x.example/t', }, - implicit: { - authorizationUrl: 'https://x.example/a', - }, - password: { - tokenUrl: 'https://x.example/t', - }, }); expect(sexprs(element)).toMatchSnapshot(); diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/__snapshots__/index.ts.snap deleted file mode 100644 index b0370502c0..0000000000 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/__snapshots__/index.ts.snap +++ /dev/null @@ -1,14 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`refractor elements PasswordOAuthFlowElement should refract to semantic ApiDOM tree 1`] = ` -(PasswordOAuthFlowElement - (MemberElement - (StringElement) - (StringElement)) - (MemberElement - (StringElement) - (StringElement)) - (MemberElement - (StringElement) - (ObjectElement))) -`; diff --git a/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/index.ts b/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/index.ts deleted file mode 100644 index 55b6c26280..0000000000 --- a/packages/apidom-ns-a2a-1/test/refractor/elements/PasswordOAuthFlow/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { expect } from 'chai'; -import { sexprs } from '@swagger-api/apidom-core'; - -import { PasswordOAuthFlowElement } from '../../../../src/index.ts'; - -describe('refractor', function () { - context('elements', function () { - context('PasswordOAuthFlowElement', function () { - specify('should refract to semantic ApiDOM tree', function () { - const element = PasswordOAuthFlowElement.refract({ - tokenUrl: 'https://idp.example/token', - refreshUrl: 'https://idp.example/refresh', - scopes: {}, - }); - - expect(sexprs(element)).toMatchSnapshot(); - }); - }); - }); -}); From 78b5ae01c8f4e17524d5f0021825f8505357d76c Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Thu, 11 Jun 2026 14:49:14 +0100 Subject: [PATCH 10/14] feat(ls): add A2A type and enum lints for security schemes and agent extension (PROVCON-5343) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../agent-extension/lint/description--type.ts | 19 ++++++++++++++++++ .../config/a2a/agent-extension/lint/index.ts | 6 +++++- .../a2a/agent-extension/lint/params--type.ts | 19 ++++++++++++++++++ .../agent-extension/lint/required--type.ts | 19 ++++++++++++++++++ .../a2a/agent-extension/lint/uri--type.ts | 19 ++++++++++++++++++ .../lint/description--type.ts | 19 ++++++++++++++++++ .../a2a/api-key-security-scheme/lint/index.ts | 12 ++++++++++- .../lint/location--value.ts | 20 +++++++++++++++++++ .../lint/name--type.ts | 19 ++++++++++++++++++ .../lint/bearer-format--type.ts | 19 ++++++++++++++++++ .../lint/description--type.ts | 19 ++++++++++++++++++ .../http-auth-security-scheme/lint/index.ts | 5 ++++- .../lint/scheme--type.ts | 19 ++++++++++++++++++ .../lint/description--type.ts | 19 ++++++++++++++++++ .../mutual-tls-security-scheme/lint/index.ts | 3 ++- .../lint/description--type.ts | 19 ++++++++++++++++++ .../lint/flows--type.ts | 19 ++++++++++++++++++ .../a2a/oauth2-security-scheme/lint/index.ts | 5 ++++- .../lint/oauth2-metadata-url--type.ts | 19 ++++++++++++++++++ .../lint/description--type.ts | 19 ++++++++++++++++++ .../lint/index.ts | 4 +++- .../lint/open-id-connect-url--type.ts | 19 ++++++++++++++++++ packages/apidom-ls/src/config/codes.ts | 20 +++++++++++++++++++ 23 files changed, 354 insertions(+), 6 deletions(-) create mode 100644 packages/apidom-ls/src/config/a2a/agent-extension/lint/description--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-extension/lint/params--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-extension/lint/required--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/agent-extension/lint/uri--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/description--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/location--value.ts create mode 100644 packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/name--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/bearer-format--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/description--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/scheme--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/description--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/description--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/flows--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/oauth2-metadata-url--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/description--type.ts create mode 100644 packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/open-id-connect-url--type.ts diff --git a/packages/apidom-ls/src/config/a2a/agent-extension/lint/description--type.ts b/packages/apidom-ls/src/config/a2a/agent-extension/lint/description--type.ts new file mode 100644 index 0000000000..8501301763 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-extension/lint/description--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const descriptionTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_EXTENSION_FIELD_DESCRIPTION_TYPE, + source: 'apilint', + message: "'description' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'description', + targetSpecs: A2A1, +}; + +export default descriptionTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-extension/lint/index.ts b/packages/apidom-ls/src/config/a2a/agent-extension/lint/index.ts index 47f7d48761..5dd98e55de 100644 --- a/packages/apidom-ls/src/config/a2a/agent-extension/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/agent-extension/lint/index.ts @@ -1,5 +1,9 @@ import allowedFieldsLint from './allowed-fields.ts'; +import uriTypeLint from './uri--type.ts'; +import descriptionTypeLint from './description--type.ts'; +import requiredTypeLint from './required--type.ts'; +import paramsTypeLint from './params--type.ts'; -const lints = [allowedFieldsLint]; +const lints = [allowedFieldsLint, uriTypeLint, descriptionTypeLint, requiredTypeLint, paramsTypeLint]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/agent-extension/lint/params--type.ts b/packages/apidom-ls/src/config/a2a/agent-extension/lint/params--type.ts new file mode 100644 index 0000000000..d34f921b35 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-extension/lint/params--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const paramsTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_EXTENSION_FIELD_PARAMS_TYPE, + source: 'apilint', + message: "'params' must be an object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['object'], + marker: 'value', + target: 'params', + targetSpecs: A2A1, +}; + +export default paramsTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-extension/lint/required--type.ts b/packages/apidom-ls/src/config/a2a/agent-extension/lint/required--type.ts new file mode 100644 index 0000000000..6d1d3d0315 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-extension/lint/required--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const requiredTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_EXTENSION_FIELD_REQUIRED_TYPE, + source: 'apilint', + message: "'required' must be a boolean", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['boolean'], + marker: 'value', + target: 'required', + targetSpecs: A2A1, +}; + +export default requiredTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/agent-extension/lint/uri--type.ts b/packages/apidom-ls/src/config/a2a/agent-extension/lint/uri--type.ts new file mode 100644 index 0000000000..81843cfcbf --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/agent-extension/lint/uri--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const uriTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_AGENT_EXTENSION_FIELD_URI_TYPE, + source: 'apilint', + message: "'uri' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'uri', + targetSpecs: A2A1, +}; + +export default uriTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/description--type.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/description--type.ts new file mode 100644 index 0000000000..65b2fd7f9e --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/description--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const descriptionTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_API_KEY_SECURITY_SCHEME_FIELD_DESCRIPTION_TYPE, + source: 'apilint', + message: "'description' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'description', + targetSpecs: A2A1, +}; + +export default descriptionTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/index.ts index 80998fb078..8c059390bd 100644 --- a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/index.ts @@ -1,7 +1,17 @@ import allowedFieldsLint from './allowed-fields.ts'; import locationRequiredLint from './location--required.ts'; import nameRequiredLint from './name--required.ts'; +import nameTypeLint from './name--type.ts'; +import descriptionTypeLint from './description--type.ts'; +import locationValueLint from './location--value.ts'; -const lints = [allowedFieldsLint, locationRequiredLint, nameRequiredLint]; +const lints = [ + allowedFieldsLint, + locationRequiredLint, + nameRequiredLint, + nameTypeLint, + descriptionTypeLint, + locationValueLint, +]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/location--value.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/location--value.ts new file mode 100644 index 0000000000..525eb43fa2 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/location--value.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const locationValueLint: LinterMeta = { + code: ApilintCodes.A2A1_API_KEY_SECURITY_SCHEME_FIELD_LOCATION_VALUE, + source: 'apilint', + message: "'location' must be one of allowed values: query, header, cookie", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintValueOrArray', + linterParams: [['query', 'header', 'cookie']], + marker: 'value', + target: 'location', + data: {}, + targetSpecs: A2A1, +}; + +export default locationValueLint; diff --git a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/name--type.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/name--type.ts new file mode 100644 index 0000000000..2947a7f276 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/lint/name--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const nameTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_API_KEY_SECURITY_SCHEME_FIELD_NAME_TYPE, + source: 'apilint', + message: "'name' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'name', + targetSpecs: A2A1, +}; + +export default nameTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/bearer-format--type.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/bearer-format--type.ts new file mode 100644 index 0000000000..ef4edf9a8d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/bearer-format--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const bearerFormatTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_HTTP_AUTH_SECURITY_SCHEME_FIELD_BEARER_FORMAT_TYPE, + source: 'apilint', + message: "'bearerFormat' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'bearerFormat', + targetSpecs: A2A1, +}; + +export default bearerFormatTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/description--type.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/description--type.ts new file mode 100644 index 0000000000..bf3ab3b133 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/description--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const descriptionTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_HTTP_AUTH_SECURITY_SCHEME_FIELD_DESCRIPTION_TYPE, + source: 'apilint', + message: "'description' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'description', + targetSpecs: A2A1, +}; + +export default descriptionTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts index b78c6c9b54..cb656ca181 100644 --- a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts @@ -1,6 +1,9 @@ import allowedFieldsLint from './allowed-fields.ts'; import schemeRequiredLint from './scheme--required.ts'; +import schemeTypeLint from './scheme--type.ts'; +import descriptionTypeLint from './description--type.ts'; +import bearerFormatTypeLint from './bearer-format--type.ts'; -const lints = [allowedFieldsLint, schemeRequiredLint]; +const lints = [allowedFieldsLint, schemeRequiredLint, schemeTypeLint, descriptionTypeLint, bearerFormatTypeLint]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/scheme--type.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/scheme--type.ts new file mode 100644 index 0000000000..a3a0c1f48d --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/scheme--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const schemeTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_HTTP_AUTH_SECURITY_SCHEME_FIELD_SCHEME_TYPE, + source: 'apilint', + message: "'scheme' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'scheme', + targetSpecs: A2A1, +}; + +export default schemeTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/description--type.ts b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/description--type.ts new file mode 100644 index 0000000000..0035fed318 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/description--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const descriptionTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_MUTUAL_TLS_SECURITY_SCHEME_FIELD_DESCRIPTION_TYPE, + source: 'apilint', + message: "'description' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'description', + targetSpecs: A2A1, +}; + +export default descriptionTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/index.ts index 47f7d48761..58f6a012e8 100644 --- a/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/lint/index.ts @@ -1,5 +1,6 @@ import allowedFieldsLint from './allowed-fields.ts'; +import descriptionTypeLint from './description--type.ts'; -const lints = [allowedFieldsLint]; +const lints = [allowedFieldsLint, descriptionTypeLint]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/description--type.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/description--type.ts new file mode 100644 index 0000000000..7bf4c2131a --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/description--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const descriptionTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_OAUTH2_SECURITY_SCHEME_FIELD_DESCRIPTION_TYPE, + source: 'apilint', + message: "'description' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'description', + targetSpecs: A2A1, +}; + +export default descriptionTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/flows--type.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/flows--type.ts new file mode 100644 index 0000000000..cf5650ea8a --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/flows--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const flowsTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_OAUTH2_SECURITY_SCHEME_FIELD_FLOWS_TYPE, + source: 'apilint', + message: "'flows' must be an OAuth Flows Object", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['oauthFlows'], + marker: 'value', + target: 'flows', + targetSpecs: A2A1, +}; + +export default flowsTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts index fd26b6a59a..6c42aa7286 100644 --- a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts @@ -1,6 +1,9 @@ import allowedFieldsLint from './allowed-fields.ts'; import flowsRequiredLint from './flows--required.ts'; +import flowsTypeLint from './flows--type.ts'; +import descriptionTypeLint from './description--type.ts'; +import oauth2MetadataUrlTypeLint from './oauth2-metadata-url--type.ts'; -const lints = [allowedFieldsLint, flowsRequiredLint]; +const lints = [allowedFieldsLint, flowsRequiredLint, flowsTypeLint, descriptionTypeLint, oauth2MetadataUrlTypeLint]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/oauth2-metadata-url--type.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/oauth2-metadata-url--type.ts new file mode 100644 index 0000000000..7c4d14acf0 --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/oauth2-metadata-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const oauth2MetadataUrlTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_OAUTH2_SECURITY_SCHEME_FIELD_OAUTH2_METADATA_URL_TYPE, + source: 'apilint', + message: "'oauth2MetadataUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'oauth2MetadataUrl', + targetSpecs: A2A1, +}; + +export default oauth2MetadataUrlTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/description--type.ts b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/description--type.ts new file mode 100644 index 0000000000..1adf63c93c --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/description--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const descriptionTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_OPEN_ID_CONNECT_SECURITY_SCHEME_FIELD_DESCRIPTION_TYPE, + source: 'apilint', + message: "'description' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'description', + targetSpecs: A2A1, +}; + +export default descriptionTypeLint; diff --git a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts index ebd0eb7a6c..991a79ce16 100644 --- a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts @@ -1,6 +1,8 @@ import allowedFieldsLint from './allowed-fields.ts'; import openIdConnectUrlRequiredLint from './open-id-connect-url--required.ts'; +import openIdConnectUrlTypeLint from './open-id-connect-url--type.ts'; +import descriptionTypeLint from './description--type.ts'; -const lints = [allowedFieldsLint, openIdConnectUrlRequiredLint]; +const lints = [allowedFieldsLint, openIdConnectUrlRequiredLint, openIdConnectUrlTypeLint, descriptionTypeLint]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/open-id-connect-url--type.ts b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/open-id-connect-url--type.ts new file mode 100644 index 0000000000..34331880eb --- /dev/null +++ b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/open-id-connect-url--type.ts @@ -0,0 +1,19 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes.ts'; +import { LinterMeta } from '../../../../apidom-language-types.ts'; +import { A2A1 } from '../../target-specs.ts'; + +const openIdConnectUrlTypeLint: LinterMeta = { + code: ApilintCodes.A2A1_OPEN_ID_CONNECT_SECURITY_SCHEME_FIELD_OPEN_ID_CONNECT_URL_TYPE, + source: 'apilint', + message: "'openIdConnectUrl' must be a string", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintType', + linterParams: ['string'], + marker: 'value', + target: 'openIdConnectUrl', + targetSpecs: A2A1, +}; + +export default openIdConnectUrlTypeLint; diff --git a/packages/apidom-ls/src/config/codes.ts b/packages/apidom-ls/src/config/codes.ts index 3df8173e9d..31d7cda3f5 100644 --- a/packages/apidom-ls/src/config/codes.ts +++ b/packages/apidom-ls/src/config/codes.ts @@ -1542,18 +1542,38 @@ enum ApilintCodes { A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_TOKEN_URL_REQUIRED = 9110600, A2A1_DEVICE_CODE_OAUTH_FLOW_FIELD_SCOPES_REQUIRED = 9110700, + A2A1_AGENT_EXTENSION = 9120000, + A2A1_AGENT_EXTENSION_FIELD_URI_TYPE = 9120100, + A2A1_AGENT_EXTENSION_FIELD_DESCRIPTION_TYPE = 9120200, + A2A1_AGENT_EXTENSION_FIELD_REQUIRED_TYPE = 9120300, + A2A1_AGENT_EXTENSION_FIELD_PARAMS_TYPE = 9120400, + + A2A1_MUTUAL_TLS_SECURITY_SCHEME = 9130000, + A2A1_MUTUAL_TLS_SECURITY_SCHEME_FIELD_DESCRIPTION_TYPE = 9130100, + A2A1_API_KEY_SECURITY_SCHEME = 9140000, A2A1_API_KEY_SECURITY_SCHEME_FIELD_LOCATION_REQUIRED = 9140100, A2A1_API_KEY_SECURITY_SCHEME_FIELD_NAME_REQUIRED = 9140200, + A2A1_API_KEY_SECURITY_SCHEME_FIELD_NAME_TYPE = 9140300, + A2A1_API_KEY_SECURITY_SCHEME_FIELD_DESCRIPTION_TYPE = 9140400, + A2A1_API_KEY_SECURITY_SCHEME_FIELD_LOCATION_VALUE = 9140500, A2A1_HTTP_AUTH_SECURITY_SCHEME = 9150000, A2A1_HTTP_AUTH_SECURITY_SCHEME_FIELD_SCHEME_REQUIRED = 9150100, + A2A1_HTTP_AUTH_SECURITY_SCHEME_FIELD_SCHEME_TYPE = 9150200, + A2A1_HTTP_AUTH_SECURITY_SCHEME_FIELD_DESCRIPTION_TYPE = 9150300, + A2A1_HTTP_AUTH_SECURITY_SCHEME_FIELD_BEARER_FORMAT_TYPE = 9150400, A2A1_OAUTH2_SECURITY_SCHEME = 9160000, A2A1_OAUTH2_SECURITY_SCHEME_FIELD_FLOWS_REQUIRED = 9160100, + A2A1_OAUTH2_SECURITY_SCHEME_FIELD_FLOWS_TYPE = 9160200, + A2A1_OAUTH2_SECURITY_SCHEME_FIELD_DESCRIPTION_TYPE = 9160300, + A2A1_OAUTH2_SECURITY_SCHEME_FIELD_OAUTH2_METADATA_URL_TYPE = 9160400, A2A1_OPEN_ID_CONNECT_SECURITY_SCHEME = 9170000, A2A1_OPEN_ID_CONNECT_SECURITY_SCHEME_FIELD_OPEN_ID_CONNECT_URL_REQUIRED = 9170100, + A2A1_OPEN_ID_CONNECT_SECURITY_SCHEME_FIELD_OPEN_ID_CONNECT_URL_TYPE = 9170200, + A2A1_OPEN_ID_CONNECT_SECURITY_SCHEME_FIELD_DESCRIPTION_TYPE = 9170300, } export default ApilintCodes; From 3054eebc8a9f661a4cf04ed0dbff33acd87a851a Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Thu, 11 Jun 2026 14:49:21 +0100 Subject: [PATCH 11/14] feat(ls): add A2A completions for security schemes and agent extension (PROVCON-5343) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../config/a2a/agent-extension/completion.ts | 63 ++++++++++++++++++- .../a2a/api-key-security-scheme/completion.ts | 50 ++++++++++++++- .../http-auth-security-scheme/completion.ts | 51 ++++++++++++++- .../mutual-tls-security-scheme/completion.ts | 23 ++++++- .../a2a/oauth2-security-scheme/completion.ts | 51 ++++++++++++++- .../completion.ts | 37 ++++++++++- 6 files changed, 263 insertions(+), 12 deletions(-) diff --git a/packages/apidom-ls/src/config/a2a/agent-extension/completion.ts b/packages/apidom-ls/src/config/a2a/agent-extension/completion.ts index f97c84e35d..a17fab1e9c 100644 --- a/packages/apidom-ls/src/config/a2a/agent-extension/completion.ts +++ b/packages/apidom-ls/src/config/a2a/agent-extension/completion.ts @@ -1,5 +1,64 @@ -import { ApidomCompletionItem } from '../../../apidom-language-types.ts'; +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; -const completion: ApidomCompletionItem[] = []; +const completion: ApidomCompletionItem[] = [ + { + label: 'uri', + insertText: 'uri', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'The unique URI identifying the extension (string).', + }, + targetSpecs: A2A1, + }, + { + label: 'description', + insertText: 'description', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'A human-readable description of how this agent uses the extension (string).', + }, + targetSpecs: A2A1, + }, + { + label: 'required', + insertText: 'required', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + "If true, the client must understand and comply with the extension's requirements (boolean).", + }, + targetSpecs: A2A1, + }, + { + label: 'params', + insertText: 'params', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'Optional extension-specific configuration parameters (object).', + }, + targetSpecs: A2A1, + }, +]; export default completion; diff --git a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/completion.ts b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/completion.ts index f97c84e35d..0dcfe85894 100644 --- a/packages/apidom-ls/src/config/a2a/api-key-security-scheme/completion.ts +++ b/packages/apidom-ls/src/config/a2a/api-key-security-scheme/completion.ts @@ -1,5 +1,51 @@ -import { ApidomCompletionItem } from '../../../apidom-language-types.ts'; +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; -const completion: ApidomCompletionItem[] = []; +const completion: ApidomCompletionItem[] = [ + { + label: 'description', + insertText: 'description', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'An optional description for the security scheme (string).', + }, + targetSpecs: A2A1, + }, + { + label: 'location', + insertText: 'location', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'The location of the API key. Valid values are "query", "header", or "cookie" (string, required).', + }, + targetSpecs: A2A1, + }, + { + label: 'name', + insertText: 'name', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'The name of the header, query, or cookie parameter to be used (string, required).', + }, + targetSpecs: A2A1, + }, +]; export default completion; diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/completion.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/completion.ts index f97c84e35d..14c7f56e94 100644 --- a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/completion.ts +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/completion.ts @@ -1,5 +1,52 @@ -import { ApidomCompletionItem } from '../../../apidom-language-types.ts'; +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; -const completion: ApidomCompletionItem[] = []; +const completion: ApidomCompletionItem[] = [ + { + label: 'description', + insertText: 'description', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'An optional description for the security scheme (string).', + }, + targetSpecs: A2A1, + }, + { + label: 'scheme', + insertText: 'scheme', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'The name of the HTTP Authentication scheme to be used in the Authorization header, as defined in RFC 7235 (e.g. "Bearer") (string, required).', + }, + targetSpecs: A2A1, + }, + { + label: 'bearerFormat', + insertText: 'bearerFormat', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'A hint to the client to identify how the bearer token is formatted (e.g. "JWT") (string).', + }, + targetSpecs: A2A1, + }, +]; export default completion; diff --git a/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/completion.ts b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/completion.ts index f97c84e35d..278f4a903d 100644 --- a/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/completion.ts +++ b/packages/apidom-ls/src/config/a2a/mutual-tls-security-scheme/completion.ts @@ -1,5 +1,24 @@ -import { ApidomCompletionItem } from '../../../apidom-language-types.ts'; +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; -const completion: ApidomCompletionItem[] = []; +const completion: ApidomCompletionItem[] = [ + { + label: 'description', + insertText: 'description', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'An optional description for the security scheme (string).', + }, + targetSpecs: A2A1, + }, +]; export default completion; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/completion.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/completion.ts index f97c84e35d..71ed6f6c49 100644 --- a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/completion.ts +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/completion.ts @@ -1,5 +1,52 @@ -import { ApidomCompletionItem } from '../../../apidom-language-types.ts'; +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; -const completion: ApidomCompletionItem[] = []; +const completion: ApidomCompletionItem[] = [ + { + label: 'description', + insertText: 'description', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'An optional description for the security scheme (string).', + }, + targetSpecs: A2A1, + }, + { + label: 'flows', + insertText: 'flows', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'An object containing configuration information for the supported OAuth 2.0 flows (required).', + }, + targetSpecs: A2A1, + }, + { + label: 'oauth2MetadataUrl', + insertText: 'oauth2MetadataUrl', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'URL to the OAuth2 authorization server metadata (RFC 8414). TLS is required (string).', + }, + targetSpecs: A2A1, + }, +]; export default completion; diff --git a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/completion.ts b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/completion.ts index f97c84e35d..60785e0f97 100644 --- a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/completion.ts +++ b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/completion.ts @@ -1,5 +1,38 @@ -import { ApidomCompletionItem } from '../../../apidom-language-types.ts'; +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types.ts'; +import { A2A1 } from '../target-specs.ts'; -const completion: ApidomCompletionItem[] = []; +const completion: ApidomCompletionItem[] = [ + { + label: 'description', + insertText: 'description', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: 'An optional description for the security scheme (string).', + }, + targetSpecs: A2A1, + }, + { + label: 'openIdConnectUrl', + insertText: 'openIdConnectUrl', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + "The OpenID Connect Discovery URL for the OIDC provider's metadata (string, required).", + }, + targetSpecs: A2A1, + }, +]; export default completion; From 3a3c0a963f12431016a03e3ed9073a6abd8d44ab Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Thu, 11 Jun 2026 14:49:26 +0100 Subject: [PATCH 12/14] test(a2a): assert refracted skill and security scheme value types (PROVCON-5343) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../__snapshots__/index.ts.snap | 24 ++++++++++++++ .../plugins/replace-empty-element/index.ts | 31 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/__snapshots__/index.ts.snap b/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/__snapshots__/index.ts.snap index 5c952c5c0a..5934197a70 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/__snapshots__/index.ts.snap +++ b/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/__snapshots__/index.ts.snap @@ -13,6 +13,30 @@ exports[`refractor plugins replace-empty-element should leave non-empty values u (BooleanElement))))) `; +exports[`refractor plugins replace-empty-element should refract securitySchemes object values into SecurityScheme elements 1`] = ` +(A2aAgentCard1Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (SecuritySchemeElement))))) +`; + +exports[`refractor plugins replace-empty-element should refract skills array items into AgentSkill elements 1`] = ` +(A2aAgentCard1Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (AgentSkillElement)))) +`; + exports[`refractor plugins replace-empty-element should replace empty capabilities with an AgentCapabilitiesElement 1`] = ` (A2aAgentCard1Element (MemberElement diff --git a/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/index.ts b/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/index.ts index d75c8e0ed2..949bf87cfe 100644 --- a/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/index.ts +++ b/packages/apidom-ns-a2a-1/test/refractor/plugins/replace-empty-element/index.ts @@ -50,6 +50,20 @@ describe('refractor', function () { expect(sexprs(agentCardElement)).toMatchSnapshot(); }); + specify('should refract skills array items into AgentSkill elements', async function () { + const yamlDefinition = dedent` + name: agent example + skills: + - + `; + const apiDOM = await parse(yamlDefinition); + const agentCardElement = AgentCardElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(agentCardElement)).toMatchSnapshot(); + }); + specify( 'should replace empty securitySchemes with a SecuritySchemesElement', async function () { @@ -66,6 +80,23 @@ describe('refractor', function () { }, ); + specify( + 'should refract securitySchemes object values into SecurityScheme elements', + async function () { + const yamlDefinition = dedent` + name: agent example + securitySchemes: + securityScheme1: + `; + const apiDOM = await parse(yamlDefinition); + const agentCardElement = AgentCardElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(agentCardElement)).toMatchSnapshot(); + }, + ); + specify('should leave non-empty values untouched', async function () { const yamlDefinition = dedent` name: agent example From 2bba226bff0bc0ad4106c7dbb777bcd5033d3ba0 Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Thu, 11 Jun 2026 14:59:40 +0100 Subject: [PATCH 13/14] style(ls): fix prettier formatting in A2A lint index files (PROVCON-5343) Co-Authored-By: Claude Opus 4.8 --- .../src/config/a2a/agent-extension/lint/index.ts | 8 +++++++- .../config/a2a/http-auth-security-scheme/lint/index.ts | 8 +++++++- .../src/config/a2a/oauth2-security-scheme/lint/index.ts | 8 +++++++- .../a2a/open-id-connect-security-scheme/lint/index.ts | 7 ++++++- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/apidom-ls/src/config/a2a/agent-extension/lint/index.ts b/packages/apidom-ls/src/config/a2a/agent-extension/lint/index.ts index 5dd98e55de..cf28e86343 100644 --- a/packages/apidom-ls/src/config/a2a/agent-extension/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/agent-extension/lint/index.ts @@ -4,6 +4,12 @@ import descriptionTypeLint from './description--type.ts'; import requiredTypeLint from './required--type.ts'; import paramsTypeLint from './params--type.ts'; -const lints = [allowedFieldsLint, uriTypeLint, descriptionTypeLint, requiredTypeLint, paramsTypeLint]; +const lints = [ + allowedFieldsLint, + uriTypeLint, + descriptionTypeLint, + requiredTypeLint, + paramsTypeLint, +]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts index cb656ca181..6e81fb4778 100644 --- a/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/http-auth-security-scheme/lint/index.ts @@ -4,6 +4,12 @@ import schemeTypeLint from './scheme--type.ts'; import descriptionTypeLint from './description--type.ts'; import bearerFormatTypeLint from './bearer-format--type.ts'; -const lints = [allowedFieldsLint, schemeRequiredLint, schemeTypeLint, descriptionTypeLint, bearerFormatTypeLint]; +const lints = [ + allowedFieldsLint, + schemeRequiredLint, + schemeTypeLint, + descriptionTypeLint, + bearerFormatTypeLint, +]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts index 6c42aa7286..aee8b28e75 100644 --- a/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/oauth2-security-scheme/lint/index.ts @@ -4,6 +4,12 @@ import flowsTypeLint from './flows--type.ts'; import descriptionTypeLint from './description--type.ts'; import oauth2MetadataUrlTypeLint from './oauth2-metadata-url--type.ts'; -const lints = [allowedFieldsLint, flowsRequiredLint, flowsTypeLint, descriptionTypeLint, oauth2MetadataUrlTypeLint]; +const lints = [ + allowedFieldsLint, + flowsRequiredLint, + flowsTypeLint, + descriptionTypeLint, + oauth2MetadataUrlTypeLint, +]; export default lints; diff --git a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts index 991a79ce16..4d63904727 100644 --- a/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts +++ b/packages/apidom-ls/src/config/a2a/open-id-connect-security-scheme/lint/index.ts @@ -3,6 +3,11 @@ import openIdConnectUrlRequiredLint from './open-id-connect-url--required.ts'; import openIdConnectUrlTypeLint from './open-id-connect-url--type.ts'; import descriptionTypeLint from './description--type.ts'; -const lints = [allowedFieldsLint, openIdConnectUrlRequiredLint, openIdConnectUrlTypeLint, descriptionTypeLint]; +const lints = [ + allowedFieldsLint, + openIdConnectUrlRequiredLint, + openIdConnectUrlTypeLint, + descriptionTypeLint, +]; export default lints; From 25a4b055f1d24c2597c85527ec46c277fff40cf3 Mon Sep 17 00:00:00 2001 From: ben-smartbear Date: Thu, 11 Jun 2026 15:22:54 +0100 Subject: [PATCH 14/14] feat: further tweaks in light of skill (PROVCON-5343) --- packages/apidom-ls/src/config/a2a/a2a1/documentation.ts | 2 +- .../src/config/a2a/agent-card-signature/documentation.ts | 2 +- .../apidom-ls/src/config/a2a/agent-interface/documentation.ts | 2 +- .../apidom-ls/src/config/a2a/agent-skill/documentation.ts | 4 ++-- .../config/a2a/client-credentials-oauth-flow/documentation.ts | 2 +- .../src/config/a2a/device-code-oauth-flow/documentation.ts | 2 +- .../src/config/a2a/oauth-flows/lint/allowed-fields.ts | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/apidom-ls/src/config/a2a/a2a1/documentation.ts b/packages/apidom-ls/src/config/a2a/a2a1/documentation.ts index 10ad9ac687..64f34a9950 100644 --- a/packages/apidom-ls/src/config/a2a/a2a1/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/a2a1/documentation.ts @@ -14,7 +14,7 @@ const documentation = [ }, { target: 'capabilities', - docs: '[Agent Capabilities Object](https://a2a-protocol.org/latest/definitions/#agent-capabilities) — optional capabilities supported by the agent (streaming, push notifications, extensions, extended agent card).', + docs: '[Agent Capabilities Object](https://a2a-protocol.org/latest/definitions/#agent-capabilities) — required. Declares the A2A capability set supported by the agent (streaming, push notifications, extensions, extended agent card).', targetSpecs: A2A1, }, { diff --git a/packages/apidom-ls/src/config/a2a/agent-card-signature/documentation.ts b/packages/apidom-ls/src/config/a2a/agent-card-signature/documentation.ts index ab4cde9ff8..95e07ff6ea 100644 --- a/packages/apidom-ls/src/config/a2a/agent-card-signature/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/agent-card-signature/documentation.ts @@ -2,7 +2,7 @@ import { A2A1 } from '../target-specs.ts'; /** * Hover documentation for A2A v1 AgentCardSignature fields (RFC 7515 JWS). - * See [AgentCardSignature](https://a2a-protocol.org/latest/specification/#agentcardsignature). + * See [AgentCardSignature](https://a2a-protocol.org/latest/specification/#447-agentcardsignature). */ const documentation = [ { diff --git a/packages/apidom-ls/src/config/a2a/agent-interface/documentation.ts b/packages/apidom-ls/src/config/a2a/agent-interface/documentation.ts index 7e2517ff70..cdc0242669 100644 --- a/packages/apidom-ls/src/config/a2a/agent-interface/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/agent-interface/documentation.ts @@ -2,7 +2,7 @@ import { A2A1 } from '../target-specs.ts'; /** * Hover documentation for A2A v1 AgentInterface fields. - * See [AgentInterface](https://a2a-protocol.org/latest/specification/#agentinterface). + * See [AgentInterface](https://a2a-protocol.org/latest/specification/#446-agentinterface). */ const documentation = [ { diff --git a/packages/apidom-ls/src/config/a2a/agent-skill/documentation.ts b/packages/apidom-ls/src/config/a2a/agent-skill/documentation.ts index e33cbb3894..26fbb9545b 100644 --- a/packages/apidom-ls/src/config/a2a/agent-skill/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/agent-skill/documentation.ts @@ -2,7 +2,7 @@ import { A2A1 } from '../target-specs.ts'; /** * Hover documentation for A2A v1 AgentSkill fields. - * See [AgentSkill](https://a2a-protocol.org/latest/specification/#agentskill). + * See [AgentSkill](https://a2a-protocol.org/latest/specification/#445-agentskill). */ const documentation = [ { @@ -42,7 +42,7 @@ const documentation = [ }, { target: 'securityRequirements', - docs: 'Array of [Security Requirement Objects](https://a2a-protocol.org/latest/specification/#451-securityscheme) necessary for this skill.', + docs: 'Array of [Security Requirement Objects](https://a2a-protocol.org/latest/definitions/#security-requirement) — specifies which security schemes clients must satisfy to use this skill.', targetSpecs: A2A1, }, ]; diff --git a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/documentation.ts b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/documentation.ts index db2c6de3bd..25897fc458 100644 --- a/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/client-credentials-oauth-flow/documentation.ts @@ -2,7 +2,7 @@ import { A2A1 } from '../target-specs.ts'; /** * Hover documentation for A2A v1 ClientCredentialsOAuthFlow fields. - * See [ClientCredentialsOAuthFlow](https://a2a-protocol.org/latest/specification/#clientcredentialsoauthflow). + * See [ClientCredentialsOAuthFlow](https://a2a-protocol.org/latest/specification/#459-clientcredentialsoauthflow). */ const documentation = [ { diff --git a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/documentation.ts b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/documentation.ts index 1757c966ec..6ae3719f5e 100644 --- a/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/documentation.ts +++ b/packages/apidom-ls/src/config/a2a/device-code-oauth-flow/documentation.ts @@ -2,7 +2,7 @@ import { A2A1 } from '../target-specs.ts'; /** * Hover documentation for A2A v1 DeviceCodeOAuthFlow fields (RFC 8628). - * See [DeviceCodeOAuthFlow](https://a2a-protocol.org/latest/specification/#devicecodeoauthflow). + * See [DeviceCodeOAuthFlow](https://a2a-protocol.org/latest/specification/#4510-devicecodeoauthflow). */ const documentation = [ { diff --git a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts index e2f4878d5d..5642d509bb 100644 --- a/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts +++ b/packages/apidom-ls/src/config/a2a/oauth-flows/lint/allowed-fields.ts @@ -10,7 +10,7 @@ const allowedFieldsLint: LinterMeta = { message: 'Object includes not allowed fields', severity: DiagnosticSeverity.Error, linterFunction: 'allowedFields', - linterParams: [['authorizationCode', 'clientCredentials', 'deviceCode']], + linterParams: [['authorizationCode', 'clientCredentials', 'implicit', 'password', 'deviceCode']], marker: 'key', targetSpecs: A2A1, };