@starterlib/util 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -0
- package/fesm2022/starterlib-util.mjs +225 -0
- package/fesm2022/starterlib-util.mjs.map +1 -0
- package/package.json +36 -0
- package/types/starterlib-util.d.ts +83 -0
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Util
|
|
2
|
+
|
|
3
|
+
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 22.1.0.
|
|
4
|
+
|
|
5
|
+
## Code scaffolding
|
|
6
|
+
|
|
7
|
+
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
ng generate component component-name
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
ng generate --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Building
|
|
20
|
+
|
|
21
|
+
To build the library, run:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
ng build util
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
|
|
28
|
+
|
|
29
|
+
### Publishing the Library
|
|
30
|
+
|
|
31
|
+
Once the project is built, you can publish your library by following these steps:
|
|
32
|
+
|
|
33
|
+
1. Navigate to the `dist` directory:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
cd dist/util
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
2. Run the `npm publish` command to publish your library to the npm registry:
|
|
40
|
+
```bash
|
|
41
|
+
npm publish
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Running unit tests
|
|
45
|
+
|
|
46
|
+
To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
ng test
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Running end-to-end tests
|
|
53
|
+
|
|
54
|
+
For end-to-end (e2e) testing, run:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
ng e2e
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
|
61
|
+
|
|
62
|
+
## Additional Resources
|
|
63
|
+
|
|
64
|
+
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatage monetaire. Il vit dans le socle et non dans chaque SPA parce
|
|
3
|
+
* qu'une somme mal arrondie ou mal signee est un defaut bancaire, pas un
|
|
4
|
+
* defaut d'affichage.
|
|
5
|
+
*/
|
|
6
|
+
/** Devises a zero decimale : le yen n'a pas de centime. */
|
|
7
|
+
const SANS_DECIMALE = new Set(['JPY', 'KRW', 'VND', 'XAF', 'XOF', 'CLP', 'ISK']);
|
|
8
|
+
/** Devises a trois decimales. */
|
|
9
|
+
const TROIS_DECIMALES = new Set(['BHD', 'IQD', 'JOD', 'KWD', 'LYD', 'OMR', 'TND']);
|
|
10
|
+
function decimalesDe(devise) {
|
|
11
|
+
const code = devise.toUpperCase();
|
|
12
|
+
if (SANS_DECIMALE.has(code))
|
|
13
|
+
return 0;
|
|
14
|
+
if (TROIS_DECIMALES.has(code))
|
|
15
|
+
return 3;
|
|
16
|
+
return 2;
|
|
17
|
+
}
|
|
18
|
+
function montant(unitesMineures, devise) {
|
|
19
|
+
if (!/^[A-Za-z]{3}$/.test(devise)) {
|
|
20
|
+
throw new Error(`Code devise invalide : ${devise}`);
|
|
21
|
+
}
|
|
22
|
+
if (typeof unitesMineures === 'number' && !Number.isInteger(unitesMineures)) {
|
|
23
|
+
throw new Error(`Un montant se compte en unites mineures entieres, recu : ${String(unitesMineures)}`);
|
|
24
|
+
}
|
|
25
|
+
return { unitesMineures: BigInt(unitesMineures), devise: devise.toUpperCase() };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Rend un montant dans la locale demandee. La conversion en nombre n'a lieu
|
|
29
|
+
* qu'a l'instant du rendu, sur une valeur bornee par la precision d'affichage :
|
|
30
|
+
* le calcul, lui, n'a jamais quitte l'entier.
|
|
31
|
+
*/
|
|
32
|
+
function formaterMontant(valeur, options = {}) {
|
|
33
|
+
const { locale = 'fr-FR', signeExplicite = false, sansDevise = false } = options;
|
|
34
|
+
const decimales = decimalesDe(valeur.devise);
|
|
35
|
+
const negatif = valeur.unitesMineures < 0n;
|
|
36
|
+
const absolu = negatif ? -valeur.unitesMineures : valeur.unitesMineures;
|
|
37
|
+
const diviseur = 10n ** BigInt(decimales);
|
|
38
|
+
const entiere = absolu / diviseur;
|
|
39
|
+
const fraction = absolu % diviseur;
|
|
40
|
+
const formateur = new Intl.NumberFormat(locale, {
|
|
41
|
+
style: sansDevise ? 'decimal' : 'currency',
|
|
42
|
+
currency: valeur.devise,
|
|
43
|
+
minimumFractionDigits: decimales,
|
|
44
|
+
maximumFractionDigits: decimales,
|
|
45
|
+
signDisplay: 'never',
|
|
46
|
+
});
|
|
47
|
+
const nombre = decimales === 0
|
|
48
|
+
? Number(entiere)
|
|
49
|
+
: Number(`${entiere.toString()}.${fraction.toString().padStart(decimales, '0')}`);
|
|
50
|
+
const rendu = formateur.format(nombre);
|
|
51
|
+
if (negatif)
|
|
52
|
+
return `−${rendu}`;
|
|
53
|
+
return signeExplicite && valeur.unitesMineures > 0n ? `+${rendu}` : rendu;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* IBAN : validation par la cle ISO 7064 mod 97-10 et presentation par groupes
|
|
58
|
+
* de quatre. La validation vit ici pour qu'un champ de saisie refuse une
|
|
59
|
+
* frappe fautive sans aller-retour serveur ; elle ne remplace jamais le
|
|
60
|
+
* controle du backend.
|
|
61
|
+
*/
|
|
62
|
+
const LONGUEURS = {
|
|
63
|
+
FR: 27,
|
|
64
|
+
DE: 22,
|
|
65
|
+
ES: 24,
|
|
66
|
+
IT: 27,
|
|
67
|
+
BE: 16,
|
|
68
|
+
PT: 25,
|
|
69
|
+
NL: 18,
|
|
70
|
+
LU: 20,
|
|
71
|
+
CH: 21,
|
|
72
|
+
GB: 22,
|
|
73
|
+
IE: 22,
|
|
74
|
+
AT: 20,
|
|
75
|
+
PL: 28,
|
|
76
|
+
SE: 24,
|
|
77
|
+
NO: 15,
|
|
78
|
+
DK: 18,
|
|
79
|
+
FI: 18,
|
|
80
|
+
CZ: 24,
|
|
81
|
+
GR: 27,
|
|
82
|
+
RO: 24,
|
|
83
|
+
BG: 22,
|
|
84
|
+
HR: 21,
|
|
85
|
+
HU: 28,
|
|
86
|
+
SK: 24,
|
|
87
|
+
SI: 19,
|
|
88
|
+
LT: 20,
|
|
89
|
+
LV: 21,
|
|
90
|
+
EE: 20,
|
|
91
|
+
MT: 31,
|
|
92
|
+
CY: 28,
|
|
93
|
+
MC: 27,
|
|
94
|
+
SM: 27,
|
|
95
|
+
};
|
|
96
|
+
function normaliserIban(saisie) {
|
|
97
|
+
return saisie.replace(/[\s-]/g, '').toUpperCase();
|
|
98
|
+
}
|
|
99
|
+
/** Modulo 97 sur la chaine, chiffre par chiffre : un IBAN depasse la
|
|
100
|
+
* precision d'un nombre JavaScript. */
|
|
101
|
+
function mod97(chaine) {
|
|
102
|
+
let reste = 0;
|
|
103
|
+
for (const caractere of chaine) {
|
|
104
|
+
reste = (reste * 10 + Number(caractere)) % 97;
|
|
105
|
+
}
|
|
106
|
+
return reste;
|
|
107
|
+
}
|
|
108
|
+
function ibanEstValide(saisie) {
|
|
109
|
+
const iban = normaliserIban(saisie);
|
|
110
|
+
if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{10,30}$/.test(iban))
|
|
111
|
+
return false;
|
|
112
|
+
const pays = iban.slice(0, 2);
|
|
113
|
+
const longueurAttendue = LONGUEURS[pays];
|
|
114
|
+
if (longueurAttendue !== undefined && iban.length !== longueurAttendue)
|
|
115
|
+
return false;
|
|
116
|
+
const permute = iban.slice(4) + iban.slice(0, 4);
|
|
117
|
+
const numerique = permute.replace(/[A-Z]/g, (l) => (l.charCodeAt(0) - 55).toString());
|
|
118
|
+
return mod97(numerique) === 1;
|
|
119
|
+
}
|
|
120
|
+
/** Presentation par groupes de quatre, la seule lisible a l'oeil. */
|
|
121
|
+
function formaterIban(saisie) {
|
|
122
|
+
return normaliserIban(saisie)
|
|
123
|
+
.replace(/(.{4})/g, '$1 ')
|
|
124
|
+
.trim();
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Masque tout sauf le pays et les quatre derniers caracteres. Un IBAN complet
|
|
128
|
+
* ne s'affiche jamais dans une liste : il suffit a initier un prelevement.
|
|
129
|
+
*/
|
|
130
|
+
function masquerIban(saisie) {
|
|
131
|
+
const iban = normaliserIban(saisie);
|
|
132
|
+
if (iban.length < 8)
|
|
133
|
+
return '•'.repeat(iban.length);
|
|
134
|
+
const tete = iban.slice(0, 4);
|
|
135
|
+
const queue = iban.slice(-4);
|
|
136
|
+
return formaterIban(tete + '•'.repeat(iban.length - 8) + queue);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Masques d'affichage des identifiants sensibles.
|
|
141
|
+
*
|
|
142
|
+
* Regle commune : on garde de quoi RECONNAITRE, jamais de quoi UTILISER. Un
|
|
143
|
+
* numero complet affiche dans une liste suffit a initier une operation ; les
|
|
144
|
+
* quatre derniers caracteres suffisent a l'utilisateur pour savoir de quel
|
|
145
|
+
* moyen il s'agit.
|
|
146
|
+
*/
|
|
147
|
+
/** Groupe par quatre, la seule presentation lisible a l'oeil. */
|
|
148
|
+
function grouperParQuatre(valeur) {
|
|
149
|
+
return valeur.replace(/(.{4})/g, '$1 ').trim();
|
|
150
|
+
}
|
|
151
|
+
function normaliserCarte(saisie) {
|
|
152
|
+
return saisie.replace(/[\s-]/g, '');
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Ne laisse voir que les quatre derniers chiffres. Le reseau (les premiers
|
|
156
|
+
* chiffres) est volontairement masque : il identifie l'emetteur.
|
|
157
|
+
*/
|
|
158
|
+
function masquerCarte(saisie) {
|
|
159
|
+
const carte = normaliserCarte(saisie);
|
|
160
|
+
if (carte.length <= 4)
|
|
161
|
+
return '•'.repeat(carte.length);
|
|
162
|
+
return grouperParQuatre('•'.repeat(carte.length - 4) + carte.slice(-4));
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Validation de carte par la cle de Luhn. Elle sert a refuser une frappe
|
|
166
|
+
* fautive sans aller-retour serveur ; elle ne prouve rien sur l'existence du
|
|
167
|
+
* moyen de paiement.
|
|
168
|
+
*/
|
|
169
|
+
function carteEstValide(saisie) {
|
|
170
|
+
const carte = normaliserCarte(saisie);
|
|
171
|
+
if (!/^[0-9]{12,19}$/.test(carte))
|
|
172
|
+
return false;
|
|
173
|
+
let somme = 0;
|
|
174
|
+
let double = false;
|
|
175
|
+
for (let index = carte.length - 1; index >= 0; index--) {
|
|
176
|
+
let chiffre = Number(carte[index]);
|
|
177
|
+
if (double) {
|
|
178
|
+
chiffre *= 2;
|
|
179
|
+
if (chiffre > 9)
|
|
180
|
+
chiffre -= 9;
|
|
181
|
+
}
|
|
182
|
+
somme += chiffre;
|
|
183
|
+
double = !double;
|
|
184
|
+
}
|
|
185
|
+
return somme % 10 === 0;
|
|
186
|
+
}
|
|
187
|
+
function normaliserTelephone(saisie) {
|
|
188
|
+
return saisie.replace(/[\s.\-()]/g, '');
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Garde l'indicatif international et les deux derniers chiffres. Un numero
|
|
192
|
+
* entier permet d'usurper, de harceler, ou de tenter une prise de controle
|
|
193
|
+
* par message.
|
|
194
|
+
*/
|
|
195
|
+
function masquerTelephone(saisie) {
|
|
196
|
+
const numero = normaliserTelephone(saisie);
|
|
197
|
+
const indicatif = numero.startsWith('+') ? numero.slice(0, 3) : '';
|
|
198
|
+
const reste = numero.slice(indicatif.length);
|
|
199
|
+
if (reste.length <= 2)
|
|
200
|
+
return indicatif + '•'.repeat(reste.length);
|
|
201
|
+
return `${indicatif}${'•'.repeat(reste.length - 2)}${reste.slice(-2)}`;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Fin lisible d'un identifiant, pour composer un nom accessible : une suite de
|
|
205
|
+
* puces est epelee caractere par caractere par plusieurs lecteurs d'ecran.
|
|
206
|
+
*/
|
|
207
|
+
function quatreDerniers(saisie) {
|
|
208
|
+
const valeur = saisie.replace(/[\s-]/g, '');
|
|
209
|
+
return valeur.slice(-4);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @starterlib/util — fonctions pures partagees.
|
|
214
|
+
*
|
|
215
|
+
* Regle du paquet : aucune dependance Angular, aucun effet de bord, aucune
|
|
216
|
+
* lecture d'horloge ni de `window`. Tout ce qui vit ici doit se tester sans
|
|
217
|
+
* banc d'essai Angular.
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Generated bundle index. Do not edit.
|
|
222
|
+
*/
|
|
223
|
+
|
|
224
|
+
export { carteEstValide, decimalesDe, formaterIban, formaterMontant, ibanEstValide, masquerCarte, masquerIban, masquerTelephone, montant, normaliserCarte, normaliserIban, normaliserTelephone, quatreDerniers };
|
|
225
|
+
//# sourceMappingURL=starterlib-util.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starterlib-util.mjs","sources":["../../../../packages/util/src/lib/montant.ts","../../../../packages/util/src/lib/iban.ts","../../../../packages/util/src/lib/masque.ts","../../../../packages/util/src/public-api.ts","../../../../packages/util/src/starterlib-util.ts"],"sourcesContent":["/**\n * Formatage monetaire. Il vit dans le socle et non dans chaque SPA parce\n * qu'une somme mal arrondie ou mal signee est un defaut bancaire, pas un\n * defaut d'affichage.\n */\n\n/**\n * Un montant ne se represente JAMAIS par un nombre a virgule flottante :\n * 0.1 + 0.2 ne fait pas 0.3. On transporte des unites mineures entieres\n * (centimes) et la devise qui leur donne leur echelle.\n */\nexport interface Montant {\n /** Unites mineures. 12,34 EUR s'ecrit 1234. */\n readonly unitesMineures: bigint;\n /** Code ISO 4217, trois lettres majuscules. */\n readonly devise: string;\n}\n\n/** Devises a zero decimale : le yen n'a pas de centime. */\nconst SANS_DECIMALE = new Set(['JPY', 'KRW', 'VND', 'XAF', 'XOF', 'CLP', 'ISK']);\n/** Devises a trois decimales. */\nconst TROIS_DECIMALES = new Set(['BHD', 'IQD', 'JOD', 'KWD', 'LYD', 'OMR', 'TND']);\n\nexport function decimalesDe(devise: string): number {\n const code = devise.toUpperCase();\n if (SANS_DECIMALE.has(code)) return 0;\n if (TROIS_DECIMALES.has(code)) return 3;\n return 2;\n}\n\nexport function montant(unitesMineures: bigint | number, devise: string): Montant {\n if (!/^[A-Za-z]{3}$/.test(devise)) {\n throw new Error(`Code devise invalide : ${devise}`);\n }\n if (typeof unitesMineures === 'number' && !Number.isInteger(unitesMineures)) {\n throw new Error(\n `Un montant se compte en unites mineures entieres, recu : ${String(unitesMineures)}`,\n );\n }\n return { unitesMineures: BigInt(unitesMineures), devise: devise.toUpperCase() };\n}\n\nexport interface OptionsDeFormatage {\n readonly locale?: string;\n /** Affiche `+` devant un montant positif. Utile sur un releve. */\n readonly signeExplicite?: boolean;\n /** Masque le symbole de devise (colonnes de tableau deja titrees). */\n readonly sansDevise?: boolean;\n}\n\n/**\n * Rend un montant dans la locale demandee. La conversion en nombre n'a lieu\n * qu'a l'instant du rendu, sur une valeur bornee par la precision d'affichage :\n * le calcul, lui, n'a jamais quitte l'entier.\n */\nexport function formaterMontant(valeur: Montant, options: OptionsDeFormatage = {}): string {\n const { locale = 'fr-FR', signeExplicite = false, sansDevise = false } = options;\n const decimales = decimalesDe(valeur.devise);\n const negatif = valeur.unitesMineures < 0n;\n const absolu = negatif ? -valeur.unitesMineures : valeur.unitesMineures;\n const diviseur = 10n ** BigInt(decimales);\n const entiere = absolu / diviseur;\n const fraction = absolu % diviseur;\n\n const formateur = new Intl.NumberFormat(locale, {\n style: sansDevise ? 'decimal' : 'currency',\n currency: valeur.devise,\n minimumFractionDigits: decimales,\n maximumFractionDigits: decimales,\n signDisplay: 'never',\n });\n\n const nombre =\n decimales === 0\n ? Number(entiere)\n : Number(`${entiere.toString()}.${fraction.toString().padStart(decimales, '0')}`);\n\n const rendu = formateur.format(nombre);\n if (negatif) return `−${rendu}`;\n return signeExplicite && valeur.unitesMineures > 0n ? `+${rendu}` : rendu;\n}\n","/**\n * IBAN : validation par la cle ISO 7064 mod 97-10 et presentation par groupes\n * de quatre. La validation vit ici pour qu'un champ de saisie refuse une\n * frappe fautive sans aller-retour serveur ; elle ne remplace jamais le\n * controle du backend.\n */\n\nconst LONGUEURS: Readonly<Record<string, number>> = {\n FR: 27,\n DE: 22,\n ES: 24,\n IT: 27,\n BE: 16,\n PT: 25,\n NL: 18,\n LU: 20,\n CH: 21,\n GB: 22,\n IE: 22,\n AT: 20,\n PL: 28,\n SE: 24,\n NO: 15,\n DK: 18,\n FI: 18,\n CZ: 24,\n GR: 27,\n RO: 24,\n BG: 22,\n HR: 21,\n HU: 28,\n SK: 24,\n SI: 19,\n LT: 20,\n LV: 21,\n EE: 20,\n MT: 31,\n CY: 28,\n MC: 27,\n SM: 27,\n};\n\nexport function normaliserIban(saisie: string): string {\n return saisie.replace(/[\\s-]/g, '').toUpperCase();\n}\n\n/** Modulo 97 sur la chaine, chiffre par chiffre : un IBAN depasse la\n * precision d'un nombre JavaScript. */\nfunction mod97(chaine: string): number {\n let reste = 0;\n for (const caractere of chaine) {\n reste = (reste * 10 + Number(caractere)) % 97;\n }\n return reste;\n}\n\nexport function ibanEstValide(saisie: string): boolean {\n const iban = normaliserIban(saisie);\n if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{10,30}$/.test(iban)) return false;\n\n const pays = iban.slice(0, 2);\n const longueurAttendue = LONGUEURS[pays];\n if (longueurAttendue !== undefined && iban.length !== longueurAttendue) return false;\n\n const permute = iban.slice(4) + iban.slice(0, 4);\n const numerique = permute.replace(/[A-Z]/g, (l) => (l.charCodeAt(0) - 55).toString());\n return mod97(numerique) === 1;\n}\n\n/** Presentation par groupes de quatre, la seule lisible a l'oeil. */\nexport function formaterIban(saisie: string): string {\n return normaliserIban(saisie)\n .replace(/(.{4})/g, '$1 ')\n .trim();\n}\n\n/**\n * Masque tout sauf le pays et les quatre derniers caracteres. Un IBAN complet\n * ne s'affiche jamais dans une liste : il suffit a initier un prelevement.\n */\nexport function masquerIban(saisie: string): string {\n const iban = normaliserIban(saisie);\n if (iban.length < 8) return '•'.repeat(iban.length);\n const tete = iban.slice(0, 4);\n const queue = iban.slice(-4);\n return formaterIban(tete + '•'.repeat(iban.length - 8) + queue);\n}\n","/**\n * Masques d'affichage des identifiants sensibles.\n *\n * Regle commune : on garde de quoi RECONNAITRE, jamais de quoi UTILISER. Un\n * numero complet affiche dans une liste suffit a initier une operation ; les\n * quatre derniers caracteres suffisent a l'utilisateur pour savoir de quel\n * moyen il s'agit.\n */\n\n/** Groupe par quatre, la seule presentation lisible a l'oeil. */\nfunction grouperParQuatre(valeur: string): string {\n return valeur.replace(/(.{4})/g, '$1 ').trim();\n}\n\nexport function normaliserCarte(saisie: string): string {\n return saisie.replace(/[\\s-]/g, '');\n}\n\n/**\n * Ne laisse voir que les quatre derniers chiffres. Le reseau (les premiers\n * chiffres) est volontairement masque : il identifie l'emetteur.\n */\nexport function masquerCarte(saisie: string): string {\n const carte = normaliserCarte(saisie);\n if (carte.length <= 4) return '•'.repeat(carte.length);\n return grouperParQuatre('•'.repeat(carte.length - 4) + carte.slice(-4));\n}\n\n/**\n * Validation de carte par la cle de Luhn. Elle sert a refuser une frappe\n * fautive sans aller-retour serveur ; elle ne prouve rien sur l'existence du\n * moyen de paiement.\n */\nexport function carteEstValide(saisie: string): boolean {\n const carte = normaliserCarte(saisie);\n if (!/^[0-9]{12,19}$/.test(carte)) return false;\n\n let somme = 0;\n let double = false;\n for (let index = carte.length - 1; index >= 0; index--) {\n let chiffre = Number(carte[index]);\n if (double) {\n chiffre *= 2;\n if (chiffre > 9) chiffre -= 9;\n }\n somme += chiffre;\n double = !double;\n }\n return somme % 10 === 0;\n}\n\nexport function normaliserTelephone(saisie: string): string {\n return saisie.replace(/[\\s.\\-()]/g, '');\n}\n\n/**\n * Garde l'indicatif international et les deux derniers chiffres. Un numero\n * entier permet d'usurper, de harceler, ou de tenter une prise de controle\n * par message.\n */\nexport function masquerTelephone(saisie: string): string {\n const numero = normaliserTelephone(saisie);\n const indicatif = numero.startsWith('+') ? numero.slice(0, 3) : '';\n const reste = numero.slice(indicatif.length);\n if (reste.length <= 2) return indicatif + '•'.repeat(reste.length);\n return `${indicatif}${'•'.repeat(reste.length - 2)}${reste.slice(-2)}`;\n}\n\n/**\n * Fin lisible d'un identifiant, pour composer un nom accessible : une suite de\n * puces est epelee caractere par caractere par plusieurs lecteurs d'ecran.\n */\nexport function quatreDerniers(saisie: string): string {\n const valeur = saisie.replace(/[\\s-]/g, '');\n return valeur.slice(-4);\n}\n","/**\n * @starterlib/util — fonctions pures partagees.\n *\n * Regle du paquet : aucune dependance Angular, aucun effet de bord, aucune\n * lecture d'horloge ni de `window`. Tout ce qui vit ici doit se tester sans\n * banc d'essai Angular.\n */\nexport * from './lib/montant';\nexport * from './lib/iban';\nexport * from './lib/masque';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@starterlib/util",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Fonctions pures du socle frontal starter : montants en unites mineures, IBAN, formatage. Aucune dependance Angular.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/Arnoldkom/starter-web-platform.git",
|
|
13
|
+
"directory": "packages/util"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/Arnoldkom/starter-web-platform/tree/main/packages/util#readme",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"starter",
|
|
18
|
+
"socle-frontal",
|
|
19
|
+
"util"
|
|
20
|
+
],
|
|
21
|
+
"module": "fesm2022/starterlib-util.mjs",
|
|
22
|
+
"typings": "types/starterlib-util.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
"./package.json": {
|
|
25
|
+
"default": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./types/starterlib-util.d.ts",
|
|
29
|
+
"default": "./fesm2022/starterlib-util.mjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"type": "module",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"tslib": "^2.3.0"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatage monetaire. Il vit dans le socle et non dans chaque SPA parce
|
|
3
|
+
* qu'une somme mal arrondie ou mal signee est un defaut bancaire, pas un
|
|
4
|
+
* defaut d'affichage.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Un montant ne se represente JAMAIS par un nombre a virgule flottante :
|
|
8
|
+
* 0.1 + 0.2 ne fait pas 0.3. On transporte des unites mineures entieres
|
|
9
|
+
* (centimes) et la devise qui leur donne leur echelle.
|
|
10
|
+
*/
|
|
11
|
+
interface Montant {
|
|
12
|
+
/** Unites mineures. 12,34 EUR s'ecrit 1234. */
|
|
13
|
+
readonly unitesMineures: bigint;
|
|
14
|
+
/** Code ISO 4217, trois lettres majuscules. */
|
|
15
|
+
readonly devise: string;
|
|
16
|
+
}
|
|
17
|
+
declare function decimalesDe(devise: string): number;
|
|
18
|
+
declare function montant(unitesMineures: bigint | number, devise: string): Montant;
|
|
19
|
+
interface OptionsDeFormatage {
|
|
20
|
+
readonly locale?: string;
|
|
21
|
+
/** Affiche `+` devant un montant positif. Utile sur un releve. */
|
|
22
|
+
readonly signeExplicite?: boolean;
|
|
23
|
+
/** Masque le symbole de devise (colonnes de tableau deja titrees). */
|
|
24
|
+
readonly sansDevise?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Rend un montant dans la locale demandee. La conversion en nombre n'a lieu
|
|
28
|
+
* qu'a l'instant du rendu, sur une valeur bornee par la precision d'affichage :
|
|
29
|
+
* le calcul, lui, n'a jamais quitte l'entier.
|
|
30
|
+
*/
|
|
31
|
+
declare function formaterMontant(valeur: Montant, options?: OptionsDeFormatage): string;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* IBAN : validation par la cle ISO 7064 mod 97-10 et presentation par groupes
|
|
35
|
+
* de quatre. La validation vit ici pour qu'un champ de saisie refuse une
|
|
36
|
+
* frappe fautive sans aller-retour serveur ; elle ne remplace jamais le
|
|
37
|
+
* controle du backend.
|
|
38
|
+
*/
|
|
39
|
+
declare function normaliserIban(saisie: string): string;
|
|
40
|
+
declare function ibanEstValide(saisie: string): boolean;
|
|
41
|
+
/** Presentation par groupes de quatre, la seule lisible a l'oeil. */
|
|
42
|
+
declare function formaterIban(saisie: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Masque tout sauf le pays et les quatre derniers caracteres. Un IBAN complet
|
|
45
|
+
* ne s'affiche jamais dans une liste : il suffit a initier un prelevement.
|
|
46
|
+
*/
|
|
47
|
+
declare function masquerIban(saisie: string): string;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Masques d'affichage des identifiants sensibles.
|
|
51
|
+
*
|
|
52
|
+
* Regle commune : on garde de quoi RECONNAITRE, jamais de quoi UTILISER. Un
|
|
53
|
+
* numero complet affiche dans une liste suffit a initier une operation ; les
|
|
54
|
+
* quatre derniers caracteres suffisent a l'utilisateur pour savoir de quel
|
|
55
|
+
* moyen il s'agit.
|
|
56
|
+
*/
|
|
57
|
+
declare function normaliserCarte(saisie: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Ne laisse voir que les quatre derniers chiffres. Le reseau (les premiers
|
|
60
|
+
* chiffres) est volontairement masque : il identifie l'emetteur.
|
|
61
|
+
*/
|
|
62
|
+
declare function masquerCarte(saisie: string): string;
|
|
63
|
+
/**
|
|
64
|
+
* Validation de carte par la cle de Luhn. Elle sert a refuser une frappe
|
|
65
|
+
* fautive sans aller-retour serveur ; elle ne prouve rien sur l'existence du
|
|
66
|
+
* moyen de paiement.
|
|
67
|
+
*/
|
|
68
|
+
declare function carteEstValide(saisie: string): boolean;
|
|
69
|
+
declare function normaliserTelephone(saisie: string): string;
|
|
70
|
+
/**
|
|
71
|
+
* Garde l'indicatif international et les deux derniers chiffres. Un numero
|
|
72
|
+
* entier permet d'usurper, de harceler, ou de tenter une prise de controle
|
|
73
|
+
* par message.
|
|
74
|
+
*/
|
|
75
|
+
declare function masquerTelephone(saisie: string): string;
|
|
76
|
+
/**
|
|
77
|
+
* Fin lisible d'un identifiant, pour composer un nom accessible : une suite de
|
|
78
|
+
* puces est epelee caractere par caractere par plusieurs lecteurs d'ecran.
|
|
79
|
+
*/
|
|
80
|
+
declare function quatreDerniers(saisie: string): string;
|
|
81
|
+
|
|
82
|
+
export { carteEstValide, decimalesDe, formaterIban, formaterMontant, ibanEstValide, masquerCarte, masquerIban, masquerTelephone, montant, normaliserCarte, normaliserIban, normaliserTelephone, quatreDerniers };
|
|
83
|
+
export type { Montant, OptionsDeFormatage };
|