@xano/developer-mcp 1.0.58 → 1.0.59

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.
Files changed (38) hide show
  1. package/README.md +15 -0
  2. package/dist/tools/index.d.ts +9 -0
  3. package/dist/tools/xanoscript_docs.d.ts +9 -0
  4. package/dist/tools/xanoscript_docs.js +27 -0
  5. package/dist/xanoscript.d.ts +5 -1
  6. package/dist/xanoscript.js +70 -6
  7. package/dist/xanoscript.test.js +5 -3
  8. package/dist/xanoscript_docs/README.md +9 -43
  9. package/dist/xanoscript_docs/addons.md +0 -2
  10. package/dist/xanoscript_docs/agents.md +2 -35
  11. package/dist/xanoscript_docs/apis.md +3 -6
  12. package/dist/xanoscript_docs/branch.md +0 -2
  13. package/dist/xanoscript_docs/database.md +3 -7
  14. package/dist/xanoscript_docs/debugging.md +1 -264
  15. package/dist/xanoscript_docs/docs_index.json +22 -0
  16. package/dist/xanoscript_docs/essentials.md +1 -9
  17. package/dist/xanoscript_docs/frontend.md +1 -138
  18. package/dist/xanoscript_docs/functions.md +3 -7
  19. package/dist/xanoscript_docs/mcp-servers.md +1 -2
  20. package/dist/xanoscript_docs/middleware.md +1 -3
  21. package/dist/xanoscript_docs/performance.md +8 -198
  22. package/dist/xanoscript_docs/realtime.md +11 -161
  23. package/dist/xanoscript_docs/run.md +2 -184
  24. package/dist/xanoscript_docs/schema.md +1 -3
  25. package/dist/xanoscript_docs/security.md +82 -313
  26. package/dist/xanoscript_docs/streaming.md +2 -37
  27. package/dist/xanoscript_docs/survival.md +161 -0
  28. package/dist/xanoscript_docs/syntax.md +0 -6
  29. package/dist/xanoscript_docs/tables.md +3 -5
  30. package/dist/xanoscript_docs/tasks.md +1 -3
  31. package/dist/xanoscript_docs/tools.md +1 -3
  32. package/dist/xanoscript_docs/triggers.md +3 -69
  33. package/dist/xanoscript_docs/types.md +3 -4
  34. package/dist/xanoscript_docs/unit-testing.md +1 -55
  35. package/dist/xanoscript_docs/workflow-tests.md +8 -35
  36. package/dist/xanoscript_docs/working.md +667 -0
  37. package/dist/xanoscript_docs/workspace.md +0 -2
  38. package/package.json +1 -1
