@xanots/sdk 0.0.11 → 0.0.12

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 (97) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +11 -6
  3. package/dist/.build-fingerprint +1 -1
  4. package/dist/{agent-file-refresh-QNKN5RYD.js → agent-file-refresh-GQWAAOBV.js} +4 -4
  5. package/dist/bin.js +11 -11
  6. package/dist/{branch-commands-2BLOC2GR.js → branch-commands-5KMPAAZV.js} +7 -7
  7. package/dist/bundle.d.ts +2 -2
  8. package/dist/bundle.js +2 -2
  9. package/dist/{capture-4WVJY4DQ.js → capture-YLUVAITI.js} +2 -2
  10. package/dist/chunk-2VTJSI6X.js +192 -0
  11. package/dist/{chunk-5XZ744TS.js → chunk-2ZCUO2UG.js} +1 -1
  12. package/dist/{chunk-XEOX6AM7.js → chunk-3LQGF2WS.js} +2 -2
  13. package/dist/{chunk-WP4OZZV4.js → chunk-3VFCKHOB.js} +2 -2
  14. package/dist/{chunk-XHEXOES3.js → chunk-5YDINUAP.js} +1 -1
  15. package/dist/{chunk-LU7TRWMC.js → chunk-6VNRMKFJ.js} +2 -2
  16. package/dist/{chunk-CTD5ZCV6.js → chunk-7WRJPKGK.js} +2 -2
  17. package/dist/{chunk-3INK4Y4E.js → chunk-AE3PDSDS.js} +1 -1
  18. package/dist/{chunk-22TKBSDV.js → chunk-AG5GCZDD.js} +2 -2
  19. package/dist/{chunk-DBFU47BJ.js → chunk-AOFFKSJC.js} +2 -2
  20. package/dist/{chunk-TCFIPDB3.js → chunk-AVGDL6RB.js} +1 -1
  21. package/dist/{chunk-BC2C5GVI.js → chunk-DCMANKMX.js} +1 -1
  22. package/dist/{chunk-WHOJWOSV.js → chunk-EETVJZAZ.js} +1 -1
  23. package/dist/{chunk-4Q7ZOHH7.js → chunk-ELK7UALJ.js} +3 -3
  24. package/dist/{chunk-VNQM3V2C.js → chunk-EXENFOWE.js} +2 -2
  25. package/dist/{chunk-UOZMSF4C.js → chunk-GSP2BY4F.js} +5 -5
  26. package/dist/{chunk-AIZKXUNP.js → chunk-IMLYGQK6.js} +2 -2
  27. package/dist/{chunk-QK7ZQJLP.js → chunk-N74KDCBD.js} +39 -25
  28. package/dist/{chunk-3IGNIP6R.js → chunk-NDP7OUPS.js} +1 -1
  29. package/dist/{chunk-EQW3YT5U.js → chunk-P6PVBQL6.js} +2 -2
  30. package/dist/{chunk-HYBN4H3F.js → chunk-PLE5QQOZ.js} +27 -20
  31. package/dist/{chunk-BSK7ELHU.js → chunk-PR7OXHGZ.js} +1 -1
  32. package/dist/{chunk-OWGCOGKK.js → chunk-QYSQ3UDO.js} +8 -8
  33. package/dist/{chunk-ZQ2PKR6R.js → chunk-RQ3FXV4K.js} +2 -2
  34. package/dist/{chunk-Q77KNEUL.js → chunk-RQNMTDXD.js} +1703 -2
  35. package/dist/{chunk-QKM4U5UK.js → chunk-TJS2AF5Y.js} +2 -2
  36. package/dist/{chunk-DIA7CT7J.js → chunk-V5Y7D4LH.js} +11 -2
  37. package/dist/{chunk-KA6G2L7U.js → chunk-VK26K7AY.js} +3 -3
  38. package/dist/{chunk-4IF54NU5.js → chunk-VPAWRBK5.js} +12 -16
  39. package/dist/{chunk-QYMAZRAU.js → chunk-VRNZ2NVV.js} +5 -5
  40. package/dist/{chunk-RCT7UX7B.js → chunk-XT3XQ4PF.js} +32 -27
  41. package/dist/{chunk-VAF6A3YD.js → chunk-XWFRNJMQ.js} +1 -1
  42. package/dist/{chunk-7ZYW652H.js → chunk-YHS6VVLJ.js} +2 -2
  43. package/dist/{chunk-G4EJMQLD.js → chunk-YX22LKQE.js} +2 -2
  44. package/dist/{chunk-F6CYJ7TN.js → chunk-Z2ZIE5CO.js} +2 -2
  45. package/dist/cli.d.ts +13 -6
  46. package/dist/cli.js +10 -10
  47. package/dist/codegen-command-Y2SPMAUW.js +47 -0
  48. package/dist/codegen.d.ts +5 -5
  49. package/dist/codegen.js +2 -2
  50. package/dist/{completion-WF46272M.js → completion-HJU5QEFB.js} +2 -2
  51. package/dist/{deploy-command-IP7V7GT4.js → deploy-command-XHS5PKPV.js} +19 -19
  52. package/dist/{ephemeral-command-U4AQ3TXX.js → ephemeral-command-2NKPEXUU.js} +8 -8
  53. package/dist/index.d.ts +88 -104
  54. package/dist/index.js +9 -9
  55. package/dist/init-command-23FFNUFT.js +32 -0
  56. package/dist/internal.d.ts +10 -16
  57. package/dist/internal.js +26 -26
  58. package/dist/{io-P2H75UV2.js → io-UBDMMDH6.js} +3 -3
  59. package/dist/{live-diff-IXKBVG4K.js → live-diff-HCOTN5WC.js} +3 -3
  60. package/dist/{lock-46FWYE4D.js → lock-HQ4KARU2.js} +2 -2
  61. package/dist/{lock-commands-ZZKZ4LZJ.js → lock-commands-WCRC56ME.js} +11 -11
  62. package/dist/{login-command-Z6CHTA57.js → login-command-P7LXD5TE.js} +6 -6
  63. package/dist/{loop-D5NPL4VH.js → loop-IC5ISSYB.js} +3 -3
  64. package/dist/{marketplace-command-P4IPLJ6J.js → marketplace-command-T6JCHW7J.js} +4 -4
  65. package/dist/{meta-client-K2J4XH64.js → meta-client-LKRKR3L2.js} +4 -4
  66. package/dist/node.d.ts +6 -6
  67. package/dist/node.js +14 -14
  68. package/dist/{preflight-command-K346GPTY.js → preflight-command-TZWPHBXY.js} +16 -16
  69. package/dist/{profile-command-LJSBDV2L.js → profile-command-WBNSMQSI.js} +3 -3
  70. package/dist/{release-command-IMNTIVWK.js → release-command-WEFYRTCI.js} +26 -26
  71. package/dist/{response-BQVQ24l1.d.ts → response-D6xGLEIn.d.ts} +18 -23
  72. package/dist/{routes-manifest-PWZHDOI5.js → routes-manifest-5ZFKUQWA.js} +2 -2
  73. package/dist/{runtime-V4C3AC3A.js → runtime-LSLIDALK.js} +1 -1
  74. package/dist/scaffold.d.ts +16 -8
  75. package/dist/scaffold.js +7 -11
  76. package/dist/{static-host-3WMV7IZO.js → static-host-4JDQCHUW.js} +1 -1
  77. package/dist/{status-command-AL47VG7H.js → status-command-VARULAR5.js} +4 -4
  78. package/dist/{store-BLyNeQ8S.d.ts → store-DAnUIi1T.d.ts} +81 -86
  79. package/dist/{test-command-YAZLKLGQ.js → test-command-YCAR4O35.js} +7 -7
  80. package/dist/{upgrade-command-BN3EHAOI.js → upgrade-command-GYEIMJBG.js} +15 -16
  81. package/dist/{workspace-2COHDBM3.js → workspace-33KHFLX3.js} +2 -2
  82. package/dist/{workspace-command-MNK7Y7MQ.js → workspace-command-2ZTGT26W.js} +26 -28
  83. package/dist/{workspace-export-DURY5WYL.js → workspace-export-MLYTYZS5.js} +3 -3
  84. package/dist/{xdo-BjJj5W_E.d.ts → xdo-ODuJklk6.d.ts} +27 -23
  85. package/guides/authoring.md +11 -2
  86. package/guides/typed-frontend.md +4 -4
  87. package/llms/statements-data.md +3 -2
  88. package/llms/values.md +1 -1
  89. package/llms-full.txt +18 -18
  90. package/llms.txt +14 -15
  91. package/manifest.json +6 -2
  92. package/package.json +1 -1
  93. package/dist/chunk-ANUDXFEX.js +0 -881
  94. package/dist/chunk-JGCWTCA7.js +0 -95
  95. package/dist/chunk-YBC3IKMF.js +0 -845
  96. package/dist/codegen-command-FUT2KJB6.js +0 -49
  97. package/dist/init-command-NPVL32L6.js +0 -34
