πŸ”’ VhyxSeal
GitHub

Vanilla JS / Web Components

@vhyxseal/vanilla provides Web Components custom elements and a contract registry for framework-free JavaScript environments. Use it to add semantic contracts to any HTML page, Astro site, plain JavaScript app, or as a progressive enhancement layer on existing markup.

What the Adapter Provides

  • SealButton, SealInput, SealForm, SealNav, SealDisplay, SealConfirmation β€” Web Components custom elements with contract registration lifecycle
  • defineVhyxSealElements() β€” registers all six custom elements with the browser
  • createSealRegistry() β€” creates an isolated contract registry for non-browser environments
  • All elements auto-register with the internal module-level registry on DOM connect and unregister on disconnect

Installation

npm install @vhyxseal/vanilla @vhyxseal/core

Browser-only. Custom elements require a browser environment with customElements support (all modern browsers).

Full Setup

// Register all custom elements at app startup
import { defineVhyxSealElements } from '@vhyxseal/vanilla'

// Safe to call multiple times β€” guards with customElements.get() before defining
// Also guards typeof customElements === "undefined" for non-browser environments
defineVhyxSealElements()

// Elements are now available as custom HTML elements:
// <seal-button>, <seal-input>, <seal-form>,
// <seal-nav>, <seal-display>, <seal-confirmation>

createSealRegistry()

For server-side or testing use cases where the browser registry is not available. Creates an isolated, non-global registry.

// createSealRegistry β€” create an isolated contract registry
import { createSealRegistry } from '@vhyxseal/vanilla'
import { defineContract } from '@vhyxseal/core'

const registry = createSealRegistry()

const contract = defineContract({
  id: "checkout-btn",
  type: "action",
  intent: "place-order",
  description: "Submits the cart as a purchase order",
  requires: [
    { field: "user.authenticated", operator: "===", value: true, description: "User must be logged in" },
  ],
  requiredPermissions: ["write:orders"],
  consequence: "Creates order, charges payment, triggers fulfillment",
  affects: ["cart", "orders", "payment"],
  reversible: true, reversibleWindow: 300,
  safetyLevel: "high", requiresConfirmation: true, destructive: false,
  contractVersion: "1.0.0",
})

registry.register(contract)

// Read contracts
const all = registry.getAll()
const byId = registry.get("checkout-btn")
registry.unregister("checkout-btn")
registry.clear()

Custom Elements Usage

SealButton

Adds role=β€œbutton” and tabindex=β€œ0” on connect. Registers contract with globalRegistry.

<!-- HTML β€” use custom elements as standard HTML -->
<seal-button id="checkout-btn">Place Order</seal-button>

<script>
import { SealButton } from '@vhyxseal/vanilla'
import { defineContract } from '@vhyxseal/core'

// Get a reference to the element
const btn = document.getElementById('checkout-btn')

// Set the contract β€” triggers registration with globalRegistry on connect
btn.setContract(defineContract({
  id: "checkout-btn",
  type: "action",
  intent: "place-order",
  description: "Places the order",
  requires: [{ field: "user.authenticated", operator: "===", value: true, description: "Must be logged in" }],
  requiredPermissions: ["write:orders"],
  consequence: "Creates order and charges payment",
  affects: ["orders", "payment"],
  reversible: true, reversibleWindow: 300,
  safetyLevel: "high", requiresConfirmation: true, destructive: false,
  contractVersion: "1.0.0",
}))

// Contract is registered with globalRegistry when element connects to DOM
// Contract is unregistered when element disconnects from DOM
</script>

SealConfirmation

Custom confirmation lifecycle with typed custom events. Events bubble to parent elements.

<!-- SealConfirmation β€” custom events for confirmation lifecycle -->
<seal-confirmation id="delete-confirm"></seal-confirmation>

<script>
const confirmEl = document.getElementById('delete-confirm')

// Request confirmation (shows confirmation UI, fires 'confirmationrequested')
confirmEl.requestConfirmation()

// Listen for user decisions
confirmEl.addEventListener('confirmationconfirmed', () => {
  performDelete()
})

confirmEl.addEventListener('confirmationcancelled', () => {
  console.log('User cancelled')
})

// Programmatic confirm/cancel (e.g. from modal buttons)
document.getElementById('confirm-btn').addEventListener('click', () => {
  confirmEl.confirm()   // fires 'confirmationconfirmed', bubbles: true
})

document.getElementById('cancel-btn').addEventListener('click', () => {
  confirmEl.cancel()    // fires 'confirmationcancelled', bubbles: true
})

// Check state
console.log(confirmEl.isPending)  // true while waiting for user
console.log(confirmEl.confirmed)  // true after confirmed
</script>

