@percepta/create 3.1.5 → 3.2.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.
- package/dist/index.js +53 -46
- package/dist/index.js.map +1 -1
- package/dist/{init-OeK4Yk6_.js → init-CtCp7Tv2.js} +3 -3
- package/dist/init-CtCp7Tv2.js.map +1 -0
- package/dist/{status-DC8mvHZj.js → status-CKe4aKso.js} +2 -2
- package/dist/{status-DC8mvHZj.js.map → status-CKe4aKso.js.map} +1 -1
- package/dist/{sync-C5Pd32VM.js → sync-D1vkoofl.js} +2 -2
- package/dist/{sync-C5Pd32VM.js.map → sync-D1vkoofl.js.map} +1 -1
- package/dist/{upstream-F6m8zRBQ.js → upstream-D-LH_1z4.js} +2 -2
- package/dist/{upstream-F6m8zRBQ.js.map → upstream-D-LH_1z4.js.map} +1 -1
- package/package.json +2 -2
- package/template-versions.json +1 -1
- package/templates/monorepo/.github/workflows/access-control.yml +38 -0
- package/templates/monorepo/README.md +41 -2
- package/templates/monorepo/access/README.md +39 -0
- package/templates/monorepo/access/bootstrap-grants.yaml.example +9 -0
- package/templates/monorepo/access/dev-grants.yaml.example +19 -0
- package/templates/monorepo/access/dev-groups.yaml.example +8 -0
- package/templates/monorepo/access/reconcile.yaml.example +11 -0
- package/templates/monorepo/auth/README.md +26 -0
- package/templates/monorepo/auth/drizzle.config.ts +13 -0
- package/templates/monorepo/auth/package.json +32 -0
- package/templates/monorepo/auth/scripts/setup-database.ts +57 -0
- package/templates/monorepo/auth/src/auth.ts +77 -0
- package/templates/monorepo/auth/src/config/database.ts +31 -0
- package/templates/monorepo/auth/src/drizzle/db.ts +9 -0
- package/templates/monorepo/auth/src/drizzle/migrations/0000_shared_auth.sql +89 -0
- package/templates/monorepo/auth/src/drizzle/migrations/meta/_journal.json +13 -0
- package/templates/{webapp → monorepo/auth}/src/drizzle/schema/auth/accounts.ts +1 -6
- package/templates/{webapp → monorepo/auth}/src/drizzle/schema/auth/sessions.ts +1 -5
- package/templates/{webapp → monorepo/auth}/src/drizzle/schema/auth/verifications.ts +0 -4
- package/templates/monorepo/auth/src/drizzle/schema/groups.ts +16 -0
- package/templates/monorepo/auth/src/drizzle/schema/index.ts +5 -0
- package/templates/monorepo/auth/src/drizzle/schema/users.ts +6 -0
- package/templates/monorepo/auth/src/index.ts +1 -0
- package/templates/monorepo/auth/src/scim/README.md +6 -0
- package/templates/monorepo/auth/tsconfig.json +12 -0
- package/templates/monorepo/package.json.template +18 -6
- package/templates/monorepo/pnpm-workspace.yaml +1 -0
- package/templates/webapp/AGENTS.md +13 -6
- package/templates/webapp/README.md +34 -18
- package/templates/webapp/agent-skills/access-control.md +301 -0
- package/templates/webapp/agent-skills/database.md +1 -1
- package/templates/webapp/docker-compose.yml +16 -0
- package/templates/webapp/env.example.template +9 -0
- package/templates/webapp/next.config.ts +1 -0
- package/templates/webapp/package.json.template +8 -4
- package/templates/webapp/scripts/seed.ts +87 -36
- package/templates/webapp/scripts/setup-database.ts +7 -1
- package/templates/webapp/scripts/start.sh +0 -9
- package/templates/webapp/src/access/access.manifest.ts +15 -0
- package/templates/webapp/src/access/schema.zed +7 -0
- package/templates/webapp/src/app/(app)/admin/_lib/PrincipalRoleTable.tsx +113 -0
- package/templates/webapp/src/app/(app)/admin/_lib/accessAdmin.ts +85 -0
- package/templates/webapp/src/app/(app)/admin/groups/page.tsx +117 -0
- package/templates/webapp/src/app/(app)/admin/users/page.tsx +79 -0
- package/templates/webapp/src/app/(app)/layout.tsx +16 -2
- package/templates/webapp/src/app/(app)/page.tsx +1 -12
- package/templates/webapp/src/app/(auth)/auth/signin/page.tsx +2 -5
- package/templates/webapp/src/app/(auth)/auth/signup/page.tsx +2 -5
- package/templates/webapp/src/config/getEnvConfig.ts +8 -0
- package/templates/webapp/src/drizzle/db.ts +3 -4
- package/templates/webapp/src/drizzle/migrations/0000_eager_grandmaster.sql +1 -57
- package/templates/webapp/src/drizzle/migrations/meta/0000_snapshot.json +1 -347
- package/templates/webapp/src/drizzle/schema/index.ts +3 -4
- package/templates/webapp/src/lib/auth/index.ts +6 -81
- package/templates/webapp/src/server/api/root.ts +4 -1
- package/templates/webapp/src/server/api/routers/access.ts +13 -0
- package/templates/webapp/src/server/trpc.ts +42 -8
- package/templates/webapp/src/services/DatabaseService.ts +4 -5
- package/templates/webapp/src/services/access/AppAccessControl.ts +39 -0
- package/dist/init-OeK4Yk6_.js.map +0 -1
- package/templates/webapp/scripts/create-user.ts +0 -47
- package/templates/webapp/src/drizzle/schema/auth/users.ts +0 -38
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
|
|
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,
|
|
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,
|
|
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 ${
|
|
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:
|
|
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,
|
|
1016
|
+
const { pm, answers, variant, monorepoRelativePath, packageRelativePathFromRoot } = params;
|
|
1010
1017
|
const repoRel = shPath(monorepoRelativePath) || ".";
|
|
1011
|
-
const pkgFromRoot = `packages/${answers.name}
|
|
1012
|
-
const
|
|
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(
|
|
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
|
-
|
|
1039
|
+
console.log(chalk.dim(` ${step++}.`), devStep);
|
|
1030
1040
|
return;
|
|
1031
1041
|
}
|
|
1032
|
-
const pkgRel = shPath(packageRelativePath ?? ".") || ".";
|
|
1033
1042
|
const oneLinerParts = [];
|
|
1034
|
-
if (
|
|
1035
|
-
|
|
1036
|
-
|
|
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 (
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
1161
|
+
const { initCommand } = await import("./init-CtCp7Tv2.js");
|
|
1155
1162
|
await initCommand(options);
|
|
1156
1163
|
});
|
|
1157
1164
|
program.parse();
|
|
1158
1165
|
//#endregion
|
|
1159
|
-
export {
|
|
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
|