@@ -1,881 +0,0 @@
1
- import {
2
- colorTokens,
3
- defaultThemeChoice,
4
- fontDependencies,
5
- fontImports,
6
- fontThemeVars,
7
- orderedTokens
8
- } from "./chunk-Q77KNEUL.js";
9
-
10
- // src/emit/init-templates.ts
11
- function sdkDep(sdkVersion) {
12
- const parsed = /^(\d+)\.(\d+)\.\d+/.exec(sdkVersion);
13
- if (!parsed) return ">=0.0.1 <0.1.0";
14
- const [major, minor] = [Number(parsed[1]), Number(parsed[2])];
15
- const ceiling = major > 0 ? `${major + 1}.0.0` : `0.${minor + 1}.0`;
16
- return `>=${sdkVersion} <${ceiling}`;
17
- }
18
- function renderPackageJson({ appName, sdkVersion }, preset, choice = defaultThemeChoice()) {
19
- const check = preset.checkCmd;
20
- const pkg = {
21
- name: appName,
22
- version: "0.1.0",
23
- private: true,
24
- type: "module",
25
- scripts: {
26
- dev: "vite",
27
- build: `${check} && vite build`,
28
- preview: "vite preview",
29
- typecheck: check,
30
- // Both are prefixed with a typecheck: the export pass validates what it
31
- // can see in the encoded bundle, and TypeScript validates the rest. Only
32
- // `build` used to run `tsc`, so anyone following the documented workflow
33
- // shipped with the compiler's half of the checks never run.
34
- // `--lock` on both: object identity derives from `(type, name)`, so
35
- // without a lock a rename re-derives the guid and the engine does
36
- // delete-and-recreate instead of rename-in-place. The lock has to be
37
- // adopted BEFORE the identities matter — by the time a rename lands
38
- // against data anyone cares about, the guids have already drifted and
39
- // recovery is `xanots lock import`. The scaffold is the earliest possible
40
- // moment, so it opts in from the first export (issue #26). Once the file
41
- // exists every export uses it, flag or not.
42
- "xano:export": `${check} && xanots export ./xano/index.ts --out workspace.json --lock`,
43
- // Runs `build` rather than `check` directly: `--static ./frontend/dist`
44
- // needs that directory to EXIST, and only `vite build` writes it. Pointing
45
- // the flag at a directory the script never produced meant a fresh clone
46
- // running the one documented command had no `dist` to ship (issue #241).
47
- // `build` already prefixes the same typecheck, so this is one check, not
48
- // two.
49
- "xano:deploy": `npm run build && xanots deploy ./xano/index.ts --static ./frontend/dist --lock`,
50
- // The CI guard: fails instead of changing xano.lock, so an uncommitted
51
- // identity change is caught in review rather than on a deploy.
52
- "xano:check": `${check} && xanots export ./xano/index.ts --out workspace.json --frozen-lock`,
53
- // No compile step and no entry file: this runs what is DEPLOYED, so it
54
- // pairs with `xano:deploy` rather than repeating its work. Exits 5 on a
55
- // failing suite, which is what makes it usable as a CI gate.
56
- "xano:test": "xanots test run-all",
57
- // Framework-owned scripts, last so a preset can add to the set but the
58
- // shared xano:* contract above stays the same in every scaffold.
59
- ...preset.extraScripts
60
- },
61
- dependencies: {
62
- // `@xanots/sdk` is the only XanoTS dependency a scaffold ships with.
63
- // Add-ons (`@xanots/auth`, and the packages that follow it) are installed
64
- // on demand — a project that never registers auth should not carry it,
65
- // and an add-on's release cadence is its own, not the CLI's.
66
- "@xanots/sdk": sdkDep(sdkVersion),
67
- ...preset.dependencies,
68
- // The icon set and the typefaces are choices, not framework constants, so
69
- // they are merged here rather than sitting in `preset.dependencies`. Both
70
- // land after the preset's own entries: a preset that still names an icon
71
- // package would otherwise win over the set the user actually picked.
72
- ...preset.iconBinding(choice.icons).dependency,
73
- ...fontDependencies(choice.fonts)
74
- },
75
- devDependencies: { ...preset.devDependencies },
76
- // Not ">=20": Vite 8 and vite-plugin-svelte 7 both declare
77
- // `^20.19 || >=22.12`, so 20.0–20.18 installs the toolchain and then fails
78
- // to run it. Stating the real floor turns that into an install-time
79
- // warning instead of a confusing crash.
80
- engines: { node: ">=20.19" }
81
- };
82
- return JSON.stringify(pkg, null, 2) + "\n";
83
- }
84
- function renderTsconfig(preset) {
85
- const aliasPaths = Object.keys(preset.tsconfigPaths).length > 0 ? { paths: { ...preset.tsconfigPaths } } : {};
86
- const tsconfig = {
87
- // Present only when the framework generates a config to build on — see
88
- // FrontendPreset.tsconfigExtends. The `include` below is re-declared on
89
- // top of it deliberately: an extended config's `include` is REPLACED, not
90
- // merged, and dropping `xano` here would leave the backend unchecked while
91
- // every command still exits 0.
92
- ...preset.tsconfigExtends === void 0 ? {} : { extends: preset.tsconfigExtends },
93
- compilerOptions: {
94
- target: "ES2022",
95
- lib: ["ES2022", "DOM", "DOM.Iterable"],
96
- module: "ESNext",
97
- moduleResolution: "bundler",
98
- ...preset.tsconfigOptions,
99
- strict: true,
100
- noEmit: true,
101
- esModuleInterop: true,
102
- skipLibCheck: true,
103
- resolveJsonModule: true,
104
- isolatedModules: true,
105
- types: ["node", "vite/client"],
106
- ...aliasPaths
107
- },
108
- // Both halves of the project typecheck together: the XanoTS backend and
109
- // the frontend that derives its types from the backend's defs. A preset
110
- // that extends a generated config appends whatever that config listed —
111
- // `include` is replaced, not merged, so anything it declared is otherwise
112
- // silently dropped. See FrontendPreset.tsconfigInclude.
113
- include: ["xano", "frontend/src", ...preset.tsconfigInclude ?? []]
114
- };
115
- return JSON.stringify(tsconfig, null, 2) + "\n";
116
- }
117
- function renderViteConfig(preset) {
118
- const root = preset.viteRoot === void 0 ? "frontend" : preset.viteRoot;
119
- const build = preset.viteBuild === void 0 ? { outDir: "dist", emptyOutDir: true } : preset.viteBuild;
120
- const header = root === null ? `// Vite's root is the project root. The framework resolves its own file
121
- // locations from the plugin config below \u2014 this file is the ONLY place that
122
- // config lives \u2014 and routes the build through its adapter, which writes
123
- // frontend/dist, the directory \`npm run xano:deploy\` ships as the frontend.` : `// Vite's root is the ${root}/ folder, so index.html and the app live there
124
- // while the XanoTS backend sits in xano/ as a peer. The build lands in
125
- // ${root}/dist, which \`npm run xano:deploy\` ships as the static frontend.`;
126
- const rootBlock = root === null ? "" : ` root: ${JSON.stringify(root)},
127
- // Vite resolves \`.env\` files against \`root\`, which is ${root}/ here \u2014 but
128
- // \`.env.example\` sits at the project root, so that is where anyone will
129
- // actually put their \`.env.local\`. Point envDir back at this file's own
130
- // directory so VITE_XANO_HOST is picked up in dev.
131
- envDir: fileURLToPath(new URL(".", import.meta.url)),
132
- `;
133
- const buildBlock = build === null ? "" : ` build: ${renderInlineObject(build)},
134
- `;
135
- const aliasBlock = preset.aliasName === null ? "" : ` resolve: {
136
- alias: {
137
- // The alias the UI kit writes its imports against. Resolved from this
138
- // file rather than from Vite's root so it points at the right directory
139
- // either way. Keep in sync with the \`paths\` entry in tsconfig.json.
140
- "${preset.aliasName}": fileURLToPath(new URL("./${preset.aliasTarget}", import.meta.url)),
141
- },
142
- },
143
- `;
144
- const fileUrlImport = rootBlock === "" && aliasBlock === "" ? "" : `import { fileURLToPath } from "node:url";
145
- `;
146
- return `${fileUrlImport}import { defineConfig } from "vite";
147
- ${preset.viteImports.join("\n")}
148
-
149
- ${header}
150
- export default defineConfig({
151
- ${rootBlock}${buildBlock} plugins: [${preset.vitePlugins.join(", ")}],
152
- ${aliasBlock} server: { host: "127.0.0.1", port: 5173 },
153
- });
154
- `;
155
- }
156
- function renderInlineObject(obj) {
157
- const entries = Object.entries(obj).map(([k, v]) => `${k}: ${JSON.stringify(v)}`);
158
- return entries.length === 0 ? "{}" : `{ ${entries.join(", ")} }`;
159
- }
160
- function renderIndexHtml({ appName }, preset, choice = defaultThemeChoice()) {
161
- if (preset.entryScript === void 0) {
162
- throw new Error(`The ${preset.id} preset owns its own HTML entry; renderIndexHtml does not apply.`);
163
- }
164
- return `<!doctype html>
165
- <html lang="en">
166
- <head>
167
- <meta charset="UTF-8" />
168
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
169
- <title>${appName}</title>
170
- ${renderThemeScript(choice.dark)} </head>
171
- <body>
172
- <div id="root"></div>
173
- <script type="module" src="${preset.entryScript}"></script>
174
- </body>
175
- </html>
176
- `;
177
- }
178
- function renderGitignore(preset) {
179
- const framework = (preset?.gitignoreEntries ?? []).map((e) => `${e}
180
- `).join("");
181
- return `node_modules/
182
- dist/
183
- workspace.json
184
- .xano/
185
- .env
186
- .env.local
187
- *.local
188
- ${framework}
189
- # Not ignored, on purpose: xano/xano.lock pins object identity across renames
190
- # and environments. Commit it.
191
- `;
192
- }
193
- function renderEnvExample() {
194
- return `# Point the frontend at a deployed Xano backend. Leave unset to run the UI
195
- # with no backend. When you \`npm run xano:deploy\`, the backend URL is injected
196
- # as window.XANO_HOST at runtime instead \u2014 no rebuild needed.
197
- VITE_XANO_HOST=https://your-instance.xano.io
198
- `;
199
- }
200
- function renderReadme({ appName }, preset, choice = defaultThemeChoice()) {
201
- return `# ${appName}
202
-
203
- A [XanoTS](https://www.npmjs.com/package/@xanots/sdk) project: a Xano
204
- backend authored in TypeScript under [\`xano/\`](xano/), and a ${preset.label}
205
- frontend under [\`frontend/\`](frontend/) that derives its request paths and
206
- types from the backend defs \u2014 so the two can't drift.
207
-
208
- ## Quick start
209
-
210
- \`\`\`bash
211
- npm install
212
- npm run dev # run the frontend (no backend needed yet)
213
- \`\`\`
214
-
215
- Then author your backend in [\`xano/index.ts\`](xano/index.ts) \u2014 start with the
216
- walkthrough in [\`xano/EXAMPLE.md\`](xano/EXAMPLE.md).
217
-
218
- ## Deploy
219
-
220
- \`\`\`bash
221
- xanots login # once, to authenticate against your Xano account
222
- npm run xano:deploy # build the frontend, then ship it with the backend
223
- \`\`\`
224
-
225
- - \`npm run xano:export\` compiles the backend to \`workspace.json\` (don't commit it).
226
- - \`npm run xano:deploy\` deploys the backend and the built frontend to a live
227
- **ephemeral** environment and prints its URL. Run it again to refresh the same
228
- environment; if it expired, a fresh one is created and the new URL is called out.
229
- - \`xanots status\` says who you are signed in as, which workspace you are bound to, and
230
- which environment this project last deployed to \u2014 its URL, and when it expires. You
231
- never have to remember the environment's name.
232
- - \`npm run xano:test\` runs the tests the DEPLOYED environment carries \u2014 the \`tests\`
233
- on a query/function/middleware and any \`workflowTest()\`. It compiles nothing, so
234
- deploy first. A failing suite exits 5, distinct from a crash. \`xanots deploy
235
- ./xano/index.ts --test\` does both in one step.
236
-
237
- ## \`xano.lock\` \u2014 commit it
238
-
239
- Object identity derives from \`(type, name)\`, so a rename would otherwise change
240
- an object's guid and the engine would **delete and recreate** it rather than
241
- renaming it in place \u2014 losing its rows on a record-preserving import.
242
- [\`xano/xano.lock\`](xano/xano.lock) freezes each guid and each API group's
243
- canonical slug, so renames and re-deploys keep the same identities (and the same
244
- public URLs).
245
-
246
- It is created by \`npm run xano:export\` or \`npm run xano:deploy\`, and **must be
247
- committed**. Adopt it from the first export: once identities have drifted, the
248
- recovery path is \`xanots lock import <live-bundle.json> --lock=xano/xano.lock\`
249
- against the deployed workspace.
250
-
251
- \`\`\`bash
252
- npm run xano:check # CI: fail if the export would change xano.lock
253
- \`\`\`
254
-
255
- To rename an object: rename it in code, run \`npm run xano:export\` (stderr prints
256
- the exact fix-up), run \`xanots lock rename <kind> <old> <new> --lock=xano/xano.lock\`,
257
- then export again. \`lock rename\` and \`lock import\` need that flag here \u2014 they take no
258
- entry file, so they look for the lock in the current directory, while
259
- \`lock prune ./xano/index.ts\` derives it from the entry like \`export\` does.
260
-
261
- ## The one contract
262
-
263
- [\`frontend/src/lib/api.ts\`](frontend/src/lib/api.ts) imports the XanoTS query
264
- defs and derives paths (\`getPath()\`) and request/response types
265
- (\`InferInput\` / \`InferResponse\`) from them. Never hand-type a URL or a request
266
- body \u2014 change a def and the frontend types follow.
267
-
268
- > To spot-check a def from Node (read \`getPath()\`/\`verb\`, log a value), run a real
269
- > file with \`tsx <file.ts>\` **from inside the project root** \u2014 not \`tsx -e\`, not
270
- > bare \`node file.ts\`, and not from another directory (they mis-resolve the
271
- > intra-workspace \`.js\` imports and the \`@xanots/sdk\` specifier). Or use
272
- > \`xanots routes xano/index.ts\` to list every endpoint's verb + path.
273
-
274
- ## The frontend
275
-
276
- ${preset.readmeFrontendSection(choice.icons)}
277
-
278
- ${renderReadmeThemingSection(choice)}
279
-
280
- ## Add-ons
281
-
282
- XanoTS is composable with other \`@xanots/*\` packages:
283
-
284
- - **[\`@xanots/auth\`](https://www.npmjs.com/package/@xanots/auth)** \u2014 turnkey
285
- authentication (user/login/signup tables and endpoints). Install it with
286
- \`xanots marketplace install @xanots/auth\`, then register it in
287
- \`xano/index.ts\`. Authentication only \u2014 **not** authorization: it has no
288
- roles, permissions, or route guards. Build those with \`@xanots/sdk\` (a role
289
- column plus a \`s.precondition\` per endpoint).
290
- - More \`@xanots/*\` packages register onto the same workspace. This list
291
- does not update itself \u2014 run \`xanots marketplace list\` for the live
292
- catalogue, \`xanots marketplace search <words>\` to narrow it, and
293
- \`xanots marketplace details <package>\` to see what an add-on installs and
294
- how to register it. All three work before you log in.
295
-
296
- None of these ship with the scaffold. Install one only when you need it \u2014 an
297
- add-on you never register is weight in \`package.json\` for nothing.
298
- `;
299
- }
300
- function renderXanoIndex({ appName }) {
301
- return `import { workspace } from "@xanots/sdk";
302
-
303
- /**
304
- * The ${appName} backend.
305
- *
306
- * A workspace is assembled by registering typed objects onto a workspace()
307
- * instance and default-exporting it. This starter is intentionally empty and
308
- * already compiles + deploys \u2014 add your first table and endpoint below.
309
- *
310
- * \u2500\u2500 Add your first table + endpoint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
311
- *
312
- * import { workspace, table, apiGroup, query, f, input, s, ref, c, expect, resp } from "@xanots/sdk";
313
- *
314
- * const notes = table({
315
- * name: "notes",
316
- * // \`id\` (int PK) + \`created_at\` (epochms) are auto-injected.
317
- * schema: {
318
- * body: f.text({ required: true }),
319
- * },
320
- * });
321
- *
322
- * const api = apiGroup({ name: "notes", canonical: "notes" }); // pin the slug
323
- *
324
- * const createNote = query({
325
- * name: "create_note",
326
- * verb: "POST",
327
- * apiGroup: api,
328
- * input: { body: input.text({ required: true }) },
329
- * // ...build the stack with the s.* statement helpers...
330
- * // Assertions ride along with the object they cover; \`npm run xano:test\`
331
- * // runs them against whatever you last deployed.
332
- * tests: [
333
- * {
334
- * name: "creates a note",
335
- * input: { body: c.text("hello") },
336
- * expect: [expect.to_be_defined(resp())],
337
- * },
338
- * ],
339
- * });
340
- *
341
- * export default workspace("${appName}")
342
- * .registerTables([notes])
343
- * .registerApiGroups([api])
344
- * .registerQueries([createNote]);
345
- *
346
- * Discover the exact builders and options from the package's own types and its
347
- * shipped docs \u2014 read \`node_modules/@xanots/sdk/llms.txt\` first (it ends with a
348
- * map of the \`llms/*.md\` topic files), then the .d.ts files.
349
- * See \`xano/EXAMPLE.md\` for the full walkthrough.
350
- *
351
- * \u2500\u2500 Optional add-ons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
352
- * Nothing below is installed. Reach for an add-on when you need it, not before.
353
- *
354
- * @xanots/auth registers turnkey auth (user/login/signup) onto this same
355
- * workspace. Install it first (\`xanots marketplace install @xanots/auth\`), then
356
- * \`registerAuth(workspace("${appName}"), { canonical: "authn" })\` returns the
357
- * instance to chain your own .register*() calls onto:
358
- *
359
- * registerAuth(workspace("${appName}"), { canonical: "authn" })
360
- * .registerTables([notes])
361
- * .registerApiGroups([api])
362
- * .registerQueries([createNote]);
363
- *
364
- * That is not the whole catalogue. \`xanots marketplace list\` prints every
365
- * published add-on and \`xanots marketplace details <package>\` prints what one
366
- * installs plus the registration to paste here \u2014 no login required.
367
- */
368
- export default workspace("${appName}");
369
- `;
370
- }
371
- function renderXanoExampleMd({ appName }) {
372
- return `# Building your ${appName} backend
373
-
374
- The backend lives in [\`index.ts\`](index.ts) and is a single default-exported
375
- \`workspace()\`. You grow it by registering typed objects.
376
-
377
- ## Learn the library from the library
378
-
379
- Everything you need is in the package itself:
380
-
381
- - \`node_modules/@xanots/sdk/llms.txt\` \u2014 the router: the mental model, the deploy contract, every gotcha, and control flow. Read it in full first; it ends with a list of topic files and the condition for opening each.
382
- - \`node_modules/@xanots/sdk/llms/*.md\` \u2014 one file per surface. Open the one or two whose condition matches the task; skip the rest.
383
- - The published TypeScript types and JSDoc (\`node_modules/@xanots/sdk/**/*.d.ts\`).
384
- - \`node_modules/@xanots/sdk/manifest.json\` \u2014 the exhaustive reference; grep or \`jq\` the one entry you need rather than reading it whole.
385
-
386
- Author against those signatures \u2014 don't invent an API that isn't there.
387
-
388
- ## The shape
389
-
390
- \`\`\`
391
- xano/
392
- \u251C\u2500\u2500 index.ts default export: the workspace registering everything below
393
- \u251C\u2500\u2500 tables/<name>.ts a table (name, typed schema, indexes)
394
- \u251C\u2500\u2500 api/<group>.ts an API group; pin its canonical slug so paths are stable
395
- \u2514\u2500\u2500 api/<endpoint>.ts a query: name, verb, apiGroup, typed input, a stack, a response
396
- \`\`\`
397
-
398
- ## Steps
399
-
400
- 1. **Define a table** under \`tables/\` with \`table({ name, schema: { ... } })\`.
401
- \`id\` and \`created_at\` are auto-injected.
402
- 2. **Define an API group** with \`apiGroup({ name, canonical })\`. Pinning the
403
- canonical slug keeps the public path stable and lets \`getPath()\` resolve in
404
- the browser bundle without a lock file.
405
- 3. **Define endpoints** with \`query({ name, verb, apiGroup, input, ... })\`, building
406
- the logic from the \`s.*\` statement helpers and the expression/column/input/
407
- reference helpers.
408
- 4. **Register everything** in \`index.ts\`:
409
- \`\`\`ts
410
- export default workspace("${appName}")
411
- .registerTables([...])
412
- .registerApiGroups([...])
413
- .registerQueries([...]);
414
- \`\`\`
415
- 5. **Assert it works** \u2014 add a \`tests: [...]\` entry to a query or function
416
- (named inputs plus \`expect.*\` assertions on its response), or a
417
- \`workflowTest({ name, stack })\` when the behavior spans several objects.
418
- They live beside the code they cover and ship with it. See "Testing" below.
419
- 6. **Compile** with \`npm run xano:export\`, and **deploy** with
420
- \`npm run xano:deploy\` (after \`xanots login\`). The first of either writes
421
- \`xano/xano.lock\` \u2014 **commit it**. It pins every object's identity, so a later
422
- rename renames the object instead of deleting and recreating it. See
423
- "\`xano.lock\` \u2014 commit it" in the project README.
424
-
425
- ## Testing
426
-
427
- Two kinds of test are authored in \`xano/\` alongside the objects they cover, and
428
- both run against a DEPLOYED environment:
429
-
430
- - **Unit test** \u2014 \`tests: [...]\` on a \`query\`, \`defineFunction\`, or
431
- \`middleware\`. Each entry is a named set of inputs run against that object, with
432
- \`expect.*\` assertions on its response. A statement's \`mock\` (keyed by test
433
- NAME) makes one step return a value instead of doing its work, but only while
434
- that test runs.
435
- - **Workflow test** \u2014 \`workflowTest({ name, stack })\`, a standalone object whose
436
- stack calls others (\`s.function.call\`, \`s.api.call\`) and asserts with
437
- \`s.expect.*\`. Use it for behavior that spans objects.
438
-
439
- \`expect.*\` and \`s.expect.*\` are different builders \u2014 an assertion record versus
440
- a workflow-test statement \u2014 and are not interchangeable.
441
-
442
- \`\`\`bash
443
- npm run xano:deploy # tests run against what is deployed, so deploy first
444
- npm run xano:test # runs both kinds; exits 5 if any fail
445
- \`\`\`
446
-
447
- \`npx xanots deploy ./xano/index.ts --test\` does both in one step, and
448
- \`npx xanots test list\` shows what a deployed environment carries without running
449
- anything. Read \`node_modules/@xanots/sdk/llms/tests.md\` before authoring either
450
- kind.
451
-
452
- ### Event-driven objects
453
-
454
- A scheduled \`task\`, an \`mcpServer\`, and every trigger **fire normally on an
455
- ephemeral** \u2014 where \`deploy\` sends them \u2014 so test them by deploying and letting
456
- them run.
457
-
458
- ## Wire the frontend
459
-
460
- In [\`../frontend/src/lib/api.ts\`](../frontend/src/lib/api.ts), derive paths and
461
- types from your query defs (\`getPath()\`, \`InferInput\`, \`InferResponse\`) \u2014 never
462
- hand-type a URL or a request body.
463
-
464
- Keep the client bundle lean (**split route metadata from stack-heavy authoring**):
465
-
466
- - \`import type\` for shapes \u2014 \`InferInput\`/\`InferResponse\` erase to nothing.
467
- - Import the **one lean query def** for its \`getPath()\`/\`verb\`, never \`xano/index.ts\`
468
- (that pulls the whole workspace). A def's \`s.*\`/\`c.*\` stack calls run at module
469
- load to build it, so they can't be tree-shaken out of the bundle.
470
- - A def whose stack builds a heavy graph \u2014 an agent + its tools via \`s.ai.agent.run\`
471
- \u2014 drags that whole graph in. For those, don't import the def in the browser:
472
- declare its \`{ path, verb }\` as plain metadata (see the \`ROUTES\` example in
473
- \`api.ts\`) and verify it against the compiled bundle with
474
- \`npx xanots routes xano/index.ts\`.
475
- `;
476
- }
477
- function describeOrigin(o) {
478
- switch (o.source) {
479
- case "workspace":
480
- return `workspace ${o.origin}`;
481
- case "ephemeral":
482
- return `ephemeral "${o.origin}"`;
483
- case "file":
484
- return `the bundle at ${o.origin}`;
485
- }
486
- }
487
- function renderCodegenMarker({ sdkVersion }, origin, generatedAt, report) {
488
- return JSON.stringify(
489
- {
490
- source: origin.source,
491
- origin: origin.origin,
492
- sdkVersion,
493
- generatedAt,
494
- note: "Written by `xanots init --from`. Its presence lets a re-run refresh xano/ in place.",
495
- // The findings, so parity is trackable release over release and gateable
496
- // in CI without scraping stderr. Omitted rather than written empty when
497
- // the caller has none to record.
498
- ...report === void 0 ? {} : { report }
499
- },
500
- null,
501
- 2
502
- ) + "\n";
503
- }
504
- function renderCodegenReadme({ appName }, origin, envNames, preset) {
505
- const secrets = envNames.length === 0 ? "" : `
506
- ## Heads up: this tree contains your workspace env var values
507
-
508
- The pull carried ${envNames.length} workspace env var${envNames.length === 1 ? "" : "s"} \u2014
509
- ${envNames.map((n) => `\`${n}\``).join(", ")} \u2014 **with their values**, inline in
510
- [\`xano/index.ts\`](xano/index.ts), because that is what a deploy has to send. If any of them
511
- is a secret, do not commit \`xano/\` as-is: add it to \`.gitignore\`, or replace the values
512
- before committing.
513
- `;
514
- return `# ${appName}
515
-
516
- A [XanoTS](https://www.npmjs.com/package/@xanots/sdk) project pulled from
517
- ${describeOrigin(origin)}. The Xano backend lives in [\`xano/\`](xano/) as readable
518
- TypeScript; the ${preset.label} frontend under [\`frontend/\`](frontend/) is a starter \u2014
519
- the pull carries a backend, not a UI.
520
-
521
- ## Deploy it
522
-
523
- \`\`\`bash
524
- xanots login # once, to authenticate against your Xano account
525
- npm run xano:deploy # typecheck, build the frontend, ship both
526
- \`\`\`
527
-
528
- ## Read this before deploying
529
-
530
- - **\`xano/\` is disposable.** Re-running \`xanots init --from\` on this directory
531
- rewrites it wholesale \u2014 no merge, no diff, no preservation of hand edits. The rest of
532
- the project (this README, \`package.json\`, \`frontend/\`) is yours and is left alone.
533
- - **Deploying is a full replace.** The import path clears the target workspace and
534
- re-imports. Send this only to an **ephemeral** environment \u2014 never to a
535
- workspace holding data you care about. That is why \`npm run xano:deploy\` targets an
536
- ephemeral env and there is no deploy-to-your-real-workspace command.
537
- - **This is schema only.** Table rows are not carried, and neither are payload sections
538
- this SDK models no kind for. A deploy recreates the structure, not the data.
539
-
540
- [\`xano/README.md\`](xano/README.md) is the authoritative record of what did and did not
541
- translate cleanly on this pull. Read it before trusting the tree.
542
- ${secrets}
543
- ## Working on it
544
-
545
- \`\`\`bash
546
- npm run dev # run the starter frontend
547
- npm run typecheck # the whole project, both halves
548
- npm run xano:export # compile the backend to workspace.json (don't commit it)
549
- npx xanots routes xano/index.ts # list every endpoint's verb + path
550
- \`\`\`
551
-
552
- [\`frontend/src/lib/api.ts\`](frontend/src/lib/api.ts) shows the one contract: derive
553
- request paths and types from the query defs in \`xano/\` rather than hand-typing a URL.
554
- `;
555
- }
556
- function renderReadmeThemingSection(choice) {
557
- const { theme, dark } = choice;
558
- const darkNote = {
559
- system: `Dark mode follows the OS. An inline script in the HTML entry sets
560
- \`class="dark"\` on \`<html>\` before first paint (so the page never flashes light
561
- first), and the \`.dark\` block in the stylesheet supplies the palette. To let
562
- people override it, add a control that toggles that class \u2014 or scaffold your
563
- next project with \`--dark toggle\`, which ships one.`,
564
- toggle: `Dark mode follows the OS until the user says otherwise. The pieces:
565
- an inline script in the HTML entry applies the mode before first paint,
566
- [\`frontend/src/lib/theme.ts\`](frontend/src/lib/theme.ts) holds and persists it,
567
- and the mode toggle on the landing page cycles system \u2192 light \u2192 dark.`,
568
- off: `This project was scaffolded with \`--dark off\`, so nothing switches
569
- themes. The \`.dark\` block in the stylesheet is still there and still complete \u2014
570
- add \`class="dark"\` to \`<html>\` to see it, and wire that to a control (or the OS
571
- setting) to turn it on for real.`
572
- }[dark];
573
- return `## Theming
574
-
575
- Scaffolded with the **${theme.label}** theme. Every color in the app comes from
576
- the semantic tokens at the top of
577
- [\`frontend/src/index.css\`](frontend/src/index.css) \u2014 \`--primary\`,
578
- \`--muted-foreground\`, \`--border\`, the \`--chart-*\` ramp, the \`--sidebar-*\` set \u2014
579
- and every shadcn component reads those names, so editing one value rebrands
580
- everything that uses it. Style with the token classes (\`bg-primary\`,
581
- \`text-muted-foreground\`) rather than raw palette classes like \`bg-gray-100\`, or
582
- the theme stops being one.
583
-
584
- Tailwind v4 has no \`tailwind.config.js\`; that stylesheet *is* the config.
585
-
586
- To swap the whole palette later:
587
-
588
- \`\`\`bash
589
- npx shadcn@latest add https://ui.shadcn.com/r/themes/stone.json # or any registry theme
590
- \`\`\`
591
-
592
- ${darkNote}`;
593
- }
594
- var UI_DOCS_CTA = {
595
- label: "Browse UI components",
596
- href: "https://ui.shadcn.com/docs/components"
597
- };
598
- function initLanding({ appName }) {
599
- return {
600
- title: appName,
601
- lead: [
602
- { text: "Your XanoTS project is ready. The backend lives in " },
603
- { code: "xano/" },
604
- { text: " and this frontend in " },
605
- { code: "frontend/" },
606
- { text: "." }
607
- ],
608
- steps: [
609
- [
610
- { text: "Author your first table + endpoint in " },
611
- { code: "xano/index.ts" },
612
- { text: " (see " },
613
- { code: "xano/EXAMPLE.md" },
614
- { text: ")." }
615
- ],
616
- [{ text: "Wire it into the UI from " }, { code: "frontend/src/lib/api.ts" }, { text: "." }],
617
- [{ text: "Ship it: " }, { code: "npm run xano:deploy" }, { text: "." }]
618
- ],
619
- cta: UI_DOCS_CTA
620
- };
621
- }
622
- function codegenLanding({ appName }, origin) {
623
- return {
624
- title: appName,
625
- lead: [
626
- { text: `This project was pulled from ${describeOrigin(origin)}. The backend lives in ` },
627
- { code: "xano/" },
628
- { text: " as readable TypeScript; this frontend is a starter." }
629
- ],
630
- steps: [
631
- [
632
- { text: "Read " },
633
- { code: "xano/README.md" },
634
- { text: " \u2014 what did and did not translate cleanly on the pull." }
635
- ],
636
- [
637
- { text: "List the endpoints: " },
638
- { code: "npx xanots routes xano/index.ts" },
639
- { text: ", then wire them up in " },
640
- { code: "frontend/src/lib/api.ts" },
641
- { text: "." }
642
- ],
643
- [
644
- { text: "Ship it: " },
645
- { code: "npm run xano:deploy" },
646
- { text: " (a full replace of an ephemeral env)." }
647
- ]
648
- ],
649
- cta: UI_DOCS_CTA
650
- };
651
- }
652
- function renderIndexCss(choice = defaultThemeChoice()) {
653
- const { theme } = choice;
654
- const colorMap = colorTokens(theme.light).map((token) => ` --color-${token}: var(--${token});`).join("\n");
655
- const block = (vars) => orderedTokens(vars).map(([name, value]) => ` --${name}: ${value};`).join("\n");
656
- const faces = fontImports(choice.fonts);
657
- const fontVars = fontThemeVars(choice.fonts).map(([name, value]) => ` ${name}: ${value};`).join("\n");
658
- const headingRule = choice.fonts?.heading === void 0 ? "" : `
659
- h1,
660
- h2,
661
- h3,
662
- h4,
663
- h5,
664
- h6 {
665
- font-family: var(--font-heading);
666
- }`;
667
- return `${faces.length === 0 ? "" : faces.join("\n") + "\n"}@import "tailwindcss";
668
- @import "tw-animate-css";
669
-
670
- @custom-variant dark (&:is(.dark *));
671
-
672
- /* Map the tokens below into Tailwind's theme so \`bg-primary\` and friends
673
- resolve. Tailwind v4 does this in CSS; there is no tailwind.config.js. */
674
- @theme inline {
675
- ${colorMap}
676
- --radius-sm: calc(var(--radius) - 4px);
677
- --radius-md: calc(var(--radius) - 2px);
678
- --radius-lg: var(--radius);
679
- --radius-xl: calc(var(--radius) + 4px);${fontVars === "" ? "" : `
680
- ${fontVars}`}
681
- }
682
-
683
- /* Theme: ${theme.label}. Rebrand here \u2014 these are the only colors the
684
- components know about. Swap the whole palette for another shadcn theme with
685
- \`npx shadcn@latest add <registry-theme-url>\`, or scaffold a different one
686
- next time with \`xanots init --theme <base>-<accent>\`. */
687
- :root {
688
- ${block(theme.light)}
689
- }
690
-
691
- ${darkBlockComment(choice.dark)}
692
- .dark {
693
- ${block(theme.dark)}
694
- }
695
-
696
- @layer base {
697
- * {
698
- @apply border-border outline-ring/50;
699
- }
700
- body {
701
- @apply bg-background text-foreground;
702
- }${headingRule}
703
- }
704
- `;
705
- }
706
- function darkBlockComment(dark) {
707
- switch (dark) {
708
- case "system":
709
- return `/* Applied by the inline script in index.html, which follows the OS setting.
710
- Add a control that toggles this class to let the user override it. */`;
711
- case "toggle":
712
- return `/* Applied by frontend/src/lib/theme.ts \u2014 the OS setting by default, and the
713
- user's choice once they use the mode toggle. */`;
714
- case "off":
715
- return `/* Applied by adding \`class="dark"\` to <html> \u2014 wire that to a toggle if you
716
- want one; this scaffold was created with --dark off, so nothing does yet. */`;
717
- }
718
- }
719
- function renderThemeScript(dark) {
720
- if (dark === "off") return "";
721
- const resolve = dark === "toggle" ? `var stored = localStorage.getItem("theme");
722
- var dark = stored === "dark" || (stored !== "light" && media.matches);` : `var dark = media.matches;`;
723
- return ` <script>
724
- // Sets the \`dark\` class before first paint, so the page never flashes
725
- // light before switching. Keep this inline and above the stylesheet.
726
- (function () {
727
- try {
728
- var media = window.matchMedia("(prefers-color-scheme: dark)");
729
- ${resolve}
730
- document.documentElement.classList.toggle("dark", dark);
731
- } catch (e) {}
732
- })();
733
- </script>
734
- `;
735
- }
736
- function renderThemeModule() {
737
- return `/**
738
- * Color mode, persisted. The inline script in index.html applies the stored
739
- * value before first paint; this module is what changes it afterwards.
740
- *
741
- * "system" means no stored preference \u2014 the OS decides, and keeps deciding if
742
- * the user changes it while the page is open.
743
- */
744
- export type Mode = "light" | "dark" | "system";
745
-
746
- const KEY = "theme";
747
-
748
- const prefersDark = () =>
749
- window.matchMedia("(prefers-color-scheme: dark)").matches;
750
-
751
- /** The stored preference, or "system" when there is none. */
752
- export function getMode(): Mode {
753
- const stored = localStorage.getItem(KEY);
754
- return stored === "light" || stored === "dark" ? stored : "system";
755
- }
756
-
757
- /** Whether \`mode\` renders dark right now. */
758
- export function isDark(mode: Mode): boolean {
759
- return mode === "dark" || (mode === "system" && prefersDark());
760
- }
761
-
762
- /** Persist a mode and apply it. "system" clears the stored override. */
763
- export function setMode(mode: Mode): void {
764
- if (mode === "system") localStorage.removeItem(KEY);
765
- else localStorage.setItem(KEY, mode);
766
- document.documentElement.classList.toggle("dark", isDark(mode));
767
- }
768
-
769
- /**
770
- * Re-apply on OS changes, and on changes made in another tab. Returns an
771
- * unsubscribe function \u2014 call it from your framework's cleanup hook.
772
- *
773
- * The OS listener re-reads the mode rather than closing over it, so it stays
774
- * correct after the user picks an explicit light/dark and then goes back to
775
- * system.
776
- */
777
- export function watchMode(onChange: (mode: Mode) => void): () => void {
778
- const media = window.matchMedia("(prefers-color-scheme: dark)");
779
- const apply = () => {
780
- const mode = getMode();
781
- document.documentElement.classList.toggle("dark", isDark(mode));
782
- onChange(mode);
783
- };
784
- media.addEventListener("change", apply);
785
- window.addEventListener("storage", apply);
786
- return () => {
787
- media.removeEventListener("change", apply);
788
- window.removeEventListener("storage", apply);
789
- };
790
- }
791
- `;
792
- }
793
- function renderCnUtil(extra = "") {
794
- return `import { clsx, type ClassValue } from "clsx";
795
- import { twMerge } from "tailwind-merge";
796
-
797
- export function cn(...inputs: ClassValue[]) {
798
- return twMerge(clsx(inputs));
799
- }
800
- ${extra}`;
801
- }
802
- function renderApiTs() {
803
- return `// The one contract: derive paths and request/response *types* from your xanots
804
- // query defs. Never hand-type a URL or a request body \u2014 change a def and
805
- // everything here follows.
806
- //
807
- // Keep the client bundle lean (the split-route-metadata rule):
808
- // \u2022 \`import type\` for shapes \u2014 InferInput/InferResponse erase to nothing.
809
- // \u2022 Import the ONE lean query def module for its getPath()/verb \u2014 never
810
- // ../../../xano/index.js (that pulls the whole workspace) and never a def
811
- // whose stack builds a heavy graph (an agent + its tools via s.ai.agent.run):
812
- // those s.*/c.* factory calls run at module load and can't be tree-shaken out.
813
- // \u2022 For such a stack-heavy endpoint, don't import its def in the browser at all \u2014
814
- // declare its { path, verb } in the ROUTES table below and verify it against
815
- // the compiled bundle with \`npx xanots routes xano/index.ts\`.
816
- //
817
- // This starter has no endpoints yet. Once you add one in xano/, wire it like:
818
- //
819
- // // Types are free \u2014 always import them type-only.
820
- // import type { InferInput, InferResponse } from "@xanots/sdk";
821
- // import type { createNoteQuery } from "../../../xano/api/create-note.js";
822
- //
823
- // // Runtime path/verb: import the lean def value, OR (for a stack-heavy def)
824
- // // read it from ROUTES so the def never enters the bundle.
825
- // import { createNoteQuery } from "../../../xano/api/create-note.js";
826
- //
827
- // export type CreateNoteBody = InferInput<typeof createNoteQuery>;
828
- // export type Note = InferResponse<typeof createNoteQuery>;
829
- //
830
- // export async function createNote(body: CreateNoteBody): Promise<Note> {
831
- // const res = await fetch(XANO_HOST + createNoteQuery.getPath(), {
832
- // method: createNoteQuery.verb,
833
- // headers: { "content-type": "application/json" },
834
- // body: JSON.stringify(body),
835
- // });
836
- // if (!res.ok) throw new Error(await res.text());
837
- // return res.json();
838
- // }
839
- //
840
- // The stack-heavy escape hatch \u2014 plain metadata, no def import, no bundle cost.
841
- // Keep it in sync with \`npx xanots routes xano/index.ts\` (it prints verb + path):
842
- //
843
- // export const ROUTES = {
844
- // triageRequest: { path: "/api:notes/triage_request", verb: "POST" },
845
- // } as const;
846
-
847
- /**
848
- * The deployed Xano backend's base URL. Injected as \`window.XANO_HOST\` by
849
- * \`xanots deploy <entry> --static <dir>\`, or read from \`VITE_XANO_HOST\` in dev.
850
- * Empty string when neither is set (the UI runs with no backend).
851
- */
852
- export const XANO_HOST: string =
853
- (typeof window !== "undefined" && (window as { XANO_HOST?: string }).XANO_HOST) ||
854
- import.meta.env.VITE_XANO_HOST ||
855
- "";
856
- `;
857
- }
858
-
859
- export {
860
- sdkDep,
861
- renderPackageJson,
862
- renderTsconfig,
863
- renderViteConfig,
864
- renderIndexHtml,
865
- renderGitignore,
866
- renderEnvExample,
867
- renderReadme,
868
- renderXanoIndex,
869
- renderXanoExampleMd,
870
- describeOrigin,
871
- renderCodegenMarker,
872
- renderCodegenReadme,
873
- initLanding,
874
- codegenLanding,
875
- renderIndexCss,
876
- renderThemeScript,
877
- renderThemeModule,
878
- renderCnUtil,
879
- renderApiTs
880
- };
881
- //# sourceMappingURL=chunk-ANUDXFEX.js.map