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).
npx -y skills add chawkitariq/fidely --skill coding-standardsAssembled 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
reference/jsdoc-examples.md— exemples JSDoc complets tirés du vrai code du projetreference/typescript-patterns.md— interfaces réelles, patterns TypeScript du projet
1. TypeScript
strict: trueactivé via les configs auto-générées de Nuxt- Zéro
anyimplicite 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ément | Langue |
|---|---|
| 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ément | JSDoc 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 :
useXxxen camelCase — auto-importé par Nuxt - Exporter les interfaces utilisées par plusieurs fichiers depuis le composable
- Guard SSR :
if (import.meta.server) returnavant tout appel àindexedDB,localStorage,document,window - Pas de side-effects au niveau module (pas de
fetchouindexedDB.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 uncomputedou?? - Jamais
definePropsavec l'objet options (style Vue 2) - Jamais de logique conditionnelle complexe dans
{{ }}— utilisercomputed - Emits en
camelCasecôté émission,kebab-casedans 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é]