@beignet/cli 0.0.52 → 0.0.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +131 -1
  3. package/dist/analysis/layers.d.ts +2 -0
  4. package/dist/analysis/layers.d.ts.map +1 -1
  5. package/dist/analysis/layers.js +5 -0
  6. package/dist/analysis/layers.js.map +1 -1
  7. package/dist/analysis/port-wiring.d.ts +4 -2
  8. package/dist/analysis/port-wiring.d.ts.map +1 -1
  9. package/dist/analysis/port-wiring.js +39 -10
  10. package/dist/analysis/port-wiring.js.map +1 -1
  11. package/dist/analysis/source-index.d.ts +2 -1
  12. package/dist/analysis/source-index.d.ts.map +1 -1
  13. package/dist/analysis/source-index.js +34 -5
  14. package/dist/analysis/source-index.js.map +1 -1
  15. package/dist/analysis/workspace-routes.d.ts +8 -0
  16. package/dist/analysis/workspace-routes.d.ts.map +1 -0
  17. package/dist/analysis/workspace-routes.js +291 -0
  18. package/dist/analysis/workspace-routes.js.map +1 -0
  19. package/dist/analysis/workspace.d.ts +24 -3
  20. package/dist/analysis/workspace.d.ts.map +1 -1
  21. package/dist/analysis/workspace.js +166 -21
  22. package/dist/analysis/workspace.js.map +1 -1
  23. package/dist/app-map-changes.d.ts.map +1 -1
  24. package/dist/app-map-changes.js +19 -7
  25. package/dist/app-map-changes.js.map +1 -1
  26. package/dist/app-map.d.ts.map +1 -1
  27. package/dist/app-map.js +58 -55
  28. package/dist/app-map.js.map +1 -1
  29. package/dist/config.d.ts +7 -0
  30. package/dist/config.d.ts.map +1 -1
  31. package/dist/config.js +19 -0
  32. package/dist/config.js.map +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +20 -0
  35. package/dist/index.js.map +1 -1
  36. package/dist/inspect.d.ts.map +1 -1
  37. package/dist/inspect.js +89 -21
  38. package/dist/inspect.js.map +1 -1
  39. package/dist/lint.d.ts.map +1 -1
  40. package/dist/lint.js +106 -38
  41. package/dist/lint.js.map +1 -1
  42. package/dist/make/shared.js +1 -1
  43. package/dist/make.js +18 -1
  44. package/dist/make.js.map +1 -1
  45. package/dist/provider-add.js +2 -2
  46. package/dist/provider-add.js.map +1 -1
  47. package/dist/provider-audit.d.ts.map +1 -1
  48. package/dist/provider-audit.js +22 -6
  49. package/dist/provider-audit.js.map +1 -1
  50. package/dist/templates/shared.js +3 -3
  51. package/package.json +3 -2
  52. package/skills/app-structure/SKILL.md +42 -0
  53. package/src/analysis/layers.ts +9 -0
  54. package/src/analysis/port-wiring.ts +66 -10
  55. package/src/analysis/source-index.ts +56 -4
  56. package/src/analysis/workspace-routes.ts +385 -0
  57. package/src/analysis/workspace.ts +281 -34
  58. package/src/app-map-changes.ts +31 -5
  59. package/src/app-map.ts +75 -75
  60. package/src/config.ts +33 -0
  61. package/src/index.ts +25 -0
  62. package/src/inspect.ts +123 -16
  63. package/src/lint.ts +146 -43
  64. package/src/make/shared.ts +1 -1
  65. package/src/make.ts +31 -1
  66. package/src/provider-add.ts +2 -2
  67. package/src/provider-audit.ts +30 -10
  68. package/src/templates/shared.ts +3 -3
