@xano/developer-mcp 1.0.7 → 1.0.9
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 +28 -0
- package/dist/index.js +42 -1
- package/dist/templates/xanoscript-index.js +7 -0
- package/package.json +1 -1
- package/xanoscript_docs/README.md +38 -5
- package/xanoscript_docs/addons.md +285 -0
- package/xanoscript_docs/agents.md +84 -0
- package/xanoscript_docs/database.md +160 -0
- package/xanoscript_docs/debugging.md +342 -0
- package/xanoscript_docs/functions.md +172 -0
- package/xanoscript_docs/integrations.md +376 -7
- package/xanoscript_docs/performance.md +407 -0
- package/xanoscript_docs/realtime.md +382 -0
- package/xanoscript_docs/schema.md +292 -0
- package/xanoscript_docs/security.md +550 -0
- package/xanoscript_docs/streaming.md +318 -0
- package/xanoscript_docs/syntax.md +267 -0
- package/xanoscript_docs/triggers.md +354 -52
- package/xanoscript_docs/types.md +66 -21
|
@@ -405,6 +405,164 @@ db.external.oracle.direct_query { ... }
|
|
|
405
405
|
|
|
406
406
|
---
|
|
407
407
|
|
|
408
|
+
## Bulk Operations
|
|
409
|
+
|
|
410
|
+
Perform batch operations on multiple records efficiently.
|
|
411
|
+
|
|
412
|
+
### db.bulk.add
|
|
413
|
+
|
|
414
|
+
Insert multiple records in a single operation.
|
|
415
|
+
|
|
416
|
+
```xs
|
|
417
|
+
db.bulk.add "product" {
|
|
418
|
+
data = [
|
|
419
|
+
{ name: "Product A", price: 10.00, sku: "SKU-A" },
|
|
420
|
+
{ name: "Product B", price: 20.00, sku: "SKU-B" },
|
|
421
|
+
{ name: "Product C", price: 30.00, sku: "SKU-C" }
|
|
422
|
+
]
|
|
423
|
+
} as $inserted
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### db.bulk.update
|
|
427
|
+
|
|
428
|
+
Update multiple records matching conditions.
|
|
429
|
+
|
|
430
|
+
```xs
|
|
431
|
+
db.bulk.update "product" {
|
|
432
|
+
where = $db.product.category_id == $input.category_id
|
|
433
|
+
data = {
|
|
434
|
+
is_featured: true,
|
|
435
|
+
updated_at: now
|
|
436
|
+
}
|
|
437
|
+
} as $count
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### db.bulk.patch
|
|
441
|
+
|
|
442
|
+
Patch multiple records with variable data.
|
|
443
|
+
|
|
444
|
+
```xs
|
|
445
|
+
var $updates { value = { updated_at: now } }
|
|
446
|
+
|
|
447
|
+
conditional {
|
|
448
|
+
if ($input.discount != null) {
|
|
449
|
+
var.update $updates { value = $updates|set:"discount":$input.discount }
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
db.bulk.patch "product" {
|
|
454
|
+
where = $db.product.category_id == $input.category_id
|
|
455
|
+
data = $updates
|
|
456
|
+
} as $count
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### db.bulk.delete
|
|
460
|
+
|
|
461
|
+
Delete multiple records matching conditions.
|
|
462
|
+
|
|
463
|
+
```xs
|
|
464
|
+
db.bulk.delete "temp_session" {
|
|
465
|
+
where = $db.temp_session.expires_at < now
|
|
466
|
+
} as $deleted_count
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Bulk with Transaction
|
|
470
|
+
|
|
471
|
+
```xs
|
|
472
|
+
db.transaction {
|
|
473
|
+
stack {
|
|
474
|
+
db.bulk.delete "order_item" {
|
|
475
|
+
where = $db.order_item.order_id == $input.order_id
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
db.bulk.add "order_item" {
|
|
479
|
+
data = $input.items|map:{
|
|
480
|
+
order_id: $input.order_id,
|
|
481
|
+
product_id: $$.product_id,
|
|
482
|
+
quantity: $$.quantity
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
## Advanced Features
|
|
492
|
+
|
|
493
|
+
### db.set_datasource
|
|
494
|
+
|
|
495
|
+
Switch to a different data source within a function.
|
|
496
|
+
|
|
497
|
+
```xs
|
|
498
|
+
db.set_datasource {
|
|
499
|
+
name = "analytics_db"
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
db.query "metrics" {
|
|
503
|
+
where = $db.metrics.date >= $input.start_date
|
|
504
|
+
} as $metrics
|
|
505
|
+
|
|
506
|
+
db.set_datasource {
|
|
507
|
+
name = "default"
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### db.schema
|
|
512
|
+
|
|
513
|
+
Get schema information for a table.
|
|
514
|
+
|
|
515
|
+
```xs
|
|
516
|
+
db.schema {
|
|
517
|
+
table = "user"
|
|
518
|
+
} as $schema
|
|
519
|
+
|
|
520
|
+
// $schema contains:
|
|
521
|
+
// - columns: array of column definitions
|
|
522
|
+
// - indexes: array of index definitions
|
|
523
|
+
// - constraints: array of constraints
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### Transaction Isolation Levels
|
|
527
|
+
|
|
528
|
+
```xs
|
|
529
|
+
db.transaction {
|
|
530
|
+
isolation = "serializable" // serializable, repeatable_read, read_committed
|
|
531
|
+
stack {
|
|
532
|
+
// Operations run with specified isolation
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
### Deadlock Handling
|
|
538
|
+
|
|
539
|
+
```xs
|
|
540
|
+
try_catch {
|
|
541
|
+
try {
|
|
542
|
+
db.transaction {
|
|
543
|
+
stack {
|
|
544
|
+
db.edit "inventory" { ... }
|
|
545
|
+
db.edit "order" { ... }
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
conditional {
|
|
551
|
+
if ($error.name == "DeadlockError") {
|
|
552
|
+
// Retry logic
|
|
553
|
+
util.sleep { value = 100 }
|
|
554
|
+
function.run "retry_transaction" { input = $input }
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
throw { name = $error.name, value = $error.message }
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
408
566
|
## Best Practices
|
|
409
567
|
|
|
410
568
|
1. **Use db.query for searches** - Flexible filtering and pagination
|
|
@@ -412,3 +570,5 @@ db.external.oracle.direct_query { ... }
|
|
|
412
570
|
3. **Use db.patch for dynamic updates** - Accepts variable data
|
|
413
571
|
4. **Use transactions for atomicity** - Ensure all-or-nothing operations
|
|
414
572
|
5. **Use null-safe operators** - `==?` for optional filters
|
|
573
|
+
6. **Use bulk operations for batch processing** - More efficient than loops
|
|
574
|
+
7. **Handle deadlocks gracefully** - Implement retry logic for concurrent writes
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*.xs"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Debugging
|
|
6
|
+
|
|
7
|
+
Tools for logging, inspecting, and debugging XanoScript execution.
|
|
8
|
+
|
|
9
|
+
## Quick Reference
|
|
10
|
+
|
|
11
|
+
| Operation | Purpose |
|
|
12
|
+
|-----------|---------|
|
|
13
|
+
| `debug.log` | Log message to console |
|
|
14
|
+
| `debug.stop` | Stop execution at point |
|
|
15
|
+
| Request history | View past executions |
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## debug.log
|
|
20
|
+
|
|
21
|
+
Log messages and values for debugging.
|
|
22
|
+
|
|
23
|
+
```xs
|
|
24
|
+
debug.log { value = "Starting process" }
|
|
25
|
+
|
|
26
|
+
debug.log { value = $user }
|
|
27
|
+
|
|
28
|
+
debug.log { value = { step: "validation", data: $input } }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### With Labels
|
|
32
|
+
|
|
33
|
+
```xs
|
|
34
|
+
debug.log {
|
|
35
|
+
label = "User Data"
|
|
36
|
+
value = $user
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
debug.log {
|
|
40
|
+
label = "Query Result"
|
|
41
|
+
value = $products
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Conditional Logging
|
|
46
|
+
|
|
47
|
+
```xs
|
|
48
|
+
conditional {
|
|
49
|
+
if ($env.DEBUG == "true") {
|
|
50
|
+
debug.log {
|
|
51
|
+
label = "Debug Info"
|
|
52
|
+
value = {
|
|
53
|
+
input: $input,
|
|
54
|
+
auth: $auth,
|
|
55
|
+
timestamp: now
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## debug.stop
|
|
65
|
+
|
|
66
|
+
Halt execution at a specific point for inspection.
|
|
67
|
+
|
|
68
|
+
```xs
|
|
69
|
+
debug.log { value = "Before database call" }
|
|
70
|
+
|
|
71
|
+
db.query "user" {
|
|
72
|
+
where = $db.user.id == $input.user_id
|
|
73
|
+
} as $user
|
|
74
|
+
|
|
75
|
+
debug.stop // Execution pauses here
|
|
76
|
+
|
|
77
|
+
// Code below does not run
|
|
78
|
+
db.edit "user" { ... }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Conditional Stop
|
|
82
|
+
|
|
83
|
+
```xs
|
|
84
|
+
conditional {
|
|
85
|
+
if ($user == null) {
|
|
86
|
+
debug.log { value = "User not found, stopping" }
|
|
87
|
+
debug.stop
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### With Data Inspection
|
|
93
|
+
|
|
94
|
+
```xs
|
|
95
|
+
debug.log {
|
|
96
|
+
label = "State at stop point"
|
|
97
|
+
value = {
|
|
98
|
+
input: $input,
|
|
99
|
+
user: $user,
|
|
100
|
+
calculated: $total
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
debug.stop
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Logging Patterns
|
|
109
|
+
|
|
110
|
+
### Request Tracing
|
|
111
|
+
|
|
112
|
+
```xs
|
|
113
|
+
function "process_order" {
|
|
114
|
+
input { int order_id }
|
|
115
|
+
stack {
|
|
116
|
+
var $trace_id { value = |uuid }
|
|
117
|
+
|
|
118
|
+
debug.log {
|
|
119
|
+
label = "TRACE_START"
|
|
120
|
+
value = { trace_id: $trace_id, order_id: $input.order_id }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ... processing ...
|
|
124
|
+
|
|
125
|
+
debug.log {
|
|
126
|
+
label = "TRACE_END"
|
|
127
|
+
value = { trace_id: $trace_id, status: "success" }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Error Context
|
|
134
|
+
|
|
135
|
+
```xs
|
|
136
|
+
try_catch {
|
|
137
|
+
try {
|
|
138
|
+
function.run "risky_operation" { input = $data }
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
debug.log {
|
|
142
|
+
label = "ERROR"
|
|
143
|
+
value = {
|
|
144
|
+
operation: "risky_operation",
|
|
145
|
+
input: $data,
|
|
146
|
+
error: $error.message,
|
|
147
|
+
stack: $error.stack
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
throw { name = $error.name, value = $error.message }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Performance Timing
|
|
156
|
+
|
|
157
|
+
```xs
|
|
158
|
+
var $start { value = now|to_ms }
|
|
159
|
+
|
|
160
|
+
db.query "product" { ... } as $products
|
|
161
|
+
|
|
162
|
+
var $query_time { value = (now|to_ms) - $start }
|
|
163
|
+
|
|
164
|
+
debug.log {
|
|
165
|
+
label = "PERF"
|
|
166
|
+
value = {
|
|
167
|
+
operation: "product_query",
|
|
168
|
+
duration_ms: $query_time,
|
|
169
|
+
record_count: $products|count
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Step-by-Step
|
|
175
|
+
|
|
176
|
+
```xs
|
|
177
|
+
debug.log { value = "Step 1: Validating input" }
|
|
178
|
+
// validation code
|
|
179
|
+
|
|
180
|
+
debug.log { value = "Step 2: Fetching user" }
|
|
181
|
+
db.get "user" { ... } as $user
|
|
182
|
+
|
|
183
|
+
debug.log { value = "Step 3: Processing order" }
|
|
184
|
+
// order processing
|
|
185
|
+
|
|
186
|
+
debug.log { value = "Step 4: Sending notification" }
|
|
187
|
+
// notification code
|
|
188
|
+
|
|
189
|
+
debug.log { value = "Complete" }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Inspecting Variables
|
|
195
|
+
|
|
196
|
+
### Object Inspection
|
|
197
|
+
|
|
198
|
+
```xs
|
|
199
|
+
debug.log {
|
|
200
|
+
label = "User object"
|
|
201
|
+
value = $user
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Logs full object structure:
|
|
205
|
+
// {
|
|
206
|
+
// id: 123,
|
|
207
|
+
// name: "John",
|
|
208
|
+
// email: "john@example.com",
|
|
209
|
+
// ...
|
|
210
|
+
// }
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Array Inspection
|
|
214
|
+
|
|
215
|
+
```xs
|
|
216
|
+
debug.log {
|
|
217
|
+
label = "Products"
|
|
218
|
+
value = {
|
|
219
|
+
count: $products|count,
|
|
220
|
+
first: $products|first,
|
|
221
|
+
last: $products|last,
|
|
222
|
+
sample: $products|slice:0:3
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Expression Evaluation
|
|
228
|
+
|
|
229
|
+
```xs
|
|
230
|
+
debug.log {
|
|
231
|
+
label = "Calculated values"
|
|
232
|
+
value = {
|
|
233
|
+
total: $items|map:$$.price * $$.quantity|sum,
|
|
234
|
+
tax: ($items|map:$$.price * $$.quantity|sum) * 0.08,
|
|
235
|
+
item_count: $items|count
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Environment-Based Debugging
|
|
243
|
+
|
|
244
|
+
### Development Only
|
|
245
|
+
|
|
246
|
+
```xs
|
|
247
|
+
conditional {
|
|
248
|
+
if ($env.ENVIRONMENT == "development") {
|
|
249
|
+
debug.log { value = "Development mode: verbose logging enabled" }
|
|
250
|
+
debug.log { value = $input }
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Debug Flag
|
|
256
|
+
|
|
257
|
+
```xs
|
|
258
|
+
// Enable with environment variable DEBUG=true
|
|
259
|
+
conditional {
|
|
260
|
+
if ($env.DEBUG|to_bool == true) {
|
|
261
|
+
debug.log {
|
|
262
|
+
label = "Debug"
|
|
263
|
+
value = { step: "after_query", data: $result }
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Request History
|
|
272
|
+
|
|
273
|
+
View past request executions in the Xano dashboard:
|
|
274
|
+
|
|
275
|
+
1. Navigate to API endpoint or function
|
|
276
|
+
2. Click "Request History"
|
|
277
|
+
3. View inputs, outputs, and debug logs
|
|
278
|
+
4. Inspect step-by-step execution
|
|
279
|
+
|
|
280
|
+
### What's Captured
|
|
281
|
+
|
|
282
|
+
- Input parameters
|
|
283
|
+
- Output response
|
|
284
|
+
- All debug.log messages
|
|
285
|
+
- Execution time per step
|
|
286
|
+
- Database queries executed
|
|
287
|
+
- External API calls made
|
|
288
|
+
- Errors and stack traces
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Debugging Strategies
|
|
293
|
+
|
|
294
|
+
### Isolate the Problem
|
|
295
|
+
|
|
296
|
+
```xs
|
|
297
|
+
// Comment out sections to isolate issues
|
|
298
|
+
debug.log { value = "Checkpoint 1" }
|
|
299
|
+
// potentially problematic code
|
|
300
|
+
debug.log { value = "Checkpoint 2" }
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Validate Assumptions
|
|
304
|
+
|
|
305
|
+
```xs
|
|
306
|
+
debug.log {
|
|
307
|
+
label = "Assumption check"
|
|
308
|
+
value = {
|
|
309
|
+
user_exists: $user != null,
|
|
310
|
+
has_permission: $user.role == "admin",
|
|
311
|
+
input_valid: $input.amount > 0
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Compare Expected vs Actual
|
|
317
|
+
|
|
318
|
+
```xs
|
|
319
|
+
var $expected { value = 100 }
|
|
320
|
+
var $actual { value = $items|count }
|
|
321
|
+
|
|
322
|
+
debug.log {
|
|
323
|
+
label = "Comparison"
|
|
324
|
+
value = {
|
|
325
|
+
expected: $expected,
|
|
326
|
+
actual: $actual,
|
|
327
|
+
match: $expected == $actual
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Best Practices
|
|
335
|
+
|
|
336
|
+
1. **Remove debug.stop in production** - Causes execution to halt
|
|
337
|
+
2. **Use labels consistently** - Makes log searching easier
|
|
338
|
+
3. **Log at boundaries** - Function entry/exit, external calls
|
|
339
|
+
4. **Include context** - IDs, timestamps, relevant state
|
|
340
|
+
5. **Don't log sensitive data** - Passwords, tokens, PII
|
|
341
|
+
6. **Use environment flags** - Enable verbose logging conditionally
|
|
342
|
+
7. **Check request history** - Built-in debugging in dashboard
|
|
@@ -292,6 +292,176 @@ function "validate_email_unique" {
|
|
|
292
292
|
|
|
293
293
|
---
|
|
294
294
|
|
|
295
|
+
## Async Operations
|
|
296
|
+
|
|
297
|
+
### await
|
|
298
|
+
|
|
299
|
+
Wait for an asynchronous operation to complete.
|
|
300
|
+
|
|
301
|
+
```xs
|
|
302
|
+
stack {
|
|
303
|
+
api.request {
|
|
304
|
+
url = "https://api.example.com/data"
|
|
305
|
+
method = "GET"
|
|
306
|
+
async = true
|
|
307
|
+
} as $request
|
|
308
|
+
|
|
309
|
+
await $request as $response
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### group (Parallel Execution)
|
|
314
|
+
|
|
315
|
+
Execute multiple operations in parallel and wait for all to complete.
|
|
316
|
+
|
|
317
|
+
```xs
|
|
318
|
+
stack {
|
|
319
|
+
group {
|
|
320
|
+
api.request {
|
|
321
|
+
url = "https://api.example.com/users"
|
|
322
|
+
method = "GET"
|
|
323
|
+
} as $users
|
|
324
|
+
|
|
325
|
+
api.request {
|
|
326
|
+
url = "https://api.example.com/products"
|
|
327
|
+
method = "GET"
|
|
328
|
+
} as $products
|
|
329
|
+
|
|
330
|
+
api.request {
|
|
331
|
+
url = "https://api.example.com/orders"
|
|
332
|
+
method = "GET"
|
|
333
|
+
} as $orders
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// All three requests complete before continuing
|
|
337
|
+
var $combined {
|
|
338
|
+
value = {
|
|
339
|
+
users: $users.body,
|
|
340
|
+
products: $products.body,
|
|
341
|
+
orders: $orders.body
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Parallel Database Queries
|
|
348
|
+
|
|
349
|
+
```xs
|
|
350
|
+
stack {
|
|
351
|
+
group {
|
|
352
|
+
db.query "user" {
|
|
353
|
+
where = $db.user.is_active == true
|
|
354
|
+
return = { type: "count" }
|
|
355
|
+
} as $active_users
|
|
356
|
+
|
|
357
|
+
db.query "order" {
|
|
358
|
+
where = $db.order.created_at >= $input.start_date
|
|
359
|
+
return = { type: "count" }
|
|
360
|
+
} as $order_count
|
|
361
|
+
|
|
362
|
+
db.query "product" {
|
|
363
|
+
where = $db.product.stock == 0
|
|
364
|
+
return = { type: "count" }
|
|
365
|
+
} as $out_of_stock
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
response = {
|
|
369
|
+
active_users: $active_users,
|
|
370
|
+
orders: $order_count,
|
|
371
|
+
out_of_stock: $out_of_stock
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Advanced Loop Patterns
|
|
379
|
+
|
|
380
|
+
### remove (In-Loop Deletion)
|
|
381
|
+
|
|
382
|
+
Remove the current element during foreach iteration.
|
|
383
|
+
|
|
384
|
+
```xs
|
|
385
|
+
stack {
|
|
386
|
+
var $items { value = $input.items }
|
|
387
|
+
|
|
388
|
+
foreach ($items) {
|
|
389
|
+
each as $item {
|
|
390
|
+
conditional {
|
|
391
|
+
if ($item.expired == true) {
|
|
392
|
+
remove
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// $items now excludes expired items
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### as (Variable Aliasing)
|
|
403
|
+
|
|
404
|
+
Alias loop variables for clearer access.
|
|
405
|
+
|
|
406
|
+
```xs
|
|
407
|
+
foreach ($orders) {
|
|
408
|
+
each as $order {
|
|
409
|
+
// $order available here
|
|
410
|
+
foreach ($order.items) {
|
|
411
|
+
each as $item {
|
|
412
|
+
// Both $order and $item available
|
|
413
|
+
db.add "order_item" {
|
|
414
|
+
data = {
|
|
415
|
+
order_id: $order.id,
|
|
416
|
+
product_id: $item.product_id,
|
|
417
|
+
quantity: $item.quantity
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Loop Control
|
|
427
|
+
|
|
428
|
+
```xs
|
|
429
|
+
foreach ($items) {
|
|
430
|
+
each as $item {
|
|
431
|
+
conditional {
|
|
432
|
+
if ($item.skip) {
|
|
433
|
+
continue // Skip to next iteration
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
conditional {
|
|
438
|
+
if ($item.stop) {
|
|
439
|
+
break // Exit loop entirely
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Process item
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Loop with Index
|
|
449
|
+
|
|
450
|
+
```xs
|
|
451
|
+
foreach ($items) {
|
|
452
|
+
each as $item, $index {
|
|
453
|
+
db.add "item" {
|
|
454
|
+
data = {
|
|
455
|
+
value: $item,
|
|
456
|
+
position: $index
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
295
465
|
## Best Practices
|
|
296
466
|
|
|
297
467
|
1. **Single responsibility** - Each function does one thing well
|
|
@@ -299,3 +469,5 @@ function "validate_email_unique" {
|
|
|
299
469
|
3. **Organize in folders** - Group related functions: `utils/`, `auth/`, `orders/`
|
|
300
470
|
4. **Return early** - Use return for guard clauses
|
|
301
471
|
5. **Keep stacks shallow** - Avoid deeply nested conditionals
|
|
472
|
+
6. **Use group for parallel calls** - Improves performance for independent operations
|
|
473
|
+
7. **Use remove sparingly** - Consider filtering arrays instead
|