@nxisai/nxis 2.1.2 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,9 +19,13 @@ jobs:
19
19
  with:
20
20
  node-version: '20'
21
21
  cache: 'npm'
22
+ registry-url: 'https://npm.pkg.github.com'
23
+ scope: '@nxisai'
22
24
 
23
25
  - name: Install dependencies
24
26
  run: npm ci
27
+ env:
28
+ NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
25
29
 
26
30
  - name: Build project
27
31
  env:
@@ -59,6 +63,8 @@ jobs:
59
63
 
60
64
  - name: Install dependencies
61
65
  run: npm ci
66
+ env:
67
+ NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62
68
 
63
69
  - name: Build SSR package
64
70
  env:
@@ -78,6 +84,9 @@ jobs:
78
84
  publish-npmjs:
79
85
  needs: publish-npm
80
86
  runs-on: ubuntu-latest
87
+ permissions:
88
+ contents: read
89
+ packages: read
81
90
 
82
91
  steps:
83
92
  - name: Checkout code
@@ -88,11 +97,28 @@ jobs:
88
97
  with:
89
98
  node-version: '20'
90
99
  cache: 'npm'
91
- registry-url: 'https://registry.npmjs.org'
92
- scope: '@nxisai'
100
+
101
+ - name: Write .npmrc (auth both registries)
102
+ run: |
103
+ if [ -z "$NPM_TOKEN" ]; then
104
+ echo "::error::NPM_TOKEN secret is empty"; exit 1
105
+ fi
106
+ echo "NPM_TOKEN length: ${#NPM_TOKEN}"
107
+ # Unquoted heredoc so the shell expands the token values into the file
108
+ cat > .npmrc <<EOF
109
+ @nxisai:registry=https://npm.pkg.github.com
110
+ //npm.pkg.github.com/:_authToken=${GH_TOKEN}
111
+ //registry.npmjs.org/:_authToken=${NPM_TOKEN}
112
+ always-auth=true
113
+ EOF
114
+ env:
115
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
116
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
93
117
 
94
118
  - name: Install dependencies
95
- run: npm ci
119
+ run: |
120
+ npm ci
121
+ rm -f .npmrc
96
122
 
97
123
  - name: Build package
98
124
  env:
@@ -102,9 +128,21 @@ jobs:
102
128
  npm run build:types
103
129
 
104
130
  - name: Scope package for npm
105
- run: npm pkg set name='@nxisai/nxis'
131
+ run: |
132
+ npm pkg set name='@nxisai/nxis'
133
+ npm pkg set publishConfig.registry='https://registry.npmjs.org'
106
134
 
107
- - name: Publish to npm
108
- run: npm publish --access public
135
+ - name: Rewrite .npmrc for npmjs publish
136
+ run: |
137
+ # Drop the @nxisai->GitHub Packages scope mapping (only needed for install).
138
+ # For a scoped package, that mapping overrides publishConfig.registry and
139
+ # --registry, so it must be gone for the publish to route to npmjs.
140
+ cat > .npmrc <<EOF
141
+ //registry.npmjs.org/:_authToken=${NPM_TOKEN}
142
+ always-auth=true
143
+ EOF
109
144
  env:
110
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
145
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
146
+
147
+ - name: Publish to npm
148
+ run: npm publish --access public --registry https://registry.npmjs.org
package/README.md CHANGED
@@ -6,13 +6,79 @@
6
6
 
7
7
  # Nxis.js
8
8
 
9
+ Structured data for the AI era — inject JSON-LD so search engines, LLMs, and AI agents can understand your pages, and use WebMCP to make those pages actionable by agents.
9
10
 
