vovk 0.2.3-beta.7 → 0.2.3-beta.71

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/Segment.js CHANGED
@@ -39,6 +39,9 @@ class _Segment {
39
39
  #callMethod = async (httpMethod, req, params) => {
40
40
  const controllers = this._routes[httpMethod];
41
41
  const methodParams = {};
42
+ if (params[Object.keys(params)[0]]?.[0] === '__ping') {
43
+ return this.#respond(200, { message: 'pong' });
44
+ }
42
45
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
43
46
  const handlers = Object.fromEntries([...controllers.entries()]
44
47
  .map(([controller, staticMethods]) => {
package/cli/.eslintrc.js CHANGED
@@ -7,6 +7,6 @@ module.exports = {
7
7
  '@typescript-eslint/no-unsafe-call': 'off',
8
8
  '@typescript-eslint/no-unsafe-member-access': 'off',
9
9
  '@typescript-eslint/no-unsafe-return': 'off',
10
- 'no-console': ['error', { allow: ['info', 'error'] }]
10
+ 'no-console': ['error', { allow: ['info', 'error', 'warn'] }]
11
11
  }
12
12
  }
@@ -1,54 +1,102 @@
1
+ // @ts-check
1
2
  const fs = require('fs/promises');
2
3
  const path = require('path');
3
- const getVovkrc = require('./getVovkrc');
4
+ const getReturnPath = require('./lib/getReturnPath');
5
+
6
+ function canRequire(moduleName) {
7
+ try {
8
+ require.resolve(moduleName);
9
+ return true; // The module exists and can be required
10
+ } catch (e) {
11
+ return false; // The module does not exist
12
+ }
13
+ }
14
+
4
15
  /**
5
- * Generates client code with string concatenation so it should be super fast
6
- * @type {(rcPath: string) => Promise<void>}
16
+ * Generates client code with string concatenation so it should be much faster than using AST
17
+ * TODO: Check fetcher and streamFetcher for existence
18
+ * @type {(rcPath: import('../src').VovkEnv) => Promise<{ written: boolean; path: string }>}
7
19
  */
8
- async function generateClient(rcPath) {
9
- const vovkrc = getVovkrc(rcPath);
20
+ async function generateClient({ ...env }) {
21
+ const outDir = env.VOVK_CLIENT_OUT;
22
+ const returnDir = getReturnPath(outDir, process.cwd());
23
+ const jsonPath = path.join(returnDir, '.vovk.json');
24
+ const localJsonPath = path.join(process.cwd(), '.vovk.json');
25
+ const fetcherPath = env.VOVK_FETCHER.startsWith('.') ? path.join(returnDir, env.VOVK_FETCHER) : env.VOVK_FETCHER;
26
+ const streamFetcherPath = env.VOVK_STREAM_FETCHER.startsWith('.')
27
+ ? path.join(returnDir, env.VOVK_STREAM_FETCHER)
28
+ : env.VOVK_STREAM_FETCHER;
29
+ const validatePath = env.VOVK_VALIDATE_ON_CLIENT.startsWith('.')
30
+ ? path.join(returnDir, env.VOVK_VALIDATE_ON_CLIENT)
31
+ : env.VOVK_VALIDATE_ON_CLIENT;
32
+ const localValidatePath = env.VOVK_VALIDATE_ON_CLIENT.startsWith('.') ? path.join('..', validatePath) : validatePath;
33
+
34
+ if (!env.VOVK_VALIDATE_ON_CLIENT) {
35
+ env.VOVK_VALIDATE_ON_CLIENT = canRequire('vovk-zod/zodValidateOnClient') ? 'vovk-zod/zodValidateOnClient' : '';
36
+ } else if (env.VOVK_VALIDATE_ON_CLIENT && !canRequire(localValidatePath)) {
37
+ throw new Error(
38
+ `Unble to generate Vovk Client: cannot find "validateOnClient" module '${env.VOVK_VALIDATE_ON_CLIENT}'. Check your .vovkrc.js file`
39
+ );
40
+ }
10
41
 
11
- const fetcherPath = vovkrc.fetcher.startsWith('.') ? path.join(process.cwd(), vovkrc.fetcher) : vovkrc.fetcher;
12
- const streamFetcherPath = vovkrc.streamFetcher.startsWith('.')
13
- ? path.join(process.cwd(), vovkrc.streamFetcher)
14
- : vovkrc.streamFetcher;
42
+ if (!canRequire(localJsonPath)) {
43
+ throw new Error(`Unble to generate Vovk Client: cannot find ".vovk.json" file '${jsonPath}'.`);
44
+ }
15
45
 
16
- const controllersPath = path.join('../..', vovkrc.route).replace(/\.ts$/, '');
17
- let ts = `import type { Controllers } from "${controllersPath}";
46
+ const controllersPath = path.join(returnDir, env.VOVK_ROUTE).replace(/\.ts$/, '');
47
+ let ts = `// auto-generated
48
+ /* eslint-disable */
49
+ import type { Controllers, Workers } from "${controllersPath}";
18
50
  import type { clientizeController } from 'vovk/client';
19
51
  import type { promisifyWorker } from 'vovk/worker';
20
- import type { VovkFetcherOptions } from 'vovk/client';
52
+ import type { VovkClientFetcher } from 'vovk/client';
21
53
  import type fetcher from '${fetcherPath}';
22
54
 
23
- type Options = typeof fetcher extends VovkFetcherOptions<infer U> ? U : never;
55
+ type Options = typeof fetcher extends VovkClientFetcher<infer U> ? U : never;
24
56
  `;
25
- let js = `const { clientizeController } = require('vovk/client');
57
+ let js = `// auto-generated
58
+ /* eslint-disable */
59
+ const { clientizeController } = require('vovk/client');
26
60
  const { promisifyWorker } = require('vovk/worker');
27
- const metadata = require('./vovk-metadata.json');
28
- const fetcher = require('${fetcherPath}');
29
- const streamFetcher = require('${streamFetcherPath}');
30
- const prefix = '${vovkrc.prefix ?? '/api'}';
31
- const validateOnClient = ${vovkrc.validateOnClient ? `require('${vovkrc.validateOnClient}')` : 'null'};
61
+ const metadata = require('${jsonPath}');
62
+ const { default: fetcher } = require('${fetcherPath}');
63
+ const { default: streamFetcher } = require('${streamFetcherPath}');
64
+ const prefix = '${env.VOVK_PREFIX ?? '/api'}';
65
+ const { default: validateOnClient = null } = ${
66
+ env.VOVK_VALIDATE_ON_CLIENT ? `require('${env.VOVK_VALIDATE_ON_CLIENT}')` : '{}'
67
+ };
32
68
 
33
69
  `;
34
- const metadataJson = await fs
35
- .readFile(path.join(__dirname, '../../.vovk/vovk-metadata.json'), 'utf-8')
36
- .catch(() => null);
70
+ const metadataJson = await fs.readFile(path.join(__dirname, localJsonPath), 'utf-8').catch(() => null);
37
71
  const metadata = JSON.parse(metadataJson || '{}');
38
72
 
39
73
  for (const key of Object.keys(metadata)) {
40
74
  if (key !== 'workers') {
41
- ts += `export type ${key} = ReturnType<typeof clientizeController<Controllers["${key}"], Options>>;\n`;
75
+ ts += `export const ${key}: ReturnType<typeof clientizeController<Controllers["${key}"], Options>>;\n`;
42
76
  js += `exports.${key} = clientizeController(metadata.${key}, { fetcher, streamFetcher, validateOnClient, defaultOptions: { prefix } });\n`;
43
77
  }
44
78
  }
45
79
 
46
- /* for(const key of Object.keys(metadata.workers ?? {})) {
47
- code += `export const ${key} = promisifyWorker<${key}>(metadata.workers.${key});\n`;
48
- } */
49
- await fs.mkdir('../../.vovk');
50
- await fs.writeFile(path.join(__dirname, '../../.vovk/index.d.ts'), ts);
51
- await fs.writeFile(path.join(__dirname, '../../.vovk/index.js'), js);
80
+ for (const key of Object.keys(metadata.workers ?? {})) {
81
+ ts += `export const ${key}: ReturnType<typeof promisifyWorker<Workers["${key}"]>>;\n`;
82
+ js += `exports.${key} = promisifyWorker(null, metadata.workers.${key});\n`;
83
+ }
84
+
85
+ /* js += `
86
+ if(typeof window !== 'undefined') fetch(prefix + '/__ping', { method: 'POST' });
87
+ `; */
88
+
89
+ const localJsPath = path.join(outDir, 'index.js');
90
+ const localTsPath = path.join(outDir, 'index.d.ts');
91
+ const existingJs = await fs.readFile(localJsPath, 'utf-8').catch(() => '');
92
+ const existingTs = await fs.readFile(localTsPath, 'utf-8').catch(() => '');
93
+ if (existingJs === js && existingTs === ts) return { written: false, path: outDir };
94
+
95
+ await fs.mkdir(outDir, { recursive: true });
96
+ await fs.writeFile(localJsPath, js);
97
+ await fs.writeFile(localTsPath, ts);
98
+
99
+ return { written: true, path: outDir };
52
100
  }
53
101
 
54
102
  module.exports = generateClient;
package/cli/getVars.js ADDED
@@ -0,0 +1,45 @@
1
+ // @ts-check
2
+
3
+ const path = require('path');
4
+
5
+ let vars;
6
+ /** @type {(rcPath: string, options?: { warn?: boolean; VOVK_CLIENT_OUT?: string; }) => import('../src').VovkEnv} */
7
+ function getVars(rcPath, options = {}) {
8
+ if (vars) return vars;
9
+ /** @type {Required<import('../src').VovkRc>} */
10
+ const vovkRc = {
11
+ out: './node_modules/.vovk',
12
+ route: './src/app/api/[[...]]/route.ts',
13
+ fetcher: 'vovk/client/defaultFetcher',
14
+ streamFetcher: 'vovk/client/defaultStreamFetcher',
15
+ prefix: '/api',
16
+ validateOnClient: '',
17
+ };
18
+
19
+ try {
20
+ Object.assign(vovkRc, require(rcPath));
21
+ } catch {
22
+ if (options.warn) console.info(` 🐺 No .vovkrc.js file found in ${rcPath}`);
23
+ }
24
+
25
+ vars = {
26
+ VOVK_CLIENT_OUT:
27
+ process.env.VOVK_OUT ||
28
+ (options.VOVK_CLIENT_OUT?.startsWith('/')
29
+ ? options.VOVK_CLIENT_OUT
30
+ : options.VOVK_CLIENT_OUT
31
+ ? path.join(process.cwd(), options.VOVK_CLIENT_OUT)
32
+ : null) ||
33
+ vovkRc.out,
34
+ VOVK_PORT: process.env.VOVK_PORT || '3420',
35
+ VOVK_ROUTE: process.env.VOVK_ROUTE || vovkRc.route,
36
+ VOVK_FETCHER: process.env.VOVK_FETCHER || vovkRc.fetcher,
37
+ VOVK_STREAM_FETCHER: process.env.VOVK_STREAM_FETCHER || vovkRc.streamFetcher,
38
+ VOVK_PREFIX: process.env.VOVK_PREFIX || vovkRc.prefix,
39
+ VOVK_VALIDATE_ON_CLIENT: process.env.VOVK_VALIDATE_ON_CLIENT || vovkRc.validateOnClient,
40
+ };
41
+
42
+ return vars;
43
+ }
44
+
45
+ module.exports = getVars;
package/cli/index.js CHANGED
@@ -1,12 +1,15 @@
1
1
  #!/usr/bin/env node
2
+ // @ts-check
2
3
  const yargs = require('yargs/yargs');
3
4
  const { hideBin } = require('yargs/helpers');
4
- const { concurrently } = require('concurrently');
5
5
  const generateClient = require('./generateClient');
6
6
  const path = require('path');
7
+ const concurrent = require('./lib/concurrent');
8
+ const getAvailablePort = require('./lib/getAvailablePort');
9
+ const getVars = require('./getVars');
7
10
 
8
11
  const builder = {
9
- rc: {
12
+ vovkrc: {
10
13
  type: 'string',
11
14
  default: path.join(process.cwd(), '.vovkrc.js'),
12
15
  describe: 'Path to .vovkrc.js',
@@ -18,60 +21,68 @@ const builder = {
18
21
  describe: 'Path to Next.js project',
19
22
  },
20
23
 
21
- output: {
24
+ clientOut: {
22
25
  type: 'string',
23
- default: path.join(__dirname, '../.vovk/index.d.ts'),
24
- describe: 'Path to the wildcard route file',
26
+ default: path.join(process.cwd(), './node_modules/.vovk'),
27
+ describe: 'Path to output directory',
25
28
  },
26
29
  };
27
30
 
28
- const options = {
29
- outputStream: process.stdout,
30
- raw: true,
31
- killOthers: ['failure', 'success'],
32
- };
33
- /** @type {{ rc: string, project: string, output: string }} */
34
- const argv = yargs(hideBin(process.argv))
35
- .command('dev', 'Run development server', builder)
36
- .command('build', 'Build the app', builder)
31
+ /** @type {{ vovkrc: string, project: string, clientOut: string }} */
32
+ // @ts-expect-error yargs
33
+ const argv = yargs(hideBin(process.argv)) // @ts-expect-error yargs
34
+ .command('dev', 'Run development server', builder) // @ts-expect-error yargs
35
+ .command('build', 'Build the app', builder) // @ts-expect-error yargs
37
36
  .command('generate', 'Generate client', builder).argv;
38
37
 
39
38
  const nextArgs = process.argv.join(' ').split(' -- ')[1] ?? '';
40
39
 
41
- if (argv._.includes('dev')) {
42
- const { result } = concurrently(
43
- [
44
- { command: `node ${__dirname}/server.js`, name: 'Vovk' },
45
- { command: `node ${__dirname}/watchMetadata.js --rc ${argv.rc} --output ${argv.output}`, name: 'Watch metadata' },
46
- { command: `cd ${argv.project} && npx next dev ${nextArgs}`, name: 'Next' },
47
- ],
48
- options
49
- );
40
+ const env = getVars(argv.vovkrc, { VOVK_CLIENT_OUT: argv.clientOut });
50
41
 
51
- void result.then(() => {
52
- console.info(' 🐺 All processes have completed.');
53
- });
42
+ let VOVK_PORT = parseInt(env.VOVK_PORT);
43
+
44
+ // @ts-expect-error yargs
45
+ if (argv._.includes('dev')) {
46
+ void (async () => {
47
+ env.VOVK_PORT = await getAvailablePort(VOVK_PORT, 20).catch(() => {
48
+ throw new Error(' 🐺 Failed to find available port');
49
+ });
50
+ await concurrent(
51
+ [
52
+ {
53
+ command: `node ${__dirname}/server.js`,
54
+ name: 'Vovk',
55
+ },
56
+ { command: `cd ${argv.project} && npx next dev ${nextArgs}`, name: 'Next' },
57
+ ],
58
+ env
59
+ ).catch((e) => console.error(e));
60
+ console.info(' 🐺 All processes have completed');
61
+ })();
54
62
  }
55
63
 
64
+ // @ts-expect-error yargs
56
65
  if (argv._.includes('build')) {
57
- const { result } = concurrently(
58
- [
59
- { command: `node ${__dirname}/server.js --once`, name: 'Vovk' },
60
- { command: `cd ${argv.project} && npx next build ${nextArgs}`, name: 'Next' },
61
- ],
62
- options
63
- );
64
-
65
- void result
66
- .catch((e) => console.error(e))
67
- .then(async () => {
68
- await generateClient(argv.rc, argv.output);
69
- console.info(' 🐺 Both processes have completed and the client is generated.');
66
+ void (async () => {
67
+ env.VOVK_PORT = await getAvailablePort(VOVK_PORT, 20).catch(() => {
68
+ throw new Error(' 🐺 Failed to find available port');
70
69
  });
70
+ await concurrent(
71
+ [
72
+ {
73
+ command: `node ${__dirname}/server.js --once`,
74
+ name: 'Vovk',
75
+ },
76
+ { command: `cd ${argv.project} && npx next build ${nextArgs}`, name: 'Next' },
77
+ ],
78
+ env
79
+ ).catch((e) => console.error(e));
80
+ })();
71
81
  }
72
82
 
83
+ // @ts-expect-error yargs
73
84
  if (argv._.includes('generate')) {
74
- void generateClient(argv.rc, argv.output).then(() => {
75
- console.info(' 🐺 Client generated.');
85
+ void generateClient(env).then(({ path }) => {
86
+ console.info(` 🐺 Client generated in ${path}`);
76
87
  });
77
88
  }
@@ -0,0 +1,43 @@
1
+ // @ts-check
2
+ const { spawn } = require('child_process');
3
+
4
+ /** @type {(commands: { command: string; name: string; }[], env: import('../../src').VovkEnv) => Promise<void>} */
5
+ function concurrent(commands, env) {
6
+ return new Promise((resolve, reject) => {
7
+ /** @type {{ name: string; process: import('child_process').ChildProcess; }[]} */
8
+ let processes = [];
9
+
10
+ commands.forEach((cmd) => {
11
+ const processObj = {
12
+ name: cmd.name,
13
+ process: runCommand(cmd.command, cmd.name, (code) => handleProcessExit(code, cmd.name)),
14
+ };
15
+ processes.push(processObj);
16
+ });
17
+
18
+ function runCommand(command, name, onExit) {
19
+ const proc = spawn(command, { shell: true, env: { ...env, ...process.env }, stdio: 'inherit' });
20
+
21
+ proc.on('exit', onExit);
22
+
23
+ return proc;
24
+ }
25
+
26
+ function handleProcessExit(code, name) {
27
+ processes = processes.filter((p) => p.name !== name);
28
+
29
+ if (code !== 0) {
30
+ processes.forEach((p) => p.name !== name && p.process.kill('SIGINT'));
31
+ processes = [];
32
+ process.stdout.write('\n');
33
+ return reject(new Error(`Process ${name} exited with code ${code}`));
34
+ }
35
+
36
+ if (!processes.length) {
37
+ resolve();
38
+ }
39
+ }
40
+ });
41
+ }
42
+
43
+ module.exports = concurrent;
@@ -0,0 +1,31 @@
1
+ const net = require('net');
2
+
3
+ function checkPort(port, callback) {
4
+ const server = net.createServer();
5
+
6
+ server.listen(port, () => {
7
+ server.close(() => {
8
+ callback(true); // Port is available
9
+ });
10
+ });
11
+
12
+ server.on('error', () => {
13
+ callback(false);
14
+ });
15
+ }
16
+
17
+ function getAvailablePort(startPort, maxAttempts, attempt = 1) {
18
+ return new Promise((resolve, reject) => {
19
+ checkPort(startPort, (isAvailable) => {
20
+ if (isAvailable) {
21
+ resolve(startPort); // Found an available port
22
+ } else if (attempt < maxAttempts) {
23
+ getAvailablePort(startPort + 1, maxAttempts, attempt + 1).then(resolve, reject);
24
+ } else {
25
+ reject(null);
26
+ }
27
+ });
28
+ });
29
+ }
30
+
31
+ module.exports = getAvailablePort;
@@ -0,0 +1,25 @@
1
+ // @ts-check
2
+ function getReturnPath(fromPath, toPath) {
3
+ // Split the paths into components
4
+ const fromParts = fromPath.split('/');
5
+ const toParts = toPath.split('/');
6
+
7
+ // Find the common base path length
8
+ const length = Math.min(fromParts.length, toParts.length);
9
+ let commonBaseLength = 0;
10
+ for (let i = 0; i < length; i++) {
11
+ if (fromParts[i] !== toParts[i]) break;
12
+ commonBaseLength++;
13
+ }
14
+
15
+ // Calculate steps up to the common base
16
+ const stepsUp = '../'.repeat(fromParts.length - commonBaseLength);
17
+
18
+ // Calculate steps down to the target path
19
+ const stepsDown = toParts.slice(commonBaseLength).join('/');
20
+
21
+ // Combine steps up and steps down
22
+ return stepsUp + stepsDown;
23
+ }
24
+
25
+ module.exports = getReturnPath;
@@ -0,0 +1,27 @@
1
+ // @ts-check
2
+ const isEqual = (obj1, obj2) => {
3
+ if (obj1 === obj2) {
4
+ return true;
5
+ }
6
+
7
+ if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || obj1 == null || obj2 == null) {
8
+ return false;
9
+ }
10
+
11
+ const keys1 = Object.keys(obj1);
12
+ const keys2 = Object.keys(obj2);
13
+
14
+ if (keys1.length !== keys2.length) {
15
+ return false;
16
+ }
17
+
18
+ for (const key of keys1) {
19
+ if (!keys2.includes(key) || !isEqual(obj1[key], obj2[key])) {
20
+ return false;
21
+ }
22
+ }
23
+
24
+ return true;
25
+ };
26
+
27
+ module.exports = isEqual;
@@ -0,0 +1,21 @@
1
+ // @ts-check
2
+ const fs = require('fs/promises');
3
+ const path = require('path');
4
+
5
+ const fileExists = async (path) => !!(await fs.stat(path).catch(() => false));
6
+
7
+ async function postinstall() {
8
+ const vovk = path.join(__dirname, '../../.vovk');
9
+ const js = path.join(vovk, 'index.js');
10
+ const ts = path.join(vovk, 'index.d.ts');
11
+
12
+ if ((await fileExists(js)) || (await fileExists(ts))) {
13
+ return;
14
+ }
15
+
16
+ await fs.mkdir(vovk, { recursive: true });
17
+ await fs.writeFile(js, '/* postinstall */');
18
+ await fs.writeFile(ts, '/* postinstall */');
19
+ }
20
+
21
+ void postinstall();
package/cli/server.js CHANGED
@@ -1,44 +1,60 @@
1
+ // @ts-check
1
2
  const http = require('http');
2
3
  const fs = require('fs/promises');
3
4
  const path = require('path');
4
5
  const yargs = require('yargs/yargs');
5
6
  const { hideBin } = require('yargs/helpers');
7
+ const generateClient = require('./generateClient');
8
+ const getVars = require('./getVars');
9
+ const isEqual = require('./lib/isEqual');
6
10
 
11
+ /** @type {{ once?: boolean; vovkrc: string }} */
12
+ // @ts-expect-error yargs
7
13
  const argv = yargs(hideBin(process.argv)).argv;
8
14
 
9
15
  const once = argv.once ?? false;
10
16
 
11
- const isEqual = (obj1, obj2) => {
12
- if (obj1 === obj2) {
13
- return true;
14
- }
17
+ const metadataPath = path.join(__dirname, '../../../.vovk.json');
15
18
 
16
- if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || obj1 == null || obj2 == null) {
17
- return false;
18
- }
19
+ /** @type {(metadata: object) => Promise<{ written: boolean; path: string }>} */
20
+ const writeMetadata = async (metadata) => {
21
+ await fs.mkdir(path.dirname(metadataPath), { recursive: true });
22
+ const existingMetadata = await fs.readFile(metadataPath, 'utf-8').catch(() => 'null');
23
+ if (isEqual(JSON.parse(existingMetadata), metadata)) return { written: false, path: metadataPath };
24
+ await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
25
+ return { written: true, path: metadataPath };
26
+ };
19
27
 
20
- const keys1 = Object.keys(obj1);
21
- const keys2 = Object.keys(obj2);
28
+ const writeEmptyMetadata = async () => {
29
+ await fs.mkdir(path.dirname(metadataPath), { recursive: true });
30
+ const existingMetadata = await fs.readFile(metadataPath, 'utf-8').catch(() => null);
31
+ if (!existingMetadata) await fs.writeFile(metadataPath, '{}');
32
+ };
22
33
 
23
- if (keys1.length !== keys2.length) {
24
- return false;
25
- }
34
+ void writeEmptyMetadata();
26
35
 
27
- for (const key of keys1) {
28
- if (!keys2.includes(key) || !isEqual(obj1[key], obj2[key])) {
29
- return false;
30
- }
31
- }
36
+ let pingInterval;
32
37
 
33
- return true;
34
- };
38
+ const vars = getVars(argv.vovkrc, { warn: false });
35
39
 
36
- const writeMetadata = async (metadataPath, metadata) => {
37
- await fs.mkdir(path.dirname(metadataPath), { recursive: true });
38
- const existingMetadata = await fs.readFile(metadataPath, 'utf-8').catch(() => '{}');
39
- if (isEqual(JSON.parse(existingMetadata), metadata)) return;
40
- await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
41
- console.info(' 🐺 JSON data received and metadata file created');
40
+ const startPinging = (port) => {
41
+ clearInterval(pingInterval);
42
+ pingInterval = setInterval(() => {
43
+ let prefix = vars.VOVK_PREFIX;
44
+ prefix = prefix.startsWith('http://')
45
+ ? prefix
46
+ : `http://localhost:${port}/${prefix.startsWith('/') ? prefix.slice(1) : prefix}`;
47
+ const endpoint = `${prefix.endsWith('/') ? prefix.slice(0, -1) : prefix}/__ping`;
48
+ // Create the HTTP GET request
49
+ const req = http.get(endpoint, () => {
50
+ // noop
51
+ });
52
+
53
+ // Error handling for the request
54
+ req.on('error', (err) => {
55
+ console.error(`🐺 Error during HTTP request made to ${endpoint}:`, err.message);
56
+ });
57
+ }, 1000 * 3);
42
58
  };
43
59
 
44
60
  const server = http.createServer((req, res) => {
@@ -46,23 +62,37 @@ const server = http.createServer((req, res) => {
46
62
  let body = '';
47
63
 
48
64
  req.on('data', (chunk) => {
49
- body += chunk.toString(); // Convert Buffer to string
65
+ body += chunk.toString();
50
66
  });
51
67
 
52
68
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
53
69
  req.on('end', async () => {
54
70
  try {
55
- const metadata = JSON.parse(body); // Parse the JSON data
56
- const filePath = path.join(__dirname, '../../.vovk/vovk-metadata.json');
57
- await writeMetadata(filePath, metadata);
71
+ const { metadata, PORT } = JSON.parse(body); // Parse the JSON data
72
+ const metadataWritten = metadata ? await writeMetadata(metadata) : { written: false, path: metadataPath };
73
+ const codeWritten = await generateClient(vars);
58
74
  res.writeHead(200, { 'Content-Type': 'text/plain' });
59
75
  res.end('JSON data received and file created');
76
+ if (metadataWritten.written) {
77
+ console.info(` 🐺 JSON metadata updated in ${metadataWritten.path}`);
78
+ }
79
+
80
+ if (codeWritten.written) {
81
+ console.info(` 🐺 Client generated in ${codeWritten.path}`);
82
+ }
83
+
84
+ if (PORT && !once) {
85
+ startPinging(PORT);
86
+ }
87
+
88
+ if (once && metadata) server.close();
60
89
  } catch (err) {
61
90
  res.writeHead(400, { 'Content-Type': 'text/plain' });
62
- res.end('Invalid JSON');
91
+ res.end(err.message ?? 'Error');
63
92
  console.error(' ❌ ' + err.message);
93
+
94
+ if (once) server.close();
64
95
  }
65
- if (once) server.close();
66
96
  });
67
97
  } else {
68
98
  res.writeHead(404, { 'Content-Type': 'text/plain' });
@@ -70,7 +100,11 @@ const server = http.createServer((req, res) => {
70
100
  }
71
101
  });
72
102
 
73
- const PORT = process.env.VOVK_PORT || 3420;
74
- server.listen(PORT, () => {
75
- console.info(` 🐺 Vovk Server running on port ${PORT}`);
103
+ const VOVK_PORT = process.env.VOVK_PORT;
104
+ if (!VOVK_PORT) {
105
+ console.error(' 🐺 Unable to run Vovk Metadata Server: no port specified');
106
+ process.exit(1);
107
+ }
108
+ server.listen(VOVK_PORT, () => {
109
+ console.info(` 🐺 Vovk Metadata Server is running on port ${VOVK_PORT}`);
76
110
  });
@@ -1,4 +1,22 @@
1
1
  import { type _VovkControllerMetadataJson as VovkControllerMetadataJson } from '../types';
2
- import { type _VovkClientOptions as VovkClientOptions, type _VovkClient as VovkClient } from './types';
2
+ import { type _VovkClientOptions as VovkClientOptions, type _VovkClient as VovkClient, type _StreamAsyncIterator as StreamAsyncIterator } from './types';
3
3
  import { type _DefaultFetcherOptions as DefaultFetcherOptions } from './defaultFetcher';
4
- export declare const _clientizeController: <T, OPTS extends Record<string, any> = DefaultFetcherOptions>(givenController: VovkControllerMetadataJson, options?: VovkClientOptions<OPTS> | undefined) => VovkClient<T, OPTS>;
4
+ export declare const _clientizeController: <T, OPTS extends Record<string, any> = DefaultFetcherOptions>(givenController: VovkControllerMetadataJson, options?: VovkClientOptions<OPTS> | undefined) => { [K_1 in keyof { [K in keyof T]: T[K] extends (...args: any) => any ? <R>(options: (import("./types")._StaticMethodInput<T[K]> extends {
5
+ body?: null | undefined;
6
+ query?: undefined;
7
+ params?: undefined;
8
+ } ? unknown : Parameters<T[K]>[0] extends void ? import("./types")._StaticMethodInput<T[K]>["params"] extends object ? {
9
+ params: import("./types")._StaticMethodInput<T[K]>["params"];
10
+ } : unknown : import("./types")._StaticMethodInput<T[K]>) & (void | Partial<OPTS>)) => ReturnType<T[K]> extends import("../StreamResponse")._StreamResponse<infer U> | Iterator<infer U, any, undefined> | AsyncIterator<infer U, any, undefined> | Promise<import("../StreamResponse")._StreamResponse<infer U>> ? Promise<StreamAsyncIterator<U>> : R extends object ? Promise<R> : ReturnType<T[K]> extends infer T_1 ? T_1 extends ReturnType<T[K]> ? T_1 extends PromiseLike<unknown> ? T_1 : Promise<T_1> : never : never : never; } as { [K in keyof T]: T[K] extends (...args: any) => any ? <R>(options: (import("./types")._StaticMethodInput<T[K]> extends {
11
+ body?: null | undefined;
12
+ query?: undefined;
13
+ params?: undefined;
14
+ } ? unknown : Parameters<T[K]>[0] extends void ? import("./types")._StaticMethodInput<T[K]>["params"] extends object ? {
15
+ params: import("./types")._StaticMethodInput<T[K]>["params"];
16
+ } : unknown : import("./types")._StaticMethodInput<T[K]>) & (void | Partial<OPTS>)) => ReturnType<T[K]> extends import("../StreamResponse")._StreamResponse<infer U> | Iterator<infer U, any, undefined> | AsyncIterator<infer U, any, undefined> | Promise<import("../StreamResponse")._StreamResponse<infer U>> ? Promise<StreamAsyncIterator<U>> : R extends object ? Promise<R> : ReturnType<T[K]> extends infer T_2 ? T_2 extends ReturnType<T[K]> ? T_2 extends PromiseLike<unknown> ? T_2 : Promise<T_2> : never : never : never; }[K_1] extends never ? never : K_1]: { [K in keyof T]: T[K] extends (...args: any) => any ? <R>(options: (import("./types")._StaticMethodInput<T[K]> extends {
17
+ body?: null | undefined;
18
+ query?: undefined;
19
+ params?: undefined;
20
+ } ? unknown : Parameters<T[K]>[0] extends void ? import("./types")._StaticMethodInput<T[K]>["params"] extends object ? {
21
+ params: import("./types")._StaticMethodInput<T[K]>["params"];
22
+ } : unknown : import("./types")._StaticMethodInput<T[K]>) & (void | Partial<OPTS>)) => ReturnType<T[K]> extends import("../StreamResponse")._StreamResponse<infer U> | Iterator<infer U, any, undefined> | AsyncIterator<infer U, any, undefined> | Promise<import("../StreamResponse")._StreamResponse<infer U>> ? Promise<StreamAsyncIterator<U>> : R extends object ? Promise<R> : ReturnType<T[K]> extends infer T_3 ? T_3 extends ReturnType<T[K]> ? T_3 extends PromiseLike<unknown> ? T_3 : Promise<T_3> : never : never : never; }[K_1]; };