@xanots/sdk 0.0.2 → 0.0.3

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 (43) hide show
  1. package/README.md +11 -2
  2. package/dist/.build-fingerprint +1 -1
  3. package/dist/{agent-file-refresh-7XCACU34.js → agent-file-refresh-6M5M7T24.js} +4 -3
  4. package/dist/bin.js +7 -7
  5. package/dist/{chunk-MUDLJG2O.js → chunk-2V4YE6QC.js} +25 -15
  6. package/dist/{chunk-UVLVQWWF.js → chunk-3NFUWXOC.js} +70 -20
  7. package/dist/chunk-6DHJVLYP.js +2044 -0
  8. package/dist/{chunk-IZW6MK3U.js → chunk-6USV65XA.js} +2 -2
  9. package/dist/{chunk-XLD4BUTF.js → chunk-BT2CSEC5.js} +2 -2
  10. package/dist/{chunk-VKFQJEMW.js → chunk-CMZLPTGW.js} +37 -406
  11. package/dist/{chunk-YYRDLFSJ.js → chunk-DYCLVQXW.js} +50 -3
  12. package/dist/chunk-HQ2CBRVI.js +530 -0
  13. package/dist/{chunk-ZPEY5WE4.js → chunk-W2G2WPTB.js} +3 -3
  14. package/dist/chunk-WUSKBXXD.js +25 -0
  15. package/dist/{chunk-ODNMVJ6H.js → chunk-YZ4GU6F5.js} +283 -252
  16. package/dist/{chunk-5YTBTF25.js → chunk-ZO3HJOCJ.js} +2 -2
  17. package/dist/cli.d.ts +31 -0
  18. package/dist/cli.js +5 -5
  19. package/dist/{codegen-command-RY2GWDLX.js → codegen-command-FIADGXRN.js} +13 -11
  20. package/dist/{completion-TIPPZN7H.js → completion-BKAFCZBE.js} +2 -2
  21. package/dist/{deploy-command-DSISLUW5.js → deploy-command-4R6BYC6G.js} +13 -13
  22. package/dist/{env-target-7C2GKBCY.js → env-target-XWS2ZZ2Y.js} +2 -2
  23. package/dist/{ephemeral-command-5K573ZSG.js → ephemeral-command-JL4TPIRQ.js} +15 -15
  24. package/dist/{init-command-4AWL6CLW.js → init-command-LOK75W64.js} +11 -9
  25. package/dist/internal.js +4 -3
  26. package/dist/{lock-commands-7Q5LNZKL.js → lock-commands-XOYS75YQ.js} +9 -9
  27. package/dist/{marketplace-command-RATXTKXN.js → marketplace-command-UVV3XAOL.js} +6 -6
  28. package/dist/meta-client-OW5WKWW7.js +1 -1
  29. package/dist/node.js +8 -8
  30. package/dist/{release-command-QXLRJMY7.js → release-command-HZUY2XZX.js} +15 -15
  31. package/dist/sandbox-export-command-QCJY4GMV.js +1 -1
  32. package/dist/scaffold.d.ts +1896 -0
  33. package/dist/scaffold.js +101 -0
  34. package/dist/{test-command-BOLIU57E.js → test-command-72Y5S22H.js} +4 -4
  35. package/dist/{validate-command-SYCFEYZN.js → validate-command-KDGH537H.js} +7 -7
  36. package/dist/{workspace-command-TOB2AJ6X.js → workspace-command-ICSI6PKO.js} +13 -11
  37. package/dist/workspace-export-AJMGN3CQ.js +1 -1
  38. package/llms/kinds-agent-mcp.md +2 -1
  39. package/llms-full.txt +3 -2
  40. package/llms.txt +1 -1
  41. package/manifest.json +145 -1
  42. package/package.json +7 -1
  43. package/dist/chunk-47WDWMBJ.js +0 -14
