sv 0.11.4 → 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 { 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-XDkWi9-7.mjs";
3
- import { $ as getNodeTypesVersion, A as createCall, B as hasTypeProperty, C as declaration, Ct as qt, D as addNamespace, E as addNamed, 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 kt, T as addEmpty, Tt as Ct, U as addFragment, V as parseExpression, W as addSlot, X as createPrinter, Z as addEslintConfigPrettier, _ as getConfig, _t as Qt, a as getPackageJson, at as walk, b as createDefault, bt as Se, c as writeFile, et as isVersionUnsupportedBelow, f as packageScriptsUpsert, ft as resolveCommand, g as addPlugin, gt as Pt, ht as Nt, i as formatFiles, it as getErrorHint, j as create$2, k as remove, l as parse$1, lt as q, mt as Mt, n as commonFilePaths, nt as defineAddon, o as installPackages, p as addAttribute, pt as Lt, 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 R, w as addDefault, x as createNamed, xt as Wt, y as addHooksHandle, yt as Rt, z as createSpread } from "./utils-IP07VUBl.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";
@@ -609,6 +610,430 @@ function pipe(...pipe$1) {
609
610
  };
610
611
  }
611
612
 
613
+ //#endregion
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
+
612
1037
  //#endregion
613
1038
  //#region lib/addons/devtools-json.ts
