@percepta/create 3.1.5 → 3.3.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.
Files changed (77) hide show
  1. package/dist/index.js +53 -46
  2. package/dist/index.js.map +1 -1
  3. package/dist/{init-OeK4Yk6_.js → init-CtCp7Tv2.js} +3 -3
  4. package/dist/init-CtCp7Tv2.js.map +1 -0
  5. package/dist/{status-DC8mvHZj.js → status-CKe4aKso.js} +2 -2
  6. package/dist/{status-DC8mvHZj.js.map → status-CKe4aKso.js.map} +1 -1
  7. package/dist/{sync-C5Pd32VM.js → sync-D1vkoofl.js} +2 -2
  8. package/dist/{sync-C5Pd32VM.js.map → sync-D1vkoofl.js.map} +1 -1
  9. package/dist/{upstream-F6m8zRBQ.js → upstream-D-LH_1z4.js} +2 -2
  10. package/dist/{upstream-F6m8zRBQ.js.map → upstream-D-LH_1z4.js.map} +1 -1
  11. package/package.json +2 -2
  12. package/template-versions.json +1 -1
  13. package/templates/monorepo/.github/workflows/access-control.yml +38 -0
  14. package/templates/monorepo/README.md +42 -2
  15. package/templates/monorepo/access/README.md +39 -0
  16. package/templates/monorepo/access/bootstrap-grants.yaml.example +9 -0
  17. package/templates/monorepo/access/dev-grants.yaml.example +19 -0
  18. package/templates/monorepo/access/dev-groups.yaml.example +8 -0
  19. package/templates/monorepo/access/reconcile.yaml.example +11 -0
  20. package/templates/monorepo/auth/README.md +27 -0
  21. package/templates/monorepo/auth/drizzle.config.ts +13 -0
  22. package/templates/monorepo/auth/package.json +29 -0
  23. package/templates/monorepo/auth/scripts/setup-database.ts +11 -0
  24. package/templates/monorepo/auth/src/auth.ts +47 -0
  25. package/templates/monorepo/auth/src/config/database.ts +15 -0
  26. package/templates/monorepo/auth/src/drizzle/db.ts +8 -0
  27. package/templates/monorepo/auth/src/drizzle/migrations/0000_shared_auth.sql +89 -0
  28. package/templates/monorepo/auth/src/drizzle/migrations/meta/_journal.json +13 -0
  29. package/templates/monorepo/auth/src/drizzle/schema/auth/accounts.ts +7 -0
  30. package/templates/monorepo/auth/src/drizzle/schema/auth/sessions.ts +7 -0
  31. package/templates/monorepo/auth/src/drizzle/schema/auth/verifications.ts +6 -0
  32. package/templates/monorepo/auth/src/drizzle/schema/groups.ts +16 -0
  33. package/templates/monorepo/auth/src/drizzle/schema/index.ts +5 -0
  34. package/templates/monorepo/auth/src/drizzle/schema/users.ts +6 -0
  35. package/templates/monorepo/auth/src/index.ts +1 -0
  36. package/templates/monorepo/auth/src/scim/README.md +6 -0
  37. package/templates/monorepo/auth/tsconfig.json +12 -0
  38. package/templates/monorepo/package.json.template +18 -6
  39. package/templates/monorepo/pnpm-workspace.yaml +1 -0
  40. package/templates/webapp/AGENTS.md +13 -6
  41. package/templates/webapp/README.md +34 -18
  42. package/templates/webapp/agent-skills/access-control.md +301 -0
  43. package/templates/webapp/agent-skills/database.md +1 -1
  44. package/templates/webapp/docker-compose.yml +16 -0
  45. package/templates/webapp/env.example.template +9 -0
  46. package/templates/webapp/next.config.ts +1 -0
  47. package/templates/webapp/package.json.template +8 -4
  48. package/templates/webapp/scripts/seed.ts +87 -36
  49. package/templates/webapp/scripts/setup-database.ts +7 -1
  50. package/templates/webapp/scripts/start.sh +0 -9
  51. package/templates/webapp/src/access/access.manifest.ts +15 -0
  52. package/templates/webapp/src/access/schema.zed +7 -0
  53. package/templates/webapp/src/app/(app)/admin/_lib/PrincipalRoleTable.tsx +113 -0
  54. package/templates/webapp/src/app/(app)/admin/_lib/accessAdmin.ts +85 -0
  55. package/templates/webapp/src/app/(app)/admin/groups/page.tsx +117 -0
  56. package/templates/webapp/src/app/(app)/admin/users/page.tsx +79 -0
  57. package/templates/webapp/src/app/(app)/layout.tsx +16 -2
  58. package/templates/webapp/src/app/(app)/page.tsx +1 -12
  59. package/templates/webapp/src/app/(auth)/auth/signin/page.tsx +2 -5
  60. package/templates/webapp/src/app/(auth)/auth/signup/page.tsx +2 -5
  61. package/templates/webapp/src/config/getEnvConfig.ts +8 -0
  62. package/templates/webapp/src/drizzle/db.ts +3 -4
  63. package/templates/webapp/src/drizzle/migrations/0000_eager_grandmaster.sql +1 -57
  64. package/templates/webapp/src/drizzle/migrations/meta/0000_snapshot.json +1 -347
  65. package/templates/webapp/src/drizzle/schema/index.ts +3 -4
  66. package/templates/webapp/src/lib/auth/index.ts +6 -81
  67. package/templates/webapp/src/server/api/root.ts +4 -1
  68. package/templates/webapp/src/server/api/routers/access.ts +13 -0
  69. package/templates/webapp/src/server/trpc.ts +42 -8
  70. package/templates/webapp/src/services/DatabaseService.ts +4 -5
  71. package/templates/webapp/src/services/access/AppAccessControl.ts +39 -0
  72. package/dist/init-OeK4Yk6_.js.map +0 -1
  73. package/templates/webapp/scripts/create-user.ts +0 -47
  74. package/templates/webapp/src/drizzle/schema/auth/accounts.ts +0 -33
  75. package/templates/webapp/src/drizzle/schema/auth/sessions.ts +0 -25
  76. package/templates/webapp/src/drizzle/schema/auth/users.ts +0 -38
  77. package/templates/webapp/src/drizzle/schema/auth/verifications.ts +0 -19
