@lakutata/core 3.0.0 → 3.1.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.
Files changed (36) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +29 -22
  3. package/README.zh-CN.md +182 -0
  4. package/dist/cjs/decorators/dto/Expect.d.ts +2 -1
  5. package/dist/cjs/decorators/dto/Expect.js +2 -1
  6. package/dist/cjs/dto/PaginationSearchDTO.d.ts +6 -4
  7. package/dist/cjs/dto/PaginationSearchDTO.js +5 -4
  8. package/dist/cjs/exceptions/dto/InvalidValueException.d.ts +4 -2
  9. package/dist/cjs/exceptions/dto/InvalidValueException.js +4 -2
  10. package/dist/cjs/lib/app/Application.d.ts +22 -0
  11. package/dist/cjs/lib/app/Application.js +56 -0
  12. package/dist/cjs/lib/base/BaseObject.js +3 -1
  13. package/dist/cjs/lib/base/internal/ConfigurableRecordsInjection.d.ts +7 -7
  14. package/dist/cjs/lib/base/internal/ConfigurableRecordsInjection.js +10 -14
  15. package/dist/cjs/lib/core/Container.js +3 -2
  16. package/dist/cjs/lib/dto/DTO.d.ts +3 -1
  17. package/dist/cjs/lib/dto/DTO.js +76 -5
  18. package/dist/cjs/lib/foundation/Exception.d.ts +2 -1
  19. package/dist/cjs/lib/foundation/Exception.js +27 -7
  20. package/dist/esm/decorators/dto/Expect.js +2 -1
  21. package/dist/esm/dto/PaginationSearchDTO.js +5 -4
  22. package/dist/esm/exceptions/dto/InvalidValueException.js +4 -2
  23. package/dist/esm/lib/app/Application.js +56 -0
  24. package/dist/esm/lib/base/BaseObject.js +3 -1
  25. package/dist/esm/lib/base/internal/ConfigurableRecordsInjection.js +10 -14
  26. package/dist/esm/lib/core/Container.js +3 -2
  27. package/dist/esm/lib/dto/DTO.js +76 -5
  28. package/dist/esm/lib/foundation/Exception.js +27 -7
  29. package/dist/types/decorators/dto/Expect.d.ts +2 -1
  30. package/dist/types/dto/PaginationSearchDTO.d.ts +6 -4
  31. package/dist/types/exceptions/dto/InvalidValueException.d.ts +4 -2
  32. package/dist/types/lib/app/Application.d.ts +22 -0
  33. package/dist/types/lib/base/internal/ConfigurableRecordsInjection.d.ts +7 -7
  34. package/dist/types/lib/dto/DTO.d.ts +3 -1
  35. package/dist/types/lib/foundation/Exception.d.ts +2 -1
  36. package/package.json +28 -4
@@ -2,7 +2,6 @@ import { format as utilFormat } from 'node:util';
2
2
  import { getAppId, getAppName } from './BasicInfo.js';
3
3
  import { templating } from '@lakutata/utils/Templating';
4
4
  import { As } from '@lakutata/utils/As';
5
- import { camelCase } from '@lakutata/utils/CamelCase';
6
5
  import { DevNull } from '@lakutata/utils/DevNull';
