@anteros/core 0.0.3 → 0.0.5
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/package.json +1 -1
- package/utils/func.ts +43 -12
package/package.json
CHANGED
package/utils/func.ts
CHANGED
|
@@ -208,16 +208,28 @@ function cleanDeep<T extends DeepRecord | DeepRecord[]>(data: T): Partial<T> {
|
|
|
208
208
|
return result as Partial<T>;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Vérifie si une string est une date valide.
|
|
213
|
+
* Accepte : ISO 8601 (YYYY-MM-DD, YYYY-MM-DDTHH:mm...) et timestamps UNIX (s ou ms).
|
|
214
|
+
* Rejette les strings ambiguës que certains moteurs JS parsent en date (ex: "REF-2024").
|
|
215
|
+
*/
|
|
211
216
|
function isDate(value: string): boolean {
|
|
212
217
|
if (!value || typeof value !== 'string') return false;
|
|
213
218
|
const trimmed = value.trim();
|
|
214
|
-
|
|
219
|
+
|
|
220
|
+
// Timestamps numériques purs (10 ou 13 chiffres)
|
|
215
221
|
if (/^\d+$/.test(trimmed)) {
|
|
216
222
|
const len = trimmed.length;
|
|
217
223
|
if (len !== 10 && len !== 13) return false;
|
|
218
224
|
const ms = new Date(Number(trimmed)).getTime();
|
|
219
225
|
return !isNaN(ms);
|
|
220
226
|
}
|
|
227
|
+
|
|
228
|
+
// ISO 8601 : YYYY-MM-DD avec heure optionnelle
|
|
229
|
+
if (!/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d{1,3})?)?(Z|[+-]\d{2}:\d{2})?)?$/.test(trimmed)) {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
221
233
|
const date = new Date(trimmed);
|
|
222
234
|
return !isNaN(date.getTime());
|
|
223
235
|
}
|
|
@@ -269,14 +281,26 @@ function joinFormatToDatePath(parent: string, segment: string | number): string
|
|
|
269
281
|
return parent === '' ? s : `${parent}.${s}`;
|
|
270
282
|
}
|
|
271
283
|
|
|
272
|
-
/**
|
|
284
|
+
/**
|
|
285
|
+
* Correspond à un nom de champ schéma (`createdAt`, `user.birthDate`)
|
|
286
|
+
* y compris en profondeur (`$set.createdAt`) ou derrière un opérateur
|
|
287
|
+
* MongoDB (`createdAt.$lte`, `$match.createdAt.$gte`).
|
|
288
|
+
*/
|
|
273
289
|
function pathMatchesFieldKey(path: string, key: string): boolean {
|
|
274
|
-
|
|
290
|
+
// Strip trailing MongoDB operator segments: createdAt.$lte → createdAt
|
|
291
|
+
const cleanPath = path.replace(/\.\$[a-z]+$/i, '');
|
|
292
|
+
return cleanPath === key || cleanPath.endsWith('.' + key);
|
|
275
293
|
}
|
|
276
294
|
|
|
277
295
|
function shouldFormatDateAtPath(path: string, options?: FormatToDateOptions): boolean {
|
|
278
296
|
// Aucune option → convertir partout où `isDate` est vrai
|
|
279
297
|
if (!options) return true;
|
|
298
|
+
|
|
299
|
+
// Contexte d'opérateur MongoDB ($lte, $gte, $eq, $in, $nin, $exists, etc.)
|
|
300
|
+
// → toujours convertir : un opérateur sur une date n'a de sens qu'avec un vrai Date
|
|
301
|
+
// On vérifie si un segment du chemin commence par '$' (ex: createdAt.$lte, $in.0)
|
|
302
|
+
if (path.split('.').some((seg) => /^\$[a-z]+$/i.test(seg))) return true;
|
|
303
|
+
|
|
280
304
|
if (options.omitKeys?.some((k) => pathMatchesFieldKey(path, k))) return false;
|
|
281
305
|
// Whitelist seulement si includeKeys est un tableau non vide
|
|
282
306
|
if (options.includeKeys && options.includeKeys.length > 0) {
|
|
@@ -290,6 +314,11 @@ function formatToDate(data: any, options?: FormatToDateOptions, path = ''): any
|
|
|
290
314
|
if (data instanceof Date) return data;
|
|
291
315
|
if (data instanceof ObjectId) return data;
|
|
292
316
|
|
|
317
|
+
// String racine (ex: item de tableau $in) — vérifier avant la branche objet
|
|
318
|
+
if (typeof data === 'string' && isDate(data)) {
|
|
319
|
+
return shouldFormatDateAtPath(path, options) ? stringToDate(data) : data;
|
|
320
|
+
}
|
|
321
|
+
|
|
293
322
|
if (Array.isArray(data)) {
|
|
294
323
|
return data.map((item, i) => {
|
|
295
324
|
const p = joinFormatToDatePath(path, i);
|
|
@@ -380,8 +409,10 @@ export type GenerateRandomOptions = {
|
|
|
380
409
|
useNumbers?: boolean;
|
|
381
410
|
includeSymbols?: string;
|
|
382
411
|
excludeSymbols?: string;
|
|
383
|
-
|
|
384
|
-
|
|
412
|
+
/** Préfixe (string fixe ou fonction qui reçoit la valeur générée). Ex: `(v) => 'BP-' + v` */
|
|
413
|
+
startWith?: string | ((value: string) => string);
|
|
414
|
+
/** Suffixe (string fixe ou fonction qui reçoit la valeur générée). Ex: `(v) => v + '-X'` */
|
|
415
|
+
endWith?: string | ((value: string) => string);
|
|
385
416
|
toLowerCase?: boolean;
|
|
386
417
|
toUpperCase?: boolean;
|
|
387
418
|
toNumber?: boolean;
|
|
@@ -434,7 +465,9 @@ async function generateRandom(options: GenerateRandomOptions, ctx: {
|
|
|
434
465
|
result += charset[randomIndex];
|
|
435
466
|
}
|
|
436
467
|
|
|
437
|
-
|
|
468
|
+
const start = typeof startWith === 'function' ? startWith(result) : startWith;
|
|
469
|
+
const end = typeof endWith === 'function' ? endWith(result) : endWith;
|
|
470
|
+
result = start + result + end;
|
|
438
471
|
|
|
439
472
|
if (toLowerCase) {
|
|
440
473
|
result = result.toLowerCase();
|
|
@@ -563,13 +596,11 @@ function collectDateFieldPathsFromCollection(col: Collection): string[] {
|
|
|
563
596
|
return [...paths];
|
|
564
597
|
}
|
|
565
598
|
|
|
566
|
-
function toBson<T>(data: any | any[],
|
|
599
|
+
function toBson<T>(data: any | any[], _options?: FormatInputOptions): T {
|
|
567
600
|
let data_ = formatToObjectId(data);
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
: undefined;
|
|
572
|
-
data_ = formatToDate(data_, dateOpts);
|
|
601
|
+
// Toujours convertir toutes les strings date valide en Date,
|
|
602
|
+
// quel que soit le contexte (insert, update, match, pipeline).
|
|
603
|
+
data_ = formatToDate(data_);
|
|
573
604
|
|
|
574
605
|
if (Array.isArray(data)) {
|
|
575
606
|
data.length = 0;
|