@dnax/core 0.0.1
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 +15 -0
- package/ai/gemini.ts +97 -0
- package/ai/index.ts +2 -0
- package/ai/mistral.ts +63 -0
- package/app/ctrl.ts +26 -0
- package/app/hono.ts +398 -0
- package/app/index.ts +72 -0
- package/config/index.ts +68 -0
- package/define/index.ts +35 -0
- package/driver/index.ts +19 -0
- package/driver/mongo/@types.ts +44 -0
- package/driver/mongo/connect.ts +26 -0
- package/driver/mongo/index.ts +4 -0
- package/driver/mongo/rest.ts +1214 -0
- package/driver/mongo/utils.ts +361 -0
- package/index.ts +11 -0
- package/lib/asyncLocalStorage.ts +47 -0
- package/lib/collection.ts +191 -0
- package/lib/endpoint.ts +36 -0
- package/lib/index.ts +26 -0
- package/lib/media.ts +74 -0
- package/lib/schema.ts +112 -0
- package/lib/service.ts +43 -0
- package/lib/socket.ts +51 -0
- package/lib/studio.ts +12 -0
- package/lib/tenant.ts +9 -0
- package/package.json +38 -0
- package/tsconfig.json +27 -0
- package/types/index.ts +377 -0
- package/utils/index.ts +251 -0
package/types/index.ts
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { updateParams } from "./../driver/mongo/@types";
|
|
2
|
+
import * as v from "valibot";
|
|
3
|
+
import type { Db, MongoClient } from "mongodb";
|
|
4
|
+
import { useRest } from "../driver/mongo/rest";
|
|
5
|
+
import { sessionStorage } from "../lib/asyncLocalStorage";
|
|
6
|
+
import type { Context } from "hono";
|
|
7
|
+
import type { Server as ServerIO, Socket as SocketType } from "socket.io";
|
|
8
|
+
|
|
9
|
+
type Io = InstanceType<typeof ServerIO>;
|
|
10
|
+
|
|
11
|
+
import { fn } from "../utils";
|
|
12
|
+
import type {
|
|
13
|
+
findOneParam,
|
|
14
|
+
findParam,
|
|
15
|
+
updateParams,
|
|
16
|
+
} from "../driver/mongo/@types";
|
|
17
|
+
import type { RouterRoute } from "hono/types";
|
|
18
|
+
|
|
19
|
+
export type Socket = {
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
handler: (ctx: { rest: useRest; io: Io; session: sessionCtx }) => void;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type Tenant = {
|
|
25
|
+
id: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
enabled?: boolean;
|
|
28
|
+
dir: string;
|
|
29
|
+
database: {
|
|
30
|
+
driver: "mongodb";
|
|
31
|
+
/**
|
|
32
|
+
* Connection URI string to connect
|
|
33
|
+
*/
|
|
34
|
+
uri: string;
|
|
35
|
+
client?: InstanceType<typeof MongoClient>;
|
|
36
|
+
db?: Db;
|
|
37
|
+
isConnected?: boolean;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export type Actions =
|
|
41
|
+
| "findOneAndUpdate"
|
|
42
|
+
| "find"
|
|
43
|
+
| "findOne"
|
|
44
|
+
| "insertOne"
|
|
45
|
+
| "insertMany"
|
|
46
|
+
| "updateOne"
|
|
47
|
+
| "updateMany"
|
|
48
|
+
| "deleteOne"
|
|
49
|
+
| "deleteMany"
|
|
50
|
+
| "aggregate";
|
|
51
|
+
|
|
52
|
+
export type Field = {
|
|
53
|
+
name: string;
|
|
54
|
+
type:
|
|
55
|
+
| "boolean"
|
|
56
|
+
| "ipv4"
|
|
57
|
+
| "ipv6"
|
|
58
|
+
| "url"
|
|
59
|
+
| "date"
|
|
60
|
+
| "datetime-local"
|
|
61
|
+
| "email"
|
|
62
|
+
| "array"
|
|
63
|
+
| "file"
|
|
64
|
+
| "number"
|
|
65
|
+
| "integer"
|
|
66
|
+
| "password"
|
|
67
|
+
| "random"
|
|
68
|
+
| "relationship"
|
|
69
|
+
| "string"
|
|
70
|
+
| "enum"
|
|
71
|
+
| "textarea"
|
|
72
|
+
| "richText"
|
|
73
|
+
| "json"
|
|
74
|
+
| "geojson";
|
|
75
|
+
studio?: {
|
|
76
|
+
display?: string;
|
|
77
|
+
suffix?: string;
|
|
78
|
+
prefix?: string;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
random?: {
|
|
82
|
+
length?: number;
|
|
83
|
+
useLetters?: boolean;
|
|
84
|
+
useNumbers?: boolean;
|
|
85
|
+
includeSymbols?: Array<any>;
|
|
86
|
+
excludeSymbols?: Array<any>;
|
|
87
|
+
startWith?: string;
|
|
88
|
+
endWith?: string;
|
|
89
|
+
toUpperCase?: boolean;
|
|
90
|
+
toLowerCase?: boolean;
|
|
91
|
+
toNumber?: boolean;
|
|
92
|
+
};
|
|
93
|
+
enum?: {
|
|
94
|
+
multiple?: boolean;
|
|
95
|
+
items: Array<any>;
|
|
96
|
+
};
|
|
97
|
+
nullable?: boolean;
|
|
98
|
+
defaultValue?: any;
|
|
99
|
+
unique?: boolean;
|
|
100
|
+
index?: boolean | "text" | "2dsphere";
|
|
101
|
+
description?: string;
|
|
102
|
+
sparse?: boolean;
|
|
103
|
+
relationType?: "ref-to-one" | "ref-to-many";
|
|
104
|
+
validate?: Array<any>;
|
|
105
|
+
relationTo?: string;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export type accessCtx = (ctx: {
|
|
109
|
+
token?: string;
|
|
110
|
+
action?: Actions;
|
|
111
|
+
c?: Context;
|
|
112
|
+
isAuth: boolean;
|
|
113
|
+
rest: InstanceType<typeof useRest>;
|
|
114
|
+
session: sessionCtx;
|
|
115
|
+
}) => boolean | undefined | void;
|
|
116
|
+
|
|
117
|
+
export type sessionCtx = {
|
|
118
|
+
set: (session: { state: object | any; token?: string; _v?: object }) => void;
|
|
119
|
+
get: () => { state: object; _v: object };
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export type hooksCtx = (ctx: {
|
|
123
|
+
filter?: any;
|
|
124
|
+
result?: any;
|
|
125
|
+
driver?: "mongodb" | "postgres";
|
|
126
|
+
data?: object;
|
|
127
|
+
params?: findParam;
|
|
128
|
+
options?: {};
|
|
129
|
+
id?: string;
|
|
130
|
+
ids?: string[];
|
|
131
|
+
update?: updateParams;
|
|
132
|
+
pipeline?: Array<object>;
|
|
133
|
+
sharedData?: any;
|
|
134
|
+
store?: any;
|
|
135
|
+
action?: Actions;
|
|
136
|
+
c?: Context;
|
|
137
|
+
rest: InstanceType<typeof useRest>;
|
|
138
|
+
session?: sessionCtx;
|
|
139
|
+
io: Io;
|
|
140
|
+
}) => any;
|
|
141
|
+
|
|
142
|
+
export type ctxApi = {
|
|
143
|
+
rest: useRest;
|
|
144
|
+
data?: any;
|
|
145
|
+
session?: sessionCtx;
|
|
146
|
+
io?: Io;
|
|
147
|
+
params?: findParam;
|
|
148
|
+
id?: string;
|
|
149
|
+
ids?: string[];
|
|
150
|
+
update?: updateParams;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export type Collection = {
|
|
154
|
+
init?: (ctx: {
|
|
155
|
+
rest: InstanceType<typeof useRest>;
|
|
156
|
+
collection: string;
|
|
157
|
+
}) => void;
|
|
158
|
+
type?: "document" | "media";
|
|
159
|
+
media?: {
|
|
160
|
+
/**
|
|
161
|
+
* Folder name to store files in directory uploads
|
|
162
|
+
* It take slug name as default name
|
|
163
|
+
*/
|
|
164
|
+
overwriteFolderName?: string;
|
|
165
|
+
/**
|
|
166
|
+
* Enable or disable theses : upload / serve file
|
|
167
|
+
*/
|
|
168
|
+
enabled: boolean;
|
|
169
|
+
/**
|
|
170
|
+
* Visibility of the files
|
|
171
|
+
*/
|
|
172
|
+
visibility?: "private" | "public";
|
|
173
|
+
/**
|
|
174
|
+
* Mimie type to accept
|
|
175
|
+
*/
|
|
176
|
+
accept?: Array<string> | string;
|
|
177
|
+
};
|
|
178
|
+
customApi?: {
|
|
179
|
+
insertOne?: (ctx: ctxApi) => object | null | undefined;
|
|
180
|
+
insertMany?: (ctx: ctxApi) => Array<object> | null | undefined;
|
|
181
|
+
updateOne?: (ctx: ctxApi) => object | null | undefined;
|
|
182
|
+
updateMany?: (ctx: ctxApi) => object | null | undefined;
|
|
183
|
+
deleteOne?: (ctx: ctxApi) => object | null | undefined;
|
|
184
|
+
deleteMany?: (ctx: ctxApi) => object | null | undefined;
|
|
185
|
+
find?: (ctx: ctxApi) => Array<object> | null | undefined;
|
|
186
|
+
findOne?: (ctx: ctxApi) => object;
|
|
187
|
+
};
|
|
188
|
+
schema?: object;
|
|
189
|
+
auth?: {
|
|
190
|
+
enabled?: boolean;
|
|
191
|
+
handler?: (ctx: {
|
|
192
|
+
data: {
|
|
193
|
+
payload?: object | undefined;
|
|
194
|
+
};
|
|
195
|
+
error: typeof fn.error;
|
|
196
|
+
c: Context;
|
|
197
|
+
rest: InstanceType<typeof useRest>;
|
|
198
|
+
session: sessionCtx;
|
|
199
|
+
}) => boolean | any;
|
|
200
|
+
};
|
|
201
|
+
hooks?: {
|
|
202
|
+
beforeOperation?: hooksCtx;
|
|
203
|
+
beforeFind?: hooksCtx;
|
|
204
|
+
afterFind?: hooksCtx;
|
|
205
|
+
beforeUpdate?: hooksCtx;
|
|
206
|
+
afterUpdate?: hooksCtx;
|
|
207
|
+
beforeDelete?: hooksCtx;
|
|
208
|
+
afterDelete?: hooksCtx;
|
|
209
|
+
beforeInsert?: hooksCtx;
|
|
210
|
+
afterInsert?: hooksCtx;
|
|
211
|
+
beforeAggregate?: hooksCtx;
|
|
212
|
+
afterAggregate?: hooksCtx;
|
|
213
|
+
};
|
|
214
|
+
access?: {
|
|
215
|
+
beforeAction?: accessCtx;
|
|
216
|
+
allAction?: accessCtx;
|
|
217
|
+
find?: accessCtx;
|
|
218
|
+
findOne?: accessCtx;
|
|
219
|
+
findOneAndUpdate?: accessCtx;
|
|
220
|
+
insertOne?: accessCtx;
|
|
221
|
+
insertMany?: accessCtx;
|
|
222
|
+
updateMany?: accessCtx;
|
|
223
|
+
updateOne?: accessCtx;
|
|
224
|
+
deleteOne?: accessCtx;
|
|
225
|
+
deleteMany?: accessCtx;
|
|
226
|
+
aggregate?: accessCtx;
|
|
227
|
+
upload?: accessCtx;
|
|
228
|
+
};
|
|
229
|
+
tenant_id?: string;
|
|
230
|
+
slug: string;
|
|
231
|
+
timestamps?: boolean;
|
|
232
|
+
description?: string;
|
|
233
|
+
fields?: Field[];
|
|
234
|
+
privateFields?: string[];
|
|
235
|
+
allow?: ["dropCollection", "renameCollection"];
|
|
236
|
+
indexes?: Array<{
|
|
237
|
+
[key: string]: any;
|
|
238
|
+
unique?: Boolean;
|
|
239
|
+
expireAfterSeconds?: number;
|
|
240
|
+
}>;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export type Config = {
|
|
244
|
+
studio?: {
|
|
245
|
+
/**
|
|
246
|
+
* Enable or disable the studio
|
|
247
|
+
*/
|
|
248
|
+
enabled?: boolean;
|
|
249
|
+
/**
|
|
250
|
+
* Allow access by IP
|
|
251
|
+
*/
|
|
252
|
+
enableIps?: boolean;
|
|
253
|
+
/**
|
|
254
|
+
* WhiteLists IP adresses : ['192.168.1.1']
|
|
255
|
+
*/
|
|
256
|
+
whiteListIps?: Array<string>;
|
|
257
|
+
/**
|
|
258
|
+
* Secret key for studio
|
|
259
|
+
*/
|
|
260
|
+
secretKey: string;
|
|
261
|
+
};
|
|
262
|
+
debug?: boolean;
|
|
263
|
+
ai?: {
|
|
264
|
+
driver: "mistral" | "openai" | "gemini";
|
|
265
|
+
key: string;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
server: {
|
|
269
|
+
cors?: {
|
|
270
|
+
origin: string[];
|
|
271
|
+
};
|
|
272
|
+
socket?: {
|
|
273
|
+
/**
|
|
274
|
+
* Socket port
|
|
275
|
+
* Default port is 9000
|
|
276
|
+
*/
|
|
277
|
+
port?: Number;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Port to run the server
|
|
282
|
+
*/
|
|
283
|
+
port: number;
|
|
284
|
+
};
|
|
285
|
+
/**
|
|
286
|
+
* Tenants database for API
|
|
287
|
+
*/
|
|
288
|
+
tenants: Tenant[];
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Dont touch it automatically generated
|
|
292
|
+
*/
|
|
293
|
+
collections?: Array<Collection>;
|
|
294
|
+
/**
|
|
295
|
+
* Dont touch it automatically generated
|
|
296
|
+
*/
|
|
297
|
+
endpoints?: Array<Endpoint>;
|
|
298
|
+
/**
|
|
299
|
+
* Dont touch it automatically generated
|
|
300
|
+
*/
|
|
301
|
+
services?: Array<Service>;
|
|
302
|
+
/**
|
|
303
|
+
* Dont touch it automatically generated
|
|
304
|
+
*/
|
|
305
|
+
cwd?: string;
|
|
306
|
+
io?: any;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
export type Q = {
|
|
310
|
+
name: string;
|
|
311
|
+
useCache: boolean;
|
|
312
|
+
cleanDeep: boolean;
|
|
313
|
+
collection: string;
|
|
314
|
+
action:
|
|
315
|
+
| "findOneAndUpdate"
|
|
316
|
+
| "find"
|
|
317
|
+
| "findOne"
|
|
318
|
+
| "insertOne"
|
|
319
|
+
| "insertMany"
|
|
320
|
+
| "deleteMany"
|
|
321
|
+
| "deleteOne"
|
|
322
|
+
| "updateOne"
|
|
323
|
+
| "updateMany"
|
|
324
|
+
| "aggregate"
|
|
325
|
+
| "authCollection"
|
|
326
|
+
| "auth"
|
|
327
|
+
| "upload"
|
|
328
|
+
| "execService";
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
export type endpointCtx = {
|
|
332
|
+
enabled: boolean;
|
|
333
|
+
handler: (ctx: {
|
|
334
|
+
router: {
|
|
335
|
+
post: (path: string, clb: (c: Context) => void) => {};
|
|
336
|
+
get: (path: string, clb: (c: Context) => void) => {};
|
|
337
|
+
};
|
|
338
|
+
}) => any;
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
export type Endpoint = endpointCtx;
|
|
342
|
+
|
|
343
|
+
export type Service = {
|
|
344
|
+
/**
|
|
345
|
+
* Dont touch it automatically generated
|
|
346
|
+
*/
|
|
347
|
+
tenant_id?: string;
|
|
348
|
+
/**
|
|
349
|
+
* Service's name
|
|
350
|
+
*/
|
|
351
|
+
name: string;
|
|
352
|
+
/**
|
|
353
|
+
* Function to execute when service is calling
|
|
354
|
+
* @param ctx
|
|
355
|
+
* @returns
|
|
356
|
+
*/
|
|
357
|
+
fx: (ctx: {
|
|
358
|
+
/**
|
|
359
|
+
* Incomming data [payload]
|
|
360
|
+
*/
|
|
361
|
+
data: object;
|
|
362
|
+
/**
|
|
363
|
+
* Local rest linked to current tenant
|
|
364
|
+
*/
|
|
365
|
+
rest: InstanceType<typeof useRest>;
|
|
366
|
+
error: typeof fn.error;
|
|
367
|
+
/**
|
|
368
|
+
* Session of request
|
|
369
|
+
*/
|
|
370
|
+
session: sessionCtx;
|
|
371
|
+
/**
|
|
372
|
+
* authenticated user or no
|
|
373
|
+
*/
|
|
374
|
+
isAuth: boolean;
|
|
375
|
+
io: Io;
|
|
376
|
+
}) => any;
|
|
377
|
+
};
|
package/utils/index.ts
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { ObjectId } from "mongodb";
|
|
2
|
+
import moment from "moment";
|
|
3
|
+
import { mapKeys } from "radash";
|
|
4
|
+
import { cleanDoubleSlashes } from "ufo";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { parse, format } from "@lukeed/ms";
|
|
7
|
+
import jwto from "jsonwebtoken";
|
|
8
|
+
import generateUniqueId from "generate-unique-id";
|
|
9
|
+
|
|
10
|
+
const JWT_SECRET = process?.env?.JWT_SECRET || "secret-libv";
|
|
11
|
+
import * as _ from "radash";
|
|
12
|
+
|
|
13
|
+
const jwt = {
|
|
14
|
+
verify: (token: string): { decode: any; valid: boolean; error: any } => {
|
|
15
|
+
let decode = null;
|
|
16
|
+
let valid = true;
|
|
17
|
+
let error = null;
|
|
18
|
+
try {
|
|
19
|
+
decode = jwto.verify(token, JWT_SECRET);
|
|
20
|
+
} catch (err: any) {
|
|
21
|
+
valid = false;
|
|
22
|
+
error = err?.message;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return { decode, valid, error };
|
|
26
|
+
},
|
|
27
|
+
getToken: (prefix = "Bearer", input: string): string | null => {
|
|
28
|
+
if (input?.startsWith(prefix)) {
|
|
29
|
+
let token = input?.slice(prefix?.length + 1);
|
|
30
|
+
if (!token || token?.replace(/\s/g, "") === "") return null;
|
|
31
|
+
} else {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
sign: (
|
|
37
|
+
payload: any,
|
|
38
|
+
options: { expiresIn: string } = {
|
|
39
|
+
expiresIn: "7d",
|
|
40
|
+
}
|
|
41
|
+
) => {
|
|
42
|
+
return jwto.sign(payload, JWT_SECRET, {
|
|
43
|
+
...options,
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
auth: (
|
|
47
|
+
payload: any = {},
|
|
48
|
+
options: { expiresIn: string } = {
|
|
49
|
+
expiresIn: "7d",
|
|
50
|
+
}
|
|
51
|
+
) => {
|
|
52
|
+
return jwto.sign(
|
|
53
|
+
{
|
|
54
|
+
...payload,
|
|
55
|
+
_v: {
|
|
56
|
+
isAuth: true,
|
|
57
|
+
authAt: new Date(),
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
JWT_SECRET,
|
|
61
|
+
{
|
|
62
|
+
...options,
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
function toJson(data: object) {
|
|
69
|
+
let obj = JSON.stringify(data);
|
|
70
|
+
return JSON.parse(obj);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isDate(date: string): boolean {
|
|
74
|
+
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
75
|
+
let isDate_ = !isNaN(Date.parse(date)) && dateRegex.test(date);
|
|
76
|
+
return isDate_;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function hashPassword(
|
|
80
|
+
password: string,
|
|
81
|
+
algorithm: "argon2d" | "bcrypt" = "argon2d"
|
|
82
|
+
) {
|
|
83
|
+
return await Bun.password.hash(password, {
|
|
84
|
+
algorithm: algorithm || "argon2d",
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function verifyHashPassword(
|
|
89
|
+
password: string,
|
|
90
|
+
hash: string,
|
|
91
|
+
algorithm: "argon2d" | "bcrypt" = "argon2d"
|
|
92
|
+
) {
|
|
93
|
+
try {
|
|
94
|
+
if (await Bun.password.verify(password, hash, algorithm)) {
|
|
95
|
+
return true;
|
|
96
|
+
} else {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
} catch (err) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function toDate(data: object | string): object | string {
|
|
105
|
+
if (data) {
|
|
106
|
+
if (typeof data == "string" && isDate(data)) {
|
|
107
|
+
data = new Date(data);
|
|
108
|
+
}
|
|
109
|
+
// for object
|
|
110
|
+
if (typeof data == "object") {
|
|
111
|
+
mapKeys(data, (key: string, value: any) => {
|
|
112
|
+
data[key] = toDate(value);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return data;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function deepMerge(target: object, source: object) {
|
|
120
|
+
// Itérer à travers toutes les propriétés de la source
|
|
121
|
+
for (const key in source) {
|
|
122
|
+
const sourceValue = source[key];
|
|
123
|
+
const targetValue = target[key];
|
|
124
|
+
|
|
125
|
+
// Si les deux valeurs sont des objets, faire un appel récursif
|
|
126
|
+
if (
|
|
127
|
+
typeof sourceValue === "object" &&
|
|
128
|
+
sourceValue &&
|
|
129
|
+
!Array.isArray(sourceValue) &&
|
|
130
|
+
typeof targetValue === "object" &&
|
|
131
|
+
targetValue &&
|
|
132
|
+
!Array.isArray(targetValue)
|
|
133
|
+
) {
|
|
134
|
+
target[key] = deepMerge({ ...targetValue }, sourceValue);
|
|
135
|
+
} else {
|
|
136
|
+
// Pour tout autre type ou lorsque les types ne correspondent pas, écraser avec la valeur source
|
|
137
|
+
target[key] = sourceValue;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return target;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function cleanPath(path: string) {
|
|
144
|
+
return cleanDoubleSlashes(path).toString();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function resolvePath(filepath: string) {
|
|
148
|
+
return path.resolve(cleanDoubleSlashes(filepath).toString());
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function freeze(data: object, keys: string[]): object {
|
|
152
|
+
keys.forEach((cle) => {
|
|
153
|
+
const clePath = cle.split("."); // Divise la clé en parties selon les points
|
|
154
|
+
let currentObjet = data;
|
|
155
|
+
|
|
156
|
+
// Parcourir jusqu'à l'avant-dernier élément pour atteindre l'objet contenant la propriété
|
|
157
|
+
for (let i = 0; i < clePath.length - 1; i++) {
|
|
158
|
+
if (currentObjet.hasOwnProperty(clePath[i])) {
|
|
159
|
+
currentObjet = currentObjet[clePath[i]];
|
|
160
|
+
} else {
|
|
161
|
+
return; // Si le chemin n'existe pas, sortir de la fonction
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// La dernière partie de clePath est la clé à rendre en lecture seule
|
|
166
|
+
const lastKey = clePath[clePath.length - 1];
|
|
167
|
+
if (currentObjet.hasOwnProperty(lastKey)) {
|
|
168
|
+
Object.defineProperty(currentObjet, lastKey, {
|
|
169
|
+
writable: false,
|
|
170
|
+
configurable: false,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return data;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
*
|
|
180
|
+
* @param {object|array} data - data
|
|
181
|
+
* @param {string[]} keysToRemove - keys to remove
|
|
182
|
+
* @returns {Array|Object} - data without the keys
|
|
183
|
+
*/
|
|
184
|
+
function omit(data: object[] | object, keysToRemove: string[]) {
|
|
185
|
+
var json = data;
|
|
186
|
+
function removeKey(obj: any, keys: any): any {
|
|
187
|
+
if (!obj || keys.length === 0) {
|
|
188
|
+
return obj;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const [currentKey, ...remainingKeys] = keys;
|
|
192
|
+
|
|
193
|
+
if (Array.isArray(obj)) {
|
|
194
|
+
return obj.map((item) => removeKey(item, keys));
|
|
195
|
+
} else if (typeof obj === "object" && obj !== null) {
|
|
196
|
+
if (remainingKeys.length === 0) {
|
|
197
|
+
const { [currentKey]: _, ...rest } = obj;
|
|
198
|
+
return rest;
|
|
199
|
+
} else {
|
|
200
|
+
if (obj.hasOwnProperty(currentKey)) {
|
|
201
|
+
return {
|
|
202
|
+
...obj,
|
|
203
|
+
[currentKey]: removeKey(obj[currentKey], remainingKeys),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return obj;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
keysToRemove.forEach((keyPath) => {
|
|
212
|
+
const keys = keyPath.split(".");
|
|
213
|
+
json = removeKey(json, keys);
|
|
214
|
+
});
|
|
215
|
+
return json;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
class contextError extends Error {
|
|
219
|
+
constructor(message: string, code: number) {
|
|
220
|
+
super(message);
|
|
221
|
+
this.code = code;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const fn = {
|
|
226
|
+
error: (message: string, code: number) => {
|
|
227
|
+
throw new contextError(message || "unknow", code);
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const $ = Bun.$;
|
|
232
|
+
|
|
233
|
+
export {
|
|
234
|
+
moment,
|
|
235
|
+
$,
|
|
236
|
+
fn,
|
|
237
|
+
deepMerge,
|
|
238
|
+
toDate,
|
|
239
|
+
toJson,
|
|
240
|
+
hashPassword,
|
|
241
|
+
verifyHashPassword,
|
|
242
|
+
cleanPath,
|
|
243
|
+
freeze,
|
|
244
|
+
resolvePath,
|
|
245
|
+
isDate,
|
|
246
|
+
contextError,
|
|
247
|
+
jwt,
|
|
248
|
+
_,
|
|
249
|
+
generateUniqueId,
|
|
250
|
+
omit,
|
|
251
|
+
};
|