@xano/sdk 1.0.5 → 1.0.7
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 +35 -5
- package/README.md +56 -20
- package/dist/.build-fingerprint +1 -1
- package/dist/{agent-file-refresh-KVLR7M5Y.js → agent-file-refresh-DGGSRPJ4.js} +15 -14
- package/dist/agent-skill-command-37DFYS7Q.js +414 -0
- package/dist/{answer-shape-XGR2DGWY.js → answer-shape-RBFDCNZ7.js} +5 -5
- package/dist/{backend-dir-IE4RMUFH.js → backend-dir-KDD43X7K.js} +16 -15
- package/dist/{backend-slot-6HBWG5FN.js → backend-slot-TBOVZXDE.js} +4 -4
- package/dist/bin.js +63 -61
- package/dist/{branch-T2APKPVG.js → branch-7PIKNUD7.js} +12 -11
- package/dist/{branch-commands-HXXTCSOO.js → branch-commands-EGY7ZEZE.js} +32 -32
- package/dist/bundle.js +9 -39
- package/dist/{capture-HBXFXZEW.js → capture-GW5KNKMB.js} +3 -3
- package/dist/{chunk-3BJYQGAZ.js → chunk-26LHXLD3.js} +2 -2
- package/dist/{chunk-N6BWB35H.js → chunk-2ACWDSAQ.js} +3 -3
- package/dist/{chunk-XIABDKFQ.js → chunk-2SD4LA22.js} +2 -2
- package/dist/{chunk-K6PRKPFX.js → chunk-2XWVZRL6.js} +7 -7
- package/dist/{chunk-ES5A66IH.js → chunk-2YER2J47.js} +13 -13
- package/dist/{chunk-G57RYMKB.js → chunk-3FRGNBOC.js} +2 -2
- package/dist/{chunk-BMCZQZMI.js → chunk-46PATL23.js} +13 -9
- package/dist/{chunk-MYLPOVX6.js → chunk-47XT6WQL.js} +8 -8
- package/dist/{chunk-AWQ7BQTQ.js → chunk-4GPIOIAC.js} +6 -1
- package/dist/{chunk-7GX445AE.js → chunk-4XHAT7C5.js} +2 -2
- package/dist/{chunk-QOLOQ35F.js → chunk-567KK6XF.js} +8 -8
- package/dist/{chunk-57HMNJGB.js → chunk-5GIOCDUM.js} +893 -922
- package/dist/{chunk-OKNE3TWU.js → chunk-5KJBWIDZ.js} +83 -5
- package/dist/{chunk-SRX6B544.js → chunk-5QMJOE2K.js} +5 -5
- package/dist/{chunk-AGD7F7YI.js → chunk-5SCPRXFB.js} +4 -4
- package/dist/{chunk-SNGFEESM.js → chunk-5ZG7V2VW.js} +3 -3
- package/dist/{chunk-XJU6UAFV.js → chunk-7FIMUFBP.js} +2 -2
- package/dist/{chunk-AESE434J.js → chunk-7M6JP3FS.js} +2 -2
- package/dist/{chunk-K6CB5TKM.js → chunk-7REDHC4N.js} +2 -2
- package/dist/chunk-7VR4IO5B.js +44 -0
- package/dist/{chunk-57B56I4B.js → chunk-7XXWKTCE.js} +65 -41
- package/dist/{chunk-PL7AIAWS.js → chunk-A4R5CAY4.js} +4 -111
- package/dist/{chunk-QZTJ2556.js → chunk-AKERAUNH.js} +4 -4
- package/dist/{chunk-JZWUAEMO.js → chunk-ARWMVNDJ.js} +6 -3
- package/dist/{chunk-WYS6XVZ3.js → chunk-AVS4WPPY.js} +2 -2
- package/dist/{chunk-KUU7T4ZE.js → chunk-B2URJBC4.js} +18 -18
- package/dist/chunk-B3P25SDP.js +78 -0
- package/dist/chunk-BRANWM4U.js +853 -0
- package/dist/{chunk-BBPAXOU6.js → chunk-BSVQEKHO.js} +6 -6
- package/dist/{chunk-3WHWOKU4.js → chunk-BZ6WTG3E.js} +4 -4
- package/dist/{chunk-XNZURZ7T.js → chunk-CH23ONYG.js} +77 -6
- package/dist/{chunk-SQPIECXD.js → chunk-CJDPSCMX.js} +45 -5
- package/dist/{chunk-M7GJ6YUF.js → chunk-CNZASQPL.js} +16 -2
- package/dist/{chunk-44CWK6YC.js → chunk-CPN6WSBH.js} +4 -4
- package/dist/{chunk-L3BDO2BI.js → chunk-CT3J5JRZ.js} +7 -7
- package/dist/{chunk-3HOGFGKG.js → chunk-D3KEPRRN.js} +2 -2
- package/dist/{chunk-OGUMHW2X.js → chunk-D5OZRJ3T.js} +7 -7
- package/dist/{chunk-S5KQIFKJ.js → chunk-DG3APBUJ.js} +208 -112
- package/dist/chunk-DIWF7L33.js +995 -0
- package/dist/{chunk-WRBKLYSJ.js → chunk-DNBP4GJN.js} +4 -3
- package/dist/{chunk-LZDA6SVD.js → chunk-DXZTL7NA.js} +141 -91
- package/dist/{chunk-XMDDBEEM.js → chunk-E763QI3G.js} +2 -2
- package/dist/{chunk-3VX7VAQT.js → chunk-EF4MHLEW.js} +26 -9
- package/dist/{chunk-AGSCOISO.js → chunk-ELPQTDW6.js} +108 -100
- package/dist/{chunk-Q22BMRPF.js → chunk-EPFDY5JU.js} +2 -2
- package/dist/{chunk-7DBM6XHR.js → chunk-EU32BC5B.js} +8 -8
- package/dist/{chunk-Y76ERHLK.js → chunk-FDTET7FZ.js} +6 -6
- package/dist/{chunk-Z3SNX45M.js → chunk-FENA7K7C.js} +3 -3
- package/dist/{chunk-WOGJB2F6.js → chunk-FNLTFOUD.js} +4 -9
- package/dist/{chunk-FV55SRQU.js → chunk-FQ53GTII.js} +9 -9
- package/dist/{chunk-DEVGLLNZ.js → chunk-GHMLRFRX.js} +8 -8
- package/dist/{chunk-GMVW6ZZK.js → chunk-H2K4GFUS.js} +4 -4
- package/dist/{chunk-NUU76247.js → chunk-HTQWAQRW.js} +3 -3
- package/dist/{chunk-ROY4YAEQ.js → chunk-HU7F56VF.js} +22 -2
- package/dist/{chunk-RXCT3OCU.js → chunk-HZ6ADG3O.js} +47 -8
- package/dist/{chunk-Z2BRSQ6M.js → chunk-I6XRUU6U.js} +91 -64
- package/dist/chunk-IPNN2KRW.js +257 -0
- package/dist/{chunk-SF2B7MZK.js → chunk-IYXL5NYU.js} +259 -31
- package/dist/chunk-JI3BLXWQ.js +107 -0
- package/dist/{chunk-4DJTQ6W4.js → chunk-JIKGT7DO.js} +2 -1
- package/dist/{chunk-OVV2HXJ6.js → chunk-JOOKM7AH.js} +2 -2
- package/dist/{chunk-65SW2BA5.js → chunk-JOWY6HFL.js} +3 -3
- package/dist/{chunk-I5VEROXW.js → chunk-JX6N73H3.js} +2 -2
- package/dist/{chunk-5D6DGF3W.js → chunk-L472YIGG.js} +8 -8
- package/dist/{chunk-HLQH3AXB.js → chunk-L6HZ67P6.js} +2 -2
- package/dist/{chunk-TM5LRSBB.js → chunk-LBJA7IEM.js} +370 -45
- package/dist/chunk-LCOCRJ4K.js +116 -0
- package/dist/{chunk-DGWHAVRG.js → chunk-LJM5CIY4.js} +16 -1
- package/dist/{chunk-5RLAF6RL.js → chunk-M2C54ZBC.js} +4 -4
- package/dist/{chunk-U6DR6464.js → chunk-M3NEK3LT.js} +4 -4
- package/dist/{chunk-5XVBT6CQ.js → chunk-MBL7CXJ7.js} +2 -2
- package/dist/{chunk-5YBNXNKU.js → chunk-MFWF2NZJ.js} +12 -5
- package/dist/{chunk-TEBS7FE6.js → chunk-MYZRMSPM.js} +2 -2
- package/dist/{chunk-YSLMMR2U.js → chunk-N3YFCFCO.js} +14 -14
- package/dist/{chunk-OJPZEUPH.js → chunk-NDJAKSE7.js} +11 -8
- package/dist/{chunk-PIPIA7K7.js → chunk-NV37YGOV.js} +7 -7
- package/dist/{chunk-ZHGWR45B.js → chunk-O56DQKPL.js} +9 -9
- package/dist/{chunk-4QBGE3EO.js → chunk-ORGNLQN6.js} +6 -6
- package/dist/{chunk-NNB7VUCH.js → chunk-PEIBGH2V.js} +55 -31
- package/dist/{chunk-UJ6ZZJDD.js → chunk-PEX4QZVI.js} +2 -2
- package/dist/{chunk-LLS2WO2K.js → chunk-PV25BDCH.js} +5 -5
- package/dist/{chunk-F3557PVM.js → chunk-PY722CHR.js} +2 -2
- package/dist/{chunk-4GDEEY2U.js → chunk-QGIZLFAS.js} +4 -4
- package/dist/{chunk-YI76TURL.js → chunk-QRQ54LUI.js} +16 -14
- package/dist/{chunk-JQOX6HW4.js → chunk-RBU2YX22.js} +3 -3
- package/dist/{chunk-4NEBBKOP.js → chunk-RRMH7WMY.js} +7 -7
- package/dist/{chunk-HOOOOJJP.js → chunk-S46ODQRA.js} +2 -2
- package/dist/{chunk-37FBPMNE.js → chunk-S56YBYMN.js} +5 -2
- package/dist/{chunk-4QFBEKXI.js → chunk-SDGV76HF.js} +2 -2
- package/dist/{chunk-CV54PASI.js → chunk-SGK3PTTM.js} +312 -132
- package/dist/{chunk-S5TEJIGP.js → chunk-SKEG3V2V.js} +2 -2
- package/dist/{chunk-VZ22VUAS.js → chunk-SLGJIWCY.js} +3 -3
- package/dist/{chunk-JSEVKRVJ.js → chunk-T4LJ5UR2.js} +29 -7
- package/dist/{chunk-JXKO6JT7.js → chunk-TWVUYS63.js} +24 -24
- package/dist/{chunk-UIEFE6FU.js → chunk-U5M7T6CI.js} +7 -7
- package/dist/{chunk-Z4VN347O.js → chunk-UYFUIHH3.js} +2 -2
- package/dist/{chunk-Y6NO3DLQ.js → chunk-V7MBRUK6.js} +3 -3
- package/dist/chunk-VAGSKUWC.js +67 -0
- package/dist/{chunk-A4NNCI4O.js → chunk-VIJ5D3RC.js} +3 -3
- package/dist/{chunk-3IYVHVM6.js → chunk-VJFMUJWZ.js} +4 -4
- package/dist/{chunk-V4IBHDIY.js → chunk-VNDR2T35.js} +7 -7
- package/dist/chunk-WAVNUTMT.js +9 -0
- package/dist/{chunk-KQLACS4Q.js → chunk-WNCW2RNB.js} +4 -4
- package/dist/chunk-WUEPP5HD.js +111 -0
- package/dist/{chunk-IV446ZLJ.js → chunk-Y453TW6J.js} +2 -2
- package/dist/{local-engine-command-73NHWD6V.js → chunk-YCUDOF6Q.js} +22 -76
- package/dist/{chunk-CJ76PSJT.js → chunk-YH2JVJAO.js} +12 -12
- package/dist/{chunk-GPI3OWRE.js → chunk-YHB34BN6.js} +2 -1
- package/dist/{chunk-AMBEITWL.js → chunk-ZGLNHA7T.js} +7 -7
- package/dist/{chunk-NGAODDLX.js → chunk-ZJOAWRED.js} +8 -8
- package/dist/{chunk-TUEFTRC2.js → chunk-ZXRDD3Q2.js} +50 -27
- package/dist/cli.d.ts +40 -11
- package/dist/cli.js +60 -56
- package/dist/{codegen-command-L2OO6OTE.js → codegen-command-T7TC7PQO.js} +79 -72
- package/dist/codegen.d.ts +2 -2
- package/dist/codegen.js +2 -2
- package/dist/{commands-2E4JB3DB.js → commands-JDENTDJS.js} +3 -3
- package/dist/{completion-LPJIH5VQ.js → completion-YFD5OVJW.js} +9 -5
- package/dist/{config-QDCRKEWD.js → config-YJFUTRJW.js} +8 -8
- package/dist/{deploy-command-HJZNYCUY.js → deploy-command-OEVEE6UG.js} +65 -63
- package/dist/deploy-source-HYVZEYXV.js +83 -0
- package/dist/{dev-env-sync-GZ4QQJGV.js → dev-env-sync-EFDCJ6KU.js} +63 -61
- package/dist/{diagnostics-LSMGB4TY.js → diagnostics-OMN5Y2AH.js} +2 -2
- package/dist/{display-name-hint-AHCNH6TF.js → display-name-hint-SW3GB3V7.js} +14 -13
- package/dist/{emit-DqG6qddr.d.ts → emit-Dov_92kn.d.ts} +66 -20
- package/dist/env-example-refresh-WNSOH534.js +134 -0
- package/dist/{env-pull-command-Z4RLVEGJ.js → env-pull-command-UYEXMQ3P.js} +53 -52
- package/dist/{env-target-ZHQUS5BO.js → env-target-JBSLBKNX.js} +15 -14
- package/dist/{env-var-command-FKQ4VVQ6.js → env-var-command-BJNRAXNV.js} +51 -49
- package/dist/{ephemeral-L4KUOGEU.js → ephemeral-CEOGMIEA.js} +11 -10
- package/dist/{ephemeral-command-GRPFI3TD.js → ephemeral-command-IPSGBCS5.js} +70 -69
- package/dist/{ephemeral-state-VLYU2BXR.js → ephemeral-state-MTBASPRS.js} +13 -12
- package/dist/{generate-command-XNODJFDQ.js → generate-command-XQXODBS6.js} +96 -86
- package/dist/{gitignore-VWRSZX5F.js → gitignore-RUVCIZO4.js} +21 -20
- package/dist/{guards-OPSGB6LG.js → guards-KYEOCVHN.js} +6 -6
- package/dist/{impersonate-command-AEKIZHHK.js → impersonate-command-MA27O7S5.js} +46 -44
- package/dist/{import-PHSSFFLA.js → import-XKRFHABE.js} +5 -5
- package/dist/index.d.ts +14 -20
- package/dist/index.js +13 -13
- package/dist/init-command-6RDTCMAB.js +143 -0
- package/dist/{init-modules-SGSX7XEY.js → init-modules-DHTBOCQT.js} +10 -9
- package/dist/{init-web-6HX3YLQP.js → init-web-ELBXSXDL.js} +3 -3
- package/dist/internal.d.ts +5 -5
- package/dist/internal.js +47 -20
- package/dist/{io-TAF4WXYQ.js → io-PGTCBSIQ.js} +6 -4
- package/dist/{keep-data-merge-GUGF7MVZ.js → keep-data-merge-TDJOIAVT.js} +33 -34
- package/dist/{lambda-config-reconcile-65U6I4KU.js → lambda-config-reconcile-47X5C2E4.js} +6 -5
- package/dist/{landed-QA6PRTV2.js → landed-F7PCHGCK.js} +3 -3
- package/dist/{landing-record-FCAWLCZU.js → landing-record-TPPYFQAS.js} +26 -25
- package/dist/{live-diff-IM4W3ELT.js → live-diff-5E2BQWQ7.js} +9 -8
- package/dist/local-engine-choice-S6VHPJRU.js +81 -0
- package/dist/local-engine-command-4EZCGZ6M.js +85 -0
- package/dist/{local-engine-config-W3KB7RWM.js → local-engine-config-2EBL3QKA.js} +3 -2
- package/dist/{local-engine-deploy-Y4OAWL7K.js → local-engine-deploy-FOPNWU7X.js} +18 -16
- package/dist/local-engine-lookup-GM3E3NPP.js +43 -0
- package/dist/local-engine-mcp-bridge-QKZHUGW3.js +423 -0
- package/dist/local-engine-mcp-command-BFQVLXZC.js +181 -0
- package/dist/{local-engine-process-6YPJHKYM.js → local-engine-process-XUQPQENQ.js} +12 -9
- package/dist/{local-engine-state-OCW5YDD7.js → local-engine-state-JQPKGB65.js} +4 -3
- package/dist/{local-engine-update-check-X7TZGRFL.js → local-engine-update-check-S7UF6ZF5.js} +7 -6
- package/dist/{lock-WXXC3QHL.js → lock-HDCHD2ON.js} +4 -2
- package/dist/{lock-commands-PKDBNRSD.js → lock-commands-HJ6Y5QVS.js} +66 -64
- package/dist/{login-command-V4U4FA5G.js → login-command-JO55NZAS.js} +34 -33
- package/dist/{logout-command-TV3P545Z.js → logout-command-MPRIRSJA.js} +18 -17
- package/dist/{loop-VKHGQ6MP.js → loop-SUB6C2LK.js} +8 -9
- package/dist/{marketplace-command-LEFOAEAV.js → marketplace-command-BM6G257N.js} +90 -54
- package/dist/{marketplace-resolve-ME7O7JBJ.js → marketplace-resolve-AWH3RG57.js} +7 -6
- package/dist/{meta-client-TEEECA5W.js → meta-client-AOSWODQQ.js} +16 -15
- package/dist/{module-manifest-7YLGIRCY.js → module-manifest-GXI46PEF.js} +7 -4
- package/dist/node.d.ts +5 -5
- package/dist/node.js +68 -67
- package/dist/operation-outcome-4UKPXTNC.js +53 -0
- package/dist/{output-target-5FWX3NGZ.js → output-target-RAZ4KN5J.js} +22 -21
- package/dist/{package-manager-PRAHUSCN.js → package-manager-K762W7XT.js} +2 -2
- package/dist/{plan-disclosure-QGL5UYQR.js → plan-disclosure-JR52DWXN.js} +21 -21
- package/dist/{plugin-contributions-GNFTWDEB.js → plugin-contributions-E6XHLQWK.js} +6 -6
- package/dist/{plugin-prompt-VEQGMZ4T.js → plugin-prompt-IODX5EU6.js} +5 -5
- package/dist/{plugin-questions-BGRL23GO.js → plugin-questions-AAAJ7POP.js} +4 -4
- package/dist/plugin.d.ts +254 -10
- package/dist/{preflight-command-VKJKXRRI.js → preflight-command-7GKJBWOQ.js} +71 -70
- package/dist/{profile-command-P7A3BH26.js → profile-command-CM6J7E2M.js} +35 -34
- package/dist/{profile-pointer-HG7IXE4I.js → profile-pointer-KTYXL6FU.js} +5 -4
- package/dist/{project-dependency-Z4CKOCJI.js → project-dependency-OQ5575QQ.js} +11 -8
- package/dist/{project-sdk-JWZWGXXR.js → project-sdk-2ESHHZVL.js} +4 -4
- package/dist/promote-command-BDRPG6UU.js +147 -0
- package/dist/{prompt-SURZOUUW.js → prompt-MQPGU4ZI.js} +4 -4
- package/dist/{publish-command-HGAB3H4A.js → publish-command-IEMYZ7LF.js} +69 -67
- package/dist/pull-command-S5E2ABZ4.js +169 -0
- package/dist/{pull-superseded-TOT5S4QU.js → pull-superseded-E34BZVLH.js} +4 -4
- package/dist/{raw-where-EoWl5fqY.d.ts → raw-where-ZMLND6AG.d.ts} +20 -3
- package/dist/release-command-7F5TBKO2.js +151 -0
- package/dist/{release-ns-command-WOQE2BDQ.js → release-ns-command-EK2ZQ4CC.js} +101 -100
- package/dist/{retry-command-JRGMZZAC.js → retry-command-GL5HCMVB.js} +10 -9
- package/dist/{routes-manifest-URFMAWJE.js → routes-manifest-YR4ZIMLM.js} +15 -7
- package/dist/routes-manifest-file-LCIKPDAG.js +41 -0
- package/dist/scaffold.js +4 -4
- package/dist/secrets-IV4N5HYI.js +20 -0
- package/dist/{secrets-file-YWZWVJ52.js → secrets-file-LXREYU3M.js} +21 -20
- package/dist/{secrets-fill-command-HIHK3VH2.js → secrets-fill-command-QHZS3KWM.js} +64 -62
- package/dist/{seed-24DTEY5S.js → seed-FRKMDN2Z.js} +9 -9
- package/dist/{source-resolve-OWD3MIEO.js → source-resolve-BBHYTOXI.js} +26 -24
- package/dist/{static-host-UIR2OLPQ.js → static-host-7LC7OZR4.js} +5 -5
- package/dist/{status-command-JKTHH5QE.js → status-command-TY253MJT.js} +54 -52
- package/dist/{store-zxw2go0f.d.ts → store-Boba9OOh.d.ts} +2 -2
- package/dist/{store-5YAQS6KU.js → store-VJ3OS6VO.js} +8 -7
- package/dist/sync-baseline-DOV5AGIO.js +36 -0
- package/dist/{sync-contributions-RBB6JVOL.js → sync-contributions-63542VE4.js} +20 -12
- package/dist/sync-record-RGHK7EAL.js +43 -0
- package/dist/{table-MQFVYA4K.js → table-OAYGFGEN.js} +8 -8
- package/dist/{tables-command-5Q4BSUNO.js → tables-command-PZIVDAH3.js} +45 -43
- package/dist/{tenant-Q3LPVSSM.js → tenant-Y3II4VY3.js} +12 -11
- package/dist/{tenant-command-BFVU2PEX.js → tenant-command-G6XON7CF.js} +113 -107
- package/dist/{test-command-F4VUU4QR.js → test-command-3FZOX7O3.js} +44 -42
- package/dist/{tests-5ZSDFVIJ.js → tests-KF6N6BZ7.js} +6 -6
- package/dist/{token-TERSTNU4.js → token-DJJUW5HY.js} +14 -13
- package/dist/toolchain-hooks-7PWCFLSU.js +46 -0
- package/dist/{toolchain-modules-FD5ANV2U.js → toolchain-modules-OTNSKK52.js} +8 -5
- package/dist/toolchain-peers-KQ7TQ2AR.js +28 -0
- package/dist/tracked-backend-AJCGTO5Y.js +82 -0
- package/dist/{update-check-O4C2LCZH.js → update-check-4E5L6GAW.js} +61 -59
- package/dist/{upgrade-command-NVFWLMTA.js → upgrade-command-BXMSQN4C.js} +68 -64
- package/dist/{whoami-command-YKNTISIV.js → whoami-command-NLCCIEUM.js} +17 -16
- package/dist/{workspace-GGVGE2S5.js → workspace-PNMJOXE4.js} +5 -5
- package/dist/{workspace-binding-OS4ZOX4S.js → workspace-binding-J5GWDFRI.js} +16 -15
- package/dist/{workspace-command-GTUNVN7K.js → workspace-command-MHIIY352.js} +125 -93
- package/dist/{workspace-env-467C5KHM.js → workspace-env-LLRWCHSA.js} +17 -16
- package/dist/{workspace-export-3XTVGQVM.js → workspace-export-BIKAYHWJ.js} +5 -5
- package/dist/{writable-7SSWUQJ3.js → writable-UOTO4WWC.js} +4 -4
- package/dist/{xano-6RS5XQZE.js → xano-LCI7VWPS.js} +8 -8
- package/dist/{xanosdk-import-E3WSIYUE.js → xanosdk-import-S7A3CVBN.js} +8 -8
- package/dist/{xanosdk-project-KH5TZ2KI.js → xanosdk-project-J4VCFRHP.js} +18 -17
- package/guides/README.md +2 -2
- package/guides/cli.md +73 -4
- package/guides/codegen.md +23 -5
- package/guides/codes.md +8 -2
- package/guides/coverage.md +5 -0
- package/guides/deploying.md +80 -0
- package/guides/marketplace.md +21 -0
- package/guides/module-contract.md +80 -4
- package/guides/scaffold.md +61 -10
- package/guides/typed-frontend.md +37 -0
- package/llms/client.md +11 -1
- package/llms/filters.md +4 -0
- package/llms/kinds-core.md +1 -1
- package/llms/statements-data.md +3 -2
- package/llms/toolchain.md +12 -0
- package/llms-full.txt +34 -7
- package/llms.txt +3 -3
- package/manifest.json +63 -5
- package/package.json +5 -2
- package/skills/xano-backend/SKILL.md +119 -0
- package/dist/chunk-F6IKWAIU.js +0 -664
- package/dist/chunk-M6ZFSTIZ.js +0 -630
- package/dist/chunk-MEIJDRHU.js +0 -243
- package/dist/deploy-source-5COWTBOC.js +0 -81
- package/dist/env-example-refresh-DWQIOQYR.js +0 -127
- package/dist/init-command-TI3HCKXZ.js +0 -126
- package/dist/local-engine-choice-FPJAZSSD.js +0 -79
- package/dist/operation-outcome-GNDD35WL.js +0 -51
- package/dist/promote-command-KHTIK44I.js +0 -141
- package/dist/pull-command-EUEWBRXZ.js +0 -162
- package/dist/release-command-WOVQAMUY.js +0 -147
- package/dist/toolchain-hooks-QVUZ6HZ4.js +0 -96
- package/dist/tracked-backend-B4HTME4S.js +0 -80
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
iconLibraryOf,
|
|
4
|
-
reactPreset
|
|
5
|
-
} from "./chunk-M6ZFSTIZ.js";
|
|
6
|
-
import {
|
|
2
|
+
blockVersion,
|
|
7
3
|
composeBlock,
|
|
8
4
|
gitattributesSpec,
|
|
5
|
+
gitignoreSpec,
|
|
6
|
+
htmlDialect,
|
|
9
7
|
upsertBlock
|
|
10
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LJM5CIY4.js";
|
|
11
9
|
import {
|
|
12
10
|
colorTokens,
|
|
13
11
|
defaultThemeChoice,
|
|
@@ -16,6 +14,182 @@ import {
|
|
|
16
14
|
fontThemeVars,
|
|
17
15
|
orderedTokens
|
|
18
16
|
} from "./chunk-TFFGOFVZ.js";
|
|
17
|
+
import {
|
|
18
|
+
md5Hex
|
|
19
|
+
} from "./chunk-OHX6MIUZ.js";
|
|
20
|
+
|
|
21
|
+
// src/emit/icon-presets.ts
|
|
22
|
+
var ICON_LIBRARIES = ["lucide", "tabler", "phosphor"];
|
|
23
|
+
var DEFAULT_ICON_LIBRARY = "lucide";
|
|
24
|
+
var LIBRARIES = {
|
|
25
|
+
lucide: {
|
|
26
|
+
id: "lucide",
|
|
27
|
+
label: "Lucide",
|
|
28
|
+
componentsJson: "lucide",
|
|
29
|
+
react: {
|
|
30
|
+
dependency: { "lucide-react": "^0.475.0" },
|
|
31
|
+
arrowImport: `import { ArrowRight } from "lucide-react";`,
|
|
32
|
+
arrowComponent: "ArrowRight",
|
|
33
|
+
modeImports: [`import { Monitor, Moon, Sun } from "lucide-react";`],
|
|
34
|
+
modeComponents: { system: "Monitor", light: "Sun", dark: "Moon" }
|
|
35
|
+
},
|
|
36
|
+
svelte: {
|
|
37
|
+
// Not the same version line as `lucide-react`: the Svelte icons ship from
|
|
38
|
+
// a separate package on their own 1.x.
|
|
39
|
+
dependency: { "@lucide/svelte": "^1.31.0" },
|
|
40
|
+
arrowImport: `import ArrowRight from "@lucide/svelte/icons/arrow-right";`,
|
|
41
|
+
arrowComponent: "ArrowRight",
|
|
42
|
+
modeImports: [
|
|
43
|
+
`import Monitor from "@lucide/svelte/icons/monitor";`,
|
|
44
|
+
`import Moon from "@lucide/svelte/icons/moon";`,
|
|
45
|
+
`import Sun from "@lucide/svelte/icons/sun";`
|
|
46
|
+
],
|
|
47
|
+
modeComponents: { system: "Monitor", light: "Sun", dark: "Moon" }
|
|
48
|
+
},
|
|
49
|
+
guidance: {
|
|
50
|
+
react: `Icons are [Lucide](https://lucide.dev/icons), installed as \`lucide-react\` and
|
|
51
|
+
imported by name from the package root \u2014
|
|
52
|
+
\`import { ArrowRight } from "lucide-react";\`.`,
|
|
53
|
+
svelte: `Icons are [Lucide](https://lucide.dev/icons), installed as \`@lucide/svelte\` \u2014
|
|
54
|
+
**not** \`lucide-react\`, which is the wrong package here in the same way the
|
|
55
|
+
plain \`shadcn\` CLI is. Import them one per module, and kebab-case the name into
|
|
56
|
+
the path: the icon is that module's DEFAULT export, and \`ArrowRight\` lives at
|
|
57
|
+
\`arrow-right\` \u2014 \`import ArrowRight from "@lucide/svelte/icons/arrow-right";\`.
|
|
58
|
+
Never from the package root, which pulls the whole set into the bundle.`
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
tabler: {
|
|
62
|
+
id: "tabler",
|
|
63
|
+
label: "Tabler",
|
|
64
|
+
componentsJson: "tabler",
|
|
65
|
+
react: {
|
|
66
|
+
dependency: { "@tabler/icons-react": "^3.31.0" },
|
|
67
|
+
// Every Tabler component is prefixed `Icon`, so the name in markup is not
|
|
68
|
+
// the name on the website — the single most common mistake with this set.
|
|
69
|
+
arrowImport: `import { IconArrowRight } from "@tabler/icons-react";`,
|
|
70
|
+
arrowComponent: "IconArrowRight",
|
|
71
|
+
modeImports: [
|
|
72
|
+
`import { IconDeviceDesktop, IconMoon, IconSun } from "@tabler/icons-react";`
|
|
73
|
+
],
|
|
74
|
+
modeComponents: {
|
|
75
|
+
system: "IconDeviceDesktop",
|
|
76
|
+
light: "IconSun",
|
|
77
|
+
dark: "IconMoon"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
svelte: {
|
|
81
|
+
dependency: { "@tabler/icons-svelte": "^3.31.0" },
|
|
82
|
+
arrowImport: `import { IconArrowRight } from "@tabler/icons-svelte";`,
|
|
83
|
+
arrowComponent: "IconArrowRight",
|
|
84
|
+
modeImports: [
|
|
85
|
+
`import { IconDeviceDesktop, IconMoon, IconSun } from "@tabler/icons-svelte";`
|
|
86
|
+
],
|
|
87
|
+
modeComponents: {
|
|
88
|
+
system: "IconDeviceDesktop",
|
|
89
|
+
light: "IconSun",
|
|
90
|
+
dark: "IconMoon"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
guidance: {
|
|
94
|
+
react: `Icons are [Tabler](https://tabler.io/icons), installed as \`@tabler/icons-react\`.
|
|
95
|
+
Every component is prefixed \`Icon\` \u2014 the site lists \`arrow-right\`, the import is
|
|
96
|
+
\`import { IconArrowRight } from "@tabler/icons-react";\`.`,
|
|
97
|
+
svelte: `Icons are [Tabler](https://tabler.io/icons), installed as \`@tabler/icons-svelte\` \u2014
|
|
98
|
+
**not** \`@tabler/icons-react\`, which is the wrong package here in the same way
|
|
99
|
+
the plain \`shadcn\` CLI is. Every component is prefixed \`Icon\`, so the name in
|
|
100
|
+
markup is not the name on the site: \`arrow-right\` is
|
|
101
|
+
\`import { IconArrowRight } from "@tabler/icons-svelte";\`.`
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
phosphor: {
|
|
105
|
+
id: "phosphor",
|
|
106
|
+
label: "Phosphor",
|
|
107
|
+
// shadcn/ui's CLI does not know this set. Left off components.json rather
|
|
108
|
+
// than guessed — see IconLibrary.componentsJson.
|
|
109
|
+
componentsJson: void 0,
|
|
110
|
+
react: {
|
|
111
|
+
dependency: { "@phosphor-icons/react": "^2.1.7" },
|
|
112
|
+
arrowImport: `import { ArrowRight } from "@phosphor-icons/react";`,
|
|
113
|
+
arrowComponent: "ArrowRight",
|
|
114
|
+
modeImports: [`import { Desktop, Moon, Sun } from "@phosphor-icons/react";`],
|
|
115
|
+
modeComponents: { system: "Desktop", light: "Sun", dark: "Moon" }
|
|
116
|
+
},
|
|
117
|
+
svelte: {
|
|
118
|
+
dependency: { "phosphor-svelte": "^3.0.1" },
|
|
119
|
+
arrowImport: `import ArrowRight from "phosphor-svelte/lib/ArrowRight";`,
|
|
120
|
+
arrowComponent: "ArrowRight",
|
|
121
|
+
modeImports: [
|
|
122
|
+
`import Desktop from "phosphor-svelte/lib/Desktop";`,
|
|
123
|
+
`import Moon from "phosphor-svelte/lib/Moon";`,
|
|
124
|
+
`import Sun from "phosphor-svelte/lib/Sun";`
|
|
125
|
+
],
|
|
126
|
+
modeComponents: { system: "Desktop", light: "Sun", dark: "Moon" }
|
|
127
|
+
},
|
|
128
|
+
guidance: {
|
|
129
|
+
react: `Icons are [Phosphor](https://phosphoricons.com), installed as
|
|
130
|
+
\`@phosphor-icons/react\` and imported by name \u2014
|
|
131
|
+
\`import { ArrowRight } from "@phosphor-icons/react";\`. Each takes a \`weight\` prop
|
|
132
|
+
(\`regular\`, \`bold\`, \`duotone\`, \u2026) rather than only a stroke width.`,
|
|
133
|
+
svelte: `Icons are [Phosphor](https://phosphoricons.com), installed as \`phosphor-svelte\` \u2014
|
|
134
|
+
**not** \`@phosphor-icons/react\`, which is the wrong package here in the same way
|
|
135
|
+
the plain \`shadcn\` CLI is. Import them one per module, and PascalCase the name
|
|
136
|
+
into the path: \`import ArrowRight from "phosphor-svelte/lib/ArrowRight";\`.
|
|
137
|
+
Never from the package root, which pulls the whole set into the bundle.`
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
function findIconLibrary(id) {
|
|
142
|
+
const normalized = id.trim().toLowerCase();
|
|
143
|
+
return ICON_LIBRARIES.includes(normalized) ? LIBRARIES[normalized] : void 0;
|
|
144
|
+
}
|
|
145
|
+
function allIconLibraries() {
|
|
146
|
+
return ICON_LIBRARIES.map((id) => LIBRARIES[id]);
|
|
147
|
+
}
|
|
148
|
+
function defaultIconLibrary() {
|
|
149
|
+
return LIBRARIES[DEFAULT_ICON_LIBRARY];
|
|
150
|
+
}
|
|
151
|
+
function resolveIconsFlag(raw) {
|
|
152
|
+
const library = findIconLibrary(raw);
|
|
153
|
+
if (library === void 0) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Unknown --icons "${raw}". Valid icon libraries: ${ICON_LIBRARIES.join(", ")}.`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return library;
|
|
159
|
+
}
|
|
160
|
+
function iconLibraryOf(id) {
|
|
161
|
+
return id === void 0 ? defaultIconLibrary() : LIBRARIES[id];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/emit/decode-record.ts
|
|
165
|
+
function decodeDigest(content) {
|
|
166
|
+
return md5Hex(content);
|
|
167
|
+
}
|
|
168
|
+
function decodeMarkerWith(previous, files) {
|
|
169
|
+
const sorted = [...files].sort((a, b) => a.path.localeCompare(b.path));
|
|
170
|
+
return {
|
|
171
|
+
...previous,
|
|
172
|
+
files: sorted.map((f) => f.path),
|
|
173
|
+
digests: Object.fromEntries(sorted.map((f) => [f.path, decodeDigest(f.content)]))
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
var SOURCE_KINDS = /* @__PURE__ */ new Set(["workspace", "ephemeral", "local", "tenant", "release"]);
|
|
177
|
+
function decodeMarkerHead(previous, command, provenance, sdkVersion, report) {
|
|
178
|
+
const colon = provenance.indexOf(":");
|
|
179
|
+
const kind = colon === -1 ? provenance : provenance.slice(0, colon);
|
|
180
|
+
const origin = SOURCE_KINDS.has(kind) ? { source: kind, origin: colon === -1 ? kind : provenance.slice(colon + 1) } : { source: "file", origin: provenance };
|
|
181
|
+
const { generatedBy: _generatedBy, report: _previousReport, files: _files, digests: _digests, ...kept } = previous;
|
|
182
|
+
return {
|
|
183
|
+
...kept,
|
|
184
|
+
...origin,
|
|
185
|
+
sdkVersion,
|
|
186
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
187
|
+
note: `Written by \`xanosdk ${command}\`. Its presence lets a later \`pull\`, \`generate --force\` or \`init --from\` refresh this tree in place.`,
|
|
188
|
+
// The findings of THIS decode — a previous one's would describe a source
|
|
189
|
+
// the tree no longer reflects. Dropped when this command has none to say.
|
|
190
|
+
...report !== void 0 ? { report } : {}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
19
193
|
|
|
20
194
|
// src/emit/init-templates.ts
|
|
21
195
|
var STANDALONE_NPM = { manager: "npm", member: "", declared: false };
|
|
@@ -26,12 +200,112 @@ function sdkDep(sdkVersion) {
|
|
|
26
200
|
const ceiling = major > 0 ? `${major + 1}.0.0` : `0.${minor + 1}.0`;
|
|
27
201
|
return `>=${sdkVersion} <${ceiling}`;
|
|
28
202
|
}
|
|
203
|
+
var npmKeyOrder = (a, b) => a.localeCompare(b, "en");
|
|
29
204
|
function sortedKeys(map) {
|
|
30
|
-
return Object.fromEntries(Object.entries(map).sort(([a], [b]) => a
|
|
205
|
+
return Object.fromEntries(Object.entries(map).sort(([a], [b]) => npmKeyOrder(a, b)));
|
|
206
|
+
}
|
|
207
|
+
function isSorted(keys) {
|
|
208
|
+
return keys.every((key, i) => i === 0 || npmKeyOrder(keys[i - 1], key) <= 0);
|
|
209
|
+
}
|
|
210
|
+
var BACKEND_CHECK = "tsc -p xano && tsc -p xano/lambdas";
|
|
211
|
+
function xanoScripts(check, frontend) {
|
|
212
|
+
const routes = "xanosdk routes ./xano/index.ts --emit xano/routes.gen.ts";
|
|
213
|
+
return {
|
|
214
|
+
"xano:routes": routes,
|
|
215
|
+
// Both are prefixed with a typecheck: the export pass validates what it
|
|
216
|
+
// can see in the encoded bundle, and TypeScript validates the rest. With
|
|
217
|
+
// `tsc` on `build` alone, anyone following the documented workflow would
|
|
218
|
+
// ship with the compiler's half of the checks never run.
|
|
219
|
+
// No `--lock` on either of these: every build maintains `xano/xano.lock`
|
|
220
|
+
// by default, so the flag would be redundant here and would read as
|
|
221
|
+
// though the lock were something a project opts into. It is not — object
|
|
222
|
+
// identity derives from `(type, name)`, and without a lock a rename
|
|
223
|
+
// re-derives the guid and the engine does delete-and-recreate instead of
|
|
224
|
+
// rename-in-place, so the file has to exist before the identities matter.
|
|
225
|
+
// Scaffolds written before the default flipped still pass `--lock`, and
|
|
226
|
+
// it stays accepted for exactly that reason.
|
|
227
|
+
"xano:export": `npm run xano:routes && ${check} && xanosdk export ./xano/index.ts --out workspace.json`,
|
|
228
|
+
// The default deploy, and the tight loop: redeploy the backend to the
|
|
229
|
+
// Xano Engine on this machine. It needs no Xano account, so a fresh
|
|
230
|
+
// scaffold runs it before anyone signs in.
|
|
231
|
+
//
|
|
232
|
+
// No `--static` and no `npm run build`, and both omissions are the point
|
|
233
|
+
// rather than an economy: there is no frontend to ship here. The dev
|
|
234
|
+
// server is still serving it, and the deploy points that server at the
|
|
235
|
+
// engine on its way past, so the loop is backend-only by design.
|
|
236
|
+
// (`--static` does work on a Xano Engine, for a built frontend the
|
|
237
|
+
// engine itself should serve; this loop has none.)
|
|
238
|
+
//
|
|
239
|
+
// No `--local`: the Xano Engine is where a bare deploy goes, and the
|
|
240
|
+
// project's pinned version is what it runs. Baking an engine version or
|
|
241
|
+
// URL into a scaffolded project would ship a coordinate that is not this
|
|
242
|
+
// project's to carry.
|
|
243
|
+
//
|
|
244
|
+
// `--keep-data`, because the engine is the developer's own backend and
|
|
245
|
+
// this loop runs once per code change: each run merges into what the last
|
|
246
|
+
// one left, so rows entered through the app survive the edit. The first
|
|
247
|
+
// run, and the first after the engine restarts, replaces and seeds.
|
|
248
|
+
// `--reset` on the command line still gives a clean slate.
|
|
249
|
+
"xano:deploy": `npm run xano:routes && ${check} && xanosdk deploy ./xano/index.ts --keep-data`,
|
|
250
|
+
// The cloud deploy: backend and built frontend to an ephemeral.
|
|
251
|
+
// Runs `build` rather than `check` directly: `--static ./frontend/dist`
|
|
252
|
+
// needs that directory to EXIST, and only `vite build` writes it. Pointing
|
|
253
|
+
// the flag at a directory the script never produced meant a fresh clone
|
|
254
|
+
// running the documented command had no `dist` to ship.
|
|
255
|
+
// `build` already prefixes the same typecheck, so this is one check, not
|
|
256
|
+
// two. With no frontend, the backend goes alone.
|
|
257
|
+
"xano:deploy:ephemeral": frontend ? `npm run build && xanosdk deploy ./xano/index.ts --ephemeral --static ./frontend/dist` : `npm run xano:routes && ${check} && xanosdk deploy ./xano/index.ts --ephemeral`,
|
|
258
|
+
// The frontend-only loop: rebuild and republish to the environment this
|
|
259
|
+
// project last deployed to, without recompiling or re-importing the
|
|
260
|
+
// backend. `build` runs first because publish ships only what is on disk.
|
|
261
|
+
...frontend ? { "xano:deploy:frontend": "npm run build && xanosdk publish ./frontend/dist" } : {},
|
|
262
|
+
// The CI guard: fails instead of changing xano.lock, so an uncommitted
|
|
263
|
+
// identity change is caught in review rather than on a deploy. An
|
|
264
|
+
// installed toolchain module's own check rides on the same run: a module
|
|
265
|
+
// that owns a generated tree verifies it under this same flag.
|
|
266
|
+
// `--strict` fails on a stale or missing committed manifest rather than
|
|
267
|
+
// rewriting it, the same way `--frozen-lock` treats xano.lock.
|
|
268
|
+
//
|
|
269
|
+
// `export --check`, not an export to a file: a CI checkout has no
|
|
270
|
+
// `xano/.env` and no `xano/.secrets.json`, and a real export refuses a
|
|
271
|
+
// declared documentation gate it cannot supply. The check writes nothing
|
|
272
|
+
// — no bundle, no lock — so there is nothing a missing secret could clear,
|
|
273
|
+
// and it needs none. It implies `--frozen-lock`. `--strict` as the grounding
|
|
274
|
+
// tells every CI and unattended build to: a build warning is a hard
|
|
275
|
+
// failure. Env values never reach it — a declared name with no value is
|
|
276
|
+
// `""`, which only `deploy` refuses — so a checkout with no `xano/.env`
|
|
277
|
+
// passes it (a fresh scaffold and an `init --from` tree both do).
|
|
278
|
+
"xano:check": `${routes} --strict && ${check} && xanosdk export ./xano/index.ts --check --strict`,
|
|
279
|
+
// No compile step and no entry file: this runs what is DEPLOYED, so it
|
|
280
|
+
// pairs with `xano:deploy` rather than repeating its work. Exits 5 on a
|
|
281
|
+
// failing suite, which is what makes it usable as a CI gate.
|
|
282
|
+
"xano:test": "xanosdk test run-all"
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function backendScripts() {
|
|
286
|
+
const scripts = xanoScripts(BACKEND_CHECK, false);
|
|
287
|
+
return { "xano:routes": scripts["xano:routes"], "xano:typecheck": `npm run xano:routes && ${BACKEND_CHECK}`, ...scripts };
|
|
288
|
+
}
|
|
289
|
+
var BACKEND_DEV_DEPENDENCIES = {
|
|
290
|
+
"@types/node": "^20.19.43",
|
|
291
|
+
tsx: "^4.19.2",
|
|
292
|
+
typescript: "^6.0.3"
|
|
293
|
+
};
|
|
294
|
+
function renderBackendPackageJson({ appName, sdkVersion }) {
|
|
295
|
+
const pkg = {
|
|
296
|
+
name: appName,
|
|
297
|
+
version: "0.1.0",
|
|
298
|
+
private: true,
|
|
299
|
+
type: "module",
|
|
300
|
+
scripts: backendScripts(),
|
|
301
|
+
dependencies: { "@xano/sdk": sdkDep(sdkVersion) },
|
|
302
|
+
devDependencies: sortedKeys({ ...BACKEND_DEV_DEPENDENCIES }),
|
|
303
|
+
engines: { node: `>=${NODE_MIN}` }
|
|
304
|
+
};
|
|
305
|
+
return JSON.stringify(pkg, null, 2) + "\n";
|
|
31
306
|
}
|
|
32
307
|
function renderPackageJson({ appName, sdkVersion }, preset, choice = defaultThemeChoice()) {
|
|
33
308
|
const check = `${preset.checkCmd} && tsc -p xano/lambdas`;
|
|
34
|
-
const routes = "xanosdk routes ./xano/index.ts --emit xano/routes.gen.ts";
|
|
35
309
|
const pkg = {
|
|
36
310
|
name: appName,
|
|
37
311
|
version: "0.1.0",
|
|
@@ -42,75 +316,7 @@ function renderPackageJson({ appName, sdkVersion }, preset, choice = defaultThem
|
|
|
42
316
|
build: `npm run xano:routes && ${check} && vite build`,
|
|
43
317
|
preview: "vite preview",
|
|
44
318
|
typecheck: `npm run xano:routes && ${check}`,
|
|
45
|
-
|
|
46
|
-
// Both are prefixed with a typecheck: the export pass validates what it
|
|
47
|
-
// can see in the encoded bundle, and TypeScript validates the rest. With
|
|
48
|
-
// `tsc` on `build` alone, anyone following the documented workflow would
|
|
49
|
-
// ship with the compiler's half of the checks never run.
|
|
50
|
-
// No `--lock` on either of these: every build maintains `xano/xano.lock`
|
|
51
|
-
// by default, so the flag would be redundant here and would read as
|
|
52
|
-
// though the lock were something a project opts into. It is not — object
|
|
53
|
-
// identity derives from `(type, name)`, and without a lock a rename
|
|
54
|
-
// re-derives the guid and the engine does delete-and-recreate instead of
|
|
55
|
-
// rename-in-place, so the file has to exist before the identities matter.
|
|
56
|
-
// Scaffolds written before the default flipped still pass `--lock`, and
|
|
57
|
-
// it stays accepted for exactly that reason.
|
|
58
|
-
"xano:export": `npm run xano:routes && ${check} && xanosdk export ./xano/index.ts --out workspace.json`,
|
|
59
|
-
// The default deploy, and the tight loop: redeploy the backend to the
|
|
60
|
-
// Xano Engine on this machine. It needs no Xano account, so a fresh
|
|
61
|
-
// scaffold runs it before anyone signs in.
|
|
62
|
-
//
|
|
63
|
-
// No `--static` and no `npm run build`, and both omissions are the point
|
|
64
|
-
// rather than an economy: there is no frontend to ship here. The dev
|
|
65
|
-
// server is still serving it, and the deploy points that server at the
|
|
66
|
-
// engine on its way past, so the loop is backend-only by design.
|
|
67
|
-
// (`--static` does work on a Xano Engine, for a built frontend the
|
|
68
|
-
// engine itself should serve; this loop has none.)
|
|
69
|
-
//
|
|
70
|
-
// No `--local`: the Xano Engine is where a bare deploy goes, and the
|
|
71
|
-
// project's pinned version is what it runs. Baking an engine version or
|
|
72
|
-
// URL into a scaffolded project would ship a coordinate that is not this
|
|
73
|
-
// project's to carry.
|
|
74
|
-
//
|
|
75
|
-
// `--keep-data`, because the engine is the developer's own backend and
|
|
76
|
-
// this loop runs once per code change: each run merges into what the last
|
|
77
|
-
// one left, so rows entered through the app survive the edit. The first
|
|
78
|
-
// run, and the first after the engine restarts, replaces and seeds.
|
|
79
|
-
// `--reset` on the command line still gives a clean slate.
|
|
80
|
-
"xano:deploy": `npm run xano:routes && ${check} && xanosdk deploy ./xano/index.ts --keep-data`,
|
|
81
|
-
// The cloud deploy: backend and built frontend to an ephemeral.
|
|
82
|
-
// Runs `build` rather than `check` directly: `--static ./frontend/dist`
|
|
83
|
-
// needs that directory to EXIST, and only `vite build` writes it. Pointing
|
|
84
|
-
// the flag at a directory the script never produced meant a fresh clone
|
|
85
|
-
// running the documented command had no `dist` to ship.
|
|
86
|
-
// `build` already prefixes the same typecheck, so this is one check, not
|
|
87
|
-
// two.
|
|
88
|
-
"xano:deploy:ephemeral": `npm run build && xanosdk deploy ./xano/index.ts --ephemeral --static ./frontend/dist`,
|
|
89
|
-
// The frontend-only loop: rebuild and republish to the environment this
|
|
90
|
-
// project last deployed to, without recompiling or re-importing the
|
|
91
|
-
// backend. `build` runs first because publish ships only what is on disk.
|
|
92
|
-
"xano:deploy:frontend": "npm run build && xanosdk publish ./frontend/dist",
|
|
93
|
-
// The CI guard: fails instead of changing xano.lock, so an uncommitted
|
|
94
|
-
// identity change is caught in review rather than on a deploy. An
|
|
95
|
-
// installed toolchain module's own check rides on the same run: a module
|
|
96
|
-
// that owns a generated tree verifies it under this same flag.
|
|
97
|
-
// `--strict` fails on a stale or missing committed manifest rather than
|
|
98
|
-
// rewriting it, the same way `--frozen-lock` treats xano.lock.
|
|
99
|
-
//
|
|
100
|
-
// `export --check`, not an export to a file: a CI checkout has no
|
|
101
|
-
// `xano/.env` and no `xano/.secrets.json`, and a real export refuses a
|
|
102
|
-
// declared documentation gate it cannot supply. The check writes nothing
|
|
103
|
-
// — no bundle, no lock — so there is nothing a missing secret could clear,
|
|
104
|
-
// and it needs none. It implies `--frozen-lock`. `--strict` as the grounding
|
|
105
|
-
// tells every CI and unattended build to: a build warning is a hard
|
|
106
|
-
// failure. Env values never reach it — a declared name with no value is
|
|
107
|
-
// `""`, which only `deploy` refuses — so a checkout with no `xano/.env`
|
|
108
|
-
// passes it (a fresh scaffold and an `init --from` tree both do).
|
|
109
|
-
"xano:check": `${routes} --strict && ${check} && xanosdk export ./xano/index.ts --check --strict`,
|
|
110
|
-
// No compile step and no entry file: this runs what is DEPLOYED, so it
|
|
111
|
-
// pairs with `xano:deploy` rather than repeating its work. Exits 5 on a
|
|
112
|
-
// failing suite, which is what makes it usable as a CI gate.
|
|
113
|
-
"xano:test": "xanosdk test run-all",
|
|
319
|
+
...xanoScripts(check, true),
|
|
114
320
|
// Framework-owned scripts, last so a preset can add to the set but the
|
|
115
321
|
// shared xano:* contract above stays the same in every scaffold.
|
|
116
322
|
...preset.extraScripts
|
|
@@ -176,10 +382,33 @@ function renderTsconfig(preset) {
|
|
|
176
382
|
};
|
|
177
383
|
return JSON.stringify(tsconfig, null, 2) + "\n";
|
|
178
384
|
}
|
|
385
|
+
var BACKEND_TSCONFIG_PATH = "xano/tsconfig.json";
|
|
386
|
+
function renderBackendTsconfig() {
|
|
387
|
+
const tsconfig = {
|
|
388
|
+
compilerOptions: {
|
|
389
|
+
target: "ES2022",
|
|
390
|
+
lib: ["ES2022", "DOM", "DOM.Iterable"],
|
|
391
|
+
module: "ESNext",
|
|
392
|
+
moduleResolution: "bundler",
|
|
393
|
+
strict: true,
|
|
394
|
+
noEmit: true,
|
|
395
|
+
esModuleInterop: true,
|
|
396
|
+
skipLibCheck: true,
|
|
397
|
+
resolveJsonModule: true,
|
|
398
|
+
isolatedModules: true,
|
|
399
|
+
types: ["node"]
|
|
400
|
+
},
|
|
401
|
+
include: ["**/*.ts"],
|
|
402
|
+
exclude: ["lambdas"]
|
|
403
|
+
};
|
|
404
|
+
return JSON.stringify(tsconfig, null, 2) + "\n";
|
|
405
|
+
}
|
|
179
406
|
var LAMBDA_GLOBALS_TYPES = "@xano/sdk/lambda-globals";
|
|
180
|
-
function renderLambdaTsconfig() {
|
|
407
|
+
function renderLambdaTsconfig(base = "project") {
|
|
181
408
|
const tsconfig = {
|
|
182
|
-
|
|
409
|
+
// A project with no frontend has no root tsconfig: `xano/tsconfig.json` is
|
|
410
|
+
// the config the backend checks under, and the lambdas build on that.
|
|
411
|
+
extends: base === "backend" ? "../tsconfig.json" : "../../tsconfig.json",
|
|
183
412
|
// No DOM and no node types: a lambda body runs in neither.
|
|
184
413
|
compilerOptions: { lib: ["ES2022"], types: [LAMBDA_GLOBALS_TYPES] },
|
|
185
414
|
files: [],
|
|
@@ -282,6 +511,25 @@ ${framework}
|
|
|
282
511
|
# it means each build mints identities that are thrown away and re-invented.
|
|
283
512
|
`;
|
|
284
513
|
}
|
|
514
|
+
function renderGitignoreBlock(version) {
|
|
515
|
+
return composeBlock(
|
|
516
|
+
gitignoreSpec(),
|
|
517
|
+
[
|
|
518
|
+
"# What the Xano backend writes. xano/ itself, and xano/xano.lock, are committed.",
|
|
519
|
+
"node_modules/",
|
|
520
|
+
"/workspace.json",
|
|
521
|
+
"/.xano/",
|
|
522
|
+
"xano/.env",
|
|
523
|
+
"xano/.env.tmp-*",
|
|
524
|
+
"xano/.secrets.json",
|
|
525
|
+
"xano/.secrets.json.tmp-*"
|
|
526
|
+
],
|
|
527
|
+
version
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
function mergeGitignore(existing, version) {
|
|
531
|
+
return upsertBlock(existing, gitignoreSpec(), renderGitignoreBlock(version)).text;
|
|
532
|
+
}
|
|
285
533
|
var NODE_MIN = "20.19";
|
|
286
534
|
function renderGitattributes(contributions = []) {
|
|
287
535
|
let text = `# The export writes LF. Normalizing here keeps generated output
|
|
@@ -412,25 +660,106 @@ VITE_XANO_HOST=https://your-instance.xano.io
|
|
|
412
660
|
`;
|
|
413
661
|
}
|
|
414
662
|
var XANO_BADGE = "[](https://xano.com)";
|
|
415
|
-
|
|
416
|
-
|
|
663
|
+
var README_PATH = "README.md";
|
|
664
|
+
var README_BUILT_WITH_BLOCK = {
|
|
665
|
+
dialect: htmlDialect("xanosdk-built-with"),
|
|
666
|
+
pkg: "xanosdk-built-with",
|
|
667
|
+
file: README_PATH
|
|
668
|
+
};
|
|
669
|
+
var README_BUILT_WITH_BODY = `## Built with
|
|
417
670
|
|
|
418
|
-
- [Xano
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
671
|
+
- [Xano](https://xano.com) hosts everything under [\`xano/\`](xano/): the database, APIs,
|
|
672
|
+
auth, background tasks, realtime, file storage and AI agents. It runs on your machine as
|
|
673
|
+
the Xano Engine and in the cloud from the same command. [Docs](https://docs.xano.com) \xB7
|
|
674
|
+
[Community](https://community.xano.com)
|
|
675
|
+
- [Xano SDK](https://github.com/xano-sdk/sdk) is the TypeScript the backend is authored in.
|
|
676
|
+
The [guides](https://github.com/xano-sdk/sdk/blob/main/guides/README.md) cover signing in
|
|
677
|
+
and deploying, releasing to production, the typed frontend, and every CLI command.`;
|
|
678
|
+
function renderReadmeBuiltWith(sdkVersion) {
|
|
679
|
+
return composeBlock(README_BUILT_WITH_BLOCK, [README_BUILT_WITH_BODY], sdkVersion);
|
|
680
|
+
}
|
|
681
|
+
function readmeBuiltWithVersion(text) {
|
|
682
|
+
return blockVersion(text, README_BUILT_WITH_BLOCK);
|
|
423
683
|
}
|
|
424
|
-
function
|
|
684
|
+
function refreshReadmeBuiltWith(existing, sdkVersion) {
|
|
685
|
+
return upsertBlock(existing, README_BUILT_WITH_BLOCK, renderReadmeBuiltWith(sdkVersion)).text;
|
|
686
|
+
}
|
|
687
|
+
function renderReadmeDescriptionSlot(appName) {
|
|
688
|
+
return `_Describe ${appName} here: what it does and who it is for._`;
|
|
689
|
+
}
|
|
690
|
+
var README_ENGINE_BULLETS = `- **It works on a fresh machine with nothing set.** The first run downloads the latest
|
|
691
|
+
engine once per machine and pins its version in \`package.json\`. Commit that change, so
|
|
692
|
+
everyone on the project runs the same engine.
|
|
693
|
+
- **It keeps your table rows across redeploys** (\`--keep-data\`). The first run seeds and
|
|
694
|
+
later runs merge your changes in without re-seeding. Rows survive only in tables and columns that keep
|
|
695
|
+
their names (a rename drops the old one with its rows), and only while the engine runs. An
|
|
696
|
+
engine update or restart starts it empty and the next run seeds again.
|
|
697
|
+
\`npm run xano:deploy -- --reset\` gives a clean, re-seeded slate.
|
|
698
|
+
- **Updates are yours to take.** When a newer engine ships, a deploy offers it and never
|
|
699
|
+
applies it without a yes. \`npx xanosdk local update\` moves the pin on purpose,
|
|
700
|
+
and \`npx xanosdk local cache clear\` reclaims the disk space. To try another engine
|
|
701
|
+
without moving the pin, export \`XANOSDK_ENGINE_OVERRIDE\` with a version (\`v0.1.5\`)
|
|
702
|
+
or an engine archive path.
|
|
703
|
+
- **The rest of the CLI follows.** After a local deploy, \`npm run xano:test\`,
|
|
704
|
+
\`npx xanosdk tables\` and \`npx xanosdk status\` reach the engine with no flag.
|
|
705
|
+
\`npx xanosdk local stop <name>\` shuts it down.`;
|
|
706
|
+
var README_LOCK_SECTION = `## \`xano.lock\` \u2014 commit it
|
|
707
|
+
|
|
708
|
+
Object identity derives from \`(type, name)\`, so a rename would otherwise change
|
|
709
|
+
an object's guid and the engine would **delete and recreate** it rather than
|
|
710
|
+
renaming it in place \u2014 losing its rows on a record-preserving import.
|
|
711
|
+
[\`xano/xano.lock\`](xano/xano.lock) freezes each guid and each API group's
|
|
712
|
+
canonical slug, so renames and re-deploys keep the same identities (and the same
|
|
713
|
+
public URLs).
|
|
714
|
+
|
|
715
|
+
Every build writes it \u2014 no flag \u2014 and it **must be committed**. Ignoring it means
|
|
716
|
+
each build mints identities and public URLs that are thrown away and re-invented
|
|
717
|
+
next time. If you release to a workspace that already exists, adopt what it
|
|
718
|
+
already serves first with \`npx xanosdk lock import <live-bundle.json> --lock=xano/xano.lock\`;
|
|
719
|
+
that is also the recovery path once identities have drifted.
|
|
720
|
+
|
|
721
|
+
\`\`\`bash
|
|
722
|
+
npm run xano:check # CI: fail on ANY build warning (it runs --strict), if the export
|
|
723
|
+
# would change xano.lock, or if the lock carries an entry no
|
|
724
|
+
# object matches \u2014 writes nothing and needs no secrets, so a
|
|
725
|
+
# fresh clone runs it as-is
|
|
726
|
+
\`\`\`
|
|
727
|
+
|
|
728
|
+
To rename an object: rename it in code and run \`npm run xano:export\`. Only if it
|
|
729
|
+
warns of an orphaned entry, run the \`npx xanosdk lock rename <kind> <old> <new>\` it prints, then
|
|
730
|
+
export again. Every \`lock\` subcommand finds \`xano/xano.lock\` from the project entry, as
|
|
731
|
+
\`export\` does \u2014 \`--entry=<file>\` names another entry, \`--lock=<path>\` the lock itself.`;
|
|
732
|
+
var README_ADD_ONS_SECTION = `## Add-ons
|
|
733
|
+
|
|
734
|
+
Xano SDK is composable with other \`@xano-sdk/*\` packages:
|
|
735
|
+
|
|
736
|
+
- **[\`@xano-sdk/auth\`](https://www.npmjs.com/package/@xano-sdk/auth)** \u2014 turnkey
|
|
737
|
+
authentication (user/login/signup tables and endpoints). Install it with
|
|
738
|
+
\`npx xanosdk marketplace install @xano-sdk/auth\`, then register it in
|
|
739
|
+
\`xano/index.ts\`. Authentication only \u2014 **not** authorization: it has no
|
|
740
|
+
roles, permissions, or route guards, and its tokens carry no role claim.
|
|
741
|
+
Enforce roles off the caller's row with \`@xano/sdk\`: spread
|
|
742
|
+
\`...guard.role(userTable, "admin")\` into the endpoint's stack.
|
|
743
|
+
- More \`@xano-sdk/*\` packages register onto the same workspace. This list
|
|
744
|
+
does not update itself \u2014 run \`npx xanosdk marketplace list\` for the live
|
|
745
|
+
catalogue, \`npx xanosdk marketplace search <words>\` to narrow it, and
|
|
746
|
+
\`npx xanosdk marketplace details <package>\` to see what an add-on installs and
|
|
747
|
+
how to register it. All three work before you log in.
|
|
748
|
+
|
|
749
|
+
None of these ship with the scaffold. Install one only when you need it \u2014 an
|
|
750
|
+
add-on you never register is weight in \`package.json\` for nothing.`;
|
|
751
|
+
function renderReadme({ appName, install, sdkVersion }, preset, choice = defaultThemeChoice()) {
|
|
425
752
|
return `# ${appName}
|
|
426
753
|
|
|
427
754
|
${XANO_BADGE}
|
|
428
755
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
756
|
+
${renderReadmeDescriptionSlot(appName)}
|
|
757
|
+
|
|
758
|
+
The backend under [\`xano/\`](xano/) is TypeScript, authored with the
|
|
759
|
+
[Xano SDK](https://github.com/xano-sdk/sdk) and running on [Xano](https://xano.com). It runs
|
|
760
|
+
on your machine as the **Xano Engine** and ships to Xano's cloud with the same command. The
|
|
432
761
|
${preset.label} frontend under [\`frontend/\`](frontend/) takes its request paths and types
|
|
433
|
-
from the backend defs
|
|
762
|
+
from the backend defs rather than hand-typing them.
|
|
434
763
|
|
|
435
764
|
## Quick start
|
|
436
765
|
|
|
@@ -452,22 +781,7 @@ It prints the backend URL, a link that opens Xano's visual builder on the engine
|
|
|
452
781
|
points \`npm run dev\` at it through \`.env.local\` (restart the dev server if it was already
|
|
453
782
|
running).
|
|
454
783
|
|
|
455
|
-
|
|
456
|
-
engine once per machine and pins its version in \`package.json\`. Commit that change, so
|
|
457
|
-
everyone on the project runs the same engine.
|
|
458
|
-
- **It keeps your table rows across redeploys** (\`--keep-data\`). The first run seeds and
|
|
459
|
-
later runs merge your changes in without re-seeding. Rows survive only in tables and columns that keep
|
|
460
|
-
their names (a rename drops the old one with its rows), and only while the engine runs. An
|
|
461
|
-
engine update or restart starts it empty and the next run seeds again.
|
|
462
|
-
\`npm run xano:deploy -- --reset\` gives a clean, re-seeded slate.
|
|
463
|
-
- **Updates are yours to take.** When a newer engine ships, a deploy offers it and never
|
|
464
|
-
applies it without a yes. \`npx xanosdk local update\` moves the pin on purpose,
|
|
465
|
-
and \`npx xanosdk local cache clear\` reclaims the disk space. To try another engine
|
|
466
|
-
without moving the pin, export \`XANOSDK_ENGINE_OVERRIDE\` with a version (\`v0.1.5\`)
|
|
467
|
-
or an engine archive path.
|
|
468
|
-
- **The rest of the CLI follows.** After a local deploy, \`npm run xano:test\`,
|
|
469
|
-
\`npx xanosdk tables\` and \`npx xanosdk status\` reach the engine with no flag.
|
|
470
|
-
\`npx xanosdk local stop <name>\` shuts it down.
|
|
784
|
+
${README_ENGINE_BULLETS}
|
|
471
785
|
|
|
472
786
|
## Deploy to Xano's cloud
|
|
473
787
|
|
|
@@ -499,38 +813,14 @@ The other scripts:
|
|
|
499
813
|
./xano/index.ts --test\` does both in one step.
|
|
500
814
|
- \`npm run xano:export\` compiles the backend to \`workspace.json\` (don't commit it).
|
|
501
815
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
Object identity derives from \`(type, name)\`, so a rename would otherwise change
|
|
505
|
-
an object's guid and the engine would **delete and recreate** it rather than
|
|
506
|
-
renaming it in place \u2014 losing its rows on a record-preserving import.
|
|
507
|
-
[\`xano/xano.lock\`](xano/xano.lock) freezes each guid and each API group's
|
|
508
|
-
canonical slug, so renames and re-deploys keep the same identities (and the same
|
|
509
|
-
public URLs).
|
|
510
|
-
|
|
511
|
-
Every build writes it \u2014 no flag \u2014 and it **must be committed**. Ignoring it means
|
|
512
|
-
each build mints identities and public URLs that are thrown away and re-invented
|
|
513
|
-
next time. If you release to a workspace that already exists, adopt what it
|
|
514
|
-
already serves first with \`npx xanosdk lock import <live-bundle.json> --lock=xano/xano.lock\`;
|
|
515
|
-
that is also the recovery path once identities have drifted.
|
|
516
|
-
|
|
517
|
-
\`\`\`bash
|
|
518
|
-
npm run xano:check # CI: fail on ANY build warning (it runs --strict), if the export
|
|
519
|
-
# would change xano.lock, or if the lock carries an entry no
|
|
520
|
-
# object matches \u2014 writes nothing and needs no secrets, so a
|
|
521
|
-
# fresh clone runs it as-is
|
|
522
|
-
\`\`\`
|
|
523
|
-
|
|
524
|
-
To rename an object: rename it in code and run \`npm run xano:export\`. Only if it
|
|
525
|
-
warns of an orphaned entry, run the \`npx xanosdk lock rename <kind> <old> <new>\` it prints, then
|
|
526
|
-
export again. Every \`lock\` subcommand finds \`xano/xano.lock\` from the project entry, as
|
|
527
|
-
\`export\` does \u2014 \`--entry=<file>\` names another entry, \`--lock=<path>\` the lock itself.
|
|
816
|
+
${README_LOCK_SECTION}
|
|
528
817
|
|
|
529
818
|
## The one contract
|
|
530
819
|
|
|
531
820
|
[\`frontend/src/lib/api.ts\`](frontend/src/lib/api.ts) takes request paths from
|
|
532
|
-
\`xano/routes.gen.ts\` (\`routePath("GET notes/{id}", { id })\`)
|
|
533
|
-
|
|
821
|
+
\`xano/routes.gen.ts\` (\`routePath("GET notes/{id}", { id })\`), request types from the
|
|
822
|
+
same file (\`RouteInputs["POST create_note"]\`, \`MessageInputs[...]\` for realtime), and
|
|
823
|
+
response types from the query defs (\`import type\` + \`InferResponse\`).
|
|
534
824
|
Never hand-type a URL or a request body \u2014 change a def and the frontend follows.
|
|
535
825
|
|
|
536
826
|
\`npm run xano:routes\` writes \`xano/routes.gen.ts\` from the defs; \`dev\`, \`build\`
|
|
@@ -552,27 +842,78 @@ ${preset.readmeFrontendSection(choice.icons)}
|
|
|
552
842
|
|
|
553
843
|
${renderReadmeThemingSection(choice)}
|
|
554
844
|
|
|
555
|
-
|
|
845
|
+
${README_ADD_ONS_SECTION}
|
|
556
846
|
|
|
557
|
-
|
|
847
|
+
${renderReadmeBuiltWith(sdkVersion)}
|
|
848
|
+
`;
|
|
849
|
+
}
|
|
850
|
+
function renderBackendReadme({ appName, install, sdkVersion }) {
|
|
851
|
+
return `# ${appName}
|
|
558
852
|
|
|
559
|
-
|
|
560
|
-
authentication (user/login/signup tables and endpoints). Install it with
|
|
561
|
-
\`npx xanosdk marketplace install @xano-sdk/auth\`, then register it in
|
|
562
|
-
\`xano/index.ts\`. Authentication only \u2014 **not** authorization: it has no
|
|
563
|
-
roles, permissions, or route guards, and its tokens carry no role claim.
|
|
564
|
-
Enforce roles off the caller's row with \`@xano/sdk\`: spread
|
|
565
|
-
\`...guard.role(userTable, "admin")\` into the endpoint's stack.
|
|
566
|
-
- More \`@xano-sdk/*\` packages register onto the same workspace. This list
|
|
567
|
-
does not update itself \u2014 run \`npx xanosdk marketplace list\` for the live
|
|
568
|
-
catalogue, \`npx xanosdk marketplace search <words>\` to narrow it, and
|
|
569
|
-
\`npx xanosdk marketplace details <package>\` to see what an add-on installs and
|
|
570
|
-
how to register it. All three work before you log in.
|
|
853
|
+
${XANO_BADGE}
|
|
571
854
|
|
|
572
|
-
|
|
573
|
-
|
|
855
|
+
${renderReadmeDescriptionSlot(appName)}
|
|
856
|
+
|
|
857
|
+
The backend under [\`xano/\`](xano/) is TypeScript, authored with the
|
|
858
|
+
[Xano SDK](https://github.com/xano-sdk/sdk) and running on [Xano](https://xano.com). It runs
|
|
859
|
+
on your machine as the **Xano Engine** and ships to Xano's cloud with the same command.
|
|
574
860
|
|
|
575
|
-
|
|
861
|
+
## Quick start
|
|
862
|
+
|
|
863
|
+
\`\`\`bash
|
|
864
|
+
${install?.manager ?? "npm"} install${install?.ignoreWorkspace === true ? " --ignore-workspace" : ""}
|
|
865
|
+
npm run xano:deploy # run the backend on the Xano Engine, on this machine
|
|
866
|
+
\`\`\`
|
|
867
|
+
|
|
868
|
+
No Xano account needed. Then author your backend in [\`xano/index.ts\`](xano/index.ts),
|
|
869
|
+
starting with the walkthrough in [\`xano/EXAMPLE.md\`](xano/EXAMPLE.md), and rerun
|
|
870
|
+
\`npm run xano:deploy\` after each change.
|
|
871
|
+
|
|
872
|
+
## Run it on your machine
|
|
873
|
+
|
|
874
|
+
The Xano Engine is Xano running on your machine. \`npm run xano:deploy\` typechecks the
|
|
875
|
+
backend and deploys it to the engine: no sign-in, and no network round-trip.
|
|
876
|
+
It prints the backend URL and a link that opens Xano's visual builder on the engine.
|
|
877
|
+
|
|
878
|
+
${README_ENGINE_BULLETS}
|
|
879
|
+
|
|
880
|
+
## Deploy to Xano's cloud
|
|
881
|
+
|
|
882
|
+
\`\`\`bash
|
|
883
|
+
npx xanosdk login # once, to authenticate against your Xano account
|
|
884
|
+
npm run xano:deploy:ephemeral # typecheck, then ship the backend to an ephemeral
|
|
885
|
+
\`\`\`
|
|
886
|
+
|
|
887
|
+
That deploys the backend to a live **ephemeral** environment on Xano and prints its URL.
|
|
888
|
+
Run it again to refresh the same environment; if it expired, a fresh one is created and
|
|
889
|
+
the new URL is called out. \`npx xanosdk status\` says who you are signed in as, which
|
|
890
|
+
workspace you are bound to, and which environment this project last deployed to. Your
|
|
891
|
+
**workspace** is reached through a release (\`deploy --ephemeral --test\`, \`release create\`,
|
|
892
|
+
\`promote\`); see the
|
|
893
|
+
[deploying guide](https://github.com/xano-sdk/sdk/blob/main/guides/deploying.md).
|
|
894
|
+
|
|
895
|
+
The other scripts:
|
|
896
|
+
|
|
897
|
+
- \`npm run xano:test\` runs the tests the DEPLOYED environment carries \u2014 the \`tests\`
|
|
898
|
+
on a query/function/middleware and any \`workflowTest()\`. It compiles nothing, so
|
|
899
|
+
deploy first. A failing suite exits 5, distinct from a crash. \`npx xanosdk deploy
|
|
900
|
+
./xano/index.ts --test\` does both in one step.
|
|
901
|
+
- \`npm run xano:typecheck\` type-checks \`xano/\` and its lambdas.
|
|
902
|
+
- \`npm run xano:export\` compiles the backend to \`workspace.json\` (don't commit it).
|
|
903
|
+
|
|
904
|
+
${README_LOCK_SECTION}
|
|
905
|
+
|
|
906
|
+
## Calling the backend
|
|
907
|
+
|
|
908
|
+
\`npm run xano:routes\` writes \`xano/routes.gen.ts\` from the defs: every endpoint's verb,
|
|
909
|
+
path (\`routePath("GET notes/{id}", { id })\`) and request type (\`RouteInputs["POST create_note"]\`).
|
|
910
|
+
A client takes them from there, and response types from the query defs (\`import type\` +
|
|
911
|
+
\`InferResponse\`), rather than hand-typing a URL. \`xano:check\` fails on a missing or stale
|
|
912
|
+
copy \u2014 commit it. \`npx xanosdk routes xano/index.ts\` lists every endpoint's verb + path.
|
|
913
|
+
|
|
914
|
+
${README_ADD_ONS_SECTION}
|
|
915
|
+
|
|
916
|
+
${renderReadmeBuiltWith(sdkVersion)}
|
|
576
917
|
`;
|
|
577
918
|
}
|
|
578
919
|
function renderXanoIndex({ appName }) {
|
|
@@ -743,8 +1084,11 @@ URL or a request body:
|
|
|
743
1084
|
groups share is keyed \`"<group>:<VERB> <name>"\`). Run
|
|
744
1085
|
\`npm run xano:routes\` after adding or renaming an endpoint (\`dev\`, \`build\` and
|
|
745
1086
|
\`typecheck\` also run it) and commit the file.
|
|
746
|
-
- **
|
|
747
|
-
|
|
1087
|
+
- **Request types** come from the same file: \`RouteInputs["POST create_note"]\`
|
|
1088
|
+
(\`ChannelInputs\`/\`MessageInputs\` for realtime). For runtime validation,
|
|
1089
|
+
\`npx xanosdk marketplace install zod\` adds zod schemas under the same keys.
|
|
1090
|
+
- **Response types** come from the defs with \`import type\` \u2014 \`InferResponse\`
|
|
1091
|
+
erases to nothing.
|
|
748
1092
|
- Never import a def as a value in the frontend. Its \`s.*\`/\`c.*\` stack calls run
|
|
749
1093
|
at module load, so one import for its \`getPath()\` pulls the backend graph it
|
|
750
1094
|
references and the SDK runtime into the bundle.
|
|
@@ -785,7 +1129,7 @@ function renderCodegenMarker({ sdkVersion }, origin, generatedAt, report, files)
|
|
|
785
1129
|
};
|
|
786
1130
|
return JSON.stringify(files === void 0 ? head : decodeMarkerWith(head, files), null, 2) + "\n";
|
|
787
1131
|
}
|
|
788
|
-
function renderCodegenReadme({ appName }, origin, envNames, preset) {
|
|
1132
|
+
function renderCodegenReadme({ appName, sdkVersion }, origin, envNames, preset) {
|
|
789
1133
|
const secrets = envNames.length === 0 ? "" : `
|
|
790
1134
|
## Fill in the backend env values
|
|
791
1135
|
|
|
@@ -814,19 +1158,21 @@ cleartext, which is why it is gitignored too.
|
|
|
814
1158
|
|
|
815
1159
|
${XANO_BADGE}
|
|
816
1160
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
backend
|
|
820
|
-
|
|
1161
|
+
${renderReadmeDescriptionSlot(appName)}
|
|
1162
|
+
|
|
1163
|
+
The backend under [\`xano/\`](xano/) is TypeScript pulled from ${describeCommittedOrigin(origin)}
|
|
1164
|
+
with the [Xano SDK](https://github.com/xano-sdk/sdk), and runs on [Xano](https://xano.com).${preset === null ? "" : ` The
|
|
1165
|
+
${preset.label} frontend under [\`frontend/\`](frontend/) is a starter \u2014 the pull carries a
|
|
1166
|
+
backend, not a UI.`}
|
|
821
1167
|
|
|
822
1168
|
## Run it
|
|
823
1169
|
|
|
824
1170
|
\`\`\`bash
|
|
825
1171
|
npm run xano:deploy # on the Xano Engine, on this machine (no account needed)
|
|
826
|
-
npm run dev # the starter frontend, pointed at it
|
|
827
|
-
|
|
1172
|
+
${preset === null ? "" : `npm run dev # the starter frontend, pointed at it
|
|
1173
|
+
`}
|
|
828
1174
|
npx xanosdk login # once, to authenticate against your Xano account
|
|
829
|
-
npm run xano:deploy:ephemeral # typecheck, build the frontend, ship both to an ephemeral
|
|
1175
|
+
${preset === null ? "npm run xano:deploy:ephemeral # typecheck, ship the backend to an ephemeral" : "npm run xano:deploy:ephemeral # typecheck, build the frontend, ship both to an ephemeral"}
|
|
830
1176
|
\`\`\`
|
|
831
1177
|
|
|
832
1178
|
## Read this before deploying
|
|
@@ -847,17 +1193,21 @@ translate cleanly on this pull. Read it before trusting the tree.
|
|
|
847
1193
|
${secrets}
|
|
848
1194
|
## Working on it
|
|
849
1195
|
|
|
850
|
-
|
|
1196
|
+
${preset === null ? `\`\`\`bash
|
|
1197
|
+
npm run xano:typecheck # xano/ and its lambdas
|
|
1198
|
+
npm run xano:export # compile the backend to workspace.json (don't commit it)
|
|
1199
|
+
npm run xano:routes # regenerate xano/routes.gen.ts: every endpoint's path and request type
|
|
1200
|
+
\`\`\`` : `\`\`\`bash
|
|
851
1201
|
npm run dev # run the starter frontend
|
|
852
1202
|
npm run typecheck # the whole project, both halves
|
|
853
1203
|
npm run xano:export # compile the backend to workspace.json (don't commit it)
|
|
854
|
-
npm run xano:routes # regenerate xano/routes.gen.ts
|
|
1204
|
+
npm run xano:routes # regenerate xano/routes.gen.ts: the frontend's paths and request types
|
|
855
1205
|
\`\`\`
|
|
856
1206
|
|
|
857
1207
|
[\`frontend/src/lib/api.ts\`](frontend/src/lib/api.ts) shows the one contract: paths
|
|
858
|
-
|
|
1208
|
+
and request types from \`xano/routes.gen.ts\`, response types from the query defs in \`xano/\`.`}
|
|
859
1209
|
|
|
860
|
-
${
|
|
1210
|
+
${renderReadmeBuiltWith(sdkVersion)}
|
|
861
1211
|
`;
|
|
862
1212
|
}
|
|
863
1213
|
function renderReadmeThemingSection(choice) {
|
|
@@ -1107,9 +1457,9 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
1107
1457
|
${extra}`;
|
|
1108
1458
|
}
|
|
1109
1459
|
function renderApiTs() {
|
|
1110
|
-
return `// The one contract: endpoint paths come from the generated
|
|
1111
|
-
//
|
|
1112
|
-
// or a request body: change a def and everything here follows.
|
|
1460
|
+
return `// The one contract: endpoint paths and request types come from the generated
|
|
1461
|
+
// route manifest, and response *types* from your xanosdk query defs. Never
|
|
1462
|
+
// hand-type a URL or a request body: change a def and everything here follows.
|
|
1113
1463
|
//
|
|
1114
1464
|
// Keep the backend out of the browser bundle:
|
|
1115
1465
|
// \u2022 Paths and verbs: \`routePath()\` / \`ROUTES\` from xano/routes.gen.ts, plain data
|
|
@@ -1117,16 +1467,20 @@ function renderApiTs() {
|
|
|
1117
1467
|
// regenerate it). Never import a def as a VALUE for its getPath()/verb:
|
|
1118
1468
|
// its s.*/c.* factory calls run at module load, so one import pulls in the
|
|
1119
1469
|
// backend graph it references and the SDK runtime that builds it.
|
|
1120
|
-
// \u2022
|
|
1470
|
+
// \u2022 Request types: \`RouteInputs["<VERB> <name>"]\` from the same file
|
|
1471
|
+
// (\`ChannelInputs\` / \`MessageInputs\` for realtime), types only. For
|
|
1472
|
+
// runtime validation, \`npx xanosdk marketplace install zod\` adds zod
|
|
1473
|
+
// schemas under the same keys.
|
|
1474
|
+
// \u2022 Response types: \`import type\` the def. InferResponse erases to nothing.
|
|
1121
1475
|
//
|
|
1122
1476
|
// Nothing here imports the manifest yet \u2014 an add-on's endpoints may already be
|
|
1123
1477
|
// in it. Wire an endpoint (one in xano/, or an add-on's) like:
|
|
1124
1478
|
//
|
|
1125
|
-
// import type {
|
|
1479
|
+
// import type { InferResponse } from "@xano/sdk";
|
|
1126
1480
|
// import type { createNoteQuery } from "../../../xano/api/create-note.js";
|
|
1127
|
-
// import { ROUTES, routePath } from "../../../xano/routes.gen.js";
|
|
1481
|
+
// import { ROUTES, routePath, type RouteInputs } from "../../../xano/routes.gen.js";
|
|
1128
1482
|
//
|
|
1129
|
-
// export type CreateNoteBody =
|
|
1483
|
+
// export type CreateNoteBody = RouteInputs["POST create_note"];
|
|
1130
1484
|
// export type Note = InferResponse<typeof createNoteQuery>;
|
|
1131
1485
|
//
|
|
1132
1486
|
// export async function createNote(body: CreateNoteBody): Promise<Note> {
|
|
@@ -1162,346 +1516,122 @@ export const XANO_HOST: string =
|
|
|
1162
1516
|
`;
|
|
1163
1517
|
}
|
|
1164
1518
|
|
|
1165
|
-
// src/emit/frontend-
|
|
1519
|
+
// src/emit/frontend-react.ts
|
|
1166
1520
|
var CODE_CLASS = "bg-muted rounded px-1.5 py-0.5 font-mono text-sm";
|
|
1167
|
-
function
|
|
1168
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1169
|
-
}
|
|
1170
|
-
function svelteText(s) {
|
|
1171
|
-
return htmlText(s).replace(/\{/g, "{").replace(/\}/g, "}");
|
|
1521
|
+
function jsxText(s) {
|
|
1522
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\{/g, "{").replace(/\}/g, "}");
|
|
1172
1523
|
}
|
|
1173
1524
|
function renderNode(node) {
|
|
1174
|
-
return "code" in node ? `<code
|
|
1525
|
+
return "code" in node ? `<code className="${CODE_CLASS}">${jsxText(node.code)}</code>` : jsxText(node.text);
|
|
1175
1526
|
}
|
|
1176
1527
|
function renderNodes(nodes) {
|
|
1177
1528
|
return nodes.map(renderNode).join("");
|
|
1178
1529
|
}
|
|
1179
1530
|
function renderApp(landing, choice) {
|
|
1180
|
-
const steps = landing.steps.map((step) => `
|
|
1531
|
+
const steps = landing.steps.map((step) => ` <li>${renderNodes(step)}</li>`).join("\n");
|
|
1181
1532
|
const toggle = choice.dark === "toggle";
|
|
1182
|
-
const icons = iconLibraryOf(choice.icons).
|
|
1183
|
-
return
|
|
1184
|
-
${icons.arrowImport}
|
|
1185
|
-
|
|
1186
|
-
import { Button } from "$lib/components/ui/button/index.js";
|
|
1187
|
-
import * as Card from "$lib/components/ui/card/index.js";${toggle ? `
|
|
1188
|
-
import ModeToggle from "$lib/components/mode-toggle.svelte";` : ""}
|
|
1189
|
-
</script>
|
|
1190
|
-
|
|
1191
|
-
<main class="mx-auto flex min-h-screen max-w-2xl flex-col justify-center p-8">${toggle ? `
|
|
1192
|
-
<div class="mb-4 flex justify-end">
|
|
1193
|
-
<ModeToggle />
|
|
1194
|
-
</div>` : ""}
|
|
1195
|
-
<Card.Root>
|
|
1196
|
-
<Card.Header>
|
|
1197
|
-
<Card.Title class="text-3xl tracking-tight">${svelteText(landing.title)}</Card.Title>
|
|
1198
|
-
<Card.Description class="text-base">
|
|
1199
|
-
${renderNodes(landing.lead)}
|
|
1200
|
-
</Card.Description>
|
|
1201
|
-
</Card.Header>
|
|
1202
|
-
<Card.Content>
|
|
1203
|
-
<ol class="text-muted-foreground list-inside list-decimal space-y-2">
|
|
1204
|
-
${steps}
|
|
1205
|
-
</ol>
|
|
1206
|
-
</Card.Content>
|
|
1207
|
-
<Card.Footer>
|
|
1208
|
-
<!-- Components come from shadcn-svelte \u2014 add more with
|
|
1209
|
-
\`npx shadcn-svelte@latest add <name>\`. -->
|
|
1210
|
-
<Button href="${landing.cta.href}" target="_blank" rel="noreferrer">
|
|
1211
|
-
${svelteText(landing.cta.label)}
|
|
1212
|
-
<${icons.arrowComponent} />
|
|
1213
|
-
</Button>
|
|
1214
|
-
</Card.Footer>
|
|
1215
|
-
</Card.Root>
|
|
1216
|
-
</main>
|
|
1217
|
-
`;
|
|
1218
|
-
}
|
|
1219
|
-
function renderSvelteKitPlugin() {
|
|
1220
|
-
return `sveltekit({
|
|
1221
|
-
// RUNES MODE, FORCED \u2014 not left to Svelte 5's per-component detection.
|
|
1222
|
-
//
|
|
1223
|
-
// By default a component that uses \`export let\` and \`<slot>\` simply
|
|
1224
|
-
// compiles in legacy mode and works, so the Svelte 4 API an assistant
|
|
1225
|
-
// reaches for from older training data produces no error at all. Pinning
|
|
1226
|
-
// runes here turns that into a compile failure naming the file, which is
|
|
1227
|
-
// the same guidance the agent brief gives, enforced instead of stated.
|
|
1228
|
-
//
|
|
1229
|
-
// \`node_modules\` is exempted (returning undefined restores detection):
|
|
1230
|
-
// an installed library compiles under whatever mode ITS author wrote for,
|
|
1231
|
-
// and forcing ours onto it would break dependencies this project does not
|
|
1232
|
-
// own. Removable in Svelte 6, where runes stop being opt-in.
|
|
1233
|
-
compilerOptions: {
|
|
1234
|
-
runes: ({ filename }) =>
|
|
1235
|
-
filename.split(/[/\\\\]/).includes("node_modules") ? undefined : true,
|
|
1236
|
-
},
|
|
1237
|
-
// No \`preprocess\`. vite-plugin-svelte 7 compiles \`<script lang="ts">\`
|
|
1238
|
-
// itself, so \`vitePreprocess()\` is the no-op it looks like \u2014 \`sv create\`
|
|
1239
|
-
// emits none either. Verified against a real build: components typecheck
|
|
1240
|
-
// and compile without it.
|
|
1241
|
-
//
|
|
1242
|
-
// The deploy target is a static host with NO server runtime \u2014 Xano is the
|
|
1243
|
-
// backend. Routes are PRERENDERED (see frontend/src/routes/+layout.ts):
|
|
1244
|
-
// each becomes its own HTML file at build time, and the host resolves a URL
|
|
1245
|
-
// to it by trying the exact key, then \`{path}.html\`, then
|
|
1246
|
-
// \`{path}/index.html\`. That is why no \`trailingSlash\` is configured \u2014 both
|
|
1247
|
-
// output shapes resolve, so this stays on SvelteKit's default.
|
|
1248
|
-
//
|
|
1249
|
-
// The SDK's adapter, writing to frontend/dist. Every prerendered route is
|
|
1250
|
-
// its own file; a route that sets \`prerender = false\` (a detail page for
|
|
1251
|
-
// rows created at runtime) has no file, so the host answers it with
|
|
1252
|
-
// 404.html, which the adapter writes as the app shell whenever such a
|
|
1253
|
-
// route exists: the shell boots the app and renders the route in the
|
|
1254
|
-
// browser, so a reload or a deep link works. While every route is
|
|
1255
|
-
// prerendered, 404.html stays the prerendered /404 page.
|
|
1256
|
-
//
|
|
1257
|
-
// Do not swap it for \`@sveltejs/adapter-static\` with a \`fallback\`: that
|
|
1258
|
-
// writes the shell at index.html, over the prerendered home page.
|
|
1259
|
-
//
|
|
1260
|
-
// \`xanosdk deploy --static-env\` still finds its anchor: every document
|
|
1261
|
-
// renders from app.html, so each has a <head> that gets the injected globals.
|
|
1262
|
-
adapter: adapter({ out: "frontend/dist" }),
|
|
1263
|
-
// Absolute asset URLs, pinned rather than left to the default. Prerendered
|
|
1264
|
-
// pages live at varying URL depths, and a relative \`../_app/\u2026\` computed for
|
|
1265
|
-
// one depth 404s at another.
|
|
1266
|
-
paths: { relative: false },
|
|
1267
|
-
prerender: {
|
|
1268
|
-
// A hash link in the shared layout \u2014 \`<a href="#services">\`, the default
|
|
1269
|
-
// shape of a one-page site's nav \u2014 renders into EVERY prerendered route,
|
|
1270
|
-
// including the scaffold's own /404. There it resolves to /404#services,
|
|
1271
|
-
// an anchor that by definition is not on that page, and SvelteKit's
|
|
1272
|
-
// default handler THROWS: the vite build succeeds and the prerender pass
|
|
1273
|
-
// after it kills the build, with a stack trace naming only files inside
|
|
1274
|
-
// node_modules and a route the author never wrote.
|
|
1275
|
-
//
|
|
1276
|
-
// So ignore a missing id on /404 specifically, rather than reaching for a
|
|
1277
|
-
// blanket \`handleMissingId: "warn"\`. A hash link that is genuinely broken
|
|
1278
|
-
// on a real route still fails the build, which is the check worth keeping.
|
|
1279
|
-
handleMissingId: ({ path, message }) => {
|
|
1280
|
-
if (path === "/404") return;
|
|
1281
|
-
throw new Error(message);
|
|
1282
|
-
},
|
|
1283
|
-
},
|
|
1284
|
-
// SvelteKit's project layout, redirected under frontend/ so this stays a
|
|
1285
|
-
// single-root project: one package.json, one tsconfig.json, and xano/
|
|
1286
|
-
// sitting alongside as the backend half. NOTE these are what make
|
|
1287
|
-
// frontend/src/routes the routes directory \u2014 a file added under a
|
|
1288
|
-
// top-level src/routes/ is not a route and will not be served.
|
|
1289
|
-
files: {
|
|
1290
|
-
routes: "frontend/src/routes",
|
|
1291
|
-
lib: "frontend/src/lib",
|
|
1292
|
-
appTemplate: "frontend/src/app.html",
|
|
1293
|
-
assets: "frontend/static",
|
|
1294
|
-
},
|
|
1295
|
-
})`;
|
|
1296
|
-
}
|
|
1297
|
-
function renderAppHtml({ appName }, choice) {
|
|
1298
|
-
return `<!doctype html>
|
|
1299
|
-
<html lang="en">
|
|
1300
|
-
<head>
|
|
1301
|
-
<meta charset="utf-8" />
|
|
1302
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1303
|
-
<title>${htmlText(appName)}</title>
|
|
1304
|
-
${renderThemeScript(choice.dark)}
|
|
1305
|
-
<!-- Drop a favicon.png into frontend/static/ and link it here:
|
|
1306
|
-
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
|
1307
|
-
Everything in that directory is copied to the site root verbatim. -->
|
|
1308
|
-
<!-- Keep this inside <head>: \`xanosdk deploy --static-env\` injects the
|
|
1309
|
-
backend URL here as a window global that frontend/src/lib/api.ts reads. -->
|
|
1310
|
-
%sveltekit.head%
|
|
1311
|
-
</head>
|
|
1312
|
-
<body data-sveltekit-preload-data="hover">
|
|
1313
|
-
<div style="display: contents">%sveltekit.body%</div>
|
|
1314
|
-
</body>
|
|
1315
|
-
</html>
|
|
1316
|
-
`;
|
|
1317
|
-
}
|
|
1318
|
-
function renderModeToggleSvelte(icons) {
|
|
1319
|
-
return `<script lang="ts">
|
|
1320
|
-
${icons.modeImports.join("\n ")}
|
|
1321
|
-
|
|
1322
|
-
import { Button } from "$lib/components/ui/button/index.js";
|
|
1323
|
-
import { getMode, setMode, watchMode, type Mode } from "$lib/theme.js";
|
|
1324
|
-
|
|
1325
|
-
/** system \u2192 light \u2192 dark \u2192 system. */
|
|
1326
|
-
const next: Record<Mode, Mode> = { system: "light", light: "dark", dark: "system" };
|
|
1327
|
-
|
|
1328
|
-
const icons = {
|
|
1329
|
-
system: ${icons.modeComponents.system},
|
|
1330
|
-
light: ${icons.modeComponents.light},
|
|
1331
|
-
dark: ${icons.modeComponents.dark},
|
|
1332
|
-
};
|
|
1333
|
-
|
|
1334
|
-
// Starts at "system" so the prerendered HTML is deterministic; the effect
|
|
1335
|
-
// corrects it in the browser, after the inline script has already applied
|
|
1336
|
-
// the right class.
|
|
1337
|
-
let mode = $state<Mode>("system");
|
|
1338
|
-
|
|
1339
|
-
$effect(() => {
|
|
1340
|
-
mode = getMode();
|
|
1341
|
-
return watchMode((m) => (mode = m));
|
|
1342
|
-
});
|
|
1343
|
-
|
|
1344
|
-
function change() {
|
|
1345
|
-
const target = next[mode];
|
|
1346
|
-
setMode(target);
|
|
1347
|
-
mode = target;
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
const Icon = $derived(icons[mode]);
|
|
1351
|
-
</script>
|
|
1352
|
-
|
|
1353
|
-
<Button variant="ghost" size="icon" onclick={change} aria-label="Switch to {next[mode]} theme">
|
|
1354
|
-
<Icon />
|
|
1355
|
-
</Button>
|
|
1356
|
-
`;
|
|
1357
|
-
}
|
|
1358
|
-
function renderAppDts() {
|
|
1359
|
-
return `// See https://svelte.dev/docs/kit/types#app.d.ts
|
|
1360
|
-
declare global {
|
|
1361
|
-
namespace App {
|
|
1362
|
-
// interface Error {}
|
|
1363
|
-
// interface Locals {}
|
|
1364
|
-
// interface PageData {}
|
|
1365
|
-
// interface PageState {}
|
|
1366
|
-
// interface Platform {}
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1533
|
+
const icons = iconLibraryOf(choice.icons).react;
|
|
1534
|
+
return `${icons.arrowImport}
|
|
1369
1535
|
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
</h1>
|
|
1412
|
-
<p class="text-muted-foreground text-sm">
|
|
1413
|
-
{message !== "" ? message : "The link may be broken, or the page may have moved."}
|
|
1414
|
-
</p>
|
|
1415
|
-
<a
|
|
1416
|
-
href="/"
|
|
1417
|
-
class="bg-primary text-primary-foreground ring-offset-background focus-visible:ring-ring inline-flex h-10 items-center rounded-md px-5 text-sm font-medium transition-opacity hover:opacity-90 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none"
|
|
1418
|
-
>
|
|
1419
|
-
Back to safety
|
|
1420
|
-
</a>
|
|
1421
|
-
</div>
|
|
1422
|
-
</main>
|
|
1423
|
-
`;
|
|
1536
|
+
import { Button } from "@/components/ui/button";${toggle ? `
|
|
1537
|
+
import { ModeToggle } from "@/components/mode-toggle";` : ""}
|
|
1538
|
+
import {
|
|
1539
|
+
Card,
|
|
1540
|
+
CardContent,
|
|
1541
|
+
CardDescription,
|
|
1542
|
+
CardFooter,
|
|
1543
|
+
CardHeader,
|
|
1544
|
+
CardTitle,
|
|
1545
|
+
} from "@/components/ui/card";
|
|
1546
|
+
|
|
1547
|
+
export default function App() {
|
|
1548
|
+
return (
|
|
1549
|
+
<main className="mx-auto flex min-h-screen max-w-2xl flex-col justify-center p-8">${toggle ? `
|
|
1550
|
+
<div className="mb-4 flex justify-end">
|
|
1551
|
+
<ModeToggle />
|
|
1552
|
+
</div>` : ""}
|
|
1553
|
+
<Card>
|
|
1554
|
+
<CardHeader>
|
|
1555
|
+
<CardTitle className="text-3xl tracking-tight">${jsxText(landing.title)}</CardTitle>
|
|
1556
|
+
<CardDescription className="text-base">
|
|
1557
|
+
${renderNodes(landing.lead)}
|
|
1558
|
+
</CardDescription>
|
|
1559
|
+
</CardHeader>
|
|
1560
|
+
<CardContent>
|
|
1561
|
+
<ol className="text-muted-foreground list-inside list-decimal space-y-2">
|
|
1562
|
+
${steps}
|
|
1563
|
+
</ol>
|
|
1564
|
+
</CardContent>
|
|
1565
|
+
<CardFooter>
|
|
1566
|
+
{/* asChild renders the Button's styles onto the anchor. Components come
|
|
1567
|
+
from shadcn/ui \u2014 add more with \`npx shadcn@latest add <name>\`. */}
|
|
1568
|
+
<Button asChild>
|
|
1569
|
+
<a href="${landing.cta.href}" target="_blank" rel="noreferrer">
|
|
1570
|
+
${jsxText(landing.cta.label)} <${icons.arrowComponent} />
|
|
1571
|
+
</a>
|
|
1572
|
+
</Button>
|
|
1573
|
+
</CardFooter>
|
|
1574
|
+
</Card>
|
|
1575
|
+
</main>
|
|
1576
|
+
);
|
|
1424
1577
|
}
|
|
1425
|
-
function renderNotFoundRoute() {
|
|
1426
|
-
return `<script lang="ts">
|
|
1427
|
-
// Prerendered to 404.html, which is what tells the static host to answer an
|
|
1428
|
-
// unmatched path with a real 404 instead of quietly serving the app shell
|
|
1429
|
-
// with a 200 (a missing asset would then reach the browser as HTML, which it
|
|
1430
|
-
// reports as "Unexpected token '<'").
|
|
1431
|
-
//
|
|
1432
|
-
// It must be a route, not +error.svelte: SvelteKit never prerenders the error
|
|
1433
|
-
// component to a file, so relying on it would ship no 404.html at all.
|
|
1434
|
-
//
|
|
1435
|
-
// Delete this directory and an unmatched path serves the home page with a 200
|
|
1436
|
-
// instead \u2014 which is worse, not better, so keep it unless you have a reason.
|
|
1437
|
-
import ErrorState from "$lib/components/error-state.svelte";
|
|
1438
|
-
</script>
|
|
1439
|
-
|
|
1440
|
-
<svelte:head>
|
|
1441
|
-
<title>Page not found</title>
|
|
1442
|
-
</svelte:head>
|
|
1443
|
-
|
|
1444
|
-
<ErrorState status={404} />
|
|
1445
1578
|
`;
|
|
1446
1579
|
}
|
|
1447
|
-
function
|
|
1448
|
-
return
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
</svelte:head>
|
|
1459
|
-
|
|
1460
|
-
<ErrorState status={page.status} message={page.error?.message ?? ""} />
|
|
1461
|
-
`;
|
|
1462
|
-
}
|
|
1463
|
-
function renderLayoutSvelte() {
|
|
1464
|
-
return `<script lang="ts">
|
|
1465
|
-
// The shared token stylesheet \u2014 semantic colors (bg-primary,
|
|
1466
|
-
// text-muted-foreground) plus the Tailwind v4 theme. Imported once here so
|
|
1467
|
-
// every route gets it.
|
|
1468
|
-
import "../index.css";
|
|
1469
|
-
|
|
1470
|
-
let { children } = $props();
|
|
1471
|
-
</script>
|
|
1472
|
-
|
|
1473
|
-
{@render children()}
|
|
1580
|
+
function renderMainTsx() {
|
|
1581
|
+
return `import { StrictMode } from "react";
|
|
1582
|
+
import { createRoot } from "react-dom/client";
|
|
1583
|
+
import App from "./App.js";
|
|
1584
|
+
import "./index.css";
|
|
1585
|
+
|
|
1586
|
+
createRoot(document.getElementById("root")!).render(
|
|
1587
|
+
<StrictMode>
|
|
1588
|
+
<App />
|
|
1589
|
+
</StrictMode>,
|
|
1590
|
+
);
|
|
1474
1591
|
`;
|
|
1475
1592
|
}
|
|
1476
1593
|
function renderComponentsJson(choice) {
|
|
1477
1594
|
const config = {
|
|
1478
|
-
$schema: "https://shadcn
|
|
1595
|
+
$schema: "https://ui.shadcn.com/schema.json",
|
|
1596
|
+
style: "new-york",
|
|
1597
|
+
rsc: false,
|
|
1598
|
+
tsx: true,
|
|
1479
1599
|
tailwind: {
|
|
1480
|
-
// v4 has no config file; the theme lives in the stylesheet
|
|
1481
|
-
|
|
1600
|
+
// v4 has no config file; the theme lives in the stylesheet.
|
|
1601
|
+
config: "",
|
|
1482
1602
|
css: "frontend/src/index.css",
|
|
1483
|
-
//
|
|
1484
|
-
//
|
|
1485
|
-
|
|
1603
|
+
// The base color the scaffold's stylesheet was rendered from. The CLI
|
|
1604
|
+
// reads it for the few colors a generated component hard-codes rather
|
|
1605
|
+
// than tokenises, so a value that disagrees with index.css produces
|
|
1606
|
+
// components that clash with everything already there. An imported
|
|
1607
|
+
// registry theme belongs to no upstream base and falls back to neutral.
|
|
1608
|
+
baseColor: choice.theme.baseColor ?? "neutral",
|
|
1609
|
+
cssVariables: true,
|
|
1610
|
+
prefix: ""
|
|
1486
1611
|
},
|
|
1487
1612
|
aliases: {
|
|
1488
|
-
components: "
|
|
1489
|
-
utils: "
|
|
1490
|
-
ui: "
|
|
1491
|
-
|
|
1492
|
-
|
|
1613
|
+
components: "@/components",
|
|
1614
|
+
utils: "@/lib/utils",
|
|
1615
|
+
ui: "@/components/ui",
|
|
1616
|
+
lib: "@/lib",
|
|
1617
|
+
hooks: "@/hooks"
|
|
1493
1618
|
},
|
|
1494
|
-
|
|
1495
|
-
|
|
1619
|
+
// Omitted entirely for a set the CLI does not know — writing an
|
|
1620
|
+
// unrecognised value would break `add` for every component, which is worse
|
|
1621
|
+
// than icons defaulting to lucide. See IconLibrary.componentsJson.
|
|
1622
|
+
...iconLibraryOf(choice.icons).componentsJson === void 0 ? {} : { iconLibrary: iconLibraryOf(choice.icons).componentsJson }
|
|
1496
1623
|
};
|
|
1497
1624
|
return JSON.stringify(config, null, 2) + "\n";
|
|
1498
1625
|
}
|
|
1499
|
-
function
|
|
1500
|
-
return
|
|
1501
|
-
|
|
1626
|
+
function renderButtonTsx() {
|
|
1627
|
+
return `import * as React from "react"
|
|
1628
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
1629
|
+
import { cn } from "cn"
|
|
1630
|
+
import { Slot } from "radix-ui"
|
|
1502
1631
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1632
|
+
const buttonVariants = cva(
|
|
1633
|
+
"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
1634
|
+
{
|
|
1505
1635
|
variants: {
|
|
1506
1636
|
variant: {
|
|
1507
1637
|
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
@@ -1509,503 +1639,344 @@ function renderButtonSvelte() {
|
|
|
1509
1639
|
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
1510
1640
|
outline:
|
|
1511
1641
|
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
1512
|
-
secondary:
|
|
1513
|
-
|
|
1642
|
+
secondary:
|
|
1643
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
1644
|
+
ghost:
|
|
1645
|
+
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
1514
1646
|
link: "text-primary underline-offset-4 hover:underline",
|
|
1515
1647
|
},
|
|
1516
1648
|
size: {
|
|
1517
1649
|
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
1650
|
+
xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
1518
1651
|
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
|
1519
1652
|
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
1520
1653
|
icon: "size-9",
|
|
1654
|
+
"icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
1655
|
+
"icon-sm": "size-8",
|
|
1656
|
+
"icon-lg": "size-10",
|
|
1521
1657
|
},
|
|
1522
1658
|
},
|
|
1523
1659
|
defaultVariants: {
|
|
1524
1660
|
variant: "default",
|
|
1525
1661
|
size: "default",
|
|
1526
1662
|
},
|
|
1527
|
-
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
</script>
|
|
1555
|
-
|
|
1556
|
-
{#if href}
|
|
1557
|
-
<a
|
|
1558
|
-
bind:this={ref}
|
|
1559
|
-
data-slot="button"
|
|
1560
|
-
class={cn(buttonVariants({ variant, size }), className)}
|
|
1561
|
-
{href}
|
|
1562
|
-
{...restProps}
|
|
1563
|
-
>
|
|
1564
|
-
{@render children?.()}
|
|
1565
|
-
</a>
|
|
1566
|
-
{:else}
|
|
1567
|
-
<button
|
|
1568
|
-
bind:this={ref}
|
|
1569
|
-
data-slot="button"
|
|
1570
|
-
class={cn(buttonVariants({ variant, size }), className)}
|
|
1571
|
-
{type}
|
|
1572
|
-
{...restProps}
|
|
1573
|
-
>
|
|
1574
|
-
{@render children?.()}
|
|
1575
|
-
</button>
|
|
1576
|
-
{/if}
|
|
1663
|
+
}
|
|
1664
|
+
)
|
|
1665
|
+
|
|
1666
|
+
function Button({
|
|
1667
|
+
className,
|
|
1668
|
+
variant = "default",
|
|
1669
|
+
size = "default",
|
|
1670
|
+
asChild = false,
|
|
1671
|
+
...props
|
|
1672
|
+
}: React.ComponentProps<"button"> &
|
|
1673
|
+
VariantProps<typeof buttonVariants> & {
|
|
1674
|
+
asChild?: boolean
|
|
1675
|
+
}) {
|
|
1676
|
+
const Comp = asChild ? Slot.Root : "button"
|
|
1677
|
+
|
|
1678
|
+
return (
|
|
1679
|
+
<Comp
|
|
1680
|
+
data-slot="button"
|
|
1681
|
+
data-variant={variant}
|
|
1682
|
+
data-size={size}
|
|
1683
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
1684
|
+
{...props}
|
|
1685
|
+
/>
|
|
1686
|
+
)
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
export { Button, buttonVariants }
|
|
1577
1690
|
`;
|
|
1578
1691
|
}
|
|
1579
|
-
function
|
|
1580
|
-
return `
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1692
|
+
function renderModeToggleTsx(icons) {
|
|
1693
|
+
return `import { useEffect, useState } from "react";
|
|
1694
|
+
${icons.modeImports.join("\n")}
|
|
1695
|
+
|
|
1696
|
+
import { Button } from "@/components/ui/button";
|
|
1697
|
+
import { getMode, setMode, watchMode, type Mode } from "@/lib/theme";
|
|
1698
|
+
|
|
1699
|
+
/** system \u2192 light \u2192 dark \u2192 system. */
|
|
1700
|
+
const NEXT: Record<Mode, Mode> = { system: "light", light: "dark", dark: "system" };
|
|
1701
|
+
|
|
1702
|
+
const ICON = {
|
|
1703
|
+
system: ${icons.modeComponents.system},
|
|
1704
|
+
light: ${icons.modeComponents.light},
|
|
1705
|
+
dark: ${icons.modeComponents.dark},
|
|
1706
|
+
};
|
|
1707
|
+
|
|
1708
|
+
export function ModeToggle() {
|
|
1709
|
+
// Read on mount, not during render: the inline script in index.html has
|
|
1710
|
+
// already applied the class, and touching localStorage while rendering would
|
|
1711
|
+
// break if this component is ever server-rendered.
|
|
1712
|
+
const [mode, setLocal] = useState<Mode>("system");
|
|
1713
|
+
useEffect(() => {
|
|
1714
|
+
setLocal(getMode());
|
|
1715
|
+
return watchMode(setLocal);
|
|
1716
|
+
}, []);
|
|
1717
|
+
|
|
1718
|
+
const Icon = ICON[mode];
|
|
1719
|
+
const change = () => {
|
|
1720
|
+
const next = NEXT[mode];
|
|
1721
|
+
setMode(next);
|
|
1722
|
+
setLocal(next);
|
|
1723
|
+
};
|
|
1724
|
+
|
|
1725
|
+
return (
|
|
1726
|
+
<Button variant="ghost" size="icon" onClick={change} aria-label={\`Switch to \${NEXT[mode]} theme\`}>
|
|
1727
|
+
<Icon />
|
|
1728
|
+
</Button>
|
|
1729
|
+
);
|
|
1588
1730
|
}
|
|
1589
|
-
function cardPart(slot, tag, classes) {
|
|
1590
|
-
return `<script lang="ts">
|
|
1591
|
-
import type { HTMLAttributes } from "svelte/elements";
|
|
1592
|
-
import type { WithElementRef } from "bits-ui";
|
|
1593
|
-
import { cn } from "$lib/utils.js";
|
|
1594
|
-
|
|
1595
|
-
let {
|
|
1596
|
-
class: className,
|
|
1597
|
-
ref = $bindable(null),
|
|
1598
|
-
children,
|
|
1599
|
-
...restProps
|
|
1600
|
-
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
1601
|
-
</script>
|
|
1602
|
-
|
|
1603
|
-
<${tag}
|
|
1604
|
-
bind:this={ref}
|
|
1605
|
-
data-slot="${slot}"
|
|
1606
|
-
class={cn("${classes}", className)}
|
|
1607
|
-
{...restProps}
|
|
1608
|
-
>
|
|
1609
|
-
{@render children?.()}
|
|
1610
|
-
</${tag}>
|
|
1611
1731
|
`;
|
|
1612
1732
|
}
|
|
1613
|
-
function
|
|
1614
|
-
return `import
|
|
1615
|
-
import
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1733
|
+
function renderCardTsx() {
|
|
1734
|
+
return `import * as React from "react"
|
|
1735
|
+
import { cn } from "cn"
|
|
1736
|
+
|
|
1737
|
+
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
|
1738
|
+
return (
|
|
1739
|
+
<div
|
|
1740
|
+
data-slot="card"
|
|
1741
|
+
className={cn(
|
|
1742
|
+
"flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm",
|
|
1743
|
+
className
|
|
1744
|
+
)}
|
|
1745
|
+
{...props}
|
|
1746
|
+
/>
|
|
1747
|
+
)
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
1751
|
+
return (
|
|
1752
|
+
<div
|
|
1753
|
+
data-slot="card-header"
|
|
1754
|
+
className={cn(
|
|
1755
|
+
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
|
1756
|
+
className
|
|
1757
|
+
)}
|
|
1758
|
+
{...props}
|
|
1759
|
+
/>
|
|
1760
|
+
)
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
1764
|
+
return (
|
|
1765
|
+
<div
|
|
1766
|
+
data-slot="card-title"
|
|
1767
|
+
className={cn("leading-none font-semibold", className)}
|
|
1768
|
+
{...props}
|
|
1769
|
+
/>
|
|
1770
|
+
)
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
1774
|
+
return (
|
|
1775
|
+
<div
|
|
1776
|
+
data-slot="card-description"
|
|
1777
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
1778
|
+
{...props}
|
|
1779
|
+
/>
|
|
1780
|
+
)
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
1784
|
+
return (
|
|
1785
|
+
<div
|
|
1786
|
+
data-slot="card-action"
|
|
1787
|
+
className={cn(
|
|
1788
|
+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
1789
|
+
className
|
|
1790
|
+
)}
|
|
1791
|
+
{...props}
|
|
1792
|
+
/>
|
|
1793
|
+
)
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
1797
|
+
return (
|
|
1798
|
+
<div
|
|
1799
|
+
data-slot="card-content"
|
|
1800
|
+
className={cn("px-6", className)}
|
|
1801
|
+
{...props}
|
|
1802
|
+
/>
|
|
1803
|
+
)
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
1807
|
+
return (
|
|
1808
|
+
<div
|
|
1809
|
+
data-slot="card-footer"
|
|
1810
|
+
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
|
1811
|
+
{...props}
|
|
1812
|
+
/>
|
|
1813
|
+
)
|
|
1814
|
+
}
|
|
1620
1815
|
|
|
1621
1816
|
export {
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
Content as CardContent,
|
|
1631
|
-
Description as CardDescription,
|
|
1632
|
-
Footer as CardFooter,
|
|
1633
|
-
Header as CardHeader,
|
|
1634
|
-
Title as CardTitle,
|
|
1635
|
-
};
|
|
1817
|
+
Card,
|
|
1818
|
+
CardHeader,
|
|
1819
|
+
CardFooter,
|
|
1820
|
+
CardTitle,
|
|
1821
|
+
CardAction,
|
|
1822
|
+
CardDescription,
|
|
1823
|
+
CardContent,
|
|
1824
|
+
}
|
|
1636
1825
|
`;
|
|
1637
1826
|
}
|
|
1638
|
-
var
|
|
1639
|
-
id: "
|
|
1640
|
-
label: "
|
|
1827
|
+
var reactPreset = {
|
|
1828
|
+
id: "react",
|
|
1829
|
+
label: "React + Vite",
|
|
1641
1830
|
dependencies: {
|
|
1642
|
-
// shadcn
|
|
1643
|
-
//
|
|
1644
|
-
//
|
|
1645
|
-
//
|
|
1646
|
-
//
|
|
1647
|
-
//
|
|
1831
|
+
// shadcn/ui's runtime surface. `radix-ui` is the unified primitives
|
|
1832
|
+
// package current components import (`Slot.Root` powers `asChild`) — not
|
|
1833
|
+
// the per-primitive `@radix-ui/react-*` packages, which it superseded.
|
|
1834
|
+
// cva types the variants, and `cn` is shadcn's own class merger, which its
|
|
1835
|
+
// components import directly (it replaced clsx + tailwind-merge upstream).
|
|
1836
|
+
// Pre-installed so `npx shadcn@latest add <component>` works without a
|
|
1837
|
+
// separate npm i.
|
|
1648
1838
|
//
|
|
1649
|
-
// The icon package is `--icons`, merged in by
|
|
1650
|
-
// `iconBinding` below
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
"
|
|
1839
|
+
// The icon package is deliberately absent: it is `--icons`, merged in by
|
|
1840
|
+
// renderPackageJson from `iconBinding` below. Naming lucide here too would
|
|
1841
|
+
// install it alongside whatever the user actually chose.
|
|
1842
|
+
"class-variance-authority": "^0.7.1",
|
|
1843
|
+
cn: "^0.4.0",
|
|
1844
|
+
"radix-ui": "^1.6.7",
|
|
1845
|
+
react: "^19.1.0",
|
|
1846
|
+
"react-dom": "^19.1.0"
|
|
1655
1847
|
},
|
|
1656
1848
|
devDependencies: {
|
|
1657
|
-
// The SDK's adapter (`@xano/sdk/sveltekit`) bundles its server half with
|
|
1658
|
-
// esbuild; Vite 8 does not install it.
|
|
1659
|
-
esbuild: "^0.28.1",
|
|
1660
|
-
// 2.70 peer-accepts vite ^8 and vite-plugin-svelte ^7 — the versions
|
|
1661
|
-
// pinned below. Bump this line and those two together.
|
|
1662
|
-
"@sveltejs/kit": "^2.70.2",
|
|
1663
|
-
// 7.x, matching what `sv create` installs. It peer-requires vite ^8, so
|
|
1664
|
-
// this line and the `vite` pin move together or not at all — and the React
|
|
1665
|
-
// preset's plugin has the same constraint, which is why the two presets
|
|
1666
|
-
// share one Vite major rather than each choosing.
|
|
1667
|
-
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
|
1668
1849
|
"@tailwindcss/vite": "^4.1.5",
|
|
1669
1850
|
"@types/node": "^20.19.43",
|
|
1670
|
-
|
|
1671
|
-
"
|
|
1851
|
+
"@types/react": "^19.1.0",
|
|
1852
|
+
"@types/react-dom": "^19.1.0",
|
|
1853
|
+
// 6.x, which peer-requires vite ^8 — the same constraint the Svelte
|
|
1854
|
+
// preset's plugin has, which is why both presets share one Vite major.
|
|
1855
|
+
// Its `@rolldown/plugin-babel` and `babel-plugin-react-compiler` peers are
|
|
1856
|
+
// OPTIONAL (they back the React Compiler path, which this scaffold does
|
|
1857
|
+
// not enable), so neither is shipped: verified by a clean install with no
|
|
1858
|
+
// unmet-peer warnings.
|
|
1859
|
+
"@vitejs/plugin-react": "^6.0.5",
|
|
1672
1860
|
tailwindcss: "^4.1.5",
|
|
1673
1861
|
"tw-animate-css": "^1.2.4",
|
|
1674
1862
|
tsx: "^4.19.2",
|
|
1675
|
-
//
|
|
1676
|
-
//
|
|
1677
|
-
//
|
|
1863
|
+
// Kept in step with the Svelte preset — the major `sv create` pins. 7 is
|
|
1864
|
+
// out, but svelte-check's peer range stops at 6 and the two presets track
|
|
1865
|
+
// one TypeScript major so the shared tsconfig means the same thing in both.
|
|
1678
1866
|
typescript: "^6.0.3",
|
|
1679
1867
|
vite: "^8.0.16"
|
|
1680
1868
|
},
|
|
1681
|
-
//
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
//
|
|
1686
|
-
//
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
// command alone.
|
|
1690
|
-
//
|
|
1691
|
-
// `svelte-kit sync` runs first, and is not optional: it generates
|
|
1692
|
-
// `.svelte-kit/tsconfig.json` (which the project's tsconfig extends) and the
|
|
1693
|
-
// route types. Without it svelte-check fails on a missing extends target
|
|
1694
|
-
// rather than on anything the user wrote.
|
|
1695
|
-
checkCmd: "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
1696
|
-
// SvelteKit supplies `$lib` on both sides — the Vite alias from its plugin
|
|
1697
|
-
// and the tsconfig path from the config it generates. Declaring either by
|
|
1698
|
-
// hand would be a second source of truth that can silently disagree.
|
|
1699
|
-
aliasName: null,
|
|
1700
|
-
aliasTarget: "frontend/src/lib",
|
|
1701
|
-
tsconfigPaths: {},
|
|
1702
|
-
tsconfigExtends: "./.svelte-kit/tsconfig.json",
|
|
1703
|
-
// What `svelte-kit sync` generates and the extended config lists — re-added
|
|
1704
|
-
// because our `include` replaces that config's rather than merging with it.
|
|
1705
|
-
// These are the declarations behind `$app/*`, `$env/*`, and `./$types`.
|
|
1706
|
-
tsconfigInclude: [
|
|
1707
|
-
".svelte-kit/ambient.d.ts",
|
|
1708
|
-
".svelte-kit/env.d.ts",
|
|
1709
|
-
".svelte-kit/non-ambient.d.ts",
|
|
1710
|
-
".svelte-kit/types/**/$types.d.ts"
|
|
1711
|
-
],
|
|
1712
|
-
// `prepare` runs on `npm install`, so a fresh clone has `.svelte-kit/`
|
|
1713
|
-
// before anything reads tsconfig.json.
|
|
1714
|
-
extraScripts: { prepare: "svelte-kit sync" },
|
|
1715
|
-
// shadcn-svelte's generated components import these from `$lib/utils.js`
|
|
1716
|
-
// alongside `cn()`. Without them the scaffold type-checks until the first
|
|
1717
|
-
// `npx shadcn-svelte@latest add <name>` — which the agent guidance tells
|
|
1718
|
-
// assistants to run — and then fails inside components the user never wrote,
|
|
1719
|
-
// with an error naming a file they have no reason to suspect.
|
|
1720
|
-
//
|
|
1721
|
-
// Verbatim from shadcn-svelte's own utils.ts, so a component the CLI writes
|
|
1722
|
-
// resolves against exactly what upstream expects.
|
|
1723
|
-
libUtilsExtra: `
|
|
1724
|
-
export type WithoutChild<T> = T extends { child?: any } ? Omit<T, "child"> : T;
|
|
1725
|
-
export type WithoutChildren<T> = T extends { children?: any } ? Omit<T, "children"> : T;
|
|
1726
|
-
export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>;
|
|
1727
|
-
export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & { ref?: U | null };
|
|
1728
|
-
`,
|
|
1729
|
-
gitignoreEntries: [".svelte-kit/"],
|
|
1730
|
-
// No `jsx`. `verbatimModuleSyntax` is what svelte-check expects so that
|
|
1731
|
-
// type-only imports in components are erased rather than emitted.
|
|
1732
|
-
tsconfigOptions: { verbatimModuleSyntax: true },
|
|
1869
|
+
// `tsc` checks .tsx natively, so React needs nothing beyond it.
|
|
1870
|
+
checkCmd: "tsc --noEmit",
|
|
1871
|
+
aliasName: "@",
|
|
1872
|
+
aliasTarget: "frontend/src",
|
|
1873
|
+
// `./`-rooted: with no `baseUrl` (deprecated in TypeScript 6), a `paths`
|
|
1874
|
+
// target resolves against tsconfig.json's own directory.
|
|
1875
|
+
tsconfigPaths: { "@/*": ["./frontend/src/*"] },
|
|
1876
|
+
tsconfigOptions: { jsx: "react-jsx" },
|
|
1733
1877
|
viteImports: [
|
|
1734
|
-
`import
|
|
1735
|
-
`import adapter from "@xano/sdk/sveltekit";`,
|
|
1878
|
+
`import react from "@vitejs/plugin-react";`,
|
|
1736
1879
|
`import tailwindcss from "@tailwindcss/vite";`
|
|
1737
1880
|
],
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
vitePlugins: ["tailwindcss()", renderSvelteKitPlugin()],
|
|
1744
|
-
// SvelteKit resolves its own paths from this file's directory and redirects
|
|
1745
|
-
// them into frontend/ via `files`; moving Vite's root under it puts the two
|
|
1746
|
-
// into disagreement. Output is the adapter's job, not `build.outDir`.
|
|
1747
|
-
viteRoot: null,
|
|
1748
|
-
viteBuild: null,
|
|
1749
|
-
// SvelteKit owns the page template — frontend/src/app.html. No `entryScript`
|
|
1750
|
-
// follows: there is no shared index.html for it to point at.
|
|
1751
|
-
ownsHtmlEntry: true,
|
|
1752
|
-
files(vars, landing, choice = defaultThemeChoice()) {
|
|
1753
|
-
const ui = "frontend/src/lib/components/ui";
|
|
1881
|
+
vitePlugins: ["react()", "tailwindcss()"],
|
|
1882
|
+
entryScript: "/src/main.tsx",
|
|
1883
|
+
libUtils: `export { cn, type ClassValue } from "cn";
|
|
1884
|
+
`,
|
|
1885
|
+
files(_vars, landing, choice = defaultThemeChoice()) {
|
|
1754
1886
|
return [
|
|
1887
|
+
// The shadcn/ui CLI's config — lets `npx shadcn@latest add <component>`
|
|
1888
|
+
// work in a fresh scaffold without a separate `shadcn init`.
|
|
1755
1889
|
{ path: "components.json", content: renderComponentsJson(choice) },
|
|
1756
|
-
{ path: "frontend/src/
|
|
1757
|
-
|
|
1890
|
+
{ path: "frontend/src/main.tsx", content: renderMainTsx() },
|
|
1891
|
+
{ path: "frontend/src/App.tsx", content: renderApp(landing, choice) },
|
|
1892
|
+
// Only for `--dark toggle`. `system` needs no component: the inline
|
|
1893
|
+
// script in index.html is the whole implementation.
|
|
1758
1894
|
...choice.dark === "toggle" ? [
|
|
1759
1895
|
{
|
|
1760
|
-
path: "frontend/src/
|
|
1761
|
-
content:
|
|
1896
|
+
path: "frontend/src/components/mode-toggle.tsx",
|
|
1897
|
+
content: renderModeToggleTsx(iconLibraryOf(choice.icons).react)
|
|
1762
1898
|
}
|
|
1763
1899
|
] : [],
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
{ path: "frontend/src/
|
|
1767
|
-
{ path: "frontend/src/
|
|
1768
|
-
{ path: "frontend/src/routes/+error.svelte", content: renderErrorSvelte() },
|
|
1769
|
-
{ path: "frontend/src/routes/404/+page.svelte", content: renderNotFoundRoute() },
|
|
1770
|
-
{ path: "frontend/src/lib/components/error-state.svelte", content: renderErrorState() },
|
|
1771
|
-
// `kit.files.assets` points here: static files copied to the site root
|
|
1772
|
-
// verbatim. A README rather than a .gitkeep because this one gets
|
|
1773
|
-
// PUBLISHED — everything here lands in frontend/dist, so the placeholder
|
|
1774
|
-
// that keeps the directory in git is a file real users will see deployed.
|
|
1775
|
-
{
|
|
1776
|
-
path: "frontend/static/README.md",
|
|
1777
|
-
content: `Files here are copied to the site root as-is \u2014 \`favicon.png\` is served
|
|
1778
|
-
at \`/favicon.png\`. Reference them from a component with a root-relative
|
|
1779
|
-
path, or from \`app.html\` via \`%sveltekit.assets%\`.
|
|
1780
|
-
|
|
1781
|
-
Delete this file once you add something real.
|
|
1782
|
-
`
|
|
1783
|
-
},
|
|
1784
|
-
{ path: `${ui}/button/button.svelte`, content: renderButtonSvelte() },
|
|
1785
|
-
{ path: `${ui}/button/index.ts`, content: renderButtonIndex() },
|
|
1786
|
-
{
|
|
1787
|
-
path: `${ui}/card/card.svelte`,
|
|
1788
|
-
content: cardPart(
|
|
1789
|
-
"card",
|
|
1790
|
-
"div",
|
|
1791
|
-
"flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm"
|
|
1792
|
-
)
|
|
1793
|
-
},
|
|
1794
|
-
{
|
|
1795
|
-
path: `${ui}/card/card-header.svelte`,
|
|
1796
|
-
content: cardPart(
|
|
1797
|
-
"card-header",
|
|
1798
|
-
"div",
|
|
1799
|
-
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6"
|
|
1800
|
-
)
|
|
1801
|
-
},
|
|
1802
|
-
{
|
|
1803
|
-
path: `${ui}/card/card-title.svelte`,
|
|
1804
|
-
content: cardPart("card-title", "div", "leading-none font-semibold")
|
|
1805
|
-
},
|
|
1806
|
-
{
|
|
1807
|
-
path: `${ui}/card/card-description.svelte`,
|
|
1808
|
-
content: cardPart("card-description", "div", "text-sm text-muted-foreground")
|
|
1809
|
-
},
|
|
1810
|
-
{
|
|
1811
|
-
path: `${ui}/card/card-content.svelte`,
|
|
1812
|
-
content: cardPart("card-content", "div", "px-6")
|
|
1813
|
-
},
|
|
1814
|
-
{
|
|
1815
|
-
path: `${ui}/card/card-footer.svelte`,
|
|
1816
|
-
content: cardPart("card-footer", "div", "flex items-center px-6 [.border-t]:pt-6")
|
|
1817
|
-
},
|
|
1818
|
-
{ path: `${ui}/card/index.ts`, content: renderCardIndex() }
|
|
1900
|
+
// The two components the landing page uses. Every other shadcn component
|
|
1901
|
+
// is one `npx shadcn@latest add` away and lands beside these.
|
|
1902
|
+
{ path: "frontend/src/components/ui/button.tsx", content: renderButtonTsx() },
|
|
1903
|
+
{ path: "frontend/src/components/ui/card.tsx", content: renderCardTsx() }
|
|
1819
1904
|
];
|
|
1820
1905
|
},
|
|
1821
1906
|
iconBinding(id) {
|
|
1822
|
-
return iconLibraryOf(id).
|
|
1907
|
+
return iconLibraryOf(id).react;
|
|
1823
1908
|
},
|
|
1824
1909
|
readmeFrontendSection(icons) {
|
|
1825
|
-
return `[
|
|
1826
|
-
[
|
|
1827
|
-
[
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
is a page, a subdirectory is a nested route. Routes are **prerendered**: each
|
|
1831
|
-
becomes its own HTML document at build time, so a page loads as itself rather
|
|
1832
|
-
than as a shell that fills in afterwards, and an unmatched path returns a real
|
|
1833
|
-
404 from [\`+error.svelte\`](frontend/src/routes/+error.svelte).
|
|
1834
|
-
|
|
1835
|
-
> **There is no server.** \`npm run xano:deploy:ephemeral\` ships this app to a static host
|
|
1836
|
-
> with no runtime of its own; Xano is the backend, reached through
|
|
1837
|
-
> [\`frontend/src/lib/api.ts\`](frontend/src/lib/api.ts). So \`+page.server.ts\`,
|
|
1838
|
-
> form actions, and server \`load\` have nothing to run on \u2014 and **nothing warns
|
|
1839
|
-
> you**: a project using them builds and deploys green, then fails in the
|
|
1840
|
-
> browser. Treat them as unavailable. That is the trade \u2014 the backend is
|
|
1841
|
-
> authored in [\`xano/\`](xano/) as TypeScript and deployed alongside the
|
|
1842
|
-
> frontend, so it is a real backend, just not this one.
|
|
1843
|
-
>
|
|
1844
|
-
> \`frontend/src/routes/+layout.ts\` sets \`prerender = true\` for the whole app.
|
|
1845
|
-
> Because prerendering renders at build time, module-scope \`window\`/\`document\`
|
|
1846
|
-
> access now fails the **build** rather than the browser \u2014 use \`onMount\`, or
|
|
1847
|
-
> guard with \`import { browser } from "$app/environment"\`.
|
|
1848
|
-
|
|
1849
|
-
**A dynamic route fails the build until it says how it renders.** A route like
|
|
1850
|
-
\`/posts/[id]\` cannot be prerendered unless the build knows which ids exist, so
|
|
1851
|
-
the build stops and names it. For rows created at runtime (the usual detail
|
|
1852
|
-
page), render it in the browser:
|
|
1853
|
-
|
|
1854
|
-
\`\`\`ts
|
|
1855
|
-
// frontend/src/routes/posts/[id]/+page.ts
|
|
1856
|
-
export const prerender = false;
|
|
1857
|
-
\`\`\`
|
|
1858
|
-
|
|
1859
|
-
The host has no file for \`/posts/42\`, so it answers with \`404.html\`, which the
|
|
1860
|
-
SDK's adapter (\`@xano/sdk/sveltekit\`, in \`vite.config.ts\`) writes as the app
|
|
1861
|
-
shell: it boots the app and renders the route, so a reload or a deep link works
|
|
1862
|
-
(the response status is still 404). Only for ids known at build time, list them
|
|
1863
|
-
instead \u2014 each gets its own page, and no other id has one:
|
|
1864
|
-
\`export const entries = () => [{ id: "1" }, { id: "2" }];\`
|
|
1865
|
-
|
|
1866
|
-
[\`frontend/src/routes/404/+page.svelte\`](frontend/src/routes/404/+page.svelte)
|
|
1867
|
-
prerenders to \`404.html\` while every route is prerendered, which is what makes
|
|
1868
|
-
an unmatched path return a real 404. It has to be a route: SvelteKit never
|
|
1869
|
-
prerenders \`+error.svelte\` to a file. Once a route sets \`prerender = false\`,
|
|
1870
|
-
the adapter writes the app shell there instead, and an unmatched path renders
|
|
1871
|
-
\`+error.svelte\` in the browser, still with a 404.
|
|
1872
|
-
|
|
1873
|
-
shadcn-svelte is not a dependency \u2014
|
|
1874
|
-
its components are copied into
|
|
1875
|
-
[\`frontend/src/lib/components/ui/\`](frontend/src/lib/components/ui/) and owned by
|
|
1876
|
-
this project, so edit them freely. \`Button\` and \`Card\` are already there; add
|
|
1877
|
-
more with:
|
|
1910
|
+
return `React + Vite, styled with [Tailwind CSS](https://tailwindcss.com) v4 and
|
|
1911
|
+
[shadcn/ui](https://ui.shadcn.com). shadcn is not a dependency \u2014 its components
|
|
1912
|
+
are copied into [\`frontend/src/components/ui/\`](frontend/src/components/ui/) and
|
|
1913
|
+
owned by this project, so edit them freely. \`Button\` and \`Card\` are already
|
|
1914
|
+
there; add more with:
|
|
1878
1915
|
|
|
1879
1916
|
\`\`\`bash
|
|
1880
|
-
npx shadcn
|
|
1917
|
+
npx shadcn@latest add dialog input form
|
|
1881
1918
|
\`\`\`
|
|
1882
1919
|
|
|
1883
1920
|
[\`components.json\`](components.json) is pre-configured, so that works with no
|
|
1884
|
-
\`shadcn
|
|
1921
|
+
\`shadcn init\` step. ${iconLibraryOf(icons).guidance.react}
|
|
1922
|
+
[\`frontend/src/App.tsx\`](frontend/src/App.tsx) already uses one.
|
|
1885
1923
|
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
[\`frontend/src/routes/+page.svelte\`](frontend/src/routes/+page.svelte) already
|
|
1891
|
-
does, and that is the form \`npx shadcn-svelte@latest add\` writes too.
|
|
1892
|
-
|
|
1893
|
-
Components import through the \`$lib\` alias
|
|
1894
|
-
(\`$lib/components/ui/button\`, \`$lib/utils\`), which SvelteKit points at
|
|
1895
|
-
\`frontend/src/lib/\` via \`kit.files\` in
|
|
1896
|
-
[\`vite.config.ts\`](vite.config.ts) \u2014 there is no \`paths\` entry to keep in
|
|
1897
|
-
sync, and adding one would just be a second answer that can disagree.
|
|
1924
|
+
Components import through the \`@/\` alias (\`@/components/ui/button\`), which
|
|
1925
|
+
maps to \`frontend/src/\` in both \`tsconfig.json\` and \`vite.config.ts\` \u2014 change
|
|
1926
|
+
one and change the other. \`cn()\` comes from shadcn's \`cn\` package, which is
|
|
1927
|
+
what the CLI writes into every component it adds.
|
|
1898
1928
|
|
|
1899
1929
|
Colors come from the theme tokens in
|
|
1900
|
-
[\`frontend/src/index.css\`](frontend/src/index.css) \u2014 see **Theming** below
|
|
1901
|
-
|
|
1902
|
-
> \`npm run typecheck\` runs \`svelte-kit sync && svelte-check\`, not \`tsc\`. It
|
|
1903
|
-
> checks both halves \u2014 the backend's TypeScript and the components \u2014 where
|
|
1904
|
-
> \`tsc\` cannot read \`.svelte\` files at all. Keep it in the script: it is what
|
|
1905
|
-
> makes the frontend unable to drift from the backend defs. \`sync\` regenerates
|
|
1906
|
-
> \`.svelte-kit/\`, which \`tsconfig.json\` extends; \`npm install\` runs it too.`;
|
|
1930
|
+
[\`frontend/src/index.css\`](frontend/src/index.css) \u2014 see **Theming** below.`;
|
|
1907
1931
|
},
|
|
1908
1932
|
agentGuidanceSection(icons) {
|
|
1909
|
-
return `- \`frontend/src/\` \u2014 the
|
|
1910
|
-
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
\`
|
|
1922
|
-
|
|
1923
|
-
-
|
|
1924
|
-
\`
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
access now breaks the build, not the browser. Use \`onMount\`, or guard with
|
|
1929
|
-
\`import { browser } from "$app/environment"\`. This is a page option, not a
|
|
1930
|
-
server \u2014 do not "fix" it by reaching for \`ssr\` or a server file.
|
|
1931
|
-
- **A dynamic route fails the build until it says how it renders.**
|
|
1932
|
-
\`/posts/[id]\` cannot be prerendered unless the build knows which ids exist.
|
|
1933
|
-
For rows created at runtime (a detail page), export
|
|
1934
|
-
\`const prerender = false\` from that route's \`+page.ts\`: the host answers
|
|
1935
|
-
it with the \`404.html\` app shell the SDK's adapter writes, which renders it
|
|
1936
|
-
in the browser, so reloads and deep links work. \`entries\` only fits ids
|
|
1937
|
-
known at build time \u2014 any other id has no page.
|
|
1938
|
-
- \`frontend/src/routes/404/+page.svelte\` prerenders to \`404.html\`, which is
|
|
1939
|
-
what makes an unmatched path return a real 404 instead of silently serving a
|
|
1940
|
-
page with a 200. Keep it: without it a missing asset comes back as HTML and
|
|
1941
|
-
the browser reports \`Unexpected token '<'\`. It has to be a route \u2014 SvelteKit
|
|
1942
|
-
never prerenders \`+error.svelte\` to a file, so that alone would ship no
|
|
1943
|
-
\`404.html\`.
|
|
1944
|
-
- Keep the adapter in \`vite.config.ts\` (\`@xano/sdk/sveltekit\`). Do NOT
|
|
1945
|
-
swap in \`@sveltejs/adapter-static\` with a \`fallback\`: it writes an empty
|
|
1946
|
-
shell over the prerendered home page.
|
|
1947
|
-
- Data loading: call \`api.ts\` from the component, or from a universal
|
|
1948
|
-
\`+page.ts\` \`load\` if you need it before render. Both run in the browser.
|
|
1949
|
-
- \`frontend/src/lib/components/ui/\` \u2014 shadcn-svelte components, **copied in and
|
|
1950
|
-
owned by this project**. Edit them directly; there is no library to configure
|
|
1951
|
-
around.
|
|
1952
|
-
- Need one that isn't there? \`npx shadcn-svelte@latest add <name>\` \u2014 do not
|
|
1953
|
-
hand-roll it, and do not add a different component library. Note the
|
|
1954
|
-
\`-svelte\` suffix: the plain \`shadcn\` CLI writes React and will not work here.
|
|
1955
|
-
- ${iconLibraryOf(icons).guidance.svelte.replace(/\n/g, "\n ")}
|
|
1956
|
-
- That import form is what \`frontend/src/routes/+page.svelte\` already uses and
|
|
1957
|
-
what \`npx shadcn-svelte@latest add\` writes, so matching it keeps what you
|
|
1958
|
-
write consistent with what the CLI generates beside it. Do not add another
|
|
1959
|
-
icon library and do not paste raw inline \`<svg>\` markup.
|
|
1960
|
-
- Import via the \`$lib\` alias (\`$lib/components/ui/button\`, \`$lib/utils\`),
|
|
1961
|
-
which SvelteKit generates for both TypeScript and the bundler. Do not add a
|
|
1962
|
-
\`paths\` entry for it \u2014 a hand-written one is a second source of truth that
|
|
1963
|
-
can disagree with the generated one.
|
|
1964
|
-
- Components are Svelte 5 **runes** (\`$props()\`, \`$bindable()\`,
|
|
1965
|
-
\`{@render children?.()}\`) \u2014 not the Svelte 4 \`export let\` / slot API. Runes
|
|
1966
|
-
mode is FORCED in \`vite.config.ts\`, so \`export let\` is a compile error
|
|
1967
|
-
naming the file rather than a component that quietly builds in legacy mode.
|
|
1968
|
-
Do not "fix" that error by relaxing \`compilerOptions.runes\` \u2014 write the
|
|
1969
|
-
rune.
|
|
1970
|
-
- \`npm run typecheck\` runs \`svelte-kit sync && svelte-check\`, which checks both
|
|
1971
|
-
\`xano/\` and the components. Do not swap it for \`tsc --noEmit\`: \`tsc\` cannot
|
|
1972
|
-
read \`.svelte\` files and fails on the UI kit's barrels. \`sync\` regenerates
|
|
1973
|
-
\`.svelte-kit/\`, which \`tsconfig.json\` extends \u2014 run it after adding a route.`;
|
|
1933
|
+
return `- \`frontend/src/\` \u2014 the React app. Tailwind v4 + shadcn/ui.
|
|
1934
|
+
- \`frontend/src/components/ui/\` \u2014 shadcn components, **copied in and owned by
|
|
1935
|
+
this project**. Edit them directly; there is no library to configure around.
|
|
1936
|
+
- Need one that isn't there? \`npx shadcn@latest add <name>\` \u2014 do not hand-roll
|
|
1937
|
+
it, and do not add a different component library.
|
|
1938
|
+
- ${iconLibraryOf(icons).guidance.react.replace(/\n/g, "\n ")}
|
|
1939
|
+
\`frontend/src/App.tsx\` already uses one. Do not add another icon library and
|
|
1940
|
+
do not paste raw inline \`<svg>\` markup \u2014 search the set before concluding an
|
|
1941
|
+
icon is missing.
|
|
1942
|
+
- Import via the \`@/\` alias (\`@/components/ui/button\`), declared in both
|
|
1943
|
+
\`tsconfig.json\` and \`vite.config.ts\`. \`cn()\` is \`import { cn } from "cn"\`
|
|
1944
|
+
(shadcn's own package, which the CLI writes into every component it adds);
|
|
1945
|
+
\`@/lib/utils\` re-exports it. Leave a generated component's imports as the
|
|
1946
|
+
CLI wrote them.
|
|
1947
|
+
- \`npx shadcn@latest add sonner\` writes a \`Toaster\` that reads the mode from
|
|
1948
|
+
\`next-themes\`, which this project does not use. Drop that import and
|
|
1949
|
+
uninstall \`next-themes\`, then pass \`theme\` from the project's own mode:
|
|
1950
|
+
\`getMode()\` + \`watchMode()\` from \`@/lib/theme\` when that module exists,
|
|
1951
|
+
otherwise \`"system"\` (or \`"light"\` if nothing applies the \`dark\` class).`;
|
|
1974
1952
|
}
|
|
1975
1953
|
};
|
|
1976
1954
|
|
|
1977
|
-
// src/emit/frontend-presets.ts
|
|
1978
|
-
var FRAMEWORKS = ["react", "svelte"];
|
|
1979
|
-
var DEFAULT_FRAMEWORK = "react";
|
|
1980
|
-
var PRESETS = {
|
|
1981
|
-
react: reactPreset,
|
|
1982
|
-
svelte: sveltePreset
|
|
1983
|
-
};
|
|
1984
|
-
function findFrontendPreset(id) {
|
|
1985
|
-
const normalized = id.trim().toLowerCase();
|
|
1986
|
-
return FRAMEWORKS.includes(normalized) ? PRESETS[normalized] : void 0;
|
|
1987
|
-
}
|
|
1988
|
-
function allFrontendPresets() {
|
|
1989
|
-
return FRAMEWORKS.map((id) => PRESETS[id]);
|
|
1990
|
-
}
|
|
1991
|
-
function resolveFrameworkFlag(raw) {
|
|
1992
|
-
const preset = findFrontendPreset(raw);
|
|
1993
|
-
if (preset === void 0) {
|
|
1994
|
-
throw new Error(
|
|
1995
|
-
`Unknown --framework "${raw}". Valid frameworks: ${FRAMEWORKS.join(", ")}.`
|
|
1996
|
-
);
|
|
1997
|
-
}
|
|
1998
|
-
return preset;
|
|
1999
|
-
}
|
|
2000
|
-
|
|
2001
1955
|
export {
|
|
1956
|
+
ICON_LIBRARIES,
|
|
1957
|
+
DEFAULT_ICON_LIBRARY,
|
|
1958
|
+
findIconLibrary,
|
|
1959
|
+
allIconLibraries,
|
|
1960
|
+
defaultIconLibrary,
|
|
1961
|
+
resolveIconsFlag,
|
|
1962
|
+
iconLibraryOf,
|
|
1963
|
+
reactPreset,
|
|
1964
|
+
decodeDigest,
|
|
1965
|
+
decodeMarkerWith,
|
|
1966
|
+
decodeMarkerHead,
|
|
2002
1967
|
sdkDep,
|
|
1968
|
+
sortedKeys,
|
|
1969
|
+
isSorted,
|
|
1970
|
+
renderBackendPackageJson,
|
|
2003
1971
|
renderPackageJson,
|
|
2004
1972
|
renderTsconfig,
|
|
1973
|
+
BACKEND_TSCONFIG_PATH,
|
|
1974
|
+
renderBackendTsconfig,
|
|
2005
1975
|
renderLambdaTsconfig,
|
|
2006
1976
|
renderViteConfig,
|
|
2007
1977
|
renderIndexHtml,
|
|
2008
1978
|
renderGitignore,
|
|
1979
|
+
mergeGitignore,
|
|
2009
1980
|
NODE_MIN,
|
|
2010
1981
|
renderGitattributes,
|
|
2011
1982
|
checkJobDir,
|
|
@@ -2013,7 +1984,11 @@ export {
|
|
|
2013
1984
|
checkWorkflowPath,
|
|
2014
1985
|
renderCheckWorkflow,
|
|
2015
1986
|
renderEnvExample,
|
|
1987
|
+
README_PATH,
|
|
1988
|
+
readmeBuiltWithVersion,
|
|
1989
|
+
refreshReadmeBuiltWith,
|
|
2016
1990
|
renderReadme,
|
|
1991
|
+
renderBackendReadme,
|
|
2017
1992
|
renderXanoIndex,
|
|
2018
1993
|
renderXanoExampleMd,
|
|
2019
1994
|
describeOrigin,
|
|
@@ -2022,13 +1997,9 @@ export {
|
|
|
2022
1997
|
initLanding,
|
|
2023
1998
|
codegenLanding,
|
|
2024
1999
|
renderIndexCss,
|
|
2000
|
+
renderThemeScript,
|
|
2025
2001
|
renderThemeModule,
|
|
2026
2002
|
renderCnUtil,
|
|
2027
|
-
renderApiTs
|
|
2028
|
-
FRAMEWORKS,
|
|
2029
|
-
DEFAULT_FRAMEWORK,
|
|
2030
|
-
findFrontendPreset,
|
|
2031
|
-
allFrontendPresets,
|
|
2032
|
-
resolveFrameworkFlag
|
|
2003
|
+
renderApiTs
|
|
2033
2004
|
};
|
|
2034
|
-
//# sourceMappingURL=chunk-
|
|
2005
|
+
//# sourceMappingURL=chunk-5GIOCDUM.js.map
|