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

@cooperco/nuxt-layer-base

v1.3.5

Published

Base Nuxt layer for cooperco projects

Downloads

763

Readme

Base Layer

A foundational layer that provides essential configuration and tooling for Nuxt 4 projects.

Compatibility Date: 2025-07-15

About Nuxt Layers

Nuxt Layers are a way to share configuration, modules, and app/server files across multiple Nuxt projects. An app “extends” a layer, and Nuxt will merge the layer’s modules, runtime configuration, and directory contents (like app/, server/, and plugins/) with the app’s own files. This lets teams centralize common tooling and conventions while each app remains free to add its own code.

Learn more in the official Nuxt Layers documentation: https://nuxt.com/docs/guide/going-further/layers

For app developers: Using this layer

What this layer provides

  • TypeScript with strict mode enabled
  • ESLint via @nuxt/eslint with stylistic Vue template rules
  • Internationalization (i18n) via @nuxtjs/i18n
  • Hints and best practices via @nuxt/hints
  • Accessibility checks via @nuxt/a11y
  • Nuxt DevTools enabled in development
  • Optional error and event logging to Loggly (client and server), disabled by default

Install in your app

  1. Add the layer to your project as a dev dependency
# npm
npm i -D @cooperco/nuxt-layer-base

# pnpm
pnpm add -D @cooperco/nuxt-layer-base

# yarn
yarn add -D @cooperco/nuxt-layer-base
  1. Extend the layer in your app’s Nuxt config
// nuxt.config.ts (in your app)
export default defineNuxtConfig({
  extends: ['@cooperco/nuxt-layer-base']
})

TypeScript strict mode and DevTools are active immediately. For ESLint, add a config in your app (see "ESLint in consumer apps"). For @nuxt/hints, you must also install it as a dev dependency in your app (see "Hints and Best Practices (@nuxt/hints)").

Internationalization (i18n)

This layer integrates the Nuxt i18n module. Use it directly in your components; preferred usage is an SFC <i18n> block. You can also call useI18n() in scripts.

Basic usage in a component:

<script setup lang="ts">
const { t, locale } = useI18n()
</script>

<template>
  <p>{{ t('hello') }}</p>
  <small>Current locale: {{ locale }}</small>
  <!-- Provide your own translation files in your app (e.g., locales/en.json) -->
</template>

Preferred: single‑file component local messages using an block

<script setup lang="ts">
const { t } = useI18n()
</script>

<template>
  <h1>{{ t('welcome') }}</h1>
  <p>{{ t('cta') }}</p>
</template>

<i18n lang="json5">
{
  en: {
    welcome: 'Welcome',
    cta: 'Click to continue'
  },
  fr: {
    welcome: 'Bienvenue',
    cta: 'Cliquez pour continuer'
  }
}
</i18n>

Both global files (e.g., locales/en.json) and per‑component <i18n> blocks are supported.

Hints and Best Practices (@nuxt/hints)

This layer includes @nuxt/hints, which provides real-time suggestions for improving your application's performance, security, and best practices directly in your development environment.

Important: Runtime Dependency

Because @nuxt/hints injects runtime code into your components (to track hydration and other metrics), you must add @nuxt/hints to your app's devDependencies even though it is provided by the layer. This ensures that the bundler can resolve the injected imports.

# npm
npm i -D @nuxt/hints

# pnpm
pnpm add -D @nuxt/hints

# yarn
yarn add -D @nuxt/hints

Note: If you are using a monorepo with workspaces (e.g., pnpm workspaces) where dependencies are hoisted to the root, this manual installation may not be necessary.

Accessibility (@nuxt/a11y)

This layer includes @nuxt/a11y, which integrates accessibility checks and hints directly into your development workflow. It helps you identify and fix accessibility issues as you build your application.

No additional configuration is required to use the basic features of @nuxt/a11y once the layer is extended.

Logging (optional, Loggly‑backed)

When enabled via environment variables, the base layer will:

  • Capture server-side errors (Nitro request lifecycle)
  • Capture client-side errors (Nuxt app errors, Vue component errors, global window errors, unhandled rejections)
  • Provide a useLogger() composable for custom logs (and a deprecated useLoggly() wrapper)
  • Proxy all logs through the canonical route /api/log, where payloads are normalized and sensitive fields are scrubbed before forwarding to Loggly