10
- ## Overview
11
- Nxis.js is a lightweight JavaScript client designed to integrate with the Nxis AI platform. It dynamically injects JSON-LD structured data into web pages to enhance visibility for search engines, AI agents, and large language models (LLMs). By embedding this structured data, Nxis.js helps transform standard web content into semantic data that is easily discoverable and consumable across the modern search and AI ecosystem.
11
+ [![npm version](https://img.shields.io/npm/v/@nxisai/nxis.svg?color=blue)](https://www.npmjs.com/package/@nxisai/nxis) [![npm downloads](https://img.shields.io/npm/dm/@nxisai/nxis.svg)](https://www.npmjs.com/package/@nxisai/nxis) [![license](https://img.shields.io/npm/l/@nxisai/nxis.svg)](https://github.com/nxisai/nxis-js/blob/main/LICENSE)
12
12
 
13
- Nxis.js supports **both client-side (browser)** and **server-side rendering (SSR)** environments, making it compatible with modern web frameworks.
13
+ Nxis.js is a lightweight JavaScript client for the [Nxis AI](https://nxis.ai) platform. It fetches AI-optimized JSON-LD structured data and injects it into your pages, turning ordinary web content into semantic data that's discoverable across the modern search and AI ecosystem. It also makes your pages **actionable by AI agents** — via [WebMCP](#webmcp--ai-agent-tool-registration), Nxis turns your existing forms into tools that agents can discover and invoke directly in the browser. It runs in both **browser** and **server-side rendering (SSR)** environments.
14
14
 
15
- > **Getting Started:** Create an account at [www.nxis.ai](https://www.nxis.ai) to get your API credentials and start integrating structured data into your site.
15
+ > **Getting started:** Create an account at [www.nxis.ai](https://www.nxis.ai) to get your API credentials.
16
+
17
+ ## Features
18
+
19
+ - 🌐 **Browser & SSR** — drop-in CDN `<script>` tag or a typed `NxisClient` for server-rendered apps
20
+ - 🔍 **AI-ready structured data** — JSON-LD visible to search engines, LLMs, and AI agents
21
+ - 🔑 **Automatic OAuth** — access tokens are fetched, cached, and refreshed for you
22
+ - 🤖 **WebMCP tools** — optionally expose your forms as tools AI agents can call (Chrome 138+)
23
+ - 📦 **Zero-config CDN** — static sites need no build step or local JavaScript
24
+ - 🧩 **Framework guides** — Next.js, Nuxt, Remix, Astro, SvelteKit, Express, and more
25
+
26
+ ## Table of Contents
27
+
28
+ - [Installation](#installation)
29
+ - [Basic Usage](#basic-usage)
30
+ - [How It Works](#how-it-works)
31
+ - [Server-Side Rendering (SSR)](#server-side-rendering-ssr)
32
+ - [Framework Guides](#framework-guides)
33
+ - [WebMCP — AI Agent Tool Registration](#webmcp--ai-agent-tool-registration)
34
+
35
+ ## Installation
36
+
37
+ **Browser (CDN)** — no install required. Add the script to your `<head>` (replace `${TOKEN}` with your Nxis JWT token):
38
+
39
+ ```html
40
+ <script
41
+ type="text/javascript"
42
+ async
43
+ src="https://cdn.nxis.ai/nxis.js?token=${TOKEN}"
44
+ crossorigin="anonymous">
45
+ </script>
46
+ ```
47
+
48
+ **Server / SSR (npm):**
49
+
50
+ ```bash
51
+ npm install @nxisai/nxis
52
+ ```
53
+
54
+ ## Basic Usage
55
+
56
+ ### Browser
57
+
58
+ The CDN script is all you need. The JWT token carries your organization and site configuration, so the script automatically fetches your page's JSON-LD and injects it as a `<script type="application/ld+json">` tag — no data attributes, no extra code. Works with static HTML, WordPress, Shopify, Squarespace, or any client-rendered SPA.
59
+
60
+ ### Server-Side Rendering
61
+
62
+ Create a `NxisClient` once, then fetch JSON-LD at render time and embed it in your HTML:
63
+
64
+ ```typescript
65
+ import { NxisClient } from '@nxisai/nxis/ssr';
66
+
67
+ const nxis = new NxisClient({
68
+ clientId: process.env.NXIS_CLIENT_ID!,
69
+ clientSecret: process.env.NXIS_CLIENT_SECRET!,
70
+ });
71
+
72
+ const jsonLd = await nxis.getStructuredData({
73
+ uri: 'https://yoursite.com/',
74
+ siteId: process.env.NXIS_SITE_ID!,
75
+ });
76
+
77
+ // Serialize into your server-rendered HTML:
78
+ // <script type="application/ld+json">${JSON.stringify(jsonLd)}</script>
79
+ ```
80
+
81
+ Tokens are managed automatically — cached in memory and refreshed before expiration. See [Server-Side Rendering (SSR)](#server-side-rendering-ssr) for the full API and [Framework Guides](#framework-guides) for copy-paste setups.
16
82
 
17
83
  ## Supported Frameworks
18
84
 
@@ -30,14 +96,14 @@ Nxis.js supports **both client-side (browser)** and **server-side rendering (SSR
30
96
 
31
97
  Nxis.js supports two integration paths — **client-side (browser)** and **server-side rendering (SSR)**.
32
98
 
33
- ### Client-Side (Browser)
99
+ ### In the Browser
34
100
 
35
101
  1. **Script Loading**: Your page loads `nxis.js` via a `<script>` tag from the Nxis CDN with a signed Nxis JWT token as a query parameter.
36
- 2. **Request**: On execution, the script sends a request to the Nxis AI Proxy (`/v1/nxis:inject`) with the current page URI, referrer, and session context.
102
+ 2. **Request**: On execution, the script sends a request to the Nxis AI Proxy (`/v1/nxis:inject`) with the current page URI and referrer.
37
103
  3. **Validation & Retrieval**: The Proxy validates the JWT token, and retrieves JSON-LD structured data from the Structured Data Service.
38
104
  4. **Injection**: On success, `nxis.js` creates a `<script type="application/ld+json">` element and appends it to the document `<head>`.
39
105
 
40
- ### Server-Side Rendering (SSR)
106
+ ### On the Server (SSR)
41
107
 
42
108
  1. **Initialize**: Your application creates a `NxisClient` with OAuth client credentials. Tokens are managed automatically.
43
109
  2. **Request**: At render time, the client calls the Nxis AI Proxy (`/v1/nxis:ssr`) with the page URI and a Bearer access token.
@@ -46,25 +112,6 @@ Nxis.js supports two integration paths — **client-side (browser)** and **serve
46
112
 
47
113
  ---
48
114
 
49
- ## Client-Side Installation (Browser)
50
-
51
- Add the script tag to the head of your HTML document. Replace `${TOKEN}` with your Nxis JWT token.
52
-
53
- ```html
54
- <script
55
- type="text/javascript"
56
- async
57
- src="https://cdn.nxis.ai/nxis.js?token=${TOKEN}"
58
- crossorigin="anonymous">
59
- </script>
60
- ```
61
-
62
- The JWT token contains your organization and site configuration. No additional data attributes are needed — the CDN extracts everything from the token claims and injects it into the script automatically.
63
-
64
- This works with any static HTML page, CMS (WordPress, Shopify, Squarespace), or client-rendered SPA.
65
-
66
- ---
67
-
68
115
  ## Server-Side Rendering (SSR)
69
116
 
70
117
  For SSR frameworks, Nxis.js provides a `NxisClient` class that fetches JSON-LD at render time so structured data is present in the initial HTML response — no client-side JavaScript required.
@@ -123,6 +170,10 @@ Access tokens are managed automatically by `NxisClient`:
123
170
 
124
171
  ---
125
172
 
173
+ ## Framework Guides
174
+
175
+ Copy-paste setups for popular SSR/SSG frameworks. Each fetches JSON-LD at render time and embeds it in the initial HTML response.
176
+
126
177
  ### Next.js (App Router)
127
178
 
128
179
  ```tsx
@@ -365,9 +416,7 @@ app.get('*', async (req, res) => {
365
416
  app.listen(3000);
366
417
  ```
367
418
 
368
- ---
369
-
370
- ## Adding Nxis SSR with Claude Code
419
+ ### Adding Nxis SSR with Claude Code
371
420
 
372
421
  You can use [Claude Code](https://docs.anthropic.com/en/docs/claude-code) to automatically add Nxis SSR to an existing Next.js project. Paste the following prompt into Claude Code from your project's root directory:
373
422
 
@@ -408,3 +457,213 @@ to search engines, LLMs, and AI agents that don't render JavaScript.
408
457
 
409
458
  This will guide Claude Code to install the package, configure environment variables, and wire up SSR structured data injection across your pages.
410
459
 
460
+ ---
461
+
462
+ ## WebMCP — AI Agent Tool Registration
463
+
464
+ Nxis.js can automatically expose your site's forms as **WebMCP tools** that AI agents (Chrome 138+) can discover and invoke. When a page contains Nxis Action JSON-LD, the `@nxisai/nxis/webmcp` module reads each Action, converts it to a JSON Schema–described tool, and registers it via `document.modelContext.registerTool()`. The agent sees a callable tool — the library handles form location, field filling, event dispatching, and submission.
465
+
466
+ ### How It Works
467
+
468
+ ```
469
+ ┌─────────────────────────────────────────────────────────────────────┐
470
+ │ Page HTML │
471
+ │ │
472
+ │ <script type="application/ld+json"> │
473
+ │ { "@type": "SearchAction", "name": "search_products", ... } │
474
+ │ </script> │
475
+ │ │
476
+ │ <form action="/search" method="GET"> │
477
+ │ <input name="query" type="text" /> │
478
+ │ </form> │
479
+ └──────────────────────────┬──────────────────────────────────────────┘
480
+ │
481
+ registerActions()
482
+ │
483
+ ┌──────────────┼──────────────────┐
484
+ ▼ ▼ ▼
485
+ ① Parse ② Schema ③ Register
486
+ Read Action Build JSON registerTool()
487
+ JSON-LD from Schema from on modelContext
488
+ <script> tags PVS descriptors
489
+ │ │ │
490
+ └──────────────┼──────────────────┘
491
+ │
492
+ Agent calls tool
493
+ │
494
+ ┌──────────────┼──────────────────┐
495
+ ▼ ▼ ▼
496
+ ④ Locate ⑤ Fill ⑥ Submit
497
+ Find matching Set values via requestSubmit()
498
+ <form> on page native setters with validation
499
+ + dispatch events
500
+ ```
501
+
502
+ 1. **Parse**: Reads all `<script type="application/ld+json">` blocks and extracts Action nodes (including subtypes like `SearchAction`, `RegisterAction`, etc.).
503
+ 2. **Schema**: Converts each Action's `PropertyValueSpecification` descriptors into a JSON Schema `inputSchema` for the tool.
504
+ 3. **Register**: Calls `document.modelContext.registerTool()` with the tool name, description, schema, and an `execute` callback.
505
+ 4. **Locate**: When the agent invokes the tool, the execute callback locates the matching `<form>` using URL/method matching and field-set containment disambiguation.
506
+ 5. **Fill**: Sets field values using native property setters (bypassing framework interceptors) and dispatches `input`/`change` events for React, Vue, Angular reactivity.
507
+ 6. **Submit**: Calls `requestSubmit()` (respects validation and submit event handlers) and returns a structured JSON result so the agent can self-correct on failure.
508
+
509
+ ### Browser Requirements
510
+
511
+ WebMCP requires `document.modelContext`, available natively in **Chrome 138+**. On older browsers, the `@nxisai/webmcp` polyfill is installed automatically — `registerActions()` gracefully no-ops in environments where WebMCP is unavailable (SSR, Node.js, workers).
512
+
513
+ ### Installation
514
+
515
+ WebMCP is included in the main package:
516
+
517
+ ```bash
518
+ npm install @nxisai/nxis
519
+ ```
520
+
521
+ Import from the `webmcp` subpath:
522
+
523
+ ```typescript
524
+ import { registerActions } from '@nxisai/nxis/webmcp';
525
+ ```
526
+
527
+ ### Quick Start
528
+
529
+ ```typescript
530
+ import { registerActions } from '@nxisai/nxis/webmcp';
531
+
532
+ // Register all Action tools found in the page's JSON-LD
533
+ const cleanup = registerActions();
534
+
535
+ // Later, to unregister all tools and disconnect observers:
536
+ cleanup();
537
+ ```
538
+
539
+ ### API Reference
540
+
541
+ ```typescript
542
+ import { registerActions } from '@nxisai/nxis/webmcp';
543
+ import type { RegisterOptions } from '@nxisai/nxis/webmcp';
544
+
545
+ const cleanup = registerActions({
546
+ doc?: Document, // Document to scan (defaults to `document`)
547
+ signal?: AbortSignal, // External cancellation signal
548
+ observe?: boolean, // MutationObserver re-scan for SPAs (default: true)
549
+ });
550
+
551
+ // cleanup() unregisters all tools and disconnects observers
552
+ ```
553
+
554
+ #### Options
555
+
556
+ | Option | Type | Default | Description |
557
+ |--------|------|---------|-------------|
558
+ | `doc` | `Document` | `document` | The document to scan for JSON-LD blocks. Useful for iframes. |
559
+ | `signal` | `AbortSignal` | — | An external signal to cancel all registrations. |
560
+ | `observe` | `boolean` | `true` | When `true`, a `MutationObserver` watches for new JSON-LD insertions and auto-registers new tools. Set to `false` for static pages. |
561
+
562
+ ### SPA Support
563
+
564
+ By default, `registerActions()` installs a `MutationObserver` that watches the entire document subtree for new `<script type="application/ld+json">` insertions. When your SPA navigates and Nxis injects new structured data, the observer automatically registers any new Action tools (de-duplicated by `@id`).
565
+
566
+ ```typescript
567
+ // SPAs: observer is enabled by default
568
+ const cleanup = registerActions();
569
+
570
+ // Static pages: opt out of the observer
571
+ const cleanup = registerActions({ observe: false });
572
+ ```
573
+
574
+ ### Using WebMCP with SSR Frameworks
575
+
576
+ WebMCP is a **browser-only** feature — it depends on the live DOM and `document.modelContext`. It cannot run during the server render: in SSR/SSG environments (`getServerSideProps`, server components, loaders, the Nuxt/SvelteKit server pass) `registerActions()` detects there is no `document` and safely no-ops.
577
+
578
+ That's by design and composes with SSR: your server render injects the JSON-LD (via `NxisClient.getStructuredData`), and `registerActions()` then reads those same `<script type="application/ld+json">` tags **in the browser after hydration**. So you can absolutely use WebMCP in an SSR framework — you just call it from a client-only lifecycle hook.
579
+
580
+ **Next.js (App Router)** — a client component:
581
+
582
+ ```tsx
583
+ 'use client';
584
+ import { useEffect } from 'react';
585
+ import { registerActions } from '@nxisai/nxis/webmcp';
586
+
587
+ export function NxisWebMCP() {
588
+ useEffect(() => {
589
+ const cleanup = registerActions(); // runs only in the browser
590
+ return cleanup; // unregister on unmount
591
+ }, []);
592
+ return null;
593
+ }
594
+ ```
595
+
596
+ Render `<NxisWebMCP />` once in your layout. Equivalent hooks for other frameworks: **Nuxt** `onMounted()` (or wrap in `<ClientOnly>`), **Remix** `useEffect`, **SvelteKit** `onMount`.
597
+
598
+ > **Note:** The `@nxisai/nxis/webmcp` subpath ships as an ES module that is safe to import on the server (it no-ops without a DOM). A CommonJS `require()` of this subpath is not supported — import it with ESM `import` syntax.
599
+
600
+ ### Supported Action Types
601
+
602
+ The backend classifier maps forms to schema.org Action subtypes:
603
+
604
+ | Action Type | Example Use Case |
605
+ |-------------|-----------------|
606
+ | `SearchAction` | Site search, product search |
607
+ | `RegisterAction` | Event registration, account sign-up |
608
+ | `SubscribeAction` | Newsletter opt-in, mailing list |
609
+ | `BuyAction` | Purchase, checkout |
610
+ | `OrderAction` | Food ordering, service booking |
611
+ | `ScheduleAction` | Appointment scheduling |
612
+ | `ReserveAction` | Reservation forms |
613
+ | `CommunicateAction` | Contact forms, feedback |
614
+ | `ApplyAction` | Job applications |
615
+ | `AskAction` | Q&A, support tickets |
616
+ | `QuoteAction` | Quote requests |
617
+ | `ReviewAction` | Reviews, ratings |
618
+ | `CommentAction` | Comment forms |
619
+
620
+ ### CDN Usage (No Build Step)
621
+
622
+ For static sites, no manual configuration or local JavaScript setup is required. The Nxis CDN dynamically customizes the script for your site at request time:
623
+
624
+ 1. When a browser requests `nxis.js?token=${TOKEN}`, the proxy decodes the HS256-signed JWT token.
625
+ 2. It extracts your organization, site, and entitlement claims, including the **`webmcp`** claim which defines the WebMCP access configuration (e.g., `"browser"`, `"ssr"`, or `"none"`).
626
+ 3. The proxy then prepends a dynamic initialization header directly into the served script file:
627
+ ```javascript
628
+ window.__NXIS_CONFIG__ = {
629
+ org_id: "your_org_id",
630
+ site_id: "your_site_id",
631
+ sub_id: "your_subscription_id",
632
+ webmcp: { mode: "browser" } // Extracted from the JWT claim
633
+ };
634
+ ```
635
+
636
+ To enable this on a static HTML page, simply add the standard CDN script tag to your document's `<head>`:
637
+
638
+ ```html
639
+ <script
640
+ type="text/javascript"
641
+ async
642
+ src="https://cdn.nxis.ai/nxis.js?token=${TOKEN}"
643
+ crossorigin="anonymous">
644
+ </script>
645
+ ```
646
+
647
+ When the script executes in the browser, it reads the prepended configuration from `window.__NXIS_CONFIG__`, fetches/injects the page's JSON-LD, and automatically registers your forms as WebMCP tools (if the `webmcp.mode` claim is set to `"browser"`, which is the default).
648
+
649
+ ### Advanced Exports
650
+
651
+ For consumers who need lower-level access:
652
+
653
+ ```typescript
654
+ // Parse Action JSON-LD without registering tools
655
+ import { readActions } from '@nxisai/nxis/webmcp';
656
+ const actions = readActions(document);
657
+
658
+ // Build a JSON Schema from an Action (for testing or custom registration)
659
+ import { buildInputSchema } from '@nxisai/nxis/webmcp';
660
+ const schema = buildInputSchema(action);
661
+
662
+ // Contract types
663
+ import type {
664
+ NxisAction,
665
+ NxisEntryPoint,
666
+ NxisPropertyValueSpecification,
667
+ } from '@nxisai/nxis/webmcp';
668
+ ```
669
+
package/build/api.d.ts CHANGED
@@ -34,6 +34,19 @@ export type NxisConfig = {
34
34
  site_id: string;
35
35
  /** The subscription identifier used for quota and entitlement checks. */
36
36
  sub_id: string;
37
+ /**
38
+ * WebMCP configuration. Omit entirely to use defaults (`mode: "browser"`,
39
+ * `declarative: false`).
40
+ */
41
+ webmcp?: {
42
+ /**
43
+ * Integration mode:
44
+ * - `"browser"` (default): Fetch JSON-LD from API, inject into page, register WebMCP tools.
45
+ * - `"ssr"`: Skip fetch/inject (server already rendered JSON-LD), only register WebMCP tools from existing DOM.
46
+ * - `"none"`: Fetch and inject JSON-LD for SEO, but do not register WebMCP tools.
47
+ */
48
+ mode: "browser" | "ssr" | "none";
49
+ };
37
50
  };
38
51
  /**
39
52
  * Parameters for a client-side structured data request sent to the
@@ -48,8 +61,6 @@ export type StructuredDataRequest = {
48
61
  uri: HttpUri;
49
62
  /** The `document.referrer` value, if available. */
50
63
  referrer?: string;
51
- /** A client-generated session ID for request correlation. */
52
- session?: string;
53
64
  };
54
65
  /**
55
66
  * The response body returned by both the `/v1/nxis:inject` and `/v1/nxis:ssr`
package/build/core.d.ts CHANGED
@@ -4,7 +4,7 @@ import { StructuredDataRequest, StructuredDataResponse } from './api';
4
4
  * the client-side `/v1/nxis:inject` endpoint.
5
5
  *
6
6
  * @param request - The structured data request containing org, site, URI,
7
- * referrer, and session info.
7
+ * and referrer info.
8
8
  * @returns A `URLSearchParams` instance ready to be appended to the request URL.
9
9
  *
10
10
  * @internal
package/build/nxis.js CHANGED
@@ -1 +1 @@
1
- !function(){const e={NXIS_API_HOST:"https://proxy.nxis.ai"};try{if(process)return process.env=Object.assign({},process.env),void Object.assign(process.env,e)}catch(e){}globalThis.process={env:e}}();class e extends Error{constructor(t){super(t),Object.setPrototypeOf(this,e.prototype)}}var t;!function(e){e[e.OK=0]="OK",e[e.INVALID_ARGUMENT=3]="INVALID_ARGUMENT",e[e.NOT_FOUND=5]="NOT_FOUND",e[e.PERMISSION_DENIED=7]="PERMISSION_DENIED",e[e.RESOURCE_EXHAUSTED=8]="RESOURCE_EXHAUSTED",e[e.INTERNAL=13]="INTERNAL",e[e.UNAUTHENTICATED=16]="UNAUTHENTICATED"}(t||(t={})),t.OK,t.INVALID_ARGUMENT,t.NOT_FOUND,t.PERMISSION_DENIED,t.RESOURCE_EXHAUSTED,t.INTERNAL,t.UNAUTHENTICATED;class n extends Error{constructor(e,t,r){super(r),this.code=e,this.httpStatus=t,this.name="ProxyError",Object.setPrototypeOf(this,n.prototype)}get isRetryable(){return this.code===t.RESOURCE_EXHAUSTED||this.code===t.INTERNAL}get isAuthError(){return this.code===t.UNAUTHENTICATED||this.code===t.PERMISSION_DENIED}static fromResponse(e,t){return new n(t.code,e,t.message)}}const r=[];for(let e=0;e<256;++e)r.push((e+256).toString(16).slice(1));const o=new Uint8Array(16);function s(e,t,n){return t||e||!crypto.randomUUID?function(e,t,n){e=e||{};const s=e.random??e.rng?.()??crypto.getRandomValues(o);if(s.length<16)throw new Error("Random bytes length must be >= 16");if(s[6]=15&s[6]|64,s[8]=63&s[8]|128,t){if((n=n||0)<0||n+16>t.length)throw new RangeError(`UUID byte range ${n}:${n+15} is out of buffer bounds`);for(let e=0;e<16;++e)t[n+e]=s[e];return t}return function(e,t=0){return(r[e[t+0]]+r[e[t+1]]+r[e[t+2]]+r[e[t+3]]+"-"+r[e[t+4]]+r[e[t+5]]+"-"+r[e[t+6]]+r[e[t+7]]+"-"+r[e[t+8]]+r[e[t+9]]+"-"+r[e[t+10]]+r[e[t+11]]+r[e[t+12]]+r[e[t+13]]+r[e[t+14]]+r[e[t+15]]).toLowerCase()}(s)}(e,t,n):crypto.randomUUID()}function i(e){if(e.data&&Object.keys(e.data).length>0){const t=document.createComment("Nxis.ai"),n=document.createComment("End Nxis.ai"),r=document.createElement("script");r.setAttribute("type","application/ld+json"),r.textContent=JSON.stringify(e.data),document.head.appendChild(r),r.before(t),r.after(n)}}function a(t){t instanceof e?console.warn(JSON.parse(t.message).message):console.error(`Unable to retrieve structured data for ${window.location.href}`)}!function(){window.NXIS_SESSION_ID=s();const t=process.env.NXIS_API_HOST,r=window.__NXIS_CONFIG__,o=new Headers,c=new URL(`${t}/v1/nxis:inject`);c.search=function(e){var t,n;const r=new URLSearchParams;return(null===(t=e.organization_id)||void 0===t?void 0:t.value)&&r.append("organization_id",e.organization_id.value),(null===(n=e.site_id)||void 0===n?void 0:n.value)&&r.append("site_id",e.site_id.value),r.append("uri",e.uri.href),e.referrer&&r.append("referrer",e.referrer),e.session&&r.append("session",e.session),r}({organization_id:{value:null==r?void 0:r.org_id},site_id:{value:null==r?void 0:r.site_id},uri:new URL(window.location.href),referrer:document.referrer,session:window.NXIS_SESSION_ID}).toString(),(null==r?void 0:r.sub_id)&&o.set("X-NXIS-SUBSCRIPTION",r.sub_id),o.set("Content-Type","application/json"),o.set("User-Agent",navigator.userAgent),fetch(`${c}`,{headers:o}).then(t=>async function(t,r){const o=null!=r?r:"unknown";if(200===t.status){const n=await t.json();if((null==n?void 0:n.data)&&Object.keys(n.data).length>0)return n;throw new e(JSON.stringify({message:`No Nxis resource found for ${o}`}))}const s=await async function(e){try{return await e.json()}catch(e){return null}}(t);if(void 0!==(null==s?void 0:s.code)&&(null==s?void 0:s.message))throw n.fromResponse(t.status,s);switch(t.status){case 500:throw new n(13,500,"An internal server error occurred, please contact technical support.");case 502:throw new n(13,502,"An internal server error occurred, please contact technical support.");case 503:throw new n(8,503,"Service is temporarily unavailable, please try again later.");default:throw new n(13,t.status,`Unexpected response status: ${t.status}`)}}(t,window.location.href)).then(i).catch(a)}();
1
+ !function(){const e={NXIS_API_HOST:"https://proxy.nxis.ai"};try{if(process)return process.env=Object.assign({},process.env),void Object.assign(process.env,e)}catch(e){}globalThis.process={env:e}}();class e extends Error{constructor(t){super(t),Object.setPrototypeOf(this,e.prototype)}}var t;!function(e){e[e.OK=0]="OK",e[e.INVALID_ARGUMENT=3]="INVALID_ARGUMENT",e[e.NOT_FOUND=5]="NOT_FOUND",e[e.PERMISSION_DENIED=7]="PERMISSION_DENIED",e[e.RESOURCE_EXHAUSTED=8]="RESOURCE_EXHAUSTED",e[e.INTERNAL=13]="INTERNAL",e[e.UNAUTHENTICATED=16]="UNAUTHENTICATED"}(t||(t={})),t.OK,t.INVALID_ARGUMENT,t.NOT_FOUND,t.PERMISSION_DENIED,t.RESOURCE_EXHAUSTED,t.INTERNAL,t.UNAUTHENTICATED;class n extends Error{constructor(e,t,o){super(o),this.code=e,this.httpStatus=t,this.name="ProxyError",Object.setPrototypeOf(this,n.prototype)}get isRetryable(){return this.code===t.RESOURCE_EXHAUSTED||this.code===t.INTERNAL}get isAuthError(){return this.code===t.UNAUTHENTICATED||this.code===t.PERMISSION_DENIED}static fromResponse(e,t){return new n(t.code,e,t.message)}}function o(e){return null===e?"null":Array.isArray(e)?"array":typeof e}function r(e,t){if(e===t)return!0;if(o(e)!==o(t))return!1;if(Array.isArray(e)&&Array.isArray(t))return e.length===t.length&&e.every((e,n)=>r(e,t[n]));if("object"===o(e)){const n=e,o=t,i=Object.keys(n);return i.length===Object.keys(o).length&&i.every(e=>r(n[e],o[e]))}return!1}function i(e,t,n="$"){if(!t||"object"!=typeof t)return[];const a=[];if(void 0===t.const||r(e,t.const)||a.push({path:n,message:`must equal const ${JSON.stringify(t.const)}`}),t.enum&&!t.enum.some(t=>r(e,t))&&a.push({path:n,message:`must be one of ${JSON.stringify(t.enum)}`}),void 0!==t.type){const r=Array.isArray(t.type)?t.type:[t.type];if(!r.some(t=>function(e,t){switch(t){case"object":return"object"===o(e);case"array":return Array.isArray(e);case"string":return"string"==typeof e;case"number":return"number"==typeof e&&Number.isFinite(e);case"integer":return"number"==typeof e&&Number.isInteger(e);case"boolean":return"boolean"==typeof e;case"null":return null===e;default:return!0}}(e,t)))return a.push({path:n,message:`expected type ${r.join(" | ")}, got ${o(e)}`}),a}if("string"==typeof e&&(void 0!==t.minLength&&e.length<t.minLength&&a.push({path:n,message:`must be at least ${t.minLength} characters`}),void 0!==t.maxLength&&e.length>t.maxLength&&a.push({path:n,message:`must be at most ${t.maxLength} characters`}),void 0!==t.pattern)){let o=null;try{o=new RegExp(t.pattern,"u")}catch{}o&&!o.test(e)&&a.push({path:n,message:`must match pattern ${t.pattern}`})}if("number"==typeof e&&(void 0!==t.minimum&&e<t.minimum&&a.push({path:n,message:`must be >= ${t.minimum}`}),void 0!==t.maximum&&e>t.maximum&&a.push({path:n,message:`must be <= ${t.maximum}`})),Array.isArray(e)&&(void 0!==t.minItems&&e.length<t.minItems&&a.push({path:n,message:`must have at least ${t.minItems} items`}),void 0!==t.maxItems&&e.length>t.maxItems&&a.push({path:n,message:`must have at most ${t.maxItems} items`}),t.items&&e.forEach((e,o)=>{a.push(...i(e,t.items,`${n}[${o}]`))})),"object"===o(e)&&!Array.isArray(e)&&null!==e){const o=e;for(const e of t.required??[])e in o||a.push({path:n,message:`missing required property "${e}"`});const r=t.properties??{};for(const[e,t]of Object.entries(r))e in o&&a.push(...i(o[e],t,`${n}.${e}`));if(!1===t.additionalProperties)for(const e of Object.keys(o))e in r||a.push({path:n,message:`unexpected property "${e}"`});else if(t.additionalProperties&&"object"==typeof t.additionalProperties)for(const e of Object.keys(o))e in r||a.push(...i(o[e],t.additionalProperties,`${n}.${e}`))}if(t.allOf)for(const o of t.allOf)a.push(...i(e,o,n));if(t.anyOf&&t.anyOf.length>0){t.anyOf.some(t=>0===i(e,t,n).length)||a.push({path:n,message:"does not match any schema in anyOf"})}if(t.oneOf&&t.oneOf.length>0){const o=t.oneOf.filter(t=>0===i(e,t,n).length).length;1!==o&&a.push({path:n,message:`must match exactly one schema in oneOf (matched ${o})`})}return a}var a,s,l,u,c,f,d=function(e,t,n,o){if("a"===n&&!o)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!o:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===n?o:"a"===n?o.call(e):o?o.value:t.get(e)},p=function(e,t,n,o,r){if("m"===o)throw new TypeError("Private method is not writable");if("a"===o&&!r)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!r:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===o?r.call(e,n):r?r.value=n:t.set(e,n),n};const m={type:"object"};function h(e,t){if("function"==typeof DOMException)return new DOMException(e,t);const n=new Error(e);return n.name=t,n}function v(e){if(!e||"object"!=typeof e)throw new TypeError("Tool descriptor must be an object.");if("string"!=typeof e.name||0===e.name.length)throw new TypeError("Tool descriptor requires a non-empty string 'name'.");if("string"!=typeof e.description)throw new TypeError(`Tool "${e.name}" requires a string 'description'.`);if("function"!=typeof e.execute)throw new TypeError(`Tool "${e.name}" requires an 'execute' function.`);if(void 0!==e.inputSchema&&"object"!=typeof e.inputSchema)throw new TypeError(`Tool "${e.name}": 'inputSchema' must be an object.`)}class g extends EventTarget{constructor(){super(...arguments),a.add(this),s.set(this,new Map),l.set(this,null),u.set(this,!1)}get ontoolchange(){return d(this,l,"f")}set ontoolchange(e){d(this,l,"f")&&this.removeEventListener("toolchange",d(this,l,"f")),p(this,l,"function"==typeof e?e:null,"f"),d(this,l,"f")&&this.addEventListener("toolchange",d(this,l,"f"))}registerTool(e,t={}){v(e);const{signal:n,exposedTo:o}=t;if(n?.aborted)return;d(this,a,"m",c).call(this,e.name);const r={tool:e,exposedTo:o?[...o]:void 0,pageLevel:!1};if(n){const t=()=>d(this,a,"m",c).call(this,e.name,r,!0);n.addEventListener("abort",t,{once:!0}),r.abortListener={signal:n,listener:t}}d(this,s,"f").set(e.name,r),d(this,a,"m",f).call(this)}provideContext(e={}){const t=e.tools??[];for(const e of t)v(e);if(new Set(t.map(e=>e.name)).size!==t.length)throw new TypeError("provideContext(): duplicate tool names in 'tools'.");for(const[e,t]of d(this,s,"f"))t.pageLevel&&d(this,a,"m",c).call(this,e);for(const e of t){if(d(this,s,"f").has(e.name))throw h(`provideContext(): tool "${e.name}" is already registered via registerTool().`,"InvalidStateError");d(this,s,"f").set(e.name,{tool:e,exposedTo:void 0,pageLevel:!0})}d(this,a,"m",f).call(this)}async getTools(e={}){e.fromOrigins;const t="undefined"!=typeof location?location.origin:void 0;return[...d(this,s,"f").values()].map(({tool:e})=>{const n={name:e.name,description:e.description,inputSchema:structuredClone(e.inputSchema??m)};return e.annotations&&(n.annotations=structuredClone(e.annotations)),t&&(n.origin=t),n})}async executeTool(e,t,n={}){const o=d(this,s,"f").get(e);if(!o)throw h(`No tool registered with name "${e}".`,"NotFoundError");if(n.signal?.aborted)throw h("Tool execution was aborted.","AbortError");let r;if(null==t||""===t)r={};else if("string"==typeof t)try{r=JSON.parse(t)}catch(t){throw new TypeError(`executeTool("${e}"): input is not valid JSON: ${t.message}`)}else r=t;if(null===r||"object"!=typeof r||Array.isArray(r))throw new TypeError(`executeTool("${e}"): input must be a JSON object.`);const a=i(r,o.tool.inputSchema??m);if(a.length>0){const t=a.map(e=>`${e.path}: ${e.message}`).join("; ");throw new TypeError(`executeTool("${e}"): invalid input — ${t}`)}const l=new AbortController,u=n.signal,c=()=>l.abort(u?.reason);u?.addEventListener("abort",c,{once:!0});try{const t=await o.tool.execute(r,{signal:l.signal,toolName:e});if(u?.aborted)throw h("Tool execution was aborted.","AbortError");return function(e){return null==e?{content:[]}:"string"==typeof e?{content:[{type:"text",text:e}]}:"object"==typeof e&&Array.isArray(e.content)?e:{content:[{type:"text",text:JSON.stringify(e)}]}}(t)}catch(e){if(u?.aborted||e instanceof Error&&"AbortError"===e.name)throw h("Tool execution was aborted.","AbortError");return function(e){return{isError:!0,content:[{type:"text",text:e instanceof Error?`${e.name}: ${e.message}`:String(e)}]}}(e)}finally{u?.removeEventListener("abort",c)}}unregisterTool(e){const t=d(this,a,"m",c).call(this,e);return t&&d(this,a,"m",f).call(this),t}clearContext(){if(0!==d(this,s,"f").size){for(const e of[...d(this,s,"f").keys()])d(this,a,"m",c).call(this,e);d(this,a,"m",f).call(this)}}}function y(e,t){Object.defineProperty(e,"modelContext",{value:t,writable:!1,enumerable:!0,configurable:!0})}s=new WeakMap,l=new WeakMap,u=new WeakMap,a=new WeakSet,c=function(e,t,n=!1){const o=d(this,s,"f").get(e);return!!o&&((!t||o===t)&&(o.abortListener&&o.abortListener.signal.removeEventListener("abort",o.abortListener.listener),d(this,s,"f").delete(e),n&&d(this,a,"m",f).call(this),!0))},f=function(){d(this,u,"f")||(p(this,u,!0,"f"),queueMicrotask(()=>{p(this,u,!1,"f"),this.dispatchEvent(new Event("toolchange"))}))};const b=["AskAction","SearchAction","SubscribeAction","RegisterAction","BuyAction","OrderAction","AddAction","ScheduleAction","ReserveAction","RsvpAction","ApplyAction","QuoteAction","DownloadAction","ReviewAction","CommentAction","TrackAction","CommunicateAction","InteractAction"];const w="https://nxis.ai/ns#";function E(e,t,n){var o;return null!==(o=e[t])&&void 0!==o?o:e[n]}function x(e,t,n){const o=E(e,t,n);return Array.isArray(o)?o:[]}function A(e){if(Array.isArray(e)){const t=[];for(const n of e)t.push(...A(n));return t}if(e&&"object"==typeof e){const t=e;if(Array.isArray(t["@graph"])){const e=[];for(const n of t["@graph"])e.push(...A(n));return e}return[t]}return[]}function N(e){if(!e||"object"!=typeof e)return null;const t=e,n="string"==typeof t["@type"]?t["@type"]:null;if(!n)return null;if(!n.endsWith("Action")&&!function(e){return b.includes(e)}(n))return null;const o="string"==typeof t["@id"]?t["@id"]:null,r="string"==typeof t.name?t.name:null,i=function(e){if(!e||"object"!=typeof e)return null;const t=e,n="string"==typeof t.httpMethod?t.httpMethod.toUpperCase():null;if(!n)return null;const o={"@type":"EntryPoint",httpMethod:n};"string"==typeof t.encodingType&&(o.encodingType=t.encodingType);"string"==typeof t.urlTemplate&&(o.urlTemplate=t.urlTemplate);const r=E(t,"nxis:targetKind",w+"targetKind");"selfPage"!==r&&"clientSide"!==r||(o["nxis:targetKind"]=r);const i=E(t,"nxis:pageUrl",w+"pageUrl");"string"==typeof i&&(o["nxis:pageUrl"]=i);const a=E(t,"nxis:jsHandled",w+"jsHandled");!0===a&&(o["nxis:jsHandled"]=!0);return o}(t.target);if(!o||!r||!i)return null;const a=x(t,"nxis:input",w+"input").map($).filter(e=>null!==e),s="string"==typeof t.description?t.description:function(e,t,n){const o=e.replace(/Action$/,"").toLowerCase(),r=n.map(e=>e.valueName).join(", ");if(0===n.length)return`Trigger the ${o} action "${t}".`;return`Fill the ${o} form "${t}" with fields: ${r}.`}(n,r,a);return{"@context":t["@context"],"@type":n,"@id":o,name:r,description:s,target:i,input:a.length>0?a:void 0,"nxis:extractedAt":E(t,"nxis:extractedAt",w+"extractedAt")}}function $(e){if(!e||"object"!=typeof e)return null;const t=e,n="string"==typeof t.valueName?t.valueName:null;if(!n)return null;const o=E(t,"nxis:valueType",w+"valueType"),r={"@type":"PropertyValueSpecification",valueName:n,"nxis:valueType":"string"==typeof o?o:"string"};"string"==typeof t.description&&(r.description=t.description),!0===t.valueRequired&&(r.valueRequired=!0),"string"==typeof t.defaultValue&&(r.defaultValue=t.defaultValue),"number"==typeof t.valueMinLength&&(r.valueMinLength=t.valueMinLength),"number"==typeof t.valueMaxLength&&(r.valueMaxLength=t.valueMaxLength),"number"==typeof t.minValue&&(r.minValue=t.minValue),"number"==typeof t.maxValue&&(r.maxValue=t.maxValue),"number"==typeof t.stepValue&&(r.stepValue=t.stepValue),"string"==typeof t.valuePattern&&(r.valuePattern=t.valuePattern),!0===t.multipleValues&&(r.multipleValues=!0),!0===t.readonlyValue&&(r.readonlyValue=!0);const i=x(t,"nxis:valueOption",w+"valueOption");if(i.length>0){const e=i.filter(e=>e&&"object"==typeof e&&"string"==typeof e.value).map(e=>{const t=e;return{"@type":"nxis:ValueOption",value:t.value,name:"string"==typeof t.name?t.name:void 0}});e.length>0&&(r["nxis:valueOption"]=e)}return r}function T(e){switch(e){case"string":case"date":case"file":default:return"string";case"number":return"number";case"integer":return"integer";case"boolean":return"boolean"}}function L(e){const t={type:T(e["nxis:valueType"])},n=function(e){if("date"===e)return"date"}(e["nxis:valueType"]);if(n&&(t.format=n),e.description&&(t.description=e.description),void 0!==e.defaultValue&&(t.default=function(e,t){switch(t){case"number":case"integer":{const t=Number(e);return isNaN(t)?e:t}case"boolean":return"true"===e||"1"===e;default:return e}}(e.defaultValue,e["nxis:valueType"])),void 0!==e.valueMinLength&&(t.minLength=e.valueMinLength),void 0!==e.valueMaxLength&&(t.maxLength=e.valueMaxLength),e.valuePattern){const n=e.valuePattern,o=n.startsWith("^")&&n.endsWith("$")?n:`^${n}$`;t.pattern=o}void 0!==e.minValue&&(t.minimum=e.minValue),void 0!==e.maxValue&&(t.maximum=e.maxValue),void 0!==e.stepValue&&(t.multipleOf=e.stepValue),e.readonlyValue&&(t.readOnly=!0);const o=e["nxis:valueOption"];if(o&&o.length>0){t.enum=o.map(e=>e.value);if(o.some(e=>void 0!==e.name&&e.name!==e.value)){const e=o.filter(e=>e.name&&e.name!==e.value).map(e=>`${e.value} = "${e.name}"`).join(", "),n=t.description?`${t.description}. `:"";t.description=`${n}Values: ${e}`}}return t}function O(e){const t=Array.from(document.querySelectorAll("form"));if(0===t.length)return null;const n=function(e){return e.input?e.input.map(e=>e.valueName):[]}(e);let o;if(o=!e.target.urlTemplate?t:function(e,t,n){const o=function(e){const t=e.replace(/\{[^}]*\}/g,"");try{return new URL(t,window.location.href)}catch(e){return null}}(t);return e.filter(e=>{if((e.method||"GET").toUpperCase()!==n.toUpperCase())return!1;if(!o)return!0;const t=function(e){const t=e.getAttribute("action");if(!t||"#"===t||t.startsWith("#")||""===t)return window.location.href.split("#")[0].split("?")[0];if(t.startsWith("javascript:"))return window.location.href.split("#")[0].split("?")[0];try{return new URL(t,window.location.href).href}catch(e){return t}}(e);return function(e,t){let n;try{n=new URL(e,window.location.href)}catch(e){return!1}if(n.origin!==t.origin)return!1;const o=n.pathname.replace(/\/+$/,""),r=t.pathname.replace(/\/+$/,"");return""===r||(o===r||o.startsWith(r+"/")||r.startsWith(o+"/"))}(t,o)})}(t,e.target.urlTemplate,e.target.httpMethod),0===o.length&&(o=t),1===o.length)return o[0];const r=o.map(e=>({form:e,controlNames:S(e)})).filter(({controlNames:e})=>n.every(t=>e.has(t)));return 0===r.length?(console.warn(`[nxis/webmcp] No field-set match for action "${e.name}". Expected fields: [${n.join(", ")}]. Falling back to first candidate.`),o[0]):1===r.length?r[0].form:(r.sort((e,t)=>e.controlNames.size-t.controlNames.size),r[0].controlNames.size===r[1].controlNames.size?(console.warn(`[nxis/webmcp] Ambiguous form match for action "${e.name}". ${r.length} forms have identical field sets. Returning null.`),null):r[0].form)}function S(e){const t=new Set,n=e.elements;for(let e=0;e<n.length;e++){const o=n[e];o.name&&t.add(o.name)}return t}function k(e,t){var n;return(null!==(n=e.input)&&void 0!==n?n:[]).map(e=>function(e,t){var n;const o=t=>({valueName:e.valueName,pvs:e,strategy:t}),r=function(e,t){const n=[],o=e.elements;for(let e=0;e<o.length;e++){const r=o[e];r.name===t&&n.push(r)}return n}(t,e.valueName),i=null!==(n=r[0])&&void 0!==n?n:null;if(!i)return o({kind:"skip",reason:`No control found with name="${e.valueName}"`});if(i instanceof HTMLInputElement&&"radio"===i.type){const t=r.filter(e=>e instanceof HTMLInputElement&&"radio"===e.type&&!e.disabled);return 0===t.length?o({kind:"skip",reason:`Radio group "${e.valueName}" has no enabled options`}):o({kind:"radio",name:e.valueName,elements:t})}if(i instanceof HTMLInputElement&&"checkbox"===i.type){const t=r.filter(e=>e instanceof HTMLInputElement&&"checkbox"===e.type&&!e.disabled);return 0===t.length?o({kind:"skip",reason:`Checkbox "${e.valueName}" is disabled`}):t.length>1?o({kind:"checkboxGroup",name:e.valueName,elements:t}):o({kind:"checked",element:t[0]})}if(!0===i.readOnly||null!==i.getAttribute("readonly"))return o({kind:"skip",reason:`Control "${e.valueName}" is readonly in the DOM`});if(i.disabled)return o({kind:"skip",reason:`Control "${e.valueName}" is disabled`});if(i instanceof HTMLSelectElement){const e=[];for(let t=0;t<i.options.length;t++){const n=i.options[t];n.value&&e.push(n.value)}return o(i.multiple?{kind:"multiSelect",element:i,allowedValues:e}:{kind:"select",element:i,allowedValues:e})}if(i instanceof HTMLTextAreaElement)return o({kind:"value",element:i});if(i instanceof HTMLInputElement)return o({kind:"value",element:i});return o({kind:"skip",reason:`Unsupported control type for "${e.valueName}"`})}(e,t))}async function j(e,t){var n,o;const r=function(e){const t=e.target["nxis:pageUrl"];if(!t)return null;try{const n=new URL(t),o=new URL(window.location.href);if(n.origin!==o.origin||n.pathname!==o.pathname)return`Action "${e.name}" was extracted from ${n.origin}${n.pathname} but the current page is ${o.origin}${o.pathname}. The form may have moved or changed.`}catch(e){return null}return null}(e),i=O(e);if(!i){const t=`Form not found for action "${e.name}". The page may have changed since the tool was registered.`,n={success:!1,submitted:!1,message:r?`Warning: ${r} ${t}`:t,filled:[],skipped:[]};return r&&(n.stale=!0),JSON.stringify(n)}const a=k(e,i),s=null!==(n=e.input)&&void 0!==n?n:[],l=[],u=[];let c=!1;for(const e of s)!e.valueRequired||void 0!==t[e.valueName]&&null!==t[e.valueName]||(c=!0,u.push({field:e.valueName,reason:`Required field '${e.valueName}' was not provided. Expected type: ${e["nxis:valueType"]}.`}));for(const e of a){const n=t[e.valueName];if(null==n){u.some(t=>t.field===e.valueName)||u.push({field:e.valueName,reason:"No value provided (optional field)."});continue}const o=I(e,n);!1===o.ok?u.push({field:e.valueName,reason:o.reason}):l.push(e.valueName)}await new Promise(e=>setTimeout(e,0));for(const e of a)if("value"===e.strategy.kind&&l.includes(e.valueName)){const n=e.strategy.element,r=M(null!==(o=t[e.valueName])&&void 0!==o?o:"");n.value!==r&&(u.push({field:e.valueName,reason:`Value was set to "${r}" but the DOM now shows "${n.value}". A framework may have reverted it.`}),l.splice(l.indexOf(e.valueName),1))}const f=l.length>0&&!c;let d=!1;f&&(d=function(e){try{return"function"==typeof e.requestSubmit?e.requestSubmit():e.submit(),!0}catch(e){return console.warn("[nxis/webmcp] Form submission failed:",e),!1}}(i));const p={success:f,submitted:d,message:C(l,u,d,r),filled:l,skipped:u};return r&&(p.stale=!0),JSON.stringify(p)}function C(e,t,n,o){const r=[];if(o&&r.push(`Warning: ${o}`),0===e.length&&0===t.length)return r.push("No fields were filled. Verify field names match the input schema."),r.join(" ");if(e.length>0&&r.push(`Filled ${e.length} field(s): ${e.join(", ")}.`),t.length>0){const e=t.map(e=>`${e.field} (${e.reason})`).join("; ");r.push(`Skipped ${t.length}: ${e}`)}return n?r.push("Form submitted."):e.length>0&&r.push("Form was NOT submitted due to missing required fields or fill errors."),r.join(" ")}function I(e,t){const{strategy:n}=e;switch(n.kind){case"skip":return{ok:!1,reason:n.reason};case"value":return function(e,t){var n;const o=null===(n=Object.getOwnPropertyDescriptor(e instanceof HTMLTextAreaElement?HTMLTextAreaElement.prototype:HTMLInputElement.prototype,"value"))||void 0===n?void 0:n.set;o?o.call(e,t):e.value=t;return e.dispatchEvent(new Event("input",{bubbles:!0})),e.dispatchEvent(new Event("change",{bubbles:!0})),{ok:!0}}(n.element,M(t));case"checked":return function(e,t){return e.checked=function(e){if("boolean"==typeof e)return e;if("number"==typeof e)return 0!==e;if("string"==typeof e){const t=e.trim().toLowerCase();return""!==t&&"false"!==t&&"0"!==t&&"off"!==t&&"no"!==t}return Boolean(e)}(t),e.dispatchEvent(new Event("change",{bubbles:!0})),{ok:!0}}(n.element,t);case"radio":return function(e,t){var n,o;const r=String(t),i=null!==(o=null!==(n=e.find(e=>e.value===r))&&void 0!==n?n:e.find(e=>e.value.toLowerCase()===r.toLowerCase()))&&void 0!==o?o:e.find(e=>function(e){var t;if(e.labels&&e.labels.length>0)return(null!==(t=e.labels[0].textContent)&&void 0!==t?t:"").trim();return e.value}(e).toLowerCase()===r.toLowerCase());if(!i){const t=e.map(e=>e.value).join(", ");return{ok:!1,reason:`Value "${r}" is not an allowed option for radio "${e[0].name}". Allowed: [${t}].`}}return i.checked=!0,i.dispatchEvent(new Event("input",{bubbles:!0})),i.dispatchEvent(new Event("change",{bubbles:!0})),{ok:!0}}(n.elements,t);case"checkboxGroup":return function(e,t){const n=(Array.isArray(t)?t:[t]).map(e=>String(e)),o=n.map(e=>e.toLowerCase());let r=!1;for(const t of e){const e=n.includes(t.value)||o.includes(t.value.toLowerCase());t.checked=e,e&&(r=!0),t.dispatchEvent(new Event("change",{bubbles:!0}))}if(!r){const t=e.map(e=>e.value).join(", ");return{ok:!1,reason:`None of [${n.join(", ")}] match checkbox group "${e[0].name}". Allowed: [${t}].`}}return{ok:!0}}(n.elements,t);case"select":return function(e,t,n){const o=R(e,t,n);if(null===o)return{ok:!1,reason:`Value "${t}" is not an allowed option for "${e.name}". Allowed: [${n.join(", ")}].`};return e.value=o,e.dispatchEvent(new Event("input",{bubbles:!0})),e.dispatchEvent(new Event("change",{bubbles:!0})),{ok:!0}}(n.element,String(t),n.allowedValues);case"multiSelect":return function(e,t,n){const o=(Array.isArray(t)?t:[t]).map(e=>String(e)),r=new Set,i=[];for(const t of o){const o=R(e,t,n);null===o?i.push(t):r.add(o)}if(0===r.size)return{ok:!1,reason:`None of [${o.join(", ")}] are allowed options for "${e.name}". Allowed: [${n.join(", ")}].`};for(const t of Array.from(e.options))t.selected=r.has(t.value);if(e.dispatchEvent(new Event("input",{bubbles:!0})),e.dispatchEvent(new Event("change",{bubbles:!0})),i.length>0)return{ok:!1,reason:`Selected [${Array.from(r).join(", ")}] but [${i.join(", ")}] are not allowed options for "${e.name}". Allowed: [${n.join(", ")}].`};return{ok:!0}}(n.element,t,n.allowedValues);default:return{ok:!1,reason:`Unsupported fill strategy for field "${e.valueName}".`}}}function M(e){return Array.isArray(e)?e.map(e=>String(e)).join(","):String(e)}function R(e,t,n){if(n.includes(t))return t;const o=n.find(e=>e.toLowerCase()===t.toLowerCase());if(o)return o;for(const n of Array.from(e.options))if(n.text.toLowerCase()===t.toLowerCase())return n.value;return null}function _(e){var t;const n=!1!==(null==e?void 0:e.observe),o=null!==(t=null==e?void 0:e.doc)&&void 0!==t?t:"undefined"!=typeof document?document:void 0;if(!o)return console.warn("[nxis/webmcp] No document available (not running in a browser). Tools will not be registered."),()=>{};!function(e={}){const{force:t=!1,installNavigatorAlias:n=!0}=e,o="undefined"!=typeof document&&document.modelContext||"undefined"!=typeof navigator&&navigator.modelContext||void 0;if(o&&!t)return o;const r=new g;"undefined"!=typeof document&&y(document,r),n&&"undefined"!=typeof navigator&&y(navigator,r)}();const r=function(e){var t,n;const o=null!==(n=null===(t=e.defaultView)||void 0===t?void 0:t.navigator)&&void 0!==n?n:"undefined"!=typeof navigator?navigator:void 0;return e.modelContext||(null==o?void 0:o.modelContext)||null}(o);if(!r)return console.warn("[nxis/webmcp] WebMCP API not available — document.modelContext is undefined. Tools will not be registered."),()=>{};const i=new AbortController;(null==e?void 0:e.signal)&&(e.signal.aborted?i.abort():e.signal.addEventListener("abort",()=>i.abort()));const a={ids:new Set,names:new Set};U(o,r,i,a);let s=null;return n&&(s=function(e,t,n,o){const r=new MutationObserver(r=>{(function(e){for(const t of e)for(const e of Array.from(t.addedNodes)){if(P(e))return!0;if(e instanceof Element&&e.querySelector('script[type="application/ld+json"]'))return!0}return!1})(r)&&U(e,t,n,o)});return n.signal.addEventListener("abort",()=>r.disconnect()),r.observe(e.documentElement,{childList:!0,subtree:!0}),r}(o,r,i,a)),()=>{i.abort(),s&&(s.disconnect(),s=null)}}function U(e,t,n,o){const r=function(e=("undefined"!=typeof document?document:void 0)){const t=[];if(!e)return t;const n=e.querySelectorAll('script[type="application/ld+json"]');for(const e of Array.from(n)){const n=e.textContent;if(!n)continue;let o;try{o=JSON.parse(n)}catch(e){continue}const r=A(o);for(const e of r){const n=N(e);n&&t.push(n)}}return t}(e);let i=0;for(const e of r)if(!o.ids.has(e["@id"]))try{V(e,t,n,o),o.ids.add(e["@id"]),i++}catch(t){console.warn(`[nxis/webmcp] Failed to register tool for action "${e.name}":`,t)}i>0&&console.info(`[nxis/webmcp] Registered ${i} tool(s) (${o.ids.size} total) from ${r.length} action(s).`)}function V(e,t,n,o){const r=function(e){var t;const n={},o=[],r=null!==(t=e.input)&&void 0!==t?t:[];for(const e of r){const t=L(e);if(e.multipleValues){const o={type:"array",items:t};void 0!==e.valueMinLength&&(o.minItems=e.valueMinLength,delete t.minLength),void 0!==e.valueMaxLength&&(o.maxItems=e.valueMaxLength,delete t.maxLength),e.description&&(o.description=e.description,delete t.description),n[e.valueName]=o}else n[e.valueName]=t;e.valueRequired&&o.push(e.valueName)}const i={type:"object",properties:n};return o.length>0&&(i.required=o),i}(e),i=function(e,t){if(!t.has(e))return e;for(let n=2;;n++){const o=`_${n}`,r=e.slice(0,64-o.length)+o;if(!t.has(r))return r}}(function(e){return e.toLowerCase().replace(/[\s-]+/g,"_").replace(/[^a-z0-9_]/g,"_").replace(/_+/g,"_").replace(/^_|_$/g,"").slice(0,64)||"unnamed_action"}(e.name),o.names);t.registerTool({name:i,description:e.description,inputSchema:r,annotations:{readOnlyHint:!1,untrustedContentHint:!0},execute:async t=>j(e,t)},{signal:n.signal}),o.names.add(i)}function P(e){return e instanceof HTMLScriptElement&&"application/ld+json"===e.type}function D(t){t instanceof e?console.warn(JSON.parse(t.message).message):console.error(`Unable to retrieve structured data for ${window.location.href}`)}!function(){var t,o;const r=window.__NXIS_CONFIG__,i=null!==(o=null===(t=null==r?void 0:r.webmcp)||void 0===t?void 0:t.mode)&&void 0!==o?o:"browser";if("ssr"===i)return void _();const a=process.env.NXIS_API_HOST,s=new Headers,l=new URL(`${a}/v1/nxis:inject`);l.search=function(e){var t,n;const o=new URLSearchParams;return(null===(t=e.organization_id)||void 0===t?void 0:t.value)&&o.append("organization_id",e.organization_id.value),(null===(n=e.site_id)||void 0===n?void 0:n.value)&&o.append("site_id",e.site_id.value),o.append("uri",e.uri.href),e.referrer&&o.append("referrer",e.referrer),o}({organization_id:{value:null==r?void 0:r.org_id},site_id:{value:null==r?void 0:r.site_id},uri:new URL(window.location.href),referrer:document.referrer}).toString(),(null==r?void 0:r.sub_id)&&s.set("X-NXIS-SUBSCRIPTION",r.sub_id),s.set("Content-Type","application/json"),s.set("User-Agent",navigator.userAgent),fetch(`${l}`,{headers:s}).then(t=>async function(t,o){const r=null!=o?o:"unknown";if(200===t.status){const n=await t.json();if((null==n?void 0:n.data)&&Object.keys(n.data).length>0)return n;throw new e(JSON.stringify({message:`No Nxis resource found for ${r}`}))}const i=await async function(e){try{return await e.json()}catch(e){return null}}(t);if(void 0!==(null==i?void 0:i.code)&&(null==i?void 0:i.message))throw n.fromResponse(t.status,i);switch(t.status){case 500:throw new n(13,500,"An internal server error occurred, please contact technical support.");case 502:throw new n(13,502,"An internal server error occurred, please contact technical support.");case 503:throw new n(8,503,"Service is temporarily unavailable, please try again later.");default:throw new n(13,t.status,`Unexpected response status: ${t.status}`)}}(t,window.location.href)).then(e=>{!function(e){if(e.data&&Object.keys(e.data).length>0){const t=document.createComment("Nxis.ai"),n=document.createComment("End Nxis.ai"),o=document.createElement("script");o.setAttribute("type","application/ld+json"),o.textContent=JSON.stringify(e.data),document.head.appendChild(o),o.before(t),o.after(n)}}(e),"browser"===i&&_()}).catch(D)}();