7
6
  /**
8
7
  * The placeholders of util.format() consuming an argument
@@ -83,7 +82,8 @@ export function exceptionParams(exception) {
83
82
  * leaves its placeholder in the message; building the message never throws.
84
83
  * - `new XxxException('Order not found')`: the message as given.
85
84
  * - `new XxxException(error)`: the message of another error.
86
- * - `new XxxException()`: a message made from the class name.
85
+ * - `new XxxException()`: a message made from the words of the class name, the acronyms kept (`Order not found
86
+ * exception` for `OrderNotFoundException`, `HTTP request exception` for `HTTPRequestException`).
87
87
  *
88
88
  * The template parameters are kept for the translation of the message, see {@link exceptionParams}.
89
89
  * @example
@@ -120,9 +120,10 @@ export class Exception extends Error {
120
120
  this.statusCode = 500;
121
121
  if (b.length) {
122
122
  const template = a;
123
- const templatingArgs = b.length > 1 ? b : b[0];
123
+ //The parameters given one by one ({0}, {1}…), even a single one, or the object or array given alone
124
+ const templatingArgs = b.length > 1 || typeof b[0] !== 'object' || b[0] === null ? b : b[0];
124
125
  //Kept for the translations of the message, not an enumerable property (the errors sent are not changed)
125
- Object.defineProperty(this, PARAMS, { value: b.length > 1 || typeof b[0] !== 'object' || b[0] === null ? b : b[0], enumerable: false });
126
+ Object.defineProperty(this, PARAMS, { value: templatingArgs, enumerable: false });
126
127
  try {
127
128
  this.message = templating(template, templatingArgs, { ignoreMissing: true });
128
129
  }
@@ -143,11 +144,30 @@ export class Exception extends Error {
143
144
  }
144
145
  }
145
146
  else {
146
- //Empty message
147
- const message = camelCase(this.name).toLowerCase();
147
+ //Empty message: the words of the class name, in lower case but the acronyms
148
+ const message = this.name
149
+ .replace(/([a-z\d])([A-Z])/g, '$1 $2')
150
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
151
+ .split(/[\s_-]+/)
152
+ .filter((word) => !!word)
153
+ .map((word) => /^[A-Z\d]{2,}$/.test(word) ? word : word.toLowerCase())
154
+ .join(' ');
148
155
  this.message = `${message.charAt(0).toUpperCase()}${message.slice(1)}`;
149
156
  }
150
- this.errMsg = this.message;
157
+ //message as an accessor writing errMsg too (enumerable as the message assigned before): a subclass setting its
158
+ //message in a field initializer (run after this constructor) keeps errMsg the same, errMsg staying a data
159
+ //property (serialized, logged as it is)
160
+ let message = this.message;
161
+ Object.defineProperty(this, 'message', {
162
+ configurable: true,
163
+ enumerable: true,
164
+ get: () => message,
165
+ set: (value) => {
166
+ message = value;
167
+ As(this).errMsg = value;
168
+ }
169
+ });
170
+ this.errMsg = message;
151
171
  this.err = this.name;
152
172
  }
153
173
  /**
@@ -5,7 +5,8 @@ import { Schema } from '../../lib/validation/types/Schema.js';
5
5
  * Declare the schema of a property of a DTO class. The DTO validates the property against it when it is constructed
6
6
  * (the whole object at once: defaults applied, the keys without schema removed unless the class has an
7
7
  * `@IndexSignature`), and each time the property, or a value nested in it, is changed afterwards (the change is undone
8
- * when invalid). The schema is also part of the class schema (`MyDTO.Schema()`) used by `@Accept`, `@Return` and the
8
+ * when invalid). With `DTO.Any()`, an object of another class the property holds is given as it is, not watched: no
9
+ * change of it can be invalid. The schema is also part of the class schema (`MyDTO.Schema()`) used by `@Accept`, `@Return` and the
9
10
  * actions' input validation. Subclasses inherit the property schemas, and may declare a property again to replace its
10
11
  * schema. Ignored on a symbol property or on a class which is not a DTO.
11
12
  * @param schema The schema of the property, built with the static methods of `DTO` (`DTO.String().required()`); a
@@ -1,7 +1,8 @@
1
1
  import { DTO } from '../lib/dto/DTO.js';
2
2
  /**
3
- * The input of a paginated search: how many items to return and how many to skip. Extend it with the search criteria,
4
- * and pass the validated input to `paginator()`.
3
+ * The input of a paginated search: how many items to return and how many to skip, numbers or their text (the query
4
+ * string of a request, `?limit=20&offset=40`). Extend it with the search criteria, and pass the validated input to
5
+ * `paginator()`.
5
6
  * @example
6
7
  * ```typescript
7
8
  * import {DTO} from 'lakutata'
@@ -21,12 +22,13 @@ import { DTO } from '../lib/dto/DTO.js';
21
22
  */