Enable in your app with environment variables:

  • NUXT_PUBLIC_LOGGLY_ENABLED=true
  • NUXT_LOGGLY_TOKEN=<your Loggly customer token> (server-only)
  • NUXT_PUBLIC_LOGGLY_TAGS=app-name,env (optional, comma-separated)
  • NUXT_PUBLIC_LOG_LEVEL=error (optional; exposed to client for your own use)
  • NUXT_LOGGLY_ENDPOINT=https://logs-01.loggly.com/inputs (optional override, server-only)

Notes

  • The token is never exposed to the client. Browsers only post to your app’s /api/log endpoint (with /api/loggly kept as a deprecated alias).
  • If logging is disabled or a token is not provided, the logging code is effectively a no-op and the API returns { ok: false, skipped: true }.
  • LOG_LEVEL is exposed via runtime config but the base layer does not filter logs by level; you may use it in your own app logic.
  • Back‑compat alias: /api/loggly remains available but is deprecated. Prefer /api/log.

Tags behavior

  • Automatic, source‑specific tags are appended for captured errors:
    • Nuxt app‑level errors: client, nuxt
    • Vue component errors: client, vue
    • Global window error events: client, window
    • Unhandled rejections: client, promise
    • Server‑side errors: server
  • Your LOGGLY_TAGS (e.g., app-name,prod) and per‑call tags from useLogger() are merged with the automatic tags; duplicates are removed and the list is capped at 10 tags.

Composable usage:

// inside a component or any composable
const log = useLogger()
await log.error('Checkout failed', { orderId, step: 'place-order' }, ['checkout'])
await log.warn('Slow response', { endpoint: '/api/orders', ms: 850 })
await log.info('User clicked CTA', { campaign: 'summer' }, ['marketing'])
await log.debug('State snapshot', { state })

Automatically captured errors (when enabled):

  • Server: request-time exceptions via a Nitro plugin
  • Client: Nuxt app errors, Vue component errors, window error events, unhandled promise rejections

Security and privacy:

  • The server proxy masks common sensitive keys in meta (e.g., password, token, authorization, ssn, etc.)
  • Logging is fire-and-forget; failures to log are swallowed to avoid breaking UX

Verify locally:

  1. Add env vars in your app (e.g., .env.local):
NUXT_PUBLIC_LOGGLY_ENABLED=true
NUXT_LOGGLY_TOKEN=...your token...
NUXT_PUBLIC_LOGGLY_TAGS=app-name,local
  1. Start your app and trigger a test error.
  2. Check the network tab for POST /api/log and then confirm the entry in Loggly.

Troubleshooting:

  • No logs? Ensure LOGGLY_ENABLED=true and LOGGLY_TOKEN is present in the server environment.
  • Browser CORS or network errors? The browser never talks to Loggly directly; it only posts to /api/log.
  • High volume? Consider adding sampling or batching at the proxy later if needed.

ESLint in consumer apps

This layer integrates ESLint via the @nuxt/eslint module. When you extend this layer, ESLint is available to your app; you only need to add a config file and scripts in your project.

  1. Create eslint.config.mjs in your app root (copy from this layer and adjust only if necessary)
// eslint.config.mjs (in your app)
// Nuxt generates a flat config at ./.nuxt/eslint.config.mjs
// We enable stylistic rules and add a few custom rules matching the base layer.
// @ts-check
import withNuxt from './.nuxt/eslint.config.mjs'

export default withNuxt({
  rules: {
    /* Stylistic */
    '@stylistic/comma-dangle': [
      'error',
      'only-multiline'
    ],
    '@stylistic/no-tabs': [
      'error',
      { allowIndentationTabs: true }
    ],

    /* TS */
    '@typescript-eslint/no-unused-vars': [
      'error',
      { caughtErrorsIgnorePattern: '^_' }
    ],

    /* Vue */
    'vue/html-closing-bracket-newline': [
      'error',
      { multiline: 'never', selfClosingTag: { multiline: 'never' } }
    ],
    'vue/html-closing-bracket-spacing': [
      'error',
      { selfClosingTag: 'never' }
    ],
    'vue/html-indent': [
      'error', 'tab',
      { baseIndent: 0 }
    ],
    'vue/multi-word-component-names': ['error', {
      ignores: []
    }],
    'vue/component-name-in-template-casing': [
      'error',
      'kebab-case',
      { registeredComponentsOnly: false, ignores: [] }
    ],
    'vue/component-options-name-casing': ['error', 'kebab-case'],
    'vue/component-definition-name-casing': ['error', 'kebab-case']
  }
})
  1. Add scripts and run ESLint
{
  "scripts": {
    "lint": "nuxt prepare && eslint .",
    "lint:fix": "nuxt prepare && eslint . --fix"
  }
}

