cronus-ui 0.6.4 → 0.6.6

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/README.md CHANGED
@@ -49,7 +49,7 @@ Notes that match the commander surface:
49
49
  `packages/cli/scripts/build-registry.ts` — each item carries its source, its npm
50
50
  `dependencies`, and its `registryDependencies` (other components it imports),
51
51
  derived by parsing imports.
52
- - The default registry is pinned to the CLI package version (`v0.6.4` here), not
52
+ - The default registry is pinned to the CLI package version (`v0.6.6` here), not
53
53
  mutable `main`, so a published CLI reads the registry snapshot it was released
54
54
  with. Use `-r, --registry ./registry` when testing local registry changes before
55
55
  a release tag exists.
@@ -80,7 +80,7 @@ Notes that match the commander surface:
80
80
  "lib": "lib",
81
81
  "blocks": "components/blocks"
82
82
  },
83
- "registry": "https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v0.6.4/registry"
83
+ "registry": "https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v0.6.6/registry"
84
84
  }
85
85
  ```
86
86
 
@@ -42,10 +42,16 @@ export declare function patchHomePageSource(source: string, itemsImport: string)
42
42
  * Idempotent. Returns undefined when there is no TeamBlock and no MembersPanel.
43
43
  */
44
44
  export declare function patchTeamPageSource(source: string, membersImport: string): string | undefined;
45
+ /**
46
+ * Validate the session in the shell layout so a stale cookie cannot sit in the
47
+ * app chrome. Idempotent. Returns undefined when AppShellNav is missing.
48
+ */
49
+ export declare function patchShellLayoutSource(source: string, authImport: string): string | undefined;
45
50
  /**
46
51
  * Write sqlite + Drizzle + Better-Auth files into a composed saas/admin app.
47
52
  * Overwrites lib/auth-adapter.ts, lib/items.ts, and lib/members.ts always.
48
- * Patches the shell home and /team pages only when compose wrote them this run.
53
+ * Patches the shell home, /team, and shell layout only when compose wrote them
54
+ * this run.
49
55
  */
50
56
  export declare function applyGoldPath(options: ApplyGoldPathOptions): Promise<ApplyGoldPathResult>;
51
57
  //# sourceMappingURL=gold-path.d.ts.map
@@ -89,6 +89,13 @@ function teamPageRel(appDir, generatedFiles) {
89
89
  const any = generatedFiles.find((f) => /(^|\/)\(shell\)\/team\/page\.tsx$/.test(posix(f)));
90
90
  return any !== undefined ? posix(any) : undefined;
91
91
  }
92
+ function shellLayoutRel(appDir, generatedFiles) {
93
+ const match = generatedFiles.find((f) => posix(f) === `${appDir}/(shell)/layout.tsx`);
94
+ if (match !== undefined)
95
+ return posix(match);
96
+ const any = generatedFiles.find((f) => /(^|\/)\(shell\)\/layout\.tsx$/.test(posix(f)));
97
+ return any !== undefined ? posix(any) : undefined;
98
+ }
92
99
  function drizzleConfigSource(dbDir) {
93
100
  return `import { mkdirSync } from "node:fs";
94
101
  import { dirname } from "node:path";
@@ -238,6 +245,19 @@ function newId(): string {
238
245
  return crypto.randomUUID().replaceAll("-", "");
239
246
  }
240
247
 
