vovk-cli 0.0.1-draft.7 → 0.0.1-draft.70

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 (81) hide show
  1. package/README.md +29 -1
  2. package/client-templates/main/main.cjs.ejs +15 -0
  3. package/client-templates/main/main.d.cts.ejs +14 -0
  4. package/client-templates/module/module.d.mts.ejs +14 -0
  5. package/client-templates/module/module.mjs.ejs +24 -0
  6. package/client-templates/python/__init__.py +276 -0
  7. package/client-templates/ts/index.ts.ejs +25 -0
  8. package/dist/dev/diffSchema.d.mts +36 -0
  9. package/dist/{watcher → dev}/diffSchema.mjs +3 -11
  10. package/dist/{watcher → dev}/ensureSchemaFiles.d.mts +3 -0
  11. package/dist/{watcher → dev}/ensureSchemaFiles.mjs +26 -15
  12. package/dist/dev/index.d.mts +6 -0
  13. package/dist/{watcher → dev}/index.mjs +129 -62
  14. package/dist/{watcher → dev}/isMetadataEmpty.mjs +1 -1
  15. package/dist/{watcher → dev}/logDiffResult.d.mts +2 -2
  16. package/dist/dev/logDiffResult.mjs +57 -0
  17. package/dist/{watcher → dev}/writeOneSchemaFile.d.mts +1 -1
  18. package/dist/{watcher → dev}/writeOneSchemaFile.mjs +2 -2
  19. package/dist/generate/ensureClient.d.mts +5 -0
  20. package/dist/generate/ensureClient.mjs +27 -0
  21. package/dist/generate/getClientTemplates.d.mts +14 -0
  22. package/dist/generate/getClientTemplates.mjs +28 -0
  23. package/dist/generate/index.d.mts +13 -0
  24. package/dist/generate/index.mjs +80 -0
  25. package/dist/getProjectInfo/getConfig.d.mts +3 -3
  26. package/dist/getProjectInfo/getConfig.mjs +8 -5
  27. package/dist/getProjectInfo/getConfigAbsolutePaths.mjs +2 -2
  28. package/dist/getProjectInfo/getRelativeSrcRoot.mjs +1 -1
  29. package/dist/getProjectInfo/getUserConfig.mjs +3 -1
  30. package/dist/getProjectInfo/importUncachedModule.mjs +0 -1
  31. package/dist/getProjectInfo/importUncachedModuleWorker.mjs +0 -1
  32. package/dist/getProjectInfo/index.d.mts +14 -5
  33. package/dist/getProjectInfo/index.mjs +21 -13
  34. package/dist/index.d.mts +1 -27
  35. package/dist/index.mjs +59 -65
  36. package/dist/init/checkTSConfigForExperimentalDecorators.mjs +2 -2
  37. package/dist/init/createConfig.d.mts +3 -4
  38. package/dist/init/createConfig.mjs +9 -8
  39. package/dist/init/getTemplateFilesFromPackage.d.mts +2 -1
  40. package/dist/init/getTemplateFilesFromPackage.mjs +4 -5
  41. package/dist/init/index.d.mts +2 -3
  42. package/dist/init/index.mjs +61 -97
  43. package/dist/init/installDependencies.d.mts +4 -1
  44. package/dist/init/installDependencies.mjs +2 -2
  45. package/dist/init/logUpdateDependenciesError.d.mts +11 -0
  46. package/dist/init/logUpdateDependenciesError.mjs +45 -0
  47. package/dist/init/updateDependenciesWithoutInstalling.d.mts +3 -2
  48. package/dist/init/updateDependenciesWithoutInstalling.mjs +13 -8
  49. package/dist/init/updateNPMScripts.d.mts +3 -1
  50. package/dist/init/updateNPMScripts.mjs +10 -6
  51. package/dist/init/updateTypeScriptConfig.mjs +2 -2
  52. package/dist/initProgram.d.mts +2 -0
  53. package/dist/initProgram.mjs +22 -0
  54. package/dist/locateSegments.d.mts +7 -1
  55. package/dist/locateSegments.mjs +9 -6
  56. package/dist/new/addClassToSegmentCode.d.mts +1 -2
  57. package/dist/new/addClassToSegmentCode.mjs +9 -5
  58. package/dist/new/addCommonTerms.mjs +1 -0
  59. package/dist/new/index.d.mts +2 -2
  60. package/dist/new/index.mjs +3 -3
  61. package/dist/new/newModule.d.mts +3 -3
  62. package/dist/new/newModule.mjs +38 -27
  63. package/dist/new/newSegment.mjs +8 -6
  64. package/dist/new/render.mjs +2 -5
  65. package/dist/postinstall.mjs +16 -19
  66. package/dist/types.d.mts +48 -9
  67. package/dist/utils/debounceWithArgs.d.mts +1 -1
  68. package/dist/utils/debounceWithArgs.mjs +24 -9
  69. package/dist/utils/formatLoggedSegmentName.mjs +1 -1
  70. package/dist/utils/getAvailablePort.mjs +3 -2
  71. package/dist/utils/getFileSystemEntryType.mjs +1 -1
  72. package/package.json +21 -18
  73. package/templates/controller.ejs +12 -11
  74. package/templates/service.ejs +6 -6
  75. package/dist/generateClient.d.mts +0 -7
  76. package/dist/generateClient.mjs +0 -97
  77. package/dist/watcher/diffSchema.d.mts +0 -43
  78. package/dist/watcher/index.d.mts +0 -6
  79. package/dist/watcher/logDiffResult.mjs +0 -90
  80. package/templates/worker.ejs +0 -1
  81. /package/dist/{watcher → dev}/isMetadataEmpty.d.mts +0 -0
