cronus-ui 0.6.18 → 0.6.20

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.18` here), not
52
+ - The default registry is pinned to the CLI package version (`v0.6.20` 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.18/registry"
83
+ "registry": "https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v0.6.20/registry"
84
84
  }
85
85
  ```
86
86
 
@@ -5,7 +5,7 @@ import { tmpdir } from "node:os";
5
5
  import { join } from "node:path";
6
6
  import { createInterface } from "node:readline/promises";
7
7
  import pc from "picocolors";
8
- import { goldPatchAppShellChrome, goldPatchHomePage, goldPatchShellLayout, goldPatchTeamPage, isGoldPathTemplate, } from "../compose/gold-path.js";
8
+ import { goldPatchAppShellChrome, goldPatchHomePage, goldPatchShellLayout, goldPatchTeamPage, isGoldPathTemplate, reemitGoldPathOwned, } from "../compose/gold-path.js";
9
9
  import { buildComposePlan } from "../compose/plan.js";
10
10
  import { baseSnapshotDest, filterManifestToComposedPages, listBaseSnapshotRels, readBaseSnapshot, reloadManifest, } from "../compose/reload.js";
11
11
  import { renderPlan } from "../compose/render.js";
@@ -465,6 +465,21 @@ function goldPathSurfaceRels(cwd, composed) {
465
465
  }
466
466
  return [...rels];
467
467
  }
