Skip to content

feat: deno runtime for typescript#565

Open
dmmordvi wants to merge 38 commits intomainfrom
feat/typescript-deno
Open

feat: deno runtime for typescript#565
dmmordvi wants to merge 38 commits intomainfrom
feat/typescript-deno

Conversation

@dmmordvi
Copy link
Contributor

@dmmordvi dmmordvi commented Feb 13, 2026

Summary by Bito

This pull request introduces Deno as a new runtime for TypeScript support in Helm charts, replacing the existing JavaScript runtime implementation based on goja and esbuild with a more secure and modern Deno-based approach that enables bundling and executing TypeScript code with controlled permissions.

Detailed Changes
  • Introduces pkg/deno package with DenoRuntime for secure TypeScript bundling and execution in Helm charts, implementing restricted permissions for file access.
  • Adds comprehensive internal/ts package with utilities for TypeScript file handling, initialization, rendering, and bundling operations.
  • Removes dependencies on goja, goja_nodejs, esbuild, sourcemap, and related libraries, replacing them with Deno for improved security and performance.
  • Updates chart commands to include new TypeScript-specific subcommands and flags for Deno binary path configuration and bundle rebuilding.
  • Modifies chart packing and linting processes to utilize the new Deno-based bundler instead of the previous runtime.
  • Adds extensive test coverage across multiple new files for TypeScript functionality, including rendering, bundling, and package management.

Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
…ild`, `nelm chart pack`

Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>

# Conflicts:
#	cmd/nelm/groups.go
#	internal/chart/chart_render.go
#	internal/ts/bundle.go
#	internal/ts/files.go
#	internal/ts/init.go
#	pkg/common/common.go
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
…g + atomic mv

Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>

# Conflicts:
#	go.mod
#	go.sum
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
@dmmordvi dmmordvi marked this pull request as ready for review February 26, 2026 20:45
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
@dmmordvi
Copy link
Contributor Author

/review

Copy link

@bito-code-review bito-code-review bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #5dfe02

Actionable Suggestions - 2
  • pkg/deno/downloader.go - 1
  • pkg/deno/runtime.go - 1
Additional Suggestions - 2
  • pkg/deno/runtime.go - 1
    • Redundant comments on constants · Line 24-35
      These comments merely repeat what the constant names already convey, violating the rule against obvious/redundant comments.
      Code suggestion
       @@ -23,14 +23,8 @@
      - const (
      - 	// ChartTSBundleFile is the path to the bundle in a Helm chart.
      - 	ChartTSBundleFile = ChartTSSourceDir + "dist/bundle.js"
      - 	// ChartTSEntryPointJS is the JavaScript entry point path.
      - 	ChartTSEntryPointJS = "src/index.js"
      - 	// ChartTSEntryPointTS is the TypeScript entry point path.
      - 	ChartTSEntryPointTS = "src/index.ts"
      - 	// ChartTSSourceDir is the directory containing TypeScript sources in a Helm chart.
      - 	ChartTSSourceDir = "ts/"
      - 	// RenderInputFileName is the name of the input file with context for the Deno app.
      - 	RenderInputFileName = "input.yaml"
      - 	// RenderOutputFileName is the name of the output file with rendered manifests from the Deno app.
      - 	RenderOutputFileName = "output.yaml"
      - )
      + const (
      + 	ChartTSBundleFile = ChartTSSourceDir + "dist/bundle.js"
      + 	ChartTSEntryPointJS = "src/index.js"
      + 	ChartTSEntryPointTS = "src/index.ts"
      + 	ChartTSSourceDir = "ts/"
      + 	RenderInputFileName = "input.yaml"
      + 	RenderOutputFileName = "output.yaml"
      + )
  • go.sum - 1
    • New External Dependency Added · Line 114-114
      New external dependency github.com/dustin/go-humanize v1.0.1 has been added as a direct dependency in go.mod. According to repository guidelines (AGENTS.md), new external dependencies must be flagged to the user first. Please confirm if this addition is intentional and necessary.
Review Details
  • Files reviewed - 34 · Commit Range: e6b3552..e17d511
    • cmd/nelm/chart.go
    • cmd/nelm/chart_lint.go
    • cmd/nelm/chart_pack.go
    • cmd/nelm/chart_render.go
    • cmd/nelm/chart_ts.go
    • cmd/nelm/chart_ts_build.go
    • cmd/nelm/groups.go
    • cmd/nelm/release_install.go
    • cmd/nelm/release_plan_install.go
    • go.mod
    • go.sum
    • internal/chart/chart_render.go
    • internal/ts/bundle.go
    • internal/ts/bundle_ai_test.go
    • internal/ts/bundle_test.go
    • internal/ts/esbuild.go
    • internal/ts/export_test.go
    • internal/ts/files.go
    • internal/ts/init.go
    • internal/ts/init_templates.go
    • internal/ts/init_test.go
    • internal/ts/package.go
    • internal/ts/package_test.go
    • internal/ts/render.go
    • internal/ts/render_ai_test.go
    • internal/ts/render_test.go
    • internal/ts/runtime.go
    • pkg/action/chart_lint.go
    • pkg/action/chart_render.go
    • pkg/action/chart_ts_build.go
    • pkg/action/release_install.go
    • pkg/action/release_plan_install.go
    • pkg/deno/downloader.go
    • pkg/deno/runtime.go
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Golangci-lint (Linter) - ✔︎ Successful
    • SNYK (Security Vulnerability) - ✔︎ Successful
    • OWASP (Security Vulnerability) - ✔︎ Successful
    • GOVULNCHECK (Security Vulnerability) - ✖︎ Failed

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at ilya.lesikov@flant.com.

Documentation & Help

AI Code Review powered by Bito Logo

return fmt.Errorf("add flag: %w", err)
}

if err := cli.AddFlag(cmd, &cfg.RebuildTSBundle, "rebuild-ts", false, "Rebuild the typescript bundle even if it already exists.", cli.AddFlagOptions{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mention the limitations in the description. And maybe different flag name. How about --ignore-bundle-js? If our file is bundle.js (I don't remember) And make it cfg.IgnoreBundleJS. That we rebuild bundle under the hood if this option is true is implementation detail, the user shouldn't know it.

return fmt.Errorf("add flag: %w", err)
}

if err := cli.AddFlag(cmd, &cfg.LogLevel, "log-level", string(log.InfoLevel), "Set log level. "+allowedLogLevelsHelp(), cli.AddFlagOptions{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check which flags are present in any command. Even if not always used. At least tempdir flag is missing.

"Initialize a new chart in the specified directory. If PATH is not specified, uses the current directory.",
10, // priority for ordering in help
chartCmdGroup,
"Initialize a new TypeScript chart.",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a TS chart initialization, but an initialization of the TS directory in the existing Helm chart

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we ensure chart structure too: we create Chart.yaml and values.yaml if it doesn't exist + we check .helmignore/.gitignore have necessary keys. Should we mention in cmd description that we ensure chart structure too?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chart.yaml shouldn't be created. Alright, let's say it's not "an initialization of the TS directory", but "an initialization of files needed to render manifests via TS" or something. Write whatever, as long as the user don't think we would generate the chart from scratch.

Comment on lines +71 to +75
if rt.rebuild && bundle != nil {
chart.RemoveRuntimeFile(ChartTSBundleFile)
}

chart.AddRuntimeFile(ChartTSBundleFile, bundleRes)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will happen when rt.rebuild == false && bundle != nil? Do we get two bundles (old and new) in RuntimeFiles?

Copy link
Contributor Author

@dmmordvi dmmordvi Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theoretically it shouldn't because of this condition

if bundle == nil || rt.rebuild {
.
But i will simplify condition to if bundle != nil {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, alright, I see now

}

defer func() {
_ = denoFile.Close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log error

_ = fileReader.Close()
}()

limitReader := io.LimitReader(fileReader, 200*1024*1024)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this limiter here. Let's not overcomplicate. If its more than 200mb for whatever reason why should we care really

Comment on lines +23 to +36
const (
// ChartTSBundleFile is the path to the bundle in a Helm chart.
ChartTSBundleFile = ChartTSSourceDir + "dist/bundle.js"
// ChartTSEntryPointJS is the JavaScript entry point path.
ChartTSEntryPointJS = "src/index.js"
// ChartTSEntryPointTS is the TypeScript entry point path.
ChartTSEntryPointTS = "src/index.ts"
// ChartTSSourceDir is the directory containing TypeScript sources in a Helm chart.
ChartTSSourceDir = "ts/"
// RenderInputFileName is the name of the input file with context for the Deno app.
RenderInputFileName = "input.yaml"
// RenderOutputFileName is the name of the output file with rendered manifests from the Deno app.
RenderOutputFileName = "output.yaml"
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say let's move these constants into pkg/common, they might be useful in different contexts without any logic of this package.

}

if bundle == nil || rt.rebuild {
if err := rt.ensureBinary(ctx); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are ensuring binary every time we recurse into a chart, but really only need to do it once. Maybe we better do downloading/caching outside of denoruntime? All we need is to go through Chart{}'s recursively and check if they have ts dir? And if there is one then download the thing to cache (unless custom path is specified)

Frankly, I don't think we need DenoRuntime class in here. Just make a few functions out of it, one will download binary, the other one will build bundle.js-es, the third one will run the bundles. There is no state to share so we don't need any classes for now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used DenoRuntime class only because of 3p-helm... It is simpler to pass it like class and run BundleChartsRecursive in chart pack, and everything will work under the hood (check ts dir, download deno, bundle and add files into RuntimeFiles)

dmmordvi added 2 commits March 6, 2026 19:22
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
dmmordvi added 4 commits March 6, 2026 23:23
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
…ve/ensureDeno, disabled rule for decompress bomb

Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>

# Conflicts:
#	cmd/nelm/chart_pack.go
#	go.mod
#	go.sum
#	internal/ts/bundle.go
#	internal/ts/bundle_ai_test.go
#	internal/ts/files.go
#	internal/ts/render_ai_test.go
#	internal/ts/render_test.go
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
…for ts initialization

Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
@kitsunoff
Copy link

Hey

I have a question

What wrong with goja runtime and why required to use a full sized runtime like Deno?

@dmmordvi
Copy link
Contributor Author

dmmordvi commented Mar 16, 2026

Hi @kitsunoff

We have an idea to provide users with the flexibility to use standard ecosystem packages such as @kubernetes/client-node, cdk8s, and kubernetes-models.

Trying to use goja, we found a critical limitation: it lacks support for Node.js native APIs. Since many vital Kubernetes-related npm packages rely on these built-ins under the hood, they simply will not work in a goja_nodejs environment.

Choosing goja would result in a poor dev experience, because users will have a lot of restrictions (e.g., inability to use packages with Node dependencies or requiring external build steps for TypeScript). Deno, with its Node.js compatibility layer and native TypeScript execution, resolves these limitations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants