Reference DSL
FieldTransform (t.xxx())
Chaque methode retourne une nouvelle instance immutable. Les transforms sont chainables et stockables dans des variables.
Semantique null/undefined : les transforms sautent les valeurs null/undefined sans erreur. Exceptions : t.default() remplace null/undefined, t.require() lance une erreur.
Transforms de valeur
| Methode | Description | Exemple |
|---|---|---|
t.uppercase() | Majuscules | 'martin' -> 'MARTIN' |
t.lowercase() | Minuscules | 'MARTIN' -> 'martin' |
t.trim() | Supprime les espaces | ' foo ' -> 'foo' |
t.padStart(n, char) | Padding a gauche | t.padStart(13, '0') : '123' -> '0000000000123' |
t.replace(regex, str) | Remplacement regex | t.replace(/\s/g, '') : '01 23' -> '0123' |
t.dateFormat('YYYY-MM-DD') | Tronque ISO datetime | '2024-01-15T12:00:00Z' -> '2024-01-15' |
t.coerce('string') | Conversion de type | 42 -> '42' |
t.coerce('number') | Conversion de type | '42' -> 42 |
t.map(mapping) | Mapping de valeurs | t.map({ 'A': 'B' }) : 'A' -> 'B' |
t.edit(fn) | Fonction custom | t.edit(v => v + '_suffix') |
Default et validation
| Methode | Description | Exemple |
|---|---|---|
t.default(value) | Valeur par defaut si null/undefined | t.default('99100').apply(null) -> '99100' |
t.require() | Erreur si null/undefined | t.require().apply(null) -> throw |
Transforms structurels
| Methode | Description |
|---|---|
t.remove() | Supprime la cle du payload |
t.rename(newPath) | Renomme la cle |
Chainage et reutilisation
typescript
// Stocker un transform reutilisable
const cleanName = t.trim().uppercase()
const isoDate = t.dateFormat('YYYY-MM-DD')
// Composer : default puis nettoyage
const safeCode = t.default(' default ').trim().uppercase()
safeCode.apply(null) // 'DEFAULT'
safeCode.apply(' test ') // 'TEST'TransformBuilder (le t dans les callbacks)
Le t passe aux callbacks requestTransform / responseTransform est un TransformBuilder. Il fait double emploi :
- Creer des FieldTransforms :
t.trim(),t.uppercase(), etc. - Accumuler des operations :
t.transform(),t.stripNulls(), etc.
Les operations s'executent dans l'ordre d'appel.
Operations sur un champ
| Methode | Description |
|---|---|
t.transform(path, ft) | Applique un FieldTransform au champ path |
typescript
requestTransform: (t) => {
t.transform('apprenti.nom', t.trim().uppercase())
t.transform('contrat.dateDebut', t.dateFormat('YYYY-MM-DD'))
t.transform('contrat.dureeHeures', t.coerce('string'))
}Operations globales
| Methode | Description |
|---|---|
t.stripNulls() | Supprime toutes les cles dont la valeur est null |
t.mapDeep(key, mapping) | Mappe les valeurs d'une cle dans tout l'arbre |
t.unwrap(key) | Extrait data[key] comme nouveau root |
t.spread(key) | Merge data[key] dans le parent |
t.editData(fn) | Acces complet aux donnees (escape hatch) |
typescript
responseTransform: (t) => {
// Corriger les typos dans tous les champs 'etat'
t.mapDeep('etat', { 'RUTPURE': 'RUPTURE', 'En cours': 'EN_COURS' })
// Normaliser une reponse non paginee
t.editData((data) => {
if (!Array.isArray(data)) return data
return { total: data.length, results: data }
})
}Operations pour routes composites
| Methode | Description |
|---|---|
t.next(path) | Stocke la valeur de path pour l'etape suivante |
t.previous() | FieldTransform qui resout vers la valeur passee par t.next() |
typescript
defineCompositeRoute('/dossiers', 'POST', [
{
dest: '/v1/dossiers',
responseTransform: (t) => { t.next('numeroInterneDossier') },
},
{
dest: '/v1/conventions',
requestTransform: (t) => {
t.transform('numeroDossier', t.previous())
},
},
])defineRoute
typescript
defineRoute(path, method, {
dest: string | DestConfig, // endpoint upstream
destMethod?: 'GET' | 'POST', // override methode upstream
cacheTtl?: number, // TTL cache en secondes
rateLimit?: { maxPerMinute: number },
requestTransform?: (t: TransformBuilder) => void,
responseTransform?: (t: TransformBuilder) => void,
})dest accepte un string (JSON par defaut) ou un objet pour le multipart :
typescript
dest: {
endpoint: '/v1/factures',
format: 'multipart',
multipart: { metadata: 'facture' },
}defineProfile
typescript
defineProfile(id, {
sync?: { batchSize: number },
poll?: { intervalMs: number },
routes: AnyRouteDefinition[],
})Le profil est converti en ProfileAdapter qui implemente OpcoAdapter pour le pipeline.