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

@quvel-kit/core

v1.3.23

Published

Core utilities for Quvel UI

Downloads

1,951

Readme

@quvel-kit/core

Core utilities and infrastructure for Quasar + Laravel applications.

Features

Service Container System

  • Isomorphic per-request container with Initialize → Boot → Register lifecycle
  • SSR-aware services that can boot with request context
  • Dependency injection with type-safe service resolution
  • Service registration and lifecycle management

Core Services

All services are included and ready to use out of the box:

  • LogService - Structured logging with trace correlation
  • ApiService - Axios-based HTTP client with automatic trace headers
  • I18nService - Internationalization with module support
  • ValidationService - Zod-based validation with i18n integration
  • WebSocketService - Laravel Echo + Pusher WebSocket support
  • TaskService - Async task management with loading/error handling
  • ThemeService - Light/dark mode management

Configuration is managed directly by the ServiceContainer as a first-class property, not as a service. All services receive AppConfig in their constructor.

Module System

  • Module-based architecture for routes, i18n, services, and build config
  • Resource path helpers for module assets
  • Dynamic service registration per module

Utilities

  • Asset loading - Dynamic CSS/JS injection for client and SSR
  • Theme management - Light/dark mode with Quasar and Tailwind
  • Script loading - Dynamic external script management
  • Error handling - Laravel validation error processing
  • Config utilities - Environment-based configuration merging

Composables

  • useQuvel - Access Quvel service container in components
  • useClient - Client-side detection for SSR-aware components
  • useWindowEvent - Safe window event listener management
  • useScript - Dynamic script loading with state tracking
  • useMetaConfig - Page meta tags with SEO optimization
  • useWebSockets - WebSocket channel subscription
  • useScopedService - Component-scoped service instances
  • useRecaptcha - Google reCAPTCHA integration
  • useXsrf - XSRF token management
  • useQueryMessageHandler - URL query parameter message handling
  • useUrlQueryHandler - Generic URL query parameter processing

Installation

npm install @quvel-kit/core
# or
yarn add @quvel-kit/core

Configuration

Environment Variables

See .env.example for all available configuration options.

Required Variables

# Application name
VITE_APP_NAME="My App"

# Backend API URL (Laravel)
VITE_API_URL=http://localhost:8000

# Frontend app URL (Quasar)
VITE_APP_URL=http://localhost:9000

Optional Variables

# Application environment
VITE_APP_ENV=local

# Debug mode
VITE_DEBUG=true

# Application timezone
VITE_TIMEZONE=UTC

# Application locale
VITE_LOCALE=en

# Fallback locale
VITE_FALLBACK_LOCALE=en

# Session cookie name (must match Laravel)
VITE_SESSION_NAME=quvel_session

# Internal API URL for SSR server to backend communication
# Can be an internal network address for better performance
# Defaults to VITE_API_URL if not set
VITE_INTERNAL_API_URL=http://api-internal:8000

# Capacitor custom scheme for mobile apps
VITE_CAPACITOR_SCHEME=myapp

AppConfig Structure

The configuration follows a structured format matching the backend:

interface AppConfig {
  app: {
    name: string;              // Application name
    url: string;               // Backend API URL
    env?: string;              // Environment (local, development, production)
    debug?: boolean;           // Debug mode
    timezone?: string;         // Timezone (UTC, America/New_York, etc.)
    locale?: string;           // Default locale (en, es, fr, etc.)
    fallback_locale?: string;  // Fallback locale
  };
  frontend: {
    url: string;               // Frontend app URL
    custom_scheme?: string;    // Capacitor custom scheme
  };
  assets?: AppAssets;          // Dynamic CSS/JS injection
  meta?: AppMeta;              // Page meta configuration
  headers?: HeadersConfig;     // HTTP headers customization
  api?: AppApiConfig;          // API configuration (SSR key, etc.)
  broadcasting?: BroadcastingConfig; // WebSocket/Pusher configuration
  session?: SessionConfig;     // Session cookie configuration
  i18nCookie?: string;         // I18n locale cookie name
  userFactory?: UserFactory;   // Custom user model factory
  trace?: TraceInfo;           // Request trace context (SSR-injected)
  [key: string]: any;          // Extensible for custom config
}

