sv 0.11.3 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,13 @@
1
- import { i as __toESM, r as __require, t as __commonJSMin } from "./chunk-BjMGrMj9.mjs";
2
- import { _ as dist, a as installOption, c as up$1, d as Option, g as detect, h as sanitizeName, i as installDependencies, m as templates, n as addPnpmBuildDependencies, o as packageManagerPrompt, p as create$1, r as getUserAgent, s as any, t as AGENT_NAMES, u as Command, v as getSharedFiles } from "./package-manager-DkCPtZM1.mjs";
3
- import { $ as getNodeTypesVersion, A as createCall, B as hasTypeProperty, C as declaration, Ct as ke, D as addNamespace, E as addNamed, Et as require_picocolors, F as create$3, G as ensureScript, H as parseStatement, I as addJsDocTypeComment, J as addAtRule, L as appendStatement, M as overrideProperties, N as property, O as find, P as append, Q as addToDemoPage, R as createLiteral, S as createIdentifier, St as et, T as addEmpty, Tt as Vu, U as addFragment, V as parseExpression, W as addSlot, X as createPrinter, Z as addEslintConfigPrettier, _ as getConfig, _t as Ke, a as getPackageJson, at as walk, b as createDefault, bt as We, c as writeFile, et as isVersionUnsupportedBelow, f as packageScriptsUpsert, ft as resolveCommand, g as addPlugin, gt as J, ht as Ge, i as formatFiles, it as getErrorHint, j as create$2, k as remove, l as parse$1, lt as q, mt as Fe, n as commonFilePaths, nt as defineAddon, o as installPackages, p as addAttribute, pt as De, q as MagicString, r as fileExists, rt as defineAddonOptions, s as readFile, st as dedent_default, t as color, tt as splitVersion, u as arrayUpsert, v as addGlobalAppInterface, vt as T, w as addDefault, wt as ze, x as createNamed, y as addHooksHandle, yt as Ue, z as createSpread } from "./utils-DjBRIDJG.mjs";
1
+ import { r as __require, t as __commonJSMin } from "./chunk-BcJDCUAU.mjs";
2
+ import { _ as getSharedFiles, a as installOption, c as up$1, d as Option, g as dist, h as sanitizeName, i as installDependencies, m as templates, n as addPnpmBuildDependencies, o as packageManagerPrompt, p as create$1, r as detectPackageManager, s as any, t as AGENT_NAMES, u as Command } from "./package-manager-DYfxv5nk.mjs";
3
+ import { $ as createPrinter, A as addNamespace$1, B as appendFromString, C as createDefault, Ct as Rt, D as addDefault, E as declaration, Et as qt, F as overrideProperties, G as parseExpression, H as createLiteral, I as property, J as addSlot, K as parseStatement, L as append, M as remove, N as createCall, O as addEmpty, Ot as Ct, P as create$2, R as create$3, S as addNamespace, St as R, T as createIdentifier, Tt as kt, U as createSpread, V as appendStatement, W as hasTypeProperty, Y as ensureScript, Z as addAtRule, _t as Lt, a as getPackageJson, at as defineAddon, b as addGlobalAppInterface, bt as Pt, c as writeFile, ct as walk, et as addEslintConfigPrettier, f as packageScriptsUpsert, ft as q, gt as Ie, h as addAttribute, ht as resolveCommand, i as formatFiles, it as splitVersion, j as find, k as addNamed, l as parse$1, m as upsert, n as commonFilePaths, nt as getNodeTypesVersion, o as installPackages, ot as defineAddonOptions, q as addFragment, r as fileExists, rt as isVersionUnsupportedBelow, s as readFile, st as getErrorHint, t as color, tt as addToDemoPage, u as arrayUpsert, ut as dedent_default, v as addPlugin, vt as Mt, w as createNamed, wt as Wt, x as addHooksHandle, xt as Qt, y as getConfig, yt as Nt, z as addJsDocTypeComment } from "./utils-CnfD6Z1s.mjs";
4
4
  import fs, { existsSync, readFileSync } from "node:fs";
5
5
  import path, { dirname, join } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import process$1 from "node:process";
8
8
  import { promisify } from "node:util";
9
9
  import { exec } from "node:child_process";
10
+ import crypto from "node:crypto";
10
11
  import { platform } from "node:os";
11
12
  import { pipeline } from "node:stream/promises";
12
13
  import { createGunzip } from "node:zlib";
@@ -610,7 +611,431 @@ function pipe(...pipe$1) {
610
611
  }
611
612
 
612
613
  //#endregion
