@xanots/sdk 0.0.2 → 0.0.3-beta.0

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 (40) hide show
  1. package/README.md +10 -1
  2. package/dist/.build-fingerprint +1 -1
  3. package/dist/{agent-file-refresh-7XCACU34.js → agent-file-refresh-J3KY54E5.js} +3 -2
  4. package/dist/bin.js +7 -7
  5. package/dist/chunk-4HKZH6YK.js +307 -0
  6. package/dist/chunk-6DHJVLYP.js +2044 -0
  7. package/dist/{chunk-MUDLJG2O.js → chunk-CBL57QBQ.js} +25 -15
  8. package/dist/{chunk-5YTBTF25.js → chunk-EWFHDJY5.js} +2 -2
  9. package/dist/{chunk-IZW6MK3U.js → chunk-FY6LXRR6.js} +2 -2
  10. package/dist/{chunk-ZPEY5WE4.js → chunk-HKAX756N.js} +3 -3
  11. package/dist/{chunk-XLD4BUTF.js → chunk-MWLNBSTY.js} +2 -2
  12. package/dist/{chunk-YYRDLFSJ.js → chunk-QO4I5OYF.js} +40 -2
  13. package/dist/{chunk-UVLVQWWF.js → chunk-VVXPK6SE.js} +62 -20
  14. package/dist/{chunk-VKFQJEMW.js → chunk-WRYT2PAX.js} +14 -371
  15. package/dist/{chunk-ODNMVJ6H.js → chunk-YZ4GU6F5.js} +283 -252
  16. package/dist/cli.d.ts +22 -0
  17. package/dist/cli.js +5 -5
  18. package/dist/{codegen-command-RY2GWDLX.js → codegen-command-EXLKZ2ZI.js} +12 -10
  19. package/dist/{completion-TIPPZN7H.js → completion-2PVZK6YX.js} +2 -2
  20. package/dist/{deploy-command-DSISLUW5.js → deploy-command-SXX6RJWM.js} +13 -13
  21. package/dist/{env-target-7C2GKBCY.js → env-target-3EGIPR4D.js} +2 -2
  22. package/dist/{ephemeral-command-5K573ZSG.js → ephemeral-command-OGOYP7D6.js} +15 -15
  23. package/dist/{init-command-4AWL6CLW.js → init-command-6DGXEVZD.js} +10 -8
  24. package/dist/internal.js +1 -1
  25. package/dist/{lock-commands-7Q5LNZKL.js → lock-commands-R22J54XV.js} +9 -9
  26. package/dist/{marketplace-command-RATXTKXN.js → marketplace-command-XJIWL4YP.js} +6 -6
  27. package/dist/meta-client-OW5WKWW7.js +1 -1
  28. package/dist/node.js +8 -8
  29. package/dist/{release-command-QXLRJMY7.js → release-command-6PLMLGDK.js} +15 -15
  30. package/dist/sandbox-export-command-QCJY4GMV.js +1 -1
  31. package/dist/scaffold.d.ts +1896 -0
  32. package/dist/scaffold.js +101 -0
  33. package/dist/{test-command-BOLIU57E.js → test-command-LLPXKT35.js} +4 -4
  34. package/dist/{validate-command-SYCFEYZN.js → validate-command-WAT3ZJT3.js} +7 -7
  35. package/dist/{workspace-command-TOB2AJ6X.js → workspace-command-JKMI7RZ4.js} +13 -11
  36. package/dist/workspace-export-AJMGN3CQ.js +1 -1
  37. package/llms-full.txt +1 -1
  38. package/llms.txt +1 -1
  39. package/manifest.json +141 -1
  40. package/package.json +7 -1
package/README.md CHANGED
@@ -102,8 +102,17 @@ npx @xanots/sdk init my-app && cd my-app
102
102
  npm run dev # the frontend runs immediately
