@xanots/sdk 0.0.3-beta.0 → 0.0.4
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/CHANGELOG.md +80 -0
- package/README.md +35 -2
- package/dist/agent-file-refresh-IJ7GN2FJ.js +17 -0
- package/dist/bin.js +12 -153
- package/dist/{capture-HUV5BNTC.js → capture-7PO6SGB4.js} +2 -2
- package/dist/{chunk-WRYT2PAX.js → chunk-7SISR3CS.js} +24 -36
- package/dist/{chunk-7DKX2SPN.js → chunk-ACQCK4DT.js} +4 -4
- package/dist/{chunk-XQ22GLYS.js → chunk-AJ7JQ4OU.js} +5 -5
- package/dist/{chunk-EWFHDJY5.js → chunk-ALNQCWAW.js} +2 -2
- package/dist/{chunk-55VHDNW5.js → chunk-BPEEGJJA.js} +108 -3
- package/dist/{chunk-U3G2UW65.js → chunk-CYJ7R3AD.js} +3 -3
- package/dist/{agent-file-refresh-J3KY54E5.js → chunk-D6OGU2AI.js} +5 -6
- package/dist/{chunk-VVXPK6SE.js → chunk-DUC3PXDG.js} +150 -45
- package/dist/{chunk-4HKZH6YK.js → chunk-EAIR6VUL.js} +235 -10
- package/dist/chunk-ERQZFWIW.js +22 -0
- package/dist/{chunk-TQUO2OXY.js → chunk-FN4OQT2J.js} +4 -4
- package/dist/{chunk-FY6LXRR6.js → chunk-KGNJM4LN.js} +2 -2
- package/dist/{chunk-HJPTWBLH.js → chunk-MFIHIS6B.js} +2 -2
- package/dist/{chunk-F6JCFRKO.js → chunk-PXXLBXOP.js} +2 -2
- package/dist/{chunk-YZ4GU6F5.js → chunk-QUUB7HYK.js} +5 -838
- package/dist/{chunk-HKAX756N.js → chunk-SWIXWJIY.js} +3 -3
- package/dist/{chunk-76QBEIGO.js → chunk-T4XPCJRF.js} +3 -2
- package/dist/chunk-T6N3VMMO.js +173 -0
- package/dist/{chunk-CBL57QBQ.js → chunk-VKD3EBKG.js} +112 -21
- package/dist/{chunk-MWLNBSTY.js → chunk-VWGJQTNA.js} +3 -3
- package/dist/chunk-WGDAOOXG.js +845 -0
- package/dist/{chunk-4YMD2OOZ.js → chunk-XHEXOES3.js} +1 -1
- package/dist/{chunk-4HT3BNZ7.js → chunk-YUBJLB6G.js} +10 -2
- package/dist/{chunk-5L4X5LS6.js → chunk-YUPQOLFX.js} +77 -2
- package/dist/{chunk-QO4I5OYF.js → chunk-YYSJAAJM.js} +36 -3
- package/dist/{chunk-P3TTMWUP.js → chunk-ZOYMZZ3S.js} +8 -1
- package/dist/chunk-ZSYZTGJH.js +81 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.js +8 -8
- package/dist/{codegen-command-EXLKZ2ZI.js → codegen-command-Z7WQMOJ7.js} +21 -20
- package/dist/{completion-2PVZK6YX.js → completion-4GYN752B.js} +2 -2
- package/dist/{deploy-command-SXX6RJWM.js → deploy-command-M2S4HHLC.js} +26 -26
- package/dist/{env-target-3EGIPR4D.js → env-target-GSFCYHKZ.js} +6 -6
- package/dist/{ephemeral-command-OGOYP7D6.js → ephemeral-command-6WJW7LP6.js} +24 -24
- package/dist/index.d.ts +2 -2
- package/dist/index.js +13 -7
- package/dist/init-command-GJUPWTKA.js +30 -0
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +53 -7
- package/dist/{io-AMIKRLPC.js → io-7VIA5SON.js} +3 -3
- package/dist/{live-diff-RXSJCVJ7.js → live-diff-FP4SFNT4.js} +2 -2
- package/dist/{lock-3CVKALKT.js → lock-KXOJIGCG.js} +2 -2
- package/dist/{lock-commands-R22J54XV.js → lock-commands-7IH7RSSP.js} +9 -9
- package/dist/{login-command-ACJF6KWQ.js → login-command-QM5SBBI2.js} +2 -2
- package/dist/{logout-command-MX3MJS5U.js → logout-command-THPASOHM.js} +2 -2
- package/dist/{loop-SAWAOUFO.js → loop-7SAIGRCZ.js} +3 -3
- package/dist/{marketplace-command-XJIWL4YP.js → marketplace-command-463DT7O7.js} +11 -25
- package/dist/meta-client-OW5WKWW7.js +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.js +18 -12
- package/dist/onboard-command-EHOHKQQU.js +36 -0
- package/dist/{profile-command-SWJ3SPKR.js → profile-command-EUWPVWED.js} +6 -6
- package/dist/{release-command-6PLMLGDK.js → release-command-M4CVNTWL.js} +25 -25
- package/dist/{sandbox-details-command-HJE5SPVG.js → sandbox-details-command-BRII3QHG.js} +5 -5
- package/dist/{sandbox-export-command-QCJY4GMV.js → sandbox-export-command-PCZ5FL4U.js} +8 -8
- package/dist/scaffold.js +4 -2
- package/dist/{store-g45zwB33.d.ts → store-BJONDJoZ.d.ts} +171 -2
- package/dist/{test-command-LLPXKT35.js → test-command-U4G5Y3UM.js} +10 -10
- package/dist/upgrade-command-CKJ4BS2P.js +177 -0
- package/dist/{validate-command-WAT3ZJT3.js → validate-command-S5N37JRP.js} +12 -12
- package/dist/{workspace-command-JKMI7RZ4.js → workspace-command-RALSEJSW.js} +28 -27
- package/dist/workspace-export-AJMGN3CQ.js +1 -1
- package/guides/README.md +30 -0
- package/guides/authoring.md +642 -0
- package/guides/cli.md +191 -0
- package/guides/codegen.md +83 -0
- package/guides/coverage.md +67 -0
- package/guides/deploying.md +340 -0
- package/guides/environment.md +132 -0
- package/guides/object-kinds.md +376 -0
- package/guides/project-structure.md +43 -0
- package/guides/scaffold.md +198 -0
- package/guides/typed-frontend.md +201 -0
- package/llms/kinds-agent-mcp.md +2 -1
- package/llms/kinds-knowledge.md +20 -0
- package/llms/object-kinds.md +1 -0
- package/llms-full.txt +27 -3
- package/llms.txt +3 -2
- package/manifest.json +30 -3
- package/package.json +5 -2
- package/dist/.build-fingerprint +0 -1
- package/dist/chunk-47WDWMBJ.js +0 -14
- package/dist/init-command-6DGXEVZD.js +0 -29
package/guides/cli.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# CLI
|
|
2
|
+
|
|
3
|
+
Every `xanots` command, shell completion, and what the CLI prints when something fails.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
xanots init my-app # scaffold a full project (frontend/ + xano/)
|
|
7
|
+
xanots init my-app --framework svelte # SvelteKit instead of the default React
|
|
8
|
+
xanots init my-app --ai claude --no-install # add CLAUDE.md; skip npm install
|
|
9
|
+
xanots init my-app --theme zinc-blue --dark toggle --icons tabler # the look
|
|
10
|
+
xanots init my-app --marketplace @xanots/auth,@xanots/vector # install add-ons AND register them
|
|
11
|
+
|
|
12
|
+
xanots marketplace list # every published add-on (no login needed)
|
|
13
|
+
xanots marketplace search auth # narrow by keyword
|
|
14
|
+
xanots marketplace details @xanots/auth # what it installs + the registration to copy
|
|
15
|
+
xanots marketplace details @xanots/auth --prompt # …as a prompt for a coding agent
|
|
16
|
+
xanots marketplace install @xanots/auth # add an add-on to the project you're in
|
|
17
|
+
|
|
18
|
+
xanots export ./xano/index.ts # bundle to stdout
|
|
19
|
+
xanots export ./xano/index.ts --out ws.json
|
|
20
|
+
xanots compile ./xano/functions/get-user.ts # a single function's JSON
|
|
21
|
+
|
|
22
|
+
xanots export ./xano/index.ts --strict # CI: fail the build on any warning, don't just print it
|
|
23
|
+
xanots export ./xano/index.ts --lock # opt into xano.lock (created beside the entry)
|
|
24
|
+
xanots export ./xano/index.ts --frozen-lock # CI guard: fail if the export would change the lock
|
|
25
|
+
xanots lock rename --entry=xano/index.ts table users members # move a lock entry after renaming in code
|
|
26
|
+
xanots lock prune ./xano/index.ts --yes # drop lock entries nothing exports anymore
|
|
27
|
+
xanots lock prune --no-verify --entry=xano/index.ts --yes dbo:notes # …or drop named keys without running the workspace
|
|
28
|
+
xanots lock adopt live-export.json --entry=xano/index.ts --yes # seed the lock from a live engine export
|
|
29
|
+
|
|
30
|
+
xanots login # OAuth sign-in (once) — pick the instance + workspace at consent
|
|
31
|
+
xanots workspace details # which instance/workspace am I bound to, and via which credential?
|
|
32
|
+
xanots deploy ./xano/index.ts # compile + import into a live ephemeral (the dev loop) → URL
|
|
33
|
+
xanots deploy ./xano/index.ts --dest sandbox # …or your throwaway singleton sandbox
|
|
34
|
+
xanots deploy ./xano/index.ts --static ./frontend/dist # also deploy a static frontend (onto the ephemeral)
|
|
35
|
+
xanots deploy ./xano/index.ts --static ./frontend/dist --static-env PK=pk_live_1 # + extra public config
|
|
36
|
+
xanots deploy --bundle ws.json # deploy an already-exported bundle
|
|
37
|
+
xanots deploy ./xano/index.ts --open # …and open the deployed URL in your browser
|
|
38
|
+
xanots ephemeral list # list your ephemeral environments (--all-workspaces spans every workspace)
|
|
39
|
+
xanots ephemeral get <tenant> # base URL, state, and expiry for one (<tenant> = the tenant name, e.g. ewap-8wz9-9e13, NOT the display name — `ephemeral list` shows it in bold)
|
|
40
|
+
xanots ephemeral delete <tenant> --yes # destroy one
|
|
41
|
+
xanots ephemeral impersonate <tenant> # open it in the builder (--guest = read-only; --url-only prints the URL instead)
|
|
42
|
+
xanots release ./xano/index.ts --dry-run # preview what promoting to your main workspace would change
|
|
43
|
+
xanots release ./xano/index.ts # promote: add + update, never delete, never touch table data
|
|
44
|
+
xanots release ./xano/index.ts --prune # …also delete objects this project released and no longer defines (needs xano.lock)
|
|
45
|
+
xanots sandbox export # export the DEPLOYED sandbox workspace as a JSON bundle → ./sandbox.json
|
|
46
|
+
xanots sandbox export --format multidoc --name backend # …or the deployed sandbox as XanoScript → backend.xs
|
|
47
|
+
xanots sandbox export --format multidoc --path - # …stream the multidoc to stdout (deploy first)
|
|
48
|
+
xanots sandbox details # print the sandbox base URL + tenant details (pretty on a TTY, JSON when piped)
|
|
49
|
+
|
|
50
|
+
xanots workspace details # which workspace your token is scoped to (instance, id, name, guid)
|
|
51
|
+
xanots workspace export --path ws.json # your REAL workspace as a JSON bundle (`--path -` streams to stdout)
|
|
52
|
+
xanots workspace codegen my-app # …or as a runnable project (the pull direction)
|
|
53
|
+
xanots sandbox codegen my-app # same, from your sandbox
|
|
54
|
+
xanots ephemeral codegen <tenant> my-app # same, from an ephemeral (tenant first, path second)
|
|
55
|
+
xanots codegen ws.json my-app # …or from a bundle already on disk (offline, no auth)
|
|
56
|
+
|
|
57
|
+
xanots profile me # print the scoped user + instance base URL (pretty on a TTY, JSON when piped)
|
|
58
|
+
xanots whoami # alias for `profile me`
|
|
59
|
+
xanots logout # revoke the refresh token + clear the shared cache (--local for the project one)
|
|
60
|
+
xanots version # print the installed @xanots/sdk version
|
|
61
|
+
xanots upgrade --check # is a newer @xanots/sdk published? exits 7 if so, 0 if current
|
|
62
|
+
xanots upgrade # install it (-g or -D, matching how this CLI is installed)
|
|
63
|
+
xanots help # grouped command reference (also the no-arg default)
|
|
64
|
+
xanots <command> --help # that command's usage, subcommands, and flags (`xanots deploy --help`)
|
|
65
|
+
xanots <noun> <verb> --help # scoped to one verb (`xanots workspace codegen --help`)
|
|
66
|
+
xanots <command> --json # force JSON on stdout (otherwise: whenever stdout isn't a terminal)
|
|
67
|
+
xanots completion zsh # shell completion script (also bash, fish) — see below
|
|
68
|
+
|
|
69
|
+
xanots validate ./xano/index.ts # import into a live instance, diff each object back
|
|
70
|
+
xanots validate ./xano/index.ts --runtime # also run each deployed function on the engine
|
|
71
|
+
xanots validate ./xano/index.ts --capture # write the fetched JSON as fixture candidates
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Bundle key names vs. the names everything else uses
|
|
75
|
+
|
|
76
|
+
A bundle's `payload` arrays are keyed by the engine's **storage** name, which for five kinds
|
|
77
|
+
is not the name the SDK, the CLI, or a release plan uses for the same thing. Scripting against
|
|
78
|
+
a bundle means translating:
|
|
79
|
+
|
|
80
|
+
| You author / the plan reports | `payload` array |
|
|
81
|
+
|---|---|
|
|
82
|
+
| `table` | `payload.dbo` |
|
|
83
|
+
| `api_group` | `payload.app` |
|
|
84
|
+
| `agent` | `payload.toolset` |
|
|
85
|
+
| `mcp_server` | `payload.toolset` |
|
|
86
|
+
| `toolset` | `payload.tool` |
|
|
87
|
+
| `realtime_channel` | `payload.channel` |
|
|
88
|
+
| `realtime_message` | `payload.message` |
|
|
89
|
+
|
|
90
|
+
Everything else (`query`, `function`, `task`, `trigger`, `middleware`, `microservice`,
|
|
91
|
+
`addon`, `workflow_test`, `workspace`) is keyed the same on both sides.
|
|
92
|
+
|
|
93
|
+
Two traps worth naming. A release plan reports operations as `{"type": "table", …}` while the
|
|
94
|
+
bundle it came from stores that object under `payload.dbo` — so a table that released
|
|
95
|
+
correctly looks *missing* if you go looking for `payload.table`. And `agent` and `mcp_server`
|
|
96
|
+
both land in `payload.toolset`, while the kind actually named `toolset` lands in
|
|
97
|
+
`payload.tool`; matching on the word alone will pick the wrong array.
|
|
98
|
+
|
|
99
|
+
This is also the format `xanots lock adopt` reads, so both directions are user-facing.
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
**Build warnings, and `--strict`.** `export`/`deploy` print a `xanots:` warning for the
|
|
103
|
+
shapes that ship clean and then do the wrong thing — a `bulk.update` zero-filling the columns
|
|
104
|
+
an item omits, an `ignoreEmpty` on an operand that's already empty, a `ref()` no `as` binds,
|
|
105
|
+
a filter name the engine can't resolve, a `s.switch` case that falls through into the next
|
|
106
|
+
one, a request-time timestamp filter on a `where` operand. Each stays a warning because each
|
|
107
|
+
has a legitimate use. Nothing fails on a message nobody reads, though, so pass `--strict` in CI and in
|
|
108
|
+
unattended agent builds: every warning becomes a hard failure and the exit code carries it.
|
|
109
|
+
The programmatic equivalents are `emitBundle(app, { strict: true })` and
|
|
110
|
+
`app.export({ strict: true })`. The bundle bytes are identical either way.
|
|
111
|
+
|
|
112
|
+
## Shell completion
|
|
113
|
+
|
|
114
|
+
`xanots completion <bash|zsh|fish>` prints a completion script covering every command, verb, flag,
|
|
115
|
+
and closed value set (`--dest ephemeral|sandbox`, `--format json|multidoc`, `--ai claude|codex|cursor|none`).
|
|
116
|
+
It is generated from the CLI's own command table, so it never drifts from what the CLI accepts — but it
|
|
117
|
+
is baked at generation time, so re-run it after upgrading.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# zsh
|
|
121
|
+
xanots completion zsh > "${fpath[1]}/_xanots" # then restart your shell
|
|
122
|
+
|
|
123
|
+
# bash
|
|
124
|
+
xanots completion bash > ~/.xanots-completion.bash
|
|
125
|
+
echo 'source ~/.xanots-completion.bash' >> ~/.bashrc
|
|
126
|
+
|
|
127
|
+
# fish
|
|
128
|
+
xanots completion fish > ~/.config/fish/completions/xanots.fish
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
After a command succeeds the CLI checks npm (at most once an hour, cached in
|
|
132
|
+
`~/.xanots/update-check.json`) and prints a one-line nudge to **stderr** when a newer
|
|
133
|
+
`@xanots/sdk` is published — never to stdout, so piped bundles stay clean. The suggested
|
|
134
|
+
command adapts to how you installed it (`npm i -g …` for a global install, `npm i -D …`
|
|
135
|
+
when it's a project dependency). The check is best-effort and bounded (a slow or offline
|
|
136
|
+
registry never delays a command), and stays silent under CI or when stderr isn't a terminal.
|
|
137
|
+
Opt out with `XANOTS_NO_UPDATE_CHECK=1` (or the conventional `NO_UPDATE_NOTIFIER=1`).
|
|
138
|
+
|
|
139
|
+
`xanots upgrade` is the same question asked on purpose, and it answers under all the
|
|
140
|
+
conditions the nudge stays quiet for — CI, a piped stderr, the opt-out variables — reading the
|
|
141
|
+
registry live rather than serving the hourly cache. `--check` reports without installing and
|
|
142
|
+
exits **7** when a newer version is published, **0** when you are current, so a pipeline can
|
|
143
|
+
branch on it:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
xanots upgrade --check || echo "an upgrade is waiting"
|
|
147
|
+
xanots upgrade --check | jq -r .latest # piped stdout is JSON already
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
A registry it cannot reach is an error (exit 1), never a quiet "you are up to date". Without
|
|
151
|
+
`--check` it installs, matching how this CLI is installed — and for a project-local install it
|
|
152
|
+
then restores the `@xanots/sdk` range your project was scaffolded with (npm rewrites it to a
|
|
153
|
+
caret, which on the `0.0.x` line pins you exactly) and restamps the managed block in your
|
|
154
|
+
agent files so the guidance matches the version you now have. Set `XANOTS_INSTALL_MODE` to
|
|
155
|
+
`global` or `local` to override the detection.
|
|
156
|
+
|
|
157
|
+
## When something fails
|
|
158
|
+
|
|
159
|
+
Failures are written for the person reading the terminal. A request the instance refused
|
|
160
|
+
prints the server's own sentence on one line (`deploy failed (403 Forbidden): Access denied
|
|
161
|
+
for this workspace.`) rather than the whole JSON envelope; a request that never arrived names
|
|
162
|
+
what it could not reach and why (`workspace list could not reach https://…: fetch failed
|
|
163
|
+
(ECONNRESET)`), and a request that timed out says so separately, because "slow" and
|
|
164
|
+
"unreachable" call for different next steps. Set `XANOTS_DEBUG=1` to append the untouched
|
|
165
|
+
response body underneath — nothing is discarded, only folded away.
|
|
166
|
+
|
|
167
|
+
A malformed invocation fails before any work happens: a missing or misspelled entry file, an
|
|
168
|
+
argument the command has nowhere to put (`xanots export --lock xano.lock` — that flag takes
|
|
169
|
+
its value attached, as `--lock=xano.lock`), or a missing credential, each answered with the
|
|
170
|
+
command's usage block instead of a stack trace. `deploy` and `release` check that you are
|
|
171
|
+
signed in *before* compiling, so a lapsed session costs you a message, not a build.
|
|
172
|
+
|
|
173
|
+
Emitters that write to disk (and the programmatic CLI) are Node-only — import them from
|
|
174
|
+
`@xanots/sdk/node`. The string emitters (`emit`, `emitBundle`) stay on the browser-safe
|
|
175
|
+
`@xanots/sdk` entry.
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
import { emitBundle } from "@xanots/sdk"; // pure string — browser-safe
|
|
179
|
+
import { writeBundle } from "@xanots/sdk/node"; // writes a file — Node only
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Four entry points, and only the first is the authoring API.** `@xanots/sdk` is what you
|
|
183
|
+
define a workspace with; `@xanots/sdk/node` adds the filesystem half; `@xanots/sdk/codegen`
|
|
184
|
+
is what a generated tree imports; and `@xanots/sdk/internal` holds the compiler machinery —
|
|
185
|
+
the per-kind encoders, the kind and statement registries, the bundle serializer, the
|
|
186
|
+
`xano.lock` model. Nothing on `/internal` is needed to author anything, and it is kept off
|
|
187
|
+
the root so an agent scanning the package's exports sees the surface rather than the guts.
|
|
188
|
+
|
|
189
|
+
All three run the same build-time checks, including seed validation of a literal
|
|
190
|
+
`seed: [...]` array. A **deferred** seed (a thunk, or `seedFile()`) needs an await or the
|
|
191
|
+
filesystem, so it is materialised and checked only by `xanots export` / `xanots deploy`.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Pulling an existing workspace
|
|
2
|
+
|
|
3
|
+
What `xanots codegen` writes, how faithful the pull is, and how to read its report.
|
|
4
|
+
|
|
5
|
+
## The generated tree
|
|
6
|
+
|
|
7
|
+
Inside, `xano/` is shaped the way the workspace is: one directory per kind, with each
|
|
8
|
+
object under its parent — queries under the API group that owns them, triggers under
|
|
9
|
+
what they fire on. Each table gets its own `table/<name>.ts`, settings sit in `xano/workspace.ts`,
|
|
10
|
+
`_shared.ts` holds anything else referenced from more than one file, and `xano/README.md`
|
|
11
|
+
lists anything that did not translate cleanly.
|
|
12
|
+
Object identities (`guid`) are preserved, so cross-references stay intact. A statement
|
|
13
|
+
this SDK does not model yet round-trips verbatim rather than breaking the pull.
|
|
14
|
+
|
|
15
|
+
Pulled objects are authored the same way you would write them by hand — `table({...})`,
|
|
16
|
+
`query({...})`, `defineFunction({...})` — so the generated tree keeps its types. A pulled
|
|
17
|
+
table's columns still check on `fieldName`/`output`/`sortBy`, `InferInput<typeof q>` still
|
|
18
|
+
resolves a pulled query's payload, and a pulled agent still types `s.ai.agent.run`.
|
|
19
|
+
|
|
20
|
+
A pull states what the source workspace actually holds and leaves out what the SDK would
|
|
21
|
+
put back anyway. A table's `primary(id)` / `created_at` / `gin(xdo)` indexes are the
|
|
22
|
+
engine's standard set, so only the indexes someone created are listed. A trigger comes back
|
|
23
|
+
through the factory that built it (`tableTrigger`, `realtimeTrigger`, …) rather than a bare
|
|
24
|
+
`satisfies TriggerDef`, which keeps its typed stack handle; the two realtime types that bind
|
|
25
|
+
a def handle are the exception, since a stored trigger carries two guids with no way to know
|
|
26
|
+
they agree. And two objects that reference each other — a pair of tables joined both ways,
|
|
27
|
+
two functions that call each other — can't both be declared first, so the second reference
|
|
28
|
+
is a `{name, guid}` const hoisted to the top of the file (`const OrdersRef = {…}`) instead of
|
|
29
|
+
an import that would close a cycle. Only the guid is ever read, so it binds exactly.
|
|
30
|
+
|
|
31
|
+
`xano/README.md` also lists objects that were **already empty in the source** — an
|
|
32
|
+
endpoint someone created and never filled in pulls as a def with no `stack`, which looks
|
|
33
|
+
identical to a decode that gave up. The report is what tells the two apart.
|
|
34
|
+
|
|
35
|
+
A few options exist only so a pull can be *faithful*, and reading them in generated code
|
|
36
|
+
is the only time you should see them: `table: null` / `fn: null` (a statement whose target
|
|
37
|
+
was deleted or never bound), `merge` / `hidden` on a field, `paging: { enabled }` on a
|
|
38
|
+
query, `c.blank(tag)` (the editor's unconfigured value box — **not** a zero or an
|
|
39
|
+
empty collection; the engine reads `""` and `"0"` differently, so tidying one into the
|
|
40
|
+
other changes what the workspace stores), and `c.null("const:obj")` (the object-typed null
|
|
41
|
+
a `db.*` statement's `@meta` slot carries — different stored bytes from `c.obj(null)`,
|
|
42
|
+
which is the blank object, though both evaluate to null). They describe what the source workspace actually
|
|
43
|
+
stored — a pulled `table: null` is a defect to fix upstream, not a shape to copy — and
|
|
44
|
+
each carries that warning at the call site. A blank binding also reports, because a
|
|
45
|
+
statement wired to a table or function that no longer exists is worth seeing even though
|
|
46
|
+
it round-trips exactly.
|
|
47
|
+
|
|
48
|
+
## Verification, and the decode report
|
|
49
|
+
|
|
50
|
+
Then it checks its own work: the project it just wrote is loaded, exported, and diffed
|
|
51
|
+
against the workspace it came from. A mismatch names the object and fails the command
|
|
52
|
+
(`--no-verify` opts out). So "it compiled" and "it means the same thing" are separate
|
|
53
|
+
claims, and you get both.
|
|
54
|
+
|
|
55
|
+
The findings above are printed either way. Verification runs after decoding is finished,
|
|
56
|
+
so whether it passes, disagrees, or cannot run at all, the report describing the decode is
|
|
57
|
+
rendered first — and a tree that was written but does not re-export exits **2**, distinct
|
|
58
|
+
from the **1** you get when nothing was written because the bundle could not be read.
|
|
59
|
+
|
|
60
|
+
**Reading the report.** It opens with a headline (`27 distinct issues across 424 findings;
|
|
61
|
+
3 need your attention`) and splits into three sections, most actionable first: *problems in
|
|
62
|
+
your workspace*, *things XanoTS could not model*, and the things stated only so the output
|
|
63
|
+
is not ambiguous. That split is the question a reader actually has — a `raw()` passthrough
|
|
64
|
+
is ours to close, a lambda reading an unbound name is theirs to fix, and an empty object is
|
|
65
|
+
neither. Findings that repeat the same sentence across objects collapse to one line with a
|
|
66
|
+
count and a collapsed object list, and each names the generated file it landed in.
|
|
67
|
+
`--report full` prints every site instead; `--report json` prints the findings as data, and
|
|
68
|
+
the same data is written into `xano/.xanots-codegen.json` on every pull, so parity is
|
|
69
|
+
trackable release over release and gateable in CI without scraping output.
|
|
70
|
+
|
|
71
|
+
Re-pulling is a real workflow: a second `codegen` into the same directory refreshes
|
|
72
|
+
`xano/` and leaves the rest of the project — your `package.json`, your `frontend/` —
|
|
73
|
+
exactly as you left it. No `--force` needed, because the tree carries a marker saying it
|
|
74
|
+
was machine-written.
|
|
75
|
+
|
|
76
|
+
> ⚠️ **`xano/` is a scratch surface, and there is no `xanots workspace deploy`.**
|
|
77
|
+
> Regenerating rewrites it (a directory that isn't a previous pull still needs `--force`),
|
|
78
|
+
> it carries schema only — no table rows — and deploying it is a *full replace* of the
|
|
79
|
+
> target. Pull from your real workspace, edit, and `deploy` to a disposable ephemeral or
|
|
80
|
+
> sandbox. Workspace env var **values** ride inline in `xano/workspace.ts` (that is what a
|
|
81
|
+
> deploy sends), so treat a pulled tree as secret-bearing before you commit it.
|
|
82
|
+
|
|
83
|
+
---
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Coverage & agent grounding
|
|
2
|
+
|
|
3
|
+
What XanoTS covers against the engine catalog, what is deliberately out of scope, and the machine-readable grounding it ships for agents.
|
|
4
|
+
|
|
5
|
+
## Coverage & scope
|
|
6
|
+
|
|
7
|
+
XanoTS emits only (the engine imports/executes). Fidelity is proven by deep-equal against
|
|
8
|
+
the real Xano engine golden fixtures, and a coverage report prints on every test run.
|
|
9
|
+
|
|
10
|
+
| Surface | Coverage |
|
|
11
|
+
|---|---|
|
|
12
|
+
| Object kinds | **25 / 31** — counted over the engine's catalog, where each trigger type is its own kind |
|
|
13
|
+
| Statements (via `s`) | **214 / 214 (100%)** — every engine statement surface has a factory |
|
|
14
|
+
| Filters (via `fl`) | **226 / 226 (100%)** — every filter's signature is known: 131 carry arguments, 95 take none |
|
|
15
|
+
|
|
16
|
+
The six engine kinds you cannot author here are `tablemap`, `run.job`, `run.service`, the
|
|
17
|
+
superseded `realtime_channel`, and — correctly, since they are instance state rather than
|
|
18
|
+
workspace source — `branch` and `market_item`. `llms.txt` names them with
|
|
19
|
+
their reasons. `llms.txt` and `manifest.json` regenerate their counts from the SDK's own
|
|
20
|
+
catalogs; this table is hand-written, so a test asserts every figure in it against
|
|
21
|
+
`buildManifest()` (`@xanots/sdk/internal`) — the numbers here and the numbers an agent
|
|
22
|
+
reads cannot disagree.
|
|
23
|
+
|
|
24
|
+
The statement catalog is generated from the engine's own schemas (`npm run codegen`): 149 of
|
|
25
|
+
the 214 surfaces are declarative and carry a typed field schema, and the remaining 65 —
|
|
26
|
+
control flow, the `db` family, the call family — are hand-authored. **Reachable ≠ byte-verified**: every surface is
|
|
27
|
+
authorable, but a structural special without a persisted golden yet emits a shape *modeled*
|
|
28
|
+
on the engine schema, to be deep-equal'd against fixtures as they are captured.
|
|
29
|
+
|
|
30
|
+
**Out of scope** — reimplementing the engine's XanoScript parser, executing objects at
|
|
31
|
+
runtime (XanoTS only compiles), and generating engine-side numeric ids/timestamps.
|
|
32
|
+
(Object guids and canonicals *are* handled — deterministically derived or frozen via
|
|
33
|
+
`xano.lock`.)
|
|
34
|
+
|
|
35
|
+
**Deferred (by design)** — folder auto-discovery, and the `service` / `vault` / `branch`
|
|
36
|
+
payload sections. (Round-trip decompile is no longer deferred: that is
|
|
37
|
+
`xanots codegen`, in the [CLI guide](cli.md); nor is `workflow_test` — it is a
|
|
38
|
+
first-class kind, see `workflowTest` in [Object kinds](object-kinds.md).) `InferResponse`
|
|
39
|
+
auto-derivation covers the object-literal and single-`db`-variable cases (matching the engine's
|
|
40
|
+
static walk), and follows a CALL into the object it invokes (`s.function.call`/`.run`,
|
|
41
|
+
`s.tool.call`, `s.api.call` given a def handle bind the target's own `InferResponse`);
|
|
42
|
+
a response variable produced inside control flow or `set_var`, and addon/related-field
|
|
43
|
+
keys, resolve to `unknown` — declare `responseShape` for those.
|
|
44
|
+
|
|
45
|
+
## Agent grounding
|
|
46
|
+
|
|
47
|
+
XanoTS ships two machine-readable descriptions of its whole authoring surface so an agent
|
|
48
|
+
can learn the SDK without reading source:
|
|
49
|
+
|
|
50
|
+
- **`llms.txt`** — the always-read router: the mental model, the deploy contract, control
|
|
51
|
+
flow, the non-obvious rules in `## Gotchas`, and a `## Topic files` list naming each
|
|
52
|
+
**`llms/*.md`** and when to open it. It does not document the CLI — `xanots <command>
|
|
53
|
+
--help` and the `cli` array in `manifest.json` do, from the same registry that generates
|
|
54
|
+
the shell completions. The written version is the [CLI guide](cli.md).
|
|
55
|
+
- **`llms/*.md`** — one file per surface (object def shapes, statements, values, fields,
|
|
56
|
+
filters, lambda bodies, and the legacy names a pulled workspace carries), read only when
|
|
57
|
+
that surface is in play. **`llms-full.txt`** is all
|
|
58
|
+
of them concatenated, for a reader that wants one file rather than two.
|
|
59
|
+
- **`manifest.json`** — the exhaustive reference tier, reached by targeted lookup (grep or
|
|
60
|
+
`jq` one entry; never read it whole). Every object kind (factory, `Xano.register*` method,
|
|
61
|
+
payload key), every statement surface (the `s.<path>` accessor, stored `mvp:` name, and a
|
|
62
|
+
typed field schema for the 149 declarative statements), the value constructors, the tag
|
|
63
|
+
catalog, the filter catalog, and every CLI command and flag — plus live coverage counts.
|
|
64
|
+
|
|
65
|
+
Both derive from the SDK's own sources of truth (so they can't drift), regenerate with
|
|
66
|
+
`npm run manifest`, and are available at runtime via `buildManifest()` / `renderLlmsTxt()`,
|
|
67
|
+
both exported from `@xanots/sdk/internal`.
|