package/dist/index.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import { program } from "commander";
3
3
  import { execFile, execSync, spawn } from "node:child_process";
4
4
  import path from "node:path";
5
- import { fileURLToPath } from "node:url";
6
5
  import chalk from "chalk";
7
6
  import fs from "fs-extra";
8
7
  import ora from "ora";
8
+ import { fileURLToPath } from "node:url";
9
9
  import { parse } from "yaml";
10
10
  import { randomBytes } from "node:crypto";
11
11
  import inquirer from "inquirer";
@@ -381,6 +381,7 @@ const PROCESSABLE_EXTENSIONS = new Set([
381
381
  ".tf",
382
382
  ".tfvars",
383
383
  ".sh",
384
+ ".zed",
384
385
  ".mjs",
385
386
  ".cjs"
386
387
  ]);
@@ -520,6 +521,21 @@ async function resolvePerceptaVersionsInPackageJson(packageJsonPath, lookupLates
520
521
  };
521
522
  }
522
523
  //#endregion
524
+ //#region src/utils/template-versions.ts
525
+ const FALLBACK_TEMPLATE_VERSION = "1.0.0";
526
+ function readTemplateVersions() {
527
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
528
+ const candidates = [path.resolve(currentDir, "../template-versions.json"), path.resolve(currentDir, "../../template-versions.json")];
529
+ for (const versionsPath of candidates) try {
530
+ const content = fs.readFileSync(versionsPath, "utf-8");
531
+ return JSON.parse(content);
532
+ } catch {}
533
+ return {};
534
+ }
535
+ function getTemplateVersion(templateType) {
536
+ return readTemplateVersions()[templateType] ?? FALLBACK_TEMPLATE_VERSION;
537
+ }
538
+ //#endregion
523
539
  //#region src/commands/create.ts
524
540
  const PACKAGE_MANAGER = "pnpm";
525
541
  /** Paths in copy-paste shell commands (POSIX-style). */
@@ -541,14 +557,14 @@ function runPackageManagerInstall(packageManager, cwd, args = ["install"]) {
541
557
  });
542
558
  }
543
559
  /**
544
- * Runs the `setup` script in the package directory (docker:up + db:setup-and-migrate + db:seed).
560
+ * Runs the monorepo-root `setup` script (docker + access + db + seed).
545
561
  * Uses `pnpm run setup` (not `pnpm setup`) because `pnpm setup` is a pnpm builtin that configures
546
562
  * PNPM_HOME in the user's shell rc — it ignores the package.json script of the same name.
547
563
  */
