@pracht/cli 1.1.4 → 1.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.
@@ -1,17 +1,15 @@
1
1
  import { readFileSync, writeFileSync } from "node:fs";
2
2
 
3
+ import { defineCommand } from "citty";
4
+
3
5
  import {
4
6
  ensureTrailingNewline,
5
7
  parseApiMethods,
6
8
  parseCommaList,
7
- parseFlags,
8
- printGenerateHelp,
9
9
  quote,
10
- requireEnumOption,
11
- requireOptionalString,
12
- requirePositiveIntegerOption,
13
- requireStringOption,
14
- } from "../cli.js";
10
+ requireEnum,
11
+ requirePositiveInteger,
12
+ } from "../utils.js";
15
13
  import {
16
14
  extractRegistryEntries,
17
15
  insertArrayItem,
@@ -29,20 +27,103 @@ import {
29
27
  resolveRouteModulePath,
30
28
  resolveScopedFile,
31
29
  writeGeneratedFile,
30
+ type ProjectConfig,
32
31
  } from "../project.js";
33
32
 
34
- export async function generateCommand(args) {
35
- const [kind, ...rest] = args;
36
- if (!kind || kind === "--help" || kind === "-h") {
37
- printGenerateHelp();
38
- return;
39
- }
40
-
41
- const options = parseFlags(rest);
42
- const project = readProjectConfig(process.cwd());
43
- const result = runGenerate(kind, options, project);
33
+ interface GenerateResult {
34
+ created: string[];
35
+ kind: string;
36
+ updated: string[];
37
+ }
44
38
 
45
- if (options.json) {
39
+ const routeCommand = defineCommand({
40
+ meta: {
41
+ name: "route",
42
+ description: "Scaffold a route module",
43
+ },
44
+ args: {
45
+ path: { type: "string", required: true, description: "Route path (e.g. /dashboard)" },
46
+ render: { type: "string", description: "Render mode: ssr, spa, ssg, or isg" },
47
+ shell: { type: "string", description: "Shell name" },
48
+ middleware: { type: "string", description: "Middleware names (comma-separated)" },
49
+ loader: { type: "boolean", description: "Include loader" },
50
+ "error-boundary": { type: "boolean", description: "Include error boundary" },
51
+ "static-paths": { type: "boolean", description: "Include static paths" },
52
+ title: { type: "string", description: "Page title" },
53
+ revalidate: { type: "string", description: "ISG revalidation seconds" },
54
+ json: { type: "boolean", description: "Output as JSON" },
55
+ },
56
+ async run({ args }) {
57
+ const project = readProjectConfig(process.cwd());
58
+ const result = generateRoute(args, project);
59
+ outputResult(result, Boolean(args.json));
60
+ },
61
+ });
62
+
63
+ const shellCommand = defineCommand({
64
+ meta: {
65
+ name: "shell",
66
+ description: "Scaffold a shell component",
67
+ },
68
+ args: {
69
+ name: { type: "string", required: true, description: "Shell name" },
70
+ json: { type: "boolean", description: "Output as JSON" },
71
+ },
72
+ async run({ args }) {
73
+ const project = readProjectConfig(process.cwd());
74
+ const result = generateShell(args.name, project);
75
+ outputResult(result, Boolean(args.json));
76
+ },
77
+ });
78
+
79
+ const middlewareCommand = defineCommand({
80
+ meta: {
81
+ name: "middleware",
82
+ description: "Scaffold a middleware function",
83
+ },
84
+ args: {
85
+ name: { type: "string", required: true, description: "Middleware name" },
86
+ json: { type: "boolean", description: "Output as JSON" },
87
+ },
88
+ async run({ args }) {
89
+ const project = readProjectConfig(process.cwd());
90
+ const result = generateMiddleware(args.name, project);
91
+ outputResult(result, Boolean(args.json));
92
+ },
93
+ });
94
+
95
+ const apiCommand = defineCommand({
96
+ meta: {
97
+ name: "api",
98
+ description: "Scaffold an API route",
99
+ },
100
+ args: {
101
+ path: { type: "string", required: true, description: "API endpoint path" },
102
+ methods: { type: "string", description: "HTTP methods (comma-separated, e.g. GET,POST)" },
103
+ json: { type: "boolean", description: "Output as JSON" },
104
+ },
105
+ async run({ args }) {
106
+ const project = readProjectConfig(process.cwd());
107
+ const result = generateApi(args, project);
108
+ outputResult(result, Boolean(args.json));
109
+ },
110
+ });
111
+
112
+ export default defineCommand({
113
+ meta: {
114
+ name: "generate",
115
+ description: "Scaffold framework files",
116
+ },
117
+ subCommands: {
118
+ route: routeCommand,
119
+ shell: shellCommand,
120
+ middleware: middlewareCommand,
121
+ api: apiCommand,
122
+ },
123
+ });
124
+
125
+ function outputResult(result: GenerateResult, json: boolean): void {
126
+ if (json) {
46
127
  console.log(JSON.stringify({ ok: true, ...result }, null, 2));
47
128
  return;
48
129
  }
@@ -56,36 +137,31 @@ export async function generateCommand(args) {
56
137
  }
57
138
  }
58
139
 
59
- function runGenerate(kind, options, project) {
60
- if (kind === "route") {
61
- return generateRoute(options, project);
62
- }
63
- if (kind === "shell") {
64
- return generateShell(options, project);
65
- }
66
- if (kind === "middleware") {
67
- return generateMiddleware(options, project);
68
- }
69
- if (kind === "api") {
70
- return generateApi(options, project);
71
- }
72
-
73
- throw new Error(`Unknown generate kind: ${kind}`);
140
+ interface RouteArgs {
141
+ "error-boundary"?: boolean;
142
+ loader?: boolean;
143
+ middleware?: string;
144
+ path: string;
145
+ render?: string;
146
+ revalidate?: string;
147
+ shell?: string;
148
+ "static-paths"?: boolean;
149
+ title?: string;
74
150
  }
75
151
 
76
- function generateRoute(options, project) {
77
- const routePath = normalizeRoutePathString(requireStringOption(options, "path"));
78
- const render = requireEnumOption(options, "render", ["spa", "ssr", "ssg", "isg"], "ssr");
79
- const includeLoader = Boolean(options.loader);
80
- const includeErrorBoundary = Boolean(options["error-boundary"]);
81
- const middleware = parseCommaList(options.middleware);
152
+ function generateRoute(args: RouteArgs, project: ProjectConfig): GenerateResult {
153
+ const routePath = normalizeRoutePathString(args.path);
154
+ const render = requireEnum(args.render, "render", ["spa", "ssr", "ssg", "isg"], "ssr");
155
+ const includeLoader = Boolean(args.loader);
156
+ const includeErrorBoundary = Boolean(args["error-boundary"]);
157
+ const middleware = parseCommaList(args.middleware);
82
158
  const includeStaticPaths =
83
- Boolean(options["static-paths"]) ||
159
+ Boolean(args["static-paths"]) ||
84
160
  (hasDynamicSegments(routePath) && (render === "ssg" || render === "isg"));
85
- const title = requireOptionalString(options, "title") ?? titleFromPath(routePath);
161
+ const title = args.title ?? titleFromPath(routePath);
86
162
 
87
163
  if (project.mode === "pages") {
88
- if (options.shell) {
164
+ if (args.shell) {
89
165
  throw new Error("`pracht generate route --shell` is only available for manifest apps.");
90
166
  }
91
167
  if (middleware.length > 0) {
@@ -113,7 +189,7 @@ function generateRoute(options, project) {
113
189
  extractRegistryEntries(manifestSource, "middleware").map((entry) => entry.name),
114
190
  );
115
191
 
116
- const shellName = requireOptionalString(options, "shell");
192
+ const shellName = args.shell;
117
193
  if (shellName && !registeredShells.has(shellName)) {
118
194
  throw new Error(`Shell "${shellName}" is not registered in ${project.appFile}.`);
119
195
  }
@@ -152,7 +228,7 @@ function generateRoute(options, project) {
152
228
  meta.push(`middleware: [${middleware.map((item) => quote(item)).join(", ")}]`);
153
229
  }
154
230
  if (render === "isg") {
155
- const seconds = requirePositiveIntegerOption(options, "revalidate", 3600);
231
+ const seconds = requirePositiveInteger(args.revalidate, "revalidate", 3600);
156
232
  meta.push(`revalidate: timeRevalidate(${seconds})`);
157
233
  }
158
234
 
@@ -182,7 +258,15 @@ function generatePagesRoute({
182
258
  render,
183
259
  routePath,
184
260
  title,
185
- }) {
261
+ }: {
262
+ includeErrorBoundary: boolean;
263
+ includeLoader: boolean;
264
+ includeStaticPaths: boolean;
265
+ project: ProjectConfig;
266
+ render: string;
267
+ routePath: string;
268
+ title: string;
269
+ }): GenerateResult {
186
270
  const routeFile = resolvePagesRouteModulePath(project, routePath, ".tsx");
187
271
  writeGeneratedFile(
188
272
  routeFile.absolutePath,
@@ -203,14 +287,13 @@ function generatePagesRoute({
203
287
  };
204
288
  }
205
289
 
206
- function generateShell(options, project) {
290
+ function generateShell(name: string, project: ProjectConfig): GenerateResult {
207
291
  if (project.mode === "pages") {
208
292
  throw new Error(
209
293
  "Pages router apps use a single `_app` shell. `pracht generate shell` is only available for manifest apps.",
210
294
  );
211
295
  }
212
296
 
213
- const name = requireStringOption(options, "name");
214
297
  const manifestPath = resolveProjectPath(project.root, project.appFile);
215
298
  assertFileExists(manifestPath, `App manifest not found at ${project.appFile}.`);
216
299
 
@@ -232,14 +315,13 @@ function generateShell(options, project) {
232
315
  };
233
316
  }
234
317
 
235
- function generateMiddleware(options, project) {
318
+ function generateMiddleware(name: string, project: ProjectConfig): GenerateResult {
236
319
  if (project.mode === "pages") {
237
320
  throw new Error(
238
321
  "Pages router apps do not use manifest middleware registration. `pracht generate middleware` is only available for manifest apps.",
239
322
  );
240
323
  }
241
324
 
242
- const name = requireStringOption(options, "name");
243
325
  const manifestPath = resolveProjectPath(project.root, project.appFile);
244
326
  assertFileExists(manifestPath, `App manifest not found at ${project.appFile}.`);
245
327
 
@@ -261,9 +343,14 @@ function generateMiddleware(options, project) {
261
343
  };
262
344
  }
263
345
 
264
- function generateApi(options, project) {
265
- const endpointPath = normalizeApiPath(requireStringOption(options, "path"));
266
- const methods = parseApiMethods(options.methods);
346
+ interface ApiArgs {
347
+ methods?: string;
348
+ path: string;
349
+ }
350
+
351
+ function generateApi(args: ApiArgs, project: ProjectConfig): GenerateResult {
352
+ const endpointPath = normalizeApiPath(args.path);
353
+ const methods = parseApiMethods(args.methods);
267
354
  const apiFile = resolveApiModulePath(project, endpointPath);
268
355
  writeGeneratedFile(apiFile.absolutePath, buildApiRouteSource({ endpointPath, methods }));
269
356
 
@@ -280,10 +367,16 @@ function buildManifestRouteModuleSource({
280
367
  includeStaticPaths,
281
368
  routePath,
282
369
  title,
283
- }) {
370
+ }: {
371
+ includeErrorBoundary: boolean;
372
+ includeLoader: boolean;
373
+ includeStaticPaths: boolean;
374
+ routePath: string;
375
+ title: string;
376
+ }): string {
284
377
  const params = dynamicParamNames(routePath);
285
- const imports = [];
286
- const sections = [];
378
+ const imports: string[] = [];
379
+ const sections: string[] = [];
287
380
 
288
381
  if (includeLoader) {
289
382
  imports.push("LoaderArgs", "RouteComponentProps");
@@ -359,10 +452,17 @@ function buildPagesRouteModuleSource({
359
452
  render,
360
453
  routePath,
361
454
  title,
362
- }) {
455
+ }: {
456
+ includeErrorBoundary: boolean;
457
+ includeLoader: boolean;
458
+ includeStaticPaths: boolean;
459
+ render: string;
460
+ routePath: string;
461
+ title: string;
462
+ }): string {
363
463
  const params = dynamicParamNames(routePath);
364
- const imports = [];
365
- const sections = [];
464
+ const imports: string[] = [];
465
+ const sections: string[] = [];
366
466
 
367
467
  if (includeLoader) {
368
468
  imports.push("LoaderArgs", "RouteComponentProps");
@@ -431,7 +531,7 @@ function buildPagesRouteModuleSource({
431
531
  return `${sections.join("\n")}\n`;
432
532
  }
433
533
 
434
- function buildShellModuleSource(name) {
534
+ function buildShellModuleSource(name: string): string {
435
535
  const title = titleCase(name);
436
536
  return [
437
537
  'import type { ShellProps } from "@pracht/core";',
@@ -451,7 +551,7 @@ function buildShellModuleSource(name) {
451
551
  ].join("\n");
452
552
  }
453
553
 
454
- function buildMiddlewareModuleSource() {
554
+ function buildMiddlewareModuleSource(): string {
455
555
  return [
456
556
  'import type { MiddlewareFn } from "@pracht/core";',
457
557
  "",
@@ -462,7 +562,13 @@ function buildMiddlewareModuleSource() {
462
562
  ].join("\n");
463
563
  }
464
564
 
465
- function buildApiRouteSource({ endpointPath, methods }) {
565
+ function buildApiRouteSource({
566
+ endpointPath,
567
+ methods,
568
+ }: {
569
+ endpointPath: string;
570
+ methods: string[];
571
+ }): string {
466
572
  const methodLines = methods.flatMap((method, index) => {
467
573
  const lines = buildApiMethodSource(method, methods, endpointPath);
468
574
  if (index === methods.length - 1) return lines;
@@ -472,7 +578,7 @@ function buildApiRouteSource({ endpointPath, methods }) {
472
578
  return ['import type { BaseRouteArgs } from "@pracht/core";', "", ...methodLines, ""].join("\n");
473
579
  }
474
580
 
475
- function buildApiMethodSource(method, methods, endpointPath) {
581
+ function buildApiMethodSource(method: string, methods: string[], endpointPath: string): string[] {
476
582
  if (method === "DELETE" || method === "HEAD") {
477
583
  return [
478
584
  `export function ${method}(_args: BaseRouteArgs) {`,
@@ -509,22 +615,22 @@ function buildApiMethodSource(method, methods, endpointPath) {
509
615
  ];
510
616
  }
511
617
 
512
- function normalizeRoutePathString(value) {
618
+ function normalizeRoutePathString(value: string): string {
513
619
  if (!value || value === "/") return "/";
514
620
  const normalized = `/${value}`.replace(/\/+/g, "/");
515
621
  return normalized !== "/" && normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
516
622
  }
517
623
 
518
- function normalizeApiPath(value) {
624
+ function normalizeApiPath(value: string): string {
519
625
  const normalized = normalizeRoutePathString(value).replace(/^\/api(?=\/|$)/, "");
520
626
  return normalized || "/";
521
627
  }
522
628
 
523
- function hasDynamicSegments(routePath) {
629
+ function hasDynamicSegments(routePath: string): boolean {
524
630
  return routePath.split("/").some((segment) => segment.startsWith(":") || segment === "*");
525
631
  }
526
632
 
527
- function dynamicParamNames(routePath) {
633
+ function dynamicParamNames(routePath: string): string[] {
528
634
  return routePath
529
635
  .split("/")
530
636
  .filter(Boolean)
@@ -533,10 +639,10 @@ function dynamicParamNames(routePath) {
533
639
  if (segment === "*") return "slug";
534
640
  return null;
535
641
  })
536
- .filter(Boolean);
642
+ .filter((s): s is string => s !== null);
537
643
  }
538
644
 
539
- function routeIdFromPath(routePath) {
645
+ function routeIdFromPath(routePath: string): string {
540
646
  if (routePath === "/") return "index";
541
647
  return routePath
542
648
  .split("/")
@@ -545,13 +651,13 @@ function routeIdFromPath(routePath) {
545
651
  .join("-");
546
652
  }
547
653
 
548
- function titleFromPath(routePath) {
654
+ function titleFromPath(routePath: string): string {
549
655
  if (routePath === "/") return "Home";
550
656
  const lastSegment = routePath.split("/").filter(Boolean).at(-1) ?? "Page";
551
657
  return titleCase(lastSegment.replace(/^:/, "").replace(/\*/g, "slug"));
552
658
  }
553
659
 
554
- function titleCase(value) {
660
+ function titleCase(value: string): string {
555
661
  return value
556
662
  .split(/[-_/]/)
557
663
  .filter(Boolean)
@@ -559,7 +665,7 @@ function titleCase(value) {
559
665
  .join(" ");
560
666
  }
561
667
 
562
- function buildStaticPathsStub(params) {
668
+ function buildStaticPathsStub(params: string[]): string {
563
669
  if (params.length === 0) {
564
670
  return "{}";
565
671
  }
@@ -567,6 +673,6 @@ function buildStaticPathsStub(params) {
567
673
  return `{ ${params.map((name) => `${name}: ${quote(`example-${name}`)}`).join(", ")} }`;
568
674
  }
569
675
 
570
- function escapeJsxText(value) {
676
+ function escapeJsxText(value: string): string {
571
677
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
572
678
  }
@@ -1,40 +1,76 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
 
4
- import { createServer } from "vite";
4
+ import { defineCommand } from "citty";
5
+ import { createServer, type ViteDevServer } from "vite";
5
6
 
6
- import { handleCliError, parseFlags, printInspectHelp, requireOptionalString } from "../cli.js";
7
+ import { handleCliError } from "../utils.js";
7
8
  import { readClientBuildAssets } from "../build-metadata.js";
8
- import { HTTP_METHODS } from "../constants.js";
9
+ import { HTTP_METHODS, type HttpMethod } from "../constants.js";
9
10
  import { readProjectConfig, resolveProjectPath } from "../project.js";
10
11
 
11
12
  const INSPECT_TARGETS = new Set(["routes", "api", "build", "all"]);
12
- const METHOD_ORDER = [...HTTP_METHODS];
13
-
14
- export async function inspectCommand(args) {
15
- const options = parseFlags(args);
16
- const target = requireOptionalString(options, "target") ?? options._[0] ?? "all";
17
-
18
- if (options.help || target === "help") {
19
- printInspectHelp();
20
- return;
21
- }
22
-
23
- if (!INSPECT_TARGETS.has(target)) {
24
- handleCliError(new Error(`Unknown inspect target: ${target}`), { json: !!options.json });
25
- }
13
+ const METHOD_ORDER: HttpMethod[] = [...HTTP_METHODS];
14
+
15
+ export default defineCommand({
16
+ meta: {
17
+ name: "inspect",
18
+ description: "Inspect resolved app graph",
19
+ },
20
+ args: {
21
+ target: {
22
+ type: "positional",
23
+ description: "Inspect target: routes, api, build, or all",
24
+ required: false,
25
+ },
26
+ json: {
27
+ type: "boolean",
28
+ description: "Output as JSON",
29
+ },
30
+ },
31
+ async run({ args }) {
32
+ const target = args.target || "all";
33
+
34
+ if (!INSPECT_TARGETS.has(target)) {
35
+ handleCliError(new Error(`Unknown inspect target: ${target}`), {
36
+ json: Boolean(args.json),
37
+ });
38
+ }
26
39
 
27
- const report = await runInspect(process.cwd(), { target });
40
+ const report = await runInspect(process.cwd(), { target });
28
41
 
29
- if (options.json) {
30
- console.log(JSON.stringify(report, null, 2));
31
- return;
32
- }
42
+ if (args.json) {
43
+ console.log(JSON.stringify(report, null, 2));
44
+ return;
45
+ }
33
46
 
34
- printInspectReport(report);
47
+ printInspectReport(report);
48
+ },
49
+ });
50
+
51
+ interface InspectReport {
52
+ api?: { file: string; methods: string[]; path: string }[];
53
+ build?: {
54
+ adapterTarget: string;
55
+ clientEntryUrl: string | null;
56
+ cssManifest: Record<string, string[]>;
57
+ jsManifest: Record<string, string[]>;
58
+ };
59
+ mode: string;
60
+ routes?: {
61
+ file: string;
62
+ id: string;
63
+ loaderFile: string | null;
64
+ middleware: string[];
65
+ path: string;
66
+ render: string | null;
67
+ revalidate: unknown;
68
+ shell: string | null;
69
+ shellFile: string | null;
70
+ }[];
35
71
  }
36
72
 
37
- export async function runInspect(root, { target = "all" } = {}) {
73
+ async function runInspect(root: string, { target = "all" } = {}): Promise<InspectReport> {
38
74
  const project = readProjectConfig(root);
39
75
 
40
76
  if (!project.configFile) {
@@ -66,7 +102,7 @@ export async function runInspect(root, { target = "all" } = {}) {
66
102
 
67
103
  try {
68
104
  const serverModule = await server.ssrLoadModule("virtual:pracht/server");
69
- const report = {
105
+ const report: InspectReport = {
70
106
  mode: project.mode,
71
107
  };
72
108
 
@@ -76,7 +112,7 @@ export async function runInspect(root, { target = "all" } = {}) {
76
112
 
77
113
  if (target === "api" || target === "all") {
78
114
  report.api = await Promise.all(
79
- serverModule.apiRoutes.map(async (route) => ({
115
+ serverModule.apiRoutes.map(async (route: { file: string; path: string }) => ({
80
116
  file: route.file,
81
117
  methods: await detectApiMethods(server, root, route.file),
82
118
  path: route.path,
@@ -100,7 +136,19 @@ export async function runInspect(root, { target = "all" } = {}) {
100
136
  }
101
137
  }
102
138
 
103
- function serializeRoutes(routes) {
139
+ interface RouteEntry {
140
+ file: string;
141
+ id: string;
142
+ loaderFile?: string;
143
+ middleware: string[];
144
+ path: string;
145
+ render?: string;
146
+ revalidate?: unknown;
147
+ shell?: string;
148
+ shellFile?: string;
149
+ }
150
+
151
+ function serializeRoutes(routes: RouteEntry[]) {
104
152
  return routes.map((route) => ({
105
153
  file: route.file,
106
154
  id: route.id,
@@ -114,11 +162,14 @@ function serializeRoutes(routes) {
114
162
  }));
115
163
  }
116
164
 
117
- async function detectApiMethods(server, root, file) {
165
+ async function detectApiMethods(
166
+ server: ViteDevServer,
167
+ root: string,
168
+ file: string,
169
+ ): Promise<string[]> {
118
170
  const resolvedFile = resolve(root, `.${file}`);
119
171
  const source = readFileSync(resolvedFile, "utf-8");
120
172
 
121
- // Use module evaluation first so re-exported handlers are reflected too.
122
173
  try {
123
174
  const module = await server.ssrLoadModule(file);
124
175
  return METHOD_ORDER.filter((method) => typeof module[method] === "function");
@@ -129,7 +180,7 @@ async function detectApiMethods(server, root, file) {
129
180
  }
130
181
  }
131
182
 
132
- function printInspectReport(report) {
183
+ function printInspectReport(report: InspectReport): void {
133
184
  console.log(`Pracht inspect (${report.mode} mode)`);
134
185
 
135
186
  if (report.routes) {
@@ -0,0 +1,37 @@
1
+ import { defineCommand } from "citty";
2
+
3
+ import { runVerification } from "../verification.js";
4
+
5
+ export default defineCommand({
6
+ meta: {
7
+ name: "verify",
8
+ description: "Fast framework-aware verification",
9
+ },
10
+ args: {
11
+ changed: {
12
+ type: "boolean",
13
+ description: "Only check changed files",
14
+ },
15
+ json: {
16
+ type: "boolean",
17
+ description: "Output as JSON",
18
+ },
19
+ },
20
+ async run({ args }) {
21
+ const report = runVerification(process.cwd(), { changed: Boolean(args.changed) });
22
+
23
+ if (args.json) {
24
+ console.log(JSON.stringify(report, null, 2));
25
+ } else {
26
+ console.log(`Pracht verify (${report.mode} mode, ${report.scope} scope)`);
27
+ for (const check of report.checks) {
28
+ console.log(`${check.status.toUpperCase().padEnd(7)} ${check.message}`);
29
+ }
30
+ console.log(report.ok ? "\nNo blocking issues found." : "\nBlocking issues found.");
31
+ }
32
+
33
+ if (!report.ok) {
34
+ process.exitCode = 1;
35
+ }
36
+ },
37
+ });
@@ -1,4 +1,4 @@
1
- export const DEFAULT_SECURITY_HEADERS = {
1
+ export const DEFAULT_SECURITY_HEADERS: Record<string, string> = {
2
2
  "permissions-policy":
3
3
  "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()",
4
4
  "referrer-policy": "strict-origin-when-cross-origin",
@@ -19,4 +19,14 @@ export const PROJECT_DEFAULTS = {
19
19
  shellsDir: "/src/shells",
20
20
  };
21
21
 
22
- export const HTTP_METHODS = new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
22
+ export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
23
+
24
+ export const HTTP_METHODS = new Set<HttpMethod>([
25
+ "GET",
26
+ "POST",
27
+ "PUT",
28
+ "PATCH",
29
+ "DELETE",
30
+ "HEAD",
31
+ "OPTIONS",
32
+ ]);
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { defineCommand, runMain } from "citty";
2
+
3
+ import { VERSION } from "./constants.js";
4
+
5
+ const main = defineCommand({
6
+ meta: {
7
+ name: "pracht",
8
+ version: VERSION,
9
+ description: "The pracht CLI",
10
+ },
11
+ subCommands: {
12
+ build: () => import("./commands/build.js").then((m) => m.default),
13
+ dev: () => import("./commands/dev.js").then((m) => m.default),
14
+ doctor: () => import("./commands/doctor.js").then((m) => m.default),
15
+ generate: () => import("./commands/generate.js").then((m) => m.default),
16
+ inspect: () => import("./commands/inspect.js").then((m) => m.default),
17
+ verify: () => import("./commands/verify.js").then((m) => m.default),
18
+ },
19
+ });
20
+
21
+ runMain(main);