@xano/developer-mcp 1.0.51 → 1.0.53
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/dist/cli_docs/index.d.ts +6 -0
- package/dist/cli_docs/index.js +6 -0
- package/dist/meta_api_docs/index.d.ts +6 -0
- package/dist/meta_api_docs/index.js +6 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/mcp_version.d.ts +6 -0
- package/dist/tools/mcp_version.js +6 -0
- package/dist/tools/validate_xanoscript.d.ts +6 -0
- package/dist/tools/validate_xanoscript.js +6 -0
- package/dist/tools/xanoscript_docs.d.ts +7 -0
- package/dist/tools/xanoscript_docs.js +8 -1
- package/dist/xanoscript.js +27 -36
- package/dist/xanoscript.test.js +5 -10
- package/dist/xanoscript_docs/cheatsheet.md +17 -15
- package/dist/xanoscript_docs/debugging.md +1 -1
- package/dist/xanoscript_docs/quickstart.md +36 -4
- package/dist/xanoscript_docs/security.md +2 -2
- package/dist/xanoscript_docs/syntax.md +23 -0
- package/dist/xanoscript_docs/unit-testing.md +12 -12
- package/package.json +1 -1
package/dist/cli_docs/index.d.ts
CHANGED
|
@@ -26,6 +26,12 @@ export declare function handleCliDocs(args: CliDocsArgs): string;
|
|
|
26
26
|
*/
|
|
27
27
|
export declare const cliDocsToolDefinition: {
|
|
28
28
|
name: string;
|
|
29
|
+
annotations: {
|
|
30
|
+
readOnlyHint: boolean;
|
|
31
|
+
destructiveHint: boolean;
|
|
32
|
+
idempotentHint: boolean;
|
|
33
|
+
openWorldHint: boolean;
|
|
34
|
+
};
|
|
29
35
|
description: string;
|
|
30
36
|
inputSchema: {
|
|
31
37
|
type: string;
|
package/dist/cli_docs/index.js
CHANGED
|
@@ -59,6 +59,12 @@ export function handleCliDocs(args) {
|
|
|
59
59
|
*/
|
|
60
60
|
export const cliDocsToolDefinition = {
|
|
61
61
|
name: "cli_docs",
|
|
62
|
+
annotations: {
|
|
63
|
+
readOnlyHint: true,
|
|
64
|
+
destructiveHint: false,
|
|
65
|
+
idempotentHint: true,
|
|
66
|
+
openWorldHint: false,
|
|
67
|
+
},
|
|
62
68
|
description: `Get documentation for the Xano CLI. Use this to understand how to use the CLI for local development, code sync, and XanoScript execution.
|
|
63
69
|
|
|
64
70
|
## Topics
|
|
@@ -26,6 +26,12 @@ export declare function handleMetaApiDocs(args: MetaApiDocsArgs): string;
|
|
|
26
26
|
*/
|
|
27
27
|
export declare const metaApiDocsToolDefinition: {
|
|
28
28
|
name: string;
|
|
29
|
+
annotations: {
|
|
30
|
+
readOnlyHint: boolean;
|
|
31
|
+
destructiveHint: boolean;
|
|
32
|
+
idempotentHint: boolean;
|
|
33
|
+
openWorldHint: boolean;
|
|
34
|
+
};
|
|
29
35
|
description: string;
|
|
30
36
|
inputSchema: {
|
|
31
37
|
type: string;
|
|
@@ -77,6 +77,12 @@ export function handleMetaApiDocs(args) {
|
|
|
77
77
|
*/
|
|
78
78
|
export const metaApiDocsToolDefinition = {
|
|
79
79
|
name: "meta_api_docs",
|
|
80
|
+
annotations: {
|
|
81
|
+
readOnlyHint: true,
|
|
82
|
+
destructiveHint: false,
|
|
83
|
+
idempotentHint: true,
|
|
84
|
+
openWorldHint: false,
|
|
85
|
+
},
|
|
80
86
|
description: `Get documentation for Xano's Meta API. Use this to understand how to programmatically manage Xano workspaces, databases, APIs, functions, agents, and more.
|
|
81
87
|
|
|
82
88
|
## Topics
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -42,6 +42,12 @@ export { validateXanoscriptToolDefinition, xanoscriptDocsToolDefinition, mcpVers
|
|
|
42
42
|
*/
|
|
43
43
|
export declare const toolDefinitions: ({
|
|
44
44
|
name: string;
|
|
45
|
+
annotations: {
|
|
46
|
+
readOnlyHint: boolean;
|
|
47
|
+
destructiveHint: boolean;
|
|
48
|
+
idempotentHint: boolean;
|
|
49
|
+
openWorldHint: boolean;
|
|
50
|
+
};
|
|
45
51
|
description: string;
|
|
46
52
|
inputSchema: {
|
|
47
53
|
type: string;
|
|
@@ -50,6 +56,12 @@ export declare const toolDefinitions: ({
|
|
|
50
56
|
};
|
|
51
57
|
} | {
|
|
52
58
|
name: string;
|
|
59
|
+
annotations: {
|
|
60
|
+
readOnlyHint: boolean;
|
|
61
|
+
destructiveHint: boolean;
|
|
62
|
+
idempotentHint: boolean;
|
|
63
|
+
openWorldHint: boolean;
|
|
64
|
+
};
|
|
53
65
|
description: string;
|
|
54
66
|
inputSchema: {
|
|
55
67
|
type: string;
|
|
@@ -36,6 +36,12 @@ export declare function mcpVersion(): McpVersionResult;
|
|
|
36
36
|
export declare function mcpVersionTool(): ToolResult;
|
|
37
37
|
export declare const mcpVersionToolDefinition: {
|
|
38
38
|
name: string;
|
|
39
|
+
annotations: {
|
|
40
|
+
readOnlyHint: boolean;
|
|
41
|
+
destructiveHint: boolean;
|
|
42
|
+
idempotentHint: boolean;
|
|
43
|
+
openWorldHint: boolean;
|
|
44
|
+
};
|
|
39
45
|
description: string;
|
|
40
46
|
inputSchema: {
|
|
41
47
|
type: string;
|
|
@@ -84,6 +84,12 @@ export function mcpVersionTool() {
|
|
|
84
84
|
// =============================================================================
|
|
85
85
|
export const mcpVersionToolDefinition = {
|
|
86
86
|
name: "mcp_version",
|
|
87
|
+
annotations: {
|
|
88
|
+
readOnlyHint: true,
|
|
89
|
+
destructiveHint: false,
|
|
90
|
+
idempotentHint: true,
|
|
91
|
+
openWorldHint: false,
|
|
92
|
+
},
|
|
87
93
|
description: "Get the current version of the Xano Developer MCP server. " +
|
|
88
94
|
"Returns the version string from package.json.",
|
|
89
95
|
inputSchema: {
|
|
@@ -109,6 +109,12 @@ export declare function validateXanoscript(args: ValidateXanoscriptArgs): Valida
|
|
|
109
109
|
export declare function validateXanoscriptTool(args: ValidateXanoscriptArgs): ToolResult;
|
|
110
110
|
export declare const validateXanoscriptToolDefinition: {
|
|
111
111
|
name: string;
|
|
112
|
+
annotations: {
|
|
113
|
+
readOnlyHint: boolean;
|
|
114
|
+
destructiveHint: boolean;
|
|
115
|
+
idempotentHint: boolean;
|
|
116
|
+
openWorldHint: boolean;
|
|
117
|
+
};
|
|
112
118
|
description: string;
|
|
113
119
|
inputSchema: {
|
|
114
120
|
type: string;
|
|
@@ -388,6 +388,12 @@ export function validateXanoscriptTool(args) {
|
|
|
388
388
|
// =============================================================================
|
|
389
389
|
export const validateXanoscriptToolDefinition = {
|
|
390
390
|
name: "validate_xanoscript",
|
|
391
|
+
annotations: {
|
|
392
|
+
readOnlyHint: true,
|
|
393
|
+
destructiveHint: false,
|
|
394
|
+
idempotentHint: true,
|
|
395
|
+
openWorldHint: false,
|
|
396
|
+
},
|
|
391
397
|
description: "Validate XanoScript code for syntax errors. Supports multiple input methods:\n" +
|
|
392
398
|
"- code: Raw XanoScript code as a string\n" +
|
|
393
399
|
"- file_path: Path to a single .xs file (easier than escaping code!)\n" +
|
|
@@ -50,11 +50,18 @@ export declare function xanoscriptDocsTool(args?: XanoscriptDocsArgs): ToolResul
|
|
|
50
50
|
export declare const xanoscriptDocsToolDefinition: {
|
|
51
51
|
name: string;
|
|
52
52
|
description: string;
|
|
53
|
+
annotations: {
|
|
54
|
+
readOnlyHint: boolean;
|
|
55
|
+
destructiveHint: boolean;
|
|
56
|
+
idempotentHint: boolean;
|
|
57
|
+
openWorldHint: boolean;
|
|
58
|
+
};
|
|
53
59
|
inputSchema: {
|
|
54
60
|
type: string;
|
|
55
61
|
properties: {
|
|
56
62
|
topic: {
|
|
57
63
|
type: string;
|
|
64
|
+
enum: string[];
|
|
58
65
|
description: string;
|
|
59
66
|
};
|
|
60
67
|
file_path: {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { readFileSync } from "fs";
|
|
8
8
|
import { dirname, join } from "path";
|
|
9
9
|
import { fileURLToPath } from "url";
|
|
10
|
-
import { readXanoscriptDocsV2, getTopicDescriptions, } from "../xanoscript.js";
|
|
10
|
+
import { readXanoscriptDocsV2, getTopicNames, getTopicDescriptions, } from "../xanoscript.js";
|
|
11
11
|
// =============================================================================
|
|
12
12
|
// Path Resolution
|
|
13
13
|
// =============================================================================
|
|
@@ -105,11 +105,18 @@ export const xanoscriptDocsToolDefinition = {
|
|
|
105
105
|
"Call without parameters for overview (README). " +
|
|
106
106
|
"Use 'topic' for specific documentation, or 'file_path' for context-aware docs based on the file you're editing. " +
|
|
107
107
|
"Use mode='quick_reference' for compact syntax cheatsheet (recommended for context efficiency).",
|
|
108
|
+
annotations: {
|
|
109
|
+
readOnlyHint: true,
|
|
110
|
+
destructiveHint: false,
|
|
111
|
+
idempotentHint: true,
|
|
112
|
+
openWorldHint: false,
|
|
113
|
+
},
|
|
108
114
|
inputSchema: {
|
|
109
115
|
type: "object",
|
|
110
116
|
properties: {
|
|
111
117
|
topic: {
|
|
112
118
|
type: "string",
|
|
119
|
+
enum: getTopicNames(),
|
|
113
120
|
description: "Documentation topic to retrieve. Call without any parameters to get the README overview. " +
|
|
114
121
|
"Example: topic='syntax' for language syntax, topic='database' for database operations, topic='types' for type system.\n\n" +
|
|
115
122
|
"Available topics:\n" + getTopicDescriptions(),
|
package/dist/xanoscript.js
CHANGED
|
@@ -243,47 +243,38 @@ export function getXanoscriptDocsVersion(docsPath) {
|
|
|
243
243
|
export function readXanoscriptDocsV2(docsPath, args) {
|
|
244
244
|
const mode = args?.mode || "full";
|
|
245
245
|
const version = getXanoscriptDocsVersion(docsPath);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
return `No documentation found for file pattern: ${args.file_path}\n\nAvailable topics: ${Object.keys(XANOSCRIPT_DOCS_V2).join(", ")}`;
|
|
257
|
-
}
|
|
258
|
-
const docs = topics.map((t) => {
|
|
259
|
-
const config = XANOSCRIPT_DOCS_V2[t];
|
|
260
|
-
const content = readFileSync(join(docsPath, config.file), "utf-8");
|
|
261
|
-
return mode === "quick_reference"
|
|
262
|
-
? extractQuickReference(content, t)
|
|
263
|
-
: content;
|
|
264
|
-
});
|
|
265
|
-
const header = `# XanoScript Documentation for: ${args.file_path}\n\nMatched topics: ${topics.join(", ")}\nMode: ${mode}\nVersion: ${version}\n\n---\n\n`;
|
|
266
|
-
return header + docs.join("\n\n---\n\n");
|
|
246
|
+
// Default: return README
|
|
247
|
+
if (!args?.topic && !args?.file_path) {
|
|
248
|
+
const readme = readFileSync(join(docsPath, "README.md"), "utf-8");
|
|
249
|
+
return `${readme}\n\n---\nDocumentation version: ${version}`;
|
|
250
|
+
}
|
|
251
|
+
// Context-aware: return docs matching file pattern
|
|
252
|
+
if (args?.file_path) {
|
|
253
|
+
const topics = getDocsForFilePath(args.file_path);
|
|
254
|
+
if (topics.length === 0) {
|
|
255
|
+
throw new Error(`No documentation found for file pattern: ${args.file_path}\n\nAvailable topics: ${Object.keys(XANOSCRIPT_DOCS_V2).join(", ")}`);
|
|
267
256
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const config = XANOSCRIPT_DOCS_V2[args.topic];
|
|
271
|
-
if (!config) {
|
|
272
|
-
const availableTopics = Object.keys(XANOSCRIPT_DOCS_V2).join(", ");
|
|
273
|
-
return `Error: Unknown topic "${args.topic}".\n\nAvailable topics: ${availableTopics}`;
|
|
274
|
-
}
|
|
257
|
+
const docs = topics.map((t) => {
|
|
258
|
+
const config = XANOSCRIPT_DOCS_V2[t];
|
|
275
259
|
const content = readFileSync(join(docsPath, config.file), "utf-8");
|
|
276
|
-
|
|
277
|
-
? extractQuickReference(content,
|
|
260
|
+
return mode === "quick_reference"
|
|
261
|
+
? extractQuickReference(content, t)
|
|
278
262
|
: content;
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
return
|
|
263
|
+
});
|
|
264
|
+
const header = `# XanoScript Documentation for: ${args.file_path}\n\nMatched topics: ${topics.join(", ")}\nMode: ${mode}\nVersion: ${version}\n\n---\n\n`;
|
|
265
|
+
return header + docs.join("\n\n---\n\n");
|
|
282
266
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
267
|
+
// Topic-based: return specific doc
|
|
268
|
+
const config = XANOSCRIPT_DOCS_V2[args.topic];
|
|
269
|
+
if (!config) {
|
|
270
|
+
const availableTopics = Object.keys(XANOSCRIPT_DOCS_V2).join(", ");
|
|
271
|
+
throw new Error(`Unknown topic "${args.topic}".\n\nAvailable topics: ${availableTopics}`);
|
|
286
272
|
}
|
|
273
|
+
const content = readFileSync(join(docsPath, config.file), "utf-8");
|
|
274
|
+
const doc = mode === "quick_reference"
|
|
275
|
+
? extractQuickReference(content, args.topic)
|
|
276
|
+
: content;
|
|
277
|
+
return `${doc}\n\n---\nDocumentation version: ${version}`;
|
|
287
278
|
}
|
|
288
279
|
/**
|
|
289
280
|
* Get available topic names
|
package/dist/xanoscript.test.js
CHANGED
|
@@ -126,7 +126,7 @@ describe("xanoscript module", () => {
|
|
|
126
126
|
expect(result).toContain("branch");
|
|
127
127
|
});
|
|
128
128
|
it("should match workspace.xs", () => {
|
|
129
|
-
const result = getDocsForFilePath("workspace.xs");
|
|
129
|
+
const result = getDocsForFilePath("workspace/workspace.xs");
|
|
130
130
|
expect(result).toContain("syntax");
|
|
131
131
|
expect(result).toContain("workspace");
|
|
132
132
|
});
|
|
@@ -236,10 +236,8 @@ Even more content.
|
|
|
236
236
|
const result = readXanoscriptDocsV2(DOCS_PATH, { topic: "syntax" });
|
|
237
237
|
expect(result).toContain("Documentation version:");
|
|
238
238
|
});
|
|
239
|
-
it("should
|
|
240
|
-
|
|
241
|
-
expect(result).toContain('Error: Unknown topic "nonexistent"');
|
|
242
|
-
expect(result).toContain("Available topics:");
|
|
239
|
+
it("should throw for unknown topic", () => {
|
|
240
|
+
expect(() => readXanoscriptDocsV2(DOCS_PATH, { topic: "nonexistent" })).toThrow('Unknown topic "nonexistent"');
|
|
243
241
|
});
|
|
244
242
|
it("should return context-aware docs for file_path", () => {
|
|
245
243
|
const result = readXanoscriptDocsV2(DOCS_PATH, {
|
|
@@ -272,11 +270,8 @@ Even more content.
|
|
|
272
270
|
});
|
|
273
271
|
expect(quickResult).toContain("Mode: quick_reference");
|
|
274
272
|
});
|
|
275
|
-
it("should
|
|
276
|
-
|
|
277
|
-
topic: "syntax",
|
|
278
|
-
});
|
|
279
|
-
expect(result).toContain("Error reading XanoScript documentation:");
|
|
273
|
+
it("should throw for invalid docs path", () => {
|
|
274
|
+
expect(() => readXanoscriptDocsV2("/nonexistent/path", { topic: "syntax" })).toThrow();
|
|
280
275
|
});
|
|
281
276
|
});
|
|
282
277
|
describe("getTopicNames", () => {
|
|
@@ -6,7 +6,9 @@ applyTo: "**/*.xs"
|
|
|
6
6
|
|
|
7
7
|
> **Purpose:** Quick reference for the 20 most common XanoScript patterns. For detailed documentation, use `xanoscript_docs({ topic: "<topic>" })`.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Quick Reference
|
|
10
|
+
|
|
11
|
+
### Variable Declaration
|
|
10
12
|
|
|
11
13
|
```xs
|
|
12
14
|
var $name { value = "initial" }
|
|
@@ -15,7 +17,7 @@ var $items { value = [] }
|
|
|
15
17
|
var $data { value = { key: "value" } }
|
|
16
18
|
```
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
### Conditionals
|
|
19
21
|
|
|
20
22
|
```xs
|
|
21
23
|
conditional {
|
|
@@ -33,7 +35,7 @@ conditional {
|
|
|
33
35
|
|
|
34
36
|
> **Note:** Use `elseif` (one word), not `else if`.
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
### Switch
|
|
37
39
|
|
|
38
40
|
```xs
|
|
39
41
|
switch ($input.status) {
|
|
@@ -51,7 +53,7 @@ switch ($input.status) {
|
|
|
51
53
|
|
|
52
54
|
> **Note:** `break` goes **after** the closing `}` of each `case` block. The `default` case does not need `break`.
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
### Loops
|
|
55
57
|
|
|
56
58
|
```xs
|
|
57
59
|
// For each loop
|
|
@@ -81,7 +83,7 @@ var $names { value = $items|map:$$.name }
|
|
|
81
83
|
var $active { value = $items|filter:$$.is_active }
|
|
82
84
|
```
|
|
83
85
|
|
|
84
|
-
|
|
86
|
+
### Database CRUD
|
|
85
87
|
|
|
86
88
|
```xs
|
|
87
89
|
// Get single record by field
|
|
@@ -113,7 +115,7 @@ db.edit "user" {
|
|
|
113
115
|
db.del "user" { field_name = "id", field_value = $input.user_id }
|
|
114
116
|
```
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
### API Requests
|
|
117
119
|
|
|
118
120
|
```xs
|
|
119
121
|
api.request {
|
|
@@ -133,7 +135,7 @@ api.request {
|
|
|
133
135
|
// $api_result.response.headers → Response headers
|
|
134
136
|
```
|
|
135
137
|
|
|
136
|
-
|
|
138
|
+
### Error Handling
|
|
137
139
|
|
|
138
140
|
```xs
|
|
139
141
|
// Precondition (stops execution if false)
|
|
@@ -159,7 +161,7 @@ throw {
|
|
|
159
161
|
}
|
|
160
162
|
```
|
|
161
163
|
|
|
162
|
-
|
|
164
|
+
### Error Types
|
|
163
165
|
|
|
164
166
|
| Type | HTTP Status | Use Case |
|
|
165
167
|
|------|-------------|----------|
|
|
@@ -168,7 +170,7 @@ throw {
|
|
|
168
170
|
| `notfound` | 404 | Resource doesn't exist |
|
|
169
171
|
| `standard` | 500 | General errors |
|
|
170
172
|
|
|
171
|
-
|
|
173
|
+
### Common Filters
|
|
172
174
|
|
|
173
175
|
```xs
|
|
174
176
|
// String
|
|
@@ -210,7 +212,7 @@ $num|round:2 // Round to 2 decimals
|
|
|
210
212
|
$num|abs // Absolute value
|
|
211
213
|
```
|
|
212
214
|
|
|
213
|
-
|
|
215
|
+
### Authentication Check
|
|
214
216
|
|
|
215
217
|
```xs
|
|
216
218
|
precondition ($auth.id != null) {
|
|
@@ -219,7 +221,7 @@ precondition ($auth.id != null) {
|
|
|
219
221
|
}
|
|
220
222
|
```
|
|
221
223
|
|
|
222
|
-
|
|
224
|
+
### Function Call
|
|
223
225
|
|
|
224
226
|
```xs
|
|
225
227
|
function.run "my_function" {
|
|
@@ -227,7 +229,7 @@ function.run "my_function" {
|
|
|
227
229
|
} as $result
|
|
228
230
|
```
|
|
229
231
|
|
|
230
|
-
|
|
232
|
+
### String Concatenation
|
|
231
233
|
|
|
232
234
|
```xs
|
|
233
235
|
// Basic
|
|
@@ -237,7 +239,7 @@ var $msg { value = "Hello, " ~ $input.name ~ "!" }
|
|
|
237
239
|
var $msg { value = ($status|to_text) ~ ": " ~ ($data|json_encode) }
|
|
238
240
|
```
|
|
239
241
|
|
|
240
|
-
|
|
242
|
+
### Common Type Names
|
|
241
243
|
|
|
242
244
|
| Use This | Not This |
|
|
243
245
|
|----------|----------|
|
|
@@ -247,11 +249,11 @@ var $msg { value = ($status|to_text) ~ ": " ~ ($data|json_encode) }
|
|
|
247
249
|
| `decimal` | float, number |
|
|
248
250
|
| `type[]` | array, list |
|
|
249
251
|
|
|
250
|
-
|
|
252
|
+
### Reserved Variables (Cannot Use)
|
|
251
253
|
|
|
252
254
|
`$response`, `$output`, `$input`, `$auth`, `$env`, `$db`, `$this`, `$result`, `$index`
|
|
253
255
|
|
|
254
|
-
|
|
256
|
+
### Input Block Syntax
|
|
255
257
|
|
|
256
258
|
`?` after the **type** = nullable, `?` after the **variable name** = optional (not required).
|
|
257
259
|
|
|
@@ -10,6 +10,36 @@ Essential patterns for XanoScript development. Use this as a quick reference for
|
|
|
10
10
|
|
|
11
11
|
## Quick Reference
|
|
12
12
|
|
|
13
|
+
### Variable Access Rules
|
|
14
|
+
|
|
15
|
+
**Inputs must use `$input.fieldname` — no shorthand exists.**
|
|
16
|
+
|
|
17
|
+
Input fields declared in the `input {}` block are only accessible via the `$input` prefix. You cannot reference them as bare variables.
|
|
18
|
+
|
|
19
|
+
```xs
|
|
20
|
+
input {
|
|
21
|
+
text name
|
|
22
|
+
int age
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ❌ Wrong — $name is not defined; inputs live on $input
|
|
26
|
+
var $greeting { value = "Hello, " ~ $name }
|
|
27
|
+
|
|
28
|
+
// ✅ Correct
|
|
29
|
+
var $greeting { value = "Hello, " ~ $input.name }
|
|
30
|
+
var $is_adult { value = $input.age >= 18 }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Stack variables have an optional `$var.` prefix — both forms are identical.**
|
|
34
|
+
|
|
35
|
+
```xs
|
|
36
|
+
var $total { value = 100 }
|
|
37
|
+
|
|
38
|
+
// ✅ Both are the same thing
|
|
39
|
+
$var.total // explicit prefix form
|
|
40
|
+
$total // shorthand form (no prefix)
|
|
41
|
+
```
|
|
42
|
+
|
|
13
43
|
### Reserved Variable Names
|
|
14
44
|
|
|
15
45
|
These variable names are reserved and cannot be used:
|
|
@@ -268,7 +298,7 @@ precondition ($input.email|contains:"@") {
|
|
|
268
298
|
| `db.query` | Filtered list | `db.query "users" { where = $db.users.active == true } as $users` |
|
|
269
299
|
| `db.add` | Insert | `db.add "users" { data = { name: "John" } } as $new` |
|
|
270
300
|
| `db.edit` | Update | `db.edit "users" { field_name = "id" field_value = 1 data = { name: "Jane" } }` |
|
|
271
|
-
| `db.
|
|
301
|
+
| `db.del` | Delete | `db.del "users" { field_name = "id" field_value = 1 }` |
|
|
272
302
|
|
|
273
303
|
> **Full reference:** See `xanoscript_docs({ topic: "database" })` for joins, bulk operations, transactions, and more.
|
|
274
304
|
|
|
@@ -293,9 +323,11 @@ var $data {
|
|
|
293
323
|
### 4. Loop Through Array
|
|
294
324
|
|
|
295
325
|
```xs
|
|
296
|
-
// Using
|
|
297
|
-
|
|
298
|
-
|
|
326
|
+
// Using foreach
|
|
327
|
+
foreach ($items) {
|
|
328
|
+
each as $item {
|
|
329
|
+
debug.log { value = $item.name }
|
|
330
|
+
}
|
|
299
331
|
}
|
|
300
332
|
|
|
301
333
|
// Using map filter
|
|
@@ -96,7 +96,7 @@ function "refresh_auth" {
|
|
|
96
96
|
} as $new_token
|
|
97
97
|
|
|
98
98
|
// Rotate refresh token
|
|
99
|
-
|
|
99
|
+
security.create_uuid as $new_refresh
|
|
100
100
|
|
|
101
101
|
db.edit "refresh_token" {
|
|
102
102
|
field_name = "id"
|
|
@@ -120,7 +120,7 @@ function "refresh_auth" {
|
|
|
120
120
|
function "create_session" {
|
|
121
121
|
input { int user_id }
|
|
122
122
|
stack {
|
|
123
|
-
|
|
123
|
+
security.create_uuid as $session_id
|
|
124
124
|
|
|
125
125
|
db.add "session" {
|
|
126
126
|
data = {
|
|
@@ -85,6 +85,29 @@ Working with...
|
|
|
85
85
|
|
|
86
86
|
## Quick Reference
|
|
87
87
|
|
|
88
|
+
### Variable Access Prefixes
|
|
89
|
+
|
|
90
|
+
| Prefix | Applies to | Shorthand? |
|
|
91
|
+
|--------|-----------|------------|
|
|
92
|
+
| `$input.field` | Input parameters | No — prefix always required |
|
|
93
|
+
| `$var.field` | Stack variables | Yes — `$field` is identical |
|
|
94
|
+
| `$auth.field` | Auth context | No |
|
|
95
|
+
| `$env.NAME` | Environment variables | No |
|
|
96
|
+
| `$db.table.field` | DB field refs (queries) | No |
|
|
97
|
+
|
|
98
|
+
```xs
|
|
99
|
+
// ❌ Wrong — input fields are NOT accessible as bare variables
|
|
100
|
+
var $name { value = $name } // undefined; inputs live on $input
|
|
101
|
+
|
|
102
|
+
// ✅ Correct — always use $input for input fields
|
|
103
|
+
var $name { value = $input.name }
|
|
104
|
+
|
|
105
|
+
// ✅ Both are valid for stack variables
|
|
106
|
+
var $total { value = 0 }
|
|
107
|
+
$var.total // explicit
|
|
108
|
+
$total // shorthand — same thing
|
|
109
|
+
```
|
|
110
|
+
|
|
88
111
|
### Operators
|
|
89
112
|
| Category | Operators |
|
|
90
113
|
|----------|-----------|
|
|
@@ -107,34 +107,34 @@ function "add" {
|
|
|
107
107
|
### Value Assertions
|
|
108
108
|
|
|
109
109
|
```xs
|
|
110
|
-
|
|
110
|
+
// Equality
|
|
111
111
|
expect.to_equal ($response.status) { value = "active" }
|
|
112
112
|
expect.to_not_equal ($response.status) { value = "deleted" }
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
// Boolean
|
|
115
115
|
expect.to_be_true ($response.is_active)
|
|
116
116
|
expect.to_be_false ($response.is_deleted)
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
// Null
|
|
119
119
|
expect.to_be_null ($response.deleted_at)
|
|
120
120
|
expect.to_not_be_null ($response.created_at)
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
// Defined
|
|
123
123
|
expect.to_be_defined ($response.id)
|
|
124
124
|
expect.to_not_be_defined ($response.optional_field)
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
// Empty
|
|
127
127
|
expect.to_be_empty ($response.errors)
|
|
128
128
|
```
|
|
129
129
|
|
|
130
130
|
### Comparison Assertions
|
|
131
131
|
|
|
132
132
|
```xs
|
|
133
|
-
|
|
133
|
+
// Numeric comparisons
|
|
134
134
|
expect.to_be_greater_than ($response.total) { value = 100 }
|
|
135
135
|
expect.to_be_less_than ($response.stock) { value = 10 }
|
|
136
136
|
|
|
137
|
-
|
|
137
|
+
// Range
|
|
138
138
|
expect.to_be_within ($response.temperature) {
|
|
139
139
|
min = 20
|
|
140
140
|
max = 30
|
|
@@ -144,14 +144,14 @@ expect.to_be_within ($response.temperature) {
|
|
|
144
144
|
### String Assertions
|
|
145
145
|
|
|
146
146
|
```xs
|
|
147
|
-
|
|
147
|
+
// Starts/ends with
|
|
148
148
|
expect.to_start_with ($response.name) { value = "John" }
|
|
149
149
|
expect.to_end_with ($response.file) { value = ".pdf" }
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
// Contains
|
|
152
152
|
expect.to_contain ($response.tags) { value = "featured" }
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
// Regex match
|
|
155
155
|
expect.to_match ($response.phone) { value = "^\\+1\\d{10}$" }
|
|
156
156
|
```
|
|
157
157
|
|
|
@@ -165,13 +165,13 @@ expect.to_be_in_the_future ($response.expires_at)
|
|
|
165
165
|
### Error Assertions
|
|
166
166
|
|
|
167
167
|
```xs
|
|
168
|
-
|
|
168
|
+
// Expects any error
|
|
169
169
|
test "throws on invalid input" {
|
|
170
170
|
input = { amount: -1 }
|
|
171
171
|
expect.to_throw
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
// Expects specific error
|
|
175
175
|
test "throws validation error" {
|
|
176
176
|
input = { amount: -1 }
|
|
177
177
|
expect.to_throw { value = "InvalidInputError" }
|
package/package.json
CHANGED