@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
|
@@ -40,10 +40,7 @@ export default function Header({
|
|
|
40
40
|
return (
|
|
41
41
|
<header className="app-header">
|
|
42
42
|
<nav aria-label="Primary navigation" className="app-header-nav">
|
|
43
|
-
<Link
|
|
44
|
-
className="app-header-brand"
|
|
45
|
-
href="/"
|
|
46
|
-
>
|
|
43
|
+
<Link className="app-header-brand" href="/">
|
|
47
44
|
<span className="app-header-mark" aria-hidden={true}>
|
|
48
45
|
{appInitial}
|
|
49
46
|
</span>
|
|
@@ -66,10 +63,7 @@ export default function Header({
|
|
|
66
63
|
</div>
|
|
67
64
|
<DropdownMenu>
|
|
68
65
|
<DropdownMenuTrigger asChild={true}>
|
|
69
|
-
<button
|
|
70
|
-
className="app-account-button"
|
|
71
|
-
aria-label="Open account menu"
|
|
72
|
-
>
|
|
66
|
+
<button className="app-account-button" aria-label="Open account menu">
|
|
73
67
|
<div className="app-account-text">
|
|
74
68
|
<p className="text-sm font-medium text-foreground">
|
|
75
69
|
{user.name || "User"}
|
|
@@ -2,7 +2,7 @@ import { Label } from "@percepta/design";
|
|
|
2
2
|
import { Slot } from "@radix-ui/react-slot";
|
|
3
3
|
import { compact } from "lodash-es";
|
|
4
4
|
import React, { useId } from "react";
|
|
5
|
-
import {
|
|
5
|
+
import type { ControllerFieldState } from "react-hook-form";
|
|
6
6
|
import { cn } from "../../utils/cn";
|
|
7
7
|
|
|
8
8
|
interface FormItemProps extends React.ComponentProps<"div"> {
|
|
@@ -8,7 +8,9 @@ export const { client, db } = createDb();
|
|
|
8
8
|
function createDb(): { client: Pool; db: NodePgDatabase } {
|
|
9
9
|
const { DATABASE_URL: databaseUrl, NODE_ENV: nodeEnv } = getEnvConfig();
|
|
10
10
|
const pool = createPgPool(
|
|
11
|
-
readDatabaseConfig({
|
|
11
|
+
readDatabaseConfig({
|
|
12
|
+
env: { DATABASE_URL: databaseUrl, NODE_ENV: nodeEnv },
|
|
13
|
+
}),
|
|
12
14
|
);
|
|
13
15
|
|
|
14
16
|
return { client: pool, db: drizzle(pool) };
|
|
@@ -9,14 +9,11 @@ import { getLogger } from "./services/logger/AppLogger";
|
|
|
9
9
|
type SpanProcessor = tracing.SpanProcessor;
|
|
10
10
|
|
|
11
11
|
function setDefaultOpenTelemetryEnv(): void {
|
|
12
|
-
const {
|
|
13
|
-
|
|
14
|
-
NODE_ENV: nodeEnv,
|
|
15
|
-
} = getEnvConfig();
|
|
12
|
+
const { DEPLOYMENT_ENVIRONMENT: deploymentEnvironment, NODE_ENV: nodeEnv } =
|
|
13
|
+
getEnvConfig();
|
|
16
14
|
|
|
17
15
|
process.env.OTEL_SERVICE_NAME ??= "__APP_NAME__";
|
|
18
|
-
process.env.OTEL_RESOURCE_ATTRIBUTES ??=
|
|
19
|
-
`deployment.environment=${deploymentEnvironment ?? nodeEnv}`;
|
|
16
|
+
process.env.OTEL_RESOURCE_ATTRIBUTES ??= `deployment.environment=${deploymentEnvironment ?? nodeEnv}`;
|
|
20
17
|
}
|
|
21
18
|
|
|
22
19
|
function getOtlpTracesEndpoint(): string | undefined {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { auth } from "@
|
|
1
|
+
import { auth } from "@__REPO_NAME__/auth";
|
|
2
2
|
import { headers } from "next/headers";
|
|
3
3
|
|
|
4
|
-
export { auth, type BetterAuthSession } from "@
|
|
4
|
+
export { auth, type BetterAuthSession } from "@__REPO_NAME__/auth";
|
|
5
5
|
|
|
6
6
|
export async function getServerSession() {
|
|
7
7
|
return auth.api.getSession({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BetterAuthClientOptions } from "better-auth";
|
|
1
|
+
import type { BetterAuthClientOptions } from "better-auth";
|
|
2
2
|
import { adminClient } from "better-auth/client/plugins";
|
|
3
3
|
import { createAuthClient } from "better-auth/react";
|
|
4
4
|
|
|
@@ -6,4 +6,5 @@ const adminPlugin: ReturnType<typeof adminClient> = adminClient();
|
|
|
6
6
|
const options = {
|
|
7
7
|
plugins: [adminPlugin],
|
|
8
8
|
} satisfies BetterAuthClientOptions;
|
|
9
|
-
export const authClient: ReturnType<typeof createAuthClient<typeof options>> =
|
|
9
|
+
export const authClient: ReturnType<typeof createAuthClient<typeof options>> =
|
|
10
|
+
createAuthClient(options);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createTRPCClient, httpBatchLink } from "@trpc/client";
|
|
2
2
|
import { createTRPCContext } from "@trpc/tanstack-react-query";
|
|
3
3
|
import superjson from "superjson";
|
|
4
|
-
import {
|
|
4
|
+
import type { AppRouter } from "../server/api/root";
|
|
5
5
|
|
|
6
6
|
export const trpcClient = createTRPCClient<AppRouter>({
|
|
7
7
|
links: [
|
|
@@ -4,11 +4,11 @@ import { TRPCError, initTRPC } from "@trpc/server";
|
|
|
4
4
|
import superjson from "superjson";
|
|
5
5
|
import { accessManifest } from "../access/access.manifest";
|
|
6
6
|
import { type BetterAuthSession, getServerSession } from "../lib/auth";
|
|
7
|
-
import { AuthContextService } from "../services/AuthContextService";
|
|
8
7
|
import {
|
|
9
8
|
getAccessControl,
|
|
10
9
|
toUserSubject,
|
|
11
10
|
} from "../services/access/AppAccessControl";
|
|
11
|
+
import { AuthContextService } from "../services/AuthContextService";
|
|
12
12
|
import { getTracer } from "../services/logger/AppLogger";
|
|
13
13
|
|
|
14
14
|
export interface Context {
|
|
@@ -43,9 +43,7 @@ export function canManageDefaultRoles(userId: string): Promise<boolean> {
|
|
|
43
43
|
return appAccessRuntime.canManageDefaultRoles(userId);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
export function canManageDefaultRolesStrong(
|
|
47
|
-
userId: string,
|
|
48
|
-
): Promise<boolean> {
|
|
46
|
+
export function canManageDefaultRolesStrong(userId: string): Promise<boolean> {
|
|
49
47
|
return appAccessRuntime.canManageDefaultRolesStrong(userId);
|
|
50
48
|
}
|
|
51
49
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { ExampleEventPayload } from "./events/payloads/ExampleEventPayload";
|
|
1
2
|
import { type AppInngest, InngestService } from "./InngestService";
|
|
2
|
-
import { type ExampleEventPayload } from "./events/payloads/ExampleEventPayload";
|
|
3
3
|
|
|
4
4
|
type AppWorkflowClient = Pick<AppInngest, "send">;
|
|
5
5
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { client as authClient } from "@
|
|
1
|
+
import { client as authClient } from "@__REPO_NAME__/auth/db";
|
|
2
2
|
import { getEnvConfig } from "./config/getEnvConfig";
|
|
3
3
|
import { client } from "./drizzle/db";
|
|
4
4
|
import { getLogger } from "./services/logger/AppLogger";
|
|
@@ -204,7 +204,9 @@ body {
|
|
|
204
204
|
border-radius: var(--radius-lg);
|
|
205
205
|
padding: 0.25rem 0.375rem 0.25rem 0.75rem;
|
|
206
206
|
color: var(--foreground);
|
|
207
|
-
transition:
|
|
207
|
+
transition:
|
|
208
|
+
background 150ms ease,
|
|
209
|
+
color 150ms ease;
|
|
208
210
|
}
|
|
209
211
|
|
|
210
212
|
.app-account-text {
|
|
@@ -423,7 +425,16 @@ body {
|
|
|
423
425
|
height: 2.25rem;
|
|
424
426
|
border-bottom: 2px solid var(--primary);
|
|
425
427
|
background:
|
|
426
|
-
linear-gradient(
|
|
428
|
+
linear-gradient(
|
|
429
|
+
135deg,
|
|
430
|
+
transparent 14%,
|
|
431
|
+
var(--accent) 15% 18%,
|
|
432
|
+
transparent 19% 32%,
|
|
433
|
+
var(--accent) 33% 36%,
|
|
434
|
+
transparent 37% 52%,
|
|
435
|
+
var(--accent) 53% 56%,
|
|
436
|
+
transparent 57%
|
|
437
|
+
),
|
|
427
438
|
linear-gradient(to top, var(--accent), transparent 68%);
|
|
428
439
|
}
|
|
429
440
|
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import fs from "fs-extra";
|
|
3
|
-
//#region src/utils/design-theme.ts
|
|
4
|
-
const VALID_MOSAIC_DESIGN_THEMES = [
|
|
5
|
-
"paper",
|
|
6
|
-
"modern",
|
|
7
|
-
"dense"
|
|
8
|
-
];
|
|
9
|
-
const DEFAULT_MOSAIC_DESIGN_THEME = "modern";
|
|
10
|
-
function isValidMosaicDesignTheme(value) {
|
|
11
|
-
return typeof value === "string" && VALID_MOSAIC_DESIGN_THEMES.includes(value);
|
|
12
|
-
}
|
|
13
|
-
//#endregion
|
|
14
|
-
//#region src/utils/manifest.ts
|
|
15
|
-
const MANIFEST_FILENAME = ".mosaic-template.json";
|
|
16
|
-
function getManifestPath(dir) {
|
|
17
|
-
return path.join(dir, MANIFEST_FILENAME);
|
|
18
|
-
}
|
|
19
|
-
async function readManifest(dir) {
|
|
20
|
-
const manifestPath = getManifestPath(dir);
|
|
21
|
-
if (!await fs.pathExists(manifestPath)) throw new Error(`No ${MANIFEST_FILENAME} found in ${dir}. Run 'create init' to create one.`);
|
|
22
|
-
const content = await fs.readFile(manifestPath, "utf-8");
|
|
23
|
-
try {
|
|
24
|
-
return JSON.parse(content);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
throw new Error(`Invalid JSON in ${MANIFEST_FILENAME}: ${error.message}`);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
async function writeManifest(dir, manifest) {
|
|
30
|
-
const manifestPath = getManifestPath(dir);
|
|
31
|
-
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
32
|
-
}
|
|
33
|
-
async function manifestExists(dir) {
|
|
34
|
-
return fs.pathExists(getManifestPath(dir));
|
|
35
|
-
}
|
|
36
|
-
function derivePlaceholders(appName, appTitle, repoName = appName, customerSlug = repoName, designTheme = DEFAULT_MOSAIC_DESIGN_THEME) {
|
|
37
|
-
const nameSnake = appName.replace(/-/g, "_");
|
|
38
|
-
const repoNameSnake = repoName.replace(/-/g, "_");
|
|
39
|
-
return {
|
|
40
|
-
__APP_NAME__: appName,
|
|
41
|
-
__APP_TITLE__: appTitle,
|
|
42
|
-
__DB_NAME__: nameSnake + "_db",
|
|
43
|
-
__APP_NAME_UPPER__: appName.toUpperCase(),
|
|
44
|
-
__APP_NAME_SNAKE__: nameSnake,
|
|
45
|
-
__REPO_NAME__: repoName,
|
|
46
|
-
__REPO_NAME_SNAKE__: repoNameSnake,
|
|
47
|
-
__CUSTOMER_SLUG__: customerSlug,
|
|
48
|
-
__MOSAIC_DESIGN_THEME__: designTheme
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
function resolveMosaicTemplatePath(options) {
|
|
52
|
-
if (options.mosaicTemplatePath) return path.resolve(options.mosaicTemplatePath);
|
|
53
|
-
if (process.env.MOSAIC_TEMPLATE_PATH) return path.resolve(process.env.MOSAIC_TEMPLATE_PATH);
|
|
54
|
-
throw new Error("Mosaic repo path required. Use --mosaic-template-path or set MOSAIC_TEMPLATE_PATH.");
|
|
55
|
-
}
|
|
56
|
-
//#endregion
|
|
57
|
-
export { writeManifest as a, isValidMosaicDesignTheme as c, resolveMosaicTemplatePath as i, manifestExists as n, DEFAULT_MOSAIC_DESIGN_THEME as o, readManifest as r, VALID_MOSAIC_DESIGN_THEMES as s, derivePlaceholders as t };
|
|
58
|
-
|
|
59
|
-
//# sourceMappingURL=manifest-By1SgOjC.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-By1SgOjC.js","names":[],"sources":["../src/utils/design-theme.ts","../src/utils/manifest.ts"],"sourcesContent":["export const VALID_MOSAIC_DESIGN_THEMES = [\"paper\", \"modern\", \"dense\"] as const;\n\nexport type MosaicDesignTheme = (typeof VALID_MOSAIC_DESIGN_THEMES)[number];\n\nexport const DEFAULT_MOSAIC_DESIGN_THEME: MosaicDesignTheme = \"modern\";\n\nexport function isValidMosaicDesignTheme(\n value: unknown,\n): value is MosaicDesignTheme {\n return (\n typeof value === \"string\" &&\n VALID_MOSAIC_DESIGN_THEMES.includes(value as MosaicDesignTheme)\n );\n}\n","import path from \"node:path\";\nimport fs from \"fs-extra\";\nimport {\n DEFAULT_MOSAIC_DESIGN_THEME,\n type MosaicDesignTheme,\n} from \"./design-theme.js\";\n\nexport interface MosaicManifest {\n templateType: string;\n templateVersion: string;\n templateCommit: string;\n createdAt: string;\n lastSyncedAt?: string;\n placeholders: Record<string, string>;\n source: {\n templatePath: string;\n };\n}\n\nconst MANIFEST_FILENAME = \".mosaic-template.json\";\n\nexport function getManifestPath(dir: string): string {\n return path.join(dir, MANIFEST_FILENAME);\n}\n\nexport async function readManifest(dir: string): Promise<MosaicManifest> {\n const manifestPath = getManifestPath(dir);\n if (!(await fs.pathExists(manifestPath))) {\n throw new Error(\n `No ${MANIFEST_FILENAME} found in ${dir}. Run 'create init' to create one.`,\n );\n }\n const content = await fs.readFile(manifestPath, \"utf-8\");\n try {\n return JSON.parse(content) as MosaicManifest;\n } catch (error) {\n throw new Error(\n `Invalid JSON in ${MANIFEST_FILENAME}: ${(error as Error).message}`,\n );\n }\n}\n\nexport async function writeManifest(\n dir: string,\n manifest: MosaicManifest,\n): Promise<void> {\n const manifestPath = getManifestPath(dir);\n await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + \"\\n\");\n}\n\nexport async function manifestExists(dir: string): Promise<boolean> {\n return fs.pathExists(getManifestPath(dir));\n}\n\nexport function derivePlaceholders(\n appName: string,\n appTitle: string,\n repoName = appName,\n customerSlug = repoName,\n designTheme: MosaicDesignTheme = DEFAULT_MOSAIC_DESIGN_THEME,\n): Record<string, string> {\n const nameSnake = appName.replace(/-/g, \"_\");\n const repoNameSnake = repoName.replace(/-/g, \"_\");\n return {\n __APP_NAME__: appName,\n __APP_TITLE__: appTitle,\n __DB_NAME__: nameSnake + \"_db\",\n __APP_NAME_UPPER__: appName.toUpperCase(),\n __APP_NAME_SNAKE__: nameSnake,\n __REPO_NAME__: repoName,\n __REPO_NAME_SNAKE__: repoNameSnake,\n __CUSTOMER_SLUG__: customerSlug,\n __MOSAIC_DESIGN_THEME__: designTheme,\n };\n}\n\nexport function resolveMosaicTemplatePath(options: {\n mosaicTemplatePath?: string;\n}): string {\n if (options.mosaicTemplatePath)\n return path.resolve(options.mosaicTemplatePath);\n if (process.env.MOSAIC_TEMPLATE_PATH)\n return path.resolve(process.env.MOSAIC_TEMPLATE_PATH);\n throw new Error(\n \"Mosaic repo path required. Use --mosaic-template-path or set MOSAIC_TEMPLATE_PATH.\",\n );\n}\n"],"mappings":";;;AAAA,MAAa,6BAA6B;CAAC;CAAS;CAAU;CAAQ;AAItE,MAAa,8BAAiD;AAE9D,SAAgB,yBACd,OAC4B;AAC5B,QACE,OAAO,UAAU,YACjB,2BAA2B,SAAS,MAA2B;;;;ACQnE,MAAM,oBAAoB;AAE1B,SAAgB,gBAAgB,KAAqB;AACnD,QAAO,KAAK,KAAK,KAAK,kBAAkB;;AAG1C,eAAsB,aAAa,KAAsC;CACvE,MAAM,eAAe,gBAAgB,IAAI;AACzC,KAAI,CAAE,MAAM,GAAG,WAAW,aAAa,CACrC,OAAM,IAAI,MACR,MAAM,kBAAkB,YAAY,IAAI,oCACzC;CAEH,MAAM,UAAU,MAAM,GAAG,SAAS,cAAc,QAAQ;AACxD,KAAI;AACF,SAAO,KAAK,MAAM,QAAQ;UACnB,OAAO;AACd,QAAM,IAAI,MACR,mBAAmB,kBAAkB,IAAK,MAAgB,UAC3D;;;AAIL,eAAsB,cACpB,KACA,UACe;CACf,MAAM,eAAe,gBAAgB,IAAI;AACzC,OAAM,GAAG,UAAU,cAAc,KAAK,UAAU,UAAU,MAAM,EAAE,GAAG,KAAK;;AAG5E,eAAsB,eAAe,KAA+B;AAClE,QAAO,GAAG,WAAW,gBAAgB,IAAI,CAAC;;AAG5C,SAAgB,mBACd,SACA,UACA,WAAW,SACX,eAAe,UACf,cAAiC,6BACT;CACxB,MAAM,YAAY,QAAQ,QAAQ,MAAM,IAAI;CAC5C,MAAM,gBAAgB,SAAS,QAAQ,MAAM,IAAI;AACjD,QAAO;EACL,cAAc;EACd,eAAe;EACf,aAAa,YAAY;EACzB,oBAAoB,QAAQ,aAAa;EACzC,oBAAoB;EACpB,eAAe;EACf,qBAAqB;EACrB,mBAAmB;EACnB,yBAAyB;EAC1B;;AAGH,SAAgB,0BAA0B,SAE/B;AACT,KAAI,QAAQ,mBACV,QAAO,KAAK,QAAQ,QAAQ,mBAAmB;AACjD,KAAI,QAAQ,IAAI,qBACd,QAAO,KAAK,QAAQ,QAAQ,IAAI,qBAAqB;AACvD,OAAM,IAAI,MACR,qFACD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"register-app-DZg-Pmtd.js","names":[],"sources":["../src/commands/infra/register-app.ts"],"sourcesContent":["import path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport { isMap, isSeq, parseDocument } from \"yaml\";\nimport {\n toKebabCase,\n toSnakeCase,\n toTitleCase,\n} from \"../../utils/case-converters.js\";\nimport { detectMonorepo } from \"../../utils/detect-monorepo.js\";\nimport { validateProjectName } from \"../../utils/validate.js\";\nimport { readWorkspaceManifest } from \"../../utils/workspace-manifest.js\";\nimport {\n createInfraGitHubApi,\n createOrUpdateInfraPullRequestFiles,\n INFRA_BASE_BRANCH,\n INFRA_REPOSITORY,\n type InfraGitHubApi,\n type InfraPullRequestFile,\n resolveGitHubToken,\n} from \"./github.js\";\n\nconst OS_POSTGRESQL_TERRAFORM_ALIAS = \"os-postgresql-terraform\";\nconst OS_POSTGRESQL_TERRAFORM_SERVICES = new Set([\n \"os-postgresql-terraform-aws\",\n \"os-postgresql-terraform-azure\",\n]);\nconst OS_BLUEPRINT_INPUT_GROUPS = [\n {\n name: \"general\",\n displayName: \"General\",\n description: \"Shared OS infrastructure settings.\",\n },\n {\n name: \"applications\",\n displayName: \"Applications\",\n description: \"Generated OS webapp settings.\",\n },\n {\n name: \"aws_postgresql\",\n displayName: \"AWS PostgreSQL\",\n description: \"AWS Aurora PostgreSQL settings.\",\n condition: '{{ eq EnvironmentProviderType \"aws\" }}',\n },\n {\n name: \"azure_postgresql\",\n displayName: \"Azure PostgreSQL\",\n description: \"Azure PostgreSQL Flexible Server settings.\",\n condition: '{{ eq EnvironmentProviderType \"azure\" }}',\n },\n];\n\nexport interface RegisterAppResult {\n appName: string;\n blueprintName: string;\n blueprintPath: string;\n branchName: string;\n customerSlug: string;\n pullRequestUrl: string | null;\n repository: typeof INFRA_REPOSITORY;\n status: \"already_registered\" | \"created_pr\" | \"updated_pr\";\n servicePath: string;\n targetPath: string;\n}\n\nexport async function registerApp(\n appNameInput: string,\n args: {\n cwd?: string;\n github?: InfraGitHubApi;\n } = {},\n): Promise<RegisterAppResult> {\n const appName = normalizeAppName(appNameInput);\n const cwd = args.cwd ?? process.cwd();\n const monorepoContext = await detectMonorepo(cwd);\n if (!monorepoContext.found || !monorepoContext.rootDir) {\n throw new Error(\n \"Run this command from a Mosaic customer monorepo with a .mosaic-workspace.json file.\",\n );\n }\n\n const workspaceManifest = await readWorkspaceManifest(\n monorepoContext.rootDir,\n );\n const customerSlug = workspaceManifest?.customerSlug;\n if (!customerSlug) {\n throw new Error(\n \".mosaic-workspace.json is missing customerSlug. Recreate the monorepo with a current @percepta/create.\",\n );\n }\n\n const github = args.github ?? createInfraGitHubApi(resolveGitHubToken());\n const blueprintName = `${customerSlug}-os`;\n const branchName = `blueberry/register-${customerSlug}-${appName}`;\n const blueprintPath = [\n \"ryvn\",\n \"definitions\",\n customerSlug,\n \"blueprints\",\n `${blueprintName}.blueprint.yaml`,\n ].join(\"/\");\n const servicePath = [\n \"ryvn\",\n \"definitions\",\n customerSlug,\n \"services\",\n `${appName}.service.yaml`,\n ].join(\"/\");\n\n const mainBlueprintFile = await github.getFile(\n blueprintPath,\n INFRA_BASE_BRANCH,\n );\n if (!mainBlueprintFile) {\n throw new Error(\n `${blueprintPath} does not exist in ${INFRA_REPOSITORY}. Run \\`pnpm mosaic infra register-os-blueprint\\` and merge that infra PR first.`,\n );\n }\n\n const mainServiceFile = await github.getFile(servicePath, INFRA_BASE_BRANCH);\n const serviceContent =\n mainServiceFile == null\n ? await readLocalServiceDefinition(monorepoContext.rootDir, appName)\n : null;\n const blueprintContent = registerAppInBlueprint(\n mainBlueprintFile.content,\n appName,\n );\n\n const files: InfraPullRequestFile[] = [];\n if (blueprintContent !== mainBlueprintFile.content) {\n files.push({\n baseFileSha: mainBlueprintFile.sha,\n content: blueprintContent,\n message: `Register ${appName} in ${blueprintName}`,\n path: blueprintPath,\n });\n }\n if (serviceContent != null) {\n files.push({\n content: serviceContent,\n message: `Register ${appName} service`,\n path: servicePath,\n });\n }\n\n if (files.length === 0) {\n return {\n appName,\n blueprintName,\n blueprintPath,\n branchName,\n customerSlug,\n pullRequestUrl: null,\n repository: INFRA_REPOSITORY,\n status: \"already_registered\",\n servicePath,\n targetPath: blueprintPath,\n };\n }\n\n const pullRequest = await createOrUpdateInfraPullRequestFiles({\n branchName,\n github,\n files,\n title: `Register ${appName} app`,\n body: [\n `Registers the ${appName} service and deployment in ${blueprintName}.`,\n \"\",\n \"Generated by `mosaic infra register-app`.\",\n ].join(\"\\n\"),\n });\n\n return {\n appName,\n blueprintName,\n blueprintPath,\n branchName,\n customerSlug,\n pullRequestUrl: pullRequest.pullRequestUrl,\n repository: INFRA_REPOSITORY,\n status: pullRequest.status,\n servicePath,\n targetPath: blueprintPath,\n };\n}\n\nexport async function registerAppCommand(appName: string): Promise<void> {\n try {\n const result = await registerApp(appName);\n\n if (result.status === \"already_registered\") {\n console.log(\n chalk.green(\"✔\"),\n `${result.appName} is already registered in ${result.repository} at`,\n chalk.cyan(result.targetPath),\n );\n return;\n }\n\n const verb =\n result.status === \"created_pr\" ? \"Created\" : \"Updated existing\";\n console.log(\n chalk.green(\"✔\"),\n `${verb} infra PR for ${result.appName}:`,\n chalk.cyan(result.pullRequestUrl),\n );\n } catch (error) {\n console.error(chalk.red(\"Error:\"), (error as Error).message);\n process.exit(1);\n }\n}\n\nexport function addAppDatabaseToBlueprint(\n blueprintContent: string,\n appName: string,\n): string {\n return updateBlueprint(blueprintContent, appName, {\n appDatabase: true,\n appInstallation: false,\n appInputs: false,\n });\n}\n\nexport function registerAppInBlueprint(\n blueprintContent: string,\n appName: string,\n): string {\n return updateBlueprint(blueprintContent, appName, {\n appDatabase: true,\n appInstallation: true,\n appInputs: true,\n });\n}\n\nfunction updateBlueprint(\n blueprintContent: string,\n appName: string,\n options: {\n appDatabase: boolean;\n appInstallation: boolean;\n appInputs: boolean;\n },\n): string {\n const document = parseDocument(blueprintContent);\n if (document.errors.length > 0) {\n throw new Error(\n `Invalid OS blueprint YAML: ${document.errors.map((error) => error.message).join(\"; \")}`,\n );\n }\n\n const spec = document.get(\"spec\", true);\n if (!isMap(spec)) {\n throw new Error(\"OS blueprint must include a spec map.\");\n }\n\n let changed = false;\n const inputs = spec.get(\"inputs\", true);\n if (!isSeq(inputs)) {\n throw new Error(\"OS blueprint spec.inputs must be a sequence.\");\n }\n\n changed = ensureInputGroups(document, spec) || changed;\n\n if (options.appInputs) {\n changed =\n addAppInput(document, inputs, renderIngressDomainInput()) || changed;\n changed =\n addAppInput(document, inputs, renderBetterAuthSecretInput(appName)) ||\n changed;\n changed =\n addAppInput(document, inputs, renderLangfusePublicKeyInput()) || changed;\n changed =\n addAppInput(document, inputs, renderLangfuseSecretKeyInput()) || changed;\n }\n\n if (options.appDatabase) {\n changed = addAppDatabase(document, inputs, appName) || changed;\n }\n\n if (options.appInstallation) {\n const installations = spec.get(\"installations\", true);\n if (!isSeq(installations)) {\n throw new Error(\"OS blueprint spec.installations must be a sequence.\");\n }\n changed = ensureOsPostgresqlInstallationAlias(installations) || changed;\n changed = addAppInstallation(document, installations, appName) || changed;\n }\n\n return changed ? document.toString() : blueprintContent;\n}\n\nfunction ensureInputGroups(\n document: ReturnType<typeof parseDocument>,\n spec: {\n get(key: string, keepScalar?: true): unknown;\n set(key: string, value: unknown): void;\n },\n): boolean {\n let changed = false;\n const inputGroups = spec.get(\"inputGroups\", true);\n\n if (inputGroups == null) {\n spec.set(\"inputGroups\", document.createNode(OS_BLUEPRINT_INPUT_GROUPS));\n return true;\n }\n\n if (!isSeq(inputGroups)) {\n throw new Error(\"OS blueprint spec.inputGroups must be a sequence.\");\n }\n\n for (const group of OS_BLUEPRINT_INPUT_GROUPS) {\n const exists = inputGroups.items.some(\n (item) => isMap(item) && item.get(\"name\") === group.name,\n );\n if (exists) continue;\n\n inputGroups.add(document.createNode(group));\n changed = true;\n }\n\n return changed;\n}\n\nfunction ensureOsPostgresqlInstallationAlias(installations: {\n items: unknown[];\n}): boolean {\n let changed = false;\n\n for (const installation of installations.items) {\n if (!isMap(installation)) continue;\n\n const service = installation.get(\"service\");\n if (\n typeof service !== \"string\" ||\n !OS_POSTGRESQL_TERRAFORM_SERVICES.has(service)\n ) {\n continue;\n }\n\n if (installation.get(\"name\") === OS_POSTGRESQL_TERRAFORM_ALIAS) continue;\n\n installation.set(\"name\", OS_POSTGRESQL_TERRAFORM_ALIAS);\n changed = true;\n }\n\n return changed;\n}\n\nfunction addAppInput(\n document: ReturnType<typeof parseDocument>,\n inputs: { add(value: unknown): void; items: unknown[] },\n input: Record<string, unknown> & { name: string },\n): boolean {\n if (\n inputs.items.some((item) => isMap(item) && item.get(\"name\") === input.name)\n ) {\n return false;\n }\n\n inputs.add(document.createNode(input));\n return true;\n}\n\nfunction addAppDatabase(\n document: ReturnType<typeof parseDocument>,\n inputs: { items: unknown[] },\n appName: string,\n): boolean {\n const appDatabasesInput = inputs.items.find(\n (item) => isMap(item) && item.get(\"name\") === \"app_databases\",\n );\n if (!isMap(appDatabasesInput)) {\n throw new Error(\"OS blueprint must include an app_databases input.\");\n }\n\n const defaultValue = appDatabasesInput.get(\"default\", true);\n if (!isMap(defaultValue)) {\n throw new Error(\"OS blueprint app_databases default must be a map.\");\n }\n\n if (defaultValue.has(appName)) return false;\n\n defaultValue.flow = false;\n const appDatabaseValue = document.createNode({});\n if (isMap(appDatabaseValue)) appDatabaseValue.flow = true;\n defaultValue.set(appName, appDatabaseValue);\n return true;\n}\n\nfunction addAppInstallation(\n document: ReturnType<typeof parseDocument>,\n installations: { add(value: unknown): void; items: unknown[] },\n appName: string,\n): boolean {\n if (\n installations.items.some(\n (item) => isMap(item) && item.get(\"service\") === appName,\n )\n ) {\n return false;\n }\n\n installations.add(\n document.createNode({\n service: appName,\n env: renderAppInstallationEnv(appName),\n config: renderAppInstallationConfig(appName),\n }),\n );\n return true;\n}\n\nfunction renderIngressDomainInput(): Record<string, unknown> & {\n name: string;\n} {\n return {\n name: ingressDomainInputName(),\n type: \"string\",\n group: \"applications\",\n displayName: \"Ingress Domain\",\n description: \"Shared ingress domain for generated OS webapps.\",\n default: '{{ default \"example.local\" .ryvn.env.state.public_domain.name }}',\n };\n}\n\nfunction renderBetterAuthSecretInput(\n appName: string,\n): Record<string, unknown> & { name: string } {\n return {\n name: betterAuthSecretInputName(appName),\n type: \"string\",\n isSecret: true,\n group: \"applications\",\n displayName: `${toTitleCase(appName)} Better Auth Secret`,\n description: `Generated Better Auth signing secret for ${appName}.`,\n hidden: true,\n generated: {\n type: \"random-bytes\",\n length: 32,\n },\n };\n}\n\nfunction renderLangfusePublicKeyInput(): Record<string, unknown> & {\n name: string;\n} {\n return {\n name: langfusePublicKeyInputName(),\n type: \"string\",\n group: \"applications\",\n displayName: \"Langfuse Public Key\",\n description:\n \"Shared Langfuse public key for generated OS webapps. Leave empty to disable Langfuse export.\",\n default: \"\",\n };\n}\n\nfunction renderLangfuseSecretKeyInput(): Record<string, unknown> & {\n name: string;\n} {\n return {\n name: langfuseSecretKeyInputName(),\n type: \"string\",\n isSecret: true,\n group: \"applications\",\n displayName: \"Langfuse Secret Key\",\n description:\n \"Shared Langfuse secret key for generated OS webapps. Leave unset to disable Langfuse export.\",\n condition: `{{ ne (input \"${langfusePublicKeyInputName()}\") \"\" }}`,\n };\n}\n\nfunction renderAppInstallationEnv(\n appName: string,\n): Array<Record<string, unknown>> {\n const appHost = `${appName}.{{ input \"${ingressDomainInputName()}\" }}`;\n\n return [\n {\n key: \"DATABASE_URL\",\n isSecret: true,\n valueFromOutput: {\n serviceInstallation: \"os-postgresql-terraform\",\n name: `app_database_urls.${appName}`,\n },\n },\n {\n key: \"AUTH_DATABASE_URL\",\n isSecret: true,\n valueFromOutput: {\n serviceInstallation: \"os-postgresql-terraform\",\n name: \"auth_database_url\",\n },\n },\n {\n key: \"APP_BASE_URL\",\n value: `https://${appHost}`,\n },\n {\n key: \"DEPLOYMENT_ENVIRONMENT\",\n value: \"{{ EnvironmentName }}\",\n },\n {\n key: \"BETTER_AUTH_SECRET\",\n isSecret: true,\n valueFromInput: {\n name: betterAuthSecretInputName(appName),\n },\n },\n {\n key: \"INNGEST_BASE_URL\",\n value: '{{ (blueprintInstallation \"mosaic\").outputs.inngest_base_url }}',\n },\n {\n key: \"LANGFUSE_BASE_URL\",\n value: '{{ (blueprintInstallation \"mosaic\").outputs.langfuse_base_url }}',\n },\n {\n key: \"LANGFUSE_PUBLIC_KEY\",\n valueFromInput: {\n name: langfusePublicKeyInputName(),\n },\n },\n {\n key: \"LANGFUSE_SECRET_KEY\",\n isSecret: true,\n valueFromInput: {\n name: langfuseSecretKeyInputName(),\n },\n },\n {\n key: \"OTEL_EXPORTER_OTLP_ENDPOINT\",\n value:\n '{{ (blueprintInstallation \"mosaic\").outputs.otel_exporter_otlp_endpoint }}',\n },\n {\n key: \"SPICEDB_ENDPOINT\",\n value: '{{ (blueprintInstallation \"mosaic\").outputs.spicedb_endpoint }}',\n },\n {\n key: \"SPICEDB_PRESHARED_KEY\",\n isSecret: true,\n value:\n '{{ (blueprintInstallation \"mosaic\").outputs.spicedb_preshared_key }}',\n },\n {\n key: \"SPICEDB_INSECURE\",\n value: '{{ (blueprintInstallation \"mosaic\").outputs.spicedb_insecure }}',\n },\n ];\n}\n\nfunction renderAppInstallationConfig(appName: string): string {\n const appHost = `${appName}.{{ input \"${ingressDomainInputName()}\" }}`;\n\n return [\n \"replicaCount: 1\",\n \"\",\n \"service:\",\n \" port: 3000\",\n \"\",\n \"livenessEnabled: true\",\n \"readinessEnabled: true\",\n \"startupEnabled: true\",\n \"\",\n \"env:\",\n \" - name: INNGEST_EVENT_KEY\",\n \" valueFrom:\",\n \" secretKeyRef:\",\n ` name: '{{ (blueprintInstallation \"mosaic\").outputs.${mosaicInngestKeysSecretNameOutput()} }}'`,\n ` key: '{{ (blueprintInstallation \"mosaic\").outputs.${mosaicInngestEventKeySecretKeyOutput()} }}'`,\n \" - name: INNGEST_SIGNING_KEY\",\n \" valueFrom:\",\n \" secretKeyRef:\",\n ` name: '{{ (blueprintInstallation \"mosaic\").outputs.${mosaicInngestKeysSecretNameOutput()} }}'`,\n ` key: '{{ (blueprintInstallation \"mosaic\").outputs.${mosaicInngestSigningKeySecretKeyOutput()} }}'`,\n \"\",\n \"resources:\",\n \" requests:\",\n ' cpu: \"100m\"',\n \" memory: 256Mi\",\n \" limits:\",\n ' cpu: \"500m\"',\n \" memory: 512Mi\",\n \"\",\n \"ingress:\",\n \" enabled: true\",\n \" className: external-nginx\",\n \" annotations:\",\n \" cert-manager.io/cluster-issuer: external-issuer\",\n ' nginx.ingress.kubernetes.io/ssl-redirect: \"true\"',\n \" hosts:\",\n ` - host: '${appHost}'`,\n \" paths:\",\n \" - path: /\",\n \" pathType: Prefix\",\n \" tls:\",\n ` - secretName: ${appName}-tls`,\n \" hosts:\",\n ` - '${appHost}'`,\n \"\",\n ].join(\"\\n\");\n}\n\nasync function readLocalServiceDefinition(\n monorepoRoot: string,\n appName: string,\n): Promise<string> {\n const serviceDefinitionPath = path.join(\n monorepoRoot,\n \"packages\",\n appName,\n \"deploy\",\n \"ryvn\",\n `${appName}.service.yaml`,\n );\n if (!(await fs.pathExists(serviceDefinitionPath))) {\n throw new Error(\n `${serviceDefinitionPath} does not exist. Add the app's Ryvn service definition before registering it in infra.`,\n );\n }\n\n const content = await fs.readFile(serviceDefinitionPath, \"utf-8\");\n validateLocalServiceDefinition(content, appName, serviceDefinitionPath);\n return content.endsWith(\"\\n\") ? content : `${content}\\n`;\n}\n\nfunction validateLocalServiceDefinition(\n content: string,\n appName: string,\n serviceDefinitionPath: string,\n): void {\n const document = parseDocument(content);\n if (document.errors.length > 0) {\n throw new Error(\n `Invalid Ryvn service YAML at ${serviceDefinitionPath}: ${document.errors.map((error) => error.message).join(\"; \")}`,\n );\n }\n\n const service = document.toJS() as {\n kind?: unknown;\n metadata?: { name?: unknown };\n };\n if (service.kind !== \"Service\" || service.metadata?.name !== appName) {\n throw new Error(\n `${serviceDefinitionPath} must define kind: Service with metadata.name: ${appName}.`,\n );\n }\n}\n\nfunction ingressDomainInputName(): string {\n return \"ingress_domain\";\n}\n\nfunction betterAuthSecretInputName(appName: string): string {\n return `${toSnakeCase(appName)}_better_auth_secret`;\n}\n\nfunction mosaicInngestKeysSecretNameOutput(): string {\n return \"inngest_keys_secret_name\";\n}\n\nfunction mosaicInngestEventKeySecretKeyOutput(): string {\n return \"inngest_event_key_secret_key\";\n}\n\nfunction mosaicInngestSigningKeySecretKeyOutput(): string {\n return \"inngest_signing_key_secret_key\";\n}\n\nfunction langfusePublicKeyInputName(): string {\n return \"langfuse_public_key\";\n}\n\nfunction langfuseSecretKeyInputName(): string {\n return \"langfuse_secret_key\";\n}\n\nfunction normalizeAppName(appNameInput: string): string {\n const appName = toKebabCase(appNameInput);\n const validation = validateProjectName(appName);\n if (!validation.valid) {\n throw new Error(`Invalid app name: ${validation.error}`);\n }\n return appName;\n}\n"],"mappings":"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CN,OAAO;EACP,aAAa;EACb,aACE;EACF,SAAS;EACV;;AAGH,SAAS,+BAEP;AACA,QAAO;EACL,MAAM,4BAA4B;EAClC,MAAM;EACN,UAAU;EACV,OAAO;EACP,aAAa;EACb,aACE;EACF,WAAW,iBAAiB,4BAA4B,CAAC;EAC1D;;AAGH,SAAS,yBACP,SACgC;CAChC,MAAM,UAAU,GAAG,QAAQ,aAAa,wBAAwB,CAAC;AAEjE,QAAO;EACL;GACE,KAAK;GACL,UAAU;GACV,iBAAiB;IACf,qBAAqB;IACrB,MAAM,qBAAqB;IAC5B;GACF;EACD;GACE,KAAK;GACL,UAAU;GACV,iBAAiB;IACf,qBAAqB;IACrB,MAAM;IACP;GACF;EACD;GACE,KAAK;GACL,OAAO,WAAW;GACnB;EACD;GACE,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,UAAU;GACV,gBAAgB,EACd,MAAM,0BAA0B,QAAQ,EACzC;GACF;EACD;GACE,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,gBAAgB,EACd,MAAM,4BAA4B,EACnC;GACF;EACD;GACE,KAAK;GACL,UAAU;GACV,gBAAgB,EACd,MAAM,4BAA4B,EACnC;GACF;EACD;GACE,KAAK;GACL,OACE;GACH;EACD;GACE,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,UAAU;GACV,OACE;GACH;EACD;GACE,KAAK;GACL,OAAO;GACR;EACF;;AAGH,SAAS,4BAA4B,SAAyB;CAC5D,MAAM,UAAU,GAAG,QAAQ,aAAa,wBAAwB,CAAC;AAEjE,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,8DAA8D,mCAAmC,CAAC;EAClG,6DAA6D,sCAAsC,CAAC;EACpG;EACA;EACA;EACA,8DAA8D,mCAAmC,CAAC;EAClG,6DAA6D,wCAAwC,CAAC;EACtG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,gBAAgB,QAAQ;EACxB;EACA;EACA;EACA;EACA,qBAAqB,QAAQ;EAC7B;EACA,cAAc,QAAQ;EACtB;EACD,CAAC,KAAK,KAAK;;AAGd,eAAe,2BACb,cACA,SACiB;CACjB,MAAM,wBAAwB,KAAK,KACjC,cACA,YACA,SACA,UACA,QACA,GAAG,QAAQ,eACZ;AACD,KAAI,CAAE,MAAM,GAAG,WAAW,sBAAsB,CAC9C,OAAM,IAAI,MACR,GAAG,sBAAsB,wFAC1B;CAGH,MAAM,UAAU,MAAM,GAAG,SAAS,uBAAuB,QAAQ;AACjE,gCAA+B,SAAS,SAAS,sBAAsB;AACvE,QAAO,QAAQ,SAAS,KAAK,GAAG,UAAU,GAAG,QAAQ;;AAGvD,SAAS,+BACP,SACA,SACA,uBACM;CACN,MAAM,WAAW,cAAc,QAAQ;AACvC,KAAI,SAAS,OAAO,SAAS,EAC3B,OAAM,IAAI,MACR,gCAAgC,sBAAsB,IAAI,SAAS,OAAO,KAAK,UAAU,MAAM,QAAQ,CAAC,KAAK,KAAK,GACnH;CAGH,MAAM,UAAU,SAAS,MAAM;AAI/B,KAAI,QAAQ,SAAS,aAAa,QAAQ,UAAU,SAAS,QAC3D,OAAM,IAAI,MACR,GAAG,sBAAsB,iDAAiD,QAAQ,GACnF;;AAIL,SAAS,yBAAiC;AACxC,QAAO;;AAGT,SAAS,0BAA0B,SAAyB;AAC1D,QAAO,GAAG,YAAY,QAAQ,CAAC;;AAGjC,SAAS,oCAA4C;AACnD,QAAO;;AAGT,SAAS,uCAA+C;AACtD,QAAO;;AAGT,SAAS,yCAAiD;AACxD,QAAO;;AAGT,SAAS,6BAAqC;AAC5C,QAAO;;AAGT,SAAS,6BAAqC;AAC5C,QAAO;;AAGT,SAAS,iBAAiB,cAA8B;CACtD,MAAM,UAAU,YAAY,aAAa;CACzC,MAAM,aAAa,oBAAoB,QAAQ;AAC/C,KAAI,CAAC,WAAW,MACd,OAAM,IAAI,MAAM,qBAAqB,WAAW,QAAQ;AAE1D,QAAO"}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import fs from "fs-extra";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
//#region src/utils/case-converters.ts
|
|
5
|
-
/** Lowercase, hyphenated, npm-package-name-safe form: "My Cool App" → "my-cool-app". */
|
|
6
|
-
function toKebabCase(str) {
|
|
7
|
-
return str.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
8
|
-
}
|
|
9
|
-
/** Display form derived from a kebab-case name: "my-cool-app" → "My Cool App". */
|
|
10
|
-
function toTitleCase(str) {
|
|
11
|
-
return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
12
|
-
}
|
|
13
|
-
/** Identifier form for env vars and DB names: "my-cool-app" → "my_cool_app". */
|
|
14
|
-
function toSnakeCase(str) {
|
|
15
|
-
return str.replace(/-/g, "_");
|
|
16
|
-
}
|
|
17
|
-
//#endregion
|
|
18
|
-
//#region src/utils/template-versions.ts
|
|
19
|
-
const FALLBACK_TEMPLATE_VERSION = "1.0.0";
|
|
20
|
-
function readTemplateVersions() {
|
|
21
|
-
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
-
const candidates = [path.resolve(currentDir, "../template-versions.json"), path.resolve(currentDir, "../../template-versions.json")];
|
|
23
|
-
for (const versionsPath of candidates) try {
|
|
24
|
-
const content = fs.readFileSync(versionsPath, "utf-8");
|
|
25
|
-
return JSON.parse(content);
|
|
26
|
-
} catch {}
|
|
27
|
-
return {};
|
|
28
|
-
}
|
|
29
|
-
function getTemplateVersion(templateType) {
|
|
30
|
-
return readTemplateVersions()[templateType] ?? FALLBACK_TEMPLATE_VERSION;
|
|
31
|
-
}
|
|
32
|
-
//#endregion
|
|
33
|
-
export { toTitleCase as i, toKebabCase as n, toSnakeCase as r, getTemplateVersion as t };
|
|
34
|
-
|
|
35
|
-
//# sourceMappingURL=template-versions-CEIP9vhl.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"template-versions-CEIP9vhl.js","names":[],"sources":["../src/utils/case-converters.ts","../src/utils/template-versions.ts"],"sourcesContent":["/** Lowercase, hyphenated, npm-package-name-safe form: \"My Cool App\" → \"my-cool-app\". */\nexport function toKebabCase(str: string): string {\n return str\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\n/** Display form derived from a kebab-case name: \"my-cool-app\" → \"My Cool App\". */\nexport function toTitleCase(str: string): string {\n return str\n .split(\"-\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n\n/** Identifier form for env vars and DB names: \"my-cool-app\" → \"my_cool_app\". */\nexport function toSnakeCase(str: string): string {\n return str.replace(/-/g, \"_\");\n}\n","import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport fs from \"fs-extra\";\n\nconst FALLBACK_TEMPLATE_VERSION = \"1.0.0\";\n\nexport function readTemplateVersions(): Record<string, string> {\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(currentDir, \"../template-versions.json\"),\n path.resolve(currentDir, \"../../template-versions.json\"),\n ];\n\n for (const versionsPath of candidates) {\n try {\n const content = fs.readFileSync(versionsPath, \"utf-8\");\n return JSON.parse(content);\n } catch {\n // Try the next path. Source tests and bundled CLI resolve differently.\n }\n }\n\n return {};\n}\n\nexport function getTemplateVersion(templateType: string): string {\n return readTemplateVersions()[templateType] ?? FALLBACK_TEMPLATE_VERSION;\n}\n"],"mappings":";;;;;AACA,SAAgB,YAAY,KAAqB;AAC/C,QAAO,IACJ,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,OAAO,IAAI,CACnB,QAAQ,UAAU,GAAG;;;AAI1B,SAAgB,YAAY,KAAqB;AAC/C,QAAO,IACJ,MAAM,IAAI,CACV,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,CAC3D,KAAK,IAAI;;;AAId,SAAgB,YAAY,KAAqB;AAC/C,QAAO,IAAI,QAAQ,MAAM,IAAI;;;;ACf/B,MAAM,4BAA4B;AAElC,SAAgB,uBAA+C;CAC7D,MAAM,aAAa,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;CAC/D,MAAM,aAAa,CACjB,KAAK,QAAQ,YAAY,4BAA4B,EACrD,KAAK,QAAQ,YAAY,+BAA+B,CACzD;AAED,MAAK,MAAM,gBAAgB,WACzB,KAAI;EACF,MAAM,UAAU,GAAG,aAAa,cAAc,QAAQ;AACtD,SAAO,KAAK,MAAM,QAAQ;SACpB;AAKV,QAAO,EAAE;;AAGX,SAAgB,mBAAmB,cAA8B;AAC/D,QAAO,sBAAsB,CAAC,iBAAiB"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import validateNpmPackageName from "validate-npm-package-name";
|
|
2
|
-
//#region src/utils/validate.ts
|
|
3
|
-
function validateProjectName(name) {
|
|
4
|
-
const result = validateNpmPackageName(name);
|
|
5
|
-
if (!result.validForNewPackages) return {
|
|
6
|
-
valid: false,
|
|
7
|
-
error: [...result.errors || [], ...result.warnings || []][0] || "Invalid package name"
|
|
8
|
-
};
|
|
9
|
-
return { valid: true };
|
|
10
|
-
}
|
|
11
|
-
//#endregion
|
|
12
|
-
export { validateProjectName as t };
|
|
13
|
-
|
|
14
|
-
//# sourceMappingURL=validate-dssldJAj.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validate-dssldJAj.js","names":[],"sources":["../src/utils/validate.ts"],"sourcesContent":["import validateNpmPackageName from \"validate-npm-package-name\";\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n}\n\nexport function validateProjectName(name: string): ValidationResult {\n const result = validateNpmPackageName(name);\n\n if (!result.validForNewPackages) {\n const errors = [...(result.errors || []), ...(result.warnings || [])];\n return {\n valid: false,\n error: errors[0] || \"Invalid package name\",\n };\n }\n\n return { valid: true };\n}\n"],"mappings":";;AAOA,SAAgB,oBAAoB,MAAgC;CAClE,MAAM,SAAS,uBAAuB,KAAK;AAE3C,KAAI,CAAC,OAAO,oBAEV,QAAO;EACL,OAAO;EACP,OAAO,CAHO,GAAI,OAAO,UAAU,EAAE,EAAG,GAAI,OAAO,YAAY,EAAE,CAGpD,CAAC,MAAM;EACrB;AAGH,QAAO,EAAE,OAAO,MAAM"}
|