@percepta/create 4.1.7 → 4.1.9
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/{git-ops-BD7JNnal.js → git-ops-BNpQnEc1.js} +1 -1
- package/dist/{git-ops-BD7JNnal.js.map → git-ops-BNpQnEc1.js.map} +1 -1
- package/dist/{github-D3YOEl91.js → github-BOp8VQCY.js} +1 -1
- package/dist/{github-D3YOEl91.js.map → github-BOp8VQCY.js.map} +1 -1
- package/dist/index.js +185 -28
- package/dist/index.js.map +1 -1
- package/dist/{init-BD3EyyLO.js → init-CsuO_mu2.js} +2 -4
- package/dist/{init-BD3EyyLO.js.map → init-CsuO_mu2.js.map} +1 -1
- package/dist/{register-app-DZg-Pmtd.js → register-app-B9vKTkoI.js} +89 -37
- package/dist/register-app-B9vKTkoI.js.map +1 -0
- package/dist/{register-os-blueprint-Cgq1rXzQ.js → register-os-blueprint-Gdyn0pN1.js} +3 -4
- package/dist/{register-os-blueprint-Cgq1rXzQ.js.map → register-os-blueprint-Gdyn0pN1.js.map} +1 -1
- package/dist/{status-K6raTwwu.js → status-BrK9v1yb.js} +3 -3
- package/dist/{status-K6raTwwu.js.map → status-BrK9v1yb.js.map} +1 -1
- package/dist/{sync-Bi958-2W.js → sync-DC5DhIBT.js} +3 -3
- package/dist/{sync-Bi958-2W.js.map → sync-DC5DhIBT.js.map} +1 -1
- package/dist/{upstream-CAraZeSS.js → upstream-PNL6DGtl.js} +3 -3
- package/dist/{upstream-CAraZeSS.js.map → upstream-PNL6DGtl.js.map} +1 -1
- package/package.json +3 -3
- package/template-versions.json +2 -2
- package/templates/infra/os.blueprint.yaml.template +13 -0
- package/templates/monorepo/auth/README.md +2 -2
- package/templates/monorepo/auth/package.json +1 -1
- package/templates/monorepo/auth/src/drizzle/migrations/meta/0000_snapshot.json +547 -0
- package/templates/monorepo/package.json.template +6 -6
- package/templates/monorepo/pnpm-workspace.yaml +9 -1
- package/templates/webapp/AGENTS.md +39 -17
- package/templates/webapp/README.md +58 -59
- package/templates/webapp/agent-skills/access-control.md +13 -12
- package/templates/webapp/agent-skills/database.md +12 -5
- package/templates/webapp/agent-skills/inngest.md +10 -8
- package/templates/webapp/agent-skills/langfuse.md +7 -5
- package/templates/webapp/agent-skills/oneshot.md +15 -13
- package/templates/webapp/next.config.ts +1 -1
- package/templates/webapp/package.json.template +6 -6
- package/templates/webapp/playwright.config.ts +1 -2
- package/templates/webapp/scripts/seed.ts +3 -3
- package/templates/webapp/src/app/(app)/page.tsx +5 -3
- package/templates/webapp/src/app/(auth)/layout.tsx +2 -2
- package/templates/webapp/src/app/(settings)/settings/page.tsx +21 -22
- package/templates/webapp/src/components/FaroProvider.tsx +1 -2
- package/templates/webapp/src/components/Header.tsx +2 -8
- package/templates/webapp/src/components/form/FormItem.tsx +1 -1
- package/templates/webapp/src/drizzle/db.ts +3 -1
- package/templates/webapp/src/drizzle/schema/index.ts +1 -1
- package/templates/webapp/src/drizzle/schema/utils/jsonbFromZod.ts +1 -1
- package/templates/webapp/src/instrumentation.ts +3 -6
- package/templates/webapp/src/lib/auth/index.ts +2 -2
- package/templates/webapp/src/lib/auth-client.ts +3 -2
- package/templates/webapp/src/lib/trpc.ts +1 -1
- package/templates/webapp/src/server/trpc.ts +1 -1
- package/templates/webapp/src/services/DatabaseService.ts +1 -1
- package/templates/webapp/src/services/access/AppAccessControl.ts +1 -3
- package/templates/webapp/src/services/inngest/AppWorkflowService.ts +1 -1
- package/templates/webapp/src/startup-checks.ts +1 -1
- package/templates/webapp/src/styles/globals.css +13 -2
- package/dist/manifest-By1SgOjC.js +0 -59
- package/dist/manifest-By1SgOjC.js.map +0 -1
- package/dist/register-app-DZg-Pmtd.js.map +0 -1
- package/dist/template-versions-CEIP9vhl.js +0 -35
- package/dist/template-versions-CEIP9vhl.js.map +0 -1
- package/dist/validate-dssldJAj.js +0 -14
- package/dist/validate-dssldJAj.js.map +0 -1
|
@@ -48,4 +48,4 @@ function getFileAtTag(repoPath, tag, filePath) {
|
|
|
48
48
|
//#endregion
|
|
49
49
|
export { getTemplateVersionFromTag as i, getLatestTemplateTag as n, getTemplateDiff as r, getFileAtTag as t };
|
|
50
50
|
|
|
51
|
-
//# sourceMappingURL=git-ops-
|
|
51
|
+
//# sourceMappingURL=git-ops-BNpQnEc1.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-ops-
|
|
1
|
+
{"version":3,"file":"git-ops-BNpQnEc1.js","names":[],"sources":["../src/utils/git-ops.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\n\n// Git requires forward slashes for in-repo paths regardless of OS\nfunction toGitPath(p: string): string {\n return p.replace(/\\\\/g, \"/\");\n}\n\nexport function getLatestTemplateTag(\n type: string,\n repoPath: string,\n): string | null {\n try {\n const tags = execFileSync(\n \"git\",\n [\"tag\", \"-l\", `template/${type}/*`, \"--sort=-v:refname\"],\n { cwd: repoPath, encoding: \"utf-8\" },\n ).trim();\n if (!tags) return null;\n return tags.split(\"\\n\")[0] ?? null;\n } catch {\n return null;\n }\n}\n\nexport function getTemplateVersionFromTag(tag: string): string {\n const parts = tag.split(\"/\");\n return parts[parts.length - 1] ?? \"\";\n}\n\nexport function getTemplateDiff(\n repoPath: string,\n templatePath: string,\n fromTag: string,\n toTag: string,\n): string {\n return execFileSync(\n \"git\",\n [\"diff\", `${fromTag}..${toTag}`, \"--\", toGitPath(templatePath)],\n {\n cwd: repoPath,\n encoding: \"utf-8\",\n },\n );\n}\n\nexport function getFileAtTag(\n repoPath: string,\n tag: string,\n filePath: string,\n): string | null {\n try {\n return execFileSync(\"git\", [\"show\", `${tag}:${toGitPath(filePath)}`], {\n cwd: repoPath,\n encoding: \"utf-8\",\n });\n } catch {\n return null;\n }\n}\n"],"mappings":";;AAGA,SAAS,UAAU,GAAmB;CACpC,OAAO,EAAE,QAAQ,OAAO,GAAG;AAC7B;AAEA,SAAgB,qBACd,MACA,UACe;CACf,IAAI;EACF,MAAM,OAAO,aACX,OACA;GAAC;GAAO;GAAM,YAAY,KAAK;GAAK;EAAmB,GACvD;GAAE,KAAK;GAAU,UAAU;EAAQ,CACrC,EAAE,KAAK;EACP,IAAI,CAAC,MAAM,OAAO;EAClB,OAAO,KAAK,MAAM,IAAI,EAAE,MAAM;CAChC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,0BAA0B,KAAqB;CAC7D,MAAM,QAAQ,IAAI,MAAM,GAAG;CAC3B,OAAO,MAAM,MAAM,SAAS,MAAM;AACpC;AAEA,SAAgB,gBACd,UACA,cACA,SACA,OACQ;CACR,OAAO,aACL,OACA;EAAC;EAAQ,GAAG,QAAQ,IAAI;EAAS;EAAM,UAAU,YAAY;CAAC,GAC9D;EACE,KAAK;EACL,UAAU;CACZ,CACF;AACF;AAEA,SAAgB,aACd,UACA,KACA,UACe;CACf,IAAI;EACF,OAAO,aAAa,OAAO,CAAC,QAAQ,GAAG,IAAI,GAAG,UAAU,QAAQ,GAAG,GAAG;GACpE,KAAK;GACL,UAAU;EACZ,CAAC;CACH,QAAQ;EACN,OAAO;CACT;AACF"}
|
|
@@ -144,4 +144,4 @@ function encodePathSegments(filePath) {
|
|
|
144
144
|
//#endregion
|
|
145
145
|
export { createOrUpdateInfraPullRequestFiles as a, createOrUpdateInfraPullRequest as i, INFRA_REPOSITORY as n, resolveGitHubToken as o, createInfraGitHubApi as r, INFRA_BASE_BRANCH as t };
|
|
146
146
|
|
|
147
|
-
//# sourceMappingURL=github-
|
|
147
|
+
//# sourceMappingURL=github-BOp8VQCY.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-D3YOEl91.js","names":[],"sources":["../src/commands/infra/github.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\n\nconst GITHUB_API_BASE_URL = \"https://api.github.com\";\n\nexport const INFRA_REPOSITORY = \"Percepta-Core/infra\";\nexport const INFRA_REPOSITORY_OWNER = \"Percepta-Core\";\nexport const INFRA_BASE_BRANCH = \"main\";\n\nexport interface GitHubFile {\n sha: string;\n content: string;\n}\n\nexport interface GitHubPullRequest {\n number: number;\n url: string;\n}\n\nexport interface InfraGitHubApi {\n getFile(path: string, ref: string): Promise<GitHubFile | null>;\n getBranchSha(branch: string): Promise<string | null>;\n createBranch(branch: string, sha: string): Promise<void>;\n putFile(args: {\n path: string;\n branch: string;\n content: string;\n message: string;\n sha?: string;\n }): Promise<void>;\n findOpenPullRequest(branch: string): Promise<GitHubPullRequest | null>;\n createPullRequest(args: {\n branch: string;\n title: string;\n body: string;\n }): Promise<GitHubPullRequest>;\n}\n\nexport interface InfraPullRequestResult {\n pullRequestUrl: string;\n status: \"created_pr\" | \"updated_pr\";\n}\n\nexport interface InfraPullRequestFile {\n baseFileSha?: string;\n content: string;\n message: string;\n path: string;\n}\n\nexport async function createOrUpdateInfraPullRequest(args: {\n baseFileSha?: string;\n body: string;\n branchName: string;\n content: string;\n github: InfraGitHubApi;\n message: string;\n targetPath: string;\n title: string;\n}): Promise<InfraPullRequestResult> {\n return createOrUpdateInfraPullRequestFiles({\n body: args.body,\n branchName: args.branchName,\n files: [\n {\n baseFileSha: args.baseFileSha,\n content: args.content,\n message: args.message,\n path: args.targetPath,\n },\n ],\n github: args.github,\n title: args.title,\n });\n}\n\nexport async function createOrUpdateInfraPullRequestFiles(args: {\n body: string;\n branchName: string;\n files: InfraPullRequestFile[];\n github: InfraGitHubApi;\n title: string;\n}): Promise<InfraPullRequestResult> {\n const mainSha = await args.github.getBranchSha(INFRA_BASE_BRANCH);\n if (!mainSha) {\n throw new Error(`Could not find ${INFRA_REPOSITORY}@${INFRA_BASE_BRANCH}.`);\n }\n\n if (!(await args.github.getBranchSha(args.branchName))) {\n await args.github.createBranch(args.branchName, mainSha);\n }\n\n const existingPullRequest = await args.github.findOpenPullRequest(\n args.branchName,\n );\n\n for (const file of args.files) {\n const branchFile = await args.github.getFile(file.path, args.branchName);\n if (branchFile?.content !== file.content) {\n await args.github.putFile({\n path: file.path,\n branch: args.branchName,\n content: file.content,\n message: file.message,\n sha: branchFile?.sha ?? file.baseFileSha,\n });\n }\n }\n\n const pullRequest =\n existingPullRequest ??\n (await args.github.createPullRequest({\n branch: args.branchName,\n title: args.title,\n body: args.body,\n }));\n\n return {\n pullRequestUrl: pullRequest.url,\n status: existingPullRequest ? \"updated_pr\" : \"created_pr\",\n };\n}\n\nexport function createInfraGitHubApi(token: string): InfraGitHubApi {\n return {\n async getFile(filePath, ref) {\n const encodedPath = encodePathSegments(filePath);\n const response = await requestGitHub<{\n content?: unknown;\n sha?: unknown;\n type?: unknown;\n }>(\n token,\n \"GET\",\n `/repos/${INFRA_REPOSITORY}/contents/${encodedPath}?ref=${encodeURIComponent(ref)}`,\n );\n if (response == null) return null;\n if (\n typeof response.sha !== \"string\" ||\n typeof response.content !== \"string\" ||\n response.type !== \"file\"\n ) {\n throw new Error(\n `Unexpected GitHub content response for ${filePath} in ${INFRA_REPOSITORY}.`,\n );\n }\n\n return {\n sha: response.sha,\n content: Buffer.from(\n response.content.replace(/\\s/g, \"\"),\n \"base64\",\n ).toString(\"utf-8\"),\n };\n },\n\n async getBranchSha(branch) {\n const response = await requestGitHub<{\n object?: { sha?: unknown };\n }>(token, \"GET\", `/repos/${INFRA_REPOSITORY}/git/ref/heads/${branch}`);\n if (response == null) return null;\n const sha = response.object?.sha;\n if (typeof sha !== \"string\") {\n throw new Error(`Unexpected GitHub ref response for ${branch}.`);\n }\n return sha;\n },\n\n async createBranch(branch, sha) {\n await requestGitHub(\n token,\n \"POST\",\n `/repos/${INFRA_REPOSITORY}/git/refs`,\n {\n ref: `refs/heads/${branch}`,\n sha,\n },\n );\n },\n\n async putFile({ path: filePath, branch, content, message, sha }) {\n const encodedPath = encodePathSegments(filePath);\n await requestGitHub(\n token,\n \"PUT\",\n `/repos/${INFRA_REPOSITORY}/contents/${encodedPath}`,\n {\n branch,\n content: Buffer.from(content, \"utf-8\").toString(\"base64\"),\n message,\n sha,\n },\n );\n },\n\n async findOpenPullRequest(branch) {\n const params = new URLSearchParams({\n base: INFRA_BASE_BRANCH,\n head: `${INFRA_REPOSITORY_OWNER}:${branch}`,\n state: \"open\",\n });\n const response = await requestGitHub<\n Array<{ html_url?: unknown; number?: unknown }>\n >(token, \"GET\", `/repos/${INFRA_REPOSITORY}/pulls?${params}`);\n const pullRequest = response?.[0];\n if (!pullRequest) return null;\n if (\n typeof pullRequest.html_url !== \"string\" ||\n typeof pullRequest.number !== \"number\"\n ) {\n throw new Error(`Unexpected GitHub pull request response.`);\n }\n return {\n number: pullRequest.number,\n url: pullRequest.html_url,\n };\n },\n\n async createPullRequest({ branch, title, body }) {\n const response = await requestGitHub<{\n html_url?: unknown;\n number?: unknown;\n }>(token, \"POST\", `/repos/${INFRA_REPOSITORY}/pulls`, {\n base: INFRA_BASE_BRANCH,\n body,\n head: branch,\n maintainer_can_modify: true,\n title,\n });\n if (\n !response ||\n typeof response.html_url !== \"string\" ||\n typeof response.number !== \"number\"\n ) {\n throw new Error(`Unexpected GitHub pull request response.`);\n }\n return {\n number: response.number,\n url: response.html_url,\n };\n },\n };\n}\n\nexport function resolveGitHubToken(): string {\n const envToken = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;\n if (envToken) return envToken;\n\n try {\n const token = execFileSync(\"gh\", [\"auth\", \"token\"], {\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n if (token) return token;\n } catch {\n // Fall through to the actionable error below.\n }\n\n throw new Error(\n \"GitHub auth is required. Set GITHUB_TOKEN or GH_TOKEN, or run `gh auth login`.\",\n );\n}\n\nasync function requestGitHub<T>(\n token: string,\n method: string,\n path: string,\n body?: unknown,\n): Promise<T | null> {\n const response = await fetch(`${GITHUB_API_BASE_URL}${path}`, {\n method,\n headers: {\n Accept: \"application/vnd.github+json\",\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n if (response.status === 404) return null;\n\n if (!response.ok) {\n const responseText = await response.text();\n throw new Error(\n `GitHub ${method} ${path} failed with ${response.status}: ${responseText}`,\n );\n }\n\n if (response.status === 204) return null;\n return (await response.json()) as T;\n}\n\nfunction encodePathSegments(filePath: string): string {\n return filePath.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"github-BOp8VQCY.js","names":[],"sources":["../src/commands/infra/github.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\n\nconst GITHUB_API_BASE_URL = \"https://api.github.com\";\n\nexport const INFRA_REPOSITORY = \"Percepta-Core/infra\";\nexport const INFRA_REPOSITORY_OWNER = \"Percepta-Core\";\nexport const INFRA_BASE_BRANCH = \"main\";\n\nexport interface GitHubFile {\n sha: string;\n content: string;\n}\n\nexport interface GitHubPullRequest {\n number: number;\n url: string;\n}\n\nexport interface InfraGitHubApi {\n getFile(path: string, ref: string): Promise<GitHubFile | null>;\n getBranchSha(branch: string): Promise<string | null>;\n createBranch(branch: string, sha: string): Promise<void>;\n putFile(args: {\n path: string;\n branch: string;\n content: string;\n message: string;\n sha?: string;\n }): Promise<void>;\n findOpenPullRequest(branch: string): Promise<GitHubPullRequest | null>;\n createPullRequest(args: {\n branch: string;\n title: string;\n body: string;\n }): Promise<GitHubPullRequest>;\n}\n\nexport interface InfraPullRequestResult {\n pullRequestUrl: string;\n status: \"created_pr\" | \"updated_pr\";\n}\n\nexport interface InfraPullRequestFile {\n baseFileSha?: string;\n content: string;\n message: string;\n path: string;\n}\n\nexport async function createOrUpdateInfraPullRequest(args: {\n baseFileSha?: string;\n body: string;\n branchName: string;\n content: string;\n github: InfraGitHubApi;\n message: string;\n targetPath: string;\n title: string;\n}): Promise<InfraPullRequestResult> {\n return createOrUpdateInfraPullRequestFiles({\n body: args.body,\n branchName: args.branchName,\n files: [\n {\n baseFileSha: args.baseFileSha,\n content: args.content,\n message: args.message,\n path: args.targetPath,\n },\n ],\n github: args.github,\n title: args.title,\n });\n}\n\nexport async function createOrUpdateInfraPullRequestFiles(args: {\n body: string;\n branchName: string;\n files: InfraPullRequestFile[];\n github: InfraGitHubApi;\n title: string;\n}): Promise<InfraPullRequestResult> {\n const mainSha = await args.github.getBranchSha(INFRA_BASE_BRANCH);\n if (!mainSha) {\n throw new Error(`Could not find ${INFRA_REPOSITORY}@${INFRA_BASE_BRANCH}.`);\n }\n\n if (!(await args.github.getBranchSha(args.branchName))) {\n await args.github.createBranch(args.branchName, mainSha);\n }\n\n const existingPullRequest = await args.github.findOpenPullRequest(\n args.branchName,\n );\n\n for (const file of args.files) {\n const branchFile = await args.github.getFile(file.path, args.branchName);\n if (branchFile?.content !== file.content) {\n await args.github.putFile({\n path: file.path,\n branch: args.branchName,\n content: file.content,\n message: file.message,\n sha: branchFile?.sha ?? file.baseFileSha,\n });\n }\n }\n\n const pullRequest =\n existingPullRequest ??\n (await args.github.createPullRequest({\n branch: args.branchName,\n title: args.title,\n body: args.body,\n }));\n\n return {\n pullRequestUrl: pullRequest.url,\n status: existingPullRequest ? \"updated_pr\" : \"created_pr\",\n };\n}\n\nexport function createInfraGitHubApi(token: string): InfraGitHubApi {\n return {\n async getFile(filePath, ref) {\n const encodedPath = encodePathSegments(filePath);\n const response = await requestGitHub<{\n content?: unknown;\n sha?: unknown;\n type?: unknown;\n }>(\n token,\n \"GET\",\n `/repos/${INFRA_REPOSITORY}/contents/${encodedPath}?ref=${encodeURIComponent(ref)}`,\n );\n if (response == null) return null;\n if (\n typeof response.sha !== \"string\" ||\n typeof response.content !== \"string\" ||\n response.type !== \"file\"\n ) {\n throw new Error(\n `Unexpected GitHub content response for ${filePath} in ${INFRA_REPOSITORY}.`,\n );\n }\n\n return {\n sha: response.sha,\n content: Buffer.from(\n response.content.replace(/\\s/g, \"\"),\n \"base64\",\n ).toString(\"utf-8\"),\n };\n },\n\n async getBranchSha(branch) {\n const response = await requestGitHub<{\n object?: { sha?: unknown };\n }>(token, \"GET\", `/repos/${INFRA_REPOSITORY}/git/ref/heads/${branch}`);\n if (response == null) return null;\n const sha = response.object?.sha;\n if (typeof sha !== \"string\") {\n throw new Error(`Unexpected GitHub ref response for ${branch}.`);\n }\n return sha;\n },\n\n async createBranch(branch, sha) {\n await requestGitHub(\n token,\n \"POST\",\n `/repos/${INFRA_REPOSITORY}/git/refs`,\n {\n ref: `refs/heads/${branch}`,\n sha,\n },\n );\n },\n\n async putFile({ path: filePath, branch, content, message, sha }) {\n const encodedPath = encodePathSegments(filePath);\n await requestGitHub(\n token,\n \"PUT\",\n `/repos/${INFRA_REPOSITORY}/contents/${encodedPath}`,\n {\n branch,\n content: Buffer.from(content, \"utf-8\").toString(\"base64\"),\n message,\n sha,\n },\n );\n },\n\n async findOpenPullRequest(branch) {\n const params = new URLSearchParams({\n base: INFRA_BASE_BRANCH,\n head: `${INFRA_REPOSITORY_OWNER}:${branch}`,\n state: \"open\",\n });\n const response = await requestGitHub<\n Array<{ html_url?: unknown; number?: unknown }>\n >(token, \"GET\", `/repos/${INFRA_REPOSITORY}/pulls?${params}`);\n const pullRequest = response?.[0];\n if (!pullRequest) return null;\n if (\n typeof pullRequest.html_url !== \"string\" ||\n typeof pullRequest.number !== \"number\"\n ) {\n throw new Error(`Unexpected GitHub pull request response.`);\n }\n return {\n number: pullRequest.number,\n url: pullRequest.html_url,\n };\n },\n\n async createPullRequest({ branch, title, body }) {\n const response = await requestGitHub<{\n html_url?: unknown;\n number?: unknown;\n }>(token, \"POST\", `/repos/${INFRA_REPOSITORY}/pulls`, {\n base: INFRA_BASE_BRANCH,\n body,\n head: branch,\n maintainer_can_modify: true,\n title,\n });\n if (\n !response ||\n typeof response.html_url !== \"string\" ||\n typeof response.number !== \"number\"\n ) {\n throw new Error(`Unexpected GitHub pull request response.`);\n }\n return {\n number: response.number,\n url: response.html_url,\n };\n },\n };\n}\n\nexport function resolveGitHubToken(): string {\n const envToken = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;\n if (envToken) return envToken;\n\n try {\n const token = execFileSync(\"gh\", [\"auth\", \"token\"], {\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n if (token) return token;\n } catch {\n // Fall through to the actionable error below.\n }\n\n throw new Error(\n \"GitHub auth is required. Set GITHUB_TOKEN or GH_TOKEN, or run `gh auth login`.\",\n );\n}\n\nasync function requestGitHub<T>(\n token: string,\n method: string,\n path: string,\n body?: unknown,\n): Promise<T | null> {\n const response = await fetch(`${GITHUB_API_BASE_URL}${path}`, {\n method,\n headers: {\n Accept: \"application/vnd.github+json\",\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n if (response.status === 404) return null;\n\n if (!response.ok) {\n const responseText = await response.text();\n throw new Error(\n `GitHub ${method} ${path} failed with ${response.status}: ${responseText}`,\n );\n }\n\n if (response.status === 204) return null;\n return (await response.json()) as T;\n}\n\nfunction encodePathSegments(filePath: string): string {\n return filePath.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as toTitleCase, n as toKebabCase, r as toSnakeCase, t as getTemplateVersion } from "./template-versions-CEIP9vhl.js";
|
|
3
|
-
import { a as writeManifest, c as isValidMosaicDesignTheme, o as DEFAULT_MOSAIC_DESIGN_THEME, s as VALID_MOSAIC_DESIGN_THEMES, t as derivePlaceholders } from "./manifest-By1SgOjC.js";
|
|
4
|
-
import { t as validateProjectName } from "./validate-dssldJAj.js";
|
|
5
2
|
import { program } from "commander";
|
|
6
3
|
import { execSync, spawn } from "node:child_process";
|
|
7
4
|
import path from "node:path";
|
|
@@ -12,6 +9,21 @@ import { fileURLToPath } from "node:url";
|
|
|
12
9
|
import { parse } from "yaml";
|
|
13
10
|
import { randomBytes } from "node:crypto";
|
|
14
11
|
import inquirer from "inquirer";
|
|
12
|
+
import validateNpmPackageName from "validate-npm-package-name";
|
|
13
|
+
//#region src/utils/case-converters.ts
|
|
14
|
+
/** Lowercase, hyphenated, npm-package-name-safe form: "My Cool App" → "my-cool-app". */
|
|
15
|
+
function toKebabCase(str) {
|
|
16
|
+
return str.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
17
|
+
}
|
|
18
|
+
/** Display form derived from a kebab-case name: "my-cool-app" → "My Cool App". */
|
|
19
|
+
function toTitleCase(str) {
|
|
20
|
+
return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
21
|
+
}
|
|
22
|
+
/** Identifier form for env vars and DB names: "my-cool-app" → "my_cool_app". */
|
|
23
|
+
function toSnakeCase(str) {
|
|
24
|
+
return str.replace(/-/g, "_");
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
15
27
|
//#region src/utils/copy-template.ts
|
|
16
28
|
const __filename = fileURLToPath(import.meta.url);
|
|
17
29
|
const __dirname = path.dirname(__filename);
|
|
@@ -45,7 +57,8 @@ function shouldSkip(src) {
|
|
|
45
57
|
return false;
|
|
46
58
|
}
|
|
47
59
|
function getTemplateDir(templateType) {
|
|
48
|
-
|
|
60
|
+
const candidates = [path.resolve(__dirname, "../templates", templateType), path.resolve(__dirname, "../../templates", templateType)];
|
|
61
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) ?? candidates[0];
|
|
49
62
|
}
|
|
50
63
|
async function copyTemplate(targetDir, templateType) {
|
|
51
64
|
const templateDir = getTemplateDir(templateType);
|
|
@@ -67,6 +80,17 @@ async function copyTemplate(targetDir, templateType) {
|
|
|
67
80
|
}
|
|
68
81
|
}
|
|
69
82
|
//#endregion
|
|
83
|
+
//#region src/utils/design-theme.ts
|
|
84
|
+
const VALID_MOSAIC_DESIGN_THEMES = [
|
|
85
|
+
"paper",
|
|
86
|
+
"modern",
|
|
87
|
+
"dense"
|
|
88
|
+
];
|
|
89
|
+
const DEFAULT_MOSAIC_DESIGN_THEME = "modern";
|
|
90
|
+
function isValidMosaicDesignTheme(value) {
|
|
91
|
+
return typeof value === "string" && VALID_MOSAIC_DESIGN_THEMES.includes(value);
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
70
94
|
//#region src/utils/detect-monorepo.ts
|
|
71
95
|
const NOT_FOUND = {
|
|
72
96
|
found: false,
|
|
@@ -125,6 +149,49 @@ async function generateEnvLocal(packageDir) {
|
|
|
125
149
|
}
|
|
126
150
|
}
|
|
127
151
|
//#endregion
|
|
152
|
+
//#region src/utils/manifest.ts
|
|
153
|
+
const MANIFEST_FILENAME = ".mosaic-template.json";
|
|
154
|
+
function getManifestPath(dir) {
|
|
155
|
+
return path.join(dir, MANIFEST_FILENAME);
|
|
156
|
+
}
|
|
157
|
+
async function readManifest(dir) {
|
|
158
|
+
const manifestPath = getManifestPath(dir);
|
|
159
|
+
if (!await fs.pathExists(manifestPath)) throw new Error(`No ${MANIFEST_FILENAME} found in ${dir}. Run 'create init' to create one.`);
|
|
160
|
+
const content = await fs.readFile(manifestPath, "utf-8");
|
|
161
|
+
try {
|
|
162
|
+
return JSON.parse(content);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
throw new Error(`Invalid JSON in ${MANIFEST_FILENAME}: ${error.message}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function writeManifest(dir, manifest) {
|
|
168
|
+
const manifestPath = getManifestPath(dir);
|
|
169
|
+
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
170
|
+
}
|
|
171
|
+
async function manifestExists(dir) {
|
|
172
|
+
return fs.pathExists(getManifestPath(dir));
|
|
173
|
+
}
|
|
174
|
+
function derivePlaceholders(appName, appTitle, repoName = appName, customerSlug = repoName, designTheme = DEFAULT_MOSAIC_DESIGN_THEME) {
|
|
175
|
+
const nameSnake = appName.replace(/-/g, "_");
|
|
176
|
+
const repoNameSnake = repoName.replace(/-/g, "_");
|
|
177
|
+
return {
|
|
178
|
+
__APP_NAME__: appName,
|
|
179
|
+
__APP_TITLE__: appTitle,
|
|
180
|
+
__DB_NAME__: nameSnake + "_db",
|
|
181
|
+
__APP_NAME_UPPER__: appName.toUpperCase(),
|
|
182
|
+
__APP_NAME_SNAKE__: nameSnake,
|
|
183
|
+
__REPO_NAME__: repoName,
|
|
184
|
+
__REPO_NAME_SNAKE__: repoNameSnake,
|
|
185
|
+
__CUSTOMER_SLUG__: customerSlug,
|
|
186
|
+
__MOSAIC_DESIGN_THEME__: designTheme
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function resolveMosaicTemplatePath(options) {
|
|
190
|
+
if (options.mosaicTemplatePath) return path.resolve(options.mosaicTemplatePath);
|
|
191
|
+
if (process.env.MOSAIC_TEMPLATE_PATH) return path.resolve(process.env.MOSAIC_TEMPLATE_PATH);
|
|
192
|
+
throw new Error("Mosaic repo path required. Use --mosaic-template-path or set MOSAIC_TEMPLATE_PATH.");
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
128
195
|
//#region src/utils/package-metadata.ts
|
|
129
196
|
const FALLBACK_METADATA = {
|
|
130
197
|
name: "@percepta/create",
|
|
@@ -143,6 +210,16 @@ function readCreatePackageMetadata() {
|
|
|
143
210
|
return FALLBACK_METADATA;
|
|
144
211
|
}
|
|
145
212
|
//#endregion
|
|
213
|
+
//#region src/utils/validate.ts
|
|
214
|
+
function validateProjectName(name) {
|
|
215
|
+
const result = validateNpmPackageName(name);
|
|
216
|
+
if (!result.validForNewPackages) return {
|
|
217
|
+
valid: false,
|
|
218
|
+
error: [...result.errors || [], ...result.warnings || []][0] || "Invalid package name"
|
|
219
|
+
};
|
|
220
|
+
return { valid: true };
|
|
221
|
+
}
|
|
222
|
+
//#endregion
|
|
146
223
|
//#region src/utils/prompts.ts
|
|
147
224
|
const VALID_PROJECT_TYPES = [
|
|
148
225
|
"monorepo",
|
|
@@ -441,6 +518,21 @@ async function replacePlaceholders(targetDir, config) {
|
|
|
441
518
|
return stats;
|
|
442
519
|
}
|
|
443
520
|
//#endregion
|
|
521
|
+
//#region src/utils/template-versions.ts
|
|
522
|
+
const FALLBACK_TEMPLATE_VERSION = "1.0.0";
|
|
523
|
+
function readTemplateVersions() {
|
|
524
|
+
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
525
|
+
const candidates = [path.resolve(currentDir, "../template-versions.json"), path.resolve(currentDir, "../../template-versions.json")];
|
|
526
|
+
for (const versionsPath of candidates) try {
|
|
527
|
+
const content = fs.readFileSync(versionsPath, "utf-8");
|
|
528
|
+
return JSON.parse(content);
|
|
529
|
+
} catch {}
|
|
530
|
+
return {};
|
|
531
|
+
}
|
|
532
|
+
function getTemplateVersion(templateType) {
|
|
533
|
+
return readTemplateVersions()[templateType] ?? FALLBACK_TEMPLATE_VERSION;
|
|
534
|
+
}
|
|
535
|
+
//#endregion
|
|
444
536
|
//#region src/utils/workspace-manifest.ts
|
|
445
537
|
const WORKSPACE_MANIFEST_FILENAME = ".mosaic-workspace.json";
|
|
446
538
|
function getWorkspaceManifestPath(rootDir) {
|
|
@@ -473,8 +565,7 @@ async function readWorkspaceManifest(rootDir) {
|
|
|
473
565
|
}
|
|
474
566
|
async function writeWorkspaceManifest(rootDir, manifest) {
|
|
475
567
|
const manifestPath = getWorkspaceManifestPath(rootDir);
|
|
476
|
-
await fs.
|
|
477
|
-
await fs.appendFile(manifestPath, "\n");
|
|
568
|
+
await fs.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
478
569
|
}
|
|
479
570
|
function getCompatibleTemplateVersion(manifest, templateType) {
|
|
480
571
|
return manifest?.compatibleTemplates[templateType] ?? getTemplateVersion(templateType);
|
|
@@ -482,11 +573,14 @@ function getCompatibleTemplateVersion(manifest, templateType) {
|
|
|
482
573
|
//#endregion
|
|
483
574
|
//#region src/commands/create.ts
|
|
484
575
|
const PACKAGE_MANAGER = "pnpm";
|
|
485
|
-
const
|
|
486
|
-
const
|
|
576
|
+
const MAX_PACKAGE_MANAGER_OUTPUT_CHARS = 64e3;
|
|
577
|
+
const MAX_PACKAGE_MANAGER_OUTPUT_LINES = 80;
|
|
487
578
|
var PackageManagerCommandError = class extends Error {
|
|
488
|
-
|
|
579
|
+
command;
|
|
580
|
+
output;
|
|
581
|
+
constructor(message, command, output) {
|
|
489
582
|
super(message);
|
|
583
|
+
this.command = command;
|
|
490
584
|
this.output = output;
|
|
491
585
|
this.name = "PackageManagerCommandError";
|
|
492
586
|
}
|
|
@@ -495,13 +589,14 @@ var PackageManagerCommandError = class extends Error {
|
|
|
495
589
|
function shPath(p) {
|
|
496
590
|
return p.split(path.sep).join("/");
|
|
497
591
|
}
|
|
498
|
-
/** Non-blocking
|
|
499
|
-
function
|
|
592
|
+
/** Non-blocking package-manager command so ora can animate. */
|
|
593
|
+
function runPackageManagerCommand(packageManager, cwd, args) {
|
|
500
594
|
return new Promise((resolve, reject) => {
|
|
501
595
|
let output = "";
|
|
596
|
+
const command = `${packageManager} ${args.join(" ")}`;
|
|
502
597
|
const appendOutput = (chunk) => {
|
|
503
598
|
output += chunk.toString();
|
|
504
|
-
if (output.length >
|
|
599
|
+
if (output.length > MAX_PACKAGE_MANAGER_OUTPUT_CHARS) output = output.slice(-64e3);
|
|
505
600
|
};
|
|
506
601
|
const child = spawn(packageManager, args, {
|
|
507
602
|
cwd,
|
|
@@ -514,23 +609,27 @@ function runPackageManagerInstall(packageManager, cwd, args = ["install"]) {
|
|
|
514
609
|
child.stdout?.on("data", appendOutput);
|
|
515
610
|
child.stderr?.on("data", appendOutput);
|
|
516
611
|
child.on("error", (error) => {
|
|
517
|
-
reject(new PackageManagerCommandError(`${
|
|
612
|
+
reject(new PackageManagerCommandError(`${command} failed: ${error.message}`, command, output));
|
|
518
613
|
});
|
|
519
614
|
child.on("close", (code) => {
|
|
520
615
|
if (code === 0) resolve();
|
|
521
|
-
else reject(new PackageManagerCommandError(`${
|
|
616
|
+
else reject(new PackageManagerCommandError(`${command} exited with code ${code ?? "unknown"}`, command, output));
|
|
522
617
|
});
|
|
523
618
|
});
|
|
524
619
|
}
|
|
525
|
-
|
|
620
|
+
/** Non-blocking install so ora can animate (execSync would block timers). */
|
|
621
|
+
function runPackageManagerInstall(packageManager, cwd, args = ["install"]) {
|
|
622
|
+
return runPackageManagerCommand(packageManager, cwd, args);
|
|
623
|
+
}
|
|
624
|
+
function printPackageManagerFailureOutput(error) {
|
|
526
625
|
if (!(error instanceof PackageManagerCommandError)) return;
|
|
527
626
|
const output = error.output.trim();
|
|
528
627
|
if (!output) return;
|
|
529
628
|
const lines = output.split(/\r?\n/);
|
|
530
|
-
const omitted = Math.max(0, lines.length -
|
|
531
|
-
const visibleLines = lines.slice(-
|
|
629
|
+
const omitted = Math.max(0, lines.length - MAX_PACKAGE_MANAGER_OUTPUT_LINES);
|
|
630
|
+
const visibleLines = lines.slice(-80);
|
|
532
631
|
console.log();
|
|
533
|
-
console.log(chalk.bold(`Last ${visibleLines.length} lines from
|
|
632
|
+
console.log(chalk.bold(`Last ${visibleLines.length} lines from ${error.command}:`));
|
|
534
633
|
if (omitted > 0) console.log(chalk.dim(`... omitted ${omitted} earlier lines ...`));
|
|
535
634
|
console.log(visibleLines.join("\n"));
|
|
536
635
|
}
|
|
@@ -788,7 +887,51 @@ async function installAtMonorepoRoot(monorepoRoot, installDeps) {
|
|
|
788
887
|
return true;
|
|
789
888
|
} catch (error) {
|
|
790
889
|
spinner.warn(`Failed to install dependencies. Run '${PACKAGE_MANAGER} install' from monorepo root.`);
|
|
791
|
-
|
|
890
|
+
printPackageManagerFailureOutput(error);
|
|
891
|
+
return false;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
async function collectGeneratedFormatTargets(args) {
|
|
895
|
+
const targets = [path.relative(args.monorepoRoot, args.packageDir) || "."];
|
|
896
|
+
if (args.projectType === "webapp") {
|
|
897
|
+
const workflowPath = path.join(args.monorepoRoot, ".github", "workflows", `${path.basename(args.packageDir)}-ryvn-release.yaml`);
|
|
898
|
+
if (await fs.pathExists(workflowPath)) targets.push(path.relative(args.monorepoRoot, workflowPath));
|
|
899
|
+
}
|
|
900
|
+
return targets.map(shPath);
|
|
901
|
+
}
|
|
902
|
+
async function runGeneratedProjectChecks(args) {
|
|
903
|
+
if (!args.installSucceeded) return false;
|
|
904
|
+
const formatArgs = args.scope === "workspace" || !args.packageDir ? ["run", "format"] : [
|
|
905
|
+
"exec",
|
|
906
|
+
"oxfmt",
|
|
907
|
+
...await collectGeneratedFormatTargets({
|
|
908
|
+
monorepoRoot: args.monorepoRoot,
|
|
909
|
+
packageDir: args.packageDir,
|
|
910
|
+
projectType: args.projectType
|
|
911
|
+
})
|
|
912
|
+
];
|
|
913
|
+
const lintArgs = args.scope === "workspace" || !args.packageDir ? ["run", "lint"] : [
|
|
914
|
+
"exec",
|
|
915
|
+
"oxlint",
|
|
916
|
+
shPath(path.relative(args.monorepoRoot, args.packageDir) || ".")
|
|
917
|
+
];
|
|
918
|
+
const formatSpinner = ora("Formatting generated files...").start();
|
|
919
|
+
try {
|
|
920
|
+
await runPackageManagerCommand(PACKAGE_MANAGER, args.monorepoRoot, formatArgs);
|
|
921
|
+
formatSpinner.succeed("Formatted generated files");
|
|
922
|
+
} catch (error) {
|
|
923
|
+
formatSpinner.warn("Generated formatting failed");
|
|
924
|
+
printPackageManagerFailureOutput(error);
|
|
925
|
+
return false;
|
|
926
|
+
}
|
|
927
|
+
const lintSpinner = ora("Linting generated files...").start();
|
|
928
|
+
try {
|
|
929
|
+
await runPackageManagerCommand(PACKAGE_MANAGER, args.monorepoRoot, lintArgs);
|
|
930
|
+
lintSpinner.succeed("Linted generated files");
|
|
931
|
+
return true;
|
|
932
|
+
} catch (error) {
|
|
933
|
+
lintSpinner.warn("Generated lint failed");
|
|
934
|
+
printPackageManagerFailureOutput(error);
|
|
792
935
|
return false;
|
|
793
936
|
}
|
|
794
937
|
}
|
|
@@ -976,10 +1119,17 @@ async function createProject(options) {
|
|
|
976
1119
|
});
|
|
977
1120
|
await warnIfMissingRootNpmrc(monorepoRoot);
|
|
978
1121
|
const installSucceeded = await installAtMonorepoRoot(monorepoRoot, answers.installDeps);
|
|
1122
|
+
const checksSucceeded = await runGeneratedProjectChecks({
|
|
1123
|
+
monorepoRoot,
|
|
1124
|
+
packageDir,
|
|
1125
|
+
projectType: answers.projectType,
|
|
1126
|
+
scope: "generated-package",
|
|
1127
|
+
installSucceeded
|
|
1128
|
+
});
|
|
979
1129
|
console.log();
|
|
980
1130
|
console.log(chalk.green("✔"), chalk.bold(`Created ${typeLabel} at`), chalk.cyan(path.relative(monorepoRoot, packageDir)));
|
|
981
1131
|
console.log();
|
|
982
|
-
if (await maybeAutoRunWebapp(packageDir, monorepoRoot, answers.projectType, installSucceeded)) return;
|
|
1132
|
+
if (await maybeAutoRunWebapp(packageDir, monorepoRoot, answers.projectType, installSucceeded && checksSucceeded)) return;
|
|
983
1133
|
printNextStepsExisting(answers, packageDir, !installSucceeded);
|
|
984
1134
|
} else {
|
|
985
1135
|
const isBareMonorepo = answers.projectType === "monorepo";
|
|
@@ -1023,12 +1173,19 @@ async function createProject(options) {
|
|
|
1023
1173
|
templateCommit: getTemplateCommitSource(newWorkspaceManifest)
|
|
1024
1174
|
});
|
|
1025
1175
|
const installSucceeded = await installAtMonorepoRoot(monorepoRoot, answers.installDeps);
|
|
1176
|
+
const checksSucceeded = await runGeneratedProjectChecks({
|
|
1177
|
+
monorepoRoot,
|
|
1178
|
+
packageDir,
|
|
1179
|
+
projectType: answers.projectType,
|
|
1180
|
+
scope: "workspace",
|
|
1181
|
+
installSucceeded
|
|
1182
|
+
});
|
|
1026
1183
|
initGitRepo(monorepoRoot);
|
|
1027
1184
|
console.log();
|
|
1028
1185
|
console.log(chalk.green("✔"), chalk.bold(isBareMonorepo ? `Created ${typeLabel} at` : "Created monorepo at"), chalk.cyan(monorepoRoot));
|
|
1029
1186
|
if (!isBareMonorepo) console.log(chalk.green("✔"), chalk.bold(`Created ${typeLabel} at`), chalk.cyan(`packages/${answers.name}/`));
|
|
1030
1187
|
console.log();
|
|
1031
|
-
if (await maybeAutoRunWebapp(packageDir, monorepoRoot, answers.projectType, installSucceeded)) return;
|
|
1188
|
+
if (await maybeAutoRunWebapp(packageDir, monorepoRoot, answers.projectType, installSucceeded && checksSucceeded)) return;
|
|
1032
1189
|
printNextStepsNew(answers, monorepoRoot, !installSucceeded);
|
|
1033
1190
|
}
|
|
1034
1191
|
}
|
|
@@ -1207,31 +1364,31 @@ program.command("add").description("Add a Mosaic package to the current monorepo
|
|
|
1207
1364
|
});
|
|
1208
1365
|
const infra = program.command("infra").description("Manage Mosaic infra glue");
|
|
1209
1366
|
infra.command("register-os-blueprint").description("Register this customer monorepo's OS blueprint in infra").action(async () => {
|
|
1210
|
-
const { registerOsBlueprintCommand } = await import("./register-os-blueprint-
|
|
1367
|
+
const { registerOsBlueprintCommand } = await import("./register-os-blueprint-Gdyn0pN1.js");
|
|
1211
1368
|
await registerOsBlueprintCommand();
|
|
1212
1369
|
});
|
|
1213
1370
|
infra.command("register-app").description("Register a webapp database in this customer OS blueprint").argument("<app>", "Webapp package name").action(async (appName) => {
|
|
1214
|
-
const { registerAppCommand } = await import("./register-app-
|
|
1371
|
+
const { registerAppCommand } = await import("./register-app-B9vKTkoI.js");
|
|
1215
1372
|
await registerAppCommand(appName);
|
|
1216
1373
|
});
|
|
1217
1374
|
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) => {
|
|
1218
|
-
const { statusCommand } = await import("./status-
|
|
1375
|
+
const { statusCommand } = await import("./status-BrK9v1yb.js");
|
|
1219
1376
|
await statusCommand(options);
|
|
1220
1377
|
});
|
|
1221
1378
|
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) => {
|
|
1222
|
-
const { syncCommand } = await import("./sync-
|
|
1379
|
+
const { syncCommand } = await import("./sync-DC5DhIBT.js");
|
|
1223
1380
|
await syncCommand(options);
|
|
1224
1381
|
});
|
|
1225
1382
|
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) => {
|
|
1226
|
-
const { upstreamCommand } = await import("./upstream-
|
|
1383
|
+
const { upstreamCommand } = await import("./upstream-PNL6DGtl.js");
|
|
1227
1384
|
await upstreamCommand(options);
|
|
1228
1385
|
});
|
|
1229
1386
|
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) => {
|
|
1230
|
-
const { initCommand } = await import("./init-
|
|
1387
|
+
const { initCommand } = await import("./init-CsuO_mu2.js");
|
|
1231
1388
|
await initCommand(options);
|
|
1232
1389
|
});
|
|
1233
1390
|
program.parse();
|
|
1234
1391
|
//#endregion
|
|
1235
|
-
export { detectMonorepo as i,
|
|
1392
|
+
export { validateProjectName as a, readManifest as c, detectMonorepo as d, toKebabCase as f, isValidProjectType as i, resolveMosaicTemplatePath as l, toTitleCase as m, getTemplateVersion as n, derivePlaceholders as o, toSnakeCase as p, VALID_PROJECT_TYPES as r, manifestExists as s, readWorkspaceManifest as t, writeManifest as u };
|
|
1236
1393
|
|
|
1237
1394
|
//# sourceMappingURL=index.js.map
|