Note: The VITE_INTERNAL_API_URL environment variable is used internally by @quvel-kit/ssr's SSRApiService and is not part of the AppConfig structure exposed to your application. It configures how the SSR server communicates with the backend.

Usage

Service Container

import { createContainer, LogService } from '@quvel-kit/core';

// Create container with services
const container = createContainer(
  undefined, // SSR context (optional)
  new Map([
    ['LogService', LogService],
  ])
);

// Access config (managed by container)
const config = container.config;

// Access services
const log = container.get(LogService);

Using Services in Components

import { useQuvel } from '@quvel-kit/core';

export default defineComponent({
  setup() {
    const { config, log, api, i18n, task } = useQuvel();

    const fetchData = async () => {
      const data = await api.get('/users');
      log.info('Fetched users', { count: data.length });
      return data;
    };

    return { fetchData };
  }
});

TaskService Example

import { useQuvel } from '@quvel-kit/core';

export default defineComponent({
  setup() {
    const { task, api } = useQuvel();

    const loginTask = task({
      task: (credentials) => api.post('/login', credentials),
      showLoading: true,
      showNotification: {
        success: 'Login successful!',
        error: true, // Use default error message
      },
      handleLaravelError: true, // Auto-handle Laravel validation errors
    });

    const login = async () => {
      const result = await loginTask.run();
      if (result) {
        router.push('/dashboard');
      }
    };

    return { login, loginTask };
  }
});

Optional API Debug Interceptors

The core package provides optional debugging interceptors that can be enabled in development. Create a boot file to enable them:

// src/boot/api-debug.ts
import { boot } from 'quasar/wrappers';
import { useQuvel } from '@quvel-kit/core';
import { createDebugInterceptors } from '@quvel-kit/core/utils';

export default boot(({ ssrContext }) => {
  if (import.meta.env.VITE_AXIOS_INTERCEPTORS === 'true') {
    const quvel = useQuvel();
    const interceptors = createDebugInterceptors(quvel.log, quvel.config, {
      onRequest: (config) => {
        // Custom request logging
      },
      onResponse: (response) => {
        // Custom response logging
      }
    });

    quvel.api.instance.interceptors.request.use(
      interceptors.request.onFulfilled,
      interceptors.request.onRejected
    );

    quvel.api.instance.interceptors.response.use(
      interceptors.response.onFulfilled,
      interceptors.response.onRejected
    );
  }
});

Enable in .env:

VITE_AXIOS_INTERCEPTORS=true

Dynamic Asset Loading

import { injectAssets } from '@quvel-kit/core';

// Client-side dynamic asset injection
injectAssets({
  css: [
    {
      url: 'https://cdn.example.com/styles.css',
      priority: 'critical',
      position: 'head',
    }
  ],
  js: [
    {
      url: 'https://cdn.example.com/script.js',
      loading: 'deferred',
      position: 'body-end',
    }
  ]
});

Theme Management

import { useQuvel } from '@quvel-kit/core';

export default defineComponent({
  setup() {
    const { theme } = useQuvel();

    // Get current theme
    const currentTheme = theme.theme; // 'light' | 'dark'

    // Set specific theme
    theme.setTheme('dark');

    // Toggle between light and dark
    theme.toggleTheme();

    return { currentTheme };
  }
});

Theme is automatically loaded from cookies or system preference during SSR boot. The ThemeService manages Quasar Dark mode and Tailwind classes.

I18n with Modules

import { I18nService } from '@quvel-kit/core';
import { getTranslations } from '@quvel-kit/core';
import { modules } from './modules';

