create-cronus-stack 0.6.1 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/scaffold.js CHANGED
@@ -148,7 +148,7 @@ function packageJson(projectName, config) {
148
148
  add(devDeps, "drizzle-kit", "^0.31.10");
149
149
  switch (single(config, "database")) {
150
150
  case "db-sqlite":
151
- add(deps, "better-sqlite3", "^13.0.3");
151
+ add(deps, "better-sqlite3", "^12.0.0");
152
152
  add(devDeps, "@types/better-sqlite3", "^9.6.0");
153
153
  break;
154
154
  case "db-postgres":
@@ -311,19 +311,95 @@ export default function RootLayout({ children }: { children: ReactNode }) {
311
311
  }
312
312
  `;
313
313
  }
314
- function pageTsx() {
314
+ function fromAppImport(config, target) {
315
+ return usesImportAlias(config) ? `@/${target}` : `../${target}`;
316
+ }
317
+ function pageStatusCopy(config) {
318
+ if (emitsBetterAuth(config) && emitsDrizzle(config)) {
319
+ return ` {session ? (
320
+ <p className="max-w-prose text-fg-secondary">
321
+ Signed in as {session.user?.email}. {itemCount} items in the active workspace.
322
+ </p>
323
+ ) : (
324
+ <p className="max-w-prose text-fg-secondary">
325
+ Sign in required on this stack. Middleware redirects unsigned-in visitors to /login.
326
+ </p>
327
+ )}`;
328
+ }
329
+ if (emitsDrizzle(config)) {
330
+ return ` <p className="max-w-prose text-fg-secondary">
331
+ {itemCount} items in the database. Read KICKOFF.md before changing frameworks,
332
+ databases, auth, payments, or design-system rules.
333
+ </p>`;
334
+ }
335
+ return ` <p className="max-w-prose text-fg-secondary">
336
+ This app was scaffolded from the Cronus Stack Builder. Read KICKOFF.md before changing
337
+ frameworks, databases, auth, payments, or design-system rules.
338
+ </p>`;
339
+ }
340
+ function pageTsx(config) {
341
+ const withDrizzle = emitsDrizzle(config);
342
+ const withAuth = emitsBetterAuth(config);
343
+ const extraImports = [
344
+ withDrizzle && withAuth
345
+ ? 'import { count, eq } from "drizzle-orm";'
346
+ : withDrizzle
347
+ ? 'import { count } from "drizzle-orm";'
348
+ : undefined,
349
+ withAuth ? 'import { headers } from "next/headers";' : undefined,
350
+ withDrizzle ? `import { db } from "${fromAppImport(config, "db")}";` : undefined,
351
+ withDrizzle && withAuth
352
+ ? `import { items, member } from "${fromAppImport(config, "db/schema")}";`
353
+ : withDrizzle
354
+ ? `import { items } from "${fromAppImport(config, "db/schema")}";`
355
+ : undefined,
356
+ withAuth ? `import { auth } from "${fromAppImport(config, "lib/auth")}";` : undefined,
357
+ ]
358
+ .filter((line) => line !== undefined)
359
+ .join("\n");
360
+ const asyncKw = withDrizzle || withAuth ? "async " : "";
361
+ let loader = "";
362
+ if (withDrizzle && withAuth) {
363
+ loader = ` const session = await auth.api.getSession({ headers: await headers() });
364
+ let orgId = session?.session?.activeOrganizationId ?? null;
365
+ if (!orgId && session?.user?.id) {
366
+ const [row] = await db
367
+ .select({ organizationId: member.organizationId })
368
+ .from(member)
369
+ .where(eq(member.userId, session.user.id))
370
+ .limit(1);
371
+ orgId = row?.organizationId ?? null;
372
+ }
373
+ const [itemRow] = orgId
374
+ ? await db.select({ value: count() }).from(items).where(eq(items.workspaceId, orgId))
375
+ : [{ value: 0 }];
376
+ const itemCount = new Intl.NumberFormat("en-US").format(Number(itemRow?.value ?? 0));
377
+
378
+ `;
379
+ }
380
+ else if (withDrizzle) {
381
+ loader = ` const [itemRow] = await db.select({ value: count() }).from(items);
382
+ const itemCount = new Intl.NumberFormat("en-US").format(Number(itemRow?.value ?? 0));
383
+
384
+ `;
385
+ }
386
+ else if (withAuth) {
387
+ loader = ` const session = await auth.api.getSession({ headers: await headers() });
388
+
389
+ `;
390
+ }
315
391
  return `import { Badge } from "@cronus-ui/ui/badge";
316
392
  import { Button } from "@cronus-ui/ui/button";
317
393
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@cronus-ui/ui/card";
318
-
394
+ ${extraImports ? `${extraImports}\n` : ""}
319
395
  const metrics = [
320
396
  { label: "Revenue", value: "R$ 48.2k" },
321
397
  { label: "Active users", value: "2,318" },
322
398
  { label: "NPS", value: "72" },
323
399
  ];
324
400
 
325
- export default function Page() {
326
- return (
401
+ export default ${asyncKw}function Page() {
402
+ ${loader} return (
327
403
  <main className="mx-auto flex min-h-screen w-full max-w-5xl flex-col gap-8 px-6 py-12">
328
404
  <header className="flex flex-col gap-4">
329
405
  <Badge variant="primary" className="w-fit">
@@ -331,10 +407,7 @@ export default function Page() {
331
407
  </Badge>
332
408
  <div className="flex flex-col gap-3">
333
409
  <h1 className="text-4xl font-semibold tracking-tight text-fg">Your stack is ready</h1>
334
- <p className="max-w-prose text-fg-secondary">
335
- This app was scaffolded from the Cronus Stack Builder. Read KICKOFF.md before changing
336
- frameworks, databases, auth, payments, or design-system rules.
337
- </p>
410
+ ${pageStatusCopy(config)}
338
411
  </div>
339
412
  <div className="flex flex-wrap gap-3">
340
413
  <Button variant="primary">Start building</Button>
@@ -505,6 +578,23 @@ function kitSkillsFromConfig(config) {
505
578
  function drizzleConfig(config) {
506
579
  const dialect = sqlDialect(config) ?? "sqlite";
507
580
  const fallback = defaultDatabaseUrl(config) ?? "file:./data/app.db";
581
+ if (dialect === "sqlite") {
582
+ return `import { mkdirSync } from "node:fs";
583
+ import { dirname } from "node:path";
584
+ import { defineConfig } from "drizzle-kit";
585
+
586
+ const url = process.env.DATABASE_URL ?? "${fallback}";
587
+ const fileFromUrl = url.startsWith("file:") ? url.slice("file:".length) : url;
588
+ mkdirSync(dirname(fileFromUrl) || ".", { recursive: true });
589
+
590
+ export default defineConfig({
591
+ dialect: "sqlite",
592
+ schema: "./${dbDir(config)}/schema.ts",
593
+ out: "./drizzle",
594
+ dbCredentials: { url },
595
+ });
596
+ `;
597
+ }
508
598
  return `import { defineConfig } from "drizzle-kit";
509
599
 
510
600
  export default defineConfig({
@@ -535,6 +625,7 @@ export const session = sqliteTable("session", {
535
625
  updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
536
626
  ipAddress: text("ip_address"),
537
627
  userAgent: text("user_agent"),
628
+ activeOrganizationId: text("active_organization_id"),
538
629
  userId: text("user_id")
539
630
  .notNull()
540
631
  .references(() => user.id, { onDelete: "cascade" }),
@@ -567,6 +658,42 @@ export const verification = sqliteTable("verification", {
567
658
  createdAt: integer("created_at", { mode: "timestamp" }),
568
659
  updatedAt: integer("updated_at", { mode: "timestamp" }),
569
660
  });
661
+
662
+ export const organization = sqliteTable("organization", {
663
+ id: text("id").primaryKey(),
664
+ name: text("name").notNull(),
665
+ slug: text("slug").notNull().unique(),
666
+ logo: text("logo"),
667
+ metadata: text("metadata"),
668
+ createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
669
+ });
670
+
671
+ export const member = sqliteTable("member", {
672
+ id: text("id").primaryKey(),
673
+ organizationId: text("organization_id")
674
+ .notNull()
675
+ .references(() => organization.id, { onDelete: "cascade" }),
676
+ userId: text("user_id")
677
+ .notNull()
678
+ .references(() => user.id, { onDelete: "cascade" }),
679
+ role: text("role").notNull(),
680
+ createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
681
+ });
682
+
683
+ export const invitation = sqliteTable("invitation", {
684
+ id: text("id").primaryKey(),
685
+ organizationId: text("organization_id")
686
+ .notNull()
687
+ .references(() => organization.id, { onDelete: "cascade" }),
688
+ email: text("email").notNull(),
689
+ role: text("role"),
690
+ status: text("status").notNull(),
691
+ expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
692
+ createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
693
+ inviterId: text("inviter_id")
694
+ .notNull()
695
+ .references(() => user.id, { onDelete: "cascade" }),
696
+ });
570
697
  `;
571
698
  }
572
699
  function pgAuthTables() {
@@ -589,6 +716,7 @@ export const session = pgTable("session", {
589
716
  updatedAt: timestamp("updated_at").notNull(),
590
717
  ipAddress: text("ip_address"),
591
718
  userAgent: text("user_agent"),
719
+ activeOrganizationId: text("active_organization_id"),
592
720
  userId: text("user_id")
593
721
  .notNull()
594
722
  .references(() => user.id, { onDelete: "cascade" }),
@@ -621,6 +749,42 @@ export const verification = pgTable("verification", {
621
749
  createdAt: timestamp("created_at"),
622
750
  updatedAt: timestamp("updated_at"),
623
751
  });
752
+
753
+ export const organization = pgTable("organization", {
754
+ id: text("id").primaryKey(),
755
+ name: text("name").notNull(),
756
+ slug: text("slug").notNull().unique(),
757
+ logo: text("logo"),
758
+ metadata: text("metadata"),
759
+ createdAt: timestamp("created_at").notNull(),
760
+ });
761
+
762
+ export const member = pgTable("member", {
763
+ id: text("id").primaryKey(),
764
+ organizationId: text("organization_id")
765
+ .notNull()
766
+ .references(() => organization.id, { onDelete: "cascade" }),
767
+ userId: text("user_id")
768
+ .notNull()
769
+ .references(() => user.id, { onDelete: "cascade" }),
770
+ role: text("role").notNull(),
771
+ createdAt: timestamp("created_at").notNull(),
772
+ });
773
+
774
+ export const invitation = pgTable("invitation", {
775
+ id: text("id").primaryKey(),
776
+ organizationId: text("organization_id")
777
+ .notNull()
778
+ .references(() => organization.id, { onDelete: "cascade" }),
779
+ email: text("email").notNull(),
780
+ role: text("role"),
781
+ status: text("status").notNull(),
782
+ expiresAt: timestamp("expires_at").notNull(),
783
+ createdAt: timestamp("created_at").notNull(),
784
+ inviterId: text("inviter_id")
785
+ .notNull()
786
+ .references(() => user.id, { onDelete: "cascade" }),
787
+ });
624
788
  `;
625
789
  }
626
790
  function mysqlAuthTables() {
@@ -643,6 +807,7 @@ export const session = mysqlTable("session", {
643
807
  updatedAt: timestamp("updated_at").notNull(),
644
808
  ipAddress: text("ip_address"),
645
809
  userAgent: text("user_agent"),
810
+ activeOrganizationId: varchar("active_organization_id", { length: 36 }),
646
811
  userId: varchar("user_id", { length: 36 })
647
812
  .notNull()
648
813
  .references(() => user.id, { onDelete: "cascade" }),
@@ -675,6 +840,42 @@ export const verification = mysqlTable("verification", {
675
840
  createdAt: timestamp("created_at"),
676
841
  updatedAt: timestamp("updated_at"),
677
842
  });
843
+
844
+ export const organization = mysqlTable("organization", {
845
+ id: varchar("id", { length: 36 }).primaryKey(),
846
+ name: varchar("name", { length: 255 }).notNull(),
847
+ slug: varchar("slug", { length: 255 }).notNull().unique(),
848
+ logo: text("logo"),
849
+ metadata: text("metadata"),
850
+ createdAt: timestamp("created_at").notNull(),
851
+ });
852
+
853
+ export const member = mysqlTable("member", {
854
+ id: varchar("id", { length: 36 }).primaryKey(),
855
+ organizationId: varchar("organization_id", { length: 36 })
856
+ .notNull()
857
+ .references(() => organization.id, { onDelete: "cascade" }),
858
+ userId: varchar("user_id", { length: 36 })
859
+ .notNull()
860
+ .references(() => user.id, { onDelete: "cascade" }),
861
+ role: varchar("role", { length: 255 }).notNull(),
862
+ createdAt: timestamp("created_at").notNull(),
863
+ });
864
+
865
+ export const invitation = mysqlTable("invitation", {
866
+ id: varchar("id", { length: 36 }).primaryKey(),
867
+ organizationId: varchar("organization_id", { length: 36 })
868
+ .notNull()
869
+ .references(() => organization.id, { onDelete: "cascade" }),
870
+ email: varchar("email", { length: 255 }).notNull(),
871
+ role: varchar("role", { length: 255 }),
872
+ status: varchar("status", { length: 255 }).notNull(),
873
+ expiresAt: timestamp("expires_at").notNull(),
874
+ createdAt: timestamp("created_at").notNull(),
875
+ inviterId: varchar("inviter_id", { length: 36 })
876
+ .notNull()
877
+ .references(() => user.id, { onDelete: "cascade" }),
878
+ });
678
879
  `;
679
880
  }
680
881
  function drizzleSchema(config) {
@@ -684,34 +885,44 @@ function drizzleSchema(config) {
684
885
  const imports = withAuth
685
886
  ? 'import { boolean, integer, pgTable, text, timestamp } from "drizzle-orm/pg-core";'
686
887
  : 'import { integer, pgTable, text } from "drizzle-orm/pg-core";';
888
+ const workspaceCol = withAuth
889
+ ? `\n workspaceId: text("workspace_id").references(() => organization.id, { onDelete: "cascade" }),`
890
+ : "";
687
891
  return `${imports}
688
-
892
+ ${withAuth ? pgAuthTables() : ""}
689
893
  export const items = pgTable("items", {
690
894
  id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
691
- title: text("title").notNull(),
895
+ title: text("title").notNull(),${workspaceCol}
692
896
  });
693
- ${withAuth ? pgAuthTables() : ""}`;
897
+ `;
694
898
  }
695
899
  case "mysql": {
696
900
  const imports = withAuth
697
901
  ? 'import { boolean, int, mysqlTable, text, timestamp, varchar } from "drizzle-orm/mysql-core";'
698
902
  : 'import { int, mysqlTable, varchar } from "drizzle-orm/mysql-core";';
903
+ const workspaceCol = withAuth
904
+ ? `\n workspaceId: varchar("workspace_id", { length: 36 }).references(() => organization.id, { onDelete: "cascade" }),`
905
+ : "";
699
906
  return `${imports}
700
-
907
+ ${withAuth ? mysqlAuthTables() : ""}
701
908
  export const items = mysqlTable("items", {
702
909
  id: int("id").autoincrement().primaryKey(),
703
- title: varchar("title", { length: 255 }).notNull(),
910
+ title: varchar("title", { length: 255 }).notNull(),${workspaceCol}
704
911
  });
705
- ${withAuth ? mysqlAuthTables() : ""}`;
912
+ `;
706
913
  }
707
- default:
914
+ default: {
915
+ const workspaceCol = withAuth
916
+ ? `\n workspaceId: text("workspace_id").references(() => organization.id, { onDelete: "cascade" }),`
917
+ : "";
708
918
  return `import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
709
-
919
+ ${withAuth ? sqliteAuthTables() : ""}
710
920
  export const items = sqliteTable("items", {
711
921
  id: integer("id").primaryKey({ autoIncrement: true }),
712
- title: text("title").notNull(),
922
+ title: text("title").notNull(),${workspaceCol}
713
923
  });
714
- ${withAuth ? sqliteAuthTables() : ""}`;
924
+ `;
925
+ }
715
926
  }
716
927
  }
717
928
  function drizzleClient(config) {
@@ -759,21 +970,210 @@ function betterAuthServer(config) {
759
970
  const schemaImport = dbModuleImport(config, "schema");
760
971
  return `import { betterAuth } from "better-auth";
761
972
  import { drizzleAdapter } from "better-auth/adapters/drizzle";
973
+ import { nextCookies } from "better-auth/next-js";
974
+ import { organization } from "better-auth/plugins";
975
+ import { and, eq } from "drizzle-orm";
762
976
  import { db } from "${dbImport}";
763
977
  import * as schema from "${schemaImport}";
978
+ import {
979
+ invitation as invitationTable,
980
+ member,
981
+ organization as organizationTable,
982
+ user as userTable,
983
+ } from "${schemaImport}";
984
+
985
+ function newId(): string {
986
+ return crypto.randomUUID().replaceAll("-", "");
987
+ }
764
988
 
765
989
  export const auth = betterAuth({
766
990
  database: drizzleAdapter(db, { provider: "${provider}", schema }),
767
- emailAndPassword: { enabled: true },
991
+ emailAndPassword: {
992
+ enabled: true,
993
+ sendResetPassword: async ({ url }) => {
994
+ console.info("Password reset URL:", url);
995
+ },
996
+ },
997
+ databaseHooks: {
998
+ session: {
999
+ create: {
1000
+ before: async (session) => {
1001
+ const [existing] = await db
1002
+ .select({ organizationId: member.organizationId })
1003
+ .from(member)
1004
+ .where(eq(member.userId, session.userId))
1005
+ .limit(1);
1006
+ if (existing?.organizationId) {
1007
+ return { data: { ...session, activeOrganizationId: existing.organizationId } };
1008
+ }
1009
+ const [owner] = await db
1010
+ .select({ name: userTable.name, email: userTable.email })
1011
+ .from(userTable)
1012
+ .where(eq(userTable.id, session.userId))
1013
+ .limit(1);
1014
+ if (owner?.email) {
1015
+ const [pending] = await db
1016
+ .select({ id: invitationTable.id })
1017
+ .from(invitationTable)
1018
+ .where(
1019
+ and(eq(invitationTable.email, owner.email), eq(invitationTable.status, "pending")),
1020
+ )
1021
+ .limit(1);
1022
+ if (pending) return;
1023
+ }
1024
+ const orgId = newId();
1025
+ const now = new Date();
1026
+ await db.insert(organizationTable).values({
1027
+ id: orgId,
1028
+ name: owner?.name.trim() || "Workspace",
1029
+ slug: \`ws-\${session.userId.slice(0, 16)}\`,
1030
+ createdAt: now,
1031
+ });
1032
+ await db.insert(member).values({
1033
+ id: newId(),
1034
+ organizationId: orgId,
1035
+ userId: session.userId,
1036
+ role: "owner",
1037
+ createdAt: now,
1038
+ });
1039
+ return { data: { ...session, activeOrganizationId: orgId } };
1040
+ },
1041
+ },
1042
+ },
1043
+ },
1044
+ plugins: [
1045
+ organization({
1046
+ sendInvitationEmail: async (data) => {
1047
+ const base = process.env.BETTER_AUTH_URL ?? "http://localhost:3000";
1048
+ console.info(\`Invite \${data.email}: \${base}/accept-invitation?id=\${data.id}\`);
1049
+ },
1050
+ }),
1051
+ nextCookies(),
1052
+ ],
768
1053
  secret: process.env.BETTER_AUTH_SECRET,
769
1054
  baseURL: process.env.BETTER_AUTH_URL,
770
1055
  });
771
1056
  `;
772
1057
  }
1058
+ function betterAuthMiddleware() {
1059
+ return `import { NextRequest, NextResponse } from "next/server";
1060
+ import { getSessionCookie } from "better-auth/cookies";
1061
+
1062
+ const AUTH_PAGES = ["/login", "/signup", "/forgot-password"];
1063
+
1064
+ function invitationOf(request: NextRequest): string | null {
1065
+ const { pathname, searchParams } = request.nextUrl;
1066
+ return (
1067
+ searchParams.get("invitation") ??
1068
+ (pathname === "/accept-invitation" ? searchParams.get("id") : null)
1069
+ );
1070
+ }
1071
+
1072
+ export function middleware(request: NextRequest) {
1073
+ const sessionCookie = getSessionCookie(request);
1074
+ const path = request.nextUrl.pathname;
1075
+ const isAuthPage = AUTH_PAGES.includes(path);
1076
+ const invitation = invitationOf(request);
1077
+ if (!sessionCookie && path === "/accept-invitation") {
1078
+ const url = new URL("/signup", request.url);
1079
+ if (invitation) url.searchParams.set("invitation", invitation);
1080
+ return NextResponse.redirect(url);
1081
+ }
1082
+ if (!sessionCookie && !isAuthPage) {
1083
+ const url = new URL("/login", request.url);
1084
+ if (invitation) url.searchParams.set("invitation", invitation);
1085
+ return NextResponse.redirect(url);
1086
+ }
1087
+ if (sessionCookie && isAuthPage) {
1088
+ if (invitation) {
1089
+ return NextResponse.redirect(
1090
+ new URL(\`/accept-invitation?id=\${encodeURIComponent(invitation)}\`, request.url),
1091
+ );
1092
+ }
1093
+ return NextResponse.redirect(new URL("/", request.url));
1094
+ }
1095
+ return NextResponse.next();
1096
+ }
1097
+
1098
+ export const config = {
1099
+ matcher: ["/((?!api/auth|_next/static|_next/image|favicon.ico|.*\\\\..*).*)"],
1100
+ };
1101
+ `;
1102
+ }
1103
+ function acceptInvitationPage(config) {
1104
+ const authClientImport = fromAppImport(config, "lib/auth-client");
1105
+ return `"use client";
1106
+
1107
+ import { Suspense, useEffect, useState } from "react";
1108
+ import { useRouter, useSearchParams } from "next/navigation";
1109
+ import { authClient } from "${authClientImport}";
1110
+
1111
+ function AcceptInvitation() {
1112
+ const router = useRouter();
1113
+ const params = useSearchParams();
1114
+ const id = params.get("id") ?? params.get("invitation");
1115
+ const { data: session, isPending } = authClient.useSession();
1116
+ const [error, setError] = useState<string | null>(null);
1117
+
1118
+ useEffect(() => {
1119
+ if (isPending) return;
1120
+ if (!id) {
1121
+ setError("Invitation is missing.");
1122
+ return;
1123
+ }
1124
+ if (!session) {
1125
+ router.replace(\`/signup?invitation=\${encodeURIComponent(id)}\`);
1126
+ return;
1127
+ }
1128
+ let cancelled = false;
1129
+ void (async () => {
1130
+ const { data, error: acceptError } = await authClient.organization.acceptInvitation({
1131
+ invitationId: id,
1132
+ });
1133
+ if (cancelled) return;
1134
+ if (acceptError) {
1135
+ setError(acceptError.message || "Could not accept invitation.");
1136
+ return;
1137
+ }
1138
+ const orgId = data?.invitation?.organizationId ?? data?.member?.organizationId;
1139
+ if (orgId) {
1140
+ await authClient.organization.setActive({ organizationId: orgId });
1141
+ }
1142
+ try {
1143
+ sessionStorage.removeItem("cronus-invitation");
1144
+ } catch {
1145
+ // ignore
1146
+ }
1147
+ window.location.assign("/");
1148
+ })();
1149
+ return () => {
1150
+ cancelled = true;
1151
+ };
1152
+ }, [id, isPending, router, session]);
1153
+
1154
+ return (
1155
+ <main>
1156
+ {error ? <p role="alert">{error}</p> : <p>Accepting invitation…</p>}
1157
+ </main>
1158
+ );
1159
+ }
1160
+
1161
+ export default function AcceptInvitationPage() {
1162
+ return (
1163
+ <Suspense fallback={<main><p>Accepting invitation…</p></main>}>
1164
+ <AcceptInvitation />
1165
+ </Suspense>
1166
+ );
1167
+ }
1168
+ `;
1169
+ }
773
1170
  function betterAuthClient() {
774
- return `import { createAuthClient } from "better-auth/react";
1171
+ return `import { organizationClient } from "better-auth/client/plugins";
1172
+ import { createAuthClient } from "better-auth/react";
775
1173
 
776
- export const authClient = createAuthClient();
1174
+ export const authClient = createAuthClient({
1175
+ plugins: [organizationClient()],
1176
+ });
777
1177
  `;
778
1178
  }
779
1179
  function betterAuthRoute(config) {
@@ -866,7 +1266,7 @@ export function scaffoldStack(options) {
866
1266
  emit("postcss.config.mjs", 'export default { plugins: { "@tailwindcss/postcss": {} } };\n');
867
1267
  emit(`${app}/globals.css`, globalsCss(config));
868
1268
  emit(`${app}/layout.tsx`, layoutTsx(projectName));
869
- emit(`${app}/page.tsx`, pageTsx());
1269
+ emit(`${app}/page.tsx`, pageTsx(config));
870
1270
  emit("cronus-ui.json", `${JSON.stringify({
871
1271
  aliases: { ui: "@/components/ui", lib: "@/lib", blocks: "@/components/blocks" },
872
1272
  paths: cronusUiPaths(config),
@@ -888,6 +1288,8 @@ export function scaffoldStack(options) {
888
1288
  emit(`${libDir(config)}/auth.ts`, betterAuthServer(config));
889
1289
  emit(`${libDir(config)}/auth-client.ts`, betterAuthClient());
890
1290
  emit(`${app}/api/auth/[...all]/route.ts`, betterAuthRoute(config));
1291
+ emit(single(config, "structure") === "structure-root" ? "middleware.ts" : "src/middleware.ts", betterAuthMiddleware());
1292
+ emit(`${app}/accept-invitation/page.tsx`, acceptInvitationPage(config));
891
1293
  }
892
1294
  }
893
1295
  else {
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const CREATE_STACK_VERSION = "0.6.1";
1
+ export declare const CREATE_STACK_VERSION = "0.6.3";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const CREATE_STACK_VERSION = "0.6.1";
1
+ export const CREATE_STACK_VERSION = "0.6.3";
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-cronus-stack",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "Scaffold a Cronus stack from the Cronus Stack Builder contract: app files, stack.json, KICKOFF.md, and optional AI Kit.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -44,8 +44,8 @@
44
44
  "prepublishOnly": "tsc -p tsconfig.json"
45
45
  },
46
46
  "dependencies": {
47
- "@cronus-ui/ai-kit": "0.6.1",
48
- "@cronus-ui/stack": "0.6.1"
47
+ "@cronus-ui/ai-kit": "0.6.3",
48
+ "@cronus-ui/stack": "0.6.3"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^22.10.0",