npm package: @jaiswald159/shared-settings
Description: A lightweight shared settings library using [Pinia](https://pinia.vuejs.org/) for state management, built for Vue 3 / Nuxt 3 applications. Provides a centralized store to manage application settings such as language preferences, themes, and site-specific 
Author: Deepak Jaiswal
Latest version: 2.0.4

# @jaiswald159/shared-settings

A lightweight shared settings library using [Pinia](https://pinia.vuejs.org/) for state management, built for Vue 3 / Nuxt 3 applications. Provides a centralized store to manage application settings such as language preferences, themes, and site-specific configurations.

---

## 📦 Installation

Install the package using npm:

```bash
npm install @jaiswald159/shared-settings
```

---

## 🚀 Basic Usage

Import and use the store in your Vue or Nuxt application:

```ts
import { useSettingsStore } from '@jaiswald159/shared-settings';

// Example of a dummy app store that provides application data
const myAppStore = () => ({
  getApplicationData: (key: string) => {
    if (key === 'loggedSiteID') {
      return 'site-abc-123'; // Replace with dynamic site ID
    }
    return null;
  }
});

// Initialize the settings store
const settingsStore = useSettingsStore(myAppStore);

// Update settings
settingsStore.UPDATE_SETTINGS({ 
  siteID: 'site-abc-123', 
  theme: 'dark', 
  notifications: true 
});

// Get settings for the current site
const currentSettings = settingsStore.GET_SETTINGS();
```

---

## 🎯 Complete Example: Nuxt 3 Implementation

Here's a complete example of how to use the library in a Nuxt 3 application:

### 1. Setup Dependencies

```json
{
  "dependencies": {
    "@jaiswald159/shared-settings": "^1.0.2",
    "@pinia/nuxt": "^0.11.2",
    "pinia": "^2.1.7",
    "pinia-plugin-persistedstate": "^3.2.1"
  }
}
```

### 2. Nuxt Configuration (`nuxt.config.ts`)

```typescript
export default defineNuxtConfig({
  modules: ['@pinia/nuxt'],
  pinia: {
    autoImports: ['defineStore', 'storeToRefs']
  }
})
```

### 3. Pinia Plugin (`plugins/pinia.client.ts`)

```typescript
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

export default defineNuxtPlugin((nuxtApp) => {
  const pinia = nuxtApp.$pinia
  if (pinia) {
    pinia.use(piniaPluginPersistedstate)
  }
})
```

### 4. App Store Helper

```typescript
// composables/useAppStore.ts
export const useAppStore = () => ({
  getApplicationData: (key: string) => {
    if (key === 'loggedSiteID') return 'SITE123'
    return null
  }
})
```

### 5. Main Component (`pages/index.vue`)

```vue
<template>
  <div>
    <h2>Settings Management</h2>
    <div v-if="error" class="error">
      <p>Error: {{ error }}</p>
      <button @click="retry">Retry</button>
    </div>
    <div v-else>
      <pre>{{ currentSettings }}</pre>
      <button @click="saveSettings">Save Settings</button>
      <button @click="loadSettings">Load Settings</button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref, onMounted } from 'vue'
import { useSettingsStore } from '@jaiswald159/shared-settings'

// App store helper
const useAppStore = () => ({
  getApplicationData: (key: string) => {
    if (key === 'loggedSiteID') return 'SITE123'
    return null
  }
})

const error = ref<string | null>(null)
const store = ref<any>(null)

const retry = async () => {
  error.value = null
  await initializeStore()
}

const initializeStore = async () => {
  try {
    if (process.client) {
      // Wait for the app to be fully ready
      await new Promise(resolve => {
        if (document.readyState === 'complete') {
          resolve(undefined)
        } else {
          window.addEventListener('load', resolve)
        }
      })
  
      // Initialize the settings store
      store.value = useSettingsStore(useAppStore)
    }
  } catch (err) {
    console.error('Failed to load shared-settings:', err)
    error.value = 'Failed to load settings library.'
  }
}

const currentSettings = computed(() => {
  if (!store.value) return 'Loading...'
  try {
    return store.value.GET_SETTINGS()
  } catch (err) {
    console.error('Error getting settings:', err)
    return 'Error loading settings'
  }
})

const saveSettings = async () => {
  if (!store.value) {
    error.value = 'Store not initialized'
    return
  }
  
  try {
    store.value.UPDATE_SETTINGS({
      siteID: 'SITE123',
      language: 'fr',
      darkMode: true,
      theme: 'dark',
      notifications: true
    })
    console.log('Settings saved successfully!')
  } catch (err) {
    console.error('Error saving settings:', err)
    error.value = 'Failed to save settings'
  }
}

const loadSettings = () => {
  if (!store.value) {
    error.value = 'Store not initialized'
    return
  }
  
  try {
    const settings = store.value.GET_SETTINGS()
    console.log('Current settings:', settings)
  } catch (err) {
    console.error('Error loading settings:', err)
    error.value = 'Failed to load settings'
  }
}

onMounted(() => {
  initializeStore()
})
</script>

<style scoped>
.error {
  color: red;
  padding: 1rem;
  border: 1px solid red;
  border-radius: 4px;
  margin: 1rem 0;
}

button {
  padding: 0.5rem 1rem;
  margin: 0.5rem;
  border: 1px solid #ccc;
  border-radius: 4px;
  background: #f0f0f0;
  cursor: pointer;
}

button:hover {
  background: #e0e0e0;
}

pre {
  background: #f5f5f5;
  padding: 1rem;
  border-radius: 4px;
  overflow-x: auto;
}
</style>
```

### 6. Advanced Usage with Vue Persistence

For enhanced Vue integration, you can also create a custom implementation using `@vueuse/core`:

```typescript
// composables/useCustomSettingsStore.ts
import { ref, computed } from 'vue'
import { useStorage } from '@vueuse/core'

interface Setting {
  siteID: string
  [key: string]: any
}

interface AppStore {
  getApplicationData: (key: string) => any
}

export const useCustomSettingsStore = (useStore?: () => AppStore) => {
  const siteID = computed(() => (useStore ? useStore().getApplicationData('loggedSiteID') : null))
  
  // Use Vue's persistence state instead of localStorage
  const storage = typeof window !== 'undefined' ? localStorage : undefined
  const settings = useStorage<Setting[]>('activityAttendanceSettings', [], storage)

  const UPDATE_SETTINGS = (data: Setting) => {
    if (!data.siteID) throw new Error('siteID is required')
  
    if (settings.value.length > 0) {
      const index = settings.value.findIndex((item: Setting) => item.siteID === data.siteID)
      if (index !== -1) {
        settings.value[index] = data
      } else {
        settings.value.push(data)
      }
    } else {
      settings.value.push(data)
    }
  }

  const GET_SETTINGS = () => {
    const findSetting = settings.value.find((item: Setting) => item.siteID === siteID.value)
    return findSetting || false
  }

  return {
    settings,
    UPDATE_SETTINGS,
    GET_SETTINGS,
  }
}
```

---

## 📁 File Structure

```
shared-settings/
├── dist/                  # Compiled output files
├── useSettingsStore.ts    # Pinia store definition for shared settings
├── vite.config.ts         # Vite build configuration
├── tsconfig.json          # TypeScript configuration
├── index.ts               # Entry file
├── package.json           # NPM metadata
└── README.md              # Documentation file
```

---

## ⚙️ package.json (CSS integration)

Below is the current `package.json` from this repository and a short explanation of how CSS is included and published with the package.

```json
{
  "name": "@jaiswald159/shared-settings",
  "version": "2.0.1",
  "main": "dist/index.js",
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs",
      "style": "./dist/global.css"
    },
    "./dist/global.css": "./dist/global.css",
    "./global.css": "./global.css"
  },
  "scripts": {
    "build": "vite build",
    "dev": "vite",
    "postbuild": "powershell -Command \"if (Test-Path dist) { Copy-Item -Path global.css -Destination dist -Force }\""
  },
  "files": [
    "dist",
    "global.css"
  ],
  "style": "global.css",
  "dependencies": {
    "pinia": "^2.1.7",
    "pinia-plugin-persistedstate": "^3.2.0",
    "vue": "^3.3.4"
  }
}
```

Why this matters and how CSS is published:

- `exports["."].style` and the top-level `style` field point to the published CSS so bundlers that respect the `style` field can auto-detect the package stylesheet.
- The `postbuild` script ensures `global.css` is copied into `dist/` after `vite build` so the published package contains the CSS alongside compiled JS.
- The `files` array explicitly includes `dist` and `global.css` in the package tarball.

How to consume the CSS in host apps

- Automatic (if your bundler supports `style`): importing the package can make bundlers pick up the `style` field. Example: `import '@jaiswald159/shared-settings'` (behavior depends on the bundler).
- Manual import (recommended and explicit): in your application's entry point (Nuxt/Vite/webpack) import the stylesheet directly:

Nuxt 3 (nuxt.config.ts):

```ts
export default defineNuxtConfig({
  css: ['@jaiswald159/shared-settings/dist/global.css']
})
```

Vite / Vue main (main.ts):

```ts
import '@jaiswald159/shared-settings/dist/global.css'
import { createApp } from 'vue'
import App from './App.vue'
// ...
```

Simple theme toggle helper (use with the settings store)

```ts
// utils/applyThemeFromStore.ts
export function applyThemeFromSettings(store: any) {
  const settings = store.GET_SETTINGS?.() || {}
  // prefer an explicit theme string, fall back to darkMode boolean
  const theme = settings.theme ?? (settings.darkMode ? 'dark' : 'light')
  if (theme === 'dark') {
    document.documentElement.setAttribute('data-theme', 'dark')
  } else {
    document.documentElement.removeAttribute('data-theme')
  }
}
```

Usage example in a component (after store init):

```ts
import { useSettingsStore } from '@jaiswald159/shared-settings'
import { applyThemeFromSettings } from './utils/applyThemeFromStore'

const store = useSettingsStore(appStore)
applyThemeFromSettings(store)

// Optionally watch for changes and reapply
// watch(() => store.GET_SETTINGS(), () => applyThemeFromSettings(store))
```

Notes & compatibility

- The `style` field is widely respected by many bundlers but not an official Node resolution field — for the most predictable result import the stylesheet explicitly as shown above.
- The `postbuild` script uses PowerShell (Windows). If you publish from Unix-based CI, replace it with a portable copy command (or use an npm postbuild JS script) to ensure cross-platform builds.

## 🧱 Features

- ✅ Based on Pinia state management
- ✅ Manages site-specific settings with `siteID`
- ✅ Automatic persistence with `pinia-plugin-persistedstate`
- ✅ Reusable across multiple Vue/Nuxt projects
- ✅ Lightweight and modular
- ✅ Published as a global NPM package
- ✅ TypeScript support
- ✅ Vue 3 / Nuxt 3 compatibility

---

## 📚 API Reference

### Settings Interface

```typescript
interface Setting {
  siteID: string;
  [key: string]: any;
}
```

### App Store Interface

```typescript
interface AppStore {
  getApplicationData: (key: string) => any;
}
```

### Store Methods

- `UPDATE_SETTINGS(data: Setting)`: Save settings with persistence
- `GET_SETTINGS()`: Retrieve settings for current site
- `settings`: Reactive settings array

### Example CSS (Theme integration)

```css
/* Theme variables used by the host app to sync with settings */
:root {
  --color-bg: #ffffff;
  --color-text: #111827;
  --color-accent: #0ea5e9;
}

[data-theme="dark"] {
  --color-bg: #0b1220;
  --color-text: #e6edf3;
  --color-accent: #60a5fa;
}

/* Basic layout utilities that read the theme variables */
.app-root {
  background: var(--color-bg);
  color: var(--color-text);
  transition: background .2s ease, color .2s ease;
}

.btn {
  background: var(--color-accent);
  color: #fff;
  border-radius: 6px;
  padding: .5rem 1rem;
}
```

You can apply the active theme from the store by toggling a `data-theme` attribute on the document element based on the store value (for example, `darkMode` or `theme`).

---

## 🔍 Troubleshooting

### Common Issues

1. **Pinia not initialized**

   - Ensure `@pinia/nuxt` is in modules array
   - Check that Pinia is properly configured
2. **Persistence not working**

   - Verify `pinia-plugin-persistedstate` is installed
   - Check browser console for errors
   - Ensure client-side execution
3. **TypeScript errors**

   - Check TypeScript configuration
   - Verify all dependencies are installed
   - Restart development server

### Debug Steps

1. **Check console logs** for error messages
2. **Verify localStorage** in browser dev tools
3. **Check network tab** for failed requests
4. **Restart development server** if needed

---

## 📤 Build & Publish Guide

1. **Build the package**

   ```bash
   npm run build
   ```
2. **Update the version**

   ```bash
   npm version patch   # Or use minor / major as needed
   ```
3. **Publish to NPM**

   ```bash
   npm publish --access public
   ```

> ✅ Ensure `publishConfig.access` is set to `public` in `package.json` for scoped packages.

---

## 🌐 Usage in Multiple Vue/Nuxt Apps

Once published, this package can be installed in any Vue 3 or Nuxt 3 project and used as a shared module for managing settings across multiple applications or deployments.

---

## 🛠️ Notes

- Ensure you have logged in to NPM (`npm login`) before publishing.
- If you update files, always bump the version before publishing again.
- Remove any `paths` references in `tsconfig.json` that point outside the package root.
- The library automatically handles client-side persistence using `localStorage`.

---

## 📄 License

MIT © [jaiswald159](https://www.npmjs.com/~jaiswald159)