248
+ function authBaseURL() {
249
+ const env = process.env.BETTER_AUTH_URL;
250
+ const hosts = ["localhost:*", "127.0.0.1:*"];
251
+ if (env) {
252
+ try {
253
+ hosts.push(new URL(env).host);
254
+ } catch {
255
+ // ignore invalid BETTER_AUTH_URL
256
+ }
257
+ }
258
+ return { allowedHosts: hosts, fallback: env || "http://localhost:3000" };
259
+ }
260
+
241
261
  export const auth = betterAuth({
242
262
  database: drizzleAdapter(db, { provider: "sqlite", schema }),
243
263
  emailAndPassword: {
@@ -294,12 +314,11 @@ export const auth = betterAuth({
294
314
  },
295
315
  },
296
316
  secret: process.env.BETTER_AUTH_SECRET,
297
- baseURL: process.env.BETTER_AUTH_URL,
317
+ baseURL: authBaseURL(),
298
318
  plugins: [
299
319
  organization({
300
320
  sendInvitationEmail: async (data) => {
301
- const base = process.env.BETTER_AUTH_URL ?? "http://localhost:3000";
302
- console.info(\`Invite \${data.email}: \${base}/accept-invitation?id=\${data.id}\`);
321
+ console.info(\`Invite \${data.email}: /accept-invitation?id=\${data.id}\`);
303
322
  },
304
323
  }),
305
324
  nextCookies(),
@@ -422,13 +441,10 @@ export function middleware(request: NextRequest) {
422
441
  if (invitation) url.searchParams.set("invitation", invitation);
423
442
  return NextResponse.redirect(url);
424
443
  }
425
- if (sessionCookie && isAuthPage) {
426
- if (invitation) {
427
- return NextResponse.redirect(
428
- new URL(\`/accept-invitation?id=\${encodeURIComponent(invitation)}\`, request.url),
429
- );
430
- }
431
- return NextResponse.redirect(new URL("/", request.url));
444
+ if (sessionCookie && isAuthPage && invitation) {
445
+ return NextResponse.redirect(
446
+ new URL(\`/accept-invitation?id=\${encodeURIComponent(invitation)}\`, request.url),
447
+ );
432
448
  }
433
449
  return NextResponse.next();
434
450
  }
@@ -1022,6 +1038,29 @@ export function patchTeamPageSource(source, membersImport) {
1022
1038
  out = out.replace(/<TeamBlock\s*\/>/, "<MembersPanel />");
1023
1039
  return out;
1024
1040
  }
1041
+ /**
1042
+ * Validate the session in the shell layout so a stale cookie cannot sit in the
1043
+ * app chrome. Idempotent. Returns undefined when AppShellNav is missing.
1044
+ */
1045
+ export function patchShellLayoutSource(source, authImport) {
1046
+ if (source.includes("auth.api.getSession") && source.includes('redirect("/login")')) {
1047
+ return source;
1048
+ }
1049
+ if (!source.includes("AppShellNav") || !source.includes("{children}"))
1050
+ return undefined;
1051
+ let out = source;
1052
+ out = insertImport(out, `import { headers } from "next/headers";`);
1053
+ out = insertImport(out, `import { redirect } from "next/navigation";`);
1054
+ out = insertImport(out, `import { auth } from ${JSON.stringify(authImport)};`);
1055
+ out = out.replace(/export default(?! async) function/, "export default async function");
1056
+ if (!out.includes("auth.api.getSession")) {
1057
+ out = out.replace(/(\{ children \}: \{ children: ReactNode \}\) \{)\n/, `$1
1058
+ const session = await auth.api.getSession({ headers: await headers() });
1059
+ if (!session) redirect("/login");
1060
+ `);
1061
+ }
1062
+ return out;
1063
+ }
1025
1064
  function mergePackageJson(raw) {
1026
1065
  const pkg = JSON.parse(raw);
1027
1066
  const dependencies = { ...(pkg.dependencies ?? {}) };
@@ -1092,7 +1131,8 @@ async function writeRel(targetDir, rel, content, overwrite, always, written, ski
1092
1131
  /**
1093
1132
  * Write sqlite + Drizzle + Better-Auth files into a composed saas/admin app.
1094
1133
  * Overwrites lib/auth-adapter.ts, lib/items.ts, and lib/members.ts always.
1095
- * Patches the shell home and /team pages only when compose wrote them this run.
1134
+ * Patches the shell home, /team, and shell layout only when compose wrote them
1135
+ * this run.
1096
1136
  */
1097
1137
  export async function applyGoldPath(options) {
1098
1138
  const { targetDir, config, generatedFiles, overwrite } = options;
@@ -1198,6 +1238,22 @@ export async function applyGoldPath(options) {
1198
1238
  }
1199
1239
  }
1200
1240
  }
1241
+ const layoutRel = shellLayoutRel(appDir, generatedFiles);
1242
+ if (layoutRel !== undefined) {
1243
+ const dest = resolveSafeDest(targetDir, ".", layoutRel);
1244
+ if (existsSync(dest)) {
1245
+ const current = await readFile(dest, "utf8");
1246
+ const patched = patchShellLayoutSource(current, authImport);
1247
+ if (patched !== undefined && patched !== current) {
1248
+ await writeFileEnsured(dest, patched);
1249
+ const templateName = options.templateName;
1250
+ if (templateName !== undefined) {
1251
+ const snapDest = resolveSafeDest(targetDir, baseSnapshotDir(templateName), layoutRel);
1252
+ await writeFileEnsured(snapDest, patched);
1253
+ }
1254
+ }
1255
+ }
1256
+ }
1201
1257
  const teamRel = teamPageRel(appDir, generatedFiles);
1202
1258
  if (teamRel !== undefined) {
1203
1259
  const dest = resolveSafeDest(targetDir, ".", teamRel);
package/dist/config.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export declare const CONFIG_FILE = "cronus-ui.json";
2
- export declare const CLI_VERSION = "0.6.4";
3
- export declare const DEFAULT_REGISTRY = "https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v0.6.4/registry";
2
+ export declare const CLI_VERSION = "0.6.6";
3
+ export declare const DEFAULT_REGISTRY = "https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v0.6.6/registry";
4
4
  /** Manifest entry `add`/`upgrade` record per installed registry item. */
5
5
  export interface InstalledRecord {
6
6
  /** Registry release the files came from (git tag without the leading "v"). */
package/dist/config.js CHANGED
@@ -2,7 +2,7 @@ import { existsSync } from "node:fs";
2
2
  import { readFile, writeFile } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  export const CONFIG_FILE = "cronus-ui.json";
5
- export const CLI_VERSION = "0.6.4";
5
+ export const CLI_VERSION = "0.6.6";
6
6
  export const DEFAULT_REGISTRY = `https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v${CLI_VERSION}/registry`;
7
7
  export const DEFAULT_CONFIG = {
8
8
  aliases: { ui: "@/components/ui", lib: "@/lib", blocks: "@/components/blocks" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cronus-ui",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
4
4
  "description": "cronus-ui — add Cronus UI components to your project, shadcn-style (copy-paste registry).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -54,7 +54,7 @@
54
54
  "registry:check": "bun run scripts/check-registry.ts"
55
55
  },
56
56
  "dependencies": {
57
- "@cronus-ui/ai-kit": "0.6.4",
57
+ "@cronus-ui/ai-kit": "0.6.6",
58
58
  "commander": "^15.0.0",
59
59
  "picocolors": "^1.1.1"
60
60
  },
@@ -20,7 +20,7 @@
20
20
  "route": "/signup",
21
21
  "title": "Create account",
22
22
  "chrome": "bare",
23
- "blocks": ["signup"]
23
+ "blocks": [{ "block": "signup", "variant": "split" }]
24
24
  },
25
25
  {
26
26
  "route": "/forgot-password",