Skip to Content
vovk bundle

TypeScript Bundle

Quick CLI Ref
$ npx vovk bundle@draft --help Usage: vovk bundle|b [options] Generate TypeScript RPC and bundle it Options: --out, --out-dir <path> path to output directory for bundle --include, --include-segments <segments...> include segments --exclude, --exclude-segments <segments...> exclude segments --prebundle-out-dir, --prebundle-out <path> path to output directory for prebundle --keep-prebundle-dir do not delete prebundle directory after bundling --schema, --schema-path <path> path to schema folder (default: .vovk-schema) --config, --config-path <config> path to config file --origin <url> set the origin URL for the generated client --openapi, --openapi-spec <openapi_path_or_urls...> use OpenAPI mixins for client generation --openapi-module-name, --openapi-get-module-name <names...> module name strategies corresponding to the index of --openapi option --openapi-method-name, --openapi-get-method-name <names...> method name strategies corresponding to the index of --openapi option --openapi-root-url <urls...> root URLs corresponding to the index of --openapi option --openapi-mixin-name <names...> mixin names corresponding to the index of --openapi option --openapi-fallback <paths...> save OpenAPI spec corresponding to the index of --openapi option to a local file and use it as a fallback if URL is not available --log-level <level> set the log level -h, --help display help for command

The composed TypeScript RPC library can be bundled and published to NPM with pre-filled package.json and README.md files using the bundle command after setting up bundle.build function in the config file.

npx vovk bundle

The feature is library-agnostic, allowing you to plug in your preferred bundler of your choice, including one that’s invoked via child_process module. By the time being, tsdown  is the only bundler that has been tested with Vovk.ts. If you’re using a different bundler, please share your experience on GitHub Discussions .

Internally, the bundling is performed by executing the following steps:

  1. It generates a client into the tmp_prebundle directory (configured with bundle.prebundleOutDir: string) using the tsBase template.
  2. Calls bundle.build function to bundle the generated client to the dist directory (configured with bundle.outDir: string).
  3. Generates package.json and README.md files from the packageJson and readme templates.
  4. Deletes the tmp_prebundle directory (configured with bundle.keepPrebundleDir: boolean).

After bundling, the package can be published to NPM:

npm publish dist

Configuring the bundle

The bundling can be configured by adding the bundle object in the config file:

vovk.config.mjs
/** @type {import('vovk').VovkConfig} */ const config = { bundle: { build: async ({ entry, outDir, prebundleDir }) => { // plug in the bundler of your choice here }, prebundleOutDir: 'tmp_prebundle', // default keepPrebundleDir: false, // default outDir: 'dist', // default outputConfig: { origin: 'https://example.com', requires: { readme: '.', // default packageJson: '.', // default myTemplate: './foo', // custom template }, excludeSegments: [], includeSegments: [], package: { // modifies package.json content // by default uses values from the root package.json name: 'my-api-bundle', }, readme: {}, // modifies README.md content samples: {}, // modifies README.md samples content imports: { fetcher: './src/my-fetcher', }, reExports: {}, // modifies re-exports in the generated index.ts }, }, }; export default config;

build function (required)

The build function is an asynchronous function that takes an object with entry (index.ts file), outDir, and prebundleDir, resolved as absolute paths.

prebundleOutDir or --prebundle-out flag

The prebundleOutDir is the directory where the TypeScript client will be generated before bundling. It defaults to tmp_prebundle.

keepPrebundleDir or --keep-prebundle-dir flag

If set to true, the prebundleOutDir will not be deleted after bundling. This can be useful for debugging or other purposes. Defaults to false.

outputConfig

The outputConfig object accepts and overrides the same options as the outputConfig at the root of the config file.

Bundling with tsdown

Install tsdown as a development dependency:

npm install --save-dev tsdown

Add the following build function to the bundle object in the config file:

vovk.config.mjs
/** @type {import('vovk').VovkConfig} */ const config = { bundle: { build: async ({ entry, outDir }) => { const { build } = await import('tsdown'); await build({ entry, outDir, dts: true, format: ['cjs', 'esm'], hash: false, fixedExtension: true, clean: true, tsconfig: './tsconfig.bundle.json', // see Troubleshooting section }, // ... }, }; export default config;

The resulting bundled package will have the following structure:

    • package.json
    • README.md
    • index.cjs
    • index.d.cts
    • index.mjs
    • index.d.mts

Troubleshooting

In some TypeScript module configuration cases, you might encounter the following errors when vovk bundle is executed:

[UNLOADABLE_DEPENDENCY] Error: Could not load lib/internal/methods/object.ts - No such file or directory (os error 2).

or

[plugin rolldown-plugin-dts:generate] RollupError: tmp_prebundle/index.ts(17,14): error TS2742: The inferred type of 'XxxRPC' cannot be named without a reference to '../node_modules/vovk/mjs/client/types'. This is likely not portable. A type annotation is necessary.

It is recommended to create a separate tsconfig.bundle.json file for tsdown, optionally extending the root tsconfig.json:

vovk.config.mjs
/** @type {import('vovk').VovkConfig} */ const config = { bundle: { build: async ({ entry, outDir }) => { const { build } = await import('tsdown'); await build({ // ... tsconfig: './tsconfig.bundle.json', }); }, // ... }, }; export default config;

Setting moduleResolution to bundler fixes the first error, and setting paths to the vovk/* module fixes the second error:

tsconfig.bundle.json
{ "compilerOptions": { "moduleResolution": "bundler", "paths": { "vovk/*": ["./node_modules/vovk/*"], }, } }

Using the Bundled Package

After publishing the bundled package to NPM, it can be installed and used in other projects like any other NPM package:

npm install my-api-bundle

And imported in your TypeScript code:

import { UserRPC } from 'my-api-bundle'; await UserRPC.getUser({ params: { id: '123' } });

All features described in the TypeScript article are available to the bundled method, including client-side validation, custom options, queryKey method and more.

Schema is available under schema import as well as a property for every method individually:

import { schema, UserRPC } from 'my-api-bundle'; console.log(schema.segments[''].controllers.UserRPC.handlers.getUser.validation.params); console.log(UserRPC.getUser.schema.validation.params);

Note that the openapi object that’s usually available in the vovk-client/openapi isn’t bundled as it will bloat the package size. The schema module usually available in vovk-client/schema also isn’t present in order to keep the bundle flow simple by using only a single entry point.


The bundle methods can be used as LLM tools that will invoke corresponding HTTP endpoints when called.

import { UserRPC } from 'my-api-bundle'; import { createLLMTools } from 'vovk'; const { tools } = createLLMTools({ modules: { UserRPC }, });

Roadmap

  • ✨ Segmented bundle - create separate bundles for each segment.
Last updated on