103
103
  ```
104
104
 
105
+ In a terminal it asks three questions — framework, theme, AI instruction files — each with a
106
+ default. The theme is shadcn's own: a base color plus an optional accent, rendered into the
107
+ token stylesheet, with a complete dark palette either way.
108
+
109
+ ```bash
110
+ npx @xanots/sdk init my-app --theme zinc-blue --dark toggle
111
+ npx @xanots/sdk init my-app --theme https://ui.shadcn.com/r/themes/slate.json # or any registry theme
112
+ ```
113
+
105
114
  The starter backend is empty but already compiles and deploys — grow it from the walkthrough
106
- in `xano/EXAMPLE.md`. For the flags, the two presets, and add-ons, see
115
+ in `xano/EXAMPLE.md`. For the flags, the two presets, theming, and add-ons, see
107
116
  [The scaffolded project](https://github.com/xanots/sdk/blob/main/guides/scaffold.md).
108
117
 
109
118
  **3. Build the frontend.**
@@ -1 +1 @@
1
- c55e4f9d5b84cbeb62826b396d7824e3302e42e404c14866421d7e54d035853c
1
+ f76d4016cad82dc8dd5a4715fbed070d613d1f69be8bda57ea3ca194658c21dd
@@ -5,8 +5,9 @@ import {
5
5
  presetFilePath,
6
6
  renderPreset,
7
7
  upsertManagedBlock
8
- } from "./chunk-VKFQJEMW.js";
8
+ } from "./chunk-WRYT2PAX.js";
9
9
  import "./chunk-47WDWMBJ.js";
10
+ import "./chunk-6DHJVLYP.js";
10
11
  import {
11
12
  warn
12
13
  } from "./chunk-EZG76F7R.js";
@@ -92,4 +93,4 @@ export {
92
93
  isCI,
93
94
  refreshAgentFiles
94
95
  };
95
- //# sourceMappingURL=agent-file-refresh-7XCACU34.js.map
96
+ //# sourceMappingURL=agent-file-refresh-J3KY54E5.js.map
package/dist/bin.js CHANGED
@@ -2,11 +2,12 @@
2
2
  import {
3
3
  readVersion,
4
4
  run
5
- } from "./chunk-UVLVQWWF.js";
5
+ } from "./chunk-VVXPK6SE.js";
6
6
  import {
7
7
  exitCodeOf,
8
8
  reportFailure
9
- } from "./chunk-IZW6MK3U.js";
9
+ } from "./chunk-FY6LXRR6.js";
10
+ import "./chunk-HJPTWBLH.js";
10
11
  import {
11
12
  blank,
12
13
  clearProgress,
@@ -15,15 +16,14 @@ import {
15
16
  style,
16
17
  warn
17
18
  } from "./chunk-EZG76F7R.js";
19
+ import {
20
+ atomicWrite
21
+ } from "./chunk-GNPVYOPB.js";
18
22
  import "./chunk-55VHDNW5.js";
19
- import "./chunk-YYRDLFSJ.js";
23
+ import "./chunk-QO4I5OYF.js";
20
24
  import "./chunk-5L4X5LS6.js";
21
25
  import "./chunk-WHOJWOSV.js";
22
26
  import "./chunk-5XZ744TS.js";
23
- import "./chunk-HJPTWBLH.js";
24
- import {
25
- atomicWrite
26
- } from "./chunk-GNPVYOPB.js";
27
27
  import "./chunk-4HT3BNZ7.js";
28
28
 
29
29
  // src/emit/update-check.ts
@@ -0,0 +1,307 @@
1
+ import {
2
+ presetFilePath,
3
+ resolveAiPresets,
4
+ scaffoldProject
5
+ } from "./chunk-WRYT2PAX.js";
6
+ import {
7
+ DEFAULT_FRAMEWORK,
8
+ allFrontendPresets,
9
+ findFrontendPreset,
10
+ initLanding,
11
+ renderApiTs,
12
+ renderCnUtil,
13
+ renderEnvExample,
14
+ renderGitignore,
15
+ renderIndexCss,
16
+ renderIndexHtml,
17
+ renderPackageJson,
18
+ renderReadme,
19
+ renderThemeModule,
20
+ renderTsconfig,
21
+ renderViteConfig,
22
+ renderXanoExampleMd,
23
+ renderXanoIndex,
24
+ resolveFrameworkFlag
25
+ } from "./chunk-YZ4GU6F5.js";
26
+ import {
27
+ DEFAULT_DARK_MODE,
28
+ curatedThemes,
29
+ defaultTheme,
30
+ defaultThemeChoice,
31
+ normalizeRadius,
32
+ resolveDarkModeFlag,
33
+ resolveFontFlag,
34
+ resolveIconsFlag,
35
+ resolveThemeFlag,
36
+ themeFromRegistryItem,
37
+ themeSwatch,
38
+ withRadius
39
+ } from "./chunk-6DHJVLYP.js";
40
+ import {
41
+ readVersion
42
+ } from "./chunk-VVXPK6SE.js";
43
+ import {
44
+ blank,
45
+ detail,
46
+ info,
47
+ step,
48
+ style,
49
+ success
50
+ } from "./chunk-EZG76F7R.js";
51
+
52
+ // src/emit/init-command.ts
53
+ import { basename, resolve } from "path";
54
+
55
+ // src/emit/frontend-resolve.ts
56
+ import { createInterface } from "readline/promises";
57
+ function defaultFrontendPreset() {
58
+ const preset = findFrontendPreset(DEFAULT_FRAMEWORK);
59
+ if (preset === void 0) {
60
+ throw new Error(`Default frontend preset "${DEFAULT_FRAMEWORK}" is missing from the table.`);
61
+ }
62
+ return preset;
63
+ }
64
+ async function promptFramework() {
65
+ const presets = allFrontendPresets();
66
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
67
+ try {
68
+ process.stderr.write(
69
+ `
70
+ Which frontend framework?
71
+ ` + presets.map((p, i) => ` ${i + 1}) ${p.label}${p.id === DEFAULT_FRAMEWORK ? " (default)" : ""}`).join("\n") + `
72
+ `
73
+ );
74
+ const answer = await rl.question(`Enter a number or name, or leave blank for the default: `);
75
+ const token = answer.trim().toLowerCase();
76
+ if (token === "") return defaultFrontendPreset();
77
+ const byIndex = Number.parseInt(token, 10);
78
+ if (Number.isInteger(byIndex) && byIndex >= 1 && byIndex <= presets.length) {
79
+ return presets[byIndex - 1];
80
+ }
81
+ return findFrontendPreset(token) ?? defaultFrontendPreset();
82
+ } finally {
83
+ rl.close();
84
+ }
85
+ }
86
+ async function resolveFrontendPreset(flag) {
87
+ if (flag !== void 0 && flag !== "") return resolveFrameworkFlag(flag);
88
+ if (process.stdin.isTTY && process.stderr.isTTY) return promptFramework();
89
+ return defaultFrontendPreset();
90
+ }
91
+
92
+ // src/emit/theme-resolve.ts
93
+ import { createInterface as createInterface2 } from "readline/promises";
94
+ import { readFileSync } from "fs";
95
+ import { resolve as resolvePath } from "path";
96
+ function isRegistryRef(value) {
97
+ return /^https?:\/\//i.test(value) || value.toLowerCase().endsWith(".json");
98
+ }
99
+ async function loadRegistryTheme(ref) {
100
+ if (/^https?:\/\//i.test(ref)) {
101
+ let res;
102
+ try {
103
+ res = await fetch(ref, { headers: { accept: "application/json" } });
104
+ } catch (err) {
105
+ throw new Error(
106
+ `Could not fetch the theme at ${ref}: ${err instanceof Error ? err.message : String(err)}`
107
+ );
108
+ }
109
+ if (!res.ok) throw new Error(`Could not fetch the theme at ${ref}: ${res.status} ${res.statusText}`);
110
+ const text2 = await res.text();
111
+ return themeFromRegistryItem(parseJson(text2, ref), ref);
112
+ }
113
+ const path = resolvePath(ref);
114
+ let text;
115
+ try {
116
+ text = readFileSync(path, "utf8");
117
+ } catch (err) {
118
+ throw new Error(
119
+ `Could not read the theme file ${path}: ${err instanceof Error ? err.message : String(err)}`
120
+ );
121
+ }
122
+ return themeFromRegistryItem(parseJson(text, path), path);
123
+ }
124
+ function parseJson(text, source) {
125
+ try {
126
+ return JSON.parse(text);
127
+ } catch {
128
+ throw new Error(`${source} is not valid JSON \u2014 a registry theme is a JSON registry item.`);
129
+ }
130
+ }
131
+ async function themeFromFlag(raw) {
132
+ return isRegistryRef(raw.trim()) ? loadRegistryTheme(raw.trim()) : resolveThemeFlag(raw);
133
+ }
134
+ function swatch(theme, color) {
135
+ if (!color) return "";
136
+ const blocks = themeSwatch(theme).map(({ r, g, b }) => `\x1B[38;2;${r};${g};${b}m\u2588\x1B[0m`).join("");
137
+ return blocks === "" ? "" : `${blocks} `;
138
+ }
139
+ function colorEnabled() {
140
+ if (process.env.FORCE_COLOR) return process.env.FORCE_COLOR !== "0";
141
+ return !process.env.NO_COLOR && process.stderr.isTTY === true;
142
+ }
143
+ async function promptTheme() {
144
+ const themes = curatedThemes();
145
+ const color = colorEnabled();
146
+ const rl = createInterface2({ input: process.stdin, output: process.stderr });
147
+ try {
148
+ process.stderr.write(
149
+ `
150
+ Which theme? (shadcn/ui color tokens \u2014 everything else is identical)
151
+ ` + themes.map((t, i) => ` ${i + 1}) ${swatch(t, color)}${t.label}${i === 0 ? " (default)" : ""}`).join("\n") + `
152
+ ${style.dim(
153
+ ` \u2026or any <base>-<accent> pair, or a registry theme URL. See \`xanots init --help\`.`
154
+ )}
155
+ `
156
+ );
157
+ for (; ; ) {
158
+ const answer = (await rl.question(`Enter a number, a theme, or leave blank for the default: `)).trim();
159
+ if (answer === "") return defaultTheme();
160
+ const byIndex = Number.parseInt(answer, 10);
161
+ if (Number.isInteger(byIndex) && byIndex >= 1 && byIndex <= themes.length) {
162
+ return themes[byIndex - 1];
163
+ }
164
+ if (!isRegistryRef(answer)) {
165
+ try {
166
+ return resolveThemeFlag(answer);
167
+ } catch {
168
+ return defaultTheme();
169
+ }
170
+ }
171
+ try {
172
+ return await loadRegistryTheme(answer);
173
+ } catch (err) {
174
+ process.stderr.write(`${style.yellow("!")} ${err instanceof Error ? err.message : String(err)}
175
+ `);
176
+ }
177
+ }
178
+ } finally {
179
+ rl.close();
180
+ }
181
+ }
182
+ async function resolveThemeChoice(args) {
183
+ const dark = args.dark === void 0 || args.dark === "" ? DEFAULT_DARK_MODE : resolveDarkModeFlag(args.dark);
184
+ const fonts = {
185
+ ...args.font !== void 0 && args.font !== "" ? { sans: resolveFontFlag(args.font, "sans") } : {},
186
+ ...args.fontMono !== void 0 && args.fontMono !== "" ? { mono: resolveFontFlag(args.fontMono, "mono") } : {},
187
+ ...args.fontHeading !== void 0 && args.fontHeading !== "" ? { heading: resolveFontFlag(args.fontHeading, "heading") } : {}
188
+ };
189
+ const icons = args.icons === void 0 || args.icons === "" ? void 0 : resolveIconsFlag(args.icons).id;
190
+ let theme;
191
+ if (args.theme !== void 0 && args.theme !== "") {
192
+ theme = await themeFromFlag(args.theme);
193
+ } else if (process.stdin.isTTY && process.stderr.isTTY) {
194
+ theme = await promptTheme();
195
+ } else {
196
+ theme = defaultThemeChoice().theme;
197
+ }
198
+ if (args.radius !== void 0 && args.radius !== "") {
199
+ theme = withRadius(theme, normalizeRadius(args.radius));
200
+ }
201
+ return {
202
+ theme,
203
+ dark,
204
+ ...Object.keys(fonts).length === 0 ? {} : { fonts },
205
+ ...icons === void 0 ? {} : { icons }
206
+ };
207
+ }
208
+
209
+ // src/emit/init-command.ts
210
+ function sanitizeAppName(raw) {
211
+ const name = raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
212
+ return name === "" ? "app" : name;
213
+ }
214
+ function projectShellFiles(vars, preset, parts, choice = defaultThemeChoice()) {
215
+ return [
216
+ { path: "package.json", content: renderPackageJson(vars, preset, choice) },
217
+ { path: "tsconfig.json", content: renderTsconfig(preset) },
218
+ { path: "vite.config.ts", content: renderViteConfig(preset) },
219
+ { path: ".gitignore", content: renderGitignore(preset) },
220
+ { path: ".env.example", content: renderEnvExample() },
221
+ { path: "README.md", content: parts.readme },
222
+ // Skipped when the framework supplies its own entry document — see
223
+ // FrontendPreset.ownsHtmlEntry. A second index.html beside SvelteKit's
224
+ // app.html would never be served and would still read as the entry point.
225
+ ...preset.ownsHtmlEntry === true ? [] : [{ path: "frontend/index.html", content: renderIndexHtml(vars, preset, choice) }],
226
+ // Shared across every framework: both UI kits resolve against the same
227
+ // Tailwind v4 semantic tokens, back `cn()` with clsx + tailwind-merge, and
228
+ // look for library code at frontend/src/lib/. Only the alias NAME pointing
229
+ // at that directory differs, and the preset owns that.
230
+ { path: "frontend/src/index.css", content: renderIndexCss(choice) },
231
+ { path: "frontend/src/lib/api.ts", content: renderApiTs() },
232
+ { path: "frontend/src/lib/utils.ts", content: renderCnUtil(preset.libUtilsExtra) },
233
+ // The mode store behind `--dark toggle`, shared by both presets' toggles —
234
+ // it is plain DOM and localStorage, with nothing framework-shaped in it.
235
+ // `system` and `off` need no state, so nothing is written for them.
236
+ ...choice.dark === "toggle" ? [{ path: "frontend/src/lib/theme.ts", content: renderThemeModule() }] : [],
237
+ // The framework's own half: entry module, app component, vendored UI kit,
238
+ // and whatever tool config that kit's CLI reads.
239
+ ...preset.files(vars, parts.landing, choice)
240
+ ];
241
+ }
242
+ function buildFileSet(vars, preset, choice) {
243
+ return [
244
+ ...projectShellFiles(
245
+ vars,
246
+ preset,
247
+ { readme: renderReadme(vars, preset, choice), landing: initLanding(vars) },
248
+ choice
249
+ ),
250
+ { path: "xano/index.ts", content: renderXanoIndex(vars) },
251
+ { path: "xano/EXAMPLE.md", content: renderXanoExampleMd(vars) }
252
+ ];
253
+ }
254
+ async function runInitCommand(args) {
255
+ const targetArg = args.positionals[0] ?? ".";
256
+ const targetDir = resolve(targetArg);
257
+ const appName = sanitizeAppName(args.name ?? basename(targetDir));
258
+ const preset = await resolveFrontendPreset(args.framework);
259
+ const choice = await resolveThemeChoice(args);
260
+ const presets = await resolveAiPresets(args.ai);
261
+ const vars = { appName, sdkVersion: readVersion() };
262
+ step(
263
+ `Scaffolding ${style.bold(appName)} (${preset.label}, ${choice.theme.label}) in ${targetDir}`
264
+ );
265
+ const result = await scaffoldProject({
266
+ targetDir,
267
+ files: buildFileSet(vars, preset, choice),
268
+ presets,
269
+ appName,
270
+ force: args.force,
271
+ noInstall: args.noInstall,
272
+ // An `init` project's `xano/` is hand-authored, never machine-written: it is
273
+ // not refreshable, and `--force` must not clear it.
274
+ regenerable: false,
275
+ sdkVersion: readVersion(),
276
+ frontend: preset,
277
+ theme: choice
278
+ });
279
+ info(`Theme: ${choice.theme.label} \u2014 dark mode: ${choice.dark}`);
280
+ if (presets.length > 0) {
281
+ info(`AI instructions: ${presets.map(presetFilePath).join(", ")}`);
282
+ }
283
+ blank();
284
+ success("Project ready.");
285
+ const cdHint = targetDir === process.cwd() ? "" : ` cd ${targetArg}
286
+ `;
287
+ detail(
288
+ `Next steps:
289
+ ` + cdHint + (result.install === "installed" ? `` : ` npm install
290
+ `) + ` npm run dev # run the frontend
291
+ xanots login # authenticate with Xano
292
+ npm run xano:deploy # build the frontend, then deploy \u2192 live ephemeral URL
293
+
294
+ The first export or deploy writes xano/xano.lock \u2014 commit it. It pins each
295
+ object's identity, so renaming one later renames it instead of deleting and
296
+ recreating it. See "xano.lock \u2014 commit it" in the README.`
297
+ );
298
+ }
299
+
300
+ export {
301
+ resolveFrontendPreset,
302
+ resolveThemeChoice,
303
+ sanitizeAppName,
304
+ projectShellFiles,
305
+ runInitCommand
306
+ };
307
+ //# sourceMappingURL=chunk-4HKZH6YK.js.map