@sidebase/base-config

Shared @sidebase base configuration for Nuxt repos: ESLint / Prisma / tsconfig factories on npm, plus the streamctl file-sync preset payload
2,355
Install
npm install @sidebase/base-config
Latest Version:0.4.0
License:MIT
Last Updated:Oct 13, 2026
Links: npm
Maintainer: fervid

@sidebase/base-config

Shared setup for sidebase Nuxt 4 apps. @sidebase/streamctl syncs this package's config files (CI, Dockerfile, tsconfig, lint config, editor settings, ...) into your repo and keeps them up to date. The package also exports ESLint, Prisma and tsconfig helpers.

Quick start

Requires Nuxt 4, Node >= 24.21 and pnpm.

pnpm add -D @sidebase/streamctl
pnpm streamctl init --package @sidebase/base-config

init adds @sidebase/base-config, writes streamctl.config.ts, and runs the first sync. Review the changes and commit them.

Coming from @sidestream-tech/nuxt-config? Follow docs/migration.md instead.

What you get

File Managed as What it is
.github/workflows/ci.yaml owned Lint, typecheck and build, plus optional test and e2e jobs
.github/workflows/pr-preview-cleanup.yaml owned Removes the preview-deployed label when preview is removed
.github/workflows/streamctl-upgrade.yaml owned, opt-in Weekly upgrade PRs (see Upgrades)
Dockerfile owned Production image, runs prisma migrate deploy and then the server
tsconfig.json owned Extends the shared strict base and Nuxt's generated config
pnpm-workspace.yaml owned 7-day release cooldown and the install-script allowlist
.oxlintrc.json owned oxlint rules, run before ESLint
AGENTS.md owned Conventions for coding agents
.editorconfig, .gitignore, .dockerignore block A marked block; the rest of the file is yours
.vscode/settings.json, .vscode/extensions.json merged Only the keys the payload sets; your other keys stay
eslint.config.ts, prisma.config.ts scaffolded Written once, then yours
  • owned: streamctl writes the whole file. Local edits show up as drift in check.
  • block: only the region between the BEGIN/END streamctl MANAGED BLOCK markers is managed.

sync also updates package.json:

  • engines, packageManager, and the version ranges of nuxt, typescript, eslint, oxlint, prisma, @prisma/client, jiti, json-sort-cli and vitest. A range is only raised, never lowered.
  • scripts.postinstall (nuxt prepare) and scripts.lint (oxlint, then ESLint).

Everyday use

pnpm streamctl sync      # apply the payload after changing the config
pnpm streamctl check     # CI gate: fails when managed files drifted
pnpm streamctl upgrade   # move to the latest payload version and re-sync

sync stops on conflicts, e.g. a file you edited by hand. Resolve them with pnpm streamctl sync --interactive.

Upgrades

Upgrade by hand with pnpm streamctl upgrade, or let a scheduled workflow open the PRs:

automation: { upgradePr: true },

It runs Mondays at 06:00 UTC. Pick your own time with automation.schedule (a five-field cron) and automation.scheduleTimezone (an IANA name):

automation: { upgradePr: true, schedule: "30 14 * * 5", scheduleTimezone: "Europe/Berlin" },

Day-of-month and day-of-week combine with OR: "0 16 1-7 * 5" is not "first Friday".

The workflow runs streamctl check, lint, typecheck, test and build before it opens the PR. Files that need manual work end up in a draft PR with instructions. When a PR for a newer version opens, older upgrade PRs are closed with a link to it.

