@xano/xanoscript-language-server 12.20.0 → 12.21.0

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 CHANGED
@@ -1,24 +1,18 @@
1
- ```
2
- ██╗ ██╗███████╗ ██╗ █████╗ ███╗ ██╗ ██████╗ ██╗ ██╗ █████╗ ██████╗ ███████╗
3
- ╚██╗██╔╝██╔════╝ ██║ ██╔══██╗████╗ ██║██╔════╝ ██║ ██║██╔══██╗██╔════╝ ██╔════╝
4
- ╚███╔╝ ███████╗ ██║ ███████║██╔██╗ ██║██║ ███╗██║ ██║███████║██║ ███╗█████╗
5
- ██╔██╗ ╚════██║ ██║ ██╔══██║██║╚██╗██║██║ ██║██║ ██║██╔══██║██║ ██║██╔══╝
6
- ██╔╝ ██╗███████║ ███████╗██║ ██║██║ ╚████║╚██████╔╝╚██████╔╝██║ ██║╚██████╔╝███████╗
7
- ╚═╝ ╚═╝╚══════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝
8
-
9
- ███████╗███████╗██████╗ ██╗ ██╗███████╗██████╗
10
- ██╔════╝██╔════╝██╔══██╗██║ ██║██╔════╝██╔══██╗
11
- ███████╗█████╗ ██████╔╝██║ ██║█████╗ ██████╔╝
12
- ╚════██║██╔══╝ ██╔══██╗╚██╗ ██╔╝██╔══╝ ██╔══██╗
13
- ███████║███████╗██║ ██║ ╚████╔╝ ███████╗██║ ██║
14
- ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝
15
- ```
16
-
17
1
  # XanoScript Language Server
18
2
 