Notes

  • The base layer enables stylistic mode via Nuxt (eslint.config.stylistic: true).
  • Do not use Prettier alongside stylistic rules; remove Prettier configs/plugins from your app to avoid conflicts.
  • Tabs are allowed for indentation (including in Vue templates). The no-tabs rule is enabled but permits indentation tabs.

What this ESLint config enforces (in plain English)

  • Base: It starts from Nuxt’s generated, project‑aware flat config (via @nuxt/eslint), then turns on stylistic mode for consistent formatting in JS/TS/Vue files.
  • This layer adds a few focused rules to keep things consistent and practical:
    • @stylistic/comma-dangle: 'only-multiline' — Allow trailing commas only when the list spans multiple lines; disallow them on single‑line lists.
      • Good (multiline, trailing comma ok):
        const user = {
        	id: 1,
        	name: 'Ada',
        }
      • Good (single‑line, no trailing comma): const arr = [1, 2, 3]
    • @stylistic/no-tabs with { allowIndentationTabs: true } — Tabs are allowed for indentation but tabs elsewhere are flagged.
    • @typescript-eslint/no-unused-vars with { caughtErrorsIgnorePattern: '^_' } — Flag unused variables, but allow a try/catch parameter that starts with _ when you don’t use it.
      • Good (explicitly ignored):
        try {
        	risky()
        }
        catch (_err) {
        	// intentionally ignored
        }
    • vue/html-closing-bracket-newline: { multiline: 'never', selfClosingTag: { multiline: 'never' } } — Don’t put a newline before a closing bracket, even for multi‑line attribute lists.
      • Bad:
        <my-comp
        	a="1"
        	b="2"
        />
      • Good:
        <my-comp
        	a="1"
        	b="2" />
    • vue/html-closing-bracket-spacing: { selfClosingTag: 'never' } — No space before a self‑closing />.
      • Bad: <my-comp />
      • Good: <my-comp/>
    • vue/html-indent: ['tab', { baseIndent: 0 }] — Use tabs for indentation in Vue templates.
      • Example:
        <template>
        	<div>
        		<span>Text</span>
        	</div>
        </template>
    • vue/multi-word-component-names — Enforce multi‑word component names (no single generic names like Header conflicts). Configure ignores if needed.
    • vue/component-name-in-template-casing: 'kebab-case' — In templates, component tags must be kebab‑case (e.g., <my-widget/>, not <MyWidget/>).
    • vue/component-options-name-casing: 'kebab-case' — In SFC component options, the name should be kebab‑case.
    • vue/component-definition-name-casing: 'kebab-case' — For component definitions in script, enforce kebab‑case names.

TypeScript in consumer apps

Strict mode is enabled by default. No configuration is required in your app.

Optional type checking script (install vue-tsc in your app):

npm i -D vue-tsc
{
  "scripts": {
    "typecheck": "nuxt prepare && vue-tsc -b --noEmit"
  }
}

GitHub Actions in your app (linting and type checks)

To run the same checks in your app’s GitHub repository:

  1. Copy workflow files into your app’s repo
  • Create .github/workflows/lint.yml with a job that checks out your code, sets up Node, installs dependencies, and runs npm run lint.
  • (Optional) Create .github/workflows/typecheck.yml to run npm run typecheck if you added the script.

Minimal examples you can adapt:

.github/workflows/lint.yml

name: Lint
on:
  pull_request:
    branches: ['main']
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run lint

.github/workflows/typecheck.yml

name: Typecheck
on:
  pull_request:
    branches: ['main']
jobs:
  typecheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run typecheck
  1. Configure GitHub as needed
  • Enable Actions in your repository settings (if disabled).
  • If your app installs private packages, set appropriate repository secrets (e.g., NPM_TOKEN, organization PATs) and reference them in the workflow. For public packages only, no extra secrets are required.
  1. Open a pull request to see the checks run.

Claude Code Skills

This repo includes Claude Code skills for common tasks in apps that use these layers. To use them, copy the relevant skill files from the skills/ directory into your app's .claude/skills/ directory.

