5htp-core 0.1.2 → 0.2.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.
Files changed (127) hide show
  1. package/changelog.md +5 -0
  2. package/doc/TODO.md +71 -0
  3. package/package.json +5 -4
  4. package/src/client/{App.tsx → app/component.tsx} +15 -8
  5. package/src/client/app/index.ts +128 -0
  6. package/src/client/app/service.ts +34 -0
  7. package/src/client/app.tsconfig.json +0 -4
  8. package/src/client/assets/css/medias.less +14 -0
  9. package/src/client/components/Card/index.tsx +2 -2
  10. package/src/client/components/Dialog/Manager.tsx +39 -12
  11. package/src/client/components/Form/index.tsx +1 -1
  12. package/src/client/components/button.tsx +2 -2
  13. package/src/client/components/containers/Popover/index.tsx +1 -1
  14. package/src/client/components/data/spintext/index.tsx +1 -1
  15. package/src/client/components/dropdown/index.tsx +1 -1
  16. package/src/client/components/index.ts +8 -0
  17. package/src/client/components/input/BaseV2/index.tsx +1 -1
  18. package/src/client/components/input/UploadImage/index.tsx +1 -1
  19. package/src/client/hooks/index.ts +5 -0
  20. package/src/client/hooks/useState/index.tsx +2 -2
  21. package/src/client/hooks.ts +22 -0
  22. package/src/client/index.ts +5 -0
  23. package/src/client/pages/_layout/landing/index.tsx +0 -2
  24. package/src/client/pages/_messages/400.tsx +2 -2
  25. package/src/client/pages/_messages/401.tsx +2 -2
  26. package/src/client/pages/_messages/403.tsx +2 -2
  27. package/src/client/pages/_messages/404.tsx +2 -2
  28. package/src/client/pages/_messages/500.tsx +2 -2
  29. package/src/client/pages/bug.tsx +1 -1
  30. package/src/client/pages/useHeader.tsx +1 -1
  31. package/src/client/{context/captcha.ts → services/captcha/index.ts} +0 -0
  32. package/src/client/services/metrics/index.ts +37 -0
  33. package/src/client/{router → services/router/components}/Link.tsx +1 -1
  34. package/src/client/services/router/components/Page.tsx +59 -0
  35. package/src/client/{router/component.tsx → services/router/components/router.tsx} +43 -74
  36. package/src/client/services/router/index.tsx +448 -0
  37. package/src/client/services/router/request/api.ts +229 -0
  38. package/src/client/{router → services/router}/request/history.ts +0 -0
  39. package/src/client/services/router/request/index.ts +52 -0
  40. package/src/client/services/router/response/index.tsx +107 -0
  41. package/src/client/services/router/response/page.ts +95 -0
  42. package/src/client/{context/socket.ts → services/socket/index.ts} +2 -2
  43. package/src/client/utils/dom.ts +1 -1
  44. package/src/common/app/index.ts +9 -0
  45. package/src/common/data/chaines/index.ts +9 -6
  46. package/src/common/data/input/validate.ts +3 -166
  47. package/src/common/data/objets.ts +25 -0
  48. package/src/common/data/tableaux.ts +8 -0
  49. package/src/common/errors/index.ts +3 -1
  50. package/src/common/router/index.ts +67 -88
  51. package/src/common/router/layouts.ts +50 -0
  52. package/src/common/router/register.ts +62 -0
  53. package/src/common/router/request/api.ts +72 -0
  54. package/src/common/router/request/index.ts +31 -0
  55. package/src/common/router/{response.ts → response/index.ts} +9 -13
  56. package/src/common/router/response/page.ts +40 -56
  57. package/src/common/validation/index.ts +3 -0
  58. package/src/common/validation/schema.ts +184 -0
  59. package/src/common/validation/validator.ts +88 -0
  60. package/src/common/validation/validators.ts +313 -0
  61. package/src/server/app/config.ts +9 -27
  62. package/src/server/app/index.ts +81 -124
  63. package/src/server/app/service.ts +98 -0
  64. package/src/server/app.tsconfig.json +0 -8
  65. package/src/server/error/index.ts +13 -0
  66. package/src/server/index.ts +5 -0
  67. package/src/server/patch.ts +0 -6
  68. package/src/server/{data/Cache.ts → services/cache/index.ts} +79 -47
  69. package/src/server/services/console/bugReporter.ts +26 -16
  70. package/src/server/services/console/index.ts +59 -51
  71. package/src/server/services/cron/index.ts +12 -26
  72. package/src/server/services/database/bucket.ts +40 -0
  73. package/src/server/services/database/connection.ts +206 -75
  74. package/src/server/services/database/datatypes.ts +63 -40
  75. package/src/server/services/database/index.ts +295 -272
  76. package/src/server/services/database/metas.ts +246 -135
  77. package/src/server/services/database/stats.ts +151 -126
  78. package/src/server/services/email/index.ts +28 -52
  79. package/src/server/services/{router/request/services → metrics}/detect.ts +8 -10
  80. package/src/server/services/{router/request/services/tracking.ts → metrics/index.ts} +68 -45
  81. package/src/server/services/{http → router/http}/index.ts +28 -70
  82. package/src/server/services/{http → router/http}/multipart.ts +0 -0
  83. package/src/server/services/{http → router/http}/session.ts.old +0 -0
  84. package/src/server/services/router/index.ts +273 -203
  85. package/src/server/services/router/request/api.ts +73 -0
  86. package/src/server/services/router/request/index.ts +16 -97
  87. package/src/server/services/router/request/service.ts +21 -0
  88. package/src/server/services/router/response/index.ts +125 -64
  89. package/src/server/services/router/response/{filter → mask}/Filter.ts +0 -0
  90. package/src/server/services/router/response/{filter → mask}/index.ts +0 -2
  91. package/src/server/services/router/response/{filter → mask}/selecteurs.ts +0 -0
  92. package/src/server/services/router/response/page/document.tsx +194 -0
  93. package/src/server/services/router/response/page/index.tsx +157 -0
  94. package/src/server/{libs/pages → services/router/response/page}/schemaGenerator.ts +0 -0
  95. package/src/server/services/router/service.ts +48 -0
  96. package/src/server/services/schema/index.ts +47 -0
  97. package/src/server/services/schema/request.ts +55 -0
  98. package/src/server/services/schema/router.ts +33 -0
  99. package/src/server/services/socket/index.ts +38 -43
  100. package/src/server/services/socket/scope.ts +6 -4
  101. package/src/server/services/users/index.ts +203 -0
  102. package/src/server/services/{auth/base.ts → users/old.ts} +28 -112
  103. package/src/server/services/users/router/index.ts +72 -0
  104. package/src/server/services/users/router/request.ts +49 -0
  105. package/src/types/aliases.d.ts +43 -2
  106. package/templates/composant.tsx +1 -1
  107. package/templates/modal.tsx +1 -1
  108. package/templates/page.tsx +1 -1
  109. package/tsconfig.common.json +0 -4
  110. package/src/client/context/api.ts +0 -92
  111. package/src/client/context/index.ts +0 -246
  112. package/src/client/index.tsx +0 -129
  113. package/src/client/router/index.ts +0 -286
  114. package/src/client/router/request/index.ts +0 -106
  115. package/src/client/router/response/index.ts +0 -38
  116. package/src/client/router/route.ts +0 -75
  117. package/src/common/data/input/validators/basic.ts +0 -299
  118. package/src/common/data/input/validators/build.ts +0 -63
  119. package/src/common/router/request.ts +0 -83
  120. package/src/server/data/ApiClient.ts +0 -119
  121. package/src/server/data/input.ts +0 -41
  122. package/src/server/libs/pages/document.static.tsx +0 -41
  123. package/src/server/libs/pages/document.tsx +0 -203
  124. package/src/server/libs/pages/render.tsx +0 -90
  125. package/src/server/routes/auth.ts +0 -151
  126. package/src/server/services/redis/index.ts +0 -71
  127. package/src/server/services/router/request/services/auth.ts +0 -177