548
- function runWebappSetup(packageManager, cwd) {
564
+ function runWebappSetup(packageManager, monorepoRoot) {
549
565
  return new Promise((resolve, reject) => {
550
566
  const child = spawn(packageManager, ["run", "setup"], {
551
- cwd,
567
+ cwd: monorepoRoot,
552
568
  stdio: "inherit"
553
569
  });
554
570
  child.on("error", reject);
@@ -630,7 +646,7 @@ function openInBrowser(url) {
630
646
  }
631
647
  }
632
648
  /**
633
- * Post-scaffold orchestration for webapps: run setup (docker + db + seed),
649
+ * Post-scaffold orchestration for webapps: run root setup (docker + access + db + seed),
634
650
  * start the dev server, open the served URL in the user's browser, then hand
635
651
  * control to the dev server until the user exits with Ctrl+C.
636
652
  *
@@ -641,16 +657,16 @@ function openInBrowser(url) {
641
657
  * Callers should only invoke this when install actually succeeded —
642
658
  * starting setup without node_modules will leave an orphan Docker container.
643
659
  */
644
- async function autoRunWebapp(packageDir) {
660
+ async function autoRunWebapp(packageDir, monorepoRoot) {
645
661
  const packageManager = PACKAGE_MANAGER;
646
662
  console.log();
647
- console.log(chalk.bold("Running setup (docker, db, seed)..."));
663
+ console.log(chalk.bold("Running setup (docker, access, db, seed)..."));
648
664
  console.log();
649
665
  try {
650
- await runWebappSetup(packageManager, packageDir);
666
+ await runWebappSetup(packageManager, monorepoRoot);
651
667
  } catch (error) {
652
668
  console.log();
653
- console.log(chalk.yellow("!"), "Setup failed. You can re-run it manually:", chalk.cyan(`cd ${packageDir} && ${packageManager} run setup`));
669
+ console.log(chalk.yellow("!"), "Setup failed. You can re-run it manually:", chalk.cyan(`cd ${monorepoRoot} && ${packageManager} run setup`));
654
670
  console.log(chalk.dim(error.message));
655
671
  return false;
656
672
  }
@@ -680,19 +696,10 @@ async function autoRunWebapp(packageDir) {
680
696
  await closed;
681
697
  return true;
682
698
  }
683
- function readTemplateVersions() {
684
- const versionsPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../template-versions.json");
685
- try {
686
- const content = fs.readFileSync(versionsPath, "utf-8");
687
- return JSON.parse(content);
688
- } catch {
689
- return {};
690
- }
691
- }
692
699
  async function writeMosaicFiles(packageDir, config, projectType) {
693
700
  await writeManifest(packageDir, {
694
701
  templateType: projectType,
695
- templateVersion: readTemplateVersions()[projectType] || "1.0.0",
702
+ templateVersion: getTemplateVersion(projectType),
696
703
  templateCommit: "npm",
697
704
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
698
705
  placeholders: derivePlaceholders(config.name, config.title, config.repoName),
@@ -824,9 +831,9 @@ async function installAtMonorepoRoot(monorepoRoot, installDeps) {
824
831
  * Gated on `installSucceeded` because starting setup without node_modules
825
832
  * leaves an orphan Docker container.
826
833
  */
827
- async function maybeAutoRunWebapp(packageDir, projectType, installSucceeded) {
834
+ async function maybeAutoRunWebapp(packageDir, monorepoRoot, projectType, installSucceeded) {
828
835
  if (!packageDir || projectType !== "webapp" || !installSucceeded) return false;
829
- return autoRunWebapp(packageDir);
836
+ return autoRunWebapp(packageDir, monorepoRoot);
830
837
  }
831
838
  function getProjectTypeLabel(projectType) {
832
839
  switch (projectType) {
@@ -946,7 +953,7 @@ async function createProject(options) {
946
953
  console.log();
947
954
  console.log(chalk.green("✔"), chalk.bold(`Created ${typeLabel} at`), chalk.cyan(path.relative(monorepoRoot, packageDir)));
948
955
  console.log();
949
- if (await maybeAutoRunWebapp(packageDir, answers.projectType, installSucceeded)) return;
956
+ if (await maybeAutoRunWebapp(packageDir, monorepoRoot, answers.projectType, installSucceeded)) return;
950
957
  printNextStepsExisting(answers, packageDir);
951
958
  } else {
952
959
  const isBareMonorepo = answers.projectType === "monorepo";
@@ -986,7 +993,7 @@ async function createProject(options) {
986
993
  console.log(chalk.green("✔"), chalk.bold(isBareMonorepo ? `Created ${typeLabel} at` : "Created monorepo at"), chalk.cyan(monorepoRoot));
987
994
  if (!isBareMonorepo) console.log(chalk.green("✔"), chalk.bold(`Created ${typeLabel} at`), chalk.cyan(`packages/${answers.name}/`));
988
995
  console.log();
989
- if (await maybeAutoRunWebapp(packageDir, answers.projectType, installSucceeded)) return;
996
+ if (await maybeAutoRunWebapp(packageDir, monorepoRoot, answers.projectType, installSucceeded)) return;
990
997
  printNextStepsNew(answers, monorepoRoot);
991
998
  }
992
999
  }
@@ -1006,16 +1013,18 @@ async function resolveCreateCwd(cwdOption) {
1006
1013
  return cwd;
1007
1014
  }
1008
1015
  function printWebappNextSteps(params) {
1009
- const { pm, answers, variant, monorepoRelativePath, packageRelativePath } = params;
1016
+ const { pm, answers, variant, monorepoRelativePath, packageRelativePathFromRoot } = params;
1010
1017
  const repoRel = shPath(monorepoRelativePath) || ".";
1011
- const pkgFromRoot = `packages/${answers.name}`;
1012
- const pnpmSteps = ["pnpm run setup", "pnpm dev"];
1018
+ const pkgFromRoot = shPath(packageRelativePathFromRoot ?? `packages/${answers.name}`) || ".";
1019
+ const setupStep = "pnpm run setup";
1020
+ const devStep = "pnpm dev";
1013
1021
  if (variant === "new") {
1014
1022
  const oneLinerParts = [];
1015
1023
  if (repoRel !== ".") oneLinerParts.push(`cd ${repoRel}`);
1016
1024
  if (!answers.installDeps) oneLinerParts.push(`${pm} install`);
1025
+ oneLinerParts.push(setupStep);
1017
1026
  oneLinerParts.push(`cd ${pkgFromRoot}`);
1018
- oneLinerParts.push(...pnpmSteps);
1027
+ oneLinerParts.push(devStep);
1019
1028
  console.log(chalk.bold("Copy-paste (from your current directory):"));
1020
1029
  console.log();
1021
1030
  console.log(chalk.cyan(` ${oneLinerParts.join(" && ")}`));
@@ -1025,17 +1034,15 @@ function printWebappNextSteps(params) {
1025
1034
  let step = 1;
1026
1035
  if (repoRel !== ".") console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);
1027
1036
  if (!answers.installDeps) console.log(chalk.dim(` ${step++}.`), `${pm} install`);
1037
+ console.log(chalk.dim(` ${step++}.`), setupStep);
1028
1038
  console.log(chalk.dim(` ${step++}.`), `cd ${pkgFromRoot}`);
1029
- for (const cmd of pnpmSteps) console.log(chalk.dim(` ${step++}.`), cmd);
1039
+ console.log(chalk.dim(` ${step++}.`), devStep);
1030
1040
  return;
1031
1041
  }
1032
- const pkgRel = shPath(packageRelativePath ?? ".") || ".";
1033
1042
  const oneLinerParts = [];
1034
- if (!answers.installDeps) {
1035
- if (repoRel !== ".") oneLinerParts.push(`cd ${repoRel}`);
1036
- oneLinerParts.push(`${pm} install`, `cd ${pkgFromRoot}`);
1037
- } else if (pkgRel !== ".") oneLinerParts.push(`cd ${pkgRel}`);
1038
- oneLinerParts.push(...pnpmSteps);
1043
+ if (repoRel !== ".") oneLinerParts.push(`cd ${repoRel}`);
1044
+ if (!answers.installDeps) oneLinerParts.push(`${pm} install`);
1045
+ oneLinerParts.push(setupStep, `cd ${pkgFromRoot}`, devStep);
1039
1046
  console.log(chalk.bold("Copy-paste (from your current directory):"));
1040
1047
  console.log();
1041
1048
  console.log(chalk.cyan(` ${oneLinerParts.join(" && ")}`));
@@ -1043,12 +1050,11 @@ function printWebappNextSteps(params) {
1043
1050
  console.log(chalk.bold("Or step by step:"));
1044
1051
  console.log();
1045
1052
  let step = 1;
1046
- if (!answers.installDeps) {
1047
- if (repoRel !== ".") console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);
1048
- console.log(chalk.dim(` ${step++}.`), `${pm} install`);
1049
- console.log(chalk.dim(` ${step++}.`), `cd ${pkgFromRoot}`);
1050
- } else if (pkgRel !== ".") console.log(chalk.dim(` ${step++}.`), `cd ${pkgRel}`);
1051
- for (const cmd of pnpmSteps) console.log(chalk.dim(` ${step++}.`), cmd);
1053
+ if (repoRel !== ".") console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);
1054
+ if (!answers.installDeps) console.log(chalk.dim(` ${step++}.`), `${pm} install`);
1055
+ console.log(chalk.dim(` ${step++}.`), setupStep);
1056
+ console.log(chalk.dim(` ${step++}.`), `cd ${pkgFromRoot}`);
1057
+ console.log(chalk.dim(` ${step++}.`), devStep);
1052
1058
  }
1053
1059
  async function warnIfMissingRootNpmrc(rootDir) {
1054
1060
  const rootNpmrc = path.join(rootDir, ".npmrc");
@@ -1106,6 +1112,7 @@ function printNextStepsExisting(answers, packageDir) {
1106
1112
  const packageRelativePath = path.relative(process.cwd(), packageDir) || ".";
1107
1113
  const monorepoRoot = path.dirname(path.dirname(packageDir));
1108
1114
  const monorepoRelativePath = path.relative(process.cwd(), monorepoRoot) || ".";
1115
+ const packageRelativePathFromRoot = path.relative(monorepoRoot, packageDir) || ".";
1109
1116
  console.log("Next steps:");
1110
1117
  console.log();
1111
1118
  switch (answers.projectType) {
@@ -1115,7 +1122,7 @@ function printNextStepsExisting(answers, packageDir) {
1115
1122
  answers,
1116
1123
  variant: "existing",
1117
1124
  monorepoRelativePath,
1118
- packageRelativePath
1125
+ packageRelativePathFromRoot
1119
1126
  });
1120
1127
  break;
1121
1128
  case "library": {
@@ -1139,23 +1146,23 @@ program.name("create").description("Scaffold and manage Mosaic packages").versio
1139
1146
  }.version);
1140
1147
  program.command("create", { isDefault: true }).description("Scaffold a new Mosaic package").option("-t, --type <type>", "Package type: monorepo, webapp, or library").option("--name <name>", "Package/app name").option("--repo-name <name>", "Repository name when creating a new monorepo").option("--cwd <dir>", "Run create as if started from this directory").option("--skip-install", "Skip dependency installation (also skips the auto-run setup + dev + browser)", false).option("-y, --yes", "Skip all prompts and use defaults", false).action(createProject);
1141
1148
  program.command("status").description("Show template sync status for current app").option("--mosaic-template-path <path>", "Path to local mosaic repo checkout").action(async (options) => {
1142
- const { statusCommand } = await import("./status-DC8mvHZj.js");
1149
+ const { statusCommand } = await import("./status-CKe4aKso.js");
1143
1150
  await statusCommand(options);
1144
1151
  });
1145
1152
  program.command("sync").description("Generate downstream sync context (template → app)").option("--mosaic-template-path <path>", "Path to local mosaic repo checkout").option("--to <version>", "Target template version (default: latest)").action(async (options) => {
1146
- const { syncCommand } = await import("./sync-C5Pd32VM.js");
1153
+ const { syncCommand } = await import("./sync-D1vkoofl.js");
1147
1154
  await syncCommand(options);
1148
1155
  });
1149
1156
  program.command("upstream").description("Generate upstream context (app → template)").option("--mosaic-template-path <path>", "Path to local mosaic repo checkout").option("--files <patterns...>", "Specific files to propose upstream").action(async (options) => {
1150
- const { upstreamCommand } = await import("./upstream-F6m8zRBQ.js");
1157
+ const { upstreamCommand } = await import("./upstream-D-LH_1z4.js");
1151
1158
  await upstreamCommand(options);
1152
1159
  });
1153
1160
  program.command("init").description("Add .mosaic-template.json to an existing app").option("-t, --type <type>", "Template type (e.g., webapp, library)").option("--template-version <version>", "Template version to set").action(async (options) => {
1154
- const { initCommand } = await import("./init-OeK4Yk6_.js");
1161
+ const { initCommand } = await import("./init-CtCp7Tv2.js");
1155
1162
  await initCommand(options);
1156
1163
  });
1157
1164
  program.parse();
1158
1165
  //#endregion
1159
- export { readManifest as a, manifestExists as i, isValidProjectType as n, resolveMosaicTemplatePath as o, derivePlaceholders as r, writeManifest as s, VALID_PROJECT_TYPES as t };
1166
+ export { manifestExists as a, writeManifest as c, derivePlaceholders as i, VALID_PROJECT_TYPES as n, readManifest as o, isValidProjectType as r, resolveMosaicTemplatePath as s, getTemplateVersion as t };
1160
1167
 
1161
1168
  //# sourceMappingURL=index.js.map