@xano/developer-mcp 1.0.46 → 1.0.48
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/xanoscript_docs/cheatsheet.md +34 -9
- package/dist/xanoscript_docs/integrations/external-apis.md +20 -16
- package/dist/xanoscript_docs/performance.md +8 -5
- package/dist/xanoscript_docs/quickstart.md +22 -0
- package/dist/xanoscript_docs/syntax.md +61 -14
- package/dist/xanoscript_docs/unit-testing.md +40 -0
- package/package.json +1 -1
|
@@ -33,21 +33,46 @@ conditional {
|
|
|
33
33
|
|
|
34
34
|
> **Note:** Use `elseif` (one word), not `else if`.
|
|
35
35
|
|
|
36
|
+
## Switch
|
|
37
|
+
|
|
38
|
+
```xs
|
|
39
|
+
switch ($input.status) {
|
|
40
|
+
case ("active") {
|
|
41
|
+
var $message { value = "User is active" }
|
|
42
|
+
} break
|
|
43
|
+
case ("pending") {
|
|
44
|
+
var $message { value = "User is pending" }
|
|
45
|
+
} break
|
|
46
|
+
default {
|
|
47
|
+
var $message { value = "Unknown status" }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
> **Note:** `break` goes **after** the closing `}` of each `case` block. The `default` case does not need `break`.
|
|
53
|
+
|
|
36
54
|
## Loops
|
|
37
55
|
|
|
38
56
|
```xs
|
|
39
57
|
// For each loop
|
|
40
|
-
|
|
41
|
-
|
|
58
|
+
foreach ($input.items) {
|
|
59
|
+
each as $item {
|
|
60
|
+
debug.log { value = $item.name }
|
|
61
|
+
}
|
|
42
62
|
}
|
|
43
63
|
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
64
|
+
// For loop (iterate N times)
|
|
65
|
+
for (10) {
|
|
66
|
+
each as $index {
|
|
67
|
+
debug.log { value = $index }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// While loop
|
|
72
|
+
var $counter { value = 0 }
|
|
73
|
+
while ($counter < 10) {
|
|
74
|
+
each {
|
|
75
|
+
var.update $counter { value = $counter + 1 }
|
|
51
76
|
}
|
|
52
77
|
}
|
|
53
78
|
|
|
@@ -149,25 +149,29 @@ var $content_type { value = $api_result.response.headers|first }
|
|
|
149
149
|
var $retries { value = 0 }
|
|
150
150
|
var $success { value = false }
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
152
|
+
stack {
|
|
153
|
+
while ($retries < 3 && $success == false) {
|
|
154
|
+
each {
|
|
155
|
+
try_catch {
|
|
156
|
+
try {
|
|
157
|
+
api.request {
|
|
158
|
+
url = "https://api.example.com/data"
|
|
159
|
+
method = "GET"
|
|
160
|
+
timeout = 10
|
|
161
|
+
} as $api_result
|
|
162
|
+
|
|
163
|
+
conditional {
|
|
164
|
+
if ($api_result.response.status == 200) {
|
|
165
|
+
var.update $success { value = true }
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
var.update $retries { value = $retries + 1 }
|
|
171
|
+
util.sleep { value = 2 }
|
|
164
172
|
}
|
|
165
173
|
}
|
|
166
174
|
}
|
|
167
|
-
catch {
|
|
168
|
-
var.update $retries { value = $retries + 1 }
|
|
169
|
-
util.sleep { value = 2 }
|
|
170
|
-
}
|
|
171
175
|
}
|
|
172
176
|
}
|
|
173
177
|
```
|
|
@@ -286,11 +286,14 @@ For basic rate limiting setup, see `xanoscript_docs({ topic: "security" })`. Bel
|
|
|
286
286
|
|
|
287
287
|
```xs
|
|
288
288
|
// Different limits by user tier
|
|
289
|
-
var $limit {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
289
|
+
var $limit { value = 100 }
|
|
290
|
+
|
|
291
|
+
conditional {
|
|
292
|
+
if ($auth.tier == "premium") {
|
|
293
|
+
var.update $limit { value = 1000 }
|
|
294
|
+
}
|
|
295
|
+
elseif ($auth.tier == "pro") {
|
|
296
|
+
var.update $limit { value = 500 }
|
|
294
297
|
}
|
|
295
298
|
}
|
|
296
299
|
|
|
@@ -611,6 +611,28 @@ stack {
|
|
|
611
611
|
}
|
|
612
612
|
```
|
|
613
613
|
|
|
614
|
+
### 13. Multiple constructs in one file
|
|
615
|
+
Each `.xs` file must contain exactly **one** construct. Placing two constructs in the same file causes a parse error.
|
|
616
|
+
|
|
617
|
+
```xs
|
|
618
|
+
// ❌ Wrong - two constructs in one file
|
|
619
|
+
function "helper_a" {
|
|
620
|
+
stack { ... }
|
|
621
|
+
response = $result
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function "helper_b" {
|
|
625
|
+
stack { ... }
|
|
626
|
+
response = $result
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
```
|
|
631
|
+
// ✅ Correct - one construct per file
|
|
632
|
+
// function/helper_a.xs → contains only "helper_a"
|
|
633
|
+
// function/helper_b.xs → contains only "helper_b"
|
|
634
|
+
```
|
|
635
|
+
|
|
614
636
|
---
|
|
615
637
|
|
|
616
638
|
## Related Topics
|
|
@@ -112,22 +112,42 @@ Working with...
|
|
|
112
112
|
|
|
113
113
|
> **Note:** There is no `default` filter. Use conditional blocks or `first_notnull`/`first_notempty` instead.
|
|
114
114
|
|
|
115
|
-
###
|
|
115
|
+
### Parentheses and Filter Precedence
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
> **Rule:** Filters bind greedily to the left. Without parentheses, the parser extends the filter expression into the operator that follows — producing invalid or unexpected results. **When in doubt, wrap `$var|filter` in parentheses.**
|
|
118
|
+
|
|
119
|
+
The filter `|` grabs everything to its right until it hits a boundary. This means `$arr|count > 3` is parsed as `$arr | (length > 3)` — treating `count > 3` as the filter argument, which is invalid.
|
|
118
120
|
|
|
119
121
|
```xs
|
|
120
|
-
//
|
|
122
|
+
// ❌ Wrong — parser reads filter argument as "count == 0" (invalid)
|
|
123
|
+
if ($arr|count == 0) { ... }
|
|
124
|
+
|
|
125
|
+
// ❌ Wrong — parse error in concatenation
|
|
121
126
|
var $message {
|
|
122
|
-
value =
|
|
127
|
+
value = $status|to_text ~ ": " ~ $data|json_encode
|
|
123
128
|
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```xs
|
|
132
|
+
// ✅ Correct — evaluate filter first, then apply operator
|
|
133
|
+
|
|
134
|
+
// ✅ Correct
|
|
135
|
+
if (($arr|count) == 0) { ... }
|
|
124
136
|
|
|
125
|
-
//
|
|
137
|
+
// ✅ Correct — wrap each filtered expression for concatenation
|
|
126
138
|
var $message {
|
|
127
|
-
value = $status|to_text ~ ": " ~ $data|json_encode
|
|
139
|
+
value = ($status|to_text) ~ ": " ~ ($data|json_encode)
|
|
128
140
|
}
|
|
141
|
+
|
|
142
|
+
// ✅ Correct — filter result used in arithmetic
|
|
143
|
+
var $total { value = ($prices|sum) + $tax }
|
|
144
|
+
|
|
145
|
+
// ✅ Correct — filter result compared
|
|
146
|
+
var $is_long { value = ($text|strlen) > 100 }
|
|
129
147
|
```
|
|
130
148
|
|
|
149
|
+
**Summary:** Any time you apply an operator (`>`, `<`, `==`, `!=`, `~`, `+`, `-`, etc.) to a filtered value, wrap the `$var|filter` portion in its own parentheses.
|
|
150
|
+
|
|
131
151
|
---
|
|
132
152
|
|
|
133
153
|
## Conditional Blocks
|
|
@@ -150,16 +170,19 @@ conditional {
|
|
|
150
170
|
|
|
151
171
|
> **Important:** Use `elseif` (one word), not `else if` or `else { if (...) }`. Nested `if` inside `else` blocks is not supported.
|
|
152
172
|
|
|
153
|
-
### Conditional
|
|
173
|
+
### Conditional with Variable Updates
|
|
154
174
|
|
|
155
|
-
Use conditional blocks
|
|
175
|
+
Use conditional blocks to update a variable based on conditions:
|
|
156
176
|
|
|
157
177
|
```xs
|
|
158
|
-
var $tier_limit {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
178
|
+
var $tier_limit { value = 100 }
|
|
179
|
+
|
|
180
|
+
conditional {
|
|
181
|
+
if ($auth.tier == "premium") {
|
|
182
|
+
var.update $tier_limit { value = 1000 }
|
|
183
|
+
}
|
|
184
|
+
elseif ($auth.tier == "pro") {
|
|
185
|
+
var.update $tier_limit { value = 500 }
|
|
163
186
|
}
|
|
164
187
|
}
|
|
165
188
|
```
|
|
@@ -282,7 +305,7 @@ $db.post.date >=? $input.start_date
|
|
|
282
305
|
| `unique` | `[1,1,2]\|unique` | `[1,2]` |
|
|
283
306
|
| `flatten` | `[[1,2],[3]]\|flatten` | `[1,2,3]` |
|
|
284
307
|
| `shuffle` | `[1,2,3]\|shuffle` | Random order |
|
|
285
|
-
| `slice` | `[1,2,3,4]\|slice:1:2` | `[2,3]` |
|
|
308
|
+
| `slice` | `[1,2,3,4]\|slice:1:2` | `[2,3]` — offset 1, length 2 |
|
|
286
309
|
| `push` | `[1,2]\|push:3` | `[1,2,3]` |
|
|
287
310
|
| `pop` | `[1,2,3]\|pop` | `3` |
|
|
288
311
|
| `shift` | `[1,2,3]\|shift` | `1` |
|
|
@@ -328,6 +351,30 @@ Generate numeric ranges with the `..` operator:
|
|
|
328
351
|
[1,2,3,4]|reduce:$$+$result:0 // 10
|
|
329
352
|
```
|
|
330
353
|
|
|
354
|
+
### Array Element Access: `|get` vs `|slice`
|
|
355
|
+
|
|
356
|
+
> **`|get:N`** returns a **single element** by zero-based index.
|
|
357
|
+
> **`|slice:offset:length`** returns a **sub-array**, skipping `offset` elements and returning the next `length` elements.
|
|
358
|
+
|
|
359
|
+
| Method | Use For | Example | Result |
|
|
360
|
+
|--------|---------|---------|--------|
|
|
361
|
+
| `\|get:N` | Single element by index (0-based) | `[10,20,30]\|get:0` | `10` |
|
|
362
|
+
| `\|slice:offset:length` | Sub-array — skip N, take M | `[10,20,30,40]\|slice:1:2` | `[20,30]` |
|
|
363
|
+
| `\|first` | First element | `[10,20,30]\|first` | `10` |
|
|
364
|
+
| `\|last` | Last element | `[10,20,30]\|last` | `30` |
|
|
365
|
+
|
|
366
|
+
```xs
|
|
367
|
+
// Single element by index
|
|
368
|
+
var $third { value = $items|get:2 } // 0-based: 3rd element
|
|
369
|
+
|
|
370
|
+
// Sub-array: skip first 10, return the next 5
|
|
371
|
+
var $page { value = $items|slice:10:5 }
|
|
372
|
+
|
|
373
|
+
// ⚠️ slice:offset:length — NOT slice:start:end
|
|
374
|
+
// $arr|slice:2:3 → skip 2, return 3 elements (indices 2, 3, 4)
|
|
375
|
+
// NOT "from index 2 to index 3"
|
|
376
|
+
```
|
|
377
|
+
|
|
331
378
|
### Grouping & Indexing
|
|
332
379
|
```xs
|
|
333
380
|
// Group by property
|
|
@@ -35,6 +35,46 @@ function "<name>" {
|
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
+
## Scope Limitations
|
|
39
|
+
|
|
40
|
+
> **Unit tests can only access `$response`** — the value declared in the construct's `response = ...` field. Tests do **not** have access to intermediate stack variables.
|
|
41
|
+
|
|
42
|
+
```xs
|
|
43
|
+
function "calculate" {
|
|
44
|
+
input { int x }
|
|
45
|
+
stack {
|
|
46
|
+
var $doubled { value = $input.x * 2 } // NOT accessible in tests
|
|
47
|
+
var $result { value = $doubled + 1 } // NOT accessible in tests
|
|
48
|
+
}
|
|
49
|
+
response = $result // This becomes $response in tests
|
|
50
|
+
|
|
51
|
+
test "doubles and adds one" {
|
|
52
|
+
input = { x: 5 }
|
|
53
|
+
// ✅ Can access $response (the declared response)
|
|
54
|
+
expect.to_equal ($response) { value = 11 }
|
|
55
|
+
|
|
56
|
+
// ❌ Stack variables are not in scope — this will fail
|
|
57
|
+
// expect.to_equal ($doubled) { value = 10 }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
To assert on intermediate values, expose them through the response:
|
|
63
|
+
|
|
64
|
+
```xs
|
|
65
|
+
response = { result: $result, doubled: $doubled }
|
|
66
|
+
|
|
67
|
+
test "intermediate values" {
|
|
68
|
+
input = { x: 5 }
|
|
69
|
+
expect.to_equal ($response.doubled) { value = 10 }
|
|
70
|
+
expect.to_equal ($response.result) { value = 11 }
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
> **Note:** Function stack variables (variables defined in other functions called via `function.run`) are also not accessible — tests are fully isolated to the construct's own declared response.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
38
78
|
## Basic Test
|
|
39
79
|
|
|
40
80
|
```xs
|
package/package.json
CHANGED