@slnka/node
v1.0.0-beta.1
Published
SDK TypeScript/JavaScript officiel pour l'API SLNKA
Downloads
125
Maintainers
Readme
@slnka/node
SDK TypeScript officiel pour l'API SLNKA - Plateforme souveraine marocaine de raccourcissement d'URLs.
Table des Matières
- Installation
- Quick Start
- Exemples d'Utilisation
- Configuration Avancée
- Gestion des Erreurs
- Types TypeScript
- Support
Installation
npm
npm install @slnka/nodeyarn
yarn add @slnka/nodepnpm
pnpm add @slnka/nodePrérequis: Node.js 18+ ou navigateur moderne avec support fetch API.
Quick Start
Authentification JWT
Pour les applications frontend avec authentification utilisateur:
import { SlnkaClient } from '@slnka/node';
// 1. Créer le client (style `configure()` partagé par tous les SDK SLNKA)
const client = SlnkaClient.configure({
serverUrl: 'https://your-instance.slnka.ma',
});
// Alternative: builder fluide (idiomatique Java/Kotlin)
// const client = SlnkaClient.builder()
// .serverUrl('https://your-instance.slnka.ma')
// .build();
// 2. Login avec email/password
const authResponse = await client.auth.login({
email: '[email protected]',
password: 'password123'
});
// 3. Le client gère automatiquement le token JWT
// Toutes les requêtes suivantes utilisent le token d'accès
const links = await client.links.list();
console.log('Access Token:', authResponse.accessToken);
console.log('Links:', links.content);Avec Token Refresh Automatique
const client = SlnkaClient.builder()
.serverUrl('https://your-instance.slnka.ma') // Required
.bearerToken(
storedAccessToken, // Token d'accès existant
storedRefreshToken // Refresh token
)
.onTokenRefresh((tokens) => {
// Callback appelé lors du refresh automatique
localStorage.setItem('accessToken', tokens.accessToken);
localStorage.setItem('refreshToken', tokens.refreshToken);
console.log('Tokens refreshed automatically');
})
.build();
// Le SDK refresh automatiquement le token si expiré
const link = await client.links.create({
originalUrl: 'https://example.com'
});Authentification API Key
Pour les applications serveur ou scripts backend:
import { SlnkaClient } from '@slnka/node';
const client = SlnkaClient.configure({
serverUrl: 'https://your-instance.slnka.ma',
apiKey: 'lsk_live_your_api_key_here', // Format: lsk_live_* (production)
});
// Alternative: builder fluide
// const client = SlnkaClient.builder()
// .serverUrl('https://your-instance.slnka.ma')
// .apiKey('lsk_live_your_api_key_here')
// .build();
// Créer un lien raccourci
const link = await client.links.create({
originalUrl: 'https://example.com/very-long-url',
customAlias: 'mon-lien',
tags: ['marketing', 'campaign-2025']
});
console.log('Short URL:', link.shortUrl);
// Output: https://slnka.ma/mon-lienFormat API Keys:
- Production:
lsk_live_* - Test:
lsk_test_*
Exemples d'Utilisation
Gestion des Liens
Créer un lien simple
const link = await client.links.create({
originalUrl: 'https://example.com/long-url'
});
console.log(link.shortUrl); // https://slnka.ma/abc1234
console.log(link.shortCode); // abc1234
console.log(link.originalUrl); // https://example.com/long-urlCréer un lien avec alias personnalisé
const link = await client.links.create({
originalUrl: 'https://example.com/product',
customAlias: 'produit-2025',
tags: ['marketing', 'lancement']
});
console.log(link.shortUrl); // https://slnka.ma/produit-2025Créer un lien avec options avancées
const link = await client.links.create({
originalUrl: 'https://example.com/secure-content',
customAlias: 'secure-doc',
password: 'secret123', // Protection par mot de passe
expiresAt: '2025-12-31T23:59:59Z', // Date d'expiration
tags: ['confidential', 'internal'],
metadata: {
department: 'Finance',
documentId: 'DOC-2025-001'
}
});Lister les liens avec pagination
// Page 1, 20 liens par page
const page1 = await client.links.list(0, 20);
console.log('Total links:', page1.totalElements);
console.log('Total pages:', page1.totalPages);
console.log('Links:', page1.content);
// Parcourir toutes les pages
for (let page = 0; page < page1.totalPages; page++) {
const links = await client.links.list(page, 20);
links.content.forEach(link => {
console.log(`${link.shortCode} -> ${link.originalUrl}`);
});
}Obtenir un lien spécifique
const link = await client.links.get('abc1234');
console.log('Original URL:', link.originalUrl);
console.log('Created:', link.createdAt);
console.log('Clicks:', link.clickCount);
console.log('Tags:', link.tags);Mettre à jour un lien
const updated = await client.links.update('abc1234', {
originalUrl: 'https://new-destination.com',
tagIds: ['tag-id-1', 'tag-id-2']
});Supprimer un lien
await client.links.delete('abc1234');
console.log('Link deleted successfully');Gestion mot de passe
// Définir un mot de passe
await client.links.setPassword('abc1234', 'newpassword123');
// Vérifier un mot de passe
const isValid = await client.links.verifyPassword('abc1234', 'testpassword');
console.log('Password valid:', isValid);
// Supprimer le mot de passe
await client.links.removePassword('abc1234');Statistiques Analytics
Résumé analytics d'un lien
const summary = await client.analytics.getSummary(
'abc1234', // Link ID ou shortCode
'2025-01-01', // Date début
'2025-01-31' // Date fin
);
console.log('Total Clicks:', summary.totalClicks);
console.log('Unique Visitors:', summary.uniqueVisitors);
console.log('Top Countries:', summary.topCountries);
console.log('Top Browsers:', summary.topBrowsers);
console.log('Top Devices:', summary.topDevices);
// Détails par pays
summary.topCountries.forEach(c => {
console.log(`${c.country}: ${c.clicks} clicks`);
});Série temporelle
// Analytics par jour
const dailyStats = await client.analytics.getTimeSeries('abc1234', 'day');
dailyStats.forEach(point => {
console.log(`${point.timestamp}: ${point.clicks} clicks`);
});
// Analytics par heure
const hourlyStats = await client.analytics.getTimeSeries('abc1234', 'hour');Visualisation avec Chart.js
import Chart from 'chart.js/auto';
async function renderClicksChart(shortCode: string) {
// Récupérer les données
const dailyStats = await client.analytics.getTimeSeries(shortCode, 'day');
// Préparer les données pour Chart.js
const labels = dailyStats.map(point => point.timestamp);
const data = dailyStats.map(point => point.clicks);
// Créer le graphique
new Chart(document.getElementById('chart'), {
type: 'line',
data: {
labels,
datasets: [{
label: 'Clicks par jour',
data,
borderColor: '#2563eb',
backgroundColor: 'rgba(37, 99, 235, 0.1)',
tension: 0.4
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: 'Performance du lien'
}
}
}
});
}Statistiques globales du workspace
const globalStats = await client.analytics.getGlobalStats();
console.log('Total Clicks:', globalStats.totalClicks);
console.log('Unique Visitors:', globalStats.uniqueVisitors);
console.log('Top Countries:', globalStats.topCountries);
console.log('Top Browsers:', globalStats.topBrowsers);Génération QR Codes
QR Code basique
const qrCode = await client.qrCode.generate('abc1234', {
size: 300, // Taille en pixels (100-1000)
format: 'png', // 'png' ou 'svg'
fgColor: '#000000', // Couleur QR
bgColor: '#FFFFFF' // Couleur fond
});
// Le QR code est retourné comme données brutes
// Pour PNG: ArrayBuffer, pour SVG: string
console.log('Content type:', qrCode.contentType);QR Code personnalisé avec couleurs de marque
// QR Code avec couleurs personnalisées
const brandedQrCode = await client.qrCode.generate('abc1234', {
size: 500,
format: 'png',
fgColor: '#2563eb', // Bleu de marque
bgColor: '#f0f9ff' // Fond bleu clair
});
// Télécharger le QR code
const downloadQrCode = (base64Data: string, filename: string) => {
const link = document.createElement('a');
link.href = `data:image/png;base64,${base64Data}`;
link.download = filename;
link.click();
};
downloadQrCode(brandedQrCode.data, 'qrcode-abc1234.png');QR Code SVG pour impression haute qualité
// Format SVG pour qualité maximale
const svgQrCode = await client.qrCode.generate('abc1234', {
size: 1000,
format: 'svg',
fgColor: '#000000',
bgColor: '#FFFFFF'
});
// Affichage sécurisé du SVG
const svgBlob = new Blob([atob(svgQrCode.data)], { type: 'image/svg+xml' });
const svgUrl = URL.createObjectURL(svgBlob);
const img = document.createElement('img');
img.src = svgUrl;
img.alt = 'QR Code';
document.getElementById('qr-container').appendChild(img);
// Nettoyer l'URL après utilisation
img.onload = () => URL.revokeObjectURL(svgUrl);Gestion des Workspaces
Lister les workspaces
const workspaces = await client.workspaces.list();
workspaces.forEach(ws => {
console.log(`${ws.name} (${ws.slug})`);
});Créer un workspace
const workspace = await client.workspaces.create({
name: 'Marketing Team',
slug: 'marketing-team'
});
console.log('Workspace ID:', workspace.id);Changer de workspace actif
// Changer le workspace pour toutes les requêtes suivantes
await client.workspaces.switch('workspace-id-123');
// Toutes les opérations utilisent maintenant ce workspace
const links = await client.links.list();Gestion des membres
// Obtenir les membres
const members = await client.workspaces.getMembers('workspace-id');
members.forEach(member => {
console.log(`${member.email}: ${member.role}`);
});
// Inviter un membre
await client.workspaces.inviteMember(
'workspace-id',
'[email protected]',
'EDITOR' // OWNER, ADMIN, EDITOR, VIEWER
);
// Changer le rôle d'un membre
await client.workspaces.updateMemberRole(
'workspace-id',
'member-id',
{ role: 'ADMIN' }
);
// Retirer un membre
await client.workspaces.removeMember('workspace-id', 'member-id');Configuration Avancée
Timeout et Retry
const client = SlnkaClient.builder()
.serverUrl('https://your-instance.slnka.ma') // Required
.apiKey('lsk_live_key')
.timeout(60000) // Timeout 60 secondes (défaut: 30s)
.build();Callbacks personnalisés
const client = SlnkaClient.builder()
.serverUrl('https://your-instance.slnka.ma') // Required
.apiKey('lsk_live_key')
.onTokenRefresh((tokens) => {
// Appelé lors du refresh automatique JWT
console.log('New access token:', tokens.accessToken);
saveTokensToStorage(tokens);
})
.build();Headers personnalisés
const client = SlnkaClient.builder()
.serverUrl('https://your-instance.slnka.ma') // Required
.apiKey('lsk_live_key')
.customHeaders({
'X-Custom-Header': 'value',
'X-Request-ID': generateRequestId()
})
.build();Environnements multiples
// Production
const prodClient = SlnkaClient.builder()
.serverUrl('https://your-instance.slnka.ma') // Required
.apiKey(process.env.SLNKA_PROD_API_KEY)
.build();
// Recette
const recetteClient = SlnkaClient.builder()
.serverUrl('https://api-recette.slnka.ma')
.apiKey(process.env.SLNKA_RECETTE_API_KEY)
.build();
// Development local
const devClient = SlnkaClient.builder()
.serverUrl('http://localhost:8080')
.apiKey('lsk_test_local_dev_key')
.build();Gestion des Erreurs
Le SDK fournit des classes d'erreur typées pour une gestion précise:
Types d'erreurs
import {
SlnkaError,
UnauthorizedError,
NotFoundError,
RateLimitError,
ValidationError,
ConflictError
} from '@slnka/node';
try {
const link = await client.links.create({
originalUrl: 'invalid-url'
});
} catch (error) {
if (error instanceof ValidationError) {
console.error('Données invalides:', error.details);
// Afficher les erreurs de validation
} else if (error instanceof UnauthorizedError) {
console.error('Non authentifié - rediriger vers login');
redirectToLogin();
} else if (error instanceof NotFoundError) {
console.error('Ressource non trouvée');
} else if (error instanceof ConflictError) {
console.error('Conflit - alias déjà utilisé');
} else if (error instanceof RateLimitError) {
console.error(`Rate limit - réessayer dans ${error.details?.retryAfter}s`);
setTimeout(() => retry(), error.details.retryAfter * 1000);
} else if (error instanceof SlnkaError) {
console.error(`Erreur API: ${error.code} - ${error.message}`);
} else {
console.error('Erreur inattendue:', error);
}
}Codes d'erreur courants
| Code | Type | Description |
|------|------|-------------|
| 400 | ValidationError | Données de requête invalides |
| 401 | UnauthorizedError | Non authentifié |
| 403 | ForbiddenError | Permission refusée |
| 404 | NotFoundError | Ressource non trouvée |
| 409 | ConflictError | Conflit (alias déjà utilisé) |
| 429 | RateLimitError | Rate limit dépassé |
| 500 | SlnkaError | Erreur serveur |
Détails d'erreur
try {
await client.links.create({ originalUrl: 'invalid' });
} catch (error) {
if (error instanceof ValidationError) {
console.log('Message:', error.message);
console.log('Code:', error.code);
console.log('Détails:', error.details);
// Détails contient les erreurs de validation par champ
// {
// originalUrl: ['Must be a valid URL'],
// customAlias: ['Already in use']
// }
}
}Types TypeScript
Le SDK est entièrement typé TypeScript. Tous les types sont exportés:
import type {
// Responses
LinkResponse,
AuthResponse,
WorkspaceResponse,
AnalyticsSummary,
QRCodeResponse,
UserProfile,
// Requests
CreateLinkRequest,
UpdateLinkRequest,
LoginRequest,
RegisterRequest,
CreateWorkspaceRequest,
UpdateMemberRoleRequest,
// Pagination
PaginatedLinks,
// Analytics
TimeSeriesPoint,
AnalyticsBreakdownItem,
TopLinkItem,
// Workspace
WorkspaceMember,
QuotaUsageResponse
} from '@slnka/node';Exemple d'utilisation des types
import type { CreateLinkRequest, LinkResponse } from '@slnka/node';
async function createMarketingLink(
url: string,
campaign: string
): Promise<LinkResponse> {
const request: CreateLinkRequest = {
originalUrl: url,
tags: ['marketing', campaign],
metadata: {
campaign,
createdBy: 'marketing-automation'
}
};
return await client.links.create(request);
}
// TypeScript infère automatiquement le type de retour
const link = await createMarketingLink(
'https://example.com/promo',
'summer-2025'
);
console.log(link.shortUrl); // Type: string
console.log(link.clickCount); // Type: numberSupport
Documentation
- Documentation API: https://docs.slnka.ma
- Guide d'intégration: https://docs.slnka.ma/sdk/typescript
- Exemples: https://github.com/slnka/sdk-typescript/examples
Assistance
- GitHub Issues: https://github.com/slnka/sdk-typescript/issues
- Email Support: [email protected]
- Status Page: https://status.slnka.ma
Communauté
- Discord: https://discord.gg/slnka
- Twitter: @SLNKA
License
MIT © SLNKA
Changelog
v1.0.0 (2025-01-19)
- ✨ Initial release
- 🔐 JWT authentication avec refresh automatique
- 🔑 API Key authentication
- 🔗 API Links complète (CRUD)
- 📊 Analytics avec séries temporelles
- 📱 QR Code generation
- 🏢 Workspace management
- 🛡️ TypeScript types complets
- ⚡ Error handling typé
- 🎯 Rate limiting avec retry automatique