468
+ /**
469
+ * `--all` on saas/admin: re-emit compose's `always: true` gold-path files so
470
+ * CLI gold-path fixes reach apps composed on an older release. Named upgrades
471
+ * (e.g. `upgrade stats`) do not touch them. auth.ts / schema stay user-owned.
472
+ */
473
+ async function reemitGoldPathOwnedOnUpgrade(cwd, config, composed, upgradeComposed) {
474
+ if (!upgradeComposed)
475
+ return;
476
+ if (!Object.keys(composed).some((key) => isGoldPathTemplate(key)))
477
+ return;
478
+ const written = await reemitGoldPathOwned(cwd, config);
479
+ if (written.length === 0)
480
+ return;
481
+ log.ok(`gold-path owned: re-emitted ${written.join(", ")}`);
482
+ }
468
483
  async function repatchGoldPathSurfaces(cwd, config, composed) {
469
484
  if (!Object.keys(composed).some((key) => isGoldPathTemplate(key)))
470
485
  return;
@@ -834,6 +849,7 @@ export async function upgrade(names, options) {
834
849
  const newlyInstalled = await installPendingBlocks(itemPlans, config, cwd, targetVersion);
835
850
  Object.assign(installed, newlyInstalled);
836
851
  const written = await applyPlans(itemPlans, options, confirm);
852
+ await reemitGoldPathOwnedOnUpgrade(cwd, config, composed, upgradeComposed);
837
853
  await repatchGoldPathSurfaces(cwd, config, composed);
838
854
  await refreshComposeSnapshots(itemPlans);
839
855
  // Manifest: record every item that now fully embodies the target release.
@@ -67,9 +67,15 @@ export declare function patchTeamPageSource(source: string, membersImport: strin
67
67
  * app chrome. Idempotent. Returns undefined when AppShellNav is missing.
68
68
  */
69
69
  export declare function patchShellLayoutSource(source: string, authImport: string): string | undefined;
70
+ /**
71
+ * Re-emit compose's `always: true` gold-path files. Idempotent when bytes
72
+ * already match. Used by `upgrade --all` so CLI gold-path fixes reach apps
73
+ * composed on an older release without clobbering auth.ts / schema.
74
+ */
75
+ export declare function reemitGoldPathOwned(targetDir: string, config: CronusUIConfig): Promise<string[]>;
70
76
  /**
71
77
  * Write sqlite + Drizzle + Better-Auth files into a composed saas/admin app.
72
- * Overwrites lib/auth-adapter.ts, lib/items.ts, and lib/members.ts always.
78
+ * Overwrites owned adapters/panels always (`always: true`).
73
79
  * Patches the shell home, /team, and shell layout only when compose wrote them
74
80
  * this run.
75
81
  */
@@ -2,6 +2,7 @@
2
2
  * Authenticated gold path for saas/admin compose: SQLite + Drizzle + Better-Auth.
3
3
  * Always sqlite — postgres/mysql live in create-cronus-stack.
4
4
  */
5
+ import { randomBytes } from "node:crypto";
5
6
  import { existsSync } from "node:fs";
6
7
  import { readFile } from "node:fs/promises";
7
8
  import { join } from "node:path";
@@ -66,12 +67,13 @@ const GOLD_PATH_SCRIPTS = {
66
67
  "db:generate": "drizzle-kit generate",
67
68
  "db:studio": "drizzle-kit studio",
68
69
  };
70
+ const ENV_EXAMPLE_SECRET = "change-me-to-a-32-character-secret";
69
71
  const ENV_VARS = {
70
72
  DATABASE_URL: "file:./data/app.db",
71
- BETTER_AUTH_SECRET: "change-me-to-a-32-character-secret",
73
+ BETTER_AUTH_SECRET: ENV_EXAMPLE_SECRET,
72
74
  BETTER_AUTH_URL: "http://localhost:3000",
73
75
  };
74
- const GITIGNORE_ENTRIES = ["*.db", "data/", "drizzle/"];
76
+ const GITIGNORE_ENTRIES = ["*.db", "data/", "drizzle/", ".env"];
75
77
  const DATABASE_URL_FALLBACK = "file:./data/app.db";
76
78
  const TITLE_MAX = 200;
77
79
  export function goldPathLayout(config) {
@@ -1251,6 +1253,21 @@ function mergeNextConfig(raw) {
1251
1253
  }
1252
1254
  return `${raw.trimEnd()}\n`;
1253
1255
  }
1256
+ function envFileBody(secret) {
1257
+ return `${Object.entries({ ...ENV_VARS, BETTER_AUTH_SECRET: secret })
1258
+ .map(([key, value]) => `${key}=${value}`)
1259
+ .join("\n")}\n`;
1260
+ }
1261
+ function newAuthSecret() {
1262
+ return randomBytes(32).toString("hex");
1263
+ }
1264
+ /** Write `.env` once so first `bun dev` has a real Better Auth secret. Never overwrite. */
1265
+ async function writeDotEnvIfMissing(targetDir) {
1266
+ const dest = resolveSafeDest(targetDir, ".", ".env");
1267
+ if (existsSync(dest))
1268
+ return;
1269
+ await writeFileEnsured(dest, envFileBody(newAuthSecret()));
1270
+ }
1254
1271
  function mergeEnvExample(raw) {
1255
1272
  const lines = raw.split(/\r?\n/);
1256
1273
  const keys = new Set(lines.map((line) => {
@@ -1286,97 +1303,115 @@ async function writeRel(targetDir, rel, content, overwrite, always, written, ski
1286
1303
  written.push(rel);
1287
1304
  }
1288
1305
  /**
1289
- * Write sqlite + Drizzle + Better-Auth files into a composed saas/admin app.
1290
- * Overwrites lib/auth-adapter.ts, lib/items.ts, and lib/members.ts always.
1291
- * Patches the shell home, /team, and shell layout only when compose wrote them
1292
- * this run.
1306
+ * Gold-path files compose always overwrites (`always: true`). Scaffold files
1307
+ * (auth.ts, schema, middleware) are not in this list — those stay user-owned
1308
+ * after the first write.
1293
1309
  */
1294
- export async function applyGoldPath(options) {
1295
- const { targetDir, config, generatedFiles, overwrite } = options;
1310
+ function goldPathOwnedFiles(config, appDir) {
1296
1311
  const layout = goldPathLayout(config);
1297
- const appDir = resolveAppDir(targetDir, layout, generatedFiles);
1298
- const middlewareRel = appDir === "src/app"
1299
- ? "src/middleware.ts"
1300
- : appDir === "app"
1301
- ? "middleware.ts"
1302
- : layout.middlewareRel;
1303
1312
  const authImport = `${config.aliases.lib}/auth`;
1304
1313
  const authClientImport = `${config.aliases.lib}/auth-client`;
1305
1314
  const itemsActionsImport = `${config.aliases.lib}/items`;
1306
1315
  const itemsViewImport = "@/components/items-view";
1307
- const itemsImport = "@/components/items-panel";
1308
1316
  const membersActionsImport = `${config.aliases.lib}/members`;
1309
1317
  const membersViewImport = "@/components/members-view";
1310
- const membersImport = "@/components/members-panel";
1311
- const workspaceImport = "@/components/workspace-menu";
1312
1318
  const inviteImport = "@/components/invite-member";
1313
- const sessionImport = "@/components/session-user";
1314
- const chromeRel = `${posix(config.paths.blocks)}/app-shell-chrome.tsx`;
1315
- const written = [];
1316
- const skipped = [];
1317
- const files = [
1318
- { rel: "drizzle.config.ts", content: drizzleConfigSource(layout.dbDir) },
1319
- { rel: `${layout.dbDir}/schema.ts`, content: dbSchemaSource() },
1320
- { rel: `${layout.dbDir}/index.ts`, content: dbClientSource() },
1321
- { rel: `${layout.libDir}/auth.ts`, content: authServerSource() },
1322
- { rel: `${layout.libDir}/auth-client.ts`, content: authClientSource() },
1323
- { rel: `${layout.libDir}/auth-adapter.ts`, content: authAdapterSource(), always: true },
1324
- {
1325
- rel: `${layout.libDir}/items.ts`,
1326
- content: itemsActionsSource(authImport),
1327
- always: true,
1328
- },
1329
- {
1330
- rel: `${appDir}/api/auth/[...all]/route.ts`,
1331
- content: authRouteSource(authImport),
1332
- },
1333
- { rel: middlewareRel, content: middlewareSource() },
1319
+ return [
1320
+ { rel: `${layout.libDir}/auth-adapter.ts`, content: authAdapterSource() },
1321
+ { rel: `${layout.libDir}/items.ts`, content: itemsActionsSource(authImport) },
1334
1322
  {
1335
1323
  rel: `${layout.componentsDir}/items-panel.tsx`,
1336
1324
  content: itemsPanelSource(itemsActionsImport, itemsViewImport),
1337
- always: true,
1338
1325
  },
1339
1326
  {
1340
1327
  rel: `${layout.componentsDir}/items-view.tsx`,
1341
1328
  content: itemsViewSource(itemsActionsImport),
1342
- always: true,
1343
- },
1344
- {
1345
- rel: `${layout.libDir}/members.ts`,
1346
- content: membersActionsSource(authImport),
1347
- always: true,
1348
1329
  },
1330
+ { rel: `${layout.libDir}/members.ts`, content: membersActionsSource(authImport) },
1349
1331
  {
1350
1332
  rel: `${layout.componentsDir}/members-panel.tsx`,
1351
1333
  content: membersPanelSource(membersActionsImport, membersViewImport),
1352
- always: true,
1353
1334
  },
1354
1335
  {
1355
1336
  rel: `${layout.componentsDir}/members-view.tsx`,
1356
1337
  content: membersViewSource(inviteImport),
1357
- always: true,
1358
1338
  },
1359
1339
  {
1360
1340
  rel: `${layout.componentsDir}/workspace-menu.tsx`,
1361
1341
  content: workspaceMenuSource(authClientImport),
1362
- always: true,
1363
1342
  },
1364
1343
  {
1365
1344
  rel: `${layout.componentsDir}/invite-member.tsx`,
1366
1345
  content: inviteMemberSource(authClientImport),
1367
- always: true,
1368
1346
  },
1369
1347
  {
1370
1348
  rel: `${layout.componentsDir}/session-user.tsx`,
1371
1349
  content: sessionUserSource(authClientImport),
1372
- always: true,
1373
1350
  },
1374
1351
  {
1375
1352
  rel: `${appDir}/(bare)/accept-invitation/page.tsx`,
1376
1353
  content: acceptInvitationPageSource(authClientImport),
1377
- always: true,
1378
1354
  },
1379
1355
  ];
1356
+ }
1357
+ /**
1358
+ * Re-emit compose's `always: true` gold-path files. Idempotent when bytes
1359
+ * already match. Used by `upgrade --all` so CLI gold-path fixes reach apps
1360
+ * composed on an older release without clobbering auth.ts / schema.
1361
+ */
1362
+ export async function reemitGoldPathOwned(targetDir, config) {
1363
+ const layout = goldPathLayout(config);
1364
+ const appDir = resolveAppDir(targetDir, layout, []);
1365
+ const written = [];
1366
+ for (const file of goldPathOwnedFiles(config, appDir)) {
1367
+ const dest = resolveSafeDest(targetDir, ".", file.rel);
1368
+ if (existsSync(dest)) {
1369
+ const current = await readFile(dest, "utf8");
1370
+ if (current === file.content)
1371
+ continue;
1372
+ }
1373
+ await writeFileEnsured(dest, file.content);
1374
+ written.push(file.rel);
1375
+ }
1376
+ return written;
1377
+ }
1378
+ /**
1379
+ * Write sqlite + Drizzle + Better-Auth files into a composed saas/admin app.
1380
+ * Overwrites owned adapters/panels always (`always: true`).
1381
+ * Patches the shell home, /team, and shell layout only when compose wrote them
1382
+ * this run.
1383
+ */
1384
+ export async function applyGoldPath(options) {
1385
+ const { targetDir, config, generatedFiles, overwrite } = options;
1386
+ const layout = goldPathLayout(config);
1387
+ const appDir = resolveAppDir(targetDir, layout, generatedFiles);
1388
+ const middlewareRel = appDir === "src/app"
1389
+ ? "src/middleware.ts"
1390
+ : appDir === "app"
1391
+ ? "middleware.ts"
1392
+ : layout.middlewareRel;
1393
+ const authImport = `${config.aliases.lib}/auth`;
1394
+ const itemsImport = "@/components/items-panel";
1395
+ const membersImport = "@/components/members-panel";
1396
+ const workspaceImport = "@/components/workspace-menu";
1397
+ const inviteImport = "@/components/invite-member";
1398
+ const sessionImport = "@/components/session-user";
1399
+ const chromeRel = `${posix(config.paths.blocks)}/app-shell-chrome.tsx`;
1400
+ const written = [];
1401
+ const skipped = [];
1402
+ const files = [
1403
+ { rel: "drizzle.config.ts", content: drizzleConfigSource(layout.dbDir) },
1404
+ { rel: `${layout.dbDir}/schema.ts`, content: dbSchemaSource() },
1405
+ { rel: `${layout.dbDir}/index.ts`, content: dbClientSource() },
1406
+ { rel: `${layout.libDir}/auth.ts`, content: authServerSource() },
1407
+ { rel: `${layout.libDir}/auth-client.ts`, content: authClientSource() },
1408
+ {
1409
+ rel: `${appDir}/api/auth/[...all]/route.ts`,
1410
+ content: authRouteSource(authImport),
1411
+ },
1412
+ { rel: middlewareRel, content: middlewareSource() },
1413
+ ...goldPathOwnedFiles(config, appDir).map((file) => ({ ...file, always: true })),
1414
+ ];
1380
1415
  for (const file of files) {
1381
1416
  await writeRel(targetDir, file.rel, file.content, overwrite, file.always === true, written, skipped);
1382
1417
  }
@@ -1445,9 +1480,8 @@ export async function applyGoldPath(options) {
1445
1480
  }
1446
1481
  await mergeTextFile(targetDir, "package.json", mergePackageJson);
1447
1482
  await mergeOrCreate(targetDir, "next.config.mjs", nextConfigSource(), mergeNextConfig);
1448
- await mergeOrCreate(targetDir, ".env.example", `${Object.entries(ENV_VARS)
1449
- .map(([k, v]) => `${k}=${v}`)
1450
- .join("\n")}\n`, mergeEnvExample);
1483
+ await mergeOrCreate(targetDir, ".env.example", envFileBody(ENV_EXAMPLE_SECRET), mergeEnvExample);
1484
+ await writeDotEnvIfMissing(targetDir);
1451
1485
  await mergeOrCreate(targetDir, ".gitignore", `${GITIGNORE_ENTRIES.join("\n")}\n`, mergeGitignore);
1452
1486
  return { written, skipped };
1453
1487
  }
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.18";
3
- export declare const DEFAULT_REGISTRY = "https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v0.6.18/registry";
2
+ export declare const CLI_VERSION = "0.6.20";
3
+ export declare const DEFAULT_REGISTRY = "https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v0.6.20/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.18";
5
+ export const CLI_VERSION = "0.6.20";
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.18",
3
+ "version": "0.6.20",
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.18",
57
+ "@cronus-ui/ai-kit": "0.6.20",
58
58
  "commander": "^15.0.0",
59
59
  "picocolors": "^1.1.1"
60
60
  },