agentsclimarketplace

Coding standards

Skill chawkitariq/fidely/.claude/skills/coding-standards

PWA de fidélité pour commerces, 100% offline, sans backend, sans compte. Deux modes : Commerçant (gestion des cartes et scan client) et Client (QR code personnel).

Install
npx -y skills add chawkitariq/fidely --skill coding-standards

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Standards de codage à appliquer sur tout code écrit ou modifié dans Fidely. Déclencher quand : écriture ou revue de TypeScript/Vue, création de composable (useDB, useClient), ajout de fonction dans utils/, revue de types (Card, ClientData, CardFormData), JSDoc manquant. Mots-clés : TypeScript strict, any, import type, JSDoc, @param, @returns, <script setup>, defineProps, defineEmits, computed, uuidv7, zod, eslint, interface Card, interface ClientData.

SKILL.md

10.5 KB, as published. Nobody here has run it

Skill : Standards de codage Fidely

Ces règles s'appliquent à tout code écrit ou modifié, sans exception.

Ressources disponibles


1. TypeScript

  • strict: true activé via les configs auto-générées de Nuxt
  • Zéro any implicite ou explicite — typer toutes les interfaces
  • Exporter les interfaces depuis les composables qui les définissent :
    // app/composables/useDB.ts — interfaces réelles du projet
    export interface Card {
      id: string           // UUID v7
      title: string
      reward_points: number
      reward: string
      created_at: number   // timestamp ms
    }
    
    export interface ClientData {
      cards: Record<string, number>  // cardId → points
      last_seen: number              // timestamp ms
    }
    
  • Utiliser import type { ... } pour les imports de types seuls
  • Typer explicitement les retours de fonctions async :
    async function getAllCards(): Promise<Card[]> { ... }
    async function updatePoints(clientId: string, cardId: string, delta: number): Promise<number> { ... }
    async function addCard(data: Omit<Card, 'id' | 'created_at'>): Promise<Card> { ... }
    

2. ESLint & formatage

  • Pas de virgule finale (commaDangle: 'never')
  • Accolades style 1tbs (braceStyle: '1tbs')
  • Indentation 2 espaces (.editorconfig)
  • Pas de point-virgule superflu (defaults Nuxt ESLint)
  • Vérification obligatoire avant commit :
    pnpm lint       # 0 warning
    pnpm typecheck  # 0 erreur
    

3. Langue du code — anglais obligatoire

Tout le code est écrit en anglais, sans exception.

