@nxisai/nxis 2.2.1 → 2.2.3
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.
- package/README.md +143 -68
- package/build/nxis.js +1 -1
- package/build/webmcp.js +1 -1
- package/package.json +4 -2
- package/src/webmcp/parse.ts +64 -29
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
|
-
|
|
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
|
+
[](https://www.npmjs.com/package/@nxisai/nxis) [](https://www.npmjs.com/package/@nxisai/nxis) [](https://github.com/nxisai/nxis-js/blob/main/LICENSE)
|
|
12
12
|
|
|
13
|
-
Nxis.js
|
|
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
|
|
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
|
-
###
|
|
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
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
|
|
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,6 +416,47 @@ app.get('*', async (req, res) => {
|
|
|
365
416
|
app.listen(3000);
|
|
366
417
|
```
|
|
367
418
|
|
|
419
|
+
### Adding Nxis SSR with Claude Code
|
|
420
|
+
|
|
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:
|
|
422
|
+
|
|
423
|
+
```
|
|
424
|
+
Install the @nxisai/nxis package and add server-side structured data injection to my
|
|
425
|
+
Next.js App Router project. Here's what I need:
|
|
426
|
+
|
|
427
|
+
1. Install the @nxisai/nxis npm package
|
|
428
|
+
2. Add NXIS_API_HOST, NXIS_CLIENT_ID, and NXIS_CLIENT_SECRET to my .env.local
|
|
429
|
+
3. Create a shared NxisClient instance using `NxisClient` from `@nxisai/nxis/ssr`
|
|
430
|
+
initialized with the OAuth client credentials from environment variables
|
|
431
|
+
4. Update my root layout (or individual pages) to:
|
|
432
|
+
- Import `headers` from `next/headers`
|
|
433
|
+
- Derive the page URI from the incoming request's `host` and
|
|
434
|
+
`x-forwarded-proto` headers
|
|
435
|
+
- Pass the `user-agent` header for bot detection
|
|
436
|
+
- Inject the returned JSON-LD as a <script type="application/ld+json"> tag
|
|
437
|
+
|
|
438
|
+
Here's the API reference:
|
|
439
|
+
|
|
440
|
+
import { NxisClient } from '@nxisai/nxis/ssr';
|
|
441
|
+
|
|
442
|
+
const nxis = new NxisClient({
|
|
443
|
+
clientId: process.env.NXIS_CLIENT_ID!,
|
|
444
|
+
clientSecret: process.env.NXIS_CLIENT_SECRET!,
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
const jsonLd: Record<string, unknown> = await nxis.getStructuredData({
|
|
448
|
+
uri: pageUri,
|
|
449
|
+
siteId: process.env.NXIS_SITE_ID!,
|
|
450
|
+
userAgent: headersList.get('user-agent') ?? undefined,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
Tokens are managed automatically — no manual token handling needed.
|
|
454
|
+
Make sure JSON-LD is present in the initial HTML response so it's visible
|
|
455
|
+
to search engines, LLMs, and AI agents that don't render JavaScript.
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
This will guide Claude Code to install the package, configure environment variables, and wire up SSR structured data injection across your pages.
|
|
459
|
+
|
|
368
460
|
---
|
|
369
461
|
|
|
370
462
|
## WebMCP — AI Agent Tool Registration
|
|
@@ -479,6 +571,32 @@ const cleanup = registerActions();
|
|
|
479
571
|
const cleanup = registerActions({ observe: false });
|
|
480
572
|
```
|
|
481
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
|
+
|
|
482
600
|
### Supported Action Types
|
|
483
601
|
|
|
484
602
|
The backend classifier maps forms to schema.org Action subtypes:
|
|
@@ -549,46 +667,3 @@ import type {
|
|
|
549
667
|
} from '@nxisai/nxis/webmcp';
|
|
550
668
|
```
|
|
551
669
|
|
|
552
|
-
---
|
|
553
|
-
|
|
554
|
-
## Adding Nxis SSR with Claude Code
|
|
555
|
-
|
|
556
|
-
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:
|
|
557
|
-
|
|
558
|
-
```
|
|
559
|
-
Install the @nxisai/nxis package and add server-side structured data injection to my
|
|
560
|
-
Next.js App Router project. Here's what I need:
|
|
561
|
-
|
|
562
|
-
1. Install the @nxisai/nxis npm package
|
|
563
|
-
2. Add NXIS_API_HOST, NXIS_CLIENT_ID, and NXIS_CLIENT_SECRET to my .env.local
|
|
564
|
-
3. Create a shared NxisClient instance using `NxisClient` from `@nxisai/nxis/ssr`
|
|
565
|
-
initialized with the OAuth client credentials from environment variables
|
|
566
|
-
4. Update my root layout (or individual pages) to:
|
|
567
|
-
- Import `headers` from `next/headers`
|
|
568
|
-
- Derive the page URI from the incoming request's `host` and
|
|
569
|
-
`x-forwarded-proto` headers
|
|
570
|
-
- Pass the `user-agent` header for bot detection
|
|
571
|
-
- Inject the returned JSON-LD as a <script type="application/ld+json"> tag
|
|
572
|
-
|
|
573
|
-
Here's the API reference:
|
|
574
|
-
|
|
575
|
-
import { NxisClient } from '@nxisai/nxis/ssr';
|
|
576
|
-
|
|
577
|
-
const nxis = new NxisClient({
|
|
578
|
-
clientId: process.env.NXIS_CLIENT_ID!,
|
|
579
|
-
clientSecret: process.env.NXIS_CLIENT_SECRET!,
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
const jsonLd: Record<string, unknown> = await nxis.getStructuredData({
|
|
583
|
-
uri: pageUri,
|
|
584
|
-
siteId: process.env.NXIS_SITE_ID!,
|
|
585
|
-
userAgent: headersList.get('user-agent') ?? undefined,
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
Tokens are managed automatically — no manual token handling needed.
|
|
589
|
-
Make sure JSON-LD is present in the initial HTML response so it's visible
|
|
590
|
-
to search engines, LLMs, and AI agents that don't render JavaScript.
|
|
591
|
-
```
|
|
592
|
-
|
|
593
|
-
This will guide Claude Code to install the package, configure environment variables, and wire up SSR structured data injection across your pages.
|
|
594
|
-
|
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,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)}();
|
|
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,d,f=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 f(this,l,"f")}set ontoolchange(e){f(this,l,"f")&&this.removeEventListener("toolchange",f(this,l,"f")),p(this,l,"function"==typeof e?e:null,"f"),f(this,l,"f")&&this.addEventListener("toolchange",f(this,l,"f"))}registerTool(e,t={}){v(e);const{signal:n,exposedTo:o}=t;if(n?.aborted)return;f(this,a,"m",c).call(this,e.name);const r={tool:e,exposedTo:o?[...o]:void 0,pageLevel:!1};if(n){const t=()=>f(this,a,"m",c).call(this,e.name,r,!0);n.addEventListener("abort",t,{once:!0}),r.abortListener={signal:n,listener:t}}f(this,s,"f").set(e.name,r),f(this,a,"m",d).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 f(this,s,"f"))t.pageLevel&&f(this,a,"m",c).call(this,e);for(const e of t){if(f(this,s,"f").has(e.name))throw h(`provideContext(): tool "${e.name}" is already registered via registerTool().`,"InvalidStateError");f(this,s,"f").set(e.name,{tool:e,exposedTo:void 0,pageLevel:!0})}f(this,a,"m",d).call(this)}async getTools(e={}){e.fromOrigins;const t="undefined"!=typeof location?location.origin:void 0;return[...f(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=f(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=f(this,a,"m",c).call(this,e);return t&&f(this,a,"m",d).call(this),t}clearContext(){if(0!==f(this,s,"f").size){for(const e of[...f(this,s,"f").keys()])f(this,a,"m",c).call(this,e);f(this,a,"m",d).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=f(this,s,"f").get(e);return!!o&&((!t||o===t)&&(o.abortListener&&o.abortListener.signal.removeEventListener("abort",o.abortListener.listener),f(this,s,"f").delete(e),n&&f(this,a,"m",d).call(this),!0))},d=function(){f(this,u,"f")||(p(this,u,!0,"f"),queueMicrotask(()=>{p(this,u,!1,"f"),this.dispatchEvent(new Event("toolchange"))}))};const w=["AskAction","SearchAction","SubscribeAction","RegisterAction","BuyAction","OrderAction","AddAction","ScheduleAction","ReserveAction","RsvpAction","ApplyAction","QuoteAction","DownloadAction","ReviewAction","CommentAction","TrackAction","CommunicateAction","InteractAction"];const b="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){return E(e,t,"https://schema.org/"+t)}function A(e,t){const n=x(e,t);return"string"==typeof n?n:null}function N(e,t,n){const o=E(e,t,n);return Array.isArray(o)?o:[]}function $(e){if(Array.isArray(e)){const t=[];for(const n of e)t.push(...$(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(...$(n));return e}return[t]}return[]}function T(e){var t;if(!e||"object"!=typeof e)return null;const n=e,o="string"==typeof n["@type"]?n["@type"]:null;if(!o)return null;if(!o.endsWith("Action")&&!function(e){return w.includes(e)}(o))return null;const r="string"==typeof n["@id"]?n["@id"]:null,i=A(n,"name"),a=function(e){var t,n;if(!e||"object"!=typeof e)return null;const o=e,r=null!==(n=null===(t=A(o,"httpMethod"))||void 0===t?void 0:t.toUpperCase())&&void 0!==n?n:null;if(!r)return null;const i={"@type":"EntryPoint",httpMethod:r},a=A(o,"encodingType");null!==a&&(i.encodingType=a);const s=A(o,"urlTemplate");null!==s&&(i.urlTemplate=s);const l=E(o,"nxis:targetKind",b+"targetKind");"selfPage"!==l&&"clientSide"!==l||(i["nxis:targetKind"]=l);const u=E(o,"nxis:pageUrl",b+"pageUrl");"string"==typeof u&&(i["nxis:pageUrl"]=u);const c=E(o,"nxis:jsHandled",b+"jsHandled");!0===c&&(i["nxis:jsHandled"]=!0);return i}(x(n,"target"));if(!r||!i||!a)return null;const s=N(n,"nxis:input",b+"input").map(L).filter(e=>null!==e),l=null!==(t=A(n,"description"))&&void 0!==t?t:function(e,t,n){const o=e.replace(/^.*[/#]/,"").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}.`}(o,i,s);return{"@context":n["@context"],"@type":o,"@id":r,name:i,description:l,target:a,input:s.length>0?s:void 0,"nxis:extractedAt":E(n,"nxis:extractedAt",b+"extractedAt")}}function L(e){if(!e||"object"!=typeof e)return null;const t=e,n=A(t,"valueName");if(!n)return null;const o=E(t,"nxis:valueType",b+"valueType"),r={"@type":"PropertyValueSpecification",valueName:n,"nxis:valueType":"string"==typeof o?o:"string"},i=A(t,"description");null!==i&&(r.description=i),!0===x(t,"valueRequired")&&(r.valueRequired=!0);const a=A(t,"defaultValue");null!==a&&(r.defaultValue=a);const s=e=>{const n=x(t,e);return"number"==typeof n?n:null},l=s("valueMinLength");null!==l&&(r.valueMinLength=l);const u=s("valueMaxLength");null!==u&&(r.valueMaxLength=u);const c=s("minValue");null!==c&&(r.minValue=c);const d=s("maxValue");null!==d&&(r.maxValue=d);const f=s("stepValue");null!==f&&(r.stepValue=f);const p=A(t,"valuePattern");null!==p&&(r.valuePattern=p),!0===x(t,"multipleValues")&&(r.multipleValues=!0),!0===x(t,"readonlyValue")&&(r.readonlyValue=!0);const m=N(t,"nxis:valueOption",b+"valueOption");if(m.length>0){const e=m.filter(e=>e&&"object"==typeof e&&null!==A(e,"value")).map(e=>{var t;const n=e;return{"@type":"nxis:ValueOption",value:A(n,"value"),name:null!==(t=A(n,"name"))&&void 0!==t?t:void 0}});e.length>0&&(r["nxis:valueOption"]=e)}return r}function O(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 S(e){const t={type:O(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 k(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:j(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 j(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 C(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 I(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=k(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=C(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=R(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=_(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 d=l.length>0&&!c;let f=!1;d&&(f=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:d,submitted:f,message:M(l,u,f,r),filled:l,skipped:u};return r&&(p.stale=!0),JSON.stringify(p)}function M(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 R(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,_(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=U(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=U(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 _(e){return Array.isArray(e)?e.map(e=>String(e)).join(","):String(e)}function U(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 V(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};P(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(H(e))return!0;if(e instanceof Element&&e.querySelector('script[type="application/ld+json"]'))return!0}return!1})(r)&&P(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 P(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=$(o);for(const e of r){const n=T(e);n&&t.push(n)}}return t}(e);let i=0;for(const e of r)if(!o.ids.has(e["@id"]))try{D(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 D(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=S(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=>I(e,t)},{signal:n.signal}),o.names.add(i)}function H(e){return e instanceof HTMLScriptElement&&"application/ld+json"===e.type}function q(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 V();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&&V()}).catch(q)}();
|
package/build/webmcp.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){return null===e?"null":Array.isArray(e)?"array":typeof e}function t(n,o){if(n===o)return!0;if(e(n)!==e(o))return!1;if(Array.isArray(n)&&Array.isArray(o))return n.length===o.length&&n.every((e,n)=>t(e,o[n]));if("object"===e(n)){const e=n,r=o,i=Object.keys(e);return i.length===Object.keys(r).length&&i.every(n=>t(e[n],r[n]))}return!1}function n(o,r,i="$"){if(!r||"object"!=typeof r)return[];const a=[];if(void 0===r.const||t(o,r.const)||a.push({path:i,message:`must equal const ${JSON.stringify(r.const)}`}),r.enum&&!r.enum.some(e=>t(o,e))&&a.push({path:i,message:`must be one of ${JSON.stringify(r.enum)}`}),void 0!==r.type){const t=Array.isArray(r.type)?r.type:[r.type];if(!t.some(t=>function(t,n){switch(n){case"object":return"object"===e(t);case"array":return Array.isArray(t);case"string":return"string"==typeof t;case"number":return"number"==typeof t&&Number.isFinite(t);case"integer":return"number"==typeof t&&Number.isInteger(t);case"boolean":return"boolean"==typeof t;case"null":return null===t;default:return!0}}(o,t)))return a.push({path:i,message:`expected type ${t.join(" | ")}, got ${e(o)}`}),a}if("string"==typeof o&&(void 0!==r.minLength&&o.length<r.minLength&&a.push({path:i,message:`must be at least ${r.minLength} characters`}),void 0!==r.maxLength&&o.length>r.maxLength&&a.push({path:i,message:`must be at most ${r.maxLength} characters`}),void 0!==r.pattern)){let e=null;try{e=new RegExp(r.pattern,"u")}catch{}e&&!e.test(o)&&a.push({path:i,message:`must match pattern ${r.pattern}`})}if("number"==typeof o&&(void 0!==r.minimum&&o<r.minimum&&a.push({path:i,message:`must be >= ${r.minimum}`}),void 0!==r.maximum&&o>r.maximum&&a.push({path:i,message:`must be <= ${r.maximum}`})),Array.isArray(o)&&(void 0!==r.minItems&&o.length<r.minItems&&a.push({path:i,message:`must have at least ${r.minItems} items`}),void 0!==r.maxItems&&o.length>r.maxItems&&a.push({path:i,message:`must have at most ${r.maxItems} items`}),r.items&&o.forEach((e,t)=>{a.push(...n(e,r.items,`${i}[${t}]`))})),"object"===e(o)&&!Array.isArray(o)&&null!==o){const e=o;for(const t of r.required??[])t in e||a.push({path:i,message:`missing required property "${t}"`});const t=r.properties??{};for(const[o,r]of Object.entries(t))o in e&&a.push(...n(e[o],r,`${i}.${o}`));if(!1===r.additionalProperties)for(const n of Object.keys(e))n in t||a.push({path:i,message:`unexpected property "${n}"`});else if(r.additionalProperties&&"object"==typeof r.additionalProperties)for(const o of Object.keys(e))o in t||a.push(...n(e[o],r.additionalProperties,`${i}.${o}`))}if(r.allOf)for(const e of r.allOf)a.push(...n(o,e,i));if(r.anyOf&&r.anyOf.length>0){r.anyOf.some(e=>0===n(o,e,i).length)||a.push({path:i,message:"does not match any schema in anyOf"})}if(r.oneOf&&r.oneOf.length>0){const e=r.oneOf.filter(e=>0===n(o,e,i).length).length;1!==e&&a.push({path:i,message:`must match exactly one schema in oneOf (matched ${e})`})}return a}var o,r,i,a,s,l,u=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)},c=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 f={type:"object"};function p(e,t){if("function"==typeof DOMException)return new DOMException(e,t);const n=new Error(e);return n.name=t,n}function m(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 d extends EventTarget{constructor(){super(...arguments),o.add(this),r.set(this,new Map),i.set(this,null),a.set(this,!1)}get ontoolchange(){return u(this,i,"f")}set ontoolchange(e){u(this,i,"f")&&this.removeEventListener("toolchange",u(this,i,"f")),c(this,i,"function"==typeof e?e:null,"f"),u(this,i,"f")&&this.addEventListener("toolchange",u(this,i,"f"))}registerTool(e,t={}){m(e);const{signal:n,exposedTo:i}=t;if(n?.aborted)return;u(this,o,"m",s).call(this,e.name);const a={tool:e,exposedTo:i?[...i]:void 0,pageLevel:!1};if(n){const t=()=>u(this,o,"m",s).call(this,e.name,a,!0);n.addEventListener("abort",t,{once:!0}),a.abortListener={signal:n,listener:t}}u(this,r,"f").set(e.name,a),u(this,o,"m",l).call(this)}provideContext(e={}){const t=e.tools??[];for(const e of t)m(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 u(this,r,"f"))t.pageLevel&&u(this,o,"m",s).call(this,e);for(const e of t){if(u(this,r,"f").has(e.name))throw p(`provideContext(): tool "${e.name}" is already registered via registerTool().`,"InvalidStateError");u(this,r,"f").set(e.name,{tool:e,exposedTo:void 0,pageLevel:!0})}u(this,o,"m",l).call(this)}async getTools(e={}){e.fromOrigins;const t="undefined"!=typeof location?location.origin:void 0;return[...u(this,r,"f").values()].map(({tool:e})=>{const n={name:e.name,description:e.description,inputSchema:structuredClone(e.inputSchema??f)};return e.annotations&&(n.annotations=structuredClone(e.annotations)),t&&(n.origin=t),n})}async executeTool(e,t,o={}){const i=u(this,r,"f").get(e);if(!i)throw p(`No tool registered with name "${e}".`,"NotFoundError");if(o.signal?.aborted)throw p("Tool execution was aborted.","AbortError");let a;if(null==t||""===t)a={};else if("string"==typeof t)try{a=JSON.parse(t)}catch(t){throw new TypeError(`executeTool("${e}"): input is not valid JSON: ${t.message}`)}else a=t;if(null===a||"object"!=typeof a||Array.isArray(a))throw new TypeError(`executeTool("${e}"): input must be a JSON object.`);const s=n(a,i.tool.inputSchema??f);if(s.length>0){const t=s.map(e=>`${e.path}: ${e.message}`).join("; ");throw new TypeError(`executeTool("${e}"): invalid input — ${t}`)}const l=new AbortController,c=o.signal,m=()=>l.abort(c?.reason);c?.addEventListener("abort",m,{once:!0});try{const t=await i.tool.execute(a,{signal:l.signal,toolName:e});if(c?.aborted)throw p("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(c?.aborted||e instanceof Error&&"AbortError"===e.name)throw p("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{c?.removeEventListener("abort",m)}}unregisterTool(e){const t=u(this,o,"m",s).call(this,e);return t&&u(this,o,"m",l).call(this),t}clearContext(){if(0!==u(this,r,"f").size){for(const e of[...u(this,r,"f").keys()])u(this,o,"m",s).call(this,e);u(this,o,"m",l).call(this)}}}function h(e,t){Object.defineProperty(e,"modelContext",{value:t,writable:!1,enumerable:!0,configurable:!0})}r=new WeakMap,i=new WeakMap,a=new WeakMap,o=new WeakSet,s=function(e,t,n=!1){const i=u(this,r,"f").get(e);return!!i&&((!t||i===t)&&(i.abortListener&&i.abortListener.signal.removeEventListener("abort",i.abortListener.listener),u(this,r,"f").delete(e),n&&u(this,o,"m",l).call(this),!0))},l=function(){u(this,a,"f")||(c(this,a,!0,"f"),queueMicrotask(()=>{c(this,a,!1,"f"),this.dispatchEvent(new Event("toolchange"))}))};const g=["AskAction","SearchAction","SubscribeAction","RegisterAction","BuyAction","OrderAction","AddAction","ScheduleAction","ReserveAction","RsvpAction","ApplyAction","QuoteAction","DownloadAction","ReviewAction","CommentAction","TrackAction","CommunicateAction","InteractAction"];function v(e){return g.includes(e)}const y="https://nxis.ai/ns#";function b(e,t,n){var o;return null!==(o=e[t])&&void 0!==o?o:e[n]}function w(e,t,n){const o=b(e,t,n);return Array.isArray(o)?o:[]}function x(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=$(o);for(const e of r){const n=A(e);n&&t.push(n)}}return t}function $(e){if(Array.isArray(e)){const t=[];for(const n of e)t.push(...$(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(...$(n));return e}return[t]}return[]}function A(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")&&!v(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=b(t,"nxis:targetKind",y+"targetKind");"selfPage"!==r&&"clientSide"!==r||(o["nxis:targetKind"]=r);const i=b(t,"nxis:pageUrl",y+"pageUrl");"string"==typeof i&&(o["nxis:pageUrl"]=i);const a=b(t,"nxis:jsHandled",y+"jsHandled");!0===a&&(o["nxis:jsHandled"]=!0);return o}(t.target);if(!o||!r||!i)return null;const a=w(t,"nxis:input",y+"input").map(E).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":b(t,"nxis:extractedAt",y+"extractedAt")}}function E(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=b(t,"nxis:valueType",y+"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=w(t,"nxis:valueOption",y+"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){var t;const n={},o=[],r=null!==(t=e.input)&&void 0!==t?t:[];for(const e of r){const t=N(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}function N(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 k(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:j(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 j(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 S(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 O(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=k(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=S(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=M(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=V(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 p=!1;f&&(p=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 m={success:f,submitted:p,message:C(l,u,p,r),filled:l,skipped:u};return r&&(m.stale=!0),JSON.stringify(m)}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 M(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,V(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=P(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=P(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 V(e){return Array.isArray(e)?e.map(e=>String(e)).join(","):String(e)}function P(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 q(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 d;"undefined"!=typeof document&&h(document,r),n&&"undefined"!=typeof navigator&&h(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};I(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(H(e))return!0;if(e instanceof Element&&e.querySelector('script[type="application/ld+json"]'))return!0}return!1})(r)&&I(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 I(e,t,n,o){const r=x(e);let i=0;for(const e of r)if(!o.ids.has(e["@id"]))try{R(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 R(e,t,n,o){const r=L(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=>O(e,t)},{signal:n.signal}),o.names.add(i)}function H(e){return e instanceof HTMLScriptElement&&"application/ld+json"===e.type}export{g as ACTION_SUBTYPES,L as buildInputSchema,v as isActionType,x as readActions,q as registerActions};
|
|
1
|
+
function e(e){return null===e?"null":Array.isArray(e)?"array":typeof e}function t(n,o){if(n===o)return!0;if(e(n)!==e(o))return!1;if(Array.isArray(n)&&Array.isArray(o))return n.length===o.length&&n.every((e,n)=>t(e,o[n]));if("object"===e(n)){const e=n,r=o,i=Object.keys(e);return i.length===Object.keys(r).length&&i.every(n=>t(e[n],r[n]))}return!1}function n(o,r,i="$"){if(!r||"object"!=typeof r)return[];const a=[];if(void 0===r.const||t(o,r.const)||a.push({path:i,message:`must equal const ${JSON.stringify(r.const)}`}),r.enum&&!r.enum.some(e=>t(o,e))&&a.push({path:i,message:`must be one of ${JSON.stringify(r.enum)}`}),void 0!==r.type){const t=Array.isArray(r.type)?r.type:[r.type];if(!t.some(t=>function(t,n){switch(n){case"object":return"object"===e(t);case"array":return Array.isArray(t);case"string":return"string"==typeof t;case"number":return"number"==typeof t&&Number.isFinite(t);case"integer":return"number"==typeof t&&Number.isInteger(t);case"boolean":return"boolean"==typeof t;case"null":return null===t;default:return!0}}(o,t)))return a.push({path:i,message:`expected type ${t.join(" | ")}, got ${e(o)}`}),a}if("string"==typeof o&&(void 0!==r.minLength&&o.length<r.minLength&&a.push({path:i,message:`must be at least ${r.minLength} characters`}),void 0!==r.maxLength&&o.length>r.maxLength&&a.push({path:i,message:`must be at most ${r.maxLength} characters`}),void 0!==r.pattern)){let e=null;try{e=new RegExp(r.pattern,"u")}catch{}e&&!e.test(o)&&a.push({path:i,message:`must match pattern ${r.pattern}`})}if("number"==typeof o&&(void 0!==r.minimum&&o<r.minimum&&a.push({path:i,message:`must be >= ${r.minimum}`}),void 0!==r.maximum&&o>r.maximum&&a.push({path:i,message:`must be <= ${r.maximum}`})),Array.isArray(o)&&(void 0!==r.minItems&&o.length<r.minItems&&a.push({path:i,message:`must have at least ${r.minItems} items`}),void 0!==r.maxItems&&o.length>r.maxItems&&a.push({path:i,message:`must have at most ${r.maxItems} items`}),r.items&&o.forEach((e,t)=>{a.push(...n(e,r.items,`${i}[${t}]`))})),"object"===e(o)&&!Array.isArray(o)&&null!==o){const e=o;for(const t of r.required??[])t in e||a.push({path:i,message:`missing required property "${t}"`});const t=r.properties??{};for(const[o,r]of Object.entries(t))o in e&&a.push(...n(e[o],r,`${i}.${o}`));if(!1===r.additionalProperties)for(const n of Object.keys(e))n in t||a.push({path:i,message:`unexpected property "${n}"`});else if(r.additionalProperties&&"object"==typeof r.additionalProperties)for(const o of Object.keys(e))o in t||a.push(...n(e[o],r.additionalProperties,`${i}.${o}`))}if(r.allOf)for(const e of r.allOf)a.push(...n(o,e,i));if(r.anyOf&&r.anyOf.length>0){r.anyOf.some(e=>0===n(o,e,i).length)||a.push({path:i,message:"does not match any schema in anyOf"})}if(r.oneOf&&r.oneOf.length>0){const e=r.oneOf.filter(e=>0===n(o,e,i).length).length;1!==e&&a.push({path:i,message:`must match exactly one schema in oneOf (matched ${e})`})}return a}var o,r,i,a,s,l,u=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)},c=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 f={type:"object"};function d(e,t){if("function"==typeof DOMException)return new DOMException(e,t);const n=new Error(e);return n.name=t,n}function p(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 m extends EventTarget{constructor(){super(...arguments),o.add(this),r.set(this,new Map),i.set(this,null),a.set(this,!1)}get ontoolchange(){return u(this,i,"f")}set ontoolchange(e){u(this,i,"f")&&this.removeEventListener("toolchange",u(this,i,"f")),c(this,i,"function"==typeof e?e:null,"f"),u(this,i,"f")&&this.addEventListener("toolchange",u(this,i,"f"))}registerTool(e,t={}){p(e);const{signal:n,exposedTo:i}=t;if(n?.aborted)return;u(this,o,"m",s).call(this,e.name);const a={tool:e,exposedTo:i?[...i]:void 0,pageLevel:!1};if(n){const t=()=>u(this,o,"m",s).call(this,e.name,a,!0);n.addEventListener("abort",t,{once:!0}),a.abortListener={signal:n,listener:t}}u(this,r,"f").set(e.name,a),u(this,o,"m",l).call(this)}provideContext(e={}){const t=e.tools??[];for(const e of t)p(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 u(this,r,"f"))t.pageLevel&&u(this,o,"m",s).call(this,e);for(const e of t){if(u(this,r,"f").has(e.name))throw d(`provideContext(): tool "${e.name}" is already registered via registerTool().`,"InvalidStateError");u(this,r,"f").set(e.name,{tool:e,exposedTo:void 0,pageLevel:!0})}u(this,o,"m",l).call(this)}async getTools(e={}){e.fromOrigins;const t="undefined"!=typeof location?location.origin:void 0;return[...u(this,r,"f").values()].map(({tool:e})=>{const n={name:e.name,description:e.description,inputSchema:structuredClone(e.inputSchema??f)};return e.annotations&&(n.annotations=structuredClone(e.annotations)),t&&(n.origin=t),n})}async executeTool(e,t,o={}){const i=u(this,r,"f").get(e);if(!i)throw d(`No tool registered with name "${e}".`,"NotFoundError");if(o.signal?.aborted)throw d("Tool execution was aborted.","AbortError");let a;if(null==t||""===t)a={};else if("string"==typeof t)try{a=JSON.parse(t)}catch(t){throw new TypeError(`executeTool("${e}"): input is not valid JSON: ${t.message}`)}else a=t;if(null===a||"object"!=typeof a||Array.isArray(a))throw new TypeError(`executeTool("${e}"): input must be a JSON object.`);const s=n(a,i.tool.inputSchema??f);if(s.length>0){const t=s.map(e=>`${e.path}: ${e.message}`).join("; ");throw new TypeError(`executeTool("${e}"): invalid input — ${t}`)}const l=new AbortController,c=o.signal,p=()=>l.abort(c?.reason);c?.addEventListener("abort",p,{once:!0});try{const t=await i.tool.execute(a,{signal:l.signal,toolName:e});if(c?.aborted)throw d("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(c?.aborted||e instanceof Error&&"AbortError"===e.name)throw d("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{c?.removeEventListener("abort",p)}}unregisterTool(e){const t=u(this,o,"m",s).call(this,e);return t&&u(this,o,"m",l).call(this),t}clearContext(){if(0!==u(this,r,"f").size){for(const e of[...u(this,r,"f").keys()])u(this,o,"m",s).call(this,e);u(this,o,"m",l).call(this)}}}function h(e,t){Object.defineProperty(e,"modelContext",{value:t,writable:!1,enumerable:!0,configurable:!0})}r=new WeakMap,i=new WeakMap,a=new WeakMap,o=new WeakSet,s=function(e,t,n=!1){const i=u(this,r,"f").get(e);return!!i&&((!t||i===t)&&(i.abortListener&&i.abortListener.signal.removeEventListener("abort",i.abortListener.listener),u(this,r,"f").delete(e),n&&u(this,o,"m",l).call(this),!0))},l=function(){u(this,a,"f")||(c(this,a,!0,"f"),queueMicrotask(()=>{c(this,a,!1,"f"),this.dispatchEvent(new Event("toolchange"))}))};const v=["AskAction","SearchAction","SubscribeAction","RegisterAction","BuyAction","OrderAction","AddAction","ScheduleAction","ReserveAction","RsvpAction","ApplyAction","QuoteAction","DownloadAction","ReviewAction","CommentAction","TrackAction","CommunicateAction","InteractAction"];function g(e){return v.includes(e)}const y="https://nxis.ai/ns#";function b(e,t,n){var o;return null!==(o=e[t])&&void 0!==o?o:e[n]}function w(e,t){return b(e,t,"https://schema.org/"+t)}function x(e,t){const n=w(e,t);return"string"==typeof n?n:null}function $(e,t,n){const o=b(e,t,n);return Array.isArray(o)?o:[]}function A(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=E(o);for(const e of r){const n=T(e);n&&t.push(n)}}return t}function E(e){if(Array.isArray(e)){const t=[];for(const n of e)t.push(...E(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(...E(n));return e}return[t]}return[]}function T(e){var t;if(!e||"object"!=typeof e)return null;const n=e,o="string"==typeof n["@type"]?n["@type"]:null;if(!o)return null;if(!o.endsWith("Action")&&!g(o))return null;const r="string"==typeof n["@id"]?n["@id"]:null,i=x(n,"name"),a=function(e){var t,n;if(!e||"object"!=typeof e)return null;const o=e,r=null!==(n=null===(t=x(o,"httpMethod"))||void 0===t?void 0:t.toUpperCase())&&void 0!==n?n:null;if(!r)return null;const i={"@type":"EntryPoint",httpMethod:r},a=x(o,"encodingType");null!==a&&(i.encodingType=a);const s=x(o,"urlTemplate");null!==s&&(i.urlTemplate=s);const l=b(o,"nxis:targetKind",y+"targetKind");"selfPage"!==l&&"clientSide"!==l||(i["nxis:targetKind"]=l);const u=b(o,"nxis:pageUrl",y+"pageUrl");"string"==typeof u&&(i["nxis:pageUrl"]=u);const c=b(o,"nxis:jsHandled",y+"jsHandled");!0===c&&(i["nxis:jsHandled"]=!0);return i}(w(n,"target"));if(!r||!i||!a)return null;const s=$(n,"nxis:input",y+"input").map(L).filter(e=>null!==e),l=null!==(t=x(n,"description"))&&void 0!==t?t:function(e,t,n){const o=e.replace(/^.*[/#]/,"").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}.`}(o,i,s);return{"@context":n["@context"],"@type":o,"@id":r,name:i,description:l,target:a,input:s.length>0?s:void 0,"nxis:extractedAt":b(n,"nxis:extractedAt",y+"extractedAt")}}function L(e){if(!e||"object"!=typeof e)return null;const t=e,n=x(t,"valueName");if(!n)return null;const o=b(t,"nxis:valueType",y+"valueType"),r={"@type":"PropertyValueSpecification",valueName:n,"nxis:valueType":"string"==typeof o?o:"string"},i=x(t,"description");null!==i&&(r.description=i),!0===w(t,"valueRequired")&&(r.valueRequired=!0);const a=x(t,"defaultValue");null!==a&&(r.defaultValue=a);const s=e=>{const n=w(t,e);return"number"==typeof n?n:null},l=s("valueMinLength");null!==l&&(r.valueMinLength=l);const u=s("valueMaxLength");null!==u&&(r.valueMaxLength=u);const c=s("minValue");null!==c&&(r.minValue=c);const f=s("maxValue");null!==f&&(r.maxValue=f);const d=s("stepValue");null!==d&&(r.stepValue=d);const p=x(t,"valuePattern");null!==p&&(r.valuePattern=p),!0===w(t,"multipleValues")&&(r.multipleValues=!0),!0===w(t,"readonlyValue")&&(r.readonlyValue=!0);const m=$(t,"nxis:valueOption",y+"valueOption");if(m.length>0){const e=m.filter(e=>e&&"object"==typeof e&&null!==x(e,"value")).map(e=>{var t;const n=e;return{"@type":"nxis:ValueOption",value:x(n,"value"),name:null!==(t=x(n,"name"))&&void 0!==t?t:void 0}});e.length>0&&(r["nxis:valueOption"]=e)}return r}function N(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 k(e){var t;const n={},o=[],r=null!==(t=e.input)&&void 0!==t?t:[];for(const e of r){const t=j(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}function j(e){const t={type:N(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 S(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:O(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 O(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 C(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 M(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=S(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=C(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=q(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=P(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:V(l,u,d,r),filled:l,skipped:u};return r&&(p.stale=!0),JSON.stringify(p)}function V(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 q(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,P(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=I(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=I(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 P(e){return Array.isArray(e)?e.map(e=>String(e)).join(","):String(e)}function I(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 R(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 m;"undefined"!=typeof document&&h(document,r),n&&"undefined"!=typeof navigator&&h(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};H(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(W(e))return!0;if(e instanceof Element&&e.querySelector('script[type="application/ld+json"]'))return!0}return!1})(r)&&H(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 H(e,t,n,o){const r=A(e);let i=0;for(const e of r)if(!o.ids.has(e["@id"]))try{U(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 U(e,t,n,o){const r=k(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=>M(e,t)},{signal:n.signal}),o.names.add(i)}function W(e){return e instanceof HTMLScriptElement&&"application/ld+json"===e.type}export{v as ACTION_SUBTYPES,k as buildInputSchema,g as isActionType,A as readActions,R as registerActions};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxisai/nxis",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"description": "Javascript client for Nxis AI",
|
|
5
5
|
"main": "build/index.cjs.js",
|
|
6
6
|
"module": "build/index.esm.js",
|
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
"types": "./build/index.d.ts"
|
|
20
20
|
},
|
|
21
21
|
"./webmcp": {
|
|
22
|
+
"types": "./build/webmcp.d.ts",
|
|
22
23
|
"browser": "./build/webmcp.js",
|
|
23
|
-
"
|
|
24
|
+
"import": "./build/webmcp.js",
|
|
25
|
+
"default": "./build/webmcp.js"
|
|
24
26
|
}
|
|
25
27
|
},
|
|
26
28
|
"scripts": {
|
package/src/webmcp/parse.ts
CHANGED
|
@@ -16,6 +16,9 @@ import { NxisAction, NxisEntryPoint, NxisPropertyValueSpecification, isActionTyp
|
|
|
16
16
|
/** The NxisAI namespace IRI — used for expanded property lookups. */
|
|
17
17
|
const NXIS_NS = 'https://nxis.ai/ns#';
|
|
18
18
|
|
|
19
|
+
/** The schema.org namespace IRI — used for expanded property lookups. */
|
|
20
|
+
const SCHEMA_NS = 'https://schema.org/';
|
|
21
|
+
|
|
19
22
|
/**
|
|
20
23
|
* Resolve a property value from an object, trying the compact nxis: prefix
|
|
21
24
|
* first, then the fully expanded IRI. JSON-LD processors may store properties
|
|
@@ -25,6 +28,25 @@ function resolveValue(obj: Record<string, unknown>, compact: string, expanded: s
|
|
|
25
28
|
return obj[compact] ?? obj[expanded];
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a schema.org property, trying the compact term then the expanded IRI.
|
|
33
|
+
*
|
|
34
|
+
* The backend does not compact uniformly: a graph can carry a business node
|
|
35
|
+
* with compact keys (`name`, `review`) alongside an action node whose keys are
|
|
36
|
+
* fully expanded (`https://schema.org/name`). Reading only the compact form
|
|
37
|
+
* dropped every such action silently — `name` and `target` came back undefined,
|
|
38
|
+
* the required-field check failed, and the node was discarded with no error.
|
|
39
|
+
*/
|
|
40
|
+
function schemaValue(obj: Record<string, unknown>, prop: string): unknown {
|
|
41
|
+
return resolveValue(obj, prop, SCHEMA_NS + prop);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Resolve a schema.org property known to be a string. */
|
|
45
|
+
function schemaString(obj: Record<string, unknown>, prop: string): string | null {
|
|
46
|
+
const val = schemaValue(obj, prop);
|
|
47
|
+
return typeof val === 'string' ? val : null;
|
|
48
|
+
}
|
|
49
|
+
|
|
28
50
|
/**
|
|
29
51
|
* Resolve an array property, trying compact then expanded key.
|
|
30
52
|
*/
|
|
@@ -120,8 +142,8 @@ function parseActionNode(node: unknown): NxisAction | null {
|
|
|
120
142
|
|
|
121
143
|
// Required fields
|
|
122
144
|
const id = typeof obj['@id'] === 'string' ? obj['@id'] : null;
|
|
123
|
-
const name =
|
|
124
|
-
const target = parseEntryPoint(obj
|
|
145
|
+
const name = schemaString(obj, 'name');
|
|
146
|
+
const target = parseEntryPoint(schemaValue(obj, 'target'));
|
|
125
147
|
|
|
126
148
|
if (!id || !name || !target) return null;
|
|
127
149
|
|
|
@@ -134,9 +156,8 @@ function parseActionNode(node: unknown): NxisAction | null {
|
|
|
134
156
|
.map(parsePVS)
|
|
135
157
|
.filter((pvs): pvs is NxisPropertyValueSpecification => pvs !== null);
|
|
136
158
|
|
|
137
|
-
const description =
|
|
138
|
-
|
|
139
|
-
: generateFallbackDescription(type, name, input);
|
|
159
|
+
const description = schemaString(obj, 'description')
|
|
160
|
+
?? generateFallbackDescription(type, name, input);
|
|
140
161
|
|
|
141
162
|
return {
|
|
142
163
|
'@context': obj['@context'],
|
|
@@ -161,9 +182,7 @@ function parseEntryPoint(raw: unknown): NxisEntryPoint | null {
|
|
|
161
182
|
|
|
162
183
|
const obj = raw as Record<string, unknown>;
|
|
163
184
|
|
|
164
|
-
const httpMethod =
|
|
165
|
-
? obj['httpMethod'].toUpperCase()
|
|
166
|
-
: null;
|
|
185
|
+
const httpMethod = schemaString(obj, 'httpMethod')?.toUpperCase() ?? null;
|
|
167
186
|
if (!httpMethod) return null;
|
|
168
187
|
|
|
169
188
|
const entryPoint: NxisEntryPoint = {
|
|
@@ -172,13 +191,15 @@ function parseEntryPoint(raw: unknown): NxisEntryPoint | null {
|
|
|
172
191
|
};
|
|
173
192
|
|
|
174
193
|
// Optional fields
|
|
175
|
-
|
|
176
|
-
|
|
194
|
+
const encodingType = schemaString(obj, 'encodingType');
|
|
195
|
+
if (encodingType !== null) {
|
|
196
|
+
entryPoint.encodingType = encodingType;
|
|
177
197
|
}
|
|
178
198
|
|
|
179
199
|
// URL target
|
|
180
|
-
|
|
181
|
-
|
|
200
|
+
const urlTemplate = schemaString(obj, 'urlTemplate');
|
|
201
|
+
if (urlTemplate !== null) {
|
|
202
|
+
entryPoint.urlTemplate = urlTemplate;
|
|
182
203
|
}
|
|
183
204
|
|
|
184
205
|
// Endpointless target — try both compact and expanded forms
|
|
@@ -207,7 +228,7 @@ function parsePVS(raw: unknown): NxisPropertyValueSpecification | null {
|
|
|
207
228
|
|
|
208
229
|
const obj = raw as Record<string, unknown>;
|
|
209
230
|
|
|
210
|
-
const valueName =
|
|
231
|
+
const valueName = schemaString(obj, 'valueName');
|
|
211
232
|
if (!valueName) return null;
|
|
212
233
|
|
|
213
234
|
const valueType = resolveValue(obj, 'nxis:valueType', NXIS_NS + 'valueType');
|
|
@@ -222,29 +243,42 @@ function parsePVS(raw: unknown): NxisPropertyValueSpecification | null {
|
|
|
222
243
|
};
|
|
223
244
|
|
|
224
245
|
// Optional fields — only set if present and correct type
|
|
225
|
-
|
|
226
|
-
if (
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
if (
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (
|
|
246
|
+
const pvsDescription = schemaString(obj, 'description');
|
|
247
|
+
if (pvsDescription !== null) pvs.description = pvsDescription;
|
|
248
|
+
if (schemaValue(obj, 'valueRequired') === true) pvs.valueRequired = true;
|
|
249
|
+
const defaultValue = schemaString(obj, 'defaultValue');
|
|
250
|
+
if (defaultValue !== null) pvs.defaultValue = defaultValue;
|
|
251
|
+
const numeric = (prop: string): number | null => {
|
|
252
|
+
const val = schemaValue(obj, prop);
|
|
253
|
+
return typeof val === 'number' ? val : null;
|
|
254
|
+
};
|
|
255
|
+
const valueMinLength = numeric('valueMinLength');
|
|
256
|
+
if (valueMinLength !== null) pvs.valueMinLength = valueMinLength;
|
|
257
|
+
const valueMaxLength = numeric('valueMaxLength');
|
|
258
|
+
if (valueMaxLength !== null) pvs.valueMaxLength = valueMaxLength;
|
|
259
|
+
const minValue = numeric('minValue');
|
|
260
|
+
if (minValue !== null) pvs.minValue = minValue;
|
|
261
|
+
const maxValue = numeric('maxValue');
|
|
262
|
+
if (maxValue !== null) pvs.maxValue = maxValue;
|
|
263
|
+
const stepValue = numeric('stepValue');
|
|
264
|
+
if (stepValue !== null) pvs.stepValue = stepValue;
|
|
265
|
+
const valuePattern = schemaString(obj, 'valuePattern');
|
|
266
|
+
if (valuePattern !== null) pvs.valuePattern = valuePattern;
|
|
267
|
+
if (schemaValue(obj, 'multipleValues') === true) pvs.multipleValues = true;
|
|
268
|
+
if (schemaValue(obj, 'readonlyValue') === true) pvs.readonlyValue = true;
|
|
236
269
|
|
|
237
270
|
// Parse options array — try both compact and expanded forms
|
|
238
271
|
const rawOptions = resolveArray(obj, 'nxis:valueOption', NXIS_NS + 'valueOption');
|
|
239
272
|
if (rawOptions.length > 0) {
|
|
240
273
|
const options = rawOptions
|
|
241
|
-
.filter((o: unknown) => o && typeof o === 'object'
|
|
274
|
+
.filter((o: unknown) => o && typeof o === 'object'
|
|
275
|
+
&& schemaString(o as Record<string, unknown>, 'value') !== null)
|
|
242
276
|
.map((o: unknown) => {
|
|
243
277
|
const opt = o as Record<string, unknown>;
|
|
244
278
|
return {
|
|
245
279
|
'@type': 'nxis:ValueOption' as const,
|
|
246
|
-
value: opt
|
|
247
|
-
name:
|
|
280
|
+
value: schemaString(opt, 'value') as string,
|
|
281
|
+
name: schemaString(opt, 'name') ?? undefined,
|
|
248
282
|
};
|
|
249
283
|
});
|
|
250
284
|
if (options.length > 0) {
|
|
@@ -264,8 +298,9 @@ function generateFallbackDescription(
|
|
|
264
298
|
name: string,
|
|
265
299
|
input: NxisPropertyValueSpecification[]
|
|
266
300
|
): string {
|
|
267
|
-
// "CommunicateAction" → "communicate"
|
|
268
|
-
|
|
301
|
+
// "CommunicateAction" → "communicate"; also strips an expanded IRI prefix,
|
|
302
|
+
// since `type` may arrive as "https://schema.org/CommunicateAction".
|
|
303
|
+
const verb = type.replace(/^.*[/#]/, '').replace(/Action$/, '').toLowerCase();
|
|
269
304
|
const fieldList = input.map(f => f.valueName).join(', ');
|
|
270
305
|
|
|
271
306
|
if (input.length === 0) {
|