All Elements Reference

// All six custom elements
import {
  SealButton,           // role="button" + tabindex="0" on connect
  SealInput,            // role="textbox" on connect
  SealForm,             // form element with contract registration
  SealNav,              // role="navigation" on connect
  SealDisplay,          // aria-live="polite" on connect (configurable)
  SealConfirmation,     // confirmation lifecycle with custom events
  defineVhyxSealElements, // register all elements at once
} from '@vhyxseal/vanilla'

// Also exported:
import { createSealRegistry } from '@vhyxseal/vanilla'
import type { SealRegistry } from '@vhyxseal/vanilla'
ElementHTML TagARIA setup on connectNotes
SealButton<seal-button>role="button", tabindex="0"Keyboard accessible by default
SealInput<seal-input>role="textbox"Wraps any input element
SealForm<seal-form>β€”Form container with contract registration
SealNav<seal-nav>role="navigation"Navigation landmark
SealDisplay<seal-display>aria-live="polite"Configurable via aria-live attribute
SealConfirmation<seal-confirmation>β€”Fires confirmationrequested, confirmationconfirmed, confirmationcancelled custom events

TypeScript Types

// TypeScript types from @vhyxseal/vanilla
import type {
  SealRegistry,  // Interface for createSealRegistry() return value
} from '@vhyxseal/vanilla'

// SealRegistry interface:
// register(contract: ComponentContract): void
// unregister(id: string): void
// get(id: string): Readonly<ComponentContract> | undefined
// getAll(): ReadonlyMap<string, Readonly<ComponentContract>>
// clear(): void

Common Patterns

Progressive enhancement of an existing form

<!-- Example 1 β€” Progressive enhancement of an existing form -->
<form id="checkout-form">
  <seal-input id="card-number-input">
    <input type="text" name="card-number" placeholder="Card number" />
  </seal-input>
  <seal-confirmation id="payment-confirm">
    <seal-button id="pay-now-btn">Pay Now</seal-button>
  </seal-confirmation>
</form>

<script type="module">
import { defineVhyxSealElements } from '@vhyxseal/vanilla'
import { defineContract } from '@vhyxseal/core'

defineVhyxSealElements()

document.getElementById('card-number-input').setContract(
  defineContract({
    id: "card-number-input", type: "input", intent: "collect-payment",
    description: "Credit card number field",
    requires: [], requiredPermissions: [],
    consequence: "Stores card number for payment processing",
    affects: ["payment"],
    reversible: false, safetyLevel: "sensitive",
    requiresConfirmation: false, destructive: false,
    contractVersion: "1.0.0",
  })
)

document.getElementById('pay-now-btn').setContract(
  defineContract({
    id: "pay-now-btn", type: "action", intent: "make-payment",
    description: "Submits payment",
    requires: [
      { field: "payment.cardNumber.isValid", operator: "===", value: true, description: "Card number must be valid" },
    ],
    requiredPermissions: ["write:payments"],
    consequence: "Charges the card and creates a payment record",
    affects: ["payments", "orders"],
    reversible: false, safetyLevel: "critical",
    requiresConfirmation: true, destructive: false,
    contractVersion: "1.0.0",
  })
)
</script>

Generating a manifest server-side

<!-- Example 2 β€” Read the manifest using the CLI after setup -->
<!-- (Vanilla JS generates the manifest via createSealRegistry) -->

<script type="module">
import { createSealRegistry } from '@vhyxseal/vanilla'
import { generateManifest } from '@vhyxseal/core'

const registry = createSealRegistry()

// Register contracts
registry.register(searchContract)
registry.register(filterContract)

// Generate manifest for agent consumption
const manifest = generateManifest(
  Array.from(registry.getAll().values()),
  { domain: "example.com", domainVerified: false, verificationToken: "" }
)

// Expose manifest at /__agent__/manifest.json via your server
// e.g. Express: app.get('/__agent__/manifest.json', (_, res) => res.json(manifest))
console.log(manifest.fingerprint)
</script>

Known Limitations

  • Browser-only β€” custom elements require a DOM environment. Use createSealRegistry() for server-side or test environments
  • The internal globalRegistry is not exported from the public API β€” use createSealRegistry() for isolated registries
  • Private class fields (#contract, #confirmed, #isPending) compile to WeakMap-based implementation targeting ES2020 β€” true runtime privacy, small performance overhead
  • defineVhyxSealElements() is idempotent β€” safe to call multiple times, guards with customElements.get() before each define()
  • No built-in manifest serving β€” use your server framework to serve the registry contents at /__agent__/manifest.json (see example 2)