19
- A Language Server Protocol (LSP) implementation for [XanoScript](https://xano.com), built on [Chevrotain](https://chevrotain.io/).
3
+ The [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) implementation for XanoScript, the language that describes a [Xano](https://www.xano.com) backend: its APIs, functions, tables, tasks, agents, MCP servers and policies.
20
4
 
21
- Maintained by the [Xano](https://xano.com) team.
5
+ Xano is one platform to build, run and govern AI-built software. This server brings the platform's rules into the editor, so you and your coding agent see the problems a push would refuse while you write the code.
6
+
7
+ Built on [Chevrotain](https://chevrotain.io/) and maintained by the Xano team.
8
+
9
+ ## Features
10
+
11
+ - **Diagnostics** - Errors and warnings as you type, including cross-file references, variable usage and workspace policy checks
12
+ - **Completion** - Context-aware suggestions, triggered by `.`, `:`, `$` and `|`
13
+ - **Hover** - Documentation for statements, filters, input variables and policy checks
14
+ - **Go to definition** - Jump to a referenced function, task, query or table, or to where a variable is set
15
+ - **Semantic highlighting** - Token-based highlighting for every XanoScript construct
22
16
 
23
17
  ## Installation
24
18
 
@@ -26,155 +20,100 @@ Maintained by the [Xano](https://xano.com) team.
26
20
  npm install @xano/xanoscript-language-server
27
21
  ```
28
22
 
29
- **Requirements:** Node.js 20+
23
+ Requires Node.js 20 or later.
30
24
 
31
- ## Features
25
+ ## Usage
26
+
27
+ The package ships two entry points:
32
28
 
33
- The language server provides full LSP support including:
29
+ | Entry point | Runtime | Transport |
30
+ |---|---|---|
31
+ | `server.js` | Node.js | stdio / IPC, via `vscode-languageserver/node` |
32
+ | `language-server-worker.js` | Browser (Web Worker) | `postMessage`, via `vscode-languageserver/browser` |
34
33
 
35
- - **Code Completion** - Context-aware suggestions triggered by `.`, `:`, `$`, and `|`
36
- - **Syntax Highlighting** - Semantic token-based highlighting for XanoScript constructs
37
- - **Hover Information** - Documentation and type information on hover
38
- - **Diagnostics** - Real-time error detection and reporting
34
+ Point any LSP client at `server.js` for `.xs` files. For example, with VS Code's `vscode-languageclient`:
39
35
 
40
- ## Supported Object Types
36
+ ```js
37
+ const serverModule = require.resolve("@xano/xanoscript-language-server/server.js");
41
38
 
42
- The parser supports the following XanoScript constructs:
39
+ const client = new LanguageClient(
40
+ "xanoscript",
41
+ "XanoScript",
42
+ {
43
+ run: { module: serverModule, transport: TransportKind.ipc },
44
+ debug: { module: serverModule, transport: TransportKind.ipc },
45
+ },
46
+ { documentSelector: [{ scheme: "file", language: "xanoscript" }] }
47
+ );
48
+ ```
49
+
50
+ ### Policy checks
51
+
52
+ XanoScript `policy` files are checked against Xano's policy check catalogue. The server ships a snapshot of it (`parser/policy/catalogueSnapshot.js`), so what it reports is a warning: your instance may be newer than the snapshot. A host that can fetch the live catalogue can load it with `setPolicyCatalogue()` from `parser/policy/catalogue.js`.
53
+
54
+ ## Supported object types
43
55
 
44
56
  | Type | Description |
45
57
  |------|-------------|
46
- | `query` | API endpoints with HTTP verbs, input validation, and responses |
47
- | `function` | Reusable logic blocks with testing capabilities |
48
- | `task` | Scheduled operations with cron-like triggers |
58
+ | `query` | API endpoints with HTTP verbs, input validation and responses |
49
59
  | `api_group` | Collections of related API endpoints |
60
+ | `function` | Reusable logic blocks with tests |
61
+ | `task` | Scheduled operations with cron-like triggers |
50
62
  | `table` | Database schema definitions with column types |
51
- | `table_trigger` | Database table trigger definitions |
52
- | `workflow_test` | Test definitions for workflows |
53
- | `addon` | Add-on definitions |
54
- | `agent` | AI agent definitions |
55
- | `agent_trigger` | Agent trigger handlers |
56
- | `branch` | Branch logic |
57
- | `mcp_server` | MCP server definitions |
58
- | `mcp_server_trigger` | MCP server trigger handlers |
63
+ | `table_trigger` | Database table triggers |
64
+ | `workflow_test` | Workflow test definitions |
59
65
  | `middleware` | Middleware definitions |
60
- | `realtime_channel` | Realtime v1 channel definitions |
61
- | `realtime_trigger` | Realtime v1 trigger handlers |
62
- | `realtime_server` | Realtime v2 server definitions (the container that owns channels) |
63
- | `channel` | Realtime v2 channel definitions (path templates) |
66
+ | `addon` | Add-on definitions |
67
+ | `branch` | Branch definitions |
68
+ | `workspace` / `workspace_trigger` | Workspace definitions and triggers |
69
+ | `error_trigger` | Error trigger handlers |
70
+ | `agent` / `agent_trigger` | AI agent definitions and triggers |
71
+ | `tool` | Tool definitions, including MCP tool metadata |
72
+ | `mcp_server` / `mcp_server_trigger` | MCP server definitions and connection triggers |
73
+ | `prompt` / `resource` | MCP prompts and resources |
74
+ | `policy` | Workspace policies and their rules |
75
+ | `microservice` | Container and Helm microservice deployments |
76
+ | `realtime_channel` / `realtime_trigger` | Realtime v1 channels and triggers |
77
+ | `realtime_server` / `realtime_server_trigger` | Realtime v2 servers and connection triggers |
78
+ | `channel` / `channel_trigger` | Realtime v2 channels and join/leave triggers |
64
79
  | `message` | Realtime v2 channel message handlers |
65
- | `channel_trigger` | Realtime v2 channel triggers (join/leave lifecycle, deliver per-recipient) |
66
- | `realtime_server_trigger` | Realtime v2 connection lifecycle triggers (connect/disconnect) |
67
- | `tool` | Tool definitions |
68
- | `workspace` | Workspace definitions |
69
- | `workspace_trigger` | Workspace trigger handlers |
70
-
71
- ## Built-in Function Domains
72
-
73
- XanoScript includes functions organized by domain:
74
-
75
- - **ai** - AI and machine learning operations
76
- - **api** - External API calls and HTTP requests
77
- - **array** - Array manipulation and iteration
78
- - **cloud** - Cloud service integrations
79
- - **controls** - Flow control (conditionals, loops, etc.)
80
- - **db** - Database queries and operations
81
- - **debug** - Debugging and logging utilities
82
- - **expect** - Testing assertions
83
- - **math** - Mathematical operations
84
- - **object** - Object manipulation
85
- - **redis** - Redis cache operations
86
- - **schema** - Schema operations
87
- - **security** - Authentication and security functions
88
- - **storage** - File storage operations
89
- - **stream** - Streaming data operations
90
- - **text** - String manipulation
91
- - **util** - General utilities
92
- - **var** - Variable operations
93
- - **zip** - Compression utilities
94
-
95
- ## Project Structure
80
+ | `run` | Run job and service definitions |
96
81
 
97
- ```
98
- ├── server.js # LSP server entry point
99
- ├── lexer/ # Lexical analysis
100
- │ ├── lexer.js # Chevrotain lexer implementation
101
- │ ├── tokens.js # Main token registry
102
- │ └── [domain].js # Domain-specific tokens (api, db, cloud, etc.)
103
- ├── parser/ # Syntax parsing
104
- │ ├── base_parser.js # Core XanoBaseParser
105
- │ ├── [type]_parser.js # Object type parsers
106
- │ ├── attributes/ # Field attributes (description, disabled, sensitive)
107
- │ ├── clauses/ # Language blocks (stack, input, response, etc.)
108
- │ ├── definitions/ # Type and column definitions
109
- │ ├── functions/ # Built-in functions by domain
110
- │ └── generic/ # Reusable parsing components
111
- ├── onCompletion/ # Code completion logic
112
- ├── onDidChangeContent/ # Diagnostics and error reporting
113
- ├── onHover/ # Hover documentation
114
- └── onSemanticCheck/ # Semantic token highlighting
115
- ```
116
-
117
- ## Development
118
-
119
- ```sh
120
- # Install dependencies
121
- npm install
122
-
123
- # Run all tests (~1800 tests)
124
- npm test
82
+ ## Built-in function domains
125
83
 
126
- # Run specific test
127
- npm test -- -g "test name"
84
+ `ai`, `api`, `array`, `cloud`, `controls`, `datadog`, `db`, `debug`, `expect`, `math`, `mcp`, `object`, `realtime`, `redis`, `schema`, `security`, `storage`, `stream`, `text`, `util`, `var`, `zip`
128
85
 
129
- # Lint code
130
- npm run lint
86
+ ## Project structure
131
87
 
132
- # Fix lint issues
133
- npm run lint:fix
134
-
135
- # Generate TextMate language definition
136
- npm run tm
88
+ ```
89
+ ├── server.js # Node LSP entry point
90
+ ├── language-server-worker.js # Browser (Web Worker) entry point
91
+ ├── lexer/ # Token definitions
92
+ ├── parser/ # Grammar rules
93
+ │ ├── base_parser.js # Core XanoBaseParser
94
+ │ ├── [type]_parser.js # One parser per object type
95
+ │ ├── attributes/ # Field attributes (description, disabled, sensitive)
96
+ │ ├── clauses/ # Blocks (stack, input, response, ...)
97
+ │ ├── definitions/ # Type and column definitions
98
+ │ ├── functions/ # Built-in statements by domain
99
+ │ ├── generic/ # Shared parsing components
100
+ │ └── policy/ # Policy check catalogue
101
+ ├── onCompletion/ # Completion
102
+ ├── onDefinition/ # Go to definition
103
+ ├── onDidChangeContent/ # Diagnostics
104
+ ├── onHover/ # Hover documentation
105
+ ├── onSemanticCheck/ # Semantic tokens
106
+ └── workspace/ # Cross-file index and validation
137
107
  ```
138
108
 
139
- ## Adding New Features
140
-
141
- ### New Object Type
142
-
143
- 1. Create a new parser in `parser/` (e.g., `my_object_parser.js`)
144
- 2. Follow existing parser patterns (see `query_parser.js`, `function_parser.js`)
145
- 3. Register it in `server.js`
146
-
147
- ### New Keyword/Function
148
-
149
- We have a hierarchy for the different statements XanoScript uses:
150
-
151
- 1. **Parser** - Top level (Query parser, Function parser, etc.)
152
- 2. **Clause** - Main blocks in a given object type (stack, response, input, etc.)
153
- 3. **Functions** - Statements like `debug.log` or `db.query`
154
- 4. **Attributes/Definitions** - Components used to define functions
155
- 5. **Generic** - Shared language toolset
156
-
157
- To add a new function:
158
-
159
- 1. Add token definition in appropriate `lexer/` file
160
- 2. Create function implementation in `parser/functions/[domain]/`
161
- 3. Register in parent clause or parser
162
- 4. Add comprehensive tests in corresponding `.spec.js` file
163
-
164
- ## Testing
165
-
166
- The language server is heavily tested and should remain that way. Tests should be:
167
-
168
- - Easy to copy, paste, and extend
169
- - Straightforward and easy to read
170
- - Avoid dynamic generation (no loops)
109
+ ## Contributing
171
110
 
172
- Test files use `.spec.js` naming convention and mirror the source structure.
111
+ This repository receives one commit per release. Development happens elsewhere, so pull requests are turned off here. Found a bug? [Open an issue](https://github.com/xano-inc/xanoscript-language-server/issues/new?template=bug_report.yml). [CONTRIBUTING.md](CONTRIBUTING.md) says what makes a report quick to fix.
173
112
 
174
- ## CI/CD
113
+ ## Security
175
114
 
176
- On commit, pre-commit hooks run linting via Husky. The CI pipeline runs all tests and linting checks. Configuration is in `.github/workflows/ci.yml`.
115
+ To report a vulnerability, see [SECURITY.md](SECURITY.md). Please don't open a public issue.
177
116
 
178
117
  ## License
179
118
 
180
- MIT License - see [LICENSE](LICENSE) for details.
119
+ [MIT](LICENSE)
@@ -81,7 +81,7 @@ This works in any context (not just `api.lambda`), making it flexible for future
81
81
  ## Client-Side vs Server-Side
82
82
 
83
83
  - **Server-side** (this folder): JavaScript implementation used by language server
84
- - **Client-side** (`src/tooling/js_embedding.ts`): TypeScript implementation used by VS Code extension
84
+ - **Client-side**: the editor extension creates the virtual documents with the same extraction logic
85
85
  - Both implement the same extraction logic but in their respective environments
86
86
 
87
87
  ## Testing
@@ -89,7 +89,6 @@ This works in any context (not just `api.lambda`), making it flexible for future
89
89
  All modules are ES6 modules and can be tested independently:
90
90
 
91
91
  ```bash
92
- cd language-server
93
92
  node -e "import('./embedded/embeddedContent.js').then(m => console.log(m.extractEmbeddedJS('code = \"\"\"\\nconsole.log()\\n\"\"\"')))"
94
93
  ```
95
94
 
package/lexer/mcp.js CHANGED
@@ -4,9 +4,8 @@ import { createTokenByName } from "./utils.js";
4
4
  // MCP 2026-07-28 authoring keywords: the `prompt` and `resource` object kinds,
5
5
  // the tool/prompt/resource metadata keys, and the `mcp.elicit` / `mcp.progress`
6
6
  // statements, plus the per-server `oauth` block and the `mcp.oauth.complete` /
7
- // `mcp.oauth.request` / `mcp.oauth.revoke` statements. Grounded against cloud-client's schema DSL
8
- // (`script/kind/schema/core/{prompt,resource,tool,mcp_server}.yaml` and
9
- // `script/kind/schema/statement/mcp.{elicit,progress}.yaml`).
7
+ // `mcp.oauth.request` / `mcp.oauth.revoke` statements. Grounded against the Xano
8
+ // backend's XanoScript schema.
10
9
  //
11
10
  // Ordering note: a keyword with `longer_alt: Identifier` loses to an EARLIER
12
11
  // shorter token, so `prompts` must precede `prompt`, and `resource` is lexed
@@ -14,7 +14,7 @@ export const RealtimeServerTriggerToken = createTokenByName(
14
14
  // longer_alt lists the trigger token so `realtime_server_trigger` wins over the
15
15
  // shorter `realtime_server` prefix, AND Identifier so a name that merely EXTENDS
16
16
  // the keyword (e.g. `realtime_server_0`, the default scaffold name) tokenizes as
17
- // one identifier instead of keyword + leftover `_0` (DEV-7582). longer_alt is not
17
+ // one identifier instead of keyword + leftover `_0`. longer_alt is not
18
18
  // transitive, so Identifier must be listed here directly — matching the
19
19
  // [ServerDetailsToken, Identifier] precedent in channel.js.
20
20
  export const RealtimeServerToken = createTokenByName("realtime_server", {
@@ -1985,10 +1985,13 @@ Removes characters (default is whitespace, or as specified by `value`) from both
1985
1985
  throw {
1986
1986
  name = "ValidationError"
1987
1987
  value = "Invalid user input provided"
1988
+ status = 400
1988
1989
  }
1989
1990
  ```
1990
1991
 
1991
- Throws an error and halts the script’s execution immediately. The `name` parameter specifies the error type (e.g., `"ValidationError"`), and `value` provides a custom error message to describe the issue.
1992
+ Throws an error and halts the script’s execution immediately. The `name` parameter specifies the error type (e.g., `"ValidationError"`), and `value` provides a custom error message to describe the issue. A `try_catch` can catch it.
1993
+
1994
+ `status` (optional, a whole number from 100 to 599) is the HTTP status the request answers with when nothing catches the throw, including a throw inside a function run with `function.run`. Without it, an uncaught throw answers 200. A caught throw never changes the status.
1992
1995
 
1993
1996
  # try_catch
1994
1997
 
package/package.json CHANGED
@@ -1,7 +1,14 @@
1
1
  {
2
2
  "name": "@xano/xanoscript-language-server",
3
- "version": "12.20.0",
4
- "description": "Language Server Protocol implementation for XanoScript",
3
+ "version": "12.21.0",
4
+ "description": "Language Server Protocol implementation for XanoScript, the language of Xano backends",
5
+ "keywords": [
6
+ "xano",
7
+ "xanoscript",
8
+ "language-server",
9
+ "lsp",
10
+ "chevrotain"
11
+ ],
5
12
  "type": "module",
6
13
  "main": "server.js",
7
14
  "files": [
@@ -31,14 +38,17 @@
31
38
  ],
32
39
  "repository": {
33
40
  "type": "git",
34
- "url": "git+https://github.com/xano-labs/xs-language-server.git"
41
+ "url": "git+https://github.com/xano-inc/xanoscript-language-server.git"
35
42
  },
36
43
  "author": "Xano <support@xano.com>",
37
44
  "license": "MIT",
45
+ "engines": {
46
+ "node": ">=20"
47
+ },
38
48
  "bugs": {
39
- "url": "https://github.com/xano-labs/xs-language-server/issues"
49
+ "url": "https://github.com/xano-inc/xanoscript-language-server/issues"
40
50
  },
41
- "homepage": "https://github.com/xano-labs/xs-language-server#readme",
51
+ "homepage": "https://github.com/xano-inc/xanoscript-language-server#readme",
42
52
  "scripts": {
43
53
  "test-one": "mocha",
44
54
  "test": "mocha \"**/*.spec.js\"",
@@ -5,8 +5,7 @@ import { HistoryToken } from "../../lexer/tokens.js";
5
5
  /**
6
6
  * Parses the history clause.
7
7
  *
8
- * `history` mirrors the backend serializer (cloud-client
9
- * helper/script/transform via `Transform::onSchemaRequestHistory`): the value is
8
+ * `history` mirrors the backend serializer: the value is
10
9
  * `false` (disabled), `"inherit"` (hand-authored sentinel), `"all"` (unbounded),
11
10
  * or ANY non-negative integer message limit — not a fixed set of magic numbers.
12
11
  * It is split across two OR branches because `schemaParseEnumFn` refuses an enum
@@ -7,10 +7,8 @@ import { AnnotationsToken, IconsToken, TitleToken } from "../../lexer/mcp.js";
7
7
  import { MultiLineStringToken } from "../../lexer/multiline.js";
8
8
 
9
9
  // Shared MCP metadata blocks for `tool`, `prompt` and `resource` (MCP spec
10
- // 2026-07-28). The rules mirror cloud-client's decode checks so the editor
11
- // reports what a push would refuse:
12
- // - `helper/mvp/mcpserver/ToolMetadata.php` (icons, tool annotations)
13
- // - `helper/script/transform/Resource.php` (resource annotations)
10
+ // 2026-07-28). The rules mirror the Xano backend's decode checks (icons, tool annotations,
11
+ // resource annotations) so the editor reports what a push would refuse.
14
12
 
15
13
  export const ICON_MIME_TYPES = ["image/png", "image/jpeg", "image/webp"];
16
14
  export const ICON_URL_MAX_LENGTH = 2048;
@@ -19,7 +17,7 @@ export const RESOURCE_AUDIENCES = ["user", "assistant"];
19
17
 
20
18
  const ICON_SCHEMA = {
21
19
  src: "[string]",
22
- // validated below: cloud-client compares it lowercased and allows ""
20
+ // validated below: the backend compares it lowercased and allows ""
23
21
  "mime_type?": "[string]",
24
22
  "sizes?": ["[string]"],
25
23
  "theme?": ["light", "dark"],
@@ -47,7 +45,7 @@ function stringValue(token) {
47
45
  }
48
46
 
49
47
  /**
50
- * Why an icon `src` is refused, or null. Same rules as ToolMetadata::srcProblem:
48
+ * Why an icon `src` is refused, or null. Same rules as the platform:
51
49
  * `https:` or a base64 `data:` URI of png/jpeg/webp. SVG and every other scheme
52
50
  * (`http:`, `javascript:`, `file:`, ...) are refused.
53
51
  * @param {string} src
@@ -13,7 +13,7 @@ export function dbLinkColumnDefinition($) {
13
13
  parentToken,
14
14
  {
15
15
  table: "[string]",
16
- "override?": { "[string]": { hidden: "[boolean]" } },
16
+ "override?": { "[string]": { "hidden?": "[boolean]", "required?": "[boolean]" } },
17
17
  },
18
18
  ],
19
19
  });
@@ -1,4 +1,25 @@
1
1
  import { ThrowToken } from "../../../lexer/control.js";
2
+ import { IntegerLiteral } from "../../../lexer/literal.js";
3
+
4
+ /**
5
+ * `status` is the HTTP status an UNCAUGHT throw answers with
6
+ * (mvp:throw_error, `status?: static:int`).
7
+ * It is a static integer from 100 to 599, never an expression, so a variable
8
+ * or an out-of-range number is caught in the editor rather than at run time.
9
+ * @param {import('../../base_parser.js').XanoBaseParser} $
10
+ */
11
+ function statusValue($) {
12
+ const token = $.CONSUME(IntegerLiteral, {
13
+ ERR_MSG: "status must be a whole number from 100 to 599",
14
+ });
15
+ const value = Number(token.image);
16
+ if (!Number.isInteger(value) || value < 100 || value > 599) {
17
+ $.addInvalidValueError(
18
+ token,
19
+ `Invalid status ${token.image}. Must be a whole number from 100 to 599`,
20
+ );
21
+ }
22
+ }
2
23
 
3
24
  /**
4
25
  * represent $var.x or $x, the only format accepting an assigment
@@ -6,14 +27,19 @@ import { ThrowToken } from "../../../lexer/control.js";
6
27
  */
7
28
  export function throwFn($) {
8
29
  const requiredAttrs = ["value", "name"];
9
- const optionalAttrs = ["description", "disabled"];
30
+ const optionalAttrs = ["description", "disabled", "status"];
10
31
 
11
32
  return () => {
12
33
  $.sectionStack.push("throw");
13
34
  const tokenFn = $.CONSUME(ThrowToken); // "throw"
14
35
  // value is a required field
15
36
  $.SUBRULE($.functionAttrReq, {
16
- ARGS: [tokenFn, requiredAttrs, optionalAttrs],
37
+ ARGS: [
38
+ tokenFn,
39
+ requiredAttrs,
40
+ optionalAttrs,
41
+ { types: { status: statusValue } },
42
+ ],
17
43
  });
18
44
 
19
45
  $.sectionStack.pop();
@@ -13,7 +13,7 @@ import { checkElicit } from "./elicitLint.js";
13
13
  // }
14
14
  // } as $answer
15
15
  //
16
- // Asks the MCP client for input (cloud-client mvp:mcp_elicit). Input is only
16
+ // Asks the MCP client for input. Input is only
17
17
  // collected from 2026-07-28 clients with form elicitation; every other client
18
18
  // (and a non-MCP run) resolves to {action: "cancel"}. The `input` block is the
19
19
  // same grammar as a tool's `input` block.
@@ -5,7 +5,7 @@ import { CompleteToken, OauthToken, RevokeToken } from "../../../lexer/mcp.js";
5
5
  import { DotToken, NewlineToken } from "../../../lexer/tokens.js";
6
6
  import { getVarName } from "../../generic/utils.js";
7
7
 
8
- // The values cloud-client's mvp:mcp_oauth_complete accepts for `decision`.
8
+ // The values `mcp.oauth.complete` accepts for `decision`.
9
9
  const DECISIONS = ["approve", "deny"];
10
10
 
11
11
  /**
@@ -35,9 +35,7 @@ function decisionValue($, identifier) {
35
35
  $.SUBRULE5($.expressionFn, { ARGS: [identifier, {}] });
36
36
  }
37
37
 
38
- // The `mcp.oauth.*` login hand-off statements (cloud-client
39
- // mvp:mcp_oauth_{complete,request,revoke}, kind schemas
40
- // statement/mcp.oauth.{complete,request,revoke}.yaml). Each takes an optional
38
+ // The `mcp.oauth.*` login hand-off statements. Each takes an optional
41
39
  // `as $var` binding.
42
40
 
43
41
  /**
@@ -6,8 +6,8 @@ import { ProgressToken } from "../../../lexer/mcp.js";
6
6
  // message = "Working" // optional
7
7
  // }
8
8
  //
9
- // Emits `notifications/progress` on the current MCP response stream
10
- // (cloud-client mvp:mcp_progress). A value that does not strictly increase is
9
+ // Emits `notifications/progress` on the current MCP response stream.
10
+ // A value that does not strictly increase is
11
11
  // dropped at run time. NO `as $var` binding.
12
12
 
13
13
  /**
@@ -9,8 +9,8 @@ import { PublishToken } from "../../../lexer/realtime.js";
9
9
  // auth_id = $auth.id // optional
10
10
  // }
11
11
  //
12
- // Injects a server-authored event onto a realtime v2 channel (cloud-client
13
- // mvp:realtime_publish). `realtime_server` + `channel` + `data` are required;
12
+ // Injects a server-authored event onto a realtime v2 channel.
13
+ // `realtime_server` + `channel` + `data` are required;
14
14
  // `message`/`auth_table`/`auth_id` are optional. Flat attrs (the PHP transform
15
15
  // maps auth_table/auth_id into the nested context.auth.{dbo_id,row_id} shape).
16
16
  // Unlike realtime.get_session it has NO `as $var` binding.
@@ -11,7 +11,7 @@ import { getVarName } from "./generic/utils.js";
11
11
 
12
12
  // First-class MCP `prompt` and `resource` workspace objects (MCP 2026-07-28),
13
13
  // attached to an `mcp_server` through its `prompts` / `resources` blocks.
14
- // Grounded against cloud-client's `script/kind/schema/core/{prompt,resource}.yaml`.
14
+ // Grounded against the Xano backend's XanoScript schema.
15
15
  //
16
16
  // prompt code_review { resource regional_order {
17
17
  // title = "Code review" uri = "orders://{region}/{order_id}"
@@ -22,7 +22,7 @@ import { getVarName } from "./generic/utils.js";
22
22
  // } response = $order
23
23
  // }
24
24
 
25
- /** The input field types a resource uri variable can be bound to (ResourceUri::VARIABLE_TYPES). */
25
+ /** The input field types a resource uri variable can be bound to. */
26
26
  export const RESOURCE_URI_VARIABLE_TYPES = [
27
27
  "text",
28
28
  "int",
@@ -39,7 +39,7 @@ const MIME_TYPE =
39
39
 
40
40
  /**
41
41
  * Parse a resource uri: a literal URI, or an RFC 6570 LEVEL-1 template.
42
- * Mirrors cloud-client's `ResourceUri::parse`.
42
+ * Mirrors the backend's resource URI parser.
43
43
  * @param {string} uri
44
44
  * @returns {{ problem: string|null, variables: string[] }}
45
45
  */
@@ -103,7 +103,7 @@ export function parseResourceUri(uri) {
103
103
 
104
104
  /**
105
105
  * Why a resource's uri variables and its input block disagree, or null.
106
- * Mirrors cloud-client's `ResourceUri::inputProblem`.
106
+ * Mirrors the backend's check that URI variables match `input`.
107
107
  * @param {string[]} variables
108
108
  * @param {Object<string, {type: string, iterable: boolean}>} inputs
109
109
  * @returns {string|null}
@@ -98,8 +98,7 @@ export function mcpServerDeclaration($) {
98
98
  },
99
99
  },
100
100
  {
101
- // Per-server OAuth (cloud-client mcp_server.yaml `oauth`; values are
102
- // validated server-side by helper/mvp/mcpoauth/OauthConfig.php).
101
+ // Per-server OAuth (values are validated server-side).
103
102
  // An assignment block like a channel's `publish`. mcp_server ONLY:
104
103
  // deliberately absent from agent_parser.js. Mode-specific rules
105
104
  // (hosted needs login_url, external needs issuer, ...) stay
@@ -82,8 +82,8 @@ export function messageDeclaration($) {
82
82
  },
83
83
  },
84
84
  {
85
- // backend Message.php `encode()` emits `docs = "..."` for a
86
- // documented message; round-tripped by `decode()`. See DEV-7620.
85
+ // the backend emits `docs = "..."` for a
86
+ // documented message; round-tripped on push.
87
87
  GATE: () => !hasDocs,
88
88
  ALT: () => {
89
89
  hasDocs = true;
@@ -91,7 +91,7 @@ export function messageDeclaration($) {
91
91
  },
92
92
  },
93
93
  {
94
- // backend Message.php `encode()` emits `active = false` for a
94
+ // the backend emits `active = false` for a
95
95
  // disabled message; mirrors the sibling `channel` declaration.
96
96
  GATE: () => !hasActive,
97
97
  ALT: () => {
@@ -25,8 +25,7 @@ import { Identifier, NewlineToken, TypeToken } from "../lexer/tokens.js";
25
25
 
26
26
  // Grammar for the top-level `microservice <name> { ... }` entity.
27
27
  //
28
- // Grounded against cloud-client's authoritative XanoScript schema DSL
29
- // (extensions/MVP/includes/xano/script/kind/schema/{core,component}/*.yaml):
28
+ // Grounded against the Xano backend's XanoScript schema:
30
29
  //
31
30
  // microservice <name> {
32
31
  // description|guid|kind|tenant_deploy = "..." # static:text (optional)
package/parser/parser.js CHANGED
@@ -80,7 +80,7 @@ export function xanoscriptParser(text, scheme, preTokenized = null) {
80
80
  }
81
81
  for (const error of lexResult.errors) {
82
82
  // The platform names the first character it cannot read; the lexer skips a run of them.
83
- // Quoted as PHP's escapeshellarg quotes it (Unexpected.php): a `'` is written `'\''`.
83
+ // Quoted as PHP's escapeshellarg quotes it: a `'` is written `'\''`.
84
84
  const first = String.fromCodePoint(text.codePointAt(error.offset)).replaceAll("'", "'\\''");
85
85
  parser.addInvalidValueError({
86
86
  image: text.slice(error.offset, error.offset + error.length),
@@ -9,7 +9,7 @@
9
9
  * language server checks the policy's block shape only.
10
10
  *
11
11
  * A host fills it:
12
- * - Studio sends its own live answer (`source: "live"`); a check-level problem is then an
12
+ * - The Xano editor sends its own live answer (`source: "live"`); a check-level problem is then an
13
13
  * error, because the instance refuses the same save.
14
14
  * - The Node server and offline tools load the generated snapshot (`catalogueSnapshot.js`,
15
15
  * `source: "snapshot"`); a check-level problem is then a warning, because the snapshot can
@@ -30,7 +30,7 @@ let version = 0;
30
30
 
31
31
  /**
32
32
  * Replace the catalogue.
33
- * @param {Object|Object[]|null} catalogue `{items, document}` as the platform serves it, Studio's
33
+ * @param {Object|Object[]|null} catalogue `{items, document}` as the platform serves it, the editor's
34
34
  * `{checks, document}`, or a bare list of checks; null clears it
35
35
  * @param {{source?: "live"|"snapshot", generatedAt?: string|null}} [options]
36
36
  */
@@ -45,7 +45,7 @@ export const isA = (token, type) => !!token && tokenMatcher(token, type);
45
45
  /** A catalogue entry's own property, never an inherited one (`constructor`, `toString`). */
46
46
  export const own = (object, key) => object && Object.hasOwn(object, key) ? object[key] : undefined;
47
47
 
48
- /** Native Parser::stripQuotes: double quotes use C escapes, single quotes only \\ and \'. */
48
+ /** As the platform reads a string: double quotes use C escapes, single quotes only \\ and \'. */
49
49
  export function policyStringValue(image) {
50
50
  if (image.startsWith('"""') || image.startsWith("'''")) {
51
51
  return image.slice(3, -3).replace(/^\r?\n/, "").replace(/\r?\n[ ]*$/, "");
@@ -6,8 +6,7 @@ import { policyLiteralValue } from "./policyLiteral.js";
6
6
  * Everything here is read from the catalogue the host loaded (see `catalogue.js`): the check
7
7
  * ids, each check's params with their types, closed value sets, nested fields, `required`,
8
8
  * `requires_one_of`, `minimum` and `non_empty`, and the scope fields that apply to it. Nothing
9
- * names a check. The findings are worded like the platform's refusals (`Registry::validateParams`,
10
- * `coerce`, `unknownCheck`) without their `rule[i] ("KEY.Rn"): params:` prefix, since a diagnostic
9
+ * names a check. The findings are worded like the platform's refusals without their `rule[i] ("KEY.Rn"): params:` prefix, since a diagnostic
11
10
  * already points at the place; they are a preview, and the platform's own answer on save decides.
12
11
  * Where the platform stops at the first refusal, this reports every problem, one per param.
13
12
  *
@@ -15,17 +14,17 @@ import { policyLiteralValue } from "./policyLiteral.js";
15
14
  * holds at most `STRING_MAX_LENGTH` characters, and a name longer than `NEAREST_MAX_LENGTH` bytes
16
15
  * gets no near miss.
17
16
  *
18
- * Check-specific rules the catalogue does not publish (`Check::validate`, `authoredRefusal`,
17
+ * Check-specific rules the catalogue does not publish (per-check validation,
19
18
  * renamed ids and params, statement wildcards) are left to the platform.
20
19
  *
21
20
  * Whether a value is a literal at all, and duplicate names, are the block's shape, not the
22
21
  * catalogue's: the parser reports those (policy_parser.js) and this skips such values.
23
22
  */
24
23
 
25
- /** `Registry::STRING_MAX_LENGTH`: the longest string a string or string[] param may hold, in characters. */
24
+ /** The platform's limit on the longest string a string or string[] param may hold, in characters. */
26
25
  const STRING_MAX_LENGTH = 1024;
27
26
 
28
- /** `Registry::NEAREST_MAX_LENGTH`: the longest name, in bytes, a near miss is looked for. */
27
+ /** The platform's limit on the longest name, in bytes, a near miss is looked for. */
29
28
  const NEAREST_MAX_LENGTH = 128;
30
29
 
31
30
  /** Edit distance, as PHP's `levenshtein` (insert, delete and replace all cost 1). */
@@ -49,7 +48,7 @@ function levenshtein(a, b) {
49
48
  }
50
49
 
51
50
  /**
52
- * The closest names to a misspelling, nearest first, at most two (`Registry::nearest`): within
51
+ * The closest names to a misspelling, nearest first, at most two, as the platform suggests them: within
53
52
  * three edits, or one containing the other. A name longer than `NEAREST_MAX_LENGTH` bytes gets
54
53
  * none, since edit distance costs the product of the two lengths per candidate.
55
54
  * @param {string} wanted
@@ -79,7 +78,7 @@ export function didYouMean(name, candidates) {
79
78
  return near.length ? `Did you mean ${near.map((n) => `"${n}"`).join(" or ")}? ` : "";
80
79
  }
81
80
 
82
- /** `Registry::provided`: set when not null/false, not an empty or blank string, not an empty list or object. */
81
+ /** Whether a param counts as provided: set when not null/false, not an empty or blank string, not an empty list or object. */
83
82
  function provided(value) {
84
83
  if (value === null || value === undefined || value === false) return false;
85
84
  if (typeof value === "string") return value.trim() !== "";
@@ -89,7 +88,7 @@ function provided(value) {
89
88
  }
90
89
 
91
90
  /**
92
- * A param's value as the platform validates it (`Registry::validateParams`): one left out, or written
91
+ * A param's value as the platform validates it: one left out, or written
93
92
  * as `null` when its type is not `any`, is its default.
94
93
  */
95
94
  const inForce = (literal, schema, name) =>
@@ -107,7 +106,7 @@ export function unknownCheckMessage(id, ids) {
107
106
  }
108
107
 
109
108
  /**
110
- * `Registry::coerce`: whether a value has its param's declared type and values.
109
+ * Whether a value has its param's declared type and values.
111
110
  * @returns {{token: Object, message: string}|null}
112
111
  */
113
112
  function coerce(node, spec, checkId, name) {
@@ -124,7 +123,7 @@ function coerce(node, spec, checkId, name) {
124
123
  if (!whole || (minimum !== undefined && node.value < minimum)) {
125
124
  return fail(`be a whole number${minimum !== undefined ? ` of at least ${minimum}` : ""}`);
126
125
  }
127
- // `Registry::INTEGER_MAX`: past 2^53 - 1 a whole number is no longer exact.
126
+ // The platform's integer limit: past 2^53 - 1 a whole number is no longer exact.
128
127
  if (Math.abs(node.value) > Number.MAX_SAFE_INTEGER) {
129
128
  return fail(`be a whole number no larger than ${Number.MAX_SAFE_INTEGER}`);
130
129
  }
@@ -188,7 +187,7 @@ function closedProperties(spec) {
188
187
 
189
188
  /**
190
189
  * Why a set scope field cannot change what a rule of this check selects, or null when it can,
191
- * read from the check's published `scope_fields` (the same reading as Studio's rule form).
190
+ * read from the check's published `scope_fields` (the same reading as the Xano editor's rule form).
192
191
  */
193
192
  function inapplicableScope(check, name, literal) {
194
193
  const field = (check.scope_fields ?? []).find((item) => item?.name === name);
@@ -21,10 +21,10 @@ import { didYouMean, validatePolicyRule } from "./policy/validatePolicyRule.js";
21
21
  // rule { <field> = <literal> } # repeating; `check` names a catalogue item, `params` its params
22
22
  // }
23
23
  //
24
- // with the comment, named-rule and literal-value rules of cloud-client's Script.php. It knows no
24
+ // with the comment, named-rule and literal-value rules the platform enforces. It knows no
25
25
  // field: which fields a policy and a rule may set, their types, value sets and defaults, the retired
26
26
  // fields and reserved blocks, the key pattern and when a rule is required all come from the
27
- // catalogue's `document` (helper/mvp/policy/Document.php), and every check from its `items`. With no
27
+ // catalogue's `document`, and every check from its `items`. With no
28
28
  // catalogue loaded, only the shape is checked. See parser/policy/catalogue.js.
29
29
 
30
30
  /** The platform's refusal of a named rule, after its `rule[<i>]: ` prefix. */
@@ -287,7 +287,7 @@ function fieldProblem(name, node, spec) {
287
287
  }
288
288
 
289
289
  /**
290
- * A rule's params: an object of literal values (Script::literal; a name written twice is already
290
+ * A rule's params: an object of literal values (a name written twice is already
291
291
  * reported by the object grammar). The catalogue then says what the object may hold.
292
292
  */
293
293
  function checkParamsShape(node, report) {
@@ -33,7 +33,7 @@ export function realtimeChannelDeclaration($) {
33
33
  anonymous_clients: "[boolean]",
34
34
  nested_channels: "[boolean]",
35
35
  // The CLI pull emits `message_history: history ?? 0` (an arbitrary
36
- // integer — see cloud-client RealtimeChannel.php `onSettings`), so
36
+ // integer), so
37
37
  // accept any numeric value rather than a fixed enum. Kept typed
38
38
  // ("[number]") so a non-numeric value still surfaces a diagnostic.
39
39
  message_history: "[number]",