npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tula-tech/config

v0.4.0

Published

Tula's config file: defineConfig() for tula.config.ts, validated with the contract's schemas. Environment settings and OAuth providers as code; secrets only by env-var reference.

Readme

@tula-tech/config

defineConfig() for tula.config.ts: an environment's settings and OAuth providers of Tula Auth as a typed, validated file that is reviewed and versioned like code. tula diff and tula apply (@tula-tech/cli) make a server match it. See docs/config.md.

bun add -d @tula-tech/config@latest @tula-tech/cli@latest

Early release (0.x). The file's shape may change from one release to the next. Install it at the same version as @tula-tech/cli. Everything else is in the repository.

import { defineConfig, env } from '@tula-tech/config'

export default defineConfig({
  environments: {
    prod: {
      kind: 'production',
      settings: {
        app: { name: 'Northline', supportEmail: '[email protected]' },
        urls: { allowedOrigins: ['https://app.northline.app'] },
        mfa: { policy: 'required' },
      },
      providers: {
        google: {
          clientId: '1234.apps.googleusercontent.com',
          clientSecret: env('GOOGLE_CLIENT_SECRET'),
        },
      },
    },
  },
})
  • Validated with the contract's schemas. settings is the body of PUT /v1/admin/settings; an unknown key is an error with its path (environments.prod.settings.pasword: unknown key).
  • Secrets only by reference. A provider's clientSecret or privateKey is env('NAME'). A literal string does not compile and is refused when the file is loaded; the error never repeats it. The variable is read by tula apply, when the provider is written.
  • Several environments in one file. The name is a label for --env; which environment a run changes is decided by the secret key it is given. kind makes the CLI refuse a key of the other kind.
  • loadConfig(path) imports the file (it is code its operator trusts) and validates it again. A TypeScript config needs Bun, or Node 22.18 or later; a .js config loads anywhere.

What a file can hold

Every key under an environment is optional. A file manages only what it names.

| Key | What it describes | Details | | --- | --- | --- | | kind | 'development' or 'production'; the CLI refuses a secret key of the wrong kind | The file | | settings | The environment's settings: app name, URLs, password policy, two-step verification, sessions | docs/config.md | | providers | OAuth providers (google, apple, microsoft and the others the server supports) | docs/config.md | | webhooks | Webhook endpoints. Absent means not managed; a list manages exactly what it names | Webhook endpoints | | hooks | The questions the server asks your endpoints before a sign-up, a session and a token | Hooks | | nativeApps | iOS and Android apps registered for the environment | Native apps | | organizationRoles | Custom organization roles | Organization roles |

What a missing field means

tula apply replaces the whole settings document, so the file is the truth: a setting you leave out goes back to its default. Two exceptions keep what the server has, because their default belongs to the deployment: password and urls.allowedOrigins. A provider the file does not mention is left alone and shown as unmanaged; tula apply --prune deletes it. See What a missing field means.

Exports

| Export | Use | | --- | --- | | defineConfig(input) | Types and validates a config at the call site. Returns it unchanged in shape. | | env(name) | A reference to a secret held in an environment variable. | | isSecretRef(value) | Whether a value is such a reference. | | loadConfig(path) | Imports the file at path and validates it again; returns { path, config }. | | parseConfig(value) | Validates an already-loaded value; throws ConfigError. | | selectEnvironment(config, name) | Picks one environment by its label (the only one, if name is undefined). | | requiredSecrets(providers) | The variable name each provider needs, never a value. | | resolveSecret(ref, variables) | Reads a reference's variable from variables; used by tula apply. | | hashEnvironmentConfig(environment) | Async. The fingerprint tula apply records, computed over variable names. | | ConfigError, isConfigError | The one error class, with a code and a list of issues (each has a path). | | DEFAULT_CONFIG_FILE | tula.config.ts, the file name the CLI looks for. |

Every config type (TulaConfig, EnvironmentConfig, WebhookEndpointConfig, HooksConfig, NativeAppConfig, OrganizationRoleConfig, ...) is exported too.

Loading a file yourself

import { ConfigError, loadConfig, requiredSecrets, selectEnvironment } from '@tula-tech/config'

try {
  const { config } = await loadConfig('tula.config.ts')
  const prod = selectEnvironment(config, 'prod')
  console.log(requiredSecrets(prod.providers)) // { google: 'GOOGLE_CLIENT_SECRET' }
} catch (error) {
  if (error instanceof ConfigError) {
    // Each issue has the path of the problem, e.g. environments.prod.settings.pasword
    console.error(error.issues)
  }
  throw error
}

Security notes

  • A secret is never in the file. A literal string where a secret belongs is a type error and refused at load time. The error names the path and never repeats the value.
  • Webhook endpoints hold no secret at all. Each endpoint's signing secret is made by the server and shown once; it is never written into the file.
  • The fingerprint says nothing about a secret's value. It is computed with each secret as its variable's name.
  • The file is code that runs when it is loaded, like a build script. Review changes to it the way you review code, and never load a file you do not trust.
  • A plan that weakens security needs a person or a flag. tula apply --yes refuses to lower, for example, mfa.policy unless --allow-weaker is given as well. See docs/config.md.

Compatibility

  • Runs in Bun and in Node 22.18 or later (a TypeScript config needs one of these; a .js config loads anywhere). ESM only.
  • Depends on Zod and on @tula-tech/contract, whose schemas it reuses. It is tooling and must never reach an application's bundle: install it as a development dependency.
  • Keep it at the same version as @tula-tech/cli, which reads the file.

Related packages

Full guide: docs/config.md.