614
1039
  var devtools_json_default = defineAddon({
@@ -638,7 +1063,7 @@ const PORTS = {
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");
@@ -735,7 +1160,7 @@ var drizzle_default = defineAddon({
735
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.16.1");
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
1166
  if (options$8.postgresql === "postgres.js") sv.dependency("postgres", "^3.4.8");
@@ -745,8 +1170,8 @@ var drizzle_default = defineAddon({
745
1170
  sv.pnpmBuildDependency("better-sqlite3");
746
1171
  }
747
1172
  if (options$8.sqlite === "libsql" || options$8.sqlite === "turso") sv.devDependency("@libsql/client", "^0.17.0");
748
- sv.file(".env", (content) => generateEnvFileContent(content, options$8));
749
- sv.file(".env.example", (content) => generateEnvFileContent(content, options$8));
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,42 +1433,36 @@ 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
1468
  //#region lib/addons/eslint.ts
@@ -1058,12 +1475,12 @@ var eslint_default = defineAddon({
1058
1475
  const typescript = language === "ts";
1059
1476
  const prettierInstalled = Boolean(dependencyVersion("prettier"));
1060
1477
  sv.devDependency("eslint", "^9.39.2");
1061
- sv.devDependency("@eslint/compat", "^2.0.1");
1478
+ sv.devDependency("@eslint/compat", "^2.0.2");
1062
1479
  sv.devDependency("eslint-plugin-svelte", "^3.14.0");
1063
- sv.devDependency("globals", "^17.1.0");
1480
+ sv.devDependency("globals", "^17.3.0");
1064
1481
  sv.devDependency("@eslint/js", "^9.39.2");
1065
1482
  sv.devDependency("@types/node", getNodeTypesVersion());
1066
- if (typescript) sv.devDependency("typescript-eslint", "^8.53.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);
@@ -1183,606 +1600,6 @@ var eslint_default = defineAddon({
1183
1600
  }
1184
1601
  });
1185
1602
 
1186
- //#endregion
1187
- //#region lib/addons/lucia.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
- R.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
- R.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
- R.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
- R.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
1603
  //#endregion
1787
1604
  //#region lib/addons/mcp.ts
1788
1605
  const options$5 = defineAddonOptions().add("ide", {
@@ -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.9.1");
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";
@@ -2040,7 +1857,7 @@ var paraglide_default = defineAddon({
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
  });
@@ -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) => {
@@ -2158,7 +1977,7 @@ var playwright_default = defineAddon({
2158
1977
  homepage: "https://playwright.dev",
2159
1978
  options: {},
2160
1979
  run: ({ sv, language, files }) => {
2161
- sv.devDependency("@playwright/test", "^1.58.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;
@@ -2409,7 +2227,7 @@ var sveltekit_adapter_default = defineAddon({
2409
2227
  return generateCode();
2410
2228
  });
2411
2229
  if (adapter.package === "@sveltejs/adapter-cloudflare") {
2412
- sv.devDependency("wrangler", "^4.60.0");
2230
+ sv.devDependency("wrangler", "^4.63.0");
2413
2231
  const configFormat = fileExists(cwd$1, "wrangler.toml") ? "toml" : "jsonc";
2414
2232
  sv.file(`wrangler.${configFormat}`, (content) => {
2415
2233
  const { data, generateCode } = configFormat === "jsonc" ? parse$1.json(content) : parse$1.toml(content);
@@ -2436,7 +2254,8 @@ var sveltekit_adapter_default = defineAddon({
2436
2254
  const jsconfig = fileExists(cwd$1, "jsconfig.json");
2437
2255
  if (language === "ts" || jsconfig) {
2438
2256
  sv.file(files.gitignore, (content) => {
2439
- 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" });
2440
2259
  });
2441
2260
  sv.file(files.package, (content) => {
2442
2261
  const { data, generateCode } = parse$1.json(content);
@@ -2633,7 +2452,7 @@ var vitest_addon_default = defineAddon({
2633
2452
  if (componentTesting) {
2634
2453
  sv.devDependency("@vitest/browser-playwright", "^4.0.18");
2635
2454
  sv.devDependency("vitest-browser-svelte", "^2.0.2");
2636
- sv.devDependency("playwright", "^1.58.0");
2455
+ sv.devDependency("playwright", "^1.58.1");
2637
2456
  }
2638
2457
  sv.file(files.package, (content) => {
2639
2458
  const { data, generateCode } = parse$1.json(content);
@@ -2763,7 +2582,7 @@ const officialAddons$1 = {
2763
2582
  sveltekitAdapter: sveltekit_adapter_default,
2764
2583
  devtoolsJson: devtools_json_default,
2765
2584
  drizzle: drizzle_default,
2766
- lucia: lucia_default,
2585
+ betterAuth: better_auth_default,
2767
2586
  mdsvex: mdsvex_default,
2768
2587
  paraglide: paraglide_default,
2769
2588
  storybook: storybook_default,
@@ -3004,7 +2823,7 @@ async function createWorkspace({ cwd: cwd$1, packageManager, override }) {
3004
2823
  const stylesheet = kit ? `${kit.routesDirectory}/layout.css` : "src/app.css";
3005
2824
  return {
3006
2825
  cwd: resolvedCwd,
3007
- packageManager: packageManager ?? (await detect({ cwd: cwd$1 }))?.name ?? getUserAgent() ?? "npm",
2826
+ packageManager: packageManager ?? await detectPackageManager(cwd$1),
3008
2827
  language: typescript ? "ts" : "js",
3009
2828
  files: {
3010
2829
  viteConfig,
@@ -3084,7 +2903,7 @@ function parseKitOptions(cwd$1) {
3084
2903
  //#endregion
3085
2904
  //#region package.json
3086
2905
  var name = "sv";
3087
- var version = "0.11.4";
2906
+ var version = "0.12.0";
3088
2907
 
3089
2908
  //#endregion
3090
2909
  //#region lib/cli/utils/errors.ts
@@ -3255,7 +3074,7 @@ const create = new Command("create").description("scaffolds a new SvelteKit proj
3255
3074
  let i = 1;
3256
3075
  const initialSteps = ["📁 Project steps", ""];
3257
3076
  const relative = path.relative(process$1.cwd(), directory);
3258
- const pm = packageManager ?? (await detect({ cwd: directory }))?.name ?? getUserAgent() ?? "npm";
3077
+ const pm = packageManager ?? await detectPackageManager(directory);
3259
3078
  if (relative !== "") {
3260
3079
  const pathHasSpaces = relative.includes(" ");
3261
3080
  initialSteps.push(` ${i++}: ${color.command(`cd ${pathHasSpaces ? `"${relative}"` : relative}`)}`);
@@ -3386,11 +3205,11 @@ async function createProject(cwd$1, options$8) {
3386
3205
  });
3387
3206
  if (options$8.fromPlayground) await createProjectFromPlayground(options$8.fromPlayground, projectPath);
3388
3207
  R.success("Project created");
3389
- let addOnNextSteps = [];
3390
3208
  let argsFormattedAddons = [];
3391
3209
  let addOnFilesToFormat = [];
3210
+ let addOnSuccessfulAddons = [];
3392
3211
  if (template !== "addon" && (options$8.addOns || options$8.add.length > 0)) {
3393
- const { nextSteps, argsFormattedAddons: argsFormatted$1, filesToFormat } = await runAddonsApply({
3212
+ const { argsFormattedAddons: argsFormatted$1, filesToFormat, successfulAddons } = await runAddonsApply({
3394
3213
  answers,
3395
3214
  options: {
3396
3215
  cwd: projectPath,
@@ -3406,7 +3225,7 @@ async function createProject(cwd$1, options$8) {
3406
3225
  });
3407
3226
  argsFormattedAddons = argsFormatted$1;
3408
3227
  addOnFilesToFormat = filesToFormat;
3409
- addOnNextSteps = nextSteps;
3228
+ addOnSuccessfulAddons = successfulAddons;
3410
3229
  }
3411
3230
  const packageManager = options$8.install === false ? null : options$8.install === true ? await packageManagerPrompt(projectPath) : options$8.install;
3412
3231
  const argsFormatted = [];
@@ -3417,6 +3236,8 @@ async function createProject(cwd$1, options$8) {
3417
3236
  if (argsFormattedAddons.length > 0) argsFormatted.push("--add", ...argsFormattedAddons);
3418
3237
  const prompt = buildAndLogArgs(packageManager, "create", argsFormatted, [directory]);
3419
3238
  updateReadme(directory, prompt);
3239
+ if (packageManager) workspace.packageManager = packageManager;
3240
+ const addOnNextSteps = getNextSteps(addOnSuccessfulAddons, workspace, answers);
3420
3241
  await addPnpmBuildDependencies(projectPath, packageManager, ["esbuild"]);
3421
3242
  if (packageManager) {
3422
3243
  await installDependencies(packageManager, projectPath);
@@ -6473,7 +6294,7 @@ async function promptAddonQuestions({ options: options$8, loadedAddons, workspac
6473
6294
  if (addons.length > 0) setupResults = setupAddons(addons, workspace);
6474
6295
  if (addons.length === 0) {
6475
6296
  const results = setupAddons(officialAddons.map((a) => createLoadedAddon(a)), workspace);
6476
- 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 }) => ({
6477
6298
  label: id,
6478
6299
  value: id,
6479
6300
  hint: `${shortDescription} - ${homepage}`
@@ -6616,7 +6437,8 @@ async function runAddonsApply({ answers, options: options$8, loadedAddons, setup
6616
6437
  if (loadedAddons.length === 0) return {
6617
6438
  nextSteps: [],
6618
6439
  argsFormattedAddons: [],
6619
- filesToFormat: []
6440
+ filesToFormat: [],
6441
+ successfulAddons: []
6620
6442
  };
6621
6443
  const { filesToFormat, pnpmBuildDependencies, status } = await applyAddons({
6622
6444
  loadedAddons,
@@ -6677,23 +6499,27 @@ async function runAddonsApply({ answers, options: options$8, loadedAddons, setup
6677
6499
  });
6678
6500
  }
6679
6501
  return {
6680
- nextSteps: successfulAddons.map((loaded) => {
6681
- const addon = loaded.addon;
6682
- if (!addon.nextSteps) return;
6683
- const addonOptions$1 = answers[addon.id];
6684
- const addonNextSteps = addon.nextSteps({
6685
- ...workspace,
6686
- options: addonOptions$1
6687
- });
6688
- if (addonNextSteps.length === 0) return;
6689
- let addonMessage = `${color.addon(addon.id)}:\n`;
6690
- addonMessage += ` - ${addonNextSteps.join("\n - ")}`;
6691
- return addonMessage;
6692
- }).filter((msg) => msg !== void 0),
6502
+ nextSteps: getNextSteps(successfulAddons, workspace, answers),
6693
6503
  argsFormattedAddons,
6694
- filesToFormat
6504
+ filesToFormat,
6505
+ successfulAddons
6695
6506
  };
6696
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
+ }
6697
6523
  /**
6698
6524
  * Handles passed add-on arguments, accumulating them into an array of AddonInput.
6699
6525
  */
@@ -6779,7 +6605,7 @@ function getOptionChoices(details) {
6779
6605
  }
6780
6606
  async function resolveNonOfficialAddons(refs, downloadCheck) {
6781
6607
  const selectedAddons = [];
6782
- const { start, stop } = Se();
6608
+ const { start, stop } = Ie();
6783
6609
  try {
6784
6610
  start(`Resolving ${refs.map((r) => color.addon(r.specifier)).join(", ")} packages`);
6785
6611
  const pkgs = await Promise.all(refs.map(async (ref) => {