package/src/app-map.ts CHANGED
@@ -23,7 +23,10 @@ import {
23
23
  import {
24
24
  type AnalysisWorkspace,
25
25
  createAnalysisWorkspace,
26
+ projectPath,
27
+ projectSource,
26
28
  } from "./analysis/workspace.js";
29
+ import { workspaceRouteContracts } from "./analysis/workspace-routes.js";
27
30
  import type { AppMapNodeKind } from "./app-map-schema.js";
28
31
  import { directoryPath } from "./config.js";
29
32
  import {
@@ -215,9 +218,8 @@ export async function mapApp(
215
218
  const definitions = new Map<string, DefinitionRecord>();
216
219
  const definitionBySource = new Map<string, string>();
217
220
  const registries: RegistryRecord[] = [];
218
- const featuresPath = directoryPath(workspace.config.paths.features);
219
- const featureNames = discoverFeatureNames(workspace, featuresPath);
220
- const testRoots = appTestRoots(workspace.config.paths);
221
+ const features = discoverFeatures(workspace);
222
+ const featureNames = [...features.keys()];
221
223
 
222
224
  const addNode = (node: AppMapNode) => {
223
225
  const existing = nodes.get(node.id);
@@ -235,11 +237,9 @@ export async function mapApp(
235
237
  };
236
238
  const addEdge = (edge: AppMapEdge) => edges.push(edge);
237
239
 
238
- for (const feature of featureNames) {
239
- const featureFiles = filesForFeature(
240
- workspace.files,
241
- featuresPath,
242
- feature,
240
+ for (const [feature, featurePath] of features) {
241
+ const featureFiles = workspace.files.filter((file) =>
242
+ file.startsWith(`${featurePath}/`),
243
243
  );
244
244
  const roles = summarizeFeatureFiles(featureFiles);
245
245
  addNode({
@@ -247,7 +247,7 @@ export async function mapApp(
247
247
  kind: "feature",
248
248
  name: feature,
249
249
  feature,
250
- source: { file: `${featuresPath}/${feature}` },
250
+ source: { file: featurePath },
251
251
  status: "declared",
252
252
  details: roles,
253
253
  });
@@ -255,10 +255,14 @@ export async function mapApp(
255
255
 
256
256
  for (const file of workspace.files.filter(
257
257
  (candidate) =>
258
- isAppTestFile(candidate) && isWithinAppTestRoots(candidate, testRoots),
258
+ isAppTestFile(candidate) &&
259
+ isWithinAppTestRoots(
260
+ projectSource(workspace, candidate).file,
261
+ appTestRoots(projectSource(workspace, candidate).project.config.paths),
262
+ ),
259
263
  )) {
260
264
  const id = `test:${file}`;
261
- const feature = featureForFile(file, featuresPath);
265
+ const feature = featureForFile(file, workspace);
262
266
  addNode({
263
267
  id,
264
268
  kind: "test",
@@ -276,7 +280,7 @@ export async function mapApp(
276
280
  const contractIdsByExport = new Map<string, string[]>();
277
281
  for (const contract of inspection.contracts) {
278
282
  const id = `contract:${contract.file}#${contract.exportName}`;
279
- const feature = featureForFile(contract.file, featuresPath);
283
+ const feature = featureForFile(contract.file, workspace);
280
284
  contractIds.set(symbolKey(contract.file, contract.exportName), id);
281
285
  const exports = contractIdsByExport.get(contract.exportName) ?? [];
282
286
  exports.push(id);
@@ -321,8 +325,8 @@ export async function mapApp(
321
325
  id,
322
326
  kind: "route",
323
327
  name: `${route.method} ${route.path}`,
324
- ...(featureForFile(route.file, featuresPath)
325
- ? { feature: featureForFile(route.file, featuresPath) }
328
+ ...(featureForFile(route.file, workspace)
329
+ ? { feature: featureForFile(route.file, workspace) }
326
330
  : {}),
327
331
  source: {
328
332
  file: route.handlerFile ?? route.file,
@@ -375,7 +379,7 @@ export async function mapApp(
375
379
  const runtimeName = literalText(useCaseCall.arguments[0]);
376
380
  if (runtimeName) {
377
381
  const kind = callMemberName(useCaseCall) as "command" | "query";
378
- const feature = featureForFile(file, featuresPath);
382
+ const feature = featureForFile(file, workspace);
379
383
  const id = `use-case:${runtimeName}`;
380
384
  const node: AppMapNode = {
381
385
  id,
@@ -418,7 +422,7 @@ export async function mapApp(
418
422
  : undefined;
419
423
  const runtimeName = definitionRuntimeName(call, definitionCall);
420
424
  if (kind && runtimeName) {
421
- const feature = featureForFile(file, featuresPath);
425
+ const feature = featureForFile(file, workspace);
422
426
  const id = `${kind}:${runtimeName}`;
423
427
  const status = definitionStatus(
424
428
  kind,
@@ -458,7 +462,7 @@ export async function mapApp(
458
462
 
459
463
  const policyCall = findCall(initializer, new Set(["definePolicy"]));
460
464
  if (policyCall && ts.isObjectLiteralExpression(policyCall.arguments[0])) {
461
- const feature = featureForFile(file, featuresPath);
465
+ const feature = featureForFile(file, workspace);
462
466
  const policyId = `policy:${file}#${exportName}`;
463
467
  addNode({
464
468
  id: policyId,
@@ -676,8 +680,11 @@ export async function mapApp(
676
680
  contractIdsByExport,
677
681
  routeIds,
678
682
  definitionBySource,
679
- featuresPath,
680
683
  diagnostics: inspection.diagnostics,
684
+ registeredGroups:
685
+ workspace.projects.length > 1
686
+ ? (await workspaceRouteContracts(workspace)).groups
687
+ : undefined,
681
688
  });
682
689
 
683
690
  await addOpenApiSurface({
@@ -711,7 +718,6 @@ export async function mapApp(
711
718
  await addUnresolvedLocalImports({ workspace, unresolved });
712
719
  await addFeatureDependencies({
713
720
  workspace,
714
- featuresPath,
715
721
  addEdge,
716
722
  featureNames: new Set(featureNames),
717
723
  });
@@ -928,12 +934,12 @@ async function addRouteGroups(args: {
928
934
  contractIdsByExport: Map<string, string[]>;
929
935
  routeIds: Map<string, string>;
930
936
  definitionBySource: Map<string, string>;
931
- featuresPath: string;
937
+ registeredGroups?: Set<string>;
932
938
  diagnostics: InspectDiagnostic[];
933
939
  }): Promise<void> {
934
940
  const routeFiles = args.workspace.files.filter(
935
941
  (file) =>
936
- featureForFile(file, args.featuresPath) !== undefined &&
942
+ featureForFile(file, args.workspace) !== undefined &&
937
943
  isAnalysisSourceFile(file) &&
938
944
  stripSourceFileExtension(file).endsWith("/routes"),
939
945
  );
@@ -949,17 +955,20 @@ async function addRouteGroups(args: {
949
955
  const options = call?.arguments[0];
950
956
  if (!call || !options || !ts.isObjectLiteralExpression(options)) continue;
951
957
 
952
- const feature = featureForFile(file, args.featuresPath);
958
+ const feature = featureForFile(file, args.workspace);
953
959
  const groupName = literalText(
954
960
  propertyAssignment(options, "name")?.initializer,
955
961
  );
956
962
  const id = `route-group:${file}#${exportName}`;
957
- const missing = args.diagnostics.some(
958
- (diagnostic) =>
959
- diagnostic.code === "BEIGNET_ROUTE_GROUP_UNREGISTERED" &&
960
- diagnostic.subject?.file === file &&
961
- diagnostic.subject.exportName === exportName,
962
- );
963
+ const missing =
964
+ (args.registeredGroups !== undefined &&
965
+ !args.registeredGroups.has(`${file}#${exportName}`)) ||
966
+ args.diagnostics.some(
967
+ (diagnostic) =>
968
+ diagnostic.code === "BEIGNET_ROUTE_GROUP_UNREGISTERED" &&
969
+ diagnostic.subject?.file === file &&
970
+ diagnostic.subject.exportName === exportName,
971
+ );
963
972
  args.addNode({
964
973
  id,
965
974
  kind: "route-group",
@@ -1080,8 +1089,10 @@ async function addPorts(args: {
1080
1089
  }): Promise<void> {
1081
1090
  const portsFile = args.workspace.config.paths.ports;
1082
1091
  if (!args.workspace.fileSet.has(portsFile)) return;
1083
- const sourceFile = await args.workspace.readSourceFile(portsFile);
1084
- const analysis = await analyzePortWiring(args.workspace);
1092
+ const analysis = await analyzePortWiring(
1093
+ args.workspace,
1094
+ args.workspace.projects.length > 1 ? args.sourceIndex : undefined,
1095
+ );
1085
1096
  if (analysis.declarationIndeterminate) {
1086
1097
  args.unresolved.push({
1087
1098
  code: "app_ports_declaration_unresolved",
@@ -1098,11 +1109,12 @@ async function addPorts(args: {
1098
1109
  }
1099
1110
  for (const name of analysis.declared) {
1100
1111
  const property = analysis.properties.get(name);
1112
+ const propertyFile = analysis.propertyFiles.get(name) ?? portsFile;
1101
1113
  const feature = property
1102
1114
  ? await portFeature(
1103
1115
  property.type,
1104
- portsFile,
1105
- args.workspace.config.paths.features,
1116
+ propertyFile,
1117
+ args.workspace,
1106
1118
  args.sourceIndex,
1107
1119
  )
1108
1120
  : undefined;
@@ -1113,7 +1125,7 @@ async function addPorts(args: {
1113
1125
  name,
1114
1126
  ...(feature ? { feature } : {}),
1115
1127
  source: property
1116
- ? await args.sourceIndex.sourceLocation(portsFile, property)
1128
+ ? await args.sourceIndex.sourceLocation(propertyFile, property)
1117
1129
  : { file: args.workspace.config.paths.portWiring },
1118
1130
  status:
1119
1131
  analysis.bound.has(name) || analysis.deferred.has(name)
@@ -1125,7 +1137,7 @@ async function addPorts(args: {
1125
1137
  : analysis.deferred.has(name)
1126
1138
  ? "deferred"
1127
1139
  : "unresolved",
1128
- ...(property?.type ? { type: property.type.getText(sourceFile) } : {}),
1140
+ ...(property?.type ? { type: property.type.getText() } : {}),
1129
1141
  },
1130
1142
  });
1131
1143
  if (feature) {
@@ -1447,7 +1459,6 @@ async function addTables(args: {
1447
1459
 
1448
1460
  async function addFeatureDependencies(args: {
1449
1461
  workspace: AnalysisWorkspace;
1450
- featuresPath: string;
1451
1462
  addEdge(edge: AppMapEdge): void;
1452
1463
  featureNames: Set<string>;
1453
1464
  }): Promise<void> {
@@ -1466,11 +1477,11 @@ async function addFeatureDependencies(args: {
1466
1477
  >();
1467
1478
 
1468
1479
  for (const file of args.workspace.files.filter(isAnalysisSourceFile)) {
1469
- const from = featureForFile(file, args.featuresPath);
1480
+ const from = featureForFile(file, args.workspace);
1470
1481
  if (!from || !args.featureNames.has(from)) continue;
1471
1482
  for (const reference of await localImportReferences(args.workspace, file)) {
1472
1483
  if (!reference.resolvedFile) continue;
1473
- const to = featureForFile(reference.resolvedFile, args.featuresPath);
1484
+ const to = featureForFile(reference.resolvedFile, args.workspace);
1474
1485
  if (!to || to === from || !args.featureNames.has(to)) continue;
1475
1486
  const key = `${from}->${to}`;
1476
1487
  const current = dependencies.get(key) ?? {
@@ -1569,16 +1580,16 @@ async function addUnresolvedContractDeclarations(args: {
1569
1580
  }
1570
1581
 
1571
1582
  function appMapContractSourceFiles(workspace: AnalysisWorkspace): string[] {
1572
- const contractsPath = directoryPath(workspace.config.paths.contracts);
1573
- const featureOwned =
1574
- contractsPath === directoryPath(workspace.config.paths.features);
1575
-
1576
1583
  return workspace.files.filter((file) => {
1577
1584
  if (!isAnalysisSourceFile(file) || isAppTestFile(file)) return false;
1578
- const sourcePath = stripSourceFileExtension(file);
1585
+ const source = projectSource(workspace, file);
1586
+ const contractsPath = directoryPath(source.project.config.paths.contracts);
1587
+ const featureOwned =
1588
+ contractsPath === directoryPath(source.project.config.paths.features);
1589
+ const sourcePath = stripSourceFileExtension(source.file);
1579
1590
  if (featureOwned) {
1580
1591
  const prefix = `${contractsPath}/`;
1581
- if (!file.startsWith(prefix)) return false;
1592
+ if (!source.file.startsWith(prefix)) return false;
1582
1593
  const parts = sourcePath.slice(prefix.length).split("/");
1583
1594
  return parts.length === 2 && parts[1] === "contracts";
1584
1595
  }
@@ -1599,10 +1610,7 @@ async function registryRecord(args: {
1599
1610
  if (!initializer) return undefined;
1600
1611
  const registry = registryValues(initializer, args.exportName);
1601
1612
  if (!registry) return undefined;
1602
- const feature = featureForFile(
1603
- args.file,
1604
- directoryPath(args.workspace.config.paths.features),
1605
- );
1613
+ const feature = featureForFile(args.file, args.workspace);
1606
1614
  return {
1607
1615
  file: args.file,
1608
1616
  declaration: args.declaration,
@@ -1814,27 +1822,19 @@ function arrayExpressions(array: ts.ArrayLiteralExpression): ts.Expression[] {
1814
1822
  });
1815
1823
  }
1816
1824
 
1817
- function discoverFeatureNames(
1818
- workspace: AnalysisWorkspace,
1819
- featuresPath: string,
1820
- ): string[] {
1821
- const names = new Set<string>();
1822
- const prefix = `${featuresPath}/`;
1825
+ function discoverFeatures(workspace: AnalysisWorkspace): Map<string, string> {
1826
+ const features = new Map<string, string>();
1823
1827
  for (const file of workspace.files) {
1824
- if (!file.startsWith(prefix)) continue;
1825
- const feature = file.slice(prefix.length).split("/")[0];
1826
- if (feature && feature !== "shared") names.add(feature);
1828
+ const feature = featureForFile(file, workspace);
1829
+ if (!feature) continue;
1830
+ const source = projectSource(workspace, file);
1831
+ const root = directoryPath(source.project.config.paths.features);
1832
+ const name = source.file.slice(`${root}/`.length).split("/")[0];
1833
+ features.set(feature, projectPath(source.project, `${root}/${name}`));
1827
1834
  }
1828
- return [...names].sort();
1829
- }
1830
-
1831
- function filesForFeature(
1832
- files: string[],
1833
- featuresPath: string,
1834
- feature: string,
1835
- ): string[] {
1836
- const prefix = `${featuresPath}/${feature}/`;
1837
- return files.filter((file) => file.startsWith(prefix));
1835
+ return new Map(
1836
+ [...features].sort(([left], [right]) => left.localeCompare(right)),
1837
+ );
1838
1838
  }
1839
1839
 
1840
1840
  function summarizeFeatureFiles(files: string[]): Record<string, number> {
@@ -1849,18 +1849,20 @@ function summarizeFeatureFiles(files: string[]): Record<string, number> {
1849
1849
 
1850
1850
  function featureForFile(
1851
1851
  file: string,
1852
- featuresPath: string,
1852
+ workspace: AnalysisWorkspace,
1853
1853
  ): string | undefined {
1854
- const prefix = `${featuresPath}/`;
1855
- if (!file.startsWith(prefix)) return undefined;
1856
- const feature = file.slice(prefix.length).split("/")[0];
1857
- return feature && feature !== "shared" ? feature : undefined;
1854
+ const source = projectSource(workspace, file);
1855
+ const prefix = `${directoryPath(source.project.config.paths.features)}/`;
1856
+ if (!source.file.startsWith(prefix)) return undefined;
1857
+ const feature = source.file.slice(prefix.length).split("/")[0];
1858
+ if (!feature || feature === "shared") return undefined;
1859
+ return source.project.prefix ? `${source.project.name}:${feature}` : feature;
1858
1860
  }
1859
1861
 
1860
1862
  async function portFeature(
1861
1863
  type: ts.TypeNode | undefined,
1862
1864
  file: string,
1863
- featuresPath: string,
1865
+ workspace: AnalysisWorkspace,
1864
1866
  sourceIndex: ReturnType<typeof createSourceIndex>,
1865
1867
  ): Promise<string | undefined> {
1866
1868
  if (
@@ -1871,9 +1873,7 @@ async function portFeature(
1871
1873
  return undefined;
1872
1874
  }
1873
1875
  const target = await sourceIndex.resolveName(file, type.typeName.text);
1874
- return target
1875
- ? featureForFile(target.file, directoryPath(featuresPath))
1876
- : undefined;
1876
+ return target ? featureForFile(target.file, workspace) : undefined;
1877
1877
  }
1878
1878
 
1879
1879
  function definitionStatus(
package/src/config.ts CHANGED
@@ -67,6 +67,12 @@ export type BeignetProviderAuditConfig = {
67
67
  ignoreRequiredEnv?: string[];
68
68
  };
69
69
 
70
+ /** Source packages included in read-only application analysis. */
71
+ export type BeignetWorkspaceConfig = {
72
+ /** Package directories relative to this app, including transitive dependencies. */
73
+ packages: string[];
74
+ };
75
+
70
76
  /**
71
77
  * User-authored Beignet CLI config.
72
78
  */
@@ -75,6 +81,7 @@ export type BeignetConfig = {
75
81
  paths?: Partial<BeignetPaths>;
76
82
  database?: BeignetDatabaseConfig;
77
83
  providerAudit?: BeignetProviderAuditConfig;
84
+ workspace?: BeignetWorkspaceConfig;
78
85
  };
79
86
 
80
87
  /**
@@ -90,6 +97,7 @@ export type ResolvedBeignetConfig = {
90
97
  providerAudit: {
91
98
  ignoreRequiredEnv: string[];
92
99
  };
100
+ workspace: BeignetWorkspaceConfig;
93
101
  configFile?: string;
94
102
  };
95
103
 
@@ -134,6 +142,7 @@ export const defaultBeignetConfig: ResolvedBeignetConfig = {
134
142
  providerAudit: {
135
143
  ignoreRequiredEnv: [],
136
144
  },
145
+ workspace: { packages: [] },
137
146
  };
138
147
 
139
148
  /**
@@ -174,6 +183,7 @@ export function resolveConfig(
174
183
  paths: normalizeConfigPaths(paths),
175
184
  database: normalizeDatabaseConfig(database),
176
185
  providerAudit: normalizeProviderAuditConfig(providerAudit),
186
+ workspace: normalizeWorkspaceConfig(config.workspace),
177
187
  configFile,
178
188
  };
179
189
  }
@@ -453,6 +463,29 @@ function normalizeIgnoredRequiredEnv(index: number, value: string): string {
453
463
  return value.trim();
454
464
  }
455
465
 
466
+ function normalizeWorkspaceConfig(
467
+ workspace: BeignetWorkspaceConfig | undefined,
468
+ ): BeignetWorkspaceConfig {
469
+ if (workspace === undefined) return { packages: [] };
470
+ if (!isRecord(workspace) || !Array.isArray(workspace.packages)) {
471
+ throw new Error("Beignet config workspace.packages must be an array.");
472
+ }
473
+ const packages = workspace.packages.map((value, index) => {
474
+ if (
475
+ typeof value !== "string" ||
476
+ !value.trim() ||
477
+ path.isAbsolute(value.trim()) ||
478
+ path.win32.isAbsolute(value.trim())
479
+ ) {
480
+ throw new Error(
481
+ `Beignet config workspace.packages[${index}] must be a relative package directory.`,
482
+ );
483
+ }
484
+ return path.posix.normalize(normalizePath(value.trim())).replace(/\/$/, "");
485
+ });
486
+ return { packages: [...new Set(packages)].sort() };
487
+ }
488
+
456
489
  function isRecord(value: unknown): value is Record<string, unknown> {
457
490
  return typeof value === "object" && value !== null && !Array.isArray(value);
458
491
  }
package/src/index.ts CHANGED
@@ -1853,6 +1853,30 @@ const scheduleRoutes = buildRouteMap({
1853
1853
  },
1854
1854
  });
1855
1855
 
1856
+ const encryptionKeyCommand = buildCommand<{ json?: boolean }, [], CliContext>({
1857
+ docs: {
1858
+ brief: "Print a new encryption key without changing environment files.",
1859
+ },
1860
+ parameters: {
1861
+ flags: { json: jsonFlag },
1862
+ },
1863
+ loader: async () => {
1864
+ const { generateEncryptionKey } = await import("@beignet/core/encryption");
1865
+ return async function runEncryptionKey(this: CliContext, flags) {
1866
+ const key = generateEncryptionKey();
1867
+ writeOutput(
1868
+ this,
1869
+ flags.json ? JSON.stringify({ schemaVersion: 1, key }) : key,
1870
+ );
1871
+ };
1872
+ },
1873
+ });
1874
+
1875
+ const encryptionRoutes = buildRouteMap({
1876
+ docs: { brief: "Manage application encryption." },
1877
+ routes: { key: encryptionKeyCommand },
1878
+ });
1879
+
1856
1880
  const completionShellFlagParameters = {
1857
1881
  shell: {
1858
1882
  kind: "enum",
@@ -2449,6 +2473,7 @@ Run npm create beignet@latest (or bun create beignet) to scaffold a new app.`,
2449
2473
  create: createCommand,
2450
2474
  db: dbRoutes,
2451
2475
  doctor: doctorCommand,
2476
+ encryption: encryptionRoutes,
2452
2477
  explain: explainRoutes,
2453
2478
  lint: lintCommand,
2454
2479
  map: mapCommand,