Skills provided by the base layer:

| Skill | File | Description | |-------|------|-------------| | /setup-layers | skills/setup-layers.md | Bootstrap a new Nuxt app to extend CooperCo layers — generates nuxt.config.ts, installs dependencies, configures ESLint and i18n | | /add-i18n | skills/add-i18n.md | Add <i18n> blocks to SFCs, create/update global locale files, or scaffold a new locale | | /add-api-route | skills/add-api-route.md | Scaffold a Nitro server route with useLogger(), TypeScript types, and sensitive field scrubbing |

# Copy base layer skills into your app
mkdir -p .claude/skills
cp node_modules/@cooperco/nuxt-layer-base/../../skills/setup-layers.md .claude/skills/
cp node_modules/@cooperco/nuxt-layer-base/../../skills/add-i18n.md .claude/skills/
cp node_modules/@cooperco/nuxt-layer-base/../../skills/add-api-route.md .claude/skills/

Or copy them directly from the nuxt-layers repository.

For maintainers: Working on this layer

Repository layout and Nuxt 4 directories

  • Layer root: layers/base
  • Nuxt 4 app directories are used inside the layer:
    • app/plugins/... (e.g., app/plugins/loggly.client.ts)
    • app/composables/... (e.g., app/composables/useLogger.ts; deprecated wrapper: app/composables/useLoggly.ts)

Development scripts

# From layers/base
npm install
npm run dev        # start a dev server for the layer (via Nuxt)
npm run lint       # ESLint (with nuxt prepare)
npm run lint:fix   # ESLint --fix
npm run typecheck  # vue-tsc

Linting and TypeScript

  • TypeScript strict mode is enforced via nuxt.config.ts
  • ESLint is provided by @nuxt/eslint with stylistic rules enabled and custom rules in eslint.config.mjs

Logging implementation (overview)

  • Client plugin: app/plugins/loggly.client.ts hooks into Nuxt/Vue error streams and window events and posts to /api/log
  • Composable: app/composables/useLogger.ts exposes error|warn|info|debug helpers posting to /api/log (deprecated wrapper useLoggly.ts delegates to useLogger())
  • Server plugin: server/plugins/loggly.ts hooks Nitro error events and posts to /api/log
  • API endpoint (canonical): server/api/log.post.ts scrubs/normalizes payloads and forwards to Loggly using LOGGLY_TOKEN
  • API endpoint (alias, deprecated): server/api/loggly.post.ts re-exports the canonical handler so /api/loggly continues to work
  • Runtime config (see nuxt.config.ts):
    • Server-only: logglyToken, logglyEndpoint
    • Public: logglyEnabled, logglyTags, logLevel
    • Supports both LOGGLY_* and NUXT_PUBLIC_LOG_* env vars where appropriate

Environment files

  • layers/base/.env is only for developing the base layer directly (not used by consuming apps)
  • For the repo playground, use playground/.env
  • For downstream apps, use .env or .env.local in the app’s root

Playground

There is a simple playground app at playground that extends the base layer and exposes UI to trigger logging. This is useful for end‑to‑end verification during development.

Run from repo root:

cd playground
npm run dev

Publishing to npm (tag-based)

This package is published via GitHub Actions when you push a tag that matches base-vX.Y.Z.

High-level flow:

  • Bump the version in layers/base/package.json (SemVer)
  • Commit and push to main
  • Create and push a tag base-vX.Y.Z matching the version
  • CI checks whether the version exists on npm and publishes if not

Important notes:

  • Do NOT rely on npm version to create the tag (it creates vX.Y.Z). Create base-vX.Y.Z yourself.
  • publishConfig.access is set to public; publishes are public to npmjs.
  • The workflow skips if the exact version already exists.

Step-by-step

  1. Bump version without creating a tag:
cd layers/base
npm version patch --no-git-tag-version  # or minor | major
  1. Commit and push:
git add layers/base/package.json
git commit -m "chore(base): bump version"
git push origin main
  1. Tag and push:
cd layers/base
VERSION=$(node -p "require('./package.json').version")
cd ../..
git tag "base-v$VERSION"
git push origin "base-v$VERSION"
  1. CI will publish
  • Workflow: .github/workflows/publish-base.yml
  • Auth: NPM_TOKEN GitHub secret

Troubleshooting

  • Version exists: bump again and retag
  • 401/403: ensure NPM_TOKEN is configured and has access