@@ -9,48 +9,53 @@ import fs from 'fs-extra';
9
9
 
10
10
  // Core
11
11
  import ConfigParser, { TEnvConfig } from './config';
12
+ import { default as Service, AnyService } from './service';
13
+ import type { default as Router, Request as ServerRequest } from '@server/services/router';
12
14
 
13
15
  /*----------------------------------
14
16
  - TYPES
15
17
  ----------------------------------*/
16
18
 
17
- type THookName = 'ready' | 'cleanup' | 'error'
18
- type THook = () => Promise<void>;
19
+ export { default as Service, TPriority } from './service';
19
20
 
20
- type TServiceOptions = {
21
- instanciate: boolean
22
- }
21
+ type Config = {
23
22
 
24
- abstract class AsyncService {
25
- public abstract loading: Promise<void> | undefined;
26
- public abstract load: () => Promise<void>;
27
- }
28
- abstract class Service {
29
- public abstract load: () => void;
30
23
  }
31
24
 
32
- interface TServiceClass {
33
- new(): AsyncService | Service;
25
+ type Hooks = {
26
+ ready: {
27
+ args: [],
28
+ },
29
+ cleanup: {
30
+ args: [],
31
+ },
32
+ error: {
33
+ args: [error: Error, request?: ServerRequest<Router>],
34
+ }
34
35
  }
35
36
 
36
37
  declare global {
37
- namespace Core {
38
- interface Services { }
38
+
39
+ //interface Services { }
40
+
41
+ interface AppHooks {
42
+
39
43
  }
44
+
40
45
  interface User { }
41
46
  }
42
47
 
43
- const servicesObj = {}
44
-
45
48
  /*----------------------------------
46
49
  - FUNCTIONS
47
50
  ----------------------------------*/
48
- export class App {
51
+ export default abstract class Application extends Service<Config, Hooks, /* TODO: this ? */Application> {
49
52
 
50
53
  /*----------------------------------
51
54
  - PROPERTIES
52
55
  ----------------------------------*/
53
56
 
57
+ public side = 'server' as 'server';
58
+
54
59
  // Context
55
60
  public hmr: __WebpackModuleApi.Hot | undefined = module.hot;
56
61
 
@@ -65,143 +70,101 @@ export class App {
65
70
 
66
71
  // Status
67
72
  public launched: boolean = false;
68
- public loading: Promise<void>[] = [];
69
73
  public status = {
70
74
  services: false
71
75
  }
72
76
 
73
- public services: Core.Services = new Proxy( servicesObj, {
74
- get: (container, serviceId, receiver) => {
75
-
76
- if (!( serviceId in container ) && typeof serviceId === 'string')
77
- throw new Error(`Service not loaded: ${serviceId}`);
78
-
79
- return container[serviceId];
80
- }
81
- }) as Core.Services;
82
-
83
- public hooks: {[name in THookName]: THook[]} = {
84
- ready: [],
85
- cleanup: [],
86
- error: []
87
- }
77
+ private servicesList: AnyService[] = []
88
78
 
89
79
  /*----------------------------------
90
80
  - INIT
91
81
  ----------------------------------*/
92
82
 
83
+ public env: TEnvConfig;
84
+ public abstract identity: Config.Identity;
85
+
93
86
  public constructor() {
94
87
 
88
+ // @ts-ignore: can't pass this to super
89
+ super();
90
+
95
91
  // Gestion crash
96
92
  process.on('unhandledRejection', (error: any, promise: any) => {
97
93
 
98
94
  console.error("Unhandled promise rejection:", error);
99
-
100
- // Send email report
101
- if (this.isLoaded('console'))
102
- $.console.bugReport.server(error);
103
- else
104
- console.error(`Unable to send bug report: console service not loaded.`);
95
+ this.runHook('error', error);
105
96
 
106
97
  });
107
98
 
108
99
  // Load config files
109
100
  const configParser = new ConfigParser( this.path.root );
110
101
  this.env = configParser.env();
111
- this.identity = configParser.identity();
112
- }
113
-
114
- // Configs
115
- public config!: Core.Config.Services;
116
- public identity: Core.Config.Identity;
117
- public env: TEnvConfig;
118
- public configure( config: Core.Config.Services) {
119
- this.config = config;
120
- console.log("Configure services with", this.config);
121
- }
122
-
123
- // Register a service
124
- public register<TServiceName extends keyof Core.Services>(
125
- id: TServiceName,
126
- Service: TServiceClass,
127
- options: Partial<TServiceOptions> = {}
128
- ) {
129
-
130
- // Pas d'export default new Service pour chaque fichier de service,
131
- // dissuaded'importer ms service sn'importe où, ce qui créé des références circulaires
132
- console.log(`[services] Registering service ${id} ...`);
133
- const service = options.instanciate !== false ? new Service() : Service;
134
- this.services[id as string] = service;
135
-
136
- if ('load' in service) {
137
-
138
- console.log(`[services] Starting service ${id} ...`);
139
-
140
- // Lorsque service.load est async, une propriété loading doit etre présente
141
- // De façon à ce que les autres services puissent savoir quand ce service est prêt
142
- if ('loading' in service) {
143
-
144
- console.log(`[services] Waiting service ${id} to be fully loaded ...`);
145
- service.loading = service.load().then(() => {
146
- console.info(`[service] Service ${id} successfully started.`);
147
- }).catch(e => {
148
- // Bug report via email
149
- console.error(`[service] Error while starting the ${id} service:`, e);
150
- e.message = `Start ${id} service: ` + e.message;
151
- $.console.bugReport.server(e);
152
- });;
153
-
154
- this.loading.push(service.loading);
155
-
156
- } else
157
- service.load();
158
- }
159
102
  }
160
103
 
161
- // Test if a service was registered
162
- public isLoaded( id: keyof Core.Services ) {
163
- return id in this.services;
164
- }
165
-
166
- public on( name: THookName, callback: THook ) {
167
- this.hooks[ name ].push( callback );
168
- return this;
169
- }
104
+ /*----------------------------------
105
+ - REGISTER
106
+ ----------------------------------*/
170
107
 
171
- public runHook( hookName: THookName ) {
172
- console.info(`[hook] Run all ${hookName} hook (${this.hooks.ready.length}).`);
173
- return Promise.all(
174
- this.hooks.ready.map(
175
- cb => cb().catch(e => {
176
- console.error(`[hook] Error while executing hook ${hookName}:`, e);
177
- })
178
- )
179
- ).then(() => {
180
- console.info(`[hook] Hooks ${hookName} executed with success.`);
181
- })
108
+ // Require a service at file scope
109
+ // Only use in files where a service is strictly required
110
+ public use<ServiceType extends Service<{}, {}, this>>( serviceName: string ): ServiceType | undefined {
111
+ return this[ serviceName ];
182
112
  }
183
113
 
184
114
  /*----------------------------------
185
115
  - LAUNCH
186
116
  ----------------------------------*/
187
- public async launch() {
117
+
118
+ public async register() {
119
+
120
+ }
121
+
122
+ public async start() {
188
123
 
189
124
  console.info(`[boot] Waiting for all services to be ready ...`);
190
- await Promise.all( this.loading );
125
+ await this.startServices()
191
126
 
192
127
  console.info(`[boot] Launching application ...`);
193
128
  await this.runHook('ready');
194
129
 
195
- // NOTE: Useless ?
196
- /*if (this.hmr)
197
- this.activateHMR();*/
130
+ console.info(`[boot] Run application-specific boot instructions ...`);
131
+ await this.boot();
198
132
 
199
133
  console.info(`[boot] Application is ready.`);
200
-
201
134
  this.launched = true;
202
135
 
203
136
  }
204
137
 
138
+ public registerService( service: AnyService ) {
139
+ console.log(`[app] Register service`, service.constructor?.name);
140
+ this.servicesList.push(service);
141
+ }
142
+
143
+ public async startServices() {
144
+
145
+ console.log(`[app] Sorting ${this.servicesList.length} services by priority`);
146
+ this.servicesList.sort((s1, s2) => s2.priority - s1.priority);
147
+
148
+ console.log(`[app] Starting ${this.servicesList.length} services.`);
149
+ for (const service of this.servicesList) {
150
+ const serviceClassName = service.constructor?.name;
151
+ console.log(`[app] Start service`, serviceClassName);
152
+
153
+ if (service.register)
154
+ service.register();
155
+
156
+ if (service.start) {
157
+ service.started = service.start();
158
+ await service.started;
159
+ }
160
+ }
161
+
162
+ console.log(`[app] All ${this.servicesList.length} services were started.`);
163
+ }
164
+
165
+ public abstract boot(): Promise<void>;
166
+
167
+ // TODO: make it work
205
168
  private activateHMR() {
206
169
 
207
170
  if (!module.hot) return;
@@ -216,17 +179,16 @@ export class App {
216
179
  console.info(`Cleaning application ...`);
217
180
 
218
181
  // Services hooks
219
- for (const id in this.services) {
182
+ /*for (const id in this.services) {
220
183
  const service = this.services[id]
221
184
  if (service.cleanup) {
222
185
  console.info(`Cleaning ${id} service ...`);
223
186
  service.cleanup();
224
187
  }
225
- }
188
+ }*/
226
189
 
227
190
  // Application specific hooks
228
- for (const callback of this.hooks.cleanup)
229
- callback();
191
+ this.runHook('cleanup');
230
192
 
231
193
  /*
232
194
  console.log("[nettoyage] Arrêt serveur socket ...");
@@ -239,9 +201,4 @@ export class App {
239
201
  });
240
202
  }
241
203
 
242
- }
243
-
244
- const app = new App;
245
- export const services = app.services;
246
- export const $ = app.services;
247
- export default app;
204
+ }
@@ -0,0 +1,98 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ import Application from ".";
6
+
7
+ /*----------------------------------
8
+ - TYPES: OPTIONS
9
+ ----------------------------------*/
10
+
11
+ export type AnyService = Service<{}, {}, Application>
12
+
13
+ type THookCallback<THookArgs extends THookOptions> = (...args: THookArgs["args"]) => Promise<void>;
14
+
15
+ type THooksList = {
16
+ [hookName: string]: THookOptions
17
+ }
18
+
19
+ type THookOptions = {
20
+ args: any[]
21
+ }
22
+
23
+ export type TPriority = -2 | -1 | 0 | 1 | 2
24
+
25
+ /*----------------------------------
26
+ - CONFIG
27
+ ----------------------------------*/
28
+
29
+ const LogPrefix = '[service]';
30
+
31
+ /*----------------------------------
32
+ - CLASS
33
+ ----------------------------------*/
34
+ export default abstract class Service<
35
+ TConfig extends {},
36
+ THooks extends THooksList,
37
+ TApplication extends Application
38
+ > {
39
+
40
+ public priority: TPriority = 0;
41
+ public started?: Promise<void>;
42
+
43
+ public constructor(
44
+ public app: TApplication,
45
+ public config: TConfig,
46
+ ) {
47
+
48
+ if (!( this instanceof Application ))
49
+ // Make the app aware of his services
50
+ app.registerService(this);
51
+
52
+ }
53
+
54
+ public abstract register?(): Promise<void>;
55
+
56
+ public abstract start?(): Promise<void>;
57
+
58
+ /*----------------------------------
59
+ - HOOKS
60
+ ----------------------------------*/
61
+ public hooks: {[name in keyof THooks]?: THookCallback< THooks[name] >[]} = {}
62
+
63
+ public on<THookName extends keyof THooksList>(
64
+ name: THookName,
65
+ callback: THookCallback<THooksList[THookName]>
66
+ ) {
67
+
68
+ const callbacks = this.hooks[ name ];
69
+ if (callbacks)
70
+ callbacks.push( callback );
71
+ else
72
+ this.hooks[ name ] = [callback]
73
+
74
+ return this;
75
+ }
76
+
77
+ public runHook<THookName extends keyof THooksList>(
78
+ name: THookName,
79
+ ...args: THooksList[THookName]["args"]
80
+ ) {
81
+
82
+ const callbacks = this.hooks[name];
83
+ if (!callbacks)
84
+ return console.info(LogPrefix, `No ${name} hook defined in the current service instance.`);
85
+
86
+ console.info(`[hook] Run all ${name} hook (${callbacks.length}).`);
87
+ return Promise.all(
88
+ callbacks.map(
89
+ cb => cb(...args).catch(e => {
90
+ console.error(`[hook] Error while executing hook ${name}:`, e);
91
+ })
92
+ )
93
+ ).then(() => {
94
+ console.info(`[hook] Hooks ${name} executed with success.`);
95
+ })
96
+ }
97
+
98
+ }
@@ -5,18 +5,10 @@
5
5
  "baseUrl": "..",
6
6
  "paths": {
7
7
 
8
- // RAPPEL: Mettre à jour webpack externals
9
-
10
8
  "@client/*": ["../node_modules/5htp-core/src/client/*"],
11
9
  "@common/*": ["../node_modules/5htp-core/src/common/*"],
12
10
  "@server/*": ["../node_modules/5htp-core/src/server/*"],
13
11
 
14
- "@validator": ["../node_modules/5htp-core/src/server/data/input"],
15
- "@router": ["../node_modules/5htp-core/src/server/services/router"],
16
- "@errors": ["../node_modules/5htp-core/src/common/errors"],
17
-
18
- "@models": ["../server/models"],
19
-
20
12
  "@/*": ["./*"],
21
13
 
22
14
  // ATTENTION: Les références à preact doivent toujours pointer vers la même instance
@@ -0,0 +1,13 @@
1
+ export class SqlError extends Error {
2
+
3
+ public constructor(
4
+ public original: Error,
5
+ public query: string
6
+ ) {
7
+ super(original.message);
8
+
9
+ this.name = original.name;
10
+ this.message = original.message;
11
+ this.stack = original.stack;
12
+ }
13
+ }
@@ -0,0 +1,5 @@
1
+ import Application from '@/server';
2
+
3
+ const app = new Application();
4
+
5
+ app.start();
@@ -1,9 +1,3 @@
1
- const moduleAlias = require('module-alias')
2
- moduleAlias.addAliases({
3
- 'react': "preact/compat",
4
- 'react-dom': "preact/compat",
5
- })
6
-
7
1
  /*----------------------------------
8
2
  - DATES & TIMZEONE
9
3
  ----------------------------------*/
@@ -10,7 +10,7 @@ import hInterval from 'human-interval';
10
10
  import fs from 'fs-extra';
11
11
 
12
12
  // Core
13
- import app from '@server/app';
13
+ import Application, { Service } from '@server/app';
14
14
 
15
15
  // Libs
16
16
 
@@ -18,15 +18,17 @@ import app from '@server/app';
18
18
  - CONFIG
19
19
  ----------------------------------*/
20
20
 
21
- const debug = false;
21
+ const LogPrefix = '[cache]';
22
22
 
23
23
  /*----------------------------------
24
24
  - TYPES
25
25
  ----------------------------------*/
26
26
 
27
- type TPrimitiveValue = string | boolean | number | undefined | {[key: string]: TPrimitiveValue} | TPrimitiveValue[];
27
+ type TPrimitiveValue = string | boolean | number | undefined | TPrimitiveValue[] | {
28
+ [key: string]: TPrimitiveValue
29
+ }
28
30
 
29
- type TExpirationDelay = string | number | Date | null;
31
+ type TExpirationDelay = 'never' | string | number | Date;
30
32
 
31
33
  type CacheEntry = {
32
34
  // Value
@@ -35,47 +37,70 @@ type CacheEntry = {
35
37
  expiration?: number
36
38
  };
37
39
 
40
+ /*----------------------------------
41
+ - TYPES
42
+ ----------------------------------*/
43
+
44
+ export type Config = {
45
+ debug: boolean
46
+ }
47
+
48
+ export type Hooks = {
49
+
50
+ }
51
+
38
52
  /*----------------------------------
39
53
  - SERVICE
40
54
  ----------------------------------*/
41
- class Cache {
55
+ export default class Cache extends Service<Config, Hooks, Application> {
42
56
 
43
- private cacheFile = path.join(app.path.data, 'cache/mem.json');
57
+ private cacheFile = path.join(this.app.path.data, 'cache/mem.json');
44
58
 
45
59
  private data: {[key: string]: CacheEntry | undefined} = {};
46
60
 
47
61
  private changes: number = 0;
48
62
 
49
- public constructor() {
63
+ public async register() {
64
+
65
+
66
+ }
67
+
68
+ public async start() {
50
69
 
51
70
  setInterval(() => this.cleanMem(), 10000);
52
71
 
72
+ // Restore persisted data
53
73
  if (fs.existsSync(this.cacheFile))
54
74
  this.data = fs.readJSONSync(this.cacheFile)
55
75
  }
56
76
 
57
77
  private cleanMem() {
58
78
 
59
- debug && console.log("[cache] Clean memory");
60
-
79
+ // Remove expired data
61
80
  const now = Date.now();
62
81
  for (const key in this.data) {
63
82
  const entry = this.data[ key ];
64
- if (entry?.expiration && entry.expiration < now)
83
+ if (entry?.expiration && entry.expiration < now) {
84
+ this.config.debug && console.log(LogPrefix, `Delete expired data: ${key}`);
65
85
  this.del(key);
86
+ }
66
87
  }
67
88
 
68
- if (this.changes > 0)
89
+ // Write changes
90
+ if (this.changes > 0) {
69
91
  fs.outputJSONSync(this.cacheFile, this.data);
92
+ this.config.debug && console.log(LogPrefix, `Flush ${this.changes} changes`);
93
+ this.changes = 0;
94
+ }
70
95
  }
71
96
 
72
97
  // Expiration = Durée de vie en secondes ou date max
73
98
  // Retourne null quand pas de valeur
74
99
  public get<TValeur extends TPrimitiveValue>(
75
100
  cle: string,
76
- func?: (() => Promise<TValeur>),
77
- expiration?: TExpirationDelay,
78
- avecDetails?: true
101
+ func: (() => Promise<TValeur>),
102
+ expiration: TExpirationDelay,
103
+ avecDetails: true
79
104
  ): Promise<CacheEntry>;
80
105
 
81
106
  public get<TValeur extends TPrimitiveValue>(
@@ -83,47 +108,44 @@ class Cache {
83
108
  func: (() => Promise<TValeur>),
84
109
  expiration?: TExpirationDelay,
85
110
  avecDetails?: false
86
- ): Promise<null | TValeur>;
111
+ ): Promise<TValeur>;
87
112
 
88
113
  public async get<TValeur extends TPrimitiveValue>(
89
114
  cle: string,
90
- func?: (() => Promise<TValeur>),
91
- expiration?: TExpirationDelay,
115
+ func: (() => Promise<TValeur>),
116
+ expiration: TExpirationDelay = 'never',
92
117
  avecDetails?: boolean
93
- ): Promise<null | TValeur | CacheEntry> {
118
+ ): Promise<TValeur | CacheEntry> {
94
119
 
95
- let retour: CacheEntry | undefined = this.data[cle];
96
-
97
- debug && console.log(`[cache] Get "${cle}".`);
120
+ let entry: CacheEntry | undefined = this.data[cle];
98
121
 
99
122
  // Expired
100
- if (retour?.expiration && retour.expiration < Date.now()){
101
- debug && console.log(`[cache] Key ${cle} expired.`);
102
- retour = undefined;
123
+ if (entry?.expiration && entry.expiration < Date.now()){
124
+ this.config.debug && console.log(LogPrefix, `Key ${cle} expired.`);
125
+ entry = undefined;
103
126
  }
104
127
 
105
128
  // Donnée inexistante
106
- if (retour === undefined && func !== undefined) {
129
+ if (entry === undefined) {
130
+
131
+ this.config.debug && console.log(LogPrefix, `Get "${cle}": refresh value`);
107
132
 
108
133
  // Rechargement
109
- retour = {
134
+ entry = {
110
135
  value: await func(),
111
- expiration: expiration
112
- ? this.delayToTimestamp(expiration)
113
- : undefined
136
+ expiration: this.delayToTimestamp(expiration)
114
137
  }
115
138
 
116
139
  // undefined retourné = pas d'enregistrement
117
- if (retour.value !== undefined)
118
- await this.set(cle, retour, expiration);
119
- }
140
+ //if (entry.value !== undefined)
141
+ await this.set(cle, entry.value, expiration);
120
142
 
121
- if (retour === undefined)
122
- return null;
143
+ } else
144
+ this.config.debug && console.log(LogPrefix, `Get "${cle}": restored via cache`);
123
145
 
124
146
  return avecDetails
125
- ? retour
126
- : retour.value as TValeur;
147
+ ? entry
148
+ : entry.value as TValeur;
127
149
  };
128
150
 
129
151
  /**
@@ -137,9 +159,9 @@ class Cache {
137
159
  * - null: no expiration (default)
138
160
  * @returns A void promise
139
161
  */
140
- public set( cle: string, val: TPrimitiveValue, expiration: TExpirationDelay = null ): void {
162
+ public set( cle: string, val: TPrimitiveValue, expiration: TExpirationDelay = 'never' ): void {
141
163
 
142
- debug && console.log("[cache] Updating cache " + cle);
164
+ this.config.debug && console.log(LogPrefix, "Updating cache " + cle);
143
165
  this.data[ cle ] = {
144
166
  value: val,
145
167
  expiration: this.delayToTimestamp(expiration)
@@ -157,24 +179,34 @@ class Cache {
157
179
  /*----------------------------------
158
180
  - UTILS
159
181
  ----------------------------------*/
160
- private delayToTimestamp( delay: TExpirationDelay ): number {
182
+ /**
183
+ *
184
+ * @param delay
185
+ * @returns number (timestamp when the data expired) or undefined (never expires)
186
+ */
187
+ private delayToTimestamp( delay: TExpirationDelay ): number | undefined {
188
+
189
+ if (delay === 'now') {
190
+
191
+ return Date.now();
192
+
193
+ } else if (delay === 'never') {
194
+
195
+ return undefined;
161
196
 
162
197
  // H expression
163
- if (typeof delay === 'string') {
198
+ } else if (typeof delay === 'string') {
164
199
 
165
200
  const ms = hInterval(delay);
166
201
  if (ms === undefined) throw new Error(`Invalid period string: ` + delay);
167
202
  return Date.now() + ms;
168
203
 
169
- // Via durée de vie en secondes
204
+ // Lifetime in seconds
170
205
  } else if (typeof delay === 'number')
171
206
  return Date.now() + delay;
172
- // Date limite
173
- else if (delay !== null)
174
- return delay.getTime();
207
+
208
+ // Date limit
175
209
  else
176
- return Date.now();
210
+ return delay.getTime();
177
211
  }
178
212
  }
179
-
180
- export default new Cache;