22
23
  export declare class PaginationSearchDTO extends DTO {
23
24
  /**
24
- * The maximum number of items to return: an integer, 0 or more.
25
+ * The maximum number of items to return: an integer, 0 or more (its text too, as a query string gives it).
25
26
  * @default 10
26
27
  */
27
28
  limit?: number;
28
29
  /**
29
- * The number of items to skip before the first one returned: an integer, 0 or more.
30
+ * The number of items to skip before the first one returned: an integer, 0 or more (its text too, as a query string
31
+ * gives it).
30
32
  * @default 0
31
33
  */
32
34
  offset?: number;
@@ -3,9 +3,11 @@ import { Exception } from '../../lib/foundation/Exception.js';
3
3
  * Thrown when a value does not match its schema: by `DTO.validate()` and `DTO.validateAsync()`, by the DTO constructor
4
4
  * and the assignments to a DTO property, and by the creation of an object whose `@Configurable` value is invalid. Its
5
5
  * message describes the first error; its causes lead to the `SchemaValidationException` holding the details (the path
6
- * and the rule of each error). Its `errno` is `E_INVALID_VALUE`; the HTTP entrypoints answer an invalid action input with the
7
- * status 400, JSON-RPC with the error "invalid params".
6
+ * and the rule of each error). Its `errno` is `E_INVALID_VALUE` and its `statusCode` 400, a value refused being an
7
+ * error of the caller: the HTTP entrypoints answer an invalid action input with the status 400, the service transports
8
+ * send the error with it, JSON-RPC answers with the error "invalid params".
8
9
  */
9
10
  export declare class InvalidValueException extends Exception {
10
11
  errno: string | number;
12
+ readonly statusCode: number;
11
13
  }
@@ -1,4 +1,5 @@
1
1
  import { Module } from '../core/Module.js';
2
+ import { __destroy } from '../base/BaseObject.js';
2
3
  import { ApplicationConfigLoader } from './ApplicationConfigLoader.js';
3
4
  import { ApplicationOptions } from '../../options/ApplicationOptions.js';
4
5
  import { Alias } from '../core/Alias.js';