@@ -0,0 +1,530 @@
1
+ import {
2
+ presetFilePath,
3
+ resolveAiPresets,
4
+ scaffoldProject
5
+ } from "./chunk-CMZLPTGW.js";
6
+ import {
7
+ runNpmCapturingStderr
8
+ } from "./chunk-WUSKBXXD.js";
9
+ import {
10
+ DEFAULT_FRAMEWORK,
11
+ allFrontendPresets,
12
+ findFrontendPreset,
13
+ initLanding,
14
+ renderApiTs,
15
+ renderCnUtil,
16
+ renderEnvExample,
17
+ renderGitignore,
18
+ renderIndexCss,
19
+ renderIndexHtml,
20
+ renderPackageJson,
21
+ renderReadme,
22
+ renderThemeModule,
23
+ renderTsconfig,
24
+ renderViteConfig,
25
+ renderXanoExampleMd,
26
+ renderXanoIndex,
27
+ resolveFrameworkFlag
28
+ } from "./chunk-YZ4GU6F5.js";
29
+ import {
30
+ DEFAULT_DARK_MODE,
31
+ curatedThemes,
32
+ defaultTheme,
33
+ defaultThemeChoice,
34
+ normalizeRadius,
35
+ resolveDarkModeFlag,
36
+ resolveFontFlag,
37
+ resolveIconsFlag,
38
+ resolveThemeFlag,
39
+ themeFromRegistryItem,
40
+ themeSwatch,
41
+ withRadius
42
+ } from "./chunk-6DHJVLYP.js";
43
+ import {
44
+ readVersion
45
+ } from "./chunk-3NFUWXOC.js";
46
+ import {
47
+ UsageError
48
+ } from "./chunk-6USV65XA.js";
49
+ import {
50
+ blank,
51
+ detail,
52
+ info,
53
+ step,
54
+ style,
55
+ success,
56
+ warn
57
+ } from "./chunk-EZG76F7R.js";
58
+
59
+ // src/emit/init-command.ts
60
+ import { writeFileSync } from "fs";
61
+ import { basename, join as join2, resolve } from "path";
62
+
63
+ // src/emit/init-modules.ts
64
+ import { existsSync, readFileSync } from "fs";
65
+ import { join } from "path";
66
+ import { pathToFileURL } from "url";
67
+ function parseMarketplaceFlag(values) {
68
+ const packages = [];
69
+ for (const value of values) {
70
+ for (const raw of value.split(",")) {
71
+ const pkg = raw.trim();
72
+ if (pkg === "") continue;
73
+ if (pkg.startsWith("-")) {
74
+ throw new UsageError(
75
+ `\`--marketplace\`: "${pkg}" starts with a dash, so npm would read it as a flag, not a package.`,
76
+ { helpFor: { command: "init" } }
77
+ );
78
+ }
79
+ if (!packages.includes(pkg)) packages.push(pkg);
80
+ }
81
+ }
82
+ return packages;
83
+ }
84
+ function packageNameOf(specifier) {
85
+ const at = specifier.lastIndexOf("@");
86
+ return at > 0 ? specifier.slice(0, at) : specifier;
87
+ }
88
+ function readOptionRefs(raw) {
89
+ if (typeof raw !== "object" || raw === null) return {};
90
+ const refs = {};
91
+ for (const [name, value] of Object.entries(raw)) {
92
+ if (typeof value !== "object" || value === null) continue;
93
+ const entry = value;
94
+ const pkg = entry["package"];
95
+ const exported = entry["export"];
96
+ if (typeof pkg === "string" && pkg !== "" && typeof exported === "string" && exported !== "") {
97
+ refs[name] = { package: pkg, export: exported };
98
+ }
99
+ }
100
+ return refs;
101
+ }
102
+ function bindingNameFor(register) {
103
+ const bare = register.replace(/^register/, "");
104
+ return bare === "" ? "handle" : bare[0].toLowerCase() + bare.slice(1);
105
+ }
106
+ function readInstalledManifest(dir, pkg) {
107
+ const path = join(dir, "node_modules", ...pkg.split("/"), "package.json");
108
+ if (!existsSync(path)) return null;
109
+ try {
110
+ return JSON.parse(readFileSync(path, "utf8"));
111
+ } catch {
112
+ return null;
113
+ }
114
+ }
115
+ function entryFileOf(dir, pkg, manifest) {
116
+ const root = join(dir, "node_modules", ...pkg.split("/"));
117
+ const exports = manifest["exports"];
118
+ const dot = typeof exports === "object" && exports !== null ? exports["."] : void 0;
119
+ const nested = typeof dot === "object" && dot !== null ? dot["import"] ?? dot["default"] : typeof dot === "string" ? dot : void 0;
120
+ const candidate = nested ?? manifest["module"] ?? manifest["main"];
121
+ if (typeof candidate !== "string") return null;
122
+ const file = join(root, candidate);
123
+ return existsSync(file) ? file : null;
124
+ }
125
+ async function readRegistration(dir, pkg) {
126
+ const manifest = readInstalledManifest(dir, pkg);
127
+ if (manifest === null) return { unwired: "it is not in node_modules after the install" };
128
+ const declared = manifest["xanots"];
129
+ if (typeof declared === "object" && declared !== null) {
130
+ const field = declared;
131
+ const register = field["register"];
132
+ if (typeof register === "string" && register !== "") {
133
+ return {
134
+ pkg,
135
+ register,
136
+ binding: field["returns"] === "handle" ? bindingNameFor(register) : null,
137
+ options: readOptionRefs(field["options"]),
138
+ source: "manifest"
139
+ };
140
+ }
141
+ }
142
+ const entry = entryFileOf(dir, pkg, manifest);
143
+ if (entry === null) return { unwired: "its package.json names no entry we could read" };
144
+ let names;
145
+ try {
146
+ const loaded = await import(pathToFileURL(entry).href);
147
+ names = Object.keys(loaded).filter(
148
+ (name) => /^register[A-Z]/.test(name) && typeof loaded[name] === "function"
149
+ );
150
+ } catch (error) {
151
+ return { unwired: `importing it failed (${error instanceof Error ? error.message : String(error)})` };
152
+ }
153
+ if (names.length === 0) {
154
+ return {
155
+ unwired: `it exports no register* function, and its package.json has no "xanots" field`
156
+ };
157
+ }
158
+ if (names.length > 1) {
159
+ return {
160
+ unwired: `it exports ${names.length} register* functions (${names.join(", ")}) \u2014 its package.json needs a "xanots" field naming the one to call`
161
+ };
162
+ }
163
+ return { pkg, register: names[0], binding: null, options: {}, source: "exports" };
164
+ }
165
+ function renderXanoIndexWithModules(appName, modules) {
166
+ const byPackage = /* @__PURE__ */ new Map();
167
+ const add = (pkg, name) => {
168
+ const names = byPackage.get(pkg) ?? /* @__PURE__ */ new Set();
169
+ names.add(name);
170
+ byPackage.set(pkg, names);
171
+ };
172
+ for (const m of modules) {
173
+ add(m.pkg, m.register);
174
+ for (const ref of Object.values(m.options)) add(ref.package, ref.export);
175
+ }
176
+ const imports = [...byPackage].map(([pkg, names]) => `import { ${[...names].sort().join(", ")} } from "${pkg}";`).join("\n");
177
+ const calls = modules.map((m) => {
178
+ const entries = Object.entries(m.options);
179
+ const opts = entries.length === 0 ? "" : `, { ${entries.map(([name, ref]) => `${name}: ${ref.export}`).join(", ")} }`;
180
+ const call = `${m.register}(app${opts});`;
181
+ return m.binding === null ? call : `const ${m.binding} = ${call}`;
182
+ }).join("\n");
183
+ const handles = modules.filter((m) => m.binding !== null);
184
+ const handleNote = handles.length === 0 ? "" : `
185
+ *
186
+ * ${handles.map((m) => `\`${m.binding}\` is ${m.pkg}'s handle \u2014 its endpoints and helpers.`).join("\n * ")}`;
187
+ return `import { workspace } from "@xanots/sdk";
188
+ ${imports}
189
+
190
+ /**
191
+ * The ${appName} backend.
192
+ *
193
+ * A workspace is assembled by registering typed objects onto a workspace()
194
+ * instance and default-exporting it. The add-ons below were installed by
195
+ * \`xanots init --marketplace\` and registered for you.
196
+ *
197
+ * Each register* call mutates the workspace, so the order does not matter and
198
+ * the thing exported is the workspace itself. Add your own tables and endpoints
199
+ * beside them \u2014 xano/EXAMPLE.md walks through the first one.${handleNote}
200
+ */
201
+ const app = workspace("${appName}");
202
+
203
+ ${calls}
204
+
205
+ export default app;
206
+ `;
207
+ }
208
+ function isPeerConflict(stderr) {
209
+ return /ERESOLVE|peer dep|peerinvalid/i.test(stderr);
210
+ }
211
+ async function installModules(dir, packages) {
212
+ const installed = [];
213
+ const failed = [];
214
+ const legacyPeer = [];
215
+ const registered = [];
216
+ const unwired = [];
217
+ for (const specifier of packages) {
218
+ step(`Adding ${specifier}`);
219
+ let result = runNpmCapturingStderr(["install", specifier], dir);
220
+ if (result.status !== 0 && isPeerConflict(result.stderr)) {
221
+ warn(
222
+ `${specifier} declares a peer range this CLI does not satisfy \u2014 retrying with --legacy-peer-deps.`
223
+ );
224
+ result = runNpmCapturingStderr(["install", specifier, "--legacy-peer-deps"], dir);
225
+ if (result.status === 0) legacyPeer.push(specifier);
226
+ }
227
+ if (result.status !== 0) {
228
+ failed.push(specifier);
229
+ warn(`npm install ${specifier} failed \u2014 the module was not added.`);
230
+ continue;
231
+ }
232
+ installed.push(specifier);
233
+ const pkg = packageNameOf(specifier);
234
+ const registration = await readRegistration(dir, pkg);
235
+ if ("unwired" in registration) unwired.push({ pkg, reason: registration.unwired });
236
+ else registered.push(registration);
237
+ }
238
+ const present = new Set(installed.map(packageNameOf));
239
+ const writable = [];
240
+ for (const registration of registered) {
241
+ const missing = Object.values(registration.options).map((ref) => ref.package).filter((pkg) => !present.has(pkg));
242
+ if (missing.length === 0) {
243
+ writable.push(registration);
244
+ continue;
245
+ }
246
+ unwired.push({
247
+ pkg: registration.pkg,
248
+ reason: `it needs ${[...new Set(missing)].join(", ")}, which this project does not have`
249
+ });
250
+ }
251
+ return { installed, failed, legacyPeer, registered: writable, unwired };
252
+ }
253
+
254
+ // src/emit/frontend-resolve.ts
255
+ import { createInterface } from "readline/promises";
256
+ function defaultFrontendPreset() {
257
+ const preset = findFrontendPreset(DEFAULT_FRAMEWORK);
258
+ if (preset === void 0) {
259
+ throw new Error(`Default frontend preset "${DEFAULT_FRAMEWORK}" is missing from the table.`);
260
+ }
261
+ return preset;
262
+ }
263
+ async function promptFramework() {
264
+ const presets = allFrontendPresets();
265
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
266
+ try {
267
+ process.stderr.write(
268
+ `
269
+ Which frontend framework?
270
+ ` + presets.map((p, i) => ` ${i + 1}) ${p.label}${p.id === DEFAULT_FRAMEWORK ? " (default)" : ""}`).join("\n") + `
271
+ `
272
+ );
273
+ const answer = await rl.question(`Enter a number or name, or leave blank for the default: `);
274
+ const token = answer.trim().toLowerCase();
275
+ if (token === "") return defaultFrontendPreset();
276
+ const byIndex = Number.parseInt(token, 10);
277
+ if (Number.isInteger(byIndex) && byIndex >= 1 && byIndex <= presets.length) {
278
+ return presets[byIndex - 1];
279
+ }
280
+ return findFrontendPreset(token) ?? defaultFrontendPreset();
281
+ } finally {
282
+ rl.close();
283
+ }
284
+ }
285
+ async function resolveFrontendPreset(flag) {
286
+ if (flag !== void 0 && flag !== "") return resolveFrameworkFlag(flag);
287
+ if (process.stdin.isTTY && process.stderr.isTTY) return promptFramework();
288
+ return defaultFrontendPreset();
289
+ }
290
+
291
+ // src/emit/theme-resolve.ts
292
+ import { createInterface as createInterface2 } from "readline/promises";
293
+ import { readFileSync as readFileSync2 } from "fs";
294
+ import { resolve as resolvePath } from "path";
295
+ function isRegistryRef(value) {
296
+ return /^https?:\/\//i.test(value) || value.toLowerCase().endsWith(".json");
297
+ }
298
+ async function loadRegistryTheme(ref) {
299
+ if (/^https?:\/\//i.test(ref)) {
300
+ let res;
301
+ try {
302
+ res = await fetch(ref, { headers: { accept: "application/json" } });
303
+ } catch (err) {
304
+ throw new Error(
305
+ `Could not fetch the theme at ${ref}: ${err instanceof Error ? err.message : String(err)}`
306
+ );
307
+ }
308
+ if (!res.ok) throw new Error(`Could not fetch the theme at ${ref}: ${res.status} ${res.statusText}`);
309
+ const text2 = await res.text();
310
+ return themeFromRegistryItem(parseJson(text2, ref), ref);
311
+ }
312
+ const path = resolvePath(ref);
313
+ let text;
314
+ try {
315
+ text = readFileSync2(path, "utf8");
316
+ } catch (err) {
317
+ throw new Error(
318
+ `Could not read the theme file ${path}: ${err instanceof Error ? err.message : String(err)}`
319
+ );
320
+ }
321
+ return themeFromRegistryItem(parseJson(text, path), path);
322
+ }
323
+ function parseJson(text, source) {
324
+ try {
325
+ return JSON.parse(text);
326
+ } catch {
327
+ throw new Error(`${source} is not valid JSON \u2014 a registry theme is a JSON registry item.`);
328
+ }
329
+ }
330
+ async function themeFromFlag(raw) {
331
+ return isRegistryRef(raw.trim()) ? loadRegistryTheme(raw.trim()) : resolveThemeFlag(raw);
332
+ }
333
+ function swatch(theme, color) {
334
+ if (!color) return "";
335
+ const blocks = themeSwatch(theme).map(({ r, g, b }) => `\x1B[38;2;${r};${g};${b}m\u2588\x1B[0m`).join("");
336
+ return blocks === "" ? "" : `${blocks} `;
337
+ }
338
+ function colorEnabled() {
339
+ if (process.env.FORCE_COLOR) return process.env.FORCE_COLOR !== "0";
340
+ return !process.env.NO_COLOR && process.stderr.isTTY === true;
341
+ }
342
+ async function promptTheme() {
343
+ const themes = curatedThemes();
344
+ const color = colorEnabled();
345
+ const rl = createInterface2({ input: process.stdin, output: process.stderr });
346
+ try {
347
+ process.stderr.write(
348
+ `
349
+ Which theme? (shadcn/ui color tokens \u2014 everything else is identical)
350
+ ` + themes.map((t, i) => ` ${i + 1}) ${swatch(t, color)}${t.label}${i === 0 ? " (default)" : ""}`).join("\n") + `
351
+ ${style.dim(
352
+ ` \u2026or any <base>-<accent> pair, or a registry theme URL. See \`xanots init --help\`.`
353
+ )}
354
+ `
355
+ );
356
+ for (; ; ) {
357
+ const answer = (await rl.question(`Enter a number, a theme, or leave blank for the default: `)).trim();
358
+ if (answer === "") return defaultTheme();
359
+ const byIndex = Number.parseInt(answer, 10);
360
+ if (Number.isInteger(byIndex) && byIndex >= 1 && byIndex <= themes.length) {
361
+ return themes[byIndex - 1];
362
+ }
363
+ if (!isRegistryRef(answer)) {
364
+ try {
365
+ return resolveThemeFlag(answer);
366
+ } catch {
367
+ return defaultTheme();
368
+ }
369
+ }
370
+ try {
371
+ return await loadRegistryTheme(answer);
372
+ } catch (err) {
373
+ process.stderr.write(`${style.yellow("!")} ${err instanceof Error ? err.message : String(err)}
374
+ `);
375
+ }
376
+ }
377
+ } finally {
378
+ rl.close();
379
+ }
380
+ }
381
+ async function resolveThemeChoice(args) {
382
+ const dark = args.dark === void 0 || args.dark === "" ? DEFAULT_DARK_MODE : resolveDarkModeFlag(args.dark);
383
+ const fonts = {
384
+ ...args.font !== void 0 && args.font !== "" ? { sans: resolveFontFlag(args.font, "sans") } : {},
385
+ ...args.fontMono !== void 0 && args.fontMono !== "" ? { mono: resolveFontFlag(args.fontMono, "mono") } : {},
386
+ ...args.fontHeading !== void 0 && args.fontHeading !== "" ? { heading: resolveFontFlag(args.fontHeading, "heading") } : {}
387
+ };
388
+ const icons = args.icons === void 0 || args.icons === "" ? void 0 : resolveIconsFlag(args.icons).id;
389
+ let theme;
390
+ if (args.theme !== void 0 && args.theme !== "") {
391
+ theme = await themeFromFlag(args.theme);
392
+ } else if (process.stdin.isTTY && process.stderr.isTTY) {
393
+ theme = await promptTheme();
394
+ } else {
395
+ theme = defaultThemeChoice().theme;
396
+ }
397
+ if (args.radius !== void 0 && args.radius !== "") {
398
+ theme = withRadius(theme, normalizeRadius(args.radius));
399
+ }
400
+ return {
401
+ theme,
402
+ dark,
403
+ ...Object.keys(fonts).length === 0 ? {} : { fonts },
404
+ ...icons === void 0 ? {} : { icons }
405
+ };
406
+ }
407
+
408
+ // src/emit/init-command.ts
409
+ function sanitizeAppName(raw) {
410
+ const name = raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
411
+ return name === "" ? "app" : name;
412
+ }
413
+ function projectShellFiles(vars, preset, parts, choice = defaultThemeChoice()) {
414
+ return [
415
+ { path: "package.json", content: renderPackageJson(vars, preset, choice) },
416
+ { path: "tsconfig.json", content: renderTsconfig(preset) },
417
+ { path: "vite.config.ts", content: renderViteConfig(preset) },
418
+ { path: ".gitignore", content: renderGitignore(preset) },
419
+ { path: ".env.example", content: renderEnvExample() },
420
+ { path: "README.md", content: parts.readme },
421
+ // Skipped when the framework supplies its own entry document — see
422
+ // FrontendPreset.ownsHtmlEntry. A second index.html beside SvelteKit's
423
+ // app.html would never be served and would still read as the entry point.
424
+ ...preset.ownsHtmlEntry === true ? [] : [{ path: "frontend/index.html", content: renderIndexHtml(vars, preset, choice) }],
425
+ // Shared across every framework: both UI kits resolve against the same
426
+ // Tailwind v4 semantic tokens, back `cn()` with clsx + tailwind-merge, and
427
+ // look for library code at frontend/src/lib/. Only the alias NAME pointing
428
+ // at that directory differs, and the preset owns that.
429
+ { path: "frontend/src/index.css", content: renderIndexCss(choice) },
430
+ { path: "frontend/src/lib/api.ts", content: renderApiTs() },
431
+ { path: "frontend/src/lib/utils.ts", content: renderCnUtil(preset.libUtilsExtra) },
432
+ // The mode store behind `--dark toggle`, shared by both presets' toggles —
433
+ // it is plain DOM and localStorage, with nothing framework-shaped in it.
434
+ // `system` and `off` need no state, so nothing is written for them.
435
+ ...choice.dark === "toggle" ? [{ path: "frontend/src/lib/theme.ts", content: renderThemeModule() }] : [],
436
+ // The framework's own half: entry module, app component, vendored UI kit,
437
+ // and whatever tool config that kit's CLI reads.
438
+ ...preset.files(vars, parts.landing, choice)
439
+ ];
440
+ }
441
+ function buildFileSet(vars, preset, choice) {
442
+ return [
443
+ ...projectShellFiles(
444
+ vars,
445
+ preset,
446
+ { readme: renderReadme(vars, preset, choice), landing: initLanding(vars) },
447
+ choice
448
+ ),
449
+ { path: "xano/index.ts", content: renderXanoIndex(vars) },
450
+ { path: "xano/EXAMPLE.md", content: renderXanoExampleMd(vars) }
451
+ ];
452
+ }
453
+ async function runInitCommand(args) {
454
+ const targetArg = args.positionals[0] ?? ".";
455
+ const targetDir = resolve(targetArg);
456
+ const appName = sanitizeAppName(args.name ?? basename(targetDir));
457
+ const preset = await resolveFrontendPreset(args.framework);
458
+ const choice = await resolveThemeChoice(args);
459
+ const presets = await resolveAiPresets(args.ai);
460
+ const vars = { appName, sdkVersion: readVersion() };
461
+ step(
462
+ `Scaffolding ${style.bold(appName)} (${preset.label}, ${choice.theme.label}) in ${targetDir}`
463
+ );
464
+ const result = await scaffoldProject({
465
+ targetDir,
466
+ files: buildFileSet(vars, preset, choice),
467
+ presets,
468
+ appName,
469
+ force: args.force,
470
+ noInstall: args.noInstall,
471
+ // An `init` project's `xano/` is hand-authored, never machine-written: it is
472
+ // not refreshable, and `--force` must not clear it.
473
+ regenerable: false,
474
+ sdkVersion: readVersion(),
475
+ frontend: preset,
476
+ theme: choice
477
+ });
478
+ info(`Theme: ${choice.theme.label} \u2014 dark mode: ${choice.dark}`);
479
+ if (presets.length > 0) {
480
+ info(`AI instructions: ${presets.map(presetFilePath).join(", ")}`);
481
+ }
482
+ const modules = parseMarketplaceFlag(args.marketplace);
483
+ if (modules.length > 0) {
484
+ blank();
485
+ const outcome = await installModules(targetDir, modules);
486
+ if (outcome.registered.length > 0) {
487
+ writeFileSync(
488
+ join2(targetDir, "xano", "index.ts"),
489
+ renderXanoIndexWithModules(appName, outcome.registered),
490
+ "utf8"
491
+ );
492
+ info(
493
+ `Registered in xano/index.ts: ${outcome.registered.map((m) => m.pkg).join(", ")}`
494
+ );
495
+ }
496
+ for (const module of outcome.unwired) {
497
+ warn(`${module.pkg} is installed but not registered \u2014 ${module.reason}.`);
498
+ detail(`Import it in xano/index.ts and register it onto the workspace yourself.`);
499
+ }
500
+ if (outcome.legacyPeer.length > 0) {
501
+ warn(
502
+ `Installed with --legacy-peer-deps: ${outcome.legacyPeer.join(", ")}. npm refused the tree on peer ranges, so this one is not the tree npm would build on its own.`
503
+ );
504
+ }
505
+ }
506
+ blank();
507
+ success("Project ready.");
508
+ const cdHint = targetDir === process.cwd() ? "" : ` cd ${targetArg}
509
+ `;
510
+ detail(
511
+ `Next steps:
512
+ ` + cdHint + (result.install === "installed" ? `` : ` npm install
513
+ `) + ` npm run dev # run the frontend
514
+ xanots login # authenticate with Xano
515
+ npm run xano:deploy # build the frontend, then deploy \u2192 live ephemeral URL
516
+
517
+ The first export or deploy writes xano/xano.lock \u2014 commit it. It pins each
518
+ object's identity, so renaming one later renames it instead of deleting and
519
+ recreating it. See "xano.lock \u2014 commit it" in the README.`
520
+ );
521
+ }
522
+
523
+ export {
524
+ resolveFrontendPreset,
525
+ resolveThemeChoice,
526
+ sanitizeAppName,
527
+ projectShellFiles,
528
+ runInitCommand
529
+ };
530
+ //# sourceMappingURL=chunk-HQ2CBRVI.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  compileBundle
3
- } from "./chunk-UVLVQWWF.js";
3
+ } from "./chunk-3NFUWXOC.js";
4
4
  import {
5
5
  missingArgument
6
- } from "./chunk-IZW6MK3U.js";
6
+ } from "./chunk-6USV65XA.js";
7
7
 
8
8
  // src/emit/bundle-input.ts
9
9
  import { existsSync, readFileSync } from "fs";
@@ -34,4 +34,4 @@ export {
34
34
  assertBundleInput,
35
35
  loadBundleText
36
36
  };
37
- //# sourceMappingURL=chunk-ZPEY5WE4.js.map
37
+ //# sourceMappingURL=chunk-W2G2WPTB.js.map
@@ -0,0 +1,25 @@
1
+ // src/emit/npm.ts
2
+ import { spawnSync } from "child_process";
3
+ function npmBinary() {
4
+ return process.platform === "win32" ? "npm.cmd" : "npm";
5
+ }
6
+ function runNpm(args, cwd) {
7
+ const res = spawnSync(npmBinary(), [...args], { cwd, stdio: "inherit" });
8
+ return res.status ?? 1;
9
+ }
10
+ function runNpmCapturingStderr(args, cwd) {
11
+ const res = spawnSync(npmBinary(), [...args], {
12
+ cwd,
13
+ stdio: ["inherit", "inherit", "pipe"],
14
+ encoding: "utf8"
15
+ });
16
+ const stderr = res.stderr ?? "";
17
+ if (stderr !== "") process.stderr.write(stderr);
18
+ return { status: res.status ?? 1, stderr };
19
+ }
20
+
21
+ export {
22
+ runNpm,
23
+ runNpmCapturingStderr
24
+ };
25
+ //# sourceMappingURL=chunk-WUSKBXXD.js.map