@xano/developer-mcp 1.0.32 → 1.0.34
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 +4 -4
- package/dist/cli_docs/topics/workspace.js +50 -6
- package/dist/xanoscript.js +5 -0
- package/dist/xanoscript.test.js +4 -2
- package/dist/xanoscript_docs/README.md +134 -37
- package/dist/xanoscript_docs/addons.md +2 -2
- package/dist/xanoscript_docs/agents.md +2 -2
- package/dist/xanoscript_docs/apis.md +9 -7
- package/dist/xanoscript_docs/branch.md +5 -4
- package/dist/xanoscript_docs/database.md +1 -1
- package/dist/xanoscript_docs/functions.md +2 -2
- package/dist/xanoscript_docs/integrations.md +76 -6
- package/dist/xanoscript_docs/mcp-servers.md +12 -12
- package/dist/xanoscript_docs/performance.md +1 -1
- package/dist/xanoscript_docs/quickstart.md +340 -0
- package/dist/xanoscript_docs/realtime.md +1 -1
- package/dist/xanoscript_docs/run.md +15 -14
- package/dist/xanoscript_docs/schema.md +1 -1
- package/dist/xanoscript_docs/security.md +1 -1
- package/dist/xanoscript_docs/streaming.md +1 -1
- package/dist/xanoscript_docs/syntax.md +82 -1
- package/dist/xanoscript_docs/tables.md +1 -1
- package/dist/xanoscript_docs/tasks.md +1 -1
- package/dist/xanoscript_docs/testing.md +1 -1
- package/dist/xanoscript_docs/tools.md +1 -1
- package/dist/xanoscript_docs/triggers.md +1 -1
- package/dist/xanoscript_docs/types.md +1 -1
- package/dist/xanoscript_docs/workspace.md +6 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -198,7 +198,7 @@ console.log(overview.documentation);
|
|
|
198
198
|
const syntaxDocs = xanoscriptDocs({ topic: 'syntax' });
|
|
199
199
|
|
|
200
200
|
// Get context-aware docs for a file path
|
|
201
|
-
const apiDocs = xanoscriptDocs({ file_path: '
|
|
201
|
+
const apiDocs = xanoscriptDocs({ file_path: 'api/users/create_post.xs' });
|
|
202
202
|
|
|
203
203
|
// Get compact quick reference
|
|
204
204
|
const quickRef = xanoscriptDocs({ topic: 'database', mode: 'quick_reference' });
|
|
@@ -326,7 +326,7 @@ Retrieves XanoScript programming language documentation with context-aware suppo
|
|
|
326
326
|
| Parameter | Type | Required | Description |
|
|
327
327
|
|-----------|------|----------|-------------|
|
|
328
328
|
| `topic` | string | No | Specific documentation topic to retrieve |
|
|
329
|
-
| `file_path` | string | No | File path being edited for context-aware docs (e.g., `
|
|
329
|
+
| `file_path` | string | No | File path being edited for context-aware docs (e.g., `api/users/create_post.xs`) |
|
|
330
330
|
| `mode` | string | No | `full` (default) or `quick_reference` for compact syntax cheatsheet |
|
|
331
331
|
|
|
332
332
|
**Available Topics:**
|
|
@@ -366,7 +366,7 @@ xanoscript_docs()
|
|
|
366
366
|
xanoscript_docs({ topic: "functions" })
|
|
367
367
|
|
|
368
368
|
// Context-aware: get all docs relevant to file being edited
|
|
369
|
-
xanoscript_docs({ file_path: "
|
|
369
|
+
xanoscript_docs({ file_path: "api/users/create_post.xs" })
|
|
370
370
|
|
|
371
371
|
// Compact quick reference (uses less context)
|
|
372
372
|
xanoscript_docs({ topic: "database", mode: "quick_reference" })
|
|
@@ -667,7 +667,7 @@ Compiles TypeScript to JavaScript in the `dist/` directory.
|
|
|
667
667
|
- Markdown files for XanoScript language reference
|
|
668
668
|
- Configured in `src/index.ts` via `XANOSCRIPT_DOCS_V2` with:
|
|
669
669
|
- **file**: The markdown file containing the documentation
|
|
670
|
-
- **applyTo**: Glob patterns for context-aware matching (e.g., `
|
|
670
|
+
- **applyTo**: Glob patterns for context-aware matching (e.g., `api/**/*.xs`)
|
|
671
671
|
- **description**: Human-readable description of the topic
|
|
672
672
|
|
|
673
673
|
**Meta API Documentation** (`src/meta_api_docs/`):
|
|
@@ -17,16 +17,59 @@ function: refresh_token
|
|
|
17
17
|
...
|
|
18
18
|
\`\`\`
|
|
19
19
|
|
|
20
|
-
When you pull, the CLI splits these into individual \`.xs\` files organized by type
|
|
20
|
+
When you pull, the CLI splits these into individual \`.xs\` files organized by type.
|
|
21
|
+
|
|
22
|
+
## Directory Structure
|
|
23
|
+
|
|
24
|
+
After \`workspace:pull\`, files are organized using snake_case naming:
|
|
25
|
+
|
|
26
|
+
\`\`\`
|
|
27
|
+
./xano-code/
|
|
28
|
+
├── workspace/
|
|
29
|
+
│ ├── my_workspace.xs # Workspace configuration
|
|
30
|
+
│ └── trigger/
|
|
31
|
+
│ └── on_deploy.xs # Workspace triggers
|
|
32
|
+
├── api/
|
|
33
|
+
│ └── users/ # API group folder (snake_case)
|
|
34
|
+
│ ├── api_group.xs # API group definition
|
|
35
|
+
│ ├── get_all.xs # GET /users
|
|
36
|
+
│ ├── get_one_get.xs # GET /users/:id
|
|
37
|
+
│ ├── create_post.xs # POST /users
|
|
38
|
+
│ └── nested/
|
|
39
|
+
│ └── profile_get.xs # GET /users/nested/profile
|
|
40
|
+
├── function/
|
|
41
|
+
│ └── validate_token.xs # Reusable functions
|
|
42
|
+
├── task/
|
|
43
|
+
│ └── daily_cleanup.xs # Scheduled tasks
|
|
44
|
+
├── table/
|
|
45
|
+
│ ├── users.xs # Table schema
|
|
46
|
+
│ └── trigger/
|
|
47
|
+
│ └── on_user_create.xs # Table triggers
|
|
48
|
+
├── agent/
|
|
49
|
+
│ ├── support_bot.xs # AI agents
|
|
50
|
+
│ └── trigger/
|
|
51
|
+
│ └── on_message.xs # Agent triggers
|
|
52
|
+
└── mcp_server/
|
|
53
|
+
├── my_server.xs # MCP server definitions
|
|
54
|
+
└── trigger/
|
|
55
|
+
└── on_connect.xs # MCP server triggers
|
|
56
|
+
\`\`\``,
|
|
21
57
|
ai_hints: `**Key concepts:**
|
|
22
58
|
- \`pull\` downloads workspace code and splits into organized .xs files
|
|
23
59
|
- \`push\` combines .xs files and uploads to Xano
|
|
24
|
-
- Files
|
|
60
|
+
- Files use snake_case naming for folders and filenames
|
|
61
|
+
- API endpoints are nested under their API group folder
|
|
25
62
|
|
|
26
63
|
**Typical workflow:**
|
|
27
64
|
1. \`xano workspace:pull ./xano-code\` - download
|
|
28
|
-
2.
|
|
29
|
-
3.
|
|
65
|
+
2. Navigate to \`api/{group}/\` for API endpoints, \`function/\` for functions, etc.
|
|
66
|
+
3. Edit .xs files with your editor/IDE
|
|
67
|
+
4. \`xano workspace:push ./xano-code\` - deploy
|
|
68
|
+
|
|
69
|
+
**File naming:**
|
|
70
|
+
- All folders and files use snake_case (e.g., \`my_function.xs\`, \`user_profile/\`)
|
|
71
|
+
- API endpoints include verb suffix (e.g., \`create_post.xs\`, \`get_one_get.xs\`)
|
|
72
|
+
- Triggers are nested under \`{type}/trigger/\` folders
|
|
30
73
|
|
|
31
74
|
**Version control:**
|
|
32
75
|
- The pulled directory structure is git-friendly
|
|
@@ -159,13 +202,14 @@ When you pull, the CLI splits these into individual \`.xs\` files organized by t
|
|
|
159
202
|
description: "Edit Xano code locally with your preferred tools",
|
|
160
203
|
steps: [
|
|
161
204
|
"Pull workspace: `xano workspace:pull ./code`",
|
|
162
|
-
"
|
|
205
|
+
"Navigate to organized folders: `api/{group}/`, `function/`, `table/`, etc.",
|
|
206
|
+
"Edit .xs files in your IDE (files use snake_case naming)",
|
|
163
207
|
"Validate changes: Use xanoscript_docs MCP tool",
|
|
164
208
|
"Push changes: `xano workspace:push ./code`",
|
|
165
209
|
"Test in Xano dashboard or via API"
|
|
166
210
|
],
|
|
167
211
|
example: `xano workspace:pull ./my-app
|
|
168
|
-
#
|
|
212
|
+
# Files organized: api/users/create_post.xs, function/validate_token.xs, etc.
|
|
169
213
|
xano workspace:push ./my-app`
|
|
170
214
|
},
|
|
171
215
|
{
|
package/dist/xanoscript.js
CHANGED
|
@@ -21,6 +21,11 @@ export const XANOSCRIPT_DOCS_V2 = {
|
|
|
21
21
|
applyTo: ["**/*.xs"],
|
|
22
22
|
description: "Expressions, operators, and filters for all XanoScript code",
|
|
23
23
|
},
|
|
24
|
+
quickstart: {
|
|
25
|
+
file: "quickstart.md",
|
|
26
|
+
applyTo: ["**/*.xs"],
|
|
27
|
+
description: "Common patterns, quick reference, and common mistakes to avoid",
|
|
28
|
+
},
|
|
24
29
|
types: {
|
|
25
30
|
file: "types.md",
|
|
26
31
|
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tools/**/*.xs", "agents/**/*.xs"],
|
package/dist/xanoscript.test.js
CHANGED
|
@@ -11,6 +11,7 @@ describe("xanoscript module", () => {
|
|
|
11
11
|
const expectedTopics = [
|
|
12
12
|
"readme",
|
|
13
13
|
"syntax",
|
|
14
|
+
"quickstart",
|
|
14
15
|
"types",
|
|
15
16
|
"tables",
|
|
16
17
|
"functions",
|
|
@@ -135,9 +136,10 @@ describe("xanoscript module", () => {
|
|
|
135
136
|
const result = getDocsForFilePath("apis/test.xs");
|
|
136
137
|
expect(result).not.toContain("readme");
|
|
137
138
|
});
|
|
138
|
-
it("should
|
|
139
|
+
it("should include syntax and quickstart for .xs files", () => {
|
|
139
140
|
const result = getDocsForFilePath("some/random/file.xs");
|
|
140
|
-
expect(result
|
|
141
|
+
expect(result).toContain("syntax");
|
|
142
|
+
expect(result).toContain("quickstart");
|
|
141
143
|
});
|
|
142
144
|
});
|
|
143
145
|
describe("extractQuickReference", () => {
|
|
@@ -4,45 +4,81 @@ XanoScript is the declarative scripting language for [Xano](https://xano.com), a
|
|
|
4
4
|
|
|
5
5
|
## Quick Reference
|
|
6
6
|
|
|
7
|
-
| Construct
|
|
8
|
-
|
|
|
9
|
-
| `
|
|
10
|
-
| `
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
|
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
17
|
-
| `
|
|
18
|
-
| `
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
7
|
+
| Construct | File Location | Purpose |
|
|
8
|
+
| ------------------- | ------------------------------------ | ----------------------------- |
|
|
9
|
+
| `workspace` | `workspace/{name}.xs` | Workspace-level configuration |
|
|
10
|
+
| `workspace_trigger` | `workspace/trigger/{name}.xs` | Workspace event handlers |
|
|
11
|
+
| `table` | `table/{name}.xs` | Database schema definition |
|
|
12
|
+
| `table_trigger` | `table/trigger/{name}.xs` | Table event handlers |
|
|
13
|
+
| `api_group` | `api/{group}/api_group.xs` | API group definition |
|
|
14
|
+
| `query` | `api/{group}/{endpoint}_{verb}.xs` | HTTP API endpoints |
|
|
15
|
+
| `function` | `function/{name}.xs` | Reusable logic blocks |
|
|
16
|
+
| `task` | `task/{name}.xs` | Scheduled/cron jobs |
|
|
17
|
+
| `agent` | `agent/{name}.xs` | AI-powered agents |
|
|
18
|
+
| `agent_trigger` | `agent/trigger/{name}.xs` | Agent event handlers |
|
|
19
|
+
| `tool` | `tool/{name}.xs` | Tools for AI agents |
|
|
20
|
+
| `mcp_server` | `mcp_server/{name}.xs` | MCP server definitions |
|
|
21
|
+
| `mcp_server_trigger`| `mcp_server/trigger/{name}.xs` | MCP server event handlers |
|
|
22
|
+
| `addon` | `addon/{name}.xs` | Subqueries for related data |
|
|
23
|
+
| `middleware` | `middleware/{name}.xs` | Request/response interceptors |
|
|
24
|
+
| `branch` | `branch.xs` | Branch-level configuration |
|
|
25
|
+
| `realtime_channel` | Configuration | Realtime channel settings |
|
|
26
|
+
|
|
27
|
+
**Naming convention:** All folder and file names use `snake_case` (e.g., `user_profile.xs`, `get_all_users_get.xs`).
|
|
22
28
|
|
|
23
29
|
**Important:** Each `.xs` file must contain exactly one definition. You cannot define multiple tables, functions, queries, or other constructs in a single file.
|
|
24
30
|
|
|
25
31
|
## Workspace Structure
|
|
26
32
|
|
|
33
|
+
After pulling from Xano, files are organized using `snake_case` naming:
|
|
34
|
+
|
|
27
35
|
```
|
|
28
36
|
project/
|
|
29
|
-
├──
|
|
30
|
-
├──
|
|
31
|
-
├──
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
├──
|
|
37
|
-
├──
|
|
38
|
-
├──
|
|
39
|
-
├──
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
├──
|
|
43
|
-
└──
|
|
37
|
+
├── branch.xs # Branch configuration
|
|
38
|
+
├── workspace/
|
|
39
|
+
│ ├── my_workspace.xs # Workspace configuration
|
|
40
|
+
│ └── trigger/
|
|
41
|
+
│ └── on_deploy.xs # Workspace triggers
|
|
42
|
+
├── api/
|
|
43
|
+
│ └── users/ # API group folder
|
|
44
|
+
│ ├── api_group.xs # API group definition
|
|
45
|
+
│ ├── get_all_get.xs # GET /users
|
|
46
|
+
│ ├── get_one_get.xs # GET /users/:id
|
|
47
|
+
│ ├── create_post.xs # POST /users
|
|
48
|
+
│ └── nested/
|
|
49
|
+
│ └── profile_get.xs # Nested endpoint: GET /users/nested/profile
|
|
50
|
+
├── function/
|
|
51
|
+
│ └── validate_token.xs # Reusable functions
|
|
52
|
+
├── task/
|
|
53
|
+
│ └── daily_cleanup.xs # Scheduled jobs
|
|
54
|
+
├── table/
|
|
55
|
+
│ ├── users.xs # Table schema
|
|
56
|
+
│ └── trigger/
|
|
57
|
+
│ └── on_user_create.xs # Table triggers
|
|
58
|
+
├── agent/
|
|
59
|
+
│ ├── support_bot.xs # AI agents
|
|
60
|
+
│ └── trigger/
|
|
61
|
+
│ └── on_message.xs # Agent triggers
|
|
62
|
+
├── tool/
|
|
63
|
+
│ └── search_docs.xs # AI tools
|
|
64
|
+
├── mcp_server/
|
|
65
|
+
│ ├── my_server.xs # MCP server definitions
|
|
66
|
+
│ └── trigger/
|
|
67
|
+
│ └── on_connect.xs # MCP server triggers
|
|
68
|
+
├── middleware/
|
|
69
|
+
│ └── auth_check.xs # Request/response interceptors
|
|
70
|
+
├── addon/
|
|
71
|
+
│ └── user_posts.xs # Query addons
|
|
72
|
+
├── static/ # Frontend files (HTML, CSS, JS)
|
|
73
|
+
└── run/ # Job and service configurations
|
|
44
74
|
```
|
|
45
75
|
|
|
76
|
+
**Key conventions:**
|
|
77
|
+
- All folders and files use `snake_case` naming
|
|
78
|
+
- API endpoints include the HTTP verb suffix (e.g., `create_post.xs`, `get_one_get.xs`)
|
|
79
|
+
- Triggers are nested under `{type}/trigger/` folders
|
|
80
|
+
- Nested API paths become nested folders (e.g., `/users/nested/profile` → `api/users/nested/profile_get.xs`)
|
|
81
|
+
|
|
46
82
|
## Environment Variables
|
|
47
83
|
|
|
48
84
|
Access with `$env.<name>`. Common built-in variables include `$env.$remote_ip`, `$env.$http_headers`, `$env.$request_method`, `$env.$datasource`, and `$env.$branch`. Custom environment variables are set in the Xano dashboard and accessed as `$env.MY_VAR`.
|
|
@@ -100,24 +136,41 @@ Documentation files use frontmatter to specify which file patterns they apply to
|
|
|
100
136
|
|
|
101
137
|
```markdown
|
|
102
138
|
---
|
|
103
|
-
applyTo: "
|
|
139
|
+
applyTo: "function/**/*.xs"
|
|
104
140
|
---
|
|
105
141
|
```
|
|
106
142
|
|
|
107
143
|
This helps AI tools apply the correct documentation based on the file being edited.
|
|
108
144
|
|
|
145
|
+
## Getting Started
|
|
146
|
+
|
|
147
|
+
For common patterns and quick examples, use:
|
|
148
|
+
```
|
|
149
|
+
xanoscript_docs({ topic: "quickstart" })
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
This includes:
|
|
153
|
+
- Variable declaration patterns
|
|
154
|
+
- Conditional logic (if/elseif/else)
|
|
155
|
+
- API requests with error handling
|
|
156
|
+
- Database CRUD operations
|
|
157
|
+
- Common mistakes to avoid
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
109
161
|
## Documentation Index
|
|
110
162
|
|
|
111
163
|
Use `xanoscript_docs({ topic: "<topic>" })` to retrieve documentation.
|
|
112
164
|
|
|
113
165
|
### Core Language
|
|
114
166
|
|
|
115
|
-
| Topic
|
|
116
|
-
|
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
167
|
+
| Topic | Description |
|
|
168
|
+
| ------------ | ---------------------------------------------------- |
|
|
169
|
+
| `quickstart` | Common patterns, quick examples, mistakes to avoid |
|
|
170
|
+
| `syntax` | Expressions, operators, filters, system variables |
|
|
171
|
+
| `types` | Data types, validation, input blocks |
|
|
172
|
+
| `functions` | Reusable function stacks, async, loops |
|
|
173
|
+
| `schema` | Runtime schema parsing and validation |
|
|
121
174
|
|
|
122
175
|
### Data
|
|
123
176
|
|
|
@@ -174,3 +227,47 @@ Use `xanoscript_docs({ topic: "<topic>" })` to retrieve documentation.
|
|
|
174
227
|
| ------------- | ------------------------------------------------------------ |
|
|
175
228
|
| `performance` | Performance optimization best practices |
|
|
176
229
|
| `security` | Security best practices for authentication and authorization |
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Example Implementations
|
|
234
|
+
|
|
235
|
+
Common integration patterns you can reference:
|
|
236
|
+
|
|
237
|
+
### External API Integrations
|
|
238
|
+
- **OpenAI/ChatGPT**: Use `api.request` with POST to `/v1/chat/completions`
|
|
239
|
+
- **Stripe**: Use `api.request` with form-encoded params for payments
|
|
240
|
+
- **SendGrid/Resend**: Use `api.request` or `util.send_email` for emails
|
|
241
|
+
- **Slack/Discord**: Use `api.request` with webhook URLs
|
|
242
|
+
- **Twilio**: Use `api.request` with Basic auth for SMS
|
|
243
|
+
|
|
244
|
+
### Common Pattern: API Integration Function
|
|
245
|
+
|
|
246
|
+
```xs
|
|
247
|
+
function "call_external_api" {
|
|
248
|
+
input {
|
|
249
|
+
text endpoint
|
|
250
|
+
object payload
|
|
251
|
+
}
|
|
252
|
+
stack {
|
|
253
|
+
api.request {
|
|
254
|
+
url = $env.API_BASE_URL ~ $input.endpoint
|
|
255
|
+
method = "POST"
|
|
256
|
+
params = $input.payload
|
|
257
|
+
headers = [
|
|
258
|
+
"Content-Type: application/json",
|
|
259
|
+
"Authorization: Bearer " ~ $env.API_KEY
|
|
260
|
+
]
|
|
261
|
+
timeout = 30
|
|
262
|
+
} as $api_result
|
|
263
|
+
|
|
264
|
+
precondition ($api_result.response.status >= 200 && $api_result.response.status < 300) {
|
|
265
|
+
error_type = "standard"
|
|
266
|
+
error = "API error: " ~ ($api_result.response.status|to_text)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
response = $api_result.response.result
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
For more patterns, see `xanoscript_docs({ topic: "quickstart" })` or `xanoscript_docs({ topic: "integrations" })`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
applyTo: "
|
|
2
|
+
applyTo: "addon/**/*.xs, function/**/*.xs, api/**/*.xs"
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Addons
|
|
@@ -231,7 +231,7 @@ db.query product {
|
|
|
231
231
|
### File Structure
|
|
232
232
|
|
|
233
233
|
```
|
|
234
|
-
|
|
234
|
+
addon/
|
|
235
235
|
├── comment_count.xs
|
|
236
236
|
├── like_count.xs
|
|
237
237
|
├── author_details.xs
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
applyTo: "
|
|
2
|
+
applyTo: "agent/**/*.xs"
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Agents
|
|
@@ -186,7 +186,7 @@ agent "Classifier" {
|
|
|
186
186
|
|
|
187
187
|
## Tools
|
|
188
188
|
|
|
189
|
-
Reference tools by name from `
|
|
189
|
+
Reference tools by name from `tool/` directory:
|
|
190
190
|
|
|
191
191
|
```xs
|
|
192
192
|
tools = [
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
applyTo: "
|
|
2
|
+
applyTo: "api/**/*.xs"
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# APIs
|
|
@@ -77,16 +77,18 @@ api_group Authentication {
|
|
|
77
77
|
### File Structure
|
|
78
78
|
|
|
79
79
|
```
|
|
80
|
-
|
|
80
|
+
api/
|
|
81
81
|
├── users/
|
|
82
|
-
│ ├── api_group.xs
|
|
83
|
-
│ ├──
|
|
84
|
-
│ └──
|
|
82
|
+
│ ├── api_group.xs # Defines group (canonical = "myapp-users")
|
|
83
|
+
│ ├── list_get.xs # GET /myapp-users/list
|
|
84
|
+
│ └── by_id_get.xs # GET /myapp-users/{id}
|
|
85
85
|
└── products/
|
|
86
|
-
├── api_group.xs
|
|
87
|
-
└──
|
|
86
|
+
├── api_group.xs # Defines group (canonical = "myapp-products")
|
|
87
|
+
└── search_get.xs # GET /myapp-products/search
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
**Naming convention:** Endpoint files use `{name}_{verb}.xs` format (e.g., `list_get.xs`, `create_post.xs`).
|
|
91
|
+
|
|
90
92
|
Full URL: `/<canonical>/<query name>` (e.g., `/myapp-users/profile`)
|
|
91
93
|
|
|
92
94
|
---
|
|
@@ -222,10 +222,11 @@ Branch configuration files are typically named `branch.xs` and placed at the wor
|
|
|
222
222
|
|
|
223
223
|
```
|
|
224
224
|
project/
|
|
225
|
-
├── branch.xs
|
|
226
|
-
├──
|
|
227
|
-
├──
|
|
228
|
-
|
|
225
|
+
├── branch.xs # Branch configuration
|
|
226
|
+
├── workspace/
|
|
227
|
+
├── table/
|
|
228
|
+
├── function/
|
|
229
|
+
└── api/
|
|
229
230
|
```
|
|
230
231
|
|
|
231
232
|
---
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
applyTo: "
|
|
2
|
+
applyTo: "function/**/*.xs"
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Functions
|
|
@@ -62,7 +62,7 @@ function "calculate_total" {
|
|
|
62
62
|
Functions can be organized in subfolders:
|
|
63
63
|
|
|
64
64
|
```
|
|
65
|
-
|
|
65
|
+
function/
|
|
66
66
|
├── math/
|
|
67
67
|
│ ├── add.xs
|
|
68
68
|
│ └── multiply.xs
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
applyTo: "
|
|
2
|
+
applyTo: "function/**/*.xs, api/**/*.xs, task/**/*.xs"
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Integrations
|
|
@@ -575,22 +575,92 @@ util.send_email {
|
|
|
575
575
|
|
|
576
576
|
---
|
|
577
577
|
|
|
578
|
-
## External APIs
|
|
578
|
+
## External APIs (api.request)
|
|
579
579
|
|
|
580
580
|
Make HTTP requests to external APIs.
|
|
581
581
|
|
|
582
|
+
### Quick Reference
|
|
583
|
+
|
|
582
584
|
```xs
|
|
583
585
|
api.request {
|
|
584
|
-
url = "https://api.example.com/
|
|
585
|
-
method = "POST"
|
|
586
|
-
params =
|
|
586
|
+
url = "https://api.example.com/endpoint"
|
|
587
|
+
method = "POST" // GET, POST, PUT, PATCH, DELETE
|
|
588
|
+
params = $payload // Request body for POST/PUT/PATCH
|
|
587
589
|
headers = ["Content-Type: application/json", "Authorization: Bearer " ~ $env.API_KEY]
|
|
588
|
-
timeout = 30
|
|
590
|
+
timeout = 30 // Timeout in seconds
|
|
589
591
|
} as $api_result
|
|
592
|
+
|
|
593
|
+
// Access response
|
|
594
|
+
$api_result.response.status // HTTP status code (200, 404, etc.)
|
|
595
|
+
$api_result.response.result // Response body (auto-parsed JSON)
|
|
596
|
+
$api_result.response.headers // Response headers array
|
|
590
597
|
```
|
|
591
598
|
|
|
599
|
+
> **Important:** The `params` parameter is used for the **request body** (POST/PUT/PATCH), not query parameters. This naming is counterintuitive but consistent across XanoScript.
|
|
600
|
+
|
|
592
601
|
> **Note:** The `headers` parameter expects an array of text strings, where each string contains the header name and value separated by a colon (e.g., `["Content-Type: application/json", "X-Custom-Header: value"]`).
|
|
593
602
|
|
|
603
|
+
### GET Request
|
|
604
|
+
|
|
605
|
+
```xs
|
|
606
|
+
api.request {
|
|
607
|
+
url = "https://api.example.com/users?page=1&limit=10"
|
|
608
|
+
method = "GET"
|
|
609
|
+
headers = ["Authorization: Bearer " ~ $env.API_KEY]
|
|
610
|
+
} as $api_result
|
|
611
|
+
|
|
612
|
+
var $users { value = $api_result.response.result }
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
### POST Request with JSON Body
|
|
616
|
+
|
|
617
|
+
```xs
|
|
618
|
+
var $payload {
|
|
619
|
+
value = {
|
|
620
|
+
name: $input.name,
|
|
621
|
+
email: $input.email
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
api.request {
|
|
626
|
+
url = "https://api.example.com/users"
|
|
627
|
+
method = "POST"
|
|
628
|
+
params = $payload
|
|
629
|
+
headers = ["Content-Type: application/json", "Authorization: Bearer " ~ $env.API_KEY]
|
|
630
|
+
} as $api_result
|
|
631
|
+
|
|
632
|
+
// Check for success
|
|
633
|
+
precondition ($api_result.response.status == 201) {
|
|
634
|
+
error_type = "standard"
|
|
635
|
+
error = "Failed to create user: " ~ ($api_result.response.result|json_encode)
|
|
636
|
+
}
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
### Error Handling Pattern
|
|
640
|
+
|
|
641
|
+
```xs
|
|
642
|
+
api.request {
|
|
643
|
+
url = "https://api.example.com/data"
|
|
644
|
+
method = "GET"
|
|
645
|
+
timeout = 30
|
|
646
|
+
} as $api_result
|
|
647
|
+
|
|
648
|
+
conditional {
|
|
649
|
+
if ($api_result.response.status >= 200 && $api_result.response.status < 300) {
|
|
650
|
+
var $data { value = $api_result.response.result }
|
|
651
|
+
}
|
|
652
|
+
elseif ($api_result.response.status == 404) {
|
|
653
|
+
throw { name = "NotFound", value = "Resource not found" }
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
throw {
|
|
657
|
+
name = "APIError",
|
|
658
|
+
value = "API returned status " ~ ($api_result.response.status|to_text)
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
```
|
|
663
|
+
|
|
594
664
|
### Response Structure
|
|
595
665
|
|
|
596
666
|
The `api.request` statement returns an object with both request and response details:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
applyTo: "
|
|
2
|
+
applyTo: "mcp_server/**/*.xs"
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# MCP Servers
|
|
@@ -52,7 +52,7 @@ mcp_server "Customer Support" {
|
|
|
52
52
|
|
|
53
53
|
## Tools Block
|
|
54
54
|
|
|
55
|
-
Reference tools from `
|
|
55
|
+
Reference tools from `tool/` directory by name:
|
|
56
56
|
|
|
57
57
|
```xs
|
|
58
58
|
tools = [
|
|
@@ -62,7 +62,7 @@ tools = [
|
|
|
62
62
|
]
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
Tool names must exactly match `.xs` file names in `
|
|
65
|
+
Tool names must exactly match `.xs` file names in `tool/`.
|
|
66
66
|
|
|
67
67
|
---
|
|
68
68
|
|
|
@@ -146,19 +146,19 @@ mcp_server "CRM" {
|
|
|
146
146
|
|
|
147
147
|
### By Domain
|
|
148
148
|
```
|
|
149
|
-
|
|
150
|
-
├── support.xs
|
|
151
|
-
├── ecommerce.xs
|
|
152
|
-
├── analytics.xs
|
|
153
|
-
└── admin.xs
|
|
149
|
+
mcp_server/
|
|
150
|
+
├── support.xs # Customer support tools
|
|
151
|
+
├── ecommerce.xs # Store management
|
|
152
|
+
├── analytics.xs # Reporting and metrics
|
|
153
|
+
└── admin.xs # Administrative functions
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
### By Access Level
|
|
157
157
|
```
|
|
158
|
-
|
|
159
|
-
├── public.xs
|
|
160
|
-
├── authenticated.xs
|
|
161
|
-
└── admin.xs
|
|
158
|
+
mcp_server/
|
|
159
|
+
├── public.xs # Public-facing tools
|
|
160
|
+
├── authenticated.xs # Requires auth
|
|
161
|
+
└── admin.xs # Admin-only tools
|
|
162
162
|
```
|
|
163
163
|
|
|
164
164
|
---
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*.xs"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Quickstart & Common Patterns
|
|
6
|
+
|
|
7
|
+
Essential patterns for XanoScript development. Use this as a quick reference for frequently-used code patterns.
|
|
8
|
+
|
|
9
|
+
## Quick Reference
|
|
10
|
+
|
|
11
|
+
### Variable Declaration
|
|
12
|
+
```xs
|
|
13
|
+
var $name { value = "initial value" }
|
|
14
|
+
var $count { value = 0 }
|
|
15
|
+
var $data { value = { key: "value" } }
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Conditional Logic
|
|
19
|
+
```xs
|
|
20
|
+
conditional {
|
|
21
|
+
if (`$status == "active"`) {
|
|
22
|
+
var $result { value = "Active user" }
|
|
23
|
+
}
|
|
24
|
+
elseif (`$status == "pending"`) {
|
|
25
|
+
var $result { value = "Pending approval" }
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
var $result { value = "Unknown status" }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### API Request with Error Handling
|
|
34
|
+
```xs
|
|
35
|
+
api.request {
|
|
36
|
+
url = "https://api.example.com/data"
|
|
37
|
+
method = "POST"
|
|
38
|
+
params = $payload
|
|
39
|
+
headers = ["Content-Type: application/json", "Authorization: Bearer " ~ $env.API_KEY]
|
|
40
|
+
} as $api_result
|
|
41
|
+
|
|
42
|
+
precondition ($api_result.response.status == 200) {
|
|
43
|
+
error_type = "standard"
|
|
44
|
+
error = "API request failed"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
var $data { value = $api_result.response.result }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### String Concatenation
|
|
51
|
+
```xs
|
|
52
|
+
var $greeting { value = "Hello, " ~ $input.name ~ "!" }
|
|
53
|
+
|
|
54
|
+
// With filters (use parentheses)
|
|
55
|
+
var $message { value = "Status: " ~ ($status|to_text) ~ " - " ~ ($data|json_encode) }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Common Patterns
|
|
61
|
+
|
|
62
|
+
### 1. Input Validation
|
|
63
|
+
|
|
64
|
+
```xs
|
|
65
|
+
// Required field check
|
|
66
|
+
precondition ($input.email != null && $input.email != "") {
|
|
67
|
+
error_type = "inputerror"
|
|
68
|
+
error = "Email is required"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Format validation
|
|
72
|
+
precondition ($input.email|contains:"@") {
|
|
73
|
+
error_type = "inputerror"
|
|
74
|
+
error = "Invalid email format"
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 2. Database CRUD
|
|
79
|
+
|
|
80
|
+
```xs
|
|
81
|
+
// Create
|
|
82
|
+
db.add "user" {
|
|
83
|
+
data = { name: $input.name, email: $input.email }
|
|
84
|
+
} as $new_user
|
|
85
|
+
|
|
86
|
+
// Read one
|
|
87
|
+
db.get "user" {
|
|
88
|
+
where = $db.user.id == $input.id
|
|
89
|
+
} as $user
|
|
90
|
+
|
|
91
|
+
// Read many
|
|
92
|
+
db.query "user" {
|
|
93
|
+
where = $db.user.is_active == true
|
|
94
|
+
order = [{ field: created_at, direction: desc }]
|
|
95
|
+
paging = { limit: 10, offset: 0 }
|
|
96
|
+
} as $users
|
|
97
|
+
|
|
98
|
+
// Update
|
|
99
|
+
db.edit "user" {
|
|
100
|
+
where = $db.user.id == $input.id
|
|
101
|
+
data = { name: $input.name }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Delete
|
|
105
|
+
db.delete "user" {
|
|
106
|
+
where = $db.user.id == $input.id
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 3. Optional Field Handling
|
|
111
|
+
|
|
112
|
+
```xs
|
|
113
|
+
// Build object with optional fields
|
|
114
|
+
var $data { value = { required_field: $input.required } }
|
|
115
|
+
|
|
116
|
+
conditional {
|
|
117
|
+
if ($input.optional_field != null) {
|
|
118
|
+
var.update $data { value = $data|set:"optional_field":$input.optional_field }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Or use set_ifnotnull
|
|
123
|
+
var $data {
|
|
124
|
+
value = { required: $input.required }|set_ifnotnull:"optional":$input.optional
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 4. Loop Through Array
|
|
129
|
+
|
|
130
|
+
```xs
|
|
131
|
+
// Using each
|
|
132
|
+
each ($items as $item) {
|
|
133
|
+
debug.log { value = $item.name }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Using map filter
|
|
137
|
+
var $names { value = $items|map:$$.name }
|
|
138
|
+
|
|
139
|
+
// Using filter
|
|
140
|
+
var $active_items { value = $items|filter:$$.is_active == true }
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 5. Error Handling with Try-Catch
|
|
144
|
+
|
|
145
|
+
```xs
|
|
146
|
+
try_catch {
|
|
147
|
+
try {
|
|
148
|
+
api.request {
|
|
149
|
+
url = "https://api.example.com/risky"
|
|
150
|
+
method = "GET"
|
|
151
|
+
} as $result
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
debug.log { value = "Request failed, using fallback" }
|
|
155
|
+
var $result { value = { response: { result: null } } }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 6. Authentication Check
|
|
161
|
+
|
|
162
|
+
```xs
|
|
163
|
+
// Require authenticated user
|
|
164
|
+
precondition ($auth.id != null) {
|
|
165
|
+
error_type = "accessdenied"
|
|
166
|
+
error = "Authentication required"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Check user owns resource
|
|
170
|
+
db.get "post" {
|
|
171
|
+
where = $db.post.id == $input.post_id
|
|
172
|
+
} as $post
|
|
173
|
+
|
|
174
|
+
precondition ($post.user_id == $auth.id) {
|
|
175
|
+
error_type = "accessdenied"
|
|
176
|
+
error = "You can only edit your own posts"
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 7. Pagination
|
|
181
|
+
|
|
182
|
+
```xs
|
|
183
|
+
var $page { value = $input.page ?? 1 }
|
|
184
|
+
var $limit { value = $input.limit ?? 20 }
|
|
185
|
+
var $offset { value = ($page - 1) * $limit }
|
|
186
|
+
|
|
187
|
+
db.query "item" {
|
|
188
|
+
where = $db.item.is_active == true
|
|
189
|
+
order = [{ field: created_at, direction: desc }]
|
|
190
|
+
paging = { limit: $limit, offset: $offset }
|
|
191
|
+
count = true
|
|
192
|
+
} as $result
|
|
193
|
+
|
|
194
|
+
response = {
|
|
195
|
+
items: $result.items,
|
|
196
|
+
total: $result.count,
|
|
197
|
+
page: $page,
|
|
198
|
+
pages: ($result.count / $limit)|ceil
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 8. Building Complex Objects
|
|
203
|
+
|
|
204
|
+
```xs
|
|
205
|
+
// Step by step
|
|
206
|
+
var $response { value = {} }
|
|
207
|
+
var.update $response { value = $response|set:"user":$user }
|
|
208
|
+
var.update $response { value = $response|set:"posts":$posts }
|
|
209
|
+
var.update $response { value = $response|set:"stats":{ count: $posts|count } }
|
|
210
|
+
|
|
211
|
+
// Or all at once
|
|
212
|
+
var $response {
|
|
213
|
+
value = {
|
|
214
|
+
user: $user,
|
|
215
|
+
posts: $posts,
|
|
216
|
+
stats: { count: $posts|count }
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 9. Date/Time Operations
|
|
222
|
+
|
|
223
|
+
```xs
|
|
224
|
+
// Current timestamp
|
|
225
|
+
var $now { value = now }
|
|
226
|
+
|
|
227
|
+
// Format for display
|
|
228
|
+
var $formatted { value = now|format_timestamp:"Y-m-d H:i:s":"UTC" }
|
|
229
|
+
|
|
230
|
+
// Relative time
|
|
231
|
+
var $yesterday { value = now|transform_timestamp:"-1 day" }
|
|
232
|
+
var $next_week { value = now|transform_timestamp:"+7 days" }
|
|
233
|
+
|
|
234
|
+
// Compare dates
|
|
235
|
+
db.query "event" {
|
|
236
|
+
where = $db.event.start_date >= now
|
|
237
|
+
} as $upcoming_events
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 10. JSON API Response
|
|
241
|
+
|
|
242
|
+
```xs
|
|
243
|
+
api.request {
|
|
244
|
+
url = "https://api.openai.com/v1/chat/completions"
|
|
245
|
+
method = "POST"
|
|
246
|
+
params = {
|
|
247
|
+
model: "gpt-4",
|
|
248
|
+
messages: [{ role: "user", content: $input.prompt }]
|
|
249
|
+
}
|
|
250
|
+
headers = [
|
|
251
|
+
"Content-Type: application/json",
|
|
252
|
+
"Authorization: Bearer " ~ $env.OPENAI_API_KEY
|
|
253
|
+
]
|
|
254
|
+
} as $api_result
|
|
255
|
+
|
|
256
|
+
conditional {
|
|
257
|
+
if ($api_result.response.status == 200) {
|
|
258
|
+
var $answer { value = $api_result.response.result.choices|first|get:"message"|get:"content" }
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
throw {
|
|
262
|
+
name = "APIError",
|
|
263
|
+
value = "OpenAI API error: " ~ ($api_result.response.status|to_text)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Common Mistakes
|
|
272
|
+
|
|
273
|
+
### 1. Using `else if` instead of `elseif`
|
|
274
|
+
```xs
|
|
275
|
+
// ❌ Wrong
|
|
276
|
+
conditional {
|
|
277
|
+
if (...) { }
|
|
278
|
+
else if (...) { } // Parse error!
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ✅ Correct
|
|
282
|
+
conditional {
|
|
283
|
+
if (...) { }
|
|
284
|
+
elseif (...) { }
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### 2. Missing parentheses in filter concatenation
|
|
289
|
+
```xs
|
|
290
|
+
// ❌ Wrong
|
|
291
|
+
var $msg { value = $status|to_text ~ " - " ~ $data|json_encode }
|
|
292
|
+
|
|
293
|
+
// ✅ Correct
|
|
294
|
+
var $msg { value = ($status|to_text) ~ " - " ~ ($data|json_encode) }
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### 3. Using `body` instead of `params` for api.request
|
|
298
|
+
```xs
|
|
299
|
+
// ❌ Wrong
|
|
300
|
+
api.request {
|
|
301
|
+
url = "..."
|
|
302
|
+
method = "POST"
|
|
303
|
+
body = $payload // "body" is not valid!
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ✅ Correct
|
|
307
|
+
api.request {
|
|
308
|
+
url = "..."
|
|
309
|
+
method = "POST"
|
|
310
|
+
params = $payload // Use "params" for request body
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### 4. Using `default` filter (doesn't exist)
|
|
315
|
+
```xs
|
|
316
|
+
// ❌ Wrong
|
|
317
|
+
var $value { value = $input.optional|default:"fallback" }
|
|
318
|
+
|
|
319
|
+
// ✅ Correct
|
|
320
|
+
var $value { value = $input.optional|first_notnull:"fallback" }
|
|
321
|
+
// or
|
|
322
|
+
var $value { value = $input.optional ?? "fallback" }
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### 5. Using $env in run.job input blocks
|
|
326
|
+
```xs
|
|
327
|
+
// ❌ Wrong - $env not allowed in input blocks
|
|
328
|
+
run.job "my_job" {
|
|
329
|
+
input {
|
|
330
|
+
text api_key = $env.API_KEY
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// ✅ Correct - use $env in the stack
|
|
335
|
+
run.job "my_job" {
|
|
336
|
+
stack {
|
|
337
|
+
var $api_key { value = $env.API_KEY }
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
```
|
|
@@ -16,9 +16,9 @@ Define job and service configurations for the Xano Job Runner.
|
|
|
16
16
|
### Directory Structure
|
|
17
17
|
```
|
|
18
18
|
run.xs
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
table/
|
|
20
|
+
function/
|
|
21
|
+
api/
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
---
|
|
@@ -216,9 +216,9 @@ function "process_data" {
|
|
|
216
216
|
|
|
217
217
|
```
|
|
218
218
|
run.xs
|
|
219
|
-
|
|
219
|
+
table/
|
|
220
220
|
└── users.xs
|
|
221
|
-
|
|
221
|
+
function/
|
|
222
222
|
└── migrate_users.xs
|
|
223
223
|
```
|
|
224
224
|
|
|
@@ -235,7 +235,7 @@ run.job "Data Migration" {
|
|
|
235
235
|
}
|
|
236
236
|
```
|
|
237
237
|
|
|
238
|
-
###
|
|
238
|
+
### table/users.xs
|
|
239
239
|
```xs
|
|
240
240
|
table users {
|
|
241
241
|
auth = false
|
|
@@ -255,7 +255,7 @@ table users {
|
|
|
255
255
|
}
|
|
256
256
|
```
|
|
257
257
|
|
|
258
|
-
###
|
|
258
|
+
### function/migrate_users.xs
|
|
259
259
|
```xs
|
|
260
260
|
function "migrate_users" {
|
|
261
261
|
input {
|
|
@@ -287,12 +287,13 @@ function "migrate_users" {
|
|
|
287
287
|
|
|
288
288
|
```
|
|
289
289
|
run.xs
|
|
290
|
-
|
|
290
|
+
table/
|
|
291
291
|
└── event.xs
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
├──
|
|
295
|
-
|
|
292
|
+
api/
|
|
293
|
+
└── events/
|
|
294
|
+
├── api_group.xs
|
|
295
|
+
├── list_get.xs
|
|
296
|
+
└── add_post.xs
|
|
296
297
|
```
|
|
297
298
|
|
|
298
299
|
### run.xs
|
|
@@ -306,7 +307,7 @@ run.service "Event Tracker" {
|
|
|
306
307
|
}
|
|
307
308
|
```
|
|
308
309
|
|
|
309
|
-
###
|
|
310
|
+
### table/event.xs
|
|
310
311
|
```xs
|
|
311
312
|
table event {
|
|
312
313
|
auth = false
|
|
@@ -322,7 +323,7 @@ table event {
|
|
|
322
323
|
}
|
|
323
324
|
```
|
|
324
325
|
|
|
325
|
-
###
|
|
326
|
+
### api/events/list_get.xs
|
|
326
327
|
```xs
|
|
327
328
|
query list verb=GET {
|
|
328
329
|
api_group = "events"
|
|
@@ -21,12 +21,67 @@ Complete reference for XanoScript expressions, operators, and filters.
|
|
|
21
21
|
| Filter | Purpose | Example |
|
|
22
22
|
|--------|---------|---------|
|
|
23
23
|
| `trim` | Remove whitespace | `$s\|trim` |
|
|
24
|
-
| `
|
|
24
|
+
| `to_lower` / `to_upper` | Case conversion | `$s\|to_lower` |
|
|
25
25
|
| `first` / `last` | Array endpoints | `$arr\|first` |
|
|
26
26
|
| `count` | Array/object length | `$arr\|count` |
|
|
27
27
|
| `get` | Object property | `$obj\|get:"key"` |
|
|
28
28
|
| `set` | Set property | `$obj\|set:"key":"val"` |
|
|
29
29
|
| `json_encode` / `json_decode` | JSON conversion | `$obj\|json_encode` |
|
|
30
|
+
| `to_text` / `to_int` | Type conversion | `$num\|to_text` |
|
|
31
|
+
|
|
32
|
+
> **Note:** There is no `default` filter. Use conditional blocks or `first_notnull`/`first_notempty` instead.
|
|
33
|
+
|
|
34
|
+
### String Concatenation with Filters
|
|
35
|
+
|
|
36
|
+
When concatenating strings that use filters, wrap each filtered expression in parentheses:
|
|
37
|
+
|
|
38
|
+
```xs
|
|
39
|
+
// ✅ Correct - parentheses around filtered expressions
|
|
40
|
+
var $message {
|
|
41
|
+
value = ($status|to_text) ~ ": " ~ ($data|json_encode)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ❌ Incorrect - missing parentheses causes parse error
|
|
45
|
+
var $message {
|
|
46
|
+
value = $status|to_text ~ ": " ~ $data|json_encode
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Conditional Blocks
|
|
53
|
+
|
|
54
|
+
Use `conditional` blocks for if/elseif/else logic:
|
|
55
|
+
|
|
56
|
+
```xs
|
|
57
|
+
conditional {
|
|
58
|
+
if (`$status == "success"`) {
|
|
59
|
+
var $message { value = "All good!" }
|
|
60
|
+
}
|
|
61
|
+
elseif (`$status == "pending"`) {
|
|
62
|
+
var $message { value = "Please wait..." }
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
var $message { value = "Unknown status" }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
> **Important:** Use `elseif` (one word), not `else if` or `else { if (...) }`. Nested `if` inside `else` blocks is not supported.
|
|
71
|
+
|
|
72
|
+
### Conditional as Expression
|
|
73
|
+
|
|
74
|
+
Use conditional blocks inline to return values:
|
|
75
|
+
|
|
76
|
+
```xs
|
|
77
|
+
var $tier_limit {
|
|
78
|
+
value = conditional {
|
|
79
|
+
if ($auth.tier == "premium") { 1000 }
|
|
80
|
+
elseif ($auth.tier == "pro") { 500 }
|
|
81
|
+
else { 100 }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
30
85
|
|
|
31
86
|
---
|
|
32
87
|
|
|
@@ -431,6 +486,32 @@ var $client_ip { value = $env.$remote_ip }
|
|
|
431
486
|
var $method { value = $env.$request_method }
|
|
432
487
|
var $headers { value = $env.$http_headers }
|
|
433
488
|
var $current_branch { value = $env.$branch }
|
|
489
|
+
|
|
490
|
+
// Custom environment variables (set in Xano dashboard)
|
|
491
|
+
var $api_key { value = $env.MY_API_KEY }
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### $env Limitations
|
|
495
|
+
|
|
496
|
+
> **Important:** `$env` variables cannot be used in `run.job` or `run.service` input blocks. Input values must be constants.
|
|
497
|
+
|
|
498
|
+
```xs
|
|
499
|
+
// ❌ Invalid - $env not allowed in run.job input
|
|
500
|
+
run.job "my_job" {
|
|
501
|
+
input {
|
|
502
|
+
text api_key = $env.API_KEY // Error!
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// ✅ Valid - access $env inside the stack instead
|
|
507
|
+
run.job "my_job" {
|
|
508
|
+
input {
|
|
509
|
+
text api_key // No default value
|
|
510
|
+
}
|
|
511
|
+
stack {
|
|
512
|
+
var $key { value = $env.API_KEY }
|
|
513
|
+
}
|
|
514
|
+
}
|
|
434
515
|
```
|
|
435
516
|
|
|
436
517
|
---
|
|
@@ -175,11 +175,12 @@ Workspace configuration is stored in `workspace.xs` at the root of your project.
|
|
|
175
175
|
|
|
176
176
|
```
|
|
177
177
|
project/
|
|
178
|
-
├──
|
|
179
|
-
├──
|
|
180
|
-
|
|
181
|
-
├──
|
|
182
|
-
|
|
178
|
+
├── branch.xs # Branch configuration
|
|
179
|
+
├── workspace/
|
|
180
|
+
│ └── my_workspace.xs # Workspace configuration
|
|
181
|
+
├── table/
|
|
182
|
+
├── function/
|
|
183
|
+
└── api/
|
|
183
184
|
```
|
|
184
185
|
|
|
185
186
|
---
|
package/package.json
CHANGED