By default it uses GITHUB_TOKEN, which may not change .github/workflows/**, so workflow changes are left out of the PR. To include them, set automation.allowWorkflowUpdates: true and add a STREAMCTL_PR_TOKEN secret (a fine-grained PAT or GitHub App token with Contents, Pull requests and Workflows write). That token also makes the PR trigger your normal CI.

Configuration

Everything is configured in streamctl.config.ts. defineNuxtBaseConfig gives you completion and type checking for every option:

import { defineNuxtBaseConfig } from "@sidebase/base-config";

export default defineNuxtBaseConfig({
  package: "@sidebase/base-config",
  base: "nuxt-app",
  version: "0.3.0",
  profile: "nuxt-4",

  ci: { unitTests: true, e2e: true },
  docker: { aptPackages: ["ffmpeg"] },
  automation: { upgradePr: true },
  editor: { i18nSourceLanguage: "en" },
});

Options

Option Default Effect
ci.unitTests false Add a test job (pnpm test)
ci.e2e false Add an e2e job with a postgres service
ci.aptPackages [] apt packages for the test/e2e jobs and upgrade validation
docker.aptPackages [] apt packages for the image, on top of openssl
docker.preInstall "" Dockerfile lines before pnpm install
docker.buildArgs "" Dockerfile lines before COPY . .
docker.buildSteps nuxi prepare, prisma generate, build The build commands
docker.finalStageSetup "" Dockerfile lines in the final stage, before the app is copied. Cached across app changes, so use it for installs that do not need the app
docker.finalStage "" Dockerfile lines in the final stage, before CMD. The image runs as root unless this adds USER node
docker.prismaRuntime copy schema, install Prisma CLI Prisma setup in the final stage
docker.prismaVersion your prisma version Prisma CLI version in the image
docker.startCommand npm exec prisma migrate deploy && exec node ... The CMD, one line
versions.node 24.21.0 Node in CI, the Dockerfile and the upgrade workflow
versions.pnpm 10.34.5 pnpm in the Dockerfile (CI uses packageManager)
automation.upgradePr false Add the scheduled upgrade workflow
automation.allowWorkflowUpdates false Include workflow changes in upgrade PRs (needs a token)
automation.prTokenSecret "STREAMCTL_PR_TOKEN" Secret name for that token
automation.prepare pnpm prisma generate if Prisma is installed Command run before validating an upgrade; "" skips
automation.schedule "0 6 * * 1" Cron for the upgrade workflow
automation.scheduleTimezone "UTC" IANA time zone for that cron
security.minimumReleaseAge "10080" Minutes a release must be public before pnpm installs it; "0" disables
pnpm.onlyBuiltDependencies [] Extra packages allowed to run install scripts
editor.i18nSourceLanguage "de" i18n-ally source language

Things to know:

  • A docker.* text option replaces the default. It does not append, so a buildSteps override must list every build command you still need.
  • docker.prismaRuntime and docker.startCommand go together. Without Prisma, set prismaRuntime: "" and a startCommand without migrate deploy, e.g. exec node .output/server/index.mjs. Keep the exec, so node gets SIGTERM.
  • Repos with English source strings must set editor.i18nSourceLanguage: "en". Otherwise every sync writes "de" back.
  • Adopting pnpm-workspace.yaml replaces your existing file, including any onlyBuiltDependencies. Move those entries to pnpm.onlyBuiltDependencies instead of running pnpm approve-builds, which the next sync reverts.

Customizing managed files

Block files: put your own settings below the END marker. For .editorconfig, later sections win, so an override below the block takes precedence. On the first sync, the block is appended after your existing content, so move that content below the block once.

Opt a file out when the options are not enough:

files: { "Dockerfile": "off" },

streamctl then leaves the file alone, including all future fixes to it.

Keep your own scripts.lint (or any other synced package.json field):

versionSyncExclude: ["scripts.lint"],

Helpers

ESLint

// eslint.config.ts
import { createSidebaseEslint } from "@sidebase/base-config/eslint";

export default createSidebaseEslint({ zod: "full", trpcGuard: true })
  .append({ rules: { "vue/multi-word-component-names": "off" } });

Wraps @antfu/eslint-config, which is a dependency of this package; don't install it yourself. Needs eslint and jiti (for the TypeScript config file) in your devDependencies; sync adds both.

Option Default Effect
zod "none" "import-style": enforce import * as z. "full": also ban .extend(), .merge(), .passthrough()
console "error" Ban all console.*, or pass allowed methods, e.g. ["warn", "error"]
trpcGuard false Ban publicProcedure in server/trpc/routers/**
prismaImportGuard false Ban ~~/prisma/ client imports outside server code
typeDefStyle "interface" Prefer interface or type
autoImportPaths ["utils/", "composables/", "~~/shared/types/"] Paths Nuxt auto-imports, so direct imports are banned in app code. The defaults cover app/utils, shared/utils, app/composables and shared/types under every Nuxt alias. Files in the root (or layers/*) server/ and shared/ dirs and relative imports are never matched
autoImportTypeOnly [] Of those, paths where import type stays allowed
ignoresTypeAware config files Files excluded from type-aware linting
testFilePattern *.test, *.spec, *.stories Files where the process.env and auto-import bans are off

Type-aware linting is off by default; run with LINT_TYPEAWARE=true to enable it.

To silence a single line, use the rule ID:

Ban Rule ID
process.env.X no-restricted-properties
import { env } from "node:process" and similar ts/no-restricted-imports
Auto-import paths, app-side Prisma client ts/no-restricted-imports
Zod import style and methods, publicProcedure no-restricted-syntax

Prisma

// prisma.config.ts
import "dotenv/config";
import { applyPrismaDevEnv, buildPrismaConfig } from "@sidebase/base-config/prisma";
import { defineConfig } from "prisma/config";

applyPrismaDevEnv(); // optional: localhost defaults for unset DB URLs
export default defineConfig(buildPrismaConfig({ views: true, typedSql: true }));
  • buildPrismaConfig reads DATABASE_URL, DIRECT_DATABASE_URL and SHADOW_DATABASE_URL. Migrations use DIRECT_DATABASE_URL, or DATABASE_URL with the pooler parameters removed. views and typedSql enable those Prisma features.
  • applyPrismaDevEnv sets localhost defaults for the URLs that are unset, so prisma migrate dev works on a fresh checkout without a .env.

Prisma 6.19 only; prisma and @prisma/client are optional peer dependencies.

tsconfig

The managed tsconfig.json extends @sidebase/base-config/tsconfig.base, the shared strict config, before Nuxt's generated one. You can extend it directly as well.

Troubleshooting

  • No version profile could be detected: the repo is not on Nuxt 4. Nuxt 3 is not supported.
  • pnpm approve-builds changes disappear: use pnpm.onlyBuiltDependencies.
  • .editorconfig settings ignored after the first sync: your old content sits above the managed block. Move it below.
  • eslint.config.ts fails to load: add jiti to devDependencies.
  • Repo with private npm dependencies: the upgrade workflow installs without registry credentials. Opt it out and use your own.

Developing this package

pnpm lint && pnpm typecheck && pnpm test
pnpm validate:presets   # manifests and cross-file invariants
pnpm e2e:dry-run        # pack, install into a scratch consumer, sync and check
pnpm e2e:docker         # build and start the rendered Dockerfile (needs Docker)
  • The payload lives in presets/ (base, and nuxt-app on top of it). The manifests in preset.json define files, options and version baselines.
  • Tests check contracts derived from the manifests, so changing a preset file rarely needs a test change.
  • Actions in managed workflows are pinned by commit SHA; the tests enforce it.
  • Requires streamctl >= 0.4.0 (manifest schema version 4).
  • Releases: docs/release.md. Agent conventions: AGENTS.md.

Related Packages

@novicell/eslint-config-nuxt

A Eslint config for Nuxt projects with TypeScript

2,445
create-sidebase

The productive way to build fullstack Nuxt 3 applications, like create-t3-app bu...

1,432 30
eslint-import-resolver-nuxt-import

ESLint plugin with rules that help validate proper imports.

1,658 2
eslint-plugin-reactive-value-suffix

This ESLint plugin enforces adding `.value` when accessing reactive values in th...

859 4
@privyid/eslint-config-persona

Eslint shareable config for Persona project

5,861 68

Version History

Version Released License
0.4.0 MIT
0.3.7 MIT
0.3.6 MIT
0.3.5 MIT
0.3.4 MIT