@@ -270,6 +271,20 @@ export declare class Application extends Module {
270
271
  * @protected
271
272
  */
272
273
  protected static listenShutdownSignals(signals: NodeJS.Signals[]): void;
274
+ /**
275
+ * End the launched application gracefully once nothing keeps the process busy (no server, connection or timer
276
+ * left): its exit, with the exit code the process has (`process.exitCode`, 0 by default), as with `exit()`. A
277
+ * process ending so does not wait for an exit, so the done handlers and the teardown would not run otherwise.
278
+ * @param app The launched application.
279
+ * @protected
280
+ */
281
+ protected static listenNaturalEnd(app: Application): void;
282
+ /**
283
+ * Stop ending the application with the process: it exits, or it is destroyed (disposed by a test, which leaves the
284
+ * end of the process to its runner)
285
+ * @protected
286
+ */
287
+ protected static stopListeningNaturalEnd(): void;
273
288
  /**
274
289
  * Launch the application: environment variables, aliases, options (or the database migration command given by the
275
290
  * `MIGRATION_*` environment variables), then the application created in a new root container.
@@ -328,6 +343,13 @@ export declare class Application extends Module {
328
343
  * variable.
329
344
  */
330
345
  get timezone(): string;
346
+ /**
347
+ * The framework's destroyer, see {@link Module}: a destroyed application no longer ends with the process.
348
+ * @param hooks The steps the framework's subclasses run before `destroy()`.
349
+ * @returns Resolves once the application and its objects are destroyed.
350
+ * @protected
351
+ */
352
+ protected [__destroy](...hooks: (() => Promise<void>)[]): Promise<void>;
331
353
  /**
332
354
  * The time the process has been running, in seconds (rounded down).
333
355
  */
@@ -1,18 +1,18 @@
1
1
  import { BaseObject } from '../BaseObject.js';
2
+ import { type Resolver } from '../../ioc/Resolvers.js';
2
3
  /**
3
- * Set configurable records to target constructor with its registration name
4
- * @param target
5
- * @param registrationName
4
+ * Set configurable records to a registration (the options it was declared with)
5
+ * @param registration
6
6
  * @param configurableRecords
7
7
  * @constructor
8
8
  */
9
- export declare function setConfigurableRecords<ClassConstructor extends typeof BaseObject>(target: ClassConstructor, registrationName: string | symbol, configurableRecords: Record<string, any>): void;
9
+ export declare function setConfigurableRecords(registration: Resolver<any>, configurableRecords: Record<string, any>): void;
10
10
  /**
11
- * Get configurable records from target constructor by its registration name
12
- * @param target
11
+ * Get configurable records from a registration
12
+ * @param registration
13
13
  * @constructor
14
14
  */
15
- export declare function getConfigurableRecords<ClassConstructor extends typeof BaseObject>(target: ClassConstructor, registrationName: string | symbol): Record<string, any>;
15
+ export declare function getConfigurableRecords(registration: Resolver<any> | null): Record<string, any>;
16
16
  /**
17
17
  * Set id to instance
18
18
  * @param target
@@ -45,7 +45,9 @@ export declare class DTO extends DataValidator {
45
45
  * Create a DTO from its data, validated against the class schema: the defaults applied, the keys without schema
46
46
  * removed (unless the class has an `@IndexSignature`). The instance then validates each change of a property, or
47
47
  * of a value nested in it (an array, an object): an invalid change throws an `InvalidValueException` and is
48
- * undone.
48
+ * undone. A property accepting any value (`DTO.Any()`, or an index signature `DTO.Any()`) gives the objects of
49
+ * other classes it holds as they are, since no change of theirs can be invalid: their getters, their private fields
50
+ * and their identity are those of the object itself.
49
51
  * @param props The data of the DTO. Default: no data (`{}`).
50
52
  * @throws {InvalidValueException} When the data does not match the class schema.
51
53
  */
@@ -30,7 +30,8 @@ export declare function exceptionParams(exception: Error): Record<string, unknow
30
30
  * leaves its placeholder in the message; building the message never throws.
31
31
  * - `new XxxException('Order not found')`: the message as given.
32
32
  * - `new XxxException(error)`: the message of another error.
33
- * - `new XxxException()`: a message made from the class name.
33
+ * - `new XxxException()`: a message made from the words of the class name, the acronyms kept (`Order not found
34
+ * exception` for `OrderNotFoundException`, `HTTP request exception` for `HTTPRequestException`).
34
35
  *
35
36
  * The template parameters are kept for the translation of the message, see {@link exceptionParams}.
36
37
  * @example
package/package.json CHANGED
@@ -1,7 +1,31 @@
1
1
  {
2
2
  "name": "@lakutata/core",
3
- "version": "3.0.0",
4
- "description": "Core of the lakutata IoC framework (IoC/DI, BaseObject, DTO/validation, decorators, console logger, monitor).",
3
+ "version": "3.1.1",
4
+ "description": "The core of the lakutata framework: the IoC container and dependency injection, the object model with asynchronous construction, and DTO schema validation with the API of joi.",
5
+ "keywords": [
6
+ "lakutata",
7
+ "ioc",
8
+ "dependency-injection",
9
+ "di",
10
+ "container",
11
+ "decorators",
12
+ "dto",
13
+ "validation",
14
+ "joi",
15
+ "typescript"
16
+ ],
17
+ "homepage": "https://lakutata.com/packages/core",
18
+ "bugs": {
19
+ "url": "https://github.com/lakutata/lakutata/issues",
20
+ "email": "admin@lakutata.com"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/lakutata/lakutata.git",
25
+ "directory": "packages/core"
26
+ },
27
+ "author": "alex.ma",
28
+ "license": "MIT",
5
29
  "type": "module",
6
30
  "main": "./dist/cjs/exports/Core.js",
7
31
  "types": "./dist/cjs/exports/Core.d.ts",
@@ -83,11 +107,11 @@
83
107
  "test:engine-types": "node scripts/check-engine-types.mjs"
84
108
  },
85
109
  "dependencies": {
86
- "@lakutata/utils": "3.0.0",
110
+ "@lakutata/utils": "3.1.1",
87
111
  "reflect-metadata": "0.2.2"
88
112
  },
89
113
  "engines": {
90
- "node": ">=22"
114
+ "node": "^22.13.0 || >=24.11.0"
91
115
  },
92
116
  "files": [
93
117
  "dist",