// Aggregate translations from modules
const messages = {
  'en-US': getTranslations(modules, 'en-US'),
  'es-ES': getTranslations(modules, 'es-ES')
};

// Set translations before booting container
I18nService.setTranslations(messages);

// In component
const { i18n } = useQuvel();
i18n.t('common.welcome'); // Access translations

WebSocket Subscriptions

import { useWebSockets } from '@quvel-kit/core';

export default defineComponent({
  setup() {
    const { subscribe, unsubscribe } = useWebSockets();

    onMounted(async () => {
      const channel = await subscribe({
        type: 'private',
        channelName: 'user.1',
        events: {
          'notification': (data) => {
            console.log('Notification received:', data);
          }
        }
      });

      onBeforeUnmount(() => {
        unsubscribe(channel);
      });
    });
  }
});

Module System

The module system organizes features into self-contained units. Each module contributes routes, services, translations, and build configuration. Modules compose into applications without tight coupling.

Module Structure

A module is a plain object with optional properties:

export interface Module {
  routes?: RouteRecordRaw[];          // Vue Router routes
  services?: ServiceRegistry;         // Service classes for DI
  i18n?: TranslationRegistry;         // Translations by locale
  build?: ModuleBuildConfig;          // Build-time configuration
}

All properties are optional. Modules implement only what they need.

Creating a Module

Define a module by exporting an object:

// modules/Auth/index.ts
import type { Module } from '@quvel-kit/core';
import { AuthService } from './services/AuthService';

export const AuthModule: Module = {
  routes: [
    {
      path: '/login',
      component: () => import('./pages/LoginPage.vue')
    },
    {
      path: '/register',
      component: () => import('./pages/RegisterPage.vue')
    }
  ],

  services: {
    AuthService: AuthService
  },

  i18n: {
    'en-US': {
      auth: {
        login: 'Login',
        register: 'Register',
        logout: 'Logout'
      }
    },
    'es-ES': {
      auth: {
        login: 'Iniciar sesión',
        register: 'Registrarse',
        logout: 'Cerrar sesión'
      }
    }
  },

  build: {
    boot: ['auth'],
    css: ['auth.css'],
    plugins: ['Notify']
  }
};

Registering Modules

Collect modules in a registry:

// src/modules/index.ts
import { AuthModule } from './Auth';
import { DashboardModule } from './Dashboard';
import { ProfileModule } from './Profile';

export const modules = {
  Auth: AuthModule,
  Dashboard: DashboardModule,
  Profile: ProfileModule
};

Router Integration

Aggregate routes from all modules:

// src/router/routes.ts
import { getRoutes } from '@quvel-kit/core';
import { modules } from '../modules';

const routes = [
  {
    path: '/',
    component: () => import('layouts/MainLayout.vue'),
    children: getRoutes(modules)  // Inject module routes
  }
];

export default routes;

Service Container Integration

Register services from modules:

// src/boot/quvel.ts
import { defineQuvelBoot, getServices } from '@quvel-kit/core';
import { modules } from '../modules';

export default defineQuvelBoot(getServices(modules));

I18n Integration

Load translations from modules:

// src/i18n/index.ts
import { getTranslations } from '@quvel-kit/core';
import { modules } from '../modules';

export default {
  messages: {
    'en-US': getTranslations(modules, 'en-US'),
    'es-ES': getTranslations(modules, 'es-ES')
  }
};

Build Configuration

Apply module build configs in Quasar config:

// quasar.config.ts
import { createModuleTransformer } from '@quvel-kit/core/config/moduleTransformer';
import { modules } from './src/modules';

export default configure((ctx) => {
  let config = {
    boot: [],
    css: [],
    framework: {
      plugins: []
    }
  };

  // Apply module configs
  config = createModuleTransformer(modules)(config, ctx);

  return config;
});

The transformer merges all module build configs into Quasar's configuration automatically.

Module Build Config

Modules declare build-time resources:

build: {
  boot: ['auth', 'permissions'],         // Boot files to load
  css: ['module.css', 'theme.scss'],     // CSS files to include
  animations: ['fadeIn', 'slideUp'],     // Quasar animations
  plugins: ['Notify', 'Dialog']          // Quasar plugins
}

Boot files support client/server targeting:

build: {
  boot: [
    { path: 'auth', server: true, client: true },
    { path: 'analytics', server: false, client: true }
  ]
}

Why Modules?

Without modules:

  • Routes scattered across files
  • Services manually wired together
  • Translations duplicated
  • Build config becomes unwieldy

With modules:

  • Features are self-contained
  • Adding a module is one line
  • Removing a module is one line
  • Zero manual wiring

Architecture

Service Lifecycle

Core uses a three-phase service lifecycle for dependency injection:

  1. Initialize - Service classes are instantiated with config and ssrContext
  2. Register - Services receive the container and store references to dependencies
  3. Boot - Services use their dependencies for initialization (SSR-aware services only)

Important: Config is passed to all services in their constructor, so it's always available. Services like LogService and I18nService self-initialize in their constructors. Other services that initialize during register() may not be ready yet when accessed by other services during their register() phase.

All services complete their register() phase before any boot() phase begins. The boot() phase is where you can safely use ALL dependencies without ordering concerns.

Service Constructor Signature

Services receive config first, then ssrContext:

import type { QSsrContext } from '@quasar/app-vite';
import type { AppConfig } from '@quvel-kit/core';
import { Service } from '@quvel-kit/core';

export class MyService extends Service {
  private readonly config: AppConfig;

  constructor(config: AppConfig, ssrContext?: QSsrContext | null) {
    super(config, ssrContext);
    this.config = config;
    // Config is always available in constructor
  }
}

Register Method Signature

Use destructuring in your register() method for clean dependency access:

import type { ServiceContainer } from '@quvel-kit/core';
import type { RegisterService } from '@quvel-kit/core';

export class MyService extends Service implements RegisterService {
  private api!: ApiService;

  // Use destructuring to access dependencies
  register({ api, config, ssrContext }: ServiceContainer): void {
    this.api = api;
    // config and ssrContext are also available from container
    this.client = createClient(ssrContext, config);
  }
}

Safety Guidelines

// ✅ ALWAYS SAFE: Config is passed in constructor
constructor(config: AppConfig, ssrContext?: QSsrContext | null) {
  super(config, ssrContext);
  this.config = config;  // ✓ Always available
  const debug = this.config.app.debug;  // ✓ Safe to use
}

// ✅ ALWAYS SAFE: Config and Log via container
register({ config, log }: ServiceContainer): void {
  const debug = config.app.debug;  // ✓ Always ready
  log.info('Service registered');  // ✓ Safe to use
}

// ⚠️ ORDERING ISSUE: Using services that initialize during register()
register({ i18n }: ServiceContainer): void {
  this.i18n = i18n;
  // This may fail if your service registers before I18nService!
  const message = this.i18n.t('key');  // Race condition based on order
}

// ✅ CORRECT: Use all dependencies in boot()
boot(): void {
  // All services are fully initialized - safe to use anything
  const locale = this.config.app.locale;
  const message = this.i18n.t('welcome');
  this.api.setHeader('locale', locale);
}

SSR Support

Services can be SSR-aware by implementing the SsrAwareService interface:

import type { QSsrContext } from '@quasar/app-vite';

export class MyService extends Service implements SsrAwareService {
  boot(ssrContext?: QSsrContext | null): void {
    // Access Quasar instance for SSR
    if (ssrContext?.$q) {
      ssrContext.$q.dark.set(true);
    }

    // Access request context
    const config = ssrContext?.req?.requestContext?.appConfig;
  }
}

TypeScript Support

Full TypeScript support with comprehensive type definitions for:

  • Service container and dependency injection
  • Configuration structures
  • API request/response types
  • WebSocket channel types
  • Task options and handlers

License

MIT