ÉlémentLangue
Noms de variables, fonctions, interfaces✅ Anglais
Commentaires inline (// ...)✅ Anglais
JSDoc (/** ... */)✅ Anglais
Messages d'erreur lancés par throw✅ Anglais
Textes UI affichés à l'utilisateur (labels, toasts, placeholders)🇫🇷 Français
// ❌ Ne pas faire
/** Retourne tous les points du client pour cette carte */
const current = client.cards[cardId] ?? 0 // points actuels

// ✅ Correct
/** Returns the client's current points for a given card. */
const current = client.cards[cardId] ?? 0 // current point balance

4. JSDoc — obligatoire

Toute fonction publique, composable, et interface exportée doit avoir un JSDoc. JSDoc rédigé en anglais (voir section 3).

Fonctions et composables

/**
 * Opens the `fidely` IndexedDB database (version 2) and returns the instance.
 * Creates the `cards`, `clients`, and `config` object stores if needed.
 * Result is cached — multiple calls return the same promise.
 *
 * @returns Promise resolved with the IDBDatabase instance
 */
async function openDB(): Promise<IDBDatabase> { ... }

/**
 * Adds or removes points for a client on a given card.
 * Implicitly creates the client if they don't exist yet.
 * Guarantees the balance never drops below 0 (Math.max).
 *
 * @param clientId - Client UUID (stored in IndexedDB config store)
 * @param cardId   - Loyalty card UUID
 * @param delta    - Points to add (positive) or remove (negative)
 * @returns New point balance for this client/card pair
 */
async function updatePoints(clientId: string, cardId: string, delta: number): Promise<number> { ... }

/**
 * Redeems a reward by deducting `rewardPoints` from the client's balance.
 * Preserves any surplus (e.g. 8 pts, threshold 6 → 2 pts remaining).
 *
 * @param clientId     - Client UUID
 * @param cardId       - Loyalty card UUID
 * @param rewardPoints - Points to deduct (= card.reward_points)
 * @returns Remaining balance after deduction
 * @throws Error 'Not enough points' if balance < rewardPoints
 */
async function consumeReward(clientId: string, cardId: string, rewardPoints: number): Promise<number> { ... }

Interfaces et types exportés

/**
 * Loyalty card created by a merchant.
 * Persisted in IndexedDB object store `cards` (keyPath: 'id').
 */
export interface Card {
  /** UUID v7 generated by uuidv7() */
  id: string
  /** Display name of the card (e.g. "Café du Coin") */
  title: string
  /** Number of points required to unlock the reward */
  reward_points: number
  /** Reward description (e.g. "1 café offert") */
  reward: string
  /** Creation timestamp in milliseconds */
  created_at: number
}

/**
 * Client loyalty data across all cards.
 * Persisted in IndexedDB object store `clients` (key = clientId UUID).
 */
export interface ClientData {
  /** Map of cardId → accumulated points */
  cards: Record<string, number>
  /** Last update timestamp in ms */
  last_seen: number
}

Composables

/**
 * Main composable for all Fidely IndexedDB operations.
 * Single entry point — never call `indexedDB` directly from a page or component.
 *
 * Object stores: `cards` (loyalty cards), `clients` (points per client),
 * `config` (app settings, including client_id).
 *
 * @example
 * const { getAllCards, addCard, updatePoints } = useDB()
 * const cards = await getAllCards()
 */
export function useDB() { ... }

/**
 * Manages the unique client identifier (stable UUID, generated once).
 * Stored in IndexedDB object store `config` under the key `client_id`.
 *
 * @example
 * const { getOrCreateClientId } = useClient()
 * const id = await getOrCreateClientId() // same value on every call
 */
export function useClient() { ... }

Règles JSDoc

ÉlémentJSDoc requis
Fonction exportée depuis un composable✅ Oui
Interface / type exporté✅ Oui
Composable lui-même (export function useXxx)✅ Oui
Fonction interne locale non exportée > 10 lignes✅ Oui
Fonction utilitaire dans utils/✅ Oui
Getter/computed trivial (computed(() => props.id))❌ Non
Handler inline évident (function closeModal())❌ Non

5. Structure d'un composable

/**
 * [Description du composable et de sa responsabilité]
 *
 * @example
 * const { items, loadItems } = useFeature()
 */
export function useFeature() {
  const items = ref<Item[]>([])

  /**
   * Charge les items depuis IndexedDB.
   * Ne fait rien côté serveur (SSR guard).
   */
  async function loadItems(): Promise<void> {
    if (import.meta.server) return   // guard SSR obligatoire si browser API
    // logique
  }

  return { items, loadItems }
}

Règles :

  • Nom : useXxx en camelCase — auto-importé par Nuxt
  • Exporter les interfaces utilisées par plusieurs fichiers depuis le composable
  • Guard SSR : if (import.meta.server) return avant tout appel à indexedDB, localStorage, document, window
  • Pas de side-effects au niveau module (pas de fetch ou indexedDB.open() au top-level)
  • useDB() est le seul point d'entrée pour IndexedDB

6. Structure d'un composant Vue

Ordre obligatoire dans <script setup lang="ts"> :

<script setup lang="ts">
// 1. imports de types uniquement
import type { Card } from '~/composables/useDB'

// 2. props — générics, jamais options object
const props = defineProps<{
  card: Card
  loading?: boolean
}>()

// 3. emits — tuple syntax
const emit = defineEmits<{
  saved: [card: Card]
  cancelled: []
}>()

// 4. composables Nuxt (auto-importés)
const toast = useToast()

// 5. state local
const isOpen = ref(false)

// 6. computed — jamais de logique dans le template
const label = computed(() => props.loading ? 'Chargement…' : 'Enregistrer')

// 7. fonctions (async avec try/catch + feedback toast)
async function handleSave(): Promise<void> {
  // logique
}
</script>

Règles :

  • <script setup lang="ts"> toujours en premier, avant <template>
  • Props optionnelles avec ? — valeur par défaut dans un computed ou ??
  • Jamais defineProps avec l'objet options (style Vue 2)
  • Jamais de logique conditionnelle complexe dans {{ }} — utiliser computed
  • Emits en camelCase côté émission, kebab-case dans le parent (@points-changed)

7. UUID — utiliser uuidv7 exclusivement

// app/utils/uuid.ts — helper du projet
import { uuidv7 } from 'uuidv7'

export function generateUUID(): string {
  return uuidv7()
}
// ❌ Ne pas utiliser crypto.randomUUID()
const id = crypto.randomUUID()

// ✅ Utiliser generateUUID() du projet
const { generateUUID } = await import('~/utils/uuid')
const id = generateUUID()

Pourquoi uuidv7 ? UUID v7 sont triés chronologiquement → meilleure indexation IndexedDB et débogage facilité.


8. Auto-imports Nuxt — ne jamais importer manuellement

// ❌ Inutile
import { ref, computed, onMounted } from 'vue'
import { useRoute, navigateTo } from '#app'

// ✅ Correct — juste utiliser directement
const route = useRoute()
const count = ref(0)

Auto-importés : ref, computed, reactive, watch, watchEffect, onMounted, onBeforeUnmount, useRoute, useRouter, navigateTo, useToast, useHead, useSeoMeta, et tous les composables/utils du dossier app/


9. Checklist avant de marquer une tâche ✅

pnpm typecheck   # 0 erreur TypeScript
pnpm lint        # 0 warning ESLint
pnpm vitest run  # 0 test en échec
[ ] Code et JSDoc rédigés en anglais (textes UI en français)
[ ] Aucun `any` explicite ou implicite
[ ] JSDoc présent sur toutes les fonctions/interfaces exportées
[ ] Guards SSR en place sur toutes les browser APIs
[ ] Retours de fonctions async explicitement typés
[ ] Ordre des blocs dans <script setup> respecté
[ ] Props/emits avec générics (pas options object)
[ ] Jamais de logique dans {{ }} — utiliser computed
[ ] uuidv7() utilisé via generateUUID() — pas crypto.randomUUID()

Confirmation en fin de tâche

Après application de ces standards, confirmer :

coding-standards appliqués

  • TypeScript : [0 any / types vérifiés]
  • JSDoc : [fonctions/interfaces documentées]
  • ESLint : [pnpm lint → 0 warning]
  • Structure Vue : [ordre des blocs respecté]

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.