613
- //#region lib/addons/devtools-json/index.ts
614
+ //#region lib/addons/better-auth.ts
615
+ const options$7 = defineAddonOptions().add("demo", {
616
+ question: "Which demo would you like to include?",
617
+ type: "multiselect",
618
+ default: ["password"],
619
+ options: [{
620
+ value: "password",
621
+ label: "Email & Password"
622
+ }, {
623
+ value: "github",
624
+ label: "GitHub OAuth"
625
+ }],
626
+ required: false
627
+ }).build();
628
+ var better_auth_default = defineAddon({
629
+ id: "better-auth",
630
+ shortDescription: "auth library",
631
+ homepage: "https://www.better-auth.com",
632
+ options: options$7,
633
+ setup: ({ kit, dependencyVersion, unsupported, dependsOn, runsAfter }) => {
634
+ if (!kit) unsupported("Requires SvelteKit");
635
+ if (!dependencyVersion("drizzle-orm")) dependsOn("drizzle");
636
+ runsAfter("tailwindcss");
637
+ },
638
+ run: ({ sv, language, options: options$8, kit, dependencyVersion, files }) => {
639
+ if (!kit) throw new Error("SvelteKit is required");
640
+ const demoPassword = options$8.demo.includes("password");
641
+ const demoGithub = options$8.demo.includes("github");
642
+ const hasDemo = demoPassword || demoGithub;
643
+ let drizzleDialect;
644
+ sv.devDependency("better-auth", "^1.4.18");
645
+ sv.file(`drizzle.config.${language}`, (content) => {
646
+ const { ast, generateCode } = parse$1.script(content);
647
+ const isProp = (name$1, node) => node.key.type === "Identifier" && node.key.name === name$1;
648
+ walk(ast, null, { Property(node) {
649
+ if (isProp("dialect", node) && node.value.type === "Literal" && typeof node.value.value === "string") drizzleDialect = node.value.value;
650
+ } });
651
+ if (!drizzleDialect) throw new Error("Failed to detect DB dialect in your `drizzle.config.[js|ts]` file");
652
+ return generateCode();
653
+ });
654
+ sv.file(".env", (content) => generateEnvFileContent$1(content, demoGithub, false));
655
+ sv.file(".env.example", (content) => generateEnvFileContent$1(content, demoGithub, true));
656
+ sv.file(`${kit?.libDirectory}/server/auth.${language}`, (content) => {
657
+ const { ast, generateCode, comments } = parse$1.script(content);
658
+ addNamed(ast, {
659
+ from: "$lib/server/db",
660
+ imports: ["db"]
661
+ });
662
+ addNamed(ast, {
663
+ from: "$app/server",
664
+ imports: ["getRequestEvent"]
665
+ });
666
+ addNamed(ast, {
667
+ from: "$env/dynamic/private",
668
+ imports: ["env"]
669
+ });
670
+ addNamed(ast, {
671
+ from: "better-auth/svelte-kit",
672
+ imports: ["sveltekitCookies"]
673
+ });
674
+ addNamed(ast, {
675
+ from: "better-auth/adapters/drizzle",
676
+ imports: ["drizzleAdapter"]
677
+ });
678
+ addNamed(ast, {
679
+ from: "better-auth",
680
+ imports: ["betterAuth"]
681
+ });
682
+ const authConfig = dedent_default`
683
+ export const auth = betterAuth({
684
+ baseURL: env.ORIGIN,
685
+ secret: env.BETTER_AUTH_SECRET,
686
+ database: drizzleAdapter(db, {
687
+ provider: '${{
688
+ mysql: "mysql",
689
+ postgresql: "pg",
690
+ sqlite: "sqlite",
691
+ turso: "sqlite"
692
+ }[drizzleDialect]}'
693
+ }),
694
+ emailAndPassword: {
695
+ enabled: true
696
+ },${demoGithub ? `
697
+ socialProviders: {
698
+ github: {
699
+ clientId: env.GITHUB_CLIENT_ID,
700
+ clientSecret: env.GITHUB_CLIENT_SECRET,
701
+ },
702
+ },` : ""}
703
+ plugins: [sveltekitCookies(getRequestEvent)], // make sure this is the last plugin in the array
704
+ });`;
705
+ appendFromString(ast, {
706
+ code: authConfig,
707
+ comments
708
+ });
709
+ return generateCode();
710
+ });
711
+ const authConfigPath = `${kit?.libDirectory}/server/auth.${language}`;
712
+ const authSchemaPath = `${kit?.libDirectory}/server/db/auth.schema.${language}`;
713
+ sv.file(files.package, (content) => {
714
+ const { data, generateCode } = parse$1.json(content);
715
+ packageScriptsUpsert(data, "auth:schema", `npx @better-auth/cli generate --config ${authConfigPath} --output ${authSchemaPath} --yes`);
716
+ return generateCode();
717
+ });
718
+ sv.file(`${kit?.libDirectory}/server/db/auth.schema.${language}`, (content) => {
719
+ if (content) return content;
720
+ return dedent_default`
721
+ // If you see this file, you have not run the auth:schema script yet, but you should!
722
+ `;
723
+ });
724
+ sv.file(`${kit?.libDirectory}/server/db/schema.${language}`, (content) => {
725
+ const { ast, generateCode } = parse$1.script(content);
726
+ addNamespace(ast, { from: "./auth.schema" });
727
+ return generateCode();
728
+ });
729
+ sv.file("src/app.d.ts", (content) => {
730
+ const { ast, generateCode } = parse$1.script(content);
731
+ addNamed(ast, {
732
+ imports: ["User", "Session"],
733
+ from: "better-auth",
734
+ isType: true
735
+ });
736
+ const locals = addGlobalAppInterface(ast, { name: "Locals" });
737
+ if (!locals) throw new Error("Failed detecting `locals` interface in `src/app.d.ts`");
738
+ const user = locals.body.body.find((prop) => hasTypeProperty(prop, { name: "user" }));
739
+ const session = locals.body.body.find((prop) => hasTypeProperty(prop, { name: "session" }));
740
+ function addProps(name$1, value, optional$1 = false) {
741
+ return {
742
+ type: "TSPropertySignature",
743
+ key: {
744
+ type: "Identifier",
745
+ name: name$1
746
+ },
747
+ computed: false,
748
+ optional: optional$1,
749
+ typeAnnotation: {
750
+ type: "TSTypeAnnotation",
751
+ typeAnnotation: {
752
+ type: "TSTypeReference",
753
+ typeName: {
754
+ type: "Identifier",
755
+ name: value
756
+ }
757
+ }
758
+ }
759
+ };
760
+ }
761
+ if (!user) locals.body.body.push(addProps("user", "User", true));
762
+ if (!session) locals.body.body.push(addProps("session", "Session", true));
763
+ return generateCode();
764
+ });
765
+ sv.file(`src/hooks.server.${language}`, (content) => {
766
+ const { ast, generateCode, comments } = parse$1.script(content);
767
+ addNamed(ast, {
768
+ imports: ["svelteKitHandler"],
769
+ from: "better-auth/svelte-kit"
770
+ });
771
+ addNamed(ast, {
772
+ imports: ["auth"],
773
+ from: "$lib/server/auth"
774
+ });
775
+ addNamed(ast, {
776
+ imports: ["building"],
777
+ from: "$app/environment"
778
+ });
779
+ const handleContent = dedent_default`
780
+ async ({ event, resolve }) => {
781
+ // Fetch current session from Better Auth
782
+ const session = await auth.api.getSession({
783
+ headers: event.request.headers
784
+ });
785
+ // Make session and user available on server
786
+ if (session) {
787
+ event.locals.session = session.session;
788
+ event.locals.user = session.user;
789
+ }
790
+ return svelteKitHandler({ event, resolve, auth, building });
791
+ };
792
+
793
+ export const handle = sequence(handleBetterAuth, handleSession);
794
+ `;
795
+ addHooksHandle(ast, {
796
+ language,
797
+ newHandleName: "handleBetterAuth",
798
+ handleContent,
799
+ comments
800
+ });
801
+ return generateCode();
802
+ });
803
+ if (hasDemo) {
804
+ sv.file(`${kit?.routesDirectory}/demo/+page.svelte`, (content) => {
805
+ return addToDemoPage(content, "better-auth", language);
806
+ });
807
+ sv.file(`${kit.routesDirectory}/demo/better-auth/login/+page.server.${language}`, (content) => {
808
+ if (content) {
809
+ const filePath = `${kit.routesDirectory}/demo/better-auth/login/+page.server.${language}`;
810
+ R.warn(`Existing ${color.warning(filePath)} file. Could not update.`);
811
+ return content;
812
+ }
813
+ const [ts] = createPrinter(language === "ts");
814
+ const signInEmailAction = demoPassword ? `
815
+ signInEmail: async (event) => {
816
+ const formData = await event.request.formData();
817
+ const email = formData.get('email')?.toString() ?? '';
818
+ const password = formData.get('password')?.toString() ?? '';
819
+
820
+ try {
821
+ await auth.api.signInEmail({
822
+ body: {
823
+ email,
824
+ password,
825
+ callbackURL: '/auth/verification-success'
826
+ }
827
+ });
828
+ } catch (error) {
829
+ if (error instanceof APIError) {
830
+ return fail(400, { message: error.message || 'Signin failed' });
831
+ }
832
+ return fail(500, { message: 'Unexpected error' });
833
+ }
834
+
835
+ return redirect(302, '/demo/better-auth');
836
+ },
837
+ signUpEmail: async (event) => {
838
+ const formData = await event.request.formData();
839
+ const email = formData.get('email')?.toString() ?? '';
840
+ const password = formData.get('password')?.toString() ?? '';
841
+ const name = formData.get('name')?.toString() ?? '';
842
+
843
+ try {
844
+ await auth.api.signUpEmail({
845
+ body: {
846
+ email,
847
+ password,
848
+ name,
849
+ callbackURL: '/auth/verification-success'
850
+ }
851
+ });
852
+ } catch (error) {
853
+ if (error instanceof APIError) {
854
+ return fail(400, { message: error.message || 'Registration failed' });
855
+ }
856
+ return fail(500, { message: 'Unexpected error' });
857
+ }
858
+
859
+ return redirect(302, '/demo/better-auth');
860
+ },` : "";
861
+ const signInSocialAction = demoGithub ? `
862
+ signInSocial: async (event) => {
863
+ const formData = await event.request.formData();
864
+ const provider = formData.get('provider')?.toString() ?? 'github';
865
+ const callbackURL = formData.get('callbackURL')?.toString() ?? '/demo/better-auth';
866
+
867
+ const result = await auth.api.signInSocial({
868
+ body: {
869
+ provider: provider${ts(" as \"github\"")},
870
+ callbackURL
871
+ }
872
+ });
873
+
874
+ if (result.url) {
875
+ return redirect(302, result.url);
876
+ }
877
+ return fail(400, { message: 'Social sign-in failed' });
878
+ },` : "";
879
+ const needsAPIError = demoPassword;
880
+ return dedent_default`
881
+ import { fail, redirect } from '@sveltejs/kit';
882
+ ${ts("import type { Actions } from './$types';")}
883
+ ${ts("import type { PageServerLoad } from './$types';")}
884
+ import { auth } from '$lib/server/auth';
885
+ ${needsAPIError ? "import { APIError } from 'better-auth';" : ""}
886
+
887
+ export const load${ts(": PageServerLoad")} = async (event) => {
888
+ if (event.locals.user) {
889
+ return redirect(302, '/demo/better-auth');
890
+ }
891
+ return {};
892
+ };
893
+
894
+ export const actions${ts(": Actions")} = {${signInEmailAction}${signInSocialAction}
895
+ };
896
+ `;
897
+ });
898
+ sv.file(`${kit.routesDirectory}/demo/better-auth/login/+page.svelte`, (content) => {
899
+ if (content) {
900
+ const filePath = `${kit.routesDirectory}/demo/better-auth/login/+page.svelte`;
901
+ R.warn(`Existing ${color.warning(filePath)} file. Could not update.`);
902
+ return content;
903
+ }
904
+ const tailwind = dependencyVersion("@tailwindcss/vite") !== void 0;
905
+ const input = tailwind ? " class=\"mt-1 px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"" : "";
906
+ const btn = tailwind ? " class=\"bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition\"" : "";
907
+ const svelte5 = !!dependencyVersion("svelte")?.startsWith("5");
908
+ const [ts, s5] = createPrinter(language === "ts", svelte5);
909
+ const passwordForm = demoPassword ? `
910
+ <form method="post" action="?/signInEmail" use:enhance>
911
+ <label>
912
+ Email
913
+ <input type="email" name="email"${input} />
914
+ </label>
915
+ <label>
916
+ Password
917
+ <input type="password" name="password"${input} />
918
+ </label>
919
+ <label>
920
+ Name (for registration)
921
+ <input name="name"${input} />
922
+ </label>
923
+ <button${btn}>Login</button>
924
+ <button formaction="?/signUpEmail"${btn}>Register</button>
925
+ </form>
926
+ ${tailwind ? `<p class="text-red-500">{form?.message ?? ''}</p>` : `<p style="color: red">{form?.message ?? ''}</p>`}` : "";
927
+ const separator = demoPassword && demoGithub ? `\n\n <hr ${tailwind ? "class=\"my-4\"" : ""} />` : "";
928
+ const githubForm = demoGithub ? `
929
+ <form method="post" action="?/signInSocial" use:enhance>
930
+ <input type="hidden" name="provider" value="github" />
931
+ <input type="hidden" name="callbackURL" value="/demo/better-auth" />
932
+ <button${btn}>Sign in with GitHub</button>
933
+ </form>` : "";
934
+ return dedent_default`
935
+ <script ${ts("lang='ts'")}>
936
+ import { enhance } from '$app/forms';
937
+ ${ts("import type { ActionData } from './$types';\n")}
938
+ ${s5(`let { form }${ts(": { form: ActionData }")} = $props();`, `export let form${ts(": ActionData")};`)}
939
+ <\/script>
940
+
941
+ <h1>Login</h1>${passwordForm}${separator}${githubForm}
942
+ `;
943
+ });
944
+ sv.file(`${kit.routesDirectory}/demo/better-auth/+page.server.${language}`, (content) => {
945
+ if (content) {
946
+ const filePath = `${kit.routesDirectory}/demo/better-auth/+page.server.${language}`;
947
+ R.warn(`Existing ${color.warning(filePath)} file. Could not update.`);
948
+ return content;
949
+ }
950
+ const [ts] = createPrinter(language === "ts");
951
+ return dedent_default`
952
+ import { redirect } from '@sveltejs/kit';
953
+ ${ts("import type { Actions } from './$types';")}
954
+ ${ts("import type { PageServerLoad } from './$types';")}
955
+ import { auth } from '$lib/server/auth';
956
+
957
+ export const load${ts(": PageServerLoad")} = async (event) => {
958
+ if (!event.locals.user) {
959
+ return redirect(302, '/demo/better-auth/login');
960
+ }
961
+ return { user: event.locals.user };
962
+ };
963
+
964
+ export const actions${ts(": Actions")} = {
965
+ signOut: async (event) => {
966
+ await auth.api.signOut({
967
+ headers: event.request.headers
968
+ });
969
+ return redirect(302, '/demo/better-auth/login');
970
+ }
971
+ };
972
+ `;
973
+ });
974
+ sv.file(`${kit.routesDirectory}/demo/better-auth/+page.svelte`, (content) => {
975
+ if (content) {
976
+ const filePath = `${kit.routesDirectory}/demo/better-auth/+page.svelte`;
977
+ R.warn(`Existing ${color.warning(filePath)} file. Could not update.`);
978
+ return content;
979
+ }
980
+ const tailwind = dependencyVersion("@tailwindcss/vite") !== void 0;
981
+ const twBtnClasses = "class=\"bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition\"";
982
+ const svelte5 = !!dependencyVersion("svelte")?.startsWith("5");
983
+ const [ts, s5] = createPrinter(language === "ts", svelte5);
984
+ return dedent_default`
985
+ <script ${ts("lang='ts'")}>
986
+ import { enhance } from '$app/forms';
987
+ ${ts("import type { PageServerData } from './$types';\n")}
988
+ ${s5(`let { data }${ts(": { data: PageServerData }")} = $props();`, `export let data${ts(": PageServerData")};`)}
989
+ <\/script>
990
+
991
+ <h1>Hi, {data.user.name}!</h1>
992
+ <p>Your user ID is {data.user.id}.</p>
993
+ <form method="post" action="?/signOut" use:enhance>
994
+ <button ${tailwind ? twBtnClasses : ""}>Sign out</button>
995
+ </form>
996
+ `;
997
+ });
998
+ }
999
+ },
1000
+ nextSteps: ({ options: options$8, packageManager }) => {
1001
+ const { command: authCmd, args: authArgs } = resolveCommand(packageManager, "run", ["auth:schema"]);
1002
+ const { command: dbCmd, args: dbArgs } = resolveCommand(packageManager, "run", ["db:push"]);
1003
+ const steps = [
1004
+ `Run ${color.command(`${authCmd} ${authArgs.join(" ")}`)} to generate the auth schema`,
1005
+ `Run ${color.command(`${dbCmd} ${dbArgs.join(" ")}`)} to update your database`,
1006
+ `Check ${color.env("ORIGIN")} & ${color.env("BETTER_AUTH_SECRET")} in ${color.path(".env")} and adjust it to your needs`
1007
+ ];
1008
+ if (options$8.demo.includes("github")) steps.push(`Set your ${color.env("GITHUB_CLIENT_ID")} and ${color.env("GITHUB_CLIENT_SECRET")} in ${color.path(".env")}`);
1009
+ if (options$8.demo.length > 0) steps.push(`Visit ${color.route("/demo/better-auth")} route to view the demo`);
1010
+ return steps;
1011
+ }
1012
+ });
1013
+ function generateEnvFileContent$1(content, demoGithub, isExample) {
1014
+ content = upsert(content, "ORIGIN", {
1015
+ value: isExample ? `""` : `"http://localhost:5173"`,
1016
+ separator: true
1017
+ });
1018
+ content = upsert(content, "BETTER_AUTH_SECRET", {
1019
+ value: isExample ? `""` : `"${crypto.randomUUID()}"`,
1020
+ comment: [
1021
+ "Better Auth",
1022
+ "For production use 32 characters and generated with high entropy",
1023
+ "https://www.better-auth.com/docs/installation"
1024
+ ],
1025
+ separator: true
1026
+ });
1027
+ if (demoGithub) {
1028
+ content = upsert(content, "GITHUB_CLIENT_ID", {
1029
+ value: `""`,
1030
+ comment: "GitHub OAuth\n# https://www.better-auth.com/docs/authentication/github"
1031
+ });
1032
+ content = upsert(content, "GITHUB_CLIENT_SECRET", { value: `""` });
1033
+ }
1034
+ return content;
1035
+ }
1036
+
1037
+ //#endregion
1038
+ //#region lib/addons/devtools-json.ts
614
1039
  var devtools_json_default = defineAddon({
615
1040
  id: "devtools-json",
616
1041
  shortDescription: "devtools json",
@@ -632,13 +1057,13 @@ var devtools_json_default = defineAddon({
632
1057
  });
633
1058
 
634
1059
  //#endregion
635
- //#region lib/addons/drizzle/index.ts
1060
+ //#region lib/addons/drizzle.ts
636
1061
  const PORTS = {
637
1062
  mysql: "3306",
638
1063
  postgresql: "5432",
639
1064
  sqlite: ""
640
1065
  };
641
- const options$7 = defineAddonOptions().add("database", {
1066
+ const options$6 = defineAddonOptions().add("database", {
642
1067
  question: "Which database would you like to use?",
643
1068
  type: "select",
644
1069
  default: "sqlite",
@@ -717,7 +1142,7 @@ var drizzle_default = defineAddon({
717
1142
  id: "drizzle",
718
1143
  shortDescription: "database orm",
719
1144
  homepage: "https://orm.drizzle.team",
720
- options: options$7,
1145
+ options: options$6,
721
1146
  setup: ({ kit, unsupported, runsAfter }) => {
722
1147
  runsAfter("prettier");
723
1148
  if (!kit) return unsupported("Requires SvelteKit");
@@ -732,21 +1157,21 @@ var drizzle_default = defineAddon({
732
1157
  database: path.resolve(baseDBPath, `index.${language}`)
733
1158
  };
734
1159
  for (const [fileType, filePath] of Object.entries(paths)) if (fs.existsSync(filePath)) return cancel(`Preexisting ${fileType} file at '${filePath}'`);
735
- sv.devDependency("drizzle-orm", "^0.45.0");
1160
+ sv.devDependency("drizzle-orm", "^0.45.1");
736
1161
  sv.devDependency("drizzle-kit", "^0.31.8");
737
1162
  sv.devDependency("@types/node", getNodeTypesVersion());
738
- if (options$8.mysql === "mysql2") sv.dependency("mysql2", "^3.15.3");
1163
+ if (options$8.mysql === "mysql2") sv.dependency("mysql2", "^3.16.3");
739
1164
  if (options$8.mysql === "planetscale") sv.dependency("@planetscale/database", "^1.19.0");
740
1165
  if (options$8.postgresql === "neon") sv.dependency("@neondatabase/serverless", "^1.0.2");
741
- if (options$8.postgresql === "postgres.js") sv.dependency("postgres", "^3.4.7");
1166
+ if (options$8.postgresql === "postgres.js") sv.dependency("postgres", "^3.4.8");
742
1167
  if (options$8.sqlite === "better-sqlite3") {
743
- sv.dependency("better-sqlite3", "^12.5.0");
1168
+ sv.dependency("better-sqlite3", "^12.6.2");
744
1169
  sv.devDependency("@types/better-sqlite3", "^7.6.13");
745
1170
  sv.pnpmBuildDependency("better-sqlite3");
746
1171
  }
747
- if (options$8.sqlite === "libsql" || options$8.sqlite === "turso") sv.devDependency("@libsql/client", "^0.15.15");
748
- sv.file(".env", (content) => generateEnvFileContent(content, options$8));
749
- sv.file(".env.example", (content) => generateEnvFileContent(content, options$8));
1172
+ if (options$8.sqlite === "libsql" || options$8.sqlite === "turso") sv.devDependency("@libsql/client", "^0.17.0");
1173
+ sv.file(".env", (content) => generateEnvFileContent(content, options$8, false));
1174
+ sv.file(".env.example", (content) => generateEnvFileContent(content, options$8, true));
750
1175
  if (options$8.docker && (options$8.mysql === "mysql2" || options$8.postgresql === "postgres.js")) {
751
1176
  const composeFileOptions = [
752
1177
  "docker-compose.yml",
@@ -806,13 +1231,11 @@ var drizzle_default = defineAddon({
806
1231
  return generateCode();
807
1232
  });
808
1233
  if (Boolean(dependencyVersion("prettier"))) sv.file(files.prettierignore, (content) => {
809
- if (!content.includes(`/drizzle/`)) return content.trimEnd() + "\n/drizzle/";
810
- return content;
1234
+ return upsert(content, "/drizzle/");
811
1235
  });
812
1236
  if (options$8.database === "sqlite") sv.file(files.gitignore, (content) => {
813
1237
  if (content.length === 0) return content;
814
- if (!content.includes("\n*.db")) content = content.trimEnd() + "\n\n# SQLite\n*.db";
815
- return content;
1238
+ return upsert(content, "*.db", { comment: "SQLite" });
816
1239
  });
817
1240
  sv.file(paths["drizzle config"], (content) => {
818
1241
  const { ast, generateCode } = parse$1.script(content);
@@ -836,7 +1259,7 @@ var drizzle_default = defineAddon({
836
1259
  });
837
1260
  sv.file(paths["database schema"], (content) => {
838
1261
  const { ast, generateCode } = parse$1.script(content);
839
- let userSchemaExpression;
1262
+ let taskSchemaExpression;
840
1263
  if (options$8.database === "sqlite") {
841
1264
  addNamed(ast, {
842
1265
  from: "drizzle-orm/sqlite-core",
@@ -846,10 +1269,11 @@ var drizzle_default = defineAddon({
846
1269
  "text"
847
1270
  ]
848
1271
  });
849
- userSchemaExpression = parseExpression(`sqliteTable('user', {
850
- id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
851
- age: integer('age')
852
- })`);
1272
+ taskSchemaExpression = parseExpression(`sqliteTable('task', {
1273
+ id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
1274
+ title: text('title').notNull(),
1275
+ priority: integer('priority').notNull().default(1)
1276
+ })`);
853
1277
  }
854
1278
  if (options$8.database === "mysql") {
855
1279
  addNamed(ast, {
@@ -857,13 +1281,15 @@ var drizzle_default = defineAddon({
857
1281
  imports: [
858
1282
  "mysqlTable",
859
1283
  "serial",
860
- "int"
1284
+ "int",
1285
+ "text"
861
1286
  ]
862
1287
  });
863
- userSchemaExpression = parseExpression(`mysqlTable('user', {
864
- id: serial('id').primaryKey(),
865
- age: int('age'),
866
- })`);
1288
+ taskSchemaExpression = parseExpression(`mysqlTable('task', {
1289
+ id: serial('id').primaryKey(),
1290
+ title: text('title').notNull(),
1291
+ priority: int('priority').notNull().default(1)
1292
+ })`);
867
1293
  }
868
1294
  if (options$8.database === "postgresql") {
869
1295
  addNamed(ast, {
@@ -871,23 +1297,25 @@ var drizzle_default = defineAddon({
871
1297
  imports: [
872
1298
  "pgTable",
873
1299
  "serial",
874
- "integer"
1300
+ "integer",
1301
+ "text"
875
1302
  ]
876
1303
  });
877
- userSchemaExpression = parseExpression(`pgTable('user', {
878
- id: serial('id').primaryKey(),
879
- age: integer('age'),
880
- })`);
881
- }
882
- if (!userSchemaExpression) throw new Error("unreachable state...");
883
- const userIdentifier = declaration(ast, {
1304
+ taskSchemaExpression = parseExpression(`pgTable('task', {
1305
+ id: serial('id').primaryKey(),
1306
+ title: text('title').notNull(),
1307
+ priority: integer('priority').notNull().default(1)
1308
+ })`);
1309
+ }
1310
+ if (!taskSchemaExpression) throw new Error("unreachable state...");
1311
+ const taskIdentifier = declaration(ast, {
884
1312
  kind: "const",
885
- name: "user",
886
- value: userSchemaExpression
1313
+ name: "task",
1314
+ value: taskSchemaExpression
887
1315
  });
888
1316
  createNamed(ast, {
889
- name: "user",
890
- fallback: userIdentifier
1317
+ name: "task",
1318
+ fallback: taskIdentifier
891
1319
  });
892
1320
  return generateCode();
893
1321
  });
@@ -897,7 +1325,7 @@ var drizzle_default = defineAddon({
897
1325
  from: "$env/dynamic/private",
898
1326
  imports: ["env"]
899
1327
  });
900
- addNamespace(ast, {
1328
+ addNamespace$1(ast, {
901
1329
  from: "./schema",
902
1330
  as: "schema"
903
1331
  });
@@ -925,12 +1353,7 @@ var drizzle_default = defineAddon({
925
1353
  imports: ["drizzle"]
926
1354
  });
927
1355
  if (options$8.sqlite === "turso") {
928
- addNamed(ast, {
929
- from: "$app/environment",
930
- imports: ["dev"]
931
- });
932
- const authTokenCheck = parseStatement("if (!dev && !env.DATABASE_AUTH_TOKEN) throw new Error('DATABASE_AUTH_TOKEN is not set');");
933
- ast.body.push(authTokenCheck);
1356
+ ast.body.push(parseStatement("if (!env.DATABASE_AUTH_TOKEN) throw new Error('DATABASE_AUTH_TOKEN is not set');"));
934
1357
  clientExpression = parseExpression("createClient({ url: env.DATABASE_URL, authToken: env.DATABASE_AUTH_TOKEN })");
935
1358
  } else clientExpression = parseExpression("createClient({ url: env.DATABASE_URL })");
936
1359
  }
@@ -1000,7 +1423,7 @@ var drizzle_default = defineAddon({
1000
1423
  });
1001
1424
  },
1002
1425
  nextSteps: ({ options: options$8, packageManager }) => {
1003
- const steps = [`You will need to set ${color.env("DATABASE_URL")} in your production environment`];
1426
+ const steps = [];
1004
1427
  if (options$8.docker) {
1005
1428
  const { command: command$1, args: args$1 } = resolveCommand(packageManager, "run", ["db:start"]);
1006
1429
  steps.push(`Run ${color.command(`${command$1} ${args$1.join(" ")}`)} to start the docker container`);
@@ -1010,45 +1433,39 @@ var drizzle_default = defineAddon({
1010
1433
  return steps;
1011
1434
  }
1012
1435
  });
1013
- function generateEnvFileContent(content, opts) {
1436
+ function generateEnvFileContent(content, opts, isExample) {
1014
1437
  const DB_URL_KEY = "DATABASE_URL";
1015
- if (opts.docker) {
1016
- const protocol = opts.database === "mysql" ? "mysql" : "postgres";
1017
- const port = PORTS[opts.database];
1018
- content = addEnvVar(content, DB_URL_KEY, `"${protocol}://root:mysecretpassword@localhost:${port}/local"`);
1019
- return content;
1020
- }
1021
- if (opts.sqlite === "better-sqlite3" || opts.sqlite === "libsql") {
1022
- const dbFile = opts.sqlite === "libsql" ? "file:local.db" : "local.db";
1023
- content = addEnvVar(content, DB_URL_KEY, dbFile);
1024
- return content;
1025
- }
1026
- content = addEnvComment(content, "Replace with your DB credentials!");
1027
- if (opts.sqlite === "turso") {
1028
- content = addEnvVar(content, DB_URL_KEY, "\"libsql://db-name-user.turso.io\"");
1029
- content = addEnvVar(content, "DATABASE_AUTH_TOKEN", "\"\"");
1030
- content = addEnvComment(content, "A local DB can also be used in dev as well");
1031
- content = addEnvComment(content, `${DB_URL_KEY}="file:local.db"`);
1032
- }
1033
- if (opts.database === "mysql") content = addEnvVar(content, DB_URL_KEY, "\"mysql://user:password@host:port/db-name\"");
1034
- if (opts.database === "postgresql") content = addEnvVar(content, DB_URL_KEY, "\"postgres://user:password@host:port/db-name\"");
1035
- return content;
1036
- }
1037
- function addEnvVar(content, key, value) {
1038
- if (!content.includes(key + "=")) content = appendEnvContent(content, `${key}=${value}`);
1039
- return content;
1040
- }
1041
- function addEnvComment(content, comment) {
1042
- const commented = `# ${comment}`;
1043
- if (!content.includes(commented)) content = appendEnvContent(content, commented);
1438
+ let value;
1439
+ const comment = ["Drizzle"];
1440
+ if (opts.docker) value = `"${opts.database === "mysql" ? "mysql" : "postgres"}://root:mysecretpassword@localhost:${PORTS[opts.database]}/local"`;
1441
+ else if (opts.sqlite === "better-sqlite3" || opts.sqlite === "libsql") value = opts.sqlite === "libsql" ? "file:local.db" : "local.db";
1442
+ else if (opts.sqlite === "turso") {
1443
+ value = "\"libsql://db-name-user.turso.io\"";
1444
+ comment.push("Replace with your DB credentials!", {
1445
+ text: "A local DB can also be used in dev as well",
1446
+ mode: "append"
1447
+ }, {
1448
+ text: `${DB_URL_KEY}="file:local.db"`,
1449
+ mode: "append"
1450
+ });
1451
+ } else if (opts.database === "mysql") {
1452
+ value = "\"mysql://user:password@host:port/db-name\"";
1453
+ comment.push("Replace with your DB credentials!");
1454
+ } else if (opts.database === "postgresql") {
1455
+ value = "\"postgres://user:password@host:port/db-name\"";
1456
+ comment.push("Replace with your DB credentials!");
1457
+ } else value = "";
1458
+ content = upsert(content, DB_URL_KEY, {
1459
+ value,
1460
+ comment,
1461
+ separator: true
1462
+ });
1463
+ if (opts.sqlite === "turso") content = upsert(content, "DATABASE_AUTH_TOKEN", { value: isExample ? `""` : `"${crypto.randomUUID()}"` });
1044
1464
  return content;
1045
1465
  }
1046
- function appendEnvContent(existing, content) {
1047
- return (!existing.length || existing.endsWith("\n") ? existing : existing + "\n") + content + "\n";
1048
- }
1049
1466
 
1050
1467
  //#endregion
1051
- //#region lib/addons/eslint/index.ts
1468
+ //#region lib/addons/eslint.ts
1052
1469
  var eslint_default = defineAddon({
1053
1470
  id: "eslint",
1054
1471
  shortDescription: "linter",
@@ -1057,13 +1474,13 @@ var eslint_default = defineAddon({
1057
1474
  run: ({ sv, language, dependencyVersion, files }) => {
1058
1475
  const typescript = language === "ts";
1059
1476
  const prettierInstalled = Boolean(dependencyVersion("prettier"));
1060
- sv.devDependency("eslint", "^9.39.1");
1061
- sv.devDependency("@eslint/compat", "^1.4.0");
1062
- sv.devDependency("eslint-plugin-svelte", "^3.13.1");
1063
- sv.devDependency("globals", "^16.5.0");
1064
- sv.devDependency("@eslint/js", "^9.39.1");
1477
+ sv.devDependency("eslint", "^9.39.2");
1478
+ sv.devDependency("@eslint/compat", "^2.0.2");
1479
+ sv.devDependency("eslint-plugin-svelte", "^3.14.0");
1480
+ sv.devDependency("globals", "^17.3.0");
1481
+ sv.devDependency("@eslint/js", "^9.39.2");
1065
1482
  sv.devDependency("@types/node", getNodeTypesVersion());
1066
- if (typescript) sv.devDependency("typescript-eslint", "^8.48.1");
1483
+ if (typescript) sv.devDependency("typescript-eslint", "^8.54.0");
1067
1484
  if (prettierInstalled) sv.devDependency("eslint-config-prettier", "^10.1.8");
1068
1485
  sv.file(files.package, (content) => {
1069
1486
  const { data, generateCode } = parse$1.json(content);
@@ -1077,7 +1494,7 @@ var eslint_default = defineAddon({
1077
1494
  from: "./svelte.config.js",
1078
1495
  as: "svelteConfig"
1079
1496
  });
1080
- const gitIgnorePathStatement = parseStatement("\nconst gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));");
1497
+ const gitIgnorePathStatement = parseStatement("\nconst gitignorePath = path.resolve(import.meta.dirname, '.gitignore');");
1081
1498
  appendStatement(ast, { statement: gitIgnorePathStatement });
1082
1499
  const ignoresConfig = parseExpression("includeIgnoreFile(gitignorePath)");
1083
1500
  eslintConfigs.push(ignoresConfig);
@@ -1145,7 +1562,7 @@ var eslint_default = defineAddon({
1145
1562
  }
1146
1563
  const { value: defaultExport, astNode } = createDefault(ast, { fallback: exportExpression });
1147
1564
  if (defaultExport !== exportExpression) {
1148
- T.warn("An eslint config is already defined. Skipping initialization.");
1565
+ R.warn("An eslint config is already defined. Skipping initialization.");
1149
1566
  return content;
1150
1567
  }
1151
1568
  if (!typescript) addJsDocTypeComment(astNode, comments, { type: "import('eslint').Linter.Config[]" });
@@ -1173,9 +1590,9 @@ var eslint_default = defineAddon({
1173
1590
  from: "@eslint/compat",
1174
1591
  imports: ["includeIgnoreFile"]
1175
1592
  });
1176
- addNamed(ast, {
1177
- from: "node:url",
1178
- imports: ["fileURLToPath"]
1593
+ addDefault(ast, {
1594
+ from: "node:path",
1595
+ as: "path"
1179
1596
  });
1180
1597
  return generateCode();
1181
1598
  });
@@ -1184,607 +1601,7 @@ var eslint_default = defineAddon({
1184
1601
  });
1185
1602
 
1186
1603
  //#endregion
1187
- //#region lib/addons/lucia/index.ts
1188
- const TABLE_TYPE = {
1189
- mysql: "mysqlTable",
1190
- postgresql: "pgTable",
1191
- sqlite: "sqliteTable",
1192
- turso: "sqliteTable"
1193
- };
1194
- let drizzleDialect;
1195
- let schemaPath;
1196
- const options$6 = defineAddonOptions().add("demo", {
1197
- type: "boolean",
1198
- default: true,
1199
- question: `Do you want to include a demo? ${color.optional("(includes a login/register page)")}`
1200
- }).build();
1201
- var lucia_default = defineAddon({
1202
- id: "lucia",
1203
- shortDescription: "auth guide",
1204
- homepage: "https://lucia-auth.com",
1205
- options: options$6,
1206
- setup: ({ kit, dependencyVersion, unsupported, dependsOn, runsAfter }) => {
1207
- if (!kit) unsupported("Requires SvelteKit");
1208
- if (!dependencyVersion("drizzle-orm")) dependsOn("drizzle");
1209
- runsAfter("tailwindcss");
1210
- },
1211
- run: ({ sv, language, options: options$8, kit, dependencyVersion }) => {
1212
- const typescript = language === "ts";
1213
- sv.devDependency("@oslojs/crypto", "^1.0.1");
1214
- sv.devDependency("@oslojs/encoding", "^1.1.0");
1215
- if (options$8.demo) sv.dependency("@node-rs/argon2", "^2.0.2");
1216
- sv.file(`drizzle.config.${language}`, (content) => {
1217
- const { ast, generateCode } = parse$1.script(content);
1218
- const isProp = (name$1, node) => node.key.type === "Identifier" && node.key.name === name$1;
1219
- walk(ast, null, { Property(node) {
1220
- if (isProp("dialect", node) && node.value.type === "Literal" && typeof node.value.value === "string") drizzleDialect = node.value.value;
1221
- if (isProp("schema", node) && node.value.type === "Literal" && typeof node.value.value === "string") schemaPath = node.value.value;
1222
- } });
1223
- if (!drizzleDialect) throw new Error("Failed to detect DB dialect in your `drizzle.config.[js|ts]` file");
1224
- if (!schemaPath) throw new Error("Failed to find schema path in your `drizzle.config.[js|ts]` file");
1225
- return generateCode();
1226
- });
1227
- sv.file(schemaPath, (content) => {
1228
- const { ast, generateCode } = parse$1.script(content);
1229
- const createTable = (name$1) => createCall({
1230
- name: TABLE_TYPE[drizzleDialect],
1231
- args: [name$1]
1232
- });
1233
- const userDecl = declaration(ast, {
1234
- kind: "const",
1235
- name: "user",
1236
- value: createTable("user")
1237
- });
1238
- const sessionDecl = declaration(ast, {
1239
- kind: "const",
1240
- name: "session",
1241
- value: createTable("session")
1242
- });
1243
- const user = createNamed(ast, {
1244
- name: "user",
1245
- fallback: userDecl
1246
- });
1247
- const session = createNamed(ast, {
1248
- name: "session",
1249
- fallback: sessionDecl
1250
- });
1251
- const userTable = getCallExpression(user);
1252
- const sessionTable = getCallExpression(session);
1253
- if (!userTable || !sessionTable) throw new Error("failed to find call expression of `user` or `session`");
1254
- if (userTable.arguments.length === 1) userTable.arguments.push(create$2({}));
1255
- if (sessionTable.arguments.length === 1) sessionTable.arguments.push(create$2({}));
1256
- const userAttributes = userTable.arguments[1];
1257
- const sessionAttributes = sessionTable.arguments[1];
1258
- if (userAttributes?.type !== "ObjectExpression" || sessionAttributes?.type !== "ObjectExpression") throw new Error("unexpected shape of `user` or `session` table definition");
1259
- if (drizzleDialect === "sqlite" || drizzleDialect === "turso") {
1260
- addNamed(ast, {
1261
- from: "drizzle-orm/sqlite-core",
1262
- imports: [
1263
- "sqliteTable",
1264
- "text",
1265
- "integer"
1266
- ]
1267
- });
1268
- overrideProperties(userAttributes, { id: parseExpression("text('id').primaryKey()") });
1269
- if (options$8.demo) overrideProperties(userAttributes, {
1270
- username: parseExpression("text('username').notNull().unique()"),
1271
- passwordHash: parseExpression("text('password_hash').notNull()")
1272
- });
1273
- overrideProperties(sessionAttributes, {
1274
- id: parseExpression("text('id').primaryKey()"),
1275
- userId: parseExpression("text('user_id').notNull().references(() => user.id)"),
1276
- expiresAt: parseExpression("integer('expires_at', { mode: 'timestamp' }).notNull()")
1277
- });
1278
- }
1279
- if (drizzleDialect === "mysql") {
1280
- addNamed(ast, {
1281
- from: "drizzle-orm/mysql-core",
1282
- imports: [
1283
- "mysqlTable",
1284
- "varchar",
1285
- "datetime"
1286
- ]
1287
- });
1288
- overrideProperties(userAttributes, { id: parseExpression("varchar('id', { length: 255 }).primaryKey()") });
1289
- if (options$8.demo) overrideProperties(userAttributes, {
1290
- username: parseExpression("varchar('username', { length: 32 }).notNull().unique()"),
1291
- passwordHash: parseExpression("varchar('password_hash', { length: 255 }).notNull()")
1292
- });
1293
- overrideProperties(sessionAttributes, {
1294
- id: parseExpression("varchar('id', { length: 255 }).primaryKey()"),
1295
- userId: parseExpression("varchar('user_id', { length: 255 }).notNull().references(() => user.id)"),
1296
- expiresAt: parseExpression("datetime('expires_at').notNull()")
1297
- });
1298
- }
1299
- if (drizzleDialect === "postgresql") {
1300
- addNamed(ast, {
1301
- from: "drizzle-orm/pg-core",
1302
- imports: [
1303
- "pgTable",
1304
- "text",
1305
- "timestamp"
1306
- ]
1307
- });
1308
- overrideProperties(userAttributes, { id: parseExpression("text('id').primaryKey()") });
1309
- if (options$8.demo) overrideProperties(userAttributes, {
1310
- username: parseExpression("text('username').notNull().unique()"),
1311
- passwordHash: parseExpression("text('password_hash').notNull()")
1312
- });
1313
- overrideProperties(sessionAttributes, {
1314
- id: parseExpression("text('id').primaryKey()"),
1315
- userId: parseExpression("text('user_id').notNull().references(() => user.id)"),
1316
- expiresAt: parseExpression("timestamp('expires_at', { withTimezone: true, mode: 'date' }).notNull()")
1317
- });
1318
- }
1319
- let code = generateCode();
1320
- if (typescript) {
1321
- if (!code.includes("export type Session =")) code += "\n\nexport type Session = typeof session.$inferSelect;";
1322
- if (!code.includes("export type User =")) code += "\n\nexport type User = typeof user.$inferSelect;";
1323
- }
1324
- return code;
1325
- });
1326
- sv.file(`${kit?.libDirectory}/server/auth.${language}`, (content) => {
1327
- const { ast, generateCode } = parse$1.script(content);
1328
- addNamespace(ast, {
1329
- from: "$lib/server/db/schema",
1330
- as: "table"
1331
- });
1332
- addNamed(ast, {
1333
- from: "$lib/server/db",
1334
- imports: ["db"]
1335
- });
1336
- addNamed(ast, {
1337
- from: "@oslojs/encoding",
1338
- imports: ["encodeBase64url", "encodeHexLowerCase"]
1339
- });
1340
- addNamed(ast, {
1341
- from: "@oslojs/crypto/sha2",
1342
- imports: ["sha256"]
1343
- });
1344
- addNamed(ast, {
1345
- from: "drizzle-orm",
1346
- imports: ["eq"]
1347
- });
1348
- if (typescript) addNamed(ast, {
1349
- from: "@sveltejs/kit",
1350
- imports: ["RequestEvent"],
1351
- isType: true
1352
- });
1353
- const ms = new MagicString(generateCode().trim());
1354
- const [ts] = createPrinter(typescript);
1355
- if (!ms.original.includes("const DAY_IN_MS")) ms.append("\n\nconst DAY_IN_MS = 1000 * 60 * 60 * 24;");
1356
- if (!ms.original.includes("export const sessionCookieName")) ms.append("\n\nexport const sessionCookieName = 'auth-session';");
1357
- if (!ms.original.includes("export function generateSessionToken")) {
1358
- const generateSessionToken = dedent_default`
1359
- export function generateSessionToken() {
1360
- const bytes = crypto.getRandomValues(new Uint8Array(18));
1361
- const token = encodeBase64url(bytes);
1362
- return token;
1363
- }`;
1364
- ms.append(`\n\n${generateSessionToken}`);
1365
- }
1366
- if (!ms.original.includes("async function createSession")) {
1367
- const createSession = dedent_default`
1368
- ${ts("", "/**")}
1369
- ${ts("", " * @param {string} token")}
1370
- ${ts("", " * @param {string} userId")}
1371
- ${ts("", " */")}
1372
- export async function createSession(token${ts(": string")}, userId${ts(": string")}) {
1373
- const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
1374
- const session${ts(": table.Session")} = {
1375
- id: sessionId,
1376
- userId,
1377
- expiresAt: new Date(Date.now() + DAY_IN_MS * 30)
1378
- };
1379
- await db.insert(table.session).values(session);
1380
- return session;
1381
- }`;
1382
- ms.append(`\n\n${createSession}`);
1383
- }
1384
- if (!ms.original.includes("async function validateSessionToken")) {
1385
- const validateSessionToken = dedent_default`
1386
- ${ts("", "/** @param {string} token */")}
1387
- export async function validateSessionToken(token${ts(": string")}) {
1388
- const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
1389
- const [result] = await db
1390
- .select({
1391
- // Adjust user table here to tweak returned data
1392
- user: { id: table.user.id, username: table.user.username },
1393
- session: table.session
1394
- })
1395
- .from(table.session)
1396
- .innerJoin(table.user, eq(table.session.userId, table.user.id))
1397
- .where(eq(table.session.id, sessionId));
1398
-
1399
- if (!result) {
1400
- return { session: null, user: null };
1401
- }
1402
- const { session, user } = result;
1403
-
1404
- const sessionExpired = Date.now() >= session.expiresAt.getTime();
1405
- if (sessionExpired) {
1406
- await db.delete(table.session).where(eq(table.session.id, session.id));
1407
- return { session: null, user: null };
1408
- }
1409
-
1410
- const renewSession = Date.now() >= session.expiresAt.getTime() - DAY_IN_MS * 15;
1411
- if (renewSession) {
1412
- session.expiresAt = new Date(Date.now() + DAY_IN_MS * 30);
1413
- await db
1414
- .update(table.session)
1415
- .set({ expiresAt: session.expiresAt })
1416
- .where(eq(table.session.id, session.id));
1417
- }
1418
-
1419
- return { session, user };
1420
- }`;
1421
- ms.append(`\n\n${validateSessionToken}`);
1422
- }
1423
- if (typescript && !ms.original.includes("export type SessionValidationResult")) ms.append(`\n\nexport type SessionValidationResult = Awaited<ReturnType<typeof validateSessionToken>>;`);
1424
- if (!ms.original.includes("async function invalidateSession")) {
1425
- const invalidateSession = dedent_default`
1426
- ${ts("", "/** @param {string} sessionId */")}
1427
- export async function invalidateSession(sessionId${ts(": string")}) {
1428
- await db.delete(table.session).where(eq(table.session.id, sessionId));
1429
- }`;
1430
- ms.append(`\n\n${invalidateSession}`);
1431
- }
1432
- if (!ms.original.includes("export function setSessionTokenCookie")) {
1433
- const setSessionTokenCookie = dedent_default`
1434
- ${ts("", "/**")}
1435
- ${ts("", " * @param {import(\"@sveltejs/kit\").RequestEvent} event")}
1436
- ${ts("", " * @param {string} token")}
1437
- ${ts("", " * @param {Date} expiresAt")}
1438
- ${ts("", " */")}
1439
- export function setSessionTokenCookie(event${ts(": RequestEvent")}, token${ts(": string")}, expiresAt${ts(": Date")}) {
1440
- event.cookies.set(sessionCookieName, token, {
1441
- expires: expiresAt,
1442
- path: '/'
1443
- });
1444
- }`;
1445
- ms.append(`\n\n${setSessionTokenCookie}`);
1446
- }
1447
- if (!ms.original.includes("export function deleteSessionTokenCookie")) {
1448
- const deleteSessionTokenCookie = dedent_default`
1449
- ${ts("", "/** @param {import(\"@sveltejs/kit\").RequestEvent} event */")}
1450
- export function deleteSessionTokenCookie(event${ts(": RequestEvent")}) {
1451
- event.cookies.delete(sessionCookieName, {
1452
- path: '/'
1453
- });
1454
- }`;
1455
- ms.append(`\n\n${deleteSessionTokenCookie}`);
1456
- }
1457
- return ms.toString();
1458
- });
1459
- if (typescript) sv.file("src/app.d.ts", (content) => {
1460
- const { ast, generateCode } = parse$1.script(content);
1461
- const locals = addGlobalAppInterface(ast, { name: "Locals" });
1462
- if (!locals) throw new Error("Failed detecting `locals` interface in `src/app.d.ts`");
1463
- const user = locals.body.body.find((prop) => hasTypeProperty(prop, { name: "user" }));
1464
- const session = locals.body.body.find((prop) => hasTypeProperty(prop, { name: "session" }));
1465
- if (!user) locals.body.body.push(createLuciaType("user"));
1466
- if (!session) locals.body.body.push(createLuciaType("session"));
1467
- return generateCode();
1468
- });
1469
- sv.file(`src/hooks.server.${language}`, (content) => {
1470
- const { ast, generateCode } = parse$1.script(content);
1471
- addNamespace(ast, {
1472
- from: "$lib/server/auth",
1473
- as: "auth"
1474
- });
1475
- addHooksHandle(ast, {
1476
- language,
1477
- newHandleName: "handleAuth",
1478
- handleContent: getAuthHandleContent()
1479
- });
1480
- return generateCode();
1481
- });
1482
- if (options$8.demo) {
1483
- sv.file(`${kit?.routesDirectory}/demo/+page.svelte`, (content) => {
1484
- return addToDemoPage(content, "lucia", language);
1485
- });
1486
- sv.file(`${kit.routesDirectory}/demo/lucia/login/+page.server.${language}`, (content) => {
1487
- if (content) {
1488
- const filePath = `${kit.routesDirectory}/demo/lucia/login/+page.server.${typescript ? "ts" : "js"}`;
1489
- T.warn(`Existing ${color.warning(filePath)} file. Could not update.`);
1490
- return content;
1491
- }
1492
- const [ts] = createPrinter(typescript);
1493
- return dedent_default`
1494
- import { hash, verify } from '@node-rs/argon2';
1495
- import { encodeBase32LowerCase } from '@oslojs/encoding';
1496
- import { fail, redirect } from '@sveltejs/kit';
1497
- import { eq } from 'drizzle-orm';
1498
- import * as auth from '$lib/server/auth';
1499
- import { db } from '$lib/server/db';
1500
- import * as table from '$lib/server/db/schema';
1501
- ${ts("import type { Actions, PageServerLoad } from './$types';\n")}
1502
- export const load${ts(": PageServerLoad")} = async (event) => {
1503
- if (event.locals.user) {
1504
- return redirect(302, '/demo/lucia');
1505
- }
1506
- return {};
1507
- };
1508
-
1509
- export const actions${ts(": Actions")} = {
1510
- login: async (event) => {
1511
- const formData = await event.request.formData();
1512
- const username = formData.get('username');
1513
- const password = formData.get('password');
1514
-
1515
- if (!validateUsername(username)) {
1516
- return fail(400, { message: 'Invalid username (min 3, max 31 characters, alphanumeric only)' });
1517
- }
1518
- if (!validatePassword(password)) {
1519
- return fail(400, { message: 'Invalid password (min 6, max 255 characters)' });
1520
- }
1521
-
1522
- const results = await db
1523
- .select()
1524
- .from(table.user)
1525
- .where(eq(table.user.username, username));
1526
-
1527
- const existingUser = results.at(0);
1528
- if (!existingUser) {
1529
- return fail(400, { message: 'Incorrect username or password' });
1530
- }
1531
-
1532
- const validPassword = await verify(existingUser.passwordHash, password, {
1533
- memoryCost: 19456,
1534
- timeCost: 2,
1535
- outputLen: 32,
1536
- parallelism: 1,
1537
- });
1538
- if (!validPassword) {
1539
- return fail(400, { message: 'Incorrect username or password' });
1540
- }
1541
-
1542
- const sessionToken = auth.generateSessionToken();
1543
- const session = await auth.createSession(sessionToken, existingUser.id);
1544
- auth.setSessionTokenCookie(event, sessionToken, session.expiresAt);
1545
-
1546
- return redirect(302, '/demo/lucia');
1547
- },
1548
- register: async (event) => {
1549
- const formData = await event.request.formData();
1550
- const username = formData.get('username');
1551
- const password = formData.get('password');
1552
-
1553
- if (!validateUsername(username)) {
1554
- return fail(400, { message: 'Invalid username' });
1555
- }
1556
- if (!validatePassword(password)) {
1557
- return fail(400, { message: 'Invalid password' });
1558
- }
1559
-
1560
- const userId = generateUserId();
1561
- const passwordHash = await hash(password, {
1562
- // recommended minimum parameters
1563
- memoryCost: 19456,
1564
- timeCost: 2,
1565
- outputLen: 32,
1566
- parallelism: 1,
1567
- });
1568
-
1569
- try {
1570
- await db.insert(table.user).values({ id: userId, username, passwordHash });
1571
-
1572
- const sessionToken = auth.generateSessionToken();
1573
- const session = await auth.createSession(sessionToken, userId);
1574
- auth.setSessionTokenCookie(event, sessionToken, session.expiresAt);
1575
- } catch {
1576
- return fail(500, { message: 'An error has occurred' });
1577
- }
1578
- return redirect(302, '/demo/lucia');
1579
- },
1580
- };
1581
-
1582
- function generateUserId() {
1583
- // ID with 120 bits of entropy, or about the same as UUID v4.
1584
- const bytes = crypto.getRandomValues(new Uint8Array(15));
1585
- const id = encodeBase32LowerCase(bytes);
1586
- return id;
1587
- }
1588
-
1589
- function validateUsername(username${ts(": unknown")})${ts(": username is string")} {
1590
- return (
1591
- typeof username === 'string' &&
1592
- username.length >= 3 &&
1593
- username.length <= 31 &&
1594
- /^[a-z0-9_-]+$/.test(username)
1595
- );
1596
- }
1597
-
1598
- function validatePassword(password${ts(": unknown")})${ts(": password is string")} {
1599
- return (
1600
- typeof password === 'string' &&
1601
- password.length >= 6 &&
1602
- password.length <= 255
1603
- );
1604
- }
1605
- `;
1606
- });
1607
- sv.file(`${kit.routesDirectory}/demo/lucia/login/+page.svelte`, (content) => {
1608
- if (content) {
1609
- const filePath = `${kit.routesDirectory}/demo/lucia/login/+page.svelte`;
1610
- T.warn(`Existing ${color.warning(filePath)} file. Could not update.`);
1611
- return content;
1612
- }
1613
- const tailwind = dependencyVersion("@tailwindcss/vite") !== void 0;
1614
- const twInputClasses = "class=\"mt-1 px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"";
1615
- const twBtnClasses = "class=\"bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition\"";
1616
- const [ts, s5] = createPrinter(typescript, !!dependencyVersion("svelte")?.startsWith("5"));
1617
- return dedent_default`
1618
- <script ${ts("lang='ts'")}>
1619
- import { enhance } from '$app/forms';
1620
- ${ts("import type { ActionData } from './$types';\n")}
1621
- ${s5(`let { form }${ts(": { form: ActionData }")} = $props();`, `export let form${ts(": ActionData")};`)}
1622
- <\/script>
1623
-
1624
- <h1>Login/Register</h1>
1625
- <form method="post" action="?/login" use:enhance>
1626
- <label>
1627
- Username
1628
- <input
1629
- name="username"
1630
- ${tailwind ? twInputClasses : ""}
1631
- />
1632
- </label>
1633
- <label>
1634
- Password
1635
- <input
1636
- type="password"
1637
- name="password"
1638
- ${tailwind ? twInputClasses : ""}
1639
- />
1640
- </label>
1641
- <button ${tailwind ? twBtnClasses : ""}
1642
- >Login</button>
1643
- <button
1644
- formaction="?/register"
1645
- ${tailwind ? twBtnClasses : ""}
1646
- >Register</button>
1647
- </form>
1648
- <p style='color: red'>{form?.message ?? ''}</p>
1649
- `;
1650
- });
1651
- sv.file(`${kit.routesDirectory}/demo/lucia/+page.server.${language}`, (content) => {
1652
- if (content) {
1653
- const filePath = `${kit.routesDirectory}/demo/lucia/+page.server.${typescript ? "ts" : "js"}`;
1654
- T.warn(`Existing ${color.warning(filePath)} file. Could not update.`);
1655
- return content;
1656
- }
1657
- const [ts] = createPrinter(typescript);
1658
- return dedent_default`
1659
- import * as auth from '$lib/server/auth';
1660
- import { fail, redirect } from '@sveltejs/kit';
1661
- import { getRequestEvent } from '$app/server';
1662
- ${ts("import type { Actions, PageServerLoad } from './$types';\n")}
1663
- export const load${ts(": PageServerLoad")} = async () => {
1664
- const user = requireLogin()
1665
- return { user };
1666
- };
1667
-
1668
- export const actions${ts(": Actions")} = {
1669
- logout: async (event) => {
1670
- if (!event.locals.session) {
1671
- return fail(401);
1672
- }
1673
- await auth.invalidateSession(event.locals.session.id);
1674
- auth.deleteSessionTokenCookie(event);
1675
-
1676
- return redirect(302, '/demo/lucia/login');
1677
- },
1678
- };
1679
-
1680
- function requireLogin() {
1681
- const { locals } = getRequestEvent();
1682
-
1683
- if (!locals.user) {
1684
- return redirect(302, "/demo/lucia/login");
1685
- }
1686
-
1687
- return locals.user;
1688
- }
1689
- `;
1690
- });
1691
- sv.file(`${kit.routesDirectory}/demo/lucia/+page.svelte`, (content) => {
1692
- if (content) {
1693
- const filePath = `${kit.routesDirectory}/demo/lucia/+page.svelte`;
1694
- T.warn(`Existing ${color.warning(filePath)} file. Could not update.`);
1695
- return content;
1696
- }
1697
- const [ts, s5] = createPrinter(typescript, !!dependencyVersion("svelte")?.startsWith("5"));
1698
- return dedent_default`
1699
- <script ${ts("lang='ts'")}>
1700
- import { enhance } from '$app/forms';
1701
- ${ts("import type { PageServerData } from './$types';\n")}
1702
- ${s5(`let { data }${ts(": { data: PageServerData }")} = $props();`, `export let data${ts(": PageServerData")};`)}
1703
- <\/script>
1704
-
1705
- <h1>Hi, {data.user.username}!</h1>
1706
- <p>Your user ID is {data.user.id}.</p>
1707
- <form method='post' action='?/logout' use:enhance>
1708
- <button>Sign out</button>
1709
- </form>
1710
- `;
1711
- });
1712
- }
1713
- },
1714
- nextSteps: ({ options: options$8, packageManager }) => {
1715
- const { command, args } = resolveCommand(packageManager, "run", ["db:push"]);
1716
- const steps = [`Run ${color.command(`${command} ${args.join(" ")}`)} to update your database schema`];
1717
- if (options$8.demo) steps.push(`Visit ${color.route("/demo/lucia")} route to view the demo`);
1718
- return steps;
1719
- }
1720
- });
1721
- function createLuciaType(name$1) {
1722
- return {
1723
- type: "TSPropertySignature",
1724
- key: {
1725
- type: "Identifier",
1726
- name: name$1
1727
- },
1728
- computed: false,
1729
- typeAnnotation: {
1730
- type: "TSTypeAnnotation",
1731
- typeAnnotation: {
1732
- type: "TSIndexedAccessType",
1733
- objectType: {
1734
- type: "TSImportType",
1735
- argument: {
1736
- type: "Literal",
1737
- value: "$lib/server/auth"
1738
- },
1739
- qualifier: {
1740
- type: "Identifier",
1741
- name: "SessionValidationResult"
1742
- }
1743
- },
1744
- indexType: {
1745
- type: "TSLiteralType",
1746
- literal: {
1747
- type: "Literal",
1748
- value: name$1
1749
- }
1750
- }
1751
- }
1752
- }
1753
- };
1754
- }
1755
- function getAuthHandleContent() {
1756
- return `
1757
- async ({ event, resolve }) => {
1758
- const sessionToken = event.cookies.get(auth.sessionCookieName);
1759
- if (!sessionToken) {
1760
- event.locals.user = null;
1761
- event.locals.session = null;
1762
- return resolve(event);
1763
- }
1764
-
1765
- const { session, user } = await auth.validateSessionToken(sessionToken);
1766
- if (session) {
1767
- auth.setSessionTokenCookie(event, sessionToken, session.expiresAt);
1768
- } else {
1769
- auth.deleteSessionTokenCookie(event);
1770
- }
1771
-
1772
- event.locals.user = user;
1773
- event.locals.session = session;
1774
-
1775
- return resolve(event);
1776
- };`;
1777
- }
1778
- function getCallExpression(ast) {
1779
- let callExpression;
1780
- walk(ast, null, { CallExpression(node) {
1781
- callExpression ??= node;
1782
- } });
1783
- return callExpression;
1784
- }
1785
-
1786
- //#endregion
1787
- //#region lib/addons/mcp/index.ts
1604
+ //#region lib/addons/mcp.ts
1788
1605
  const options$5 = defineAddonOptions().add("ide", {
1789
1606
  question: "Which client would you like to use?",
1790
1607
  type: "multiselect",
@@ -1919,7 +1736,7 @@ var mcp_default = defineAddon({
1919
1736
  return generateCode();
1920
1737
  });
1921
1738
  }
1922
- if (filesExistingAlready.length > 0) T.warn(`${filesExistingAlready.map((path$2) => color.path(path$2)).join(", ")} already exists, we didn't touch ${filesExistingAlready.length > 1 ? "them" : "it"}. See ${color.website("https://svelte.dev/docs/mcp/overview#Usage")} for manual setup.`);
1739
+ if (filesExistingAlready.length > 0) R.warn(`${filesExistingAlready.map((path$2) => color.path(path$2)).join(", ")} already exists, we didn't touch ${filesExistingAlready.length > 1 ? "them" : "it"}. See ${color.website("https://svelte.dev/docs/mcp/overview#Usage")} for manual setup.`);
1923
1740
  },
1924
1741
  nextSteps({ options: options$8 }) {
1925
1742
  const steps = [];
@@ -1929,7 +1746,7 @@ var mcp_default = defineAddon({
1929
1746
  });
1930
1747
 
1931
1748
  //#endregion
1932
- //#region lib/addons/mdsvex/index.ts
1749
+ //#region lib/addons/mdsvex.ts
1933
1750
  var mdsvex_default = defineAddon({
1934
1751
  id: "mdsvex",
1935
1752
  shortDescription: "svelte + markdown",
@@ -1971,7 +1788,7 @@ var mdsvex_default = defineAddon({
1971
1788
  });
1972
1789
 
1973
1790
  //#endregion
1974
- //#region lib/addons/paraglide/index.ts
1791
+ //#region lib/addons/paraglide.ts
1975
1792
  const DEFAULT_INLANG_PROJECT = {
1976
1793
  $schema: "https://inlang.com/schema/project-settings",
1977
1794
  modules: ["https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js"],
@@ -2007,7 +1824,7 @@ var paraglide_default = defineAddon({
2007
1824
  run: ({ sv, options: options$8, files, language, kit }) => {
2008
1825
  if (!kit) throw new Error("SvelteKit is required");
2009
1826
  const paraglideOutDir = "src/lib/paraglide";
2010
- sv.devDependency("@inlang/paraglide-js", "^2.6.0");
1827
+ sv.devDependency("@inlang/paraglide-js", "^2.10.0");
2011
1828
  sv.file(files.viteConfig, (content) => {
2012
1829
  const { ast, generateCode } = parse$1.script(content);
2013
1830
  const vitePluginName = "paraglideVitePlugin";
@@ -2036,11 +1853,11 @@ var paraglide_default = defineAddon({
2036
1853
  if (createNamed(ast, {
2037
1854
  name: "reroute",
2038
1855
  fallback: rerouteIdentifier
2039
- }).declaration !== rerouteIdentifier) T.warn("Adding the reroute hook automatically failed. Add it manually");
1856
+ }).declaration !== rerouteIdentifier) R.warn("Adding the reroute hook automatically failed. Add it manually");
2040
1857
  return generateCode();
2041
1858
  });
2042
1859
  sv.file(`src/hooks.server.${language}`, (content) => {
2043
- const { ast, generateCode } = parse$1.script(content);
1860
+ const { ast, generateCode, comments } = parse$1.script(content);
2044
1861
  addNamed(ast, {
2045
1862
  from: "$lib/paraglide/server",
2046
1863
  imports: ["paraglideMiddleware"]
@@ -2053,7 +1870,8 @@ var paraglide_default = defineAddon({
2053
1870
  return resolve(event, {
2054
1871
  transformPageChunk: ({ html }) => html.replace('%paraglide.lang%', locale)
2055
1872
  });
2056
- });`
1873
+ });`,
1874
+ comments
2057
1875
  });
2058
1876
  return generateCode();
2059
1877
  });
@@ -2061,7 +1879,7 @@ var paraglide_default = defineAddon({
2061
1879
  const { ast, generateCode } = parse$1.html(content);
2062
1880
  const htmlNode = ast.nodes.find((child) => child.type === "RegularElement" && child.name === "html");
2063
1881
  if (!htmlNode) {
2064
- T.warn("Could not find <html> node in app.html. You'll need to add the language placeholder manually");
1882
+ R.warn("Could not find <html> node in app.html. You'll need to add the language placeholder manually");
2065
1883
  return generateCode();
2066
1884
  }
2067
1885
  addAttribute(htmlNode, "lang", "%paraglide.lang%");
@@ -2069,7 +1887,8 @@ var paraglide_default = defineAddon({
2069
1887
  });
2070
1888
  sv.file(files.gitignore, (content) => {
2071
1889
  if (!content) return content;
2072
- if (!content.includes(`\n${paraglideOutDir}`)) content = content.trimEnd() + `\n\n# Paraglide\n${paraglideOutDir}\nproject.inlang/cache/`;
1890
+ content = upsert(content, paraglideOutDir, { comment: "Paraglide" });
1891
+ content = upsert(content, "project.inlang/cache/");
2073
1892
  return content;
2074
1893
  });
2075
1894
  sv.file("project.inlang/settings.json", (content) => {
@@ -2151,14 +1970,14 @@ function parseLanguageTagInput(input) {
2151
1970
  }
2152
1971
 
2153
1972
  //#endregion
2154
- //#region lib/addons/playwright/index.ts
1973
+ //#region lib/addons/playwright.ts
2155
1974
  var playwright_default = defineAddon({
2156
1975
  id: "playwright",
2157
1976
  shortDescription: "browser testing",
2158
1977
  homepage: "https://playwright.dev",
2159
1978
  options: {},
2160
1979
  run: ({ sv, language, files }) => {
2161
- sv.devDependency("@playwright/test", "^1.57.0");
1980
+ sv.devDependency("@playwright/test", "^1.58.1");
2162
1981
  sv.file(files.package, (content) => {
2163
1982
  const { data, generateCode } = parse$1.json(content);
2164
1983
  packageScriptsUpsert(data, "test:e2e", "playwright test");
@@ -2167,8 +1986,7 @@ var playwright_default = defineAddon({
2167
1986
  });
2168
1987
  sv.file(files.gitignore, (content) => {
2169
1988
  if (!content) return content;
2170
- if (content.includes("test-results")) return content;
2171
- return "test-results\n" + content.trim();
1989
+ return upsert(content, "test-results", { comment: "Playwright" });
2172
1990
  });
2173
1991
  sv.file(`e2e/demo.test.${language}`, (content) => {
2174
1992
  if (content) return content;
@@ -2199,14 +2017,14 @@ var playwright_default = defineAddon({
2199
2017
  });
2200
2018
  overrideProperties(defaultExport.arguments[0], config);
2201
2019
  } else if (defaultExport.type === "ObjectExpression") overrideProperties(defaultExport, config);
2202
- else T.warn("Unexpected playwright config for playwright add-on. Could not update.");
2020
+ else R.warn("Unexpected playwright config for playwright add-on. Could not update.");
2203
2021
  return generateCode();
2204
2022
  });
2205
2023
  }
2206
2024
  });
2207
2025
 
2208
2026
  //#endregion
2209
- //#region lib/addons/prettier/index.ts
2027
+ //#region lib/addons/prettier.ts
2210
2028
  var prettier_default = defineAddon({
2211
2029
  id: "prettier",
2212
2030
  shortDescription: "formatter",
@@ -2215,8 +2033,8 @@ var prettier_default = defineAddon({
2215
2033
  run: ({ sv, dependencyVersion, files }) => {
2216
2034
  const tailwindcssInstalled = Boolean(dependencyVersion("tailwindcss"));
2217
2035
  if (tailwindcssInstalled) sv.devDependency("prettier-plugin-tailwindcss", "^0.7.2");
2218
- sv.devDependency("prettier", "^3.7.4");
2219
- sv.devDependency("prettier-plugin-svelte", "^3.4.0");
2036
+ sv.devDependency("prettier", "^3.8.1");
2037
+ sv.devDependency("prettier-plugin-svelte", "^3.4.1");
2220
2038
  sv.file(files.prettierignore, (content) => {
2221
2039
  if (content) return content;
2222
2040
  return dedent_default`
@@ -2236,7 +2054,7 @@ var prettier_default = defineAddon({
2236
2054
  try {
2237
2055
  ({data, generateCode} = parse$1.json(content));
2238
2056
  } catch {
2239
- T.warn(`A ${color.warning(".prettierrc")} config already exists and cannot be parsed as JSON. Skipping initialization.`);
2057
+ R.warn(`A ${color.warning(".prettierrc")} config already exists and cannot be parsed as JSON. Skipping initialization.`);
2240
2058
  return content;
2241
2059
  }
2242
2060
  if (Object.keys(data).length === 0) {
@@ -2262,12 +2080,11 @@ var prettier_default = defineAddon({
2262
2080
  const eslintInstalled = hasEslint(eslintVersion);
2263
2081
  sv.file(files.package, (content) => {
2264
2082
  const { data, generateCode } = parse$1.json(content);
2265
- const cmd = `prettier --check .${eslintInstalled ? ` && eslint .` : ""}`;
2266
- packageScriptsUpsert(data, "lint", cmd);
2083
+ packageScriptsUpsert(data, "lint", "prettier --check .", { mode: "prepend" });
2267
2084
  packageScriptsUpsert(data, "format", "prettier --write .");
2268
2085
  return generateCode();
2269
2086
  });
2270
- if (eslintVersion?.startsWith(SUPPORTED_ESLINT_VERSION) === false) T.warn(`An older major version of ${color.warning("eslint")} was detected. Skipping ${color.warning("eslint-config-prettier")} installation.`);
2087
+ if (eslintVersion?.startsWith(SUPPORTED_ESLINT_VERSION) === false) R.warn(`An older major version of ${color.warning("eslint")} was detected. Skipping ${color.warning("eslint-config-prettier")} installation.`);
2271
2088
  if (eslintInstalled) {
2272
2089
  sv.devDependency("eslint-config-prettier", "^10.1.8");
2273
2090
  sv.file(files.eslintConfig, addEslintConfigPrettier);
@@ -2280,7 +2097,7 @@ function hasEslint(version$1) {
2280
2097
  }
2281
2098
 
2282
2099
  //#endregion
2283
- //#region lib/addons/storybook/index.ts
2100
+ //#region lib/addons/storybook.ts
2284
2101
  var storybook_default = defineAddon({
2285
2102
  id: "storybook",
2286
2103
  shortDescription: "frontend workshop",
@@ -2303,7 +2120,7 @@ var storybook_default = defineAddon({
2303
2120
  });
2304
2121
 
2305
2122
  //#endregion
2306
- //#region lib/addons/sveltekit-adapter/index.ts
2123
+ //#region lib/addons/sveltekit-adapter.ts
2307
2124
  const adapters = [
2308
2125
  {
2309
2126
  id: "auto",
@@ -2313,7 +2130,7 @@ const adapters = [
2313
2130
  {
2314
2131
  id: "node",
2315
2132
  package: "@sveltejs/adapter-node",
2316
- version: "^5.4.0"
2133
+ version: "^5.5.2"
2317
2134
  },
2318
2135
  {
2319
2136
  id: "static",
@@ -2323,12 +2140,12 @@ const adapters = [
2323
2140
  {
2324
2141
  id: "vercel",
2325
2142
  package: "@sveltejs/adapter-vercel",
2326
- version: "^6.2.0"
2143
+ version: "^6.3.1"
2327
2144
  },
2328
2145
  {
2329
2146
  id: "cloudflare",
2330
2147
  package: "@sveltejs/adapter-cloudflare",
2331
- version: "^7.2.4"
2148
+ version: "^7.2.6"
2332
2149
  },
2333
2150
  {
2334
2151
  id: "netlify",
@@ -2410,7 +2227,7 @@ var sveltekit_adapter_default = defineAddon({
2410
2227
  return generateCode();
2411
2228
  });
2412
2229
  if (adapter.package === "@sveltejs/adapter-cloudflare") {
2413
- sv.devDependency("wrangler", "^4.56.0");
2230
+ sv.devDependency("wrangler", "^4.63.0");
2414
2231
  const configFormat = fileExists(cwd$1, "wrangler.toml") ? "toml" : "jsonc";
2415
2232
  sv.file(`wrangler.${configFormat}`, (content) => {
2416
2233
  const { data, generateCode } = configFormat === "jsonc" ? parse$1.json(content) : parse$1.toml(content);
@@ -2437,7 +2254,8 @@ var sveltekit_adapter_default = defineAddon({
2437
2254
  const jsconfig = fileExists(cwd$1, "jsconfig.json");
2438
2255
  if (language === "ts" || jsconfig) {
2439
2256
  sv.file(files.gitignore, (content) => {
2440
- return content.includes(".wrangler") && content.includes("worker-configuration.d.ts") ? content : `${content.trimEnd()}\n\n# Cloudflare Types\n/worker-configuration.d.ts`;
2257
+ if (content.length === 0) return content;
2258
+ return upsert(content, "/worker-configuration.d.ts", { comment: "Cloudflare Types" });
2441
2259
  });
2442
2260
  sv.file(files.package, (content) => {
2443
2261
  const { data, generateCode } = parse$1.json(content);
@@ -2493,7 +2311,7 @@ function createCloudflarePlatformType(name$1, value, optional$1 = false) {
2493
2311
  }
2494
2312
 
2495
2313
  //#endregion
2496
- //#region lib/addons/tailwindcss/index.ts
2314
+ //#region lib/addons/tailwindcss.ts
2497
2315
  const plugins = [{
2498
2316
  id: "typography",
2499
2317
  package: "@tailwindcss/typography",
@@ -2501,7 +2319,7 @@ const plugins = [{
2501
2319
  }, {
2502
2320
  id: "forms",
2503
2321
  package: "@tailwindcss/forms",
2504
- version: "^0.5.10"
2322
+ version: "^0.5.11"
2505
2323
  }];
2506
2324
  const options$2 = defineAddonOptions().add("plugins", {
2507
2325
  type: "multiselect",
@@ -2522,8 +2340,8 @@ var tailwindcss_default = defineAddon({
2522
2340
  options: options$2,
2523
2341
  run: ({ sv, options: options$8, files, kit, dependencyVersion, language }) => {
2524
2342
  const prettierInstalled = Boolean(dependencyVersion("prettier"));
2525
- sv.devDependency("tailwindcss", "^4.1.17");
2526
- sv.devDependency("@tailwindcss/vite", "^4.1.17");
2343
+ sv.devDependency("tailwindcss", "^4.1.18");
2344
+ sv.devDependency("@tailwindcss/vite", "^4.1.18");
2527
2345
  sv.pnpmBuildDependency("@tailwindcss/oxide");
2528
2346
  if (prettierInstalled) sv.devDependency("prettier-plugin-tailwindcss", "^0.7.2");
2529
2347
  for (const plugin of plugins) {
@@ -2606,7 +2424,7 @@ var tailwindcss_default = defineAddon({
2606
2424
  });
2607
2425
 
2608
2426
  //#endregion
2609
- //#region lib/addons/vitest-addon/index.ts
2427
+ //#region lib/addons/vitest-addon.ts
2610
2428
  const options$1 = defineAddonOptions().add("usages", {
2611
2429
  question: "What do you want to use vitest for?",
2612
2430
  type: "multiselect",
@@ -2630,16 +2448,16 @@ var vitest_addon_default = defineAddon({
2630
2448
  const unitTesting = options$8.usages.includes("unit");
2631
2449
  const componentTesting = options$8.usages.includes("component");
2632
2450
  vitestV3Installed = (dependencyVersion("vitest") ?? "").replaceAll("^", "").replaceAll("~", "")?.startsWith("3.");
2633
- sv.devDependency("vitest", "^4.0.15");
2451
+ sv.devDependency("vitest", "^4.0.18");
2634
2452
  if (componentTesting) {
2635
- sv.devDependency("@vitest/browser-playwright", "^4.0.15");
2636
- sv.devDependency("vitest-browser-svelte", "^2.0.1");
2637
- sv.devDependency("playwright", "^1.57.0");
2453
+ sv.devDependency("@vitest/browser-playwright", "^4.0.18");
2454
+ sv.devDependency("vitest-browser-svelte", "^2.0.2");
2455
+ sv.devDependency("playwright", "^1.58.1");
2638
2456
  }
2639
2457
  sv.file(files.package, (content) => {
2640
2458
  const { data, generateCode } = parse$1.json(content);
2641
2459
  packageScriptsUpsert(data, "test:unit", "vitest");
2642
- packageScriptsUpsert(data, "test", "npm run test:unit -- --run");
2460
+ packageScriptsUpsert(data, "test", "npm run test:unit -- --run", { mode: "prepend" });
2643
2461
  return generateCode();
2644
2462
  });
2645
2463
  if (unitTesting) sv.file(`src/demo.spec.${language}`, (content) => {
@@ -2754,7 +2572,7 @@ var vitest_addon_default = defineAddon({
2754
2572
  });
2755
2573
 
2756
2574
  //#endregion
2757
- //#region lib/addons/_config/official.ts
2575
+ //#region lib/addons/_engine/official.ts
2758
2576
  const officialAddons$1 = {
2759
2577
  prettier: prettier_default,
2760
2578
  eslint: eslint_default,
@@ -2764,7 +2582,7 @@ const officialAddons$1 = {
2764
2582
  sveltekitAdapter: sveltekit_adapter_default,
2765
2583
  devtoolsJson: devtools_json_default,
2766
2584
  drizzle: drizzle_default,
2767
- lucia: lucia_default,
2585
+ betterAuth: better_auth_default,
2768
2586
  mdsvex: mdsvex_default,
2769
2587
  paraglide: paraglide_default,
2770
2588
  storybook: storybook_default,
@@ -3005,7 +2823,7 @@ async function createWorkspace({ cwd: cwd$1, packageManager, override }) {
3005
2823
  const stylesheet = kit ? `${kit.routesDirectory}/layout.css` : "src/app.css";
3006
2824
  return {
3007
2825
  cwd: resolvedCwd,
3008
- packageManager: packageManager ?? (await detect({ cwd: cwd$1 }))?.name ?? getUserAgent() ?? "npm",
2826
+ packageManager: packageManager ?? await detectPackageManager(cwd$1),
3009
2827
  language: typescript ? "ts" : "js",
3010
2828
  files: {
3011
2829
  viteConfig,
@@ -3037,7 +2855,9 @@ function findWorkspaceRoot(cwd$1) {
3037
2855
  const { data } = getPackageJson(directory);
3038
2856
  if (data.workspaces) return directory;
3039
2857
  }
3040
- directory = path.dirname(directory);
2858
+ const parent = path.dirname(directory);
2859
+ if (directory.includes(".test-output") && !parent.includes(".test-output")) break;
2860
+ directory = parent;
3041
2861
  }
3042
2862
  return cwd$1;
3043
2863
  }
@@ -3083,7 +2903,7 @@ function parseKitOptions(cwd$1) {
3083
2903
  //#endregion
3084
2904
  //#region package.json
3085
2905
  var name = "sv";
3086
- var version = "0.11.3";
2906
+ var version = "0.12.0";
3087
2907
 
3088
2908
  //#endregion
3089
2909
  //#region lib/cli/utils/errors.ts
@@ -3098,7 +2918,6 @@ var UnsupportedError = class extends Error {
3098
2918
 
3099
2919
  //#endregion
3100
2920
  //#region lib/cli/utils/common.ts
3101
- var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
3102
2921
  const NO_PREFIX = "--no-";
3103
2922
  let options = [];
3104
2923
  function getLongFlag(flags) {
@@ -3129,37 +2948,33 @@ const helpConfig = {
3129
2948
  if (options.some((o) => getLongFlag(o.flags) === noVariant)) return `--[no-]${longFlag.slice(2)}`;
3130
2949
  return option.flags;
3131
2950
  },
3132
- styleTitle: (str) => import_picocolors.default.underline(str),
3133
- styleCommandText: (str) => import_picocolors.default.red(str),
3134
- styleDescriptionText: (str) => import_picocolors.default.gray(str),
3135
- styleOptionText: (str) => import_picocolors.default.white(str),
3136
- styleArgumentText: (str) => import_picocolors.default.white(str),
3137
- styleSubcommandText: (str) => import_picocolors.default.red(str)
2951
+ styleCommandText: (str) => color.success(str),
2952
+ styleDescriptionText: (str) => color.optional(str)
3138
2953
  };
3139
2954
  function formatDescription(arg) {
3140
2955
  let output = arg.description;
3141
- if (arg.defaultValue !== void 0 && String(arg.defaultValue)) output += import_picocolors.default.dim(` (default: ${JSON.stringify(arg.defaultValue)})`);
3142
- if (arg.argChoices !== void 0 && String(arg.argChoices)) output += import_picocolors.default.dim(` (choices: ${arg.argChoices.join(", ")})`);
2956
+ if (arg.defaultValue !== void 0 && String(arg.defaultValue)) output += color.dim(` (default: ${JSON.stringify(arg.defaultValue)})`);
2957
+ if (arg.argChoices !== void 0 && String(arg.argChoices)) output += color.dim(` (choices: ${arg.argChoices.join(", ")})`);
3143
2958
  return output;
3144
2959
  }
3145
2960
  async function runCommand(action) {
3146
2961
  try {
3147
- Ge(`Welcome to the Svelte CLI! ${import_picocolors.default.gray(`(v${version})`)}`);
2962
+ Nt(`Welcome to the Svelte CLI! ${color.optional(`(v${version})`)}`);
3148
2963
  const minimumVersion = "18.3.0";
3149
- if (isVersionUnsupportedBelow(process$1.versions.node, minimumVersion)) T.warn(`You are using Node.js ${import_picocolors.default.red(process$1.versions.node)}, please upgrade to Node.js ${import_picocolors.default.green(minimumVersion)} or higher.`);
2964
+ if (isVersionUnsupportedBelow(process$1.versions.node, minimumVersion)) R.warn(`You are using Node.js ${color.error(process$1.versions.node)}, please upgrade to Node.js ${color.success(minimumVersion)} or higher.`);
3150
2965
  await action();
3151
- Fe("You're all set!");
2966
+ Wt("You're all set!");
3152
2967
  } catch (e) {
3153
2968
  if (e instanceof UnsupportedError) {
3154
2969
  const padding = getPadding(e.reasons.map((r) => r.id));
3155
- const message = e.reasons.map((r) => ` ${r.id.padEnd(padding)} ${import_picocolors.default.red(r.reason)}`).join("\n");
3156
- T.error(`${e.name}\n\n${message}`);
3157
- T.message();
2970
+ const message = e.reasons.map((r) => ` ${r.id.padEnd(padding)} ${color.error(r.reason)}`).join("\n");
2971
+ R.error(`${e.name}\n\n${message}`);
2972
+ R.message();
3158
2973
  } else if (e instanceof Error) {
3159
- T.error(e.stack ?? String(e));
3160
- T.message();
2974
+ R.error(e.stack ?? String(e));
2975
+ R.message();
3161
2976
  }
3162
- De("Operation failed.");
2977
+ Pt("Operation failed.");
3163
2978
  }
3164
2979
  }
3165
2980
  function getPadding(lines) {
@@ -3190,7 +3005,8 @@ function buildAndLogArgs(agent, command, args, lastArgs = []) {
3190
3005
  else allArgs.push("--install", agent);
3191
3006
  const res = resolveCommand(agent ?? "npm", "execute", [...allArgs, ...lastArgs]);
3192
3007
  const message = [res.command, ...res.args].join(" ");
3193
- T.info(import_picocolors.default.dim(`Re-run without prompts:\n${message}`));
3008
+ R.message(color.optional(color.dim(`To skip prompts next time, run:`)));
3009
+ R.info(color.optional(message), { spacing: -1 });
3194
3010
  return message;
3195
3011
  }
3196
3012
  function updateReadme(projectPath, command) {
@@ -3211,9 +3027,9 @@ ${command}\n\`\`\`
3211
3027
  fs.writeFileSync(readmePath, content);
3212
3028
  }
3213
3029
  function errorAndExit(message) {
3214
- T.error(message);
3215
- T.message();
3216
- De("Operation failed.");
3030
+ R.error(message);
3031
+ R.message();
3032
+ Pt("Operation failed.");
3217
3033
  process$1.exit(1);
3218
3034
  }
3219
3035
  const normalizePosix = (dir) => {
@@ -3250,31 +3066,30 @@ const create = new Command("create").description("scaffolds a new SvelteKit proj
3250
3066
  const cwd$1 = parse(ProjectPathSchema, projectPath);
3251
3067
  const options$8 = parse(OptionsSchema$1, opts);
3252
3068
  if (options$8.fromPlayground && !validatePlaygroundUrl(options$8.fromPlayground)) {
3253
- console.error(import_picocolors.default.red(`Error: Invalid playground URL: ${options$8.fromPlayground}`));
3069
+ console.error(color.error(`Error: Invalid playground URL: ${options$8.fromPlayground}`));
3254
3070
  process$1.exit(1);
3255
3071
  }
3256
3072
  runCommand(async () => {
3257
3073
  const { directory, addOnNextSteps, packageManager } = await createProject(cwd$1, options$8);
3258
- const highlight = (str) => import_picocolors.default.bold(import_picocolors.default.cyan(str));
3259
3074
  let i = 1;
3260
3075
  const initialSteps = ["📁 Project steps", ""];
3261
3076
  const relative = path.relative(process$1.cwd(), directory);
3262
- const pm = packageManager ?? (await detect({ cwd: directory }))?.name ?? getUserAgent() ?? "npm";
3077
+ const pm = packageManager ?? await detectPackageManager(directory);
3263
3078
  if (relative !== "") {
3264
3079
  const pathHasSpaces = relative.includes(" ");
3265
- initialSteps.push(` ${i++}: ${highlight(`cd ${pathHasSpaces ? `"${relative}"` : relative}`)}`);
3080
+ initialSteps.push(` ${i++}: ${color.command(`cd ${pathHasSpaces ? `"${relative}"` : relative}`)}`);
3266
3081
  }
3267
3082
  if (!packageManager) {
3268
3083
  const { args: args$1, command: command$1 } = resolveCommand(pm, "install", []);
3269
- initialSteps.push(` ${i++}: ${highlight(`${command$1} ${args$1.join(" ")}`)}`);
3084
+ initialSteps.push(` ${i++}: ${color.command(`${command$1} ${args$1.join(" ")}`)}`);
3270
3085
  }
3271
3086
  const { args, command } = resolveCommand(pm, "run", ["dev", "--open"]);
3272
3087
  const pmRunCmd = `${command} ${args.join(" ")}`;
3273
3088
  const steps = [
3274
3089
  ...initialSteps,
3275
- ` ${i++}: ${highlight(pmRunCmd)}`,
3090
+ ` ${i++}: ${color.command(pmRunCmd)}`,
3276
3091
  "",
3277
- `To close the dev server, hit ${highlight("Ctrl-C")}`
3092
+ `To close the dev server, hit ${color.command("Ctrl-C")}`
3278
3093
  ];
3279
3094
  if (addOnNextSteps.length > 0) {
3280
3095
  steps.push("", "🧩 Add-on steps", "");
@@ -3283,17 +3098,17 @@ const create = new Command("create").description("scaffolds a new SvelteKit proj
3283
3098
  steps.push(` ${indented}`);
3284
3099
  }
3285
3100
  }
3286
- steps.push("", `Stuck? Visit us at ${import_picocolors.default.cyan("https://svelte.dev/chat")}`);
3287
- Ke(steps.join("\n"), "What's next?", { format: (line) => line });
3101
+ steps.push("", `Stuck? Visit us at ${color.website("https://svelte.dev/chat")}`);
3102
+ kt(steps.join("\n"), "What's next?", { format: (line) => line });
3288
3103
  });
3289
3104
  }).showHelpAfterError(true);
3290
3105
  async function createProject(cwd$1, options$8) {
3291
- if (options$8.fromPlayground) T.warn("Svelte maintainers have not reviewed playgrounds for malicious code. Use at your discretion.");
3292
- const { directory, template, language } = await We({
3106
+ if (options$8.fromPlayground) R.warn("Svelte maintainers have not reviewed playgrounds for malicious code. Use at your discretion.");
3107
+ const { directory, template, language } = await Rt({
3293
3108
  directory: () => {
3294
3109
  const defaultPath = "./";
3295
3110
  if (cwd$1) return Promise.resolve(normalizePosix(cwd$1));
3296
- return et({
3111
+ return Qt({
3297
3112
  message: "Where would you like your project to be created?",
3298
3113
  placeholder: ` (hit Enter to use '${defaultPath}')`,
3299
3114
  defaultValue: defaultPath
@@ -3303,12 +3118,12 @@ async function createProject(cwd$1, options$8) {
3303
3118
  if (!options$8.dirCheck) return;
3304
3119
  if (!fs.existsSync(directory$1)) return;
3305
3120
  if (!fs.readdirSync(directory$1).some((file) => !file.startsWith(".git"))) return;
3306
- const force = await ke({
3121
+ const force = await Mt({
3307
3122
  message: "Directory not empty. Continue?",
3308
3123
  initialValue: false
3309
3124
  });
3310
- if (Vu(force) || !force) {
3311
- De("Exiting.");
3125
+ if (Ct(force) || !force) {
3126
+ Pt("Exiting.");
3312
3127
  process$1.exit(0);
3313
3128
  }
3314
3129
  },
@@ -3316,7 +3131,7 @@ async function createProject(cwd$1, options$8) {
3316
3131
  if (options$8.template) return Promise.resolve(options$8.template);
3317
3132
  if (options$8.fromPlayground) return Promise.resolve("minimal");
3318
3133
  const availableTemplates = templates.filter((t) => t.name !== "addon");
3319
- return ze({
3134
+ return qt({
3320
3135
  message: "Which template would you like?",
3321
3136
  initialValue: "minimal",
3322
3137
  options: availableTemplates.map((t) => ({
@@ -3329,7 +3144,7 @@ async function createProject(cwd$1, options$8) {
3329
3144
  language: (o) => {
3330
3145
  if (options$8.types) return Promise.resolve(options$8.types);
3331
3146
  if (o.results.template === "addon") return Promise.resolve("none");
3332
- return ze({
3147
+ return qt({
3333
3148
  message: "Add type checking with TypeScript?",
3334
3149
  initialValue: "typescript",
3335
3150
  options: [
@@ -3349,7 +3164,7 @@ async function createProject(cwd$1, options$8) {
3349
3164
  });
3350
3165
  }
3351
3166
  }, { onCancel: () => {
3352
- De("Operation cancelled.");
3167
+ Pt("Operation cancelled.");
3353
3168
  process$1.exit(0);
3354
3169
  } });
3355
3170
  const projectPath = path.resolve(directory);
@@ -3389,12 +3204,12 @@ async function createProject(cwd$1, options$8) {
3389
3204
  types: language
3390
3205
  });
3391
3206
  if (options$8.fromPlayground) await createProjectFromPlayground(options$8.fromPlayground, projectPath);
3392
- T.success("Project created");
3393
- let addOnNextSteps = [];
3207
+ R.success("Project created");
3394
3208
  let argsFormattedAddons = [];
3395
3209
  let addOnFilesToFormat = [];
3210
+ let addOnSuccessfulAddons = [];
3396
3211
  if (template !== "addon" && (options$8.addOns || options$8.add.length > 0)) {
3397
- const { nextSteps, argsFormattedAddons: argsFormatted$1, filesToFormat } = await runAddonsApply({
3212
+ const { argsFormattedAddons: argsFormatted$1, filesToFormat, successfulAddons } = await runAddonsApply({
3398
3213
  answers,
3399
3214
  options: {
3400
3215
  cwd: projectPath,
@@ -3410,7 +3225,7 @@ async function createProject(cwd$1, options$8) {
3410
3225
  });
3411
3226
  argsFormattedAddons = argsFormatted$1;
3412
3227
  addOnFilesToFormat = filesToFormat;
3413
- addOnNextSteps = nextSteps;
3228
+ addOnSuccessfulAddons = successfulAddons;
3414
3229
  }
3415
3230
  const packageManager = options$8.install === false ? null : options$8.install === true ? await packageManagerPrompt(projectPath) : options$8.install;
3416
3231
  const argsFormatted = [];
@@ -3421,6 +3236,8 @@ async function createProject(cwd$1, options$8) {
3421
3236
  if (argsFormattedAddons.length > 0) argsFormatted.push("--add", ...argsFormattedAddons);
3422
3237
  const prompt = buildAndLogArgs(packageManager, "create", argsFormatted, [directory]);
3423
3238
  updateReadme(directory, prompt);
3239
+ if (packageManager) workspace.packageManager = packageManager;
3240
+ const addOnNextSteps = getNextSteps(addOnSuccessfulAddons, workspace, answers);
3424
3241
  await addPnpmBuildDependencies(projectPath, packageManager, ["esbuild"]);
3425
3242
  if (packageManager) {
3426
3243
  await installDependencies(packageManager, projectPath);
@@ -3443,14 +3260,14 @@ async function createProjectFromPlayground(url, cwd$1) {
3443
3260
  }
3444
3261
  async function confirmExternalDependencies(dependencies) {
3445
3262
  if (dependencies.length === 0) return false;
3446
- const dependencyList = dependencies.map(import_picocolors.default.yellowBright).join(", ");
3447
- T.warn(`The following external dependencies were found in the playground:\n\n${dependencyList}`);
3448
- const installDeps = await ke({
3263
+ const dependencyList = dependencies.map(color.warning).join(", ");
3264
+ R.warn(`The following external dependencies were found in the playground:\n\n${dependencyList}`);
3265
+ const installDeps = await Mt({
3449
3266
  message: "Do you want to install these external dependencies?",
3450
3267
  initialValue: false
3451
3268
  });
3452
- if (Vu(installDeps)) {
3453
- De("Operation cancelled.");
3269
+ if (Ct(installDeps)) {
3270
+ Pt("Operation cancelled.");
3454
3271
  process$1.exit(0);
3455
3272
  }
3456
3273
  return installDeps;
@@ -6381,7 +6198,7 @@ const add$1 = new Command("add").description("applies specified add-ons into a p
6381
6198
  workspace,
6382
6199
  fromCommand: "add"
6383
6200
  });
6384
- if (nextSteps.length > 0) Ke(nextSteps.join("\n"), "Next steps", { format: (line) => line });
6201
+ if (nextSteps.length > 0) kt(nextSteps.join("\n"), "Next steps", { format: (line) => line });
6385
6202
  });
6386
6203
  });
6387
6204
  /**
@@ -6477,18 +6294,18 @@ async function promptAddonQuestions({ options: options$8, loadedAddons, workspac
6477
6294
  if (addons.length > 0) setupResults = setupAddons(addons, workspace);
6478
6295
  if (addons.length === 0) {
6479
6296
  const results = setupAddons(officialAddons.map((a) => createLoadedAddon(a)), workspace);
6480
- const addonOptions$1 = officialAddons.filter(({ id }) => results[id].unsupported.length === 0).map(({ id, homepage, shortDescription }) => ({
6297
+ const addonOptions$1 = officialAddons.filter(({ id, hidden }) => results[id].unsupported.length === 0 && !hidden).map(({ id, homepage, shortDescription }) => ({
6481
6298
  label: id,
6482
6299
  value: id,
6483
6300
  hint: `${shortDescription} - ${homepage}`
6484
6301
  }));
6485
- const selected = await Ue({
6486
- message: `What would you like to add to your project? ${import_picocolors.default.dim("(use arrow keys / space bar)")}`,
6302
+ const selected = await Lt({
6303
+ message: `What would you like to add to your project? ${color.dim("(use arrow keys / space bar)")}`,
6487
6304
  options: addonOptions$1,
6488
6305
  required: false
6489
6306
  });
6490
- if (Vu(selected)) {
6491
- De("Operation cancelled.");
6307
+ if (Ct(selected)) {
6308
+ Pt("Operation cancelled.");
6492
6309
  process$1.exit(1);
6493
6310
  }
6494
6311
  for (const id of selected) {
@@ -6534,8 +6351,8 @@ async function promptAddonQuestions({ options: options$8, loadedAddons, workspac
6534
6351
  answers[depId] = {};
6535
6352
  continue;
6536
6353
  }
6537
- if (await ke({ message: `The ${import_picocolors.default.bold(import_picocolors.default.cyan(addonId))} add-on requires ${import_picocolors.default.bold(import_picocolors.default.cyan(depId))} to also be setup. ${import_picocolors.default.green("Include it?")}` }) !== true) {
6538
- De("Operation cancelled.");
6354
+ if (await Mt({ message: `The ${color.addon(addonId)} add-on requires ${color.addon(depId)} to also be setup. ${color.success("Include it?")}` }) !== true) {
6355
+ Pt("Operation cancelled.");
6539
6356
  process$1.exit(1);
6540
6357
  }
6541
6358
  answers[depId] = {};
@@ -6558,14 +6375,14 @@ async function promptAddonQuestions({ options: options$8, loadedAddons, workspac
6558
6375
  });
6559
6376
  }
6560
6377
  if (fails.length > 0) {
6561
- const message = fails.map(({ name: name$1, message: message$1 }) => import_picocolors.default.yellow(`${name$1} (${message$1})`)).join("\n- ");
6562
- Ke(`- ${message}`, "Verifications not met", { format: (line) => line });
6563
- const force = await ke({
6378
+ const message = fails.map(({ name: name$1, message: message$1 }) => color.warning(`${name$1} (${message$1})`)).join("\n- ");
6379
+ kt(`- ${message}`, "Verifications not met", { format: (line) => line });
6380
+ const force = await Mt({
6564
6381
  message: "Verifications failed. Do you wish to continue?",
6565
6382
  initialValue: false
6566
6383
  });
6567
- if (Vu(force) || !force) {
6568
- De("Operation cancelled.");
6384
+ if (Ct(force) || !force) {
6385
+ Pt("Operation cancelled.");
6569
6386
  process$1.exit(1);
6570
6387
  }
6571
6388
  }
@@ -6579,23 +6396,23 @@ async function promptAddonQuestions({ options: options$8, loadedAddons, workspac
6579
6396
  if (question.condition?.(values) === false || values[questionId] !== void 0) continue;
6580
6397
  let answer;
6581
6398
  const message = questionPrefix + question.question;
6582
- if (question.type === "boolean") answer = await ke({
6399
+ if (question.type === "boolean") answer = await Mt({
6583
6400
  message,
6584
6401
  initialValue: question.default
6585
6402
  });
6586
- if (question.type === "select") answer = await ze({
6403
+ if (question.type === "select") answer = await qt({
6587
6404
  message,
6588
6405
  initialValue: question.default,
6589
6406
  options: question.options
6590
6407
  });
6591
- if (question.type === "multiselect") answer = await Ue({
6408
+ if (question.type === "multiselect") answer = await Lt({
6592
6409
  message,
6593
6410
  initialValues: question.default,
6594
6411
  required: question.required,
6595
6412
  options: question.options
6596
6413
  });
6597
6414
  if (question.type === "string" || question.type === "number") {
6598
- answer = await et({
6415
+ answer = await Qt({
6599
6416
  message,
6600
6417
  initialValue: question.default?.toString() ?? (question.type === "number" ? "0" : ""),
6601
6418
  placeholder: question.placeholder,
@@ -6603,8 +6420,8 @@ async function promptAddonQuestions({ options: options$8, loadedAddons, workspac
6603
6420
  });
6604
6421
  if (question.type === "number") answer = Number(answer);
6605
6422
  }
6606
- if (Vu(answer)) {
6607
- De("Operation cancelled.");
6423
+ if (Ct(answer)) {
6424
+ Pt("Operation cancelled.");
6608
6425
  process$1.exit(1);
6609
6426
  }
6610
6427
  values[questionId] = answer;
@@ -6620,7 +6437,8 @@ async function runAddonsApply({ answers, options: options$8, loadedAddons, setup
6620
6437
  if (loadedAddons.length === 0) return {
6621
6438
  nextSteps: [],
6622
6439
  argsFormattedAddons: [],
6623
- filesToFormat: []
6440
+ filesToFormat: [],
6441
+ successfulAddons: []
6624
6442
  };
6625
6443
  const { filesToFormat, pnpmBuildDependencies, status } = await applyAddons({
6626
6444
  loadedAddons,
@@ -6632,14 +6450,14 @@ async function runAddonsApply({ answers, options: options$8, loadedAddons, setup
6632
6450
  const canceledAddonIds = [];
6633
6451
  for (const [addonId, info] of Object.entries(status)) if (info === "success") addonSuccess.push(addonId);
6634
6452
  else {
6635
- T.warn(`Canceled ${addonId}: ${info.join(", ")}`);
6453
+ R.warn(`Canceled ${addonId}: ${info.join(", ")}`);
6636
6454
  canceledAddonIds.push(addonId);
6637
6455
  }
6638
6456
  const successfulAddons = loadedAddons.filter((a) => !canceledAddonIds.includes(a.addon.id));
6639
6457
  if (addonSuccess.length === 0) {
6640
- De("All selected add-ons were canceled.");
6458
+ Pt("All selected add-ons were canceled.");
6641
6459
  process$1.exit(1);
6642
- } else T.success(`Successfully setup add-ons: ${addonSuccess.map((c) => color.addon(c)).join(", ")}`);
6460
+ } else R.success(`Successfully setup add-ons: ${addonSuccess.map((c) => color.addon(c)).join(", ")}`);
6643
6461
  const packageManager = options$8.install === false ? null : options$8.install === true ? await packageManagerPrompt(options$8.cwd) : options$8.install;
6644
6462
  await addPnpmBuildDependencies(workspace.cwd, packageManager, ["esbuild", ...pnpmBuildDependencies]);
6645
6463
  const argsFormattedAddons = [];
@@ -6681,23 +6499,27 @@ async function runAddonsApply({ answers, options: options$8, loadedAddons, setup
6681
6499
  });
6682
6500
  }
6683
6501
  return {
6684
- nextSteps: successfulAddons.map((loaded) => {
6685
- const addon = loaded.addon;
6686
- if (!addon.nextSteps) return;
6687
- const addonOptions$1 = answers[addon.id];
6688
- const addonNextSteps = addon.nextSteps({
6689
- ...workspace,
6690
- options: addonOptions$1
6691
- });
6692
- if (addonNextSteps.length === 0) return;
6693
- let addonMessage = `${import_picocolors.default.green(addon.id)}:\n`;
6694
- addonMessage += ` - ${addonNextSteps.join("\n - ")}`;
6695
- return addonMessage;
6696
- }).filter((msg) => msg !== void 0),
6502
+ nextSteps: getNextSteps(successfulAddons, workspace, answers),
6697
6503
  argsFormattedAddons,
6698
- filesToFormat
6504
+ filesToFormat,
6505
+ successfulAddons
6699
6506
  };
6700
6507
  }
6508
+ function getNextSteps(loadedAddons, workspace, answers) {
6509
+ return loadedAddons.map((loaded) => {
6510
+ const addon = loaded.addon;
6511
+ if (!addon.nextSteps) return;
6512
+ const addonOptions$1 = answers[addon.id];
6513
+ const addonNextSteps = addon.nextSteps({
6514
+ ...workspace,
6515
+ options: addonOptions$1
6516
+ });
6517
+ if (addonNextSteps.length === 0) return;
6518
+ let addonMessage = `${color.addon(addon.id)}:\n`;
6519
+ addonMessage += ` - ${addonNextSteps.join("\n - ")}`;
6520
+ return addonMessage;
6521
+ }).filter((msg) => msg !== void 0);
6522
+ }
6701
6523
  /**
6702
6524
  * Handles passed add-on arguments, accumulating them into an array of AddonInput.
6703
6525
  */
@@ -6724,7 +6546,7 @@ function getAddonOptionFlags() {
6724
6546
  const details = getAddonDetails(id);
6725
6547
  if (Object.values(details.options).length === 0) continue;
6726
6548
  const { defaults, groups } = getOptionChoices(details);
6727
- const choices = Object.entries(groups).map(([group, choices$1]) => `${import_picocolors.default.dim(`${group}:`)} ${choices$1.join(", ")}`).join("\n");
6549
+ const choices = Object.entries(groups).map(([group, choices$1]) => `${color.optional(`${group}:`)} ${color.dim(choices$1.join(", "))}`).join("\n");
6728
6550
  const preset = defaults.join(", ") || "none";
6729
6551
  options$8.push({
6730
6552
  id,
@@ -6783,7 +6605,7 @@ function getOptionChoices(details) {
6783
6605
  }
6784
6606
  async function resolveNonOfficialAddons(refs, downloadCheck) {
6785
6607
  const selectedAddons = [];
6786
- const { start, stop } = J();
6608
+ const { start, stop } = Ie();
6787
6609
  try {
6788
6610
  start(`Resolving ${refs.map((r) => color.addon(r.specifier)).join(", ")} packages`);
6789
6611
  const pkgs = await Promise.all(refs.map(async (ref) => {
@@ -6791,17 +6613,17 @@ async function resolveNonOfficialAddons(refs, downloadCheck) {
6791
6613
  return await getPackageJSON(ref);
6792
6614
  }));
6793
6615
  stop("Resolved community add-on packages");
6794
- for (const { warning } of pkgs) if (warning) T.warn(warning);
6795
- T.warn("Svelte maintainers have not reviewed community add-ons for malicious code. Use at your discretion.");
6616
+ for (const { warning } of pkgs) if (warning) R.warn(warning);
6617
+ R.warn("Svelte maintainers have not reviewed community add-ons for malicious code. Use at your discretion.");
6796
6618
  const paddingName = getPadding(pkgs.map(({ pkg }) => pkg.name));
6797
6619
  const paddingVersion = getPadding(pkgs.map(({ pkg }) => `(v${pkg.version})`));
6798
6620
  const packageInfos = pkgs.map(({ pkg, repo: _repo }) => {
6799
- return `${import_picocolors.default.yellowBright(pkg.name.padEnd(paddingName))} ${import_picocolors.default.dim(`(v${pkg.version})`.padEnd(paddingVersion))} ${import_picocolors.default.dim(`(${_repo})`)}`;
6621
+ return `${color.warning(pkg.name.padEnd(paddingName))} ${color.dim(`(v${pkg.version})`.padEnd(paddingVersion))} ${color.dim(`(${_repo})`)}`;
6800
6622
  });
6801
- T.message(packageInfos.join("\n"));
6623
+ R.message(packageInfos.join("\n"));
6802
6624
  if (downloadCheck) {
6803
- if (await ke({ message: "Would you like to continue?" }) !== true) {
6804
- De("Operation cancelled.");
6625
+ if (await Mt({ message: "Would you like to continue?" }) !== true) {
6626
+ Pt("Operation cancelled.");
6805
6627
  process$1.exit(1);
6806
6628
  }
6807
6629
  }
@@ -6824,7 +6646,7 @@ async function resolveNonOfficialAddons(refs, downloadCheck) {
6824
6646
  }
6825
6647
  stop("Downloaded community add-on packages");
6826
6648
  } catch (err) {
6827
- stop("Failed to download community add-on packages", 1);
6649
+ stop("Failed to download community add-on packages");
6828
6650
  const msg = err instanceof Error ? err.message : "Unknown error";
6829
6651
  errorAndExit(msg);
6830
6652
  }
@@ -6836,7 +6658,7 @@ async function resolveNonOfficialAddons(refs, downloadCheck) {
6836
6658
  const TESTING = process$1.env.NODE_ENV?.toLowerCase() === "test";
6837
6659
 
6838
6660
  //#endregion
6839
- //#region lib/addons/add.ts
6661
+ //#region lib/addons/_engine/add.ts
6840
6662
  async function add({ addons, cwd: cwd$1, options: options$8, packageManager = "npm" }) {
6841
6663
  const workspace = await createWorkspace({
6842
6664
  cwd: cwd$1,
@@ -6933,7 +6755,7 @@ async function runAddon({ addon, loaded, multiple, workspace, workspaceOptions }
6933
6755
  const { command, args } = resolveCommand(workspace.packageManager, "execute", commandArgs);
6934
6756
  const addonPrefix = multiple ? `${addon.id}: ` : "";
6935
6757
  const executedCommand = `${command} ${args.join(" ")}`;
6936
- if (!TESTING) T.step(`${addonPrefix}Running external command ${import_picocolors.default.gray(`(${executedCommand})`)}`);
6758
+ if (!TESTING) R.step(`${addonPrefix}Running external command ${color.optional(`(${executedCommand})`)}`);
6937
6759
  if (workspace.packageManager === "npm") args.unshift("--yes");
6938
6760
  try {
6939
6761
  await q(command, args, {