@@ -1,11 +1,11 @@
1
- import path from 'path';
2
- import fs from 'fs/promises';
3
- import getProjectInfo from '../getProjectInfo/index.mjs';
1
+ import path from 'node:path';
2
+ import fs from 'node:fs/promises';
4
3
  import render from './render.mjs';
4
+ import addClassToSegmentCode from './addClassToSegmentCode.mjs';
5
+ import getProjectInfo from '../getProjectInfo/index.mjs';
6
+ import locateSegments from '../locateSegments.mjs';
5
7
  import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
6
8
  import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
7
- import locateSegments from '../locateSegments.mjs';
8
- import addClassToSegmentCode from './addClassToSegmentCode.mjs';
9
9
  import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
10
10
  import prettify from '../utils/prettify.mjs';
11
11
  function splitByLast(str, delimiter = '/') {
@@ -18,49 +18,61 @@ function splitByLast(str, delimiter = '/') {
18
18
  const after = str.substring(index + delimiter.length);
19
19
  return [before, after];
20
20
  }
21
- export default async function newModule({ what, moduleNameWithOptionalSegment, dryRun, dirName: dirNameFlag, template: templateFlag, noSegmentUpdate, overwrite, }) {
21
+ export default async function newModule({ what, moduleNameWithOptionalSegment, dryRun, dir: dirFlag, templates: templatesFlag, noSegmentUpdate, overwrite, }) {
22
22
  const { config, log, apiDir, cwd } = await getProjectInfo();
23
- const templates = config.templates;
23
+ let templates = config.templates;
24
24
  const [segmentName, moduleName] = splitByLast(moduleNameWithOptionalSegment);
25
- // replace c by controller, s by service, w by worker, everything else keeps the same
25
+ // replace c by controller, s by service, everything else keeps the same
26
26
  what = what.map((s) => {
27
27
  switch (s) {
28
28
  case 'c':
29
29
  return 'controller';
30
30
  case 's':
31
31
  return 'service';
32
- case 'w':
33
- return 'worker';
34
32
  default:
35
33
  return s;
36
34
  }
37
35
  });
38
- // check if template exists
36
+ if (templatesFlag) {
37
+ if (templatesFlag.length > what.length) {
38
+ throw new Error('Too many templates provided');
39
+ }
40
+ templates = templatesFlag.reduce((acc, templatePath, index) => ({
41
+ ...acc,
42
+ [what[index]]: templatePath,
43
+ }), templates);
44
+ }
39
45
  for (const type of what) {
40
46
  if (!templates[type]) {
41
- throw new Error(`Template for ${type} not found in config`);
47
+ throw new Error(`Template for "${type}" not found`);
42
48
  }
43
49
  }
44
- const segments = await locateSegments(apiDir);
50
+ const segments = await locateSegments({ dir: apiDir, config });
45
51
  const segment = segments.find((s) => s.segmentName === segmentName);
46
52
  if (!segment) {
47
- throw new Error(`Segment ${segmentName} not found`);
53
+ throw new Error(`Unable to create module. Segment "${segmentName}" not found. Run "vovk new segment ${segmentName}" to create it`);
48
54
  }
49
55
  for (const type of what) {
50
- const templatePath = templateFlag ?? templates[type];
56
+ const templatePath = templates[type];
51
57
  const templateAbsolutePath = templatePath.startsWith('/') || templatePath.startsWith('.')
52
58
  ? path.resolve(cwd, templatePath)
53
59
  : path.resolve(cwd, './node_modules', templatePath);
54
60
  const templateCode = await fs.readFile(templateAbsolutePath, 'utf-8');
55
- const { dirName: renderedDirName, fileName, sourceName, compiledName, code, } = await render(templateCode, {
61
+ const { dir: renderedDir, fileName, sourceName, compiledName, code, } = await render(templateCode, {
56
62
  cwd,
57
63
  config,
58
64
  withService: what.includes('service'),
59
65
  segmentName,
60
66
  moduleName,
61
67
  });
62
- const dirName = dirNameFlag || renderedDirName;
63
- const absoluteModuleDir = path.join(cwd, config.modulesDir, dirName);
68
+ const dir = dirFlag || renderedDir;
69
+ if (!dir) {
70
+ throw new Error(`The template for "${type}" does not provide a dir`);
71
+ }
72
+ if (!fileName) {
73
+ throw new Error(`The template for "${type}" does not provide a fileName`);
74
+ }
75
+ const absoluteModuleDir = path.join(cwd, dir);
64
76
  const absoluteModulePath = path.join(absoluteModuleDir, fileName);
65
77
  const prettiedCode = await prettify(code, absoluteModulePath);
66
78
  if (!dryRun) {
@@ -70,31 +82,30 @@ export default async function newModule({ what, moduleNameWithOptionalSegment, d
70
82
  else {
71
83
  await fs.mkdir(absoluteModuleDir, { recursive: true });
72
84
  await fs.writeFile(absoluteModulePath, prettiedCode);
85
+ log.info(`Created ${chalkHighlightThing(fileName)} using ${chalkHighlightThing(`"${type}"`)} template for ${formatLoggedSegmentName(segmentName)}`);
73
86
  }
74
87
  }
75
- if (type === 'controller' || type === 'worker') {
88
+ if (type === 'controller') {
76
89
  if (!sourceName) {
77
- throw new Error('sourceName is required');
90
+ throw new Error(`The template for "${type}" does not provide a sourceName`);
78
91
  }
79
92
  if (!compiledName) {
80
- throw new Error('compiledName is required');
93
+ throw new Error(`The template for "${type}" does not provide a compiledName`);
81
94
  }
82
95
  const { routeFilePath } = segment;
83
96
  const segmentSourceCode = await fs.readFile(routeFilePath, 'utf-8');
84
- const importPath = path.relative(absoluteModuleDir, absoluteModulePath).replace(/\.(ts|tsx)$/, '');
97
+ const importPath = path.relative(path.dirname(routeFilePath), absoluteModulePath).replace(/\.(ts|tsx)$/, '');
85
98
  if (!noSegmentUpdate) {
86
- const newSegmentCode = addClassToSegmentCode(segmentSourceCode, {
99
+ const newSegmentCode = await prettify(addClassToSegmentCode(segmentSourceCode, {
87
100
  sourceName,
88
101
  compiledName,
89
- type,
90
102
  importPath,
91
- });
103
+ }), routeFilePath);
92
104
  if (!dryRun) {
93
105
  await fs.writeFile(routeFilePath, newSegmentCode);
94
106
  }
95
107
  }
96
- log.info(`Added ${chalkHighlightThing(sourceName)} ${type} to ${formatLoggedSegmentName(segmentName)} as ${chalkHighlightThing(compiledName)}`);
108
+ log.info(`Added ${chalkHighlightThing(sourceName)} ${type} as ${chalkHighlightThing(compiledName)} to ${formatLoggedSegmentName(segmentName)}`);
97
109
  }
98
- log.info(`Created ${chalkHighlightThing(fileName)} using "${chalkHighlightThing(type)}" template for ${formatLoggedSegmentName(segmentName)}`);
99
110
  }
100
111
  }
@@ -1,10 +1,11 @@
1
- import path from 'path';
2
- import fs from 'fs/promises';
1
+ import path from 'node:path';
2
+ import fs from 'node:fs/promises';
3
3
  import getProjectInfo from '../getProjectInfo/index.mjs';
4
4
  import getFileSystemEntryType from '../utils/getFileSystemEntryType.mjs';
5
5
  import chalkHighlightThing from '../utils/chalkHighlightThing.mjs';
6
6
  import formatLoggedSegmentName from '../utils/formatLoggedSegmentName.mjs';
7
7
  import prettify from '../utils/prettify.mjs';
8
+ import chalk from 'chalk';
8
9
  export default async function newSegment({ segmentName, overwrite, dryRun, }) {
9
10
  const { apiDir, cwd, log } = await getProjectInfo();
10
11
  const absoluteSegmentRoutePath = path.join(cwd, apiDir, segmentName, '[[...vovk]]/route.ts');
@@ -13,15 +14,14 @@ export default async function newSegment({ segmentName, overwrite, dryRun, }) {
13
14
  }
14
15
  const code = await prettify(`import { initVovk } from 'vovk';
15
16
 
17
+ export const runtime = 'edge';
18
+
16
19
  const controllers = {};
17
- const workers = {};
18
20
 
19
21
  export type Controllers = typeof controllers;
20
- export type Workers = typeof workers;
21
22
 
22
23
  export const { GET, POST, PATCH, PUT, HEAD, OPTIONS, DELETE } = initVovk({
23
24
  ${segmentName ? ` segmentName: '${segmentName}',\n` : ''} emitSchema: true,
24
- workers,
25
25
  controllers,
26
26
  });
27
27
  `, absoluteSegmentRoutePath);
@@ -29,5 +29,7 @@ ${segmentName ? ` segmentName: '${segmentName}',\n` : ''} emitSchema: true,
29
29
  await fs.mkdir(path.dirname(absoluteSegmentRoutePath), { recursive: true });
30
30
  await fs.writeFile(absoluteSegmentRoutePath, code);
31
31
  }
32
- log.info(`${formatLoggedSegmentName(segmentName, { upperFirst: true })} created at ${absoluteSegmentRoutePath}. Run ${chalkHighlightThing(`vovk new controller ${segmentName}/someName`)} to create a controller or modify the segment file manually`);
32
+ log.info(`${formatLoggedSegmentName(segmentName, { upperFirst: true })} created at ${absoluteSegmentRoutePath}.`);
33
+ const dir = chalk.cyanBright([segmentName, 'thing'].filter(Boolean).join('/'));
34
+ log.info(`Run ${chalkHighlightThing(`npx vovk new service controller ${dir}`)} to create a new controller with a service at modules/${dir} folder for this segment`);
33
35
  }
@@ -20,14 +20,11 @@ export default async function render(codeTemplate, { config, withService, segmen
20
20
  _, // lodash
21
21
  pluralize,
22
22
  };
23
- // first, render the front matter because it can use ejs variables
24
23
  const parsed = matter((await ejs.render(codeTemplate, templateVars, { async: true })).trim());
25
- const { dirName, fileName, sourceName, compiledName } = parsed.data;
24
+ const { dir, fileName, sourceName, compiledName } = parsed.data;
26
25
  const code = parsed.content;
27
- // const templateContent = parsed.content; TODO
28
- // const code = await ejs.render(templateContent, templateVars, { async: true });
29
26
  return {
30
- dirName,
27
+ dir,
31
28
  fileName,
32
29
  sourceName,
33
30
  compiledName,
@@ -1,24 +1,21 @@
1
- import { promises as fs } from 'fs';
2
- import path from 'path';
3
- /**
4
- * Checks if a file exists at the given path.
5
- * @param {string} filePath - The path to the file.
6
- * @returns {Promise<boolean>} - A promise that resolves to true if the file exists, false otherwise.
7
- */
8
- const getFileSystemEntryType = async (filePath) => !!(await fs.stat(filePath).catch(() => false));
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import getFileSystemEntryType from './utils/getFileSystemEntryType.mjs';
9
4
  async function postinstall() {
10
- const vovk = path.join(import.meta.dirname, '../../.vovk');
11
- const js = path.join(vovk, 'client.js');
12
- const ts = path.join(vovk, 'client.d.ts');
5
+ // TODO: The function doesn't consider client templates, how to do that?
6
+ const vovk = path.join(import.meta.dirname, '../../.vovk-client');
7
+ const js = path.join(vovk, 'compiled.js');
8
+ const ts = path.join(vovk, 'compiled.d.ts');
13
9
  const index = path.join(vovk, 'index.ts');
14
- if ((await getFileSystemEntryType(js)) ||
15
- (await getFileSystemEntryType(ts)) ||
16
- (await getFileSystemEntryType(index))) {
17
- return;
18
- }
19
10
  await fs.mkdir(vovk, { recursive: true });
20
- await fs.writeFile(js, '/* postinstall */');
21
- await fs.writeFile(ts, '/* postinstall */');
22
- await fs.writeFile(index, '/* postinstall */');
11
+ if (!(await getFileSystemEntryType(js))) {
12
+ await fs.writeFile(js, '/* postinstall */');
13
+ }
14
+ if (!(await getFileSystemEntryType(ts))) {
15
+ await fs.writeFile(ts, '/* postinstall */');
16
+ }
17
+ if (!(await getFileSystemEntryType(index))) {
18
+ await fs.writeFile(index, '/* postinstall */');
19
+ }
23
20
  }
24
21
  void postinstall();
package/dist/types.d.mts CHANGED
@@ -1,13 +1,13 @@
1
- import { LogLevelNames } from 'loglevel';
1
+ import type { LogLevelNames } from 'loglevel';
2
2
  export type KnownAny = any;
3
3
  export type VovkEnv = {
4
4
  PORT?: string;
5
5
  VOVK_CLIENT_OUT_DIR?: string;
6
6
  VOVK_SCHEMA_OUT_DIR?: string;
7
- VOVK_FETCHER?: string;
8
- VOVK_VALIDATE_ON_CLIENT?: string;
7
+ VOVK_FETCHER_PATH?: string;
8
+ VOVK_VALIDATE_ON_CLIENT_PATH?: string;
9
+ VOVK_CREATE_RPC_PATH?: string;
9
10
  VOVK_MODULES_DIR?: string;
10
- VOVK_VALIDATION_LIBRARY?: string;
11
11
  VOVK_ORIGIN?: string;
12
12
  VOVK_ROOT_ENTRY?: string;
13
13
  VOVK_API_ENTRY_POINT?: string;
@@ -16,31 +16,70 @@ export type VovkEnv = {
16
16
  VOVK_PRETTIFY_CLIENT?: string;
17
17
  VOVK_DEV_HTTPS?: string;
18
18
  __VOVK_START_WATCHER_IN_STANDALONE_MODE__?: 'true';
19
+ __VOVK_EXIT__?: 'true' | 'false';
19
20
  };
20
21
  export type VovkConfig = {
21
22
  clientOutDir?: string;
22
23
  schemaOutDir?: string;
23
- fetcher?: string;
24
- validateOnClient?: string | null;
24
+ fetcherImport?: string | string[];
25
+ validateOnClientImport?: string | string[] | null;
26
+ createRPCImport?: string | string[];
25
27
  modulesDir?: string;
26
- validationLibrary?: string | null;
27
28
  rootEntry?: string;
28
29
  origin?: string;
29
30
  rootSegmentModulesDirName?: string;
30
31
  logLevel?: LogLevelNames;
31
32
  prettifyClient?: boolean;
32
33
  devHttps?: boolean;
34
+ experimental_clientGenerateTemplateNames?: string[];
33
35
  templates?: {
34
36
  service?: string;
35
37
  controller?: string;
36
- worker?: string;
37
38
  [key: string]: string | undefined;
38
39
  };
39
40
  };
41
+ export type VovkStrictConfig = Required<Omit<VovkConfig, 'validateOnClientImport' | 'fetcherImport' | 'createRPCImport'>> & {
42
+ validateOnClientImport: string[] | null;
43
+ fetcherImport: string[];
44
+ createRPCImport: string[];
45
+ };
40
46
  export type VovkModuleRenderResult = {
41
47
  fileName: string;
42
- dirName: string;
48
+ dir: string;
43
49
  sourceName?: string;
44
50
  compiledName?: string;
45
51
  code: string;
46
52
  };
53
+ export interface DevOptions {
54
+ nextDev?: boolean;
55
+ exit?: boolean;
56
+ }
57
+ export interface GenerateOptions {
58
+ clientOutDir?: string;
59
+ templates?: string[];
60
+ prettify?: boolean;
61
+ fullSchema?: string | boolean;
62
+ }
63
+ export interface InitOptions {
64
+ yes?: boolean;
65
+ logLevel: LogLevelNames;
66
+ useNpm?: boolean;
67
+ useYarn?: boolean;
68
+ usePnpm?: boolean;
69
+ useBun?: boolean;
70
+ skipInstall?: boolean;
71
+ updateTsConfig?: boolean;
72
+ updateScripts?: 'implicit' | 'explicit';
73
+ validationLibrary?: string | null;
74
+ reactQuery?: boolean;
75
+ validateOnClient?: boolean;
76
+ dryRun?: boolean;
77
+ channel?: 'latest' | 'beta' | 'draft';
78
+ }
79
+ export interface NewOptions {
80
+ dryRun?: boolean;
81
+ templates?: string[];
82
+ dir?: string;
83
+ overwrite?: boolean;
84
+ noSegmentUpdate?: boolean;
85
+ }
@@ -1,2 +1,2 @@
1
1
  import { KnownAny } from '../types.mjs';
2
- export default function debounceWithArgs<T extends (...args: KnownAny[]) => KnownAny>(fn: T, wait: number): (...args: Parameters<T>) => void | Promise<void>;
2
+ export default function debounceWithArgs<Callback extends (...args: KnownAny[]) => KnownAny>(callback: Callback, wait: number): (...args: Parameters<Callback>) => Promise<Awaited<ReturnType<Callback>>>;
@@ -1,14 +1,29 @@
1
- import debounce from 'lodash/debounce.js';
2
- export default function debounceWithArgs(fn, wait) {
3
- const debouncedFunctions = new Map();
1
+ export default function debounceWithArgs(callback, wait) {
2
+ // Stores timeouts keyed by the stringified arguments
3
+ const timeouts = new Map();
4
4
  return (...args) => {
5
+ // Convert arguments to a JSON string (or any other stable key generation)
5
6
  const key = JSON.stringify(args);
6
- if (!debouncedFunctions.has(key)) {
7
- debouncedFunctions.set(key, debounce(fn, wait));
8
- }
9
- const debouncedFn = debouncedFunctions.get(key);
10
- if (debouncedFn) {
11
- debouncedFn(...args);
7
+ // Clear any existing timer for this specific key
8
+ if (timeouts.has(key)) {
9
+ clearTimeout(timeouts.get(key));
12
10
  }
11
+ // Return a promise that resolves/rejects after the debounce delay
12
+ return new Promise((resolve, reject) => {
13
+ const timeoutId = setTimeout(async () => {
14
+ try {
15
+ const result = await callback(...args);
16
+ resolve(result);
17
+ }
18
+ catch (error) {
19
+ reject(error);
20
+ }
21
+ finally {
22
+ // Remove the entry once the callback is invoked
23
+ timeouts.delete(key);
24
+ }
25
+ }, wait);
26
+ timeouts.set(key, timeoutId);
27
+ });
13
28
  };
14
29
  }
@@ -1,5 +1,5 @@
1
- import chalkHighlightThing from './chalkHighlightThing.mjs';
2
1
  import upperFirstLodash from 'lodash/upperFirst.js';
2
+ import chalkHighlightThing from './chalkHighlightThing.mjs';
3
3
  export default function formatLoggedSegmentName(segmentName, { withChalk = true, upperFirst = false } = {}) {
4
4
  let text = segmentName ? `segment "${segmentName}"` : 'the root segment';
5
5
  text = upperFirst ? upperFirstLodash(text) : text;
@@ -1,4 +1,5 @@
1
- import net from 'net';
1
+ import net from 'node:net';
2
+ // Created with AI
2
3
  /**
3
4
  * Checks if a port is available.
4
5
  * @param {number} port - The port to check.
@@ -34,7 +35,7 @@ function getAvailablePort(startPort, maxAttempts, attempt, onWarning) {
34
35
  getAvailablePort(startPort + 1, maxAttempts, attempt + 1, onWarning).then(resolve, reject);
35
36
  }
36
37
  else {
37
- reject('No available ports found');
38
+ reject(new Error('No available ports found'));
38
39
  }
39
40
  });
40
41
  });
@@ -1,4 +1,4 @@
1
- import fs from 'fs/promises';
1
+ import fs from 'node:fs/promises';
2
2
  export var FileSystemEntryType;
3
3
  (function (FileSystemEntryType) {
4
4
  FileSystemEntryType["FILE"] = "FILE";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vovk-cli",
3
- "version": "0.0.1-draft.7",
3
+ "version": "0.0.1-draft.70",
4
4
  "bin": {
5
5
  "vovk": "./dist/index.mjs"
6
6
  },
@@ -10,10 +10,13 @@
10
10
  "prefix": ".",
11
11
  "scripts": {
12
12
  "build": "rm -rf dist tsconfig.build.tsbuildinfo && tsc -P tsconfig.build.json",
13
+ "postbuild": "chmod +x ./dist/index.mjs",
13
14
  "build-test": "rm -rf test_dist && tsc -P tsconfig.test.json",
14
- "test": "npm run build && chmod +x ./dist/index.mjs && npm run build-test && node --test --test-only test_dist/test/**/*.mjs",
15
- "test_": "npm run build && chmod +x ./dist/index.mjs && NODE_OPTIONS=\"--loader ts-node/esm\" TS_NODE_PROJECT=./tsconfig.test.json node --test --test-only test/**.mts",
16
- "ncu": "npm-check-updates -u",
15
+ "pre-test": "npm run build && npm run build-test",
16
+ "test-only": "npm run pre-test && node --test --test-only test_dist/test/**/*.mjs",
17
+ "test": "npm run pre-test && node --test --test-concurrency=1 test_dist/test/**/*.mjs",
18
+ "tsc": "tsc --noEmit",
19
+ "ncu": "npm-check-updates -u -x commander",
17
20
  "npm-publish": "npm publish"
18
21
  },
19
22
  "repository": {
@@ -33,27 +36,27 @@
33
36
  },
34
37
  "homepage": "https://vovk.dev",
35
38
  "peerDependencies": {
36
- "vovk": "^3.0.0-draft.10"
39
+ "vovk": "^3.0.0-draft.71"
37
40
  },
38
41
  "dependencies": {
39
- "@inquirer/prompts": "^7.0.1",
40
- "@npmcli/package-json": "^6.0.1",
41
- "chalk": "^5.3.0",
42
- "chokidar": "^4.0.1",
43
- "commander": "^12.1.0",
44
- "concurrently": "^9.0.1",
45
- "dotenv": "^16.4.5",
42
+ "@inquirer/prompts": "^7.3.1",
43
+ "@npmcli/package-json": "^6.1.1",
44
+ "chalk": "^5.4.1",
45
+ "chokidar": "^4.0.3",
46
+ "commander": "^13.1.0",
47
+ "concurrently": "^9.1.2",
48
+ "dotenv": "^16.4.7",
46
49
  "ejs": "^3.1.10",
47
50
  "gray-matter": "^4.0.3",
48
- "inflection": "^3.0.0",
51
+ "inflection": "^3.0.2",
49
52
  "jsonc-parser": "^3.3.1",
50
53
  "lodash": "^4.17.21",
51
54
  "loglevel": "^1.9.2",
52
55
  "pluralize": "^8.0.0",
53
- "prettier": "^3.3.3",
56
+ "prettier": "^3.4.2",
54
57
  "tar-stream": "^3.1.7",
55
- "ts-morph": "^24.0.0",
56
- "undici": "^6.20.1"
58
+ "ts-morph": "^25.0.0",
59
+ "undici": "^7.3.0"
57
60
  },
58
61
  "devDependencies": {
59
62
  "@types/concat-stream": "^2.0.3",
@@ -62,8 +65,8 @@
62
65
  "@types/pluralize": "^0.0.33",
63
66
  "@types/tar-stream": "^3.1.3",
64
67
  "concat-stream": "^2.0.0",
65
- "create-next-app": "^15.0.1",
68
+ "create-next-app": "^15.1.6",
66
69
  "node-pty": "^1.0.0",
67
- "type-fest": "^4.26.1"
70
+ "type-fest": "^4.33.0"
68
71
  }
69
72
  }
@@ -3,11 +3,12 @@
3
3
  <% var controllerName = modulePascalName + 'Controller'; %>
4
4
  <% var compiledName = modulePascalName + 'RPC'; %>
5
5
  <% var serviceName = modulePascalName + 'Service'; %>
6
+ <% var prefix = pluralize(_.kebabCase(moduleName).toLowerCase()); %>
6
7
  ---
7
- dirName: <%= getModuleDirName(segmentName, moduleName) %> # Relative to the root dir
8
+ dir: <%= getModuleDirName(segmentName, moduleName) %>
8
9
  fileName: <%= controllerName + '.ts' %>
9
- sourceName: <%= controllerName %> # Used to define import declaration in a segment route file for the given class
10
- compiledName: <%= compiledName %> # Used to define a compiled object name
10
+ sourceName: <%= controllerName %>
11
+ compiledName: <%= compiledName %>
11
12
  ---
12
13
 
13
14
  import { prefix, get, put, post, del, type VovkRequest } from 'vovk';
@@ -15,15 +16,15 @@ import { prefix, get, put, post, del, type VovkRequest } from 'vovk';
15
16
  import <%= serviceName %> from './<%= serviceName %>';
16
17
  <% } %>
17
18
 
18
- @prefix('<%= _.kebabCase(moduleName).toLowerCase() %>')
19
+ @prefix('<%= prefix %>')
19
20
  export default class <%= controllerName %> {
20
21
  @get()
21
- static get<%= modulePascalNamePlural %> = async (req: VovkRequest<null, { q: string }>) => {
22
- const q = req.nextUrl.searchParams.get('q');
22
+ static get<%= modulePascalNamePlural %> = async (req: VovkRequest<null, { search: string }>) => {
23
+ const search = req.nextUrl.searchParams.get('search');
23
24
  <% if(withService) { %>
24
- return <%= serviceName %>.get<%= modulePascalNamePlural %>(q);
25
+ return <%= serviceName %>.get<%= modulePascalNamePlural %>(search);
25
26
  <% } else { %>
26
- return { q };
27
+ return { results: [], search };
27
28
  <% } %>
28
29
  }
29
30
 
@@ -33,19 +34,19 @@ export default class <%= controllerName %> {
33
34
  const body = await req.json();
34
35
  const q = req.nextUrl.searchParams.get('q');
35
36
  <% if(withService) { %>
36
- return MyThingService.update<%= modulePascalName %>(id, q, body);
37
+ return <%= serviceName %>.update<%= modulePascalName %>(id, q, body);
37
38
  <% } else { %>
38
39
  return { id, body, q };
39
40
  <% } %>
40
41
  };
41
42
 
42
43
  @post()
43
- static create<%= modulePascalNamePlural %> = () => {
44
+ static create<%= modulePascalName %> = () => {
44
45
  // ...
45
46
  };
46
47
 
47
48
  @del(':id')
48
- static delete<%= modulePascalNamePlural %> = () => {
49
+ static delete<%= modulePascalName %> = () => {
49
50
  // ...
50
51
  };
51
52
  }
@@ -3,22 +3,22 @@
3
3
  <% var serviceName = modulePascalName + 'Service'; %>
4
4
  <% var controllerName = modulePascalName + 'Controller'; %>
5
5
  ---
6
- dirName: <%= getModuleDirName(segmentName, moduleName) %> # Relative to the root dir
6
+ dir: <%= getModuleDirName(segmentName, moduleName) %>
7
7
  fileName: <%= serviceName + '.ts' %>
8
8
  ---
9
9
 
10
10
  import type { VovkControllerBody, VovkControllerQuery } from 'vovk';
11
- import <%= controllerName %> from './<%= controllerName %>';
11
+ import type <%= controllerName %> from './<%= controllerName %>';
12
12
 
13
13
  export default class <%= serviceName %> {
14
- static get<%= modulePascalNamePlural %> = (q: VovkControllerQuery<typeof <%= controllerName %>.get<%= modulePascalNamePlural %>>['q']) => {
15
- return [];
14
+ static get<%= modulePascalNamePlural %> = (search: VovkControllerQuery<typeof <%= controllerName %>.get<%= modulePascalNamePlural %>>['search']) => {
15
+ return { results: [], search };
16
16
  };
17
17
 
18
18
  static update<%= modulePascalName %> = (
19
19
  id: string,
20
- q: VovkControllerQuery<typeof <%= controllerName %>.get<%= modulePascalNamePlural %>>['q'],
21
- body: VovkControllerBody<typeof <%= controllerName %>.get<%= modulePascalNamePlural %>>
20
+ q: VovkControllerQuery<typeof <%= controllerName %>.update<%= modulePascalName %>>['q'],
21
+ body: VovkControllerBody<typeof <%= controllerName %>.update<%= modulePascalName %>>
22
22
  ) => {
23
23
  return { id, q, body };
24
24
  };
@@ -1,7 +0,0 @@
1
- import type { ProjectInfo } from './getProjectInfo/index.mjs';
2
- import type { Segment } from './locateSegments.mjs';
3
- import type { VovkSchema } from 'vovk';
4
- export default function generateClient(projectInfo: ProjectInfo, segments: Segment[], segmentsSchema: Record<string, VovkSchema>): Promise<{
5
- written: boolean;
6
- path: string;
7
- }>;