@anteros/core 0.0.2 → 0.0.4
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/database/file.ts +21 -7
- package/lib/files.ts +91 -49
- package/package.json +3 -3
- package/server/api.ts +327 -519
- package/server/config.ts +1 -1
- package/tests/api.test.ts +0 -8
- package/types/api.d.ts +0 -1
- package/types/collection.d.ts +13 -18
- package/utils/func.ts +36 -9
package/server/config.ts
CHANGED
|
@@ -53,7 +53,7 @@ function safePublicConfig() {
|
|
|
53
53
|
actions: [
|
|
54
54
|
'insertOne', 'insertMany', 'updateOne', 'updateMany',
|
|
55
55
|
'deleteOne', 'deleteMany', 'findOne', 'find',
|
|
56
|
-
'
|
|
56
|
+
'aggregate',
|
|
57
57
|
...Object.keys(c.actions ?? {}),
|
|
58
58
|
],
|
|
59
59
|
fields: (c.fields ?? []).map(f => ({
|
package/tests/api.test.ts
CHANGED
|
@@ -116,14 +116,6 @@ describe("updateMany", () => {
|
|
|
116
116
|
});
|
|
117
117
|
});
|
|
118
118
|
|
|
119
|
-
describe("findOneAndUpdate", () => {
|
|
120
|
-
it("finds and updates atomically", async () => {
|
|
121
|
-
await rest.insertOne("items", { title: "fau", count: 0 });
|
|
122
|
-
const doc = await rest.findOneAndUpdate("items", { title: "fau" }, { $set: { count: 10 } });
|
|
123
|
-
expect(doc.count).toBe(10);
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
|
|
127
119
|
describe("deleteMany", () => {
|
|
128
120
|
it("deletes multiple by ids", async () => {
|
|
129
121
|
const a = await rest.insertOne("items", { title: "d1" });
|
package/types/api.d.ts
CHANGED
package/types/collection.d.ts
CHANGED
|
@@ -28,20 +28,17 @@ export type Collection = {
|
|
|
28
28
|
payload: any;
|
|
29
29
|
error: typeof fn.error;
|
|
30
30
|
jwt: typeof jwt;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
delete: (name: string) => void;
|
|
43
|
-
}
|
|
44
|
-
|
|
31
|
+
cookies: {
|
|
32
|
+
set: (name: string, value: string, options?: {
|
|
33
|
+
httpOnly?: boolean;
|
|
34
|
+
secure?: boolean;
|
|
35
|
+
maxAge?: number;
|
|
36
|
+
path?: string;
|
|
37
|
+
domain?: string;
|
|
38
|
+
sameSite?: 'lax' | 'strict' | 'none';
|
|
39
|
+
}) => any;
|
|
40
|
+
get: (name: string) => string | undefined;
|
|
41
|
+
delete: (name: string) => void;
|
|
45
42
|
}
|
|
46
43
|
}) => Promise<{
|
|
47
44
|
token: string;
|
|
@@ -52,10 +49,8 @@ export type Collection = {
|
|
|
52
49
|
payload: any;
|
|
53
50
|
error: typeof fn.error;
|
|
54
51
|
jwt: typeof jwt;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
delete: (name: string) => void;
|
|
58
|
-
}
|
|
52
|
+
cookies: {
|
|
53
|
+
delete: (name: string) => void;
|
|
59
54
|
}
|
|
60
55
|
}) => Promise<void>;
|
|
61
56
|
};
|
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);
|
|
@@ -563,13 +592,11 @@ function collectDateFieldPathsFromCollection(col: Collection): string[] {
|
|
|
563
592
|
return [...paths];
|
|
564
593
|
}
|
|
565
594
|
|
|
566
|
-
function toBson<T>(data: any | any[],
|
|
595
|
+
function toBson<T>(data: any | any[], _options?: FormatInputOptions): T {
|
|
567
596
|
let data_ = formatToObjectId(data);
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
: undefined;
|
|
572
|
-
data_ = formatToDate(data_, dateOpts);
|
|
597
|
+
// Toujours convertir toutes les strings date valide en Date,
|
|
598
|
+
// quel que soit le contexte (insert, update, match, pipeline).
|
|
599
|
+
data_ = formatToDate(data_);
|
|
573
600
|
|
|
574
601
|
if (Array.isArray(data)) {
|
|
575
602
|
data.length = 0;
|