@@ -137,143 +137,20 @@ db.bulk.add "order_item" {
137
137
 
138
138
  ## Caching Strategies
139
139
 
140
- ### Redis Caching
141
-
142
- ```xs
143
- // Check cache first
144
- redis.get { key = "user:" ~ $input.user_id } as $cached
145
-
146
- conditional {
147
- if ($cached != null) {
148
- var $user { value = $cached }
149
- }
150
- else {
151
- db.get "user" {
152
- field_name = "id"
153
- field_value = $input.user_id
154
- } as $user
155
-
156
- // Cache for 5 minutes
157
- redis.set {
158
- key = "user:" ~ $input.user_id
159
- data = $user
160
- ttl = 300
161
- }
162
- }
163
- }
164
- ```
165
-
166
- ### Cache Invalidation
167
-
168
- ```xs
169
- function "update_user" {
170
- input { int user_id, object data }
171
- stack {
172
- db.patch "user" {
173
- field_name = "id"
174
- field_value = $input.user_id
175
- data = $input.data
176
- } as $user
177
-
178
- // Invalidate cache
179
- redis.del { key = "user:" ~ $input.user_id }
180
- }
181
- response = $user
182
- }
183
- ```
184
-
185
- ### Computed Value Caching
186
-
187
- ```xs
188
- // Cache expensive computations
189
- redis.get { key = "stats:daily:" ~ now|format_timestamp:"Y-m-d" } as $cached_stats
190
-
191
- conditional {
192
- if ($cached_stats == null) {
193
- // Expensive aggregation
194
- db.query "order" {
195
- where = $db.order.created_at >= now|transform_timestamp:"start of day"
196
- } as $orders
197
-
198
- var $stats {
199
- value = {
200
- count: $orders|count,
201
- total: $orders|map:$$.total|sum,
202
- avg: $orders|map:$$.total|avg
203
- }
204
- }
205
-
206
- redis.set {
207
- key = "stats:daily:" ~ now|format_timestamp:"Y-m-d"
208
- data = $stats
209
- ttl = 300
210
- }
211
-
212
- var $cached_stats { value = $stats }
213
- }
214
- }
215
- ```
216
-
217
- ---
218
-
219
- ## Organizing with Group
220
-
221
- The `group` statement is an organizational block for visually grouping related statements. It does **not** create parallel execution or a new scope — variables declared inside a group are accessible outside it.
222
-
223
- ```xs
224
- // Use group to organize related variable initialization
225
- group {
226
- stack {
227
- var $total {
228
- value = 0
229
- }
230
-
231
- var $count {
232
- value = 0
233
- }
234
- }
235
- }
236
-
237
- // $total and $count are accessible here
238
- ```
140
+ > See `xanoscript_docs({ topic: "integrations/redis" })` for full Redis caching patterns (get/set, invalidation, computed value caching).
239
141
 
240
142
  ---
241
143
 
242
144
  ## Efficient Data Transformations
243
145
 
244
- ### Use Filter Chains Wisely
245
-
246
146
  ```xs
247
147
  // Good: Single pass with chained filters
248
148
  $data|filter:$$.active|map:$$.name|unique
249
149
 
250
- // Avoid: Multiple passes over data
251
- var $active { value = $data|filter:$$.active }
252
- var $names { value = $active|map:$$.name }
253
- var $unique { value = $names|unique }
254
- ```
255
-
256
- ### Early Filtering
257
-
258
- ```xs
259
150
  // Filter in database, not in code
260
151
  db.query "product" {
261
152
  where = $db.product.is_active == true && $db.product.price > 0
262
153
  } as $products
263
-
264
- // Not: Fetch all then filter
265
- db.query "product" as $all_products
266
- var $products { value = $all_products|filter:$$.is_active && $$.price > 0 }
267
- ```
268
-
269
- ### Limit Data Transfer
270
-
271
- ```xs
272
- // Only fetch needed fields from external API
273
- api.request {
274
- url = "https://api.example.com/users"
275
- params = { fields: "id,name,email" }
276
- } as $result
277
154
  ```
278
155
 
279
156
  ---
@@ -308,92 +185,25 @@ redis.ratelimit {
308
185
 
309
186
  ## Response Optimization
310
187
 
311
- ### Stream Large Responses
312
-
313
- ```xs
314
- // Stream instead of loading into memory
315
- db.query "log" {
316
- where = $db.log.created_at >= $input.start
317
- return = { type: "stream" }
318
- } as $logs
319
-
320
- api.stream {
321
- format = "jsonl"
322
- value = $logs
323
- }
324
- ```
325
-
326
- ### Compress Responses
327
-
328
- Large JSON responses are automatically compressed when clients support it.
188
+ For large responses, use `return = { type: "stream" }` with `api.stream` to avoid loading into memory. Large JSON responses are automatically compressed when clients support it.
329
189
 
330
- ### Paginate API Responses
331
-
332
- ```xs
333
- query "list_products" {
334
- input {
335
- int page?=1
336
- int per_page?=25 filters=max:100
337
- }
338
- stack {
339
- db.query "product" {
340
- return = {
341
- type: "list",
342
- paging: {
343
- page: $input.page,
344
- per_page: $input.per_page,
345
- totals: true
346
- }
347
- }
348
- } as $products
349
- }
350
- response = $products
351
- }
352
- ```
190
+ > See `xanoscript_docs({ topic: "streaming" })` for streaming patterns.
353
191
 
354
192
  ---
355
193
 
356
194
  ## Query Analysis
357
195
 
358
- ### Log Slow Queries
359
-
360
- ```xs
361
- var $start { value = now|to_ms }
362
-
363
- db.query "product" {
364
- where = $db.product.category == $input.category
365
- } as $products
366
-
367
- var $duration { value = (now|to_ms) - $start }
196
+ Use `now|to_ms` before and after operations to measure duration, and `debug.log` to log slow queries.
368
197
 
369
- conditional {
370
- if ($duration > 100) {
371
- debug.log {
372
- label = "SLOW_QUERY"
373
- value = {
374
- query: "product by category",
375
- duration_ms: $duration,
376
- result_count: $products|count
377
- }
378
- }
379
- }
380
- }
381
- ```
198
+ > See `xanoscript_docs({ topic: "debugging" })` for timing and logging patterns.
382
199
 
383
200
  ---
384
201
 
385
202
  ## Best Practices Summary
386
203
 
387
- 1. **Index frequently queried columns** - Check query patterns
388
- 2. **Use appropriate return types** - count, exists, single
389
- 3. **Paginate large results** - Never return unbounded lists
390
- 4. **Avoid N+1 queries** - Use joins or batch fetching
391
- 5. **Use bulk operations** - For batch inserts/updates
392
- 6. **Cache expensive operations** - Redis with appropriate TTL
393
- 7. **Use group for organization** - Group related statements for readability
394
- 8. **Filter early** - In database, not application code
395
- 9. **Stream large responses** - Don't load into memory
396
- 10. **Monitor performance** - Log slow operations
204
+ 1. **Index frequently queried columns** - Use `select` for only needed fields; use `count`/`exists` return types
205
+ 2. **Avoid N+1 queries** - Use joins or bulk operations (`db.bulk.add`, `db.bulk.edit`)
206
+ 3. **Paginate large results** - Never return unbounded lists; cache expensive computations with Redis
397
207
 
398
208
  ---
399
209
 
@@ -265,135 +265,18 @@ Handle events from connected clients using `realtime_trigger`. For complete trig
265
265
 
266
266
  ---
267
267
 
268
- ## Common Patterns
269
-
270
- ### Chat Application
268
+ ## Common Pattern: Broadcast After Database Write
271
269
 
272
270
  ```xs
273
- function "send_chat_message" {
274
- input {
275
- int room_id
276
- text content filters=trim|max:1000
277
- }
278
- stack {
279
- // Save message
280
- db.add "message" {
281
- data = {
282
- room_id: $input.room_id,
283
- sender_id: $auth.id,
284
- content: $input.content,
285
- created_at: now
286
- }
287
- } as $message
288
-
289
- // Broadcast to room
290
- api.realtime_event {
291
- channel = "room:" ~ $input.room_id
292
- event = "new_message"
293
- data = {
294
- id: $message.id,
295
- sender_id: $auth.id,
296
- content: $input.content,
297
- created_at: $message.created_at
298
- }
299
- }
300
- }
301
- response = $message
302
- }
303
- ```
304
-
305
- ### Live Dashboard Updates
306
-
307
- ```xs
308
- function "update_metrics" {
309
- stack {
310
- // Calculate metrics
311
- db.query "order" {
312
- where = $db.order.created_at >= now|transform_timestamp:"-1 hour"
313
- } as $recent_orders
314
-
315
- var $metrics {
316
- value = {
317
- orders_last_hour: $recent_orders|count,
318
- revenue_last_hour: $recent_orders|map:$$.total|sum,
319
- updated_at: now
320
- }
321
- }
271
+ // Save to database, then broadcast to subscribers
272
+ db.add "message" {
273
+ data = { room_id: $input.room_id, sender_id: $auth.id, content: $input.content }
274
+ } as $message
322
275
 
323
- // Push to dashboard subscribers
324
- api.realtime_event {
325
- channel = "dashboard:metrics"
326
- event = "update"
327
- data = $metrics
328
- }
329
- }
330
- response = $metrics
331
- }
332
- ```
333
-
334
- ### Collaborative Editing
335
-
336
- ```xs
337
- realtime_trigger "document_edit" {
338
- channel = "document:*"
339
- event = "operation"
340
- stack {
341
- // Apply operation to document
342
- function.run "apply_document_op" {
343
- input = {
344
- document_id: $input.document_id,
345
- operation: $input.operation,
346
- user_id: $auth.id
347
- }
348
- } as $result
349
-
350
- // Broadcast to other editors
351
- api.realtime_event {
352
- channel = $channel
353
- event = "remote_operation"
354
- data = {
355
- operation: $input.operation,
356
- user_id: $auth.id,
357
- version: $result.version
358
- }
359
- }
360
- }
361
- }
362
- ```
363
-
364
- ### Notification System
365
-
366
- ```xs
367
- function "notify_user" {
368
- input {
369
- int user_id
370
- text type
371
- text title
372
- text body
373
- json? data
374
- }
375
- stack {
376
- // Save notification
377
- db.add "notification" {
378
- data = {
379
- user_id: $input.user_id,
380
- type: $input.type,
381
- title: $input.title,
382
- body: $input.body,
383
- data: $input.data,
384
- read: false,
385
- created_at: now
386
- }
387
- } as $notification
388
-
389
- // Push to user
390
- api.realtime_event {
391
- channel = "user:" ~ $input.user_id
392
- event = "notification"
393
- data = $notification
394
- }
395
- }
396
- response = $notification
276
+ api.realtime_event {
277
+ channel = "room:" ~ $input.room_id
278
+ event = "new_message"
279
+ data = $message
397
280
  }
398
281
  ```
399
282
 
@@ -401,39 +284,9 @@ function "notify_user" {
401
284
 
402
285
  ## Subscription Management
403
286
 
404
- Clients subscribe to channels client-side. Server controls what events to send.
287
+ Clients subscribe to channels client-side. Server controls what events to send. Validate channel access with preconditions before broadcasting.
405
288
 
406
- ### Authorization Pattern
407
-
408
- ```xs
409
- // Validate user can access channel before sending
410
- function "send_to_room" {
411
- input {
412
- int room_id
413
- text event
414
- json data
415
- }
416
- stack {
417
- // Check membership
418
- db.query "room_member" {
419
- where = $db.room_member.room_id == $input.room_id
420
- && $db.room_member.user_id == $auth.id
421
- return = { type: "exists" }
422
- } as $is_member
423
-
424
- precondition ($is_member) {
425
- error_type = "accessdenied"
426
- error = "Not a member of this room"
427
- }
428
-
429
- api.realtime_event {
430
- channel = "room:" ~ $input.room_id
431
- event = $input.event
432
- data = $input.data
433
- }
434
- }
435
- }
436
- ```
289
+ > See `xanoscript_docs({ topic: "security" })` for authorization patterns.
437
290
 
438
291
  ---
439
292
 
@@ -442,6 +295,3 @@ function "send_to_room" {
442
295
  1. **Use channel namespacing** - `type:id` format for clarity
443
296
  2. **Keep payloads small** - Send IDs, let client fetch details
444
297
  3. **Validate before broadcast** - Don't trust client event data
445
- 4. **Use triggers for client events** - Handle incoming realtime events
446
- 5. **Consider fan-out carefully** - Large broadcasts can be expensive
447
- 6. **Implement presence** - Track who's connected to channels
@@ -56,16 +56,6 @@ run.job "Random Dad Joke" {
56
56
  }
57
57
  ```
58
58
 
59
- ### With Empty Input (Alternative)
60
- ```xs
61
- run.job "Random Dad Joke" {
62
- main = {
63
- name: "fetch_dad_joke"
64
- input: {}
65
- }
66
- }
67
- ```
68
-
69
59
  ### With Input Parameters
70
60
  ```xs
71
61
  run.job "Average of values" {
@@ -134,16 +124,6 @@ run.service "Random Dad Joke" {
134
124
  }
135
125
  ```
136
126
 
137
- ### With Empty Input (Alternative)
138
- ```xs
139
- run.service "Random Dad Joke" {
140
- pre = {
141
- name: "fetch_dad_joke"
142
- input: {}
143
- }
144
- }
145
- ```
146
-
147
127
  ### With Initialization
148
128
  ```xs
149
129
  run.service "email proxy" {
@@ -173,169 +153,9 @@ run.service "webhook listener" {
173
153
 
174
154
  ## Supporting Files
175
155
 
176
- Jobs and services can include supporting tables and functions.
177
-
178
- ### Table with Seed Data
179
- ```xs
180
- table users {
181
- auth = false
182
- schema {
183
- int id
184
- text name
185
- text email
186
- timestamp created_at?=now
187
- }
188
- index = [
189
- {type: "primary", field: [{name: "id"}]}
190
- ]
191
- items = [
192
- {"id": 1, "name": "Alice", "email": "alice@example.com"}
193
- {"id": 2, "name": "Bob", "email": "bob@example.com"}
194
- ]
195
- }
196
- ```
197
-
198
- ### Function Definition
199
- ```xs
200
- function "process_data" {
201
- input {
202
- json data
203
- }
204
- stack {
205
- db.query users {
206
- return = { type: "list" }
207
- } as $users
208
- }
209
- response = $users
210
- }
211
- ```
212
-
213
- ---
214
-
215
- ## Complete Job Example
216
-
217
- ```
218
- run.xs
219
- table/
220
- └── users.xs
221
- function/
222
- └── migrate_users.xs
223
- ```
224
-
225
- ### run.xs
226
- ```xs
227
- run.job "Data Migration" {
228
- main = {
229
- name: "migrate_users"
230
- input: {
231
- batch_size: 100
232
- }
233
- }
234
- env = ["source_db_url", "webhook_url"]
235
- }
236
- ```
237
-
238
- ### table/users.xs
239
- ```xs
240
- table users {
241
- auth = false
242
- schema {
243
- int id
244
- text name
245
- text email
246
- timestamp migrated_at?
247
- }
248
- index = [
249
- {type: "primary", field: [{name: "id"}]}
250
- ]
251
- items = [
252
- {"id": 1, "name": "Alice", "email": "alice@example.com"}
253
- {"id": 2, "name": "Bob", "email": "bob@example.com"}
254
- ]
255
- }
256
- ```
257
-
258
- ### function/migrate_users.xs
259
- ```xs
260
- function "migrate_users" {
261
- input {
262
- int batch_size
263
- }
264
- stack {
265
- db.query users {
266
- where = $db.users.migrated_at == null
267
- paging = { page: 1, per_page: $input.batch_size }
268
- } as $pending
269
-
270
- foreach ($pending) {
271
- each as $user {
272
- db.edit users {
273
- field_name = "id"
274
- field_value = $user.id
275
- data = { migrated_at: now }
276
- }
277
- }
278
- }
279
- }
280
- response = { migrated: $pending|count }
281
- }
282
- ```
283
-
284
- ---
285
-
286
- ## Complete Service Example
287
-
288
- ```
289
- run.xs
290
- table/
291
- └── event.xs
292
- api/
293
- └── events/
294
- ├── api_group.xs
295
- ├── list_get.xs
296
- └── add_post.xs
297
- ```
298
-
299
- ### run.xs
300
- ```xs
301
- run.service "Event Tracker" {
302
- pre = {
303
- name: "init_tracker"
304
- input: {}
305
- }
306
- env = ["api_key"]
307
- }
308
- ```
309
-
310
- ### table/event.xs
311
- ```xs
312
- table event {
313
- auth = false
314
- schema {
315
- int id
316
- timestamp created_at?=now
317
- text name
318
- }
319
- index = [
320
- {type: "primary", field: [{name: "id"}]}
321
- ]
322
- items = []
323
- }
324
- ```
156
+ Jobs and services can include supporting tables and functions in `table/` and `function/` subdirectories.
325
157
 
326
- ### api/events/list_get.xs
327
- ```xs
328
- query list verb=GET {
329
- api_group = "events"
330
- stack {
331
- db.query event {
332
- sort = { created_at: "desc" }
333
- return = { type: "list" }
334
- } as $events
335
- }
336
- response = $events
337
- }
338
- ```
158
+ > See `xanoscript_docs({ topic: "tables" })` and `xanoscript_docs({ topic: "functions" })` for table/function syntax.
339
159
 
340
160
  ---
341
161
 
@@ -364,8 +184,6 @@ query list verb=GET {
364
184
  1. **Name clearly** - Indicate purpose: `data-migration`, `email-proxy`
365
185
  2. **Use env for secrets** - Never hardcode API keys or credentials
366
186
  3. **Keep self-contained** - Include all required tables and functions
367
- 4. **Seed test data** - Use `items` in table definitions for testing
368
- 5. **Validate inputs** - Use preconditions in functions for input validation
369
187
 
370
188
  ---
371
189
 
@@ -287,6 +287,4 @@ try_catch {
287
287
 
288
288
  1. **Define schemas upfront** - Use input blocks when structure is known
289
289
  2. **Use schema.parse for dynamic data** - External APIs, user-generated content
290
- 3. **Validate at boundaries** - Parse external input, trust internal data
291
- 4. **Provide clear error messages** - Use error_message for user-facing errors
292
- 5. **Use immutable for config** - Prevent accidental modification
290
+ 3. **Validate at boundaries** - Parse external input, trust internal data; provide clear error messages