libmodulor 0.9.0 → 0.11.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 (50) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/README.md +2 -2
  3. package/dist/esm/apps/Helper/src/lib/project.js +7 -7
  4. package/dist/esm/dt/DataType.d.ts +3 -1
  5. package/dist/esm/dt/DataTypes.js +1 -0
  6. package/dist/esm/dt/base/TBoolean.d.ts +1 -0
  7. package/dist/esm/dt/base/TBoolean.js +9 -0
  8. package/dist/esm/dt/base/TInt.js +3 -0
  9. package/dist/esm/dt/base/TNumber.js +3 -0
  10. package/dist/esm/dt/base/TObject.js +3 -0
  11. package/dist/esm/dt/base/TString.js +3 -0
  12. package/dist/esm/dt/final/TDateISO8601.js +3 -0
  13. package/dist/esm/dt/final/TEmbeddedObject.d.ts +2 -0
  14. package/dist/esm/dt/final/TEmbeddedObject.js +3 -0
  15. package/dist/esm/dt/final/THostPort.d.ts +1 -0
  16. package/dist/esm/dt/final/THostPort.js +7 -0
  17. package/dist/esm/dt/final/TJSONString.js +3 -0
  18. package/dist/esm/dt/final/TJWT.js +3 -0
  19. package/dist/esm/dt/final/TTimestamp.d.ts +1 -0
  20. package/dist/esm/dt/final/TTimestamp.js +7 -0
  21. package/dist/esm/dt/final/TYear.d.ts +9 -0
  22. package/dist/esm/dt/final/TYear.js +22 -0
  23. package/dist/esm/dt/index.d.ts +1 -0
  24. package/dist/esm/dt/index.js +1 -0
  25. package/dist/esm/index.react-native-pure.d.ts +1 -0
  26. package/dist/esm/index.react-native-pure.js +1 -0
  27. package/dist/esm/index.react-web-pure.d.ts +1 -0
  28. package/dist/esm/index.react-web-pure.js +1 -0
  29. package/dist/esm/index.react.d.ts +3 -2
  30. package/dist/esm/index.react.js +3 -2
  31. package/dist/esm/target/lib/react/UCOutputFieldValueFragment.d.ts +8 -0
  32. package/dist/esm/target/lib/react/UCOutputFieldValueFragment.js +6 -0
  33. package/dist/esm/target/lib/react/useUC.d.ts +3 -4
  34. package/dist/esm/target/lib/react/useUCOR.d.ts +4 -5
  35. package/dist/esm/target/node-express-server/middlewares/AuthenticationCheckerMiddlewareBuilder.js +8 -14
  36. package/dist/esm/target/node-express-server/middlewares/PublicApiKeyCheckerMiddlewareBuilder.js +6 -12
  37. package/dist/esm/target/node-express-server/middlewares/RequestCheckerMiddlewareBuilder.js +6 -12
  38. package/dist/esm/target/node-express-server/middlewares/RequestHandlerMiddlewareBuilder.js +39 -45
  39. package/dist/esm/target/react-native-pure/UCOutputFieldValue.d.ts +5 -0
  40. package/dist/esm/target/react-native-pure/UCOutputFieldValue.js +7 -0
  41. package/dist/esm/target/react-web-pure/UCOutputFieldValue.d.ts +5 -0
  42. package/dist/esm/target/react-web-pure/UCOutputFieldValue.js +6 -0
  43. package/dist/esm/testing/impl/VitestAppTestSuiteRunner.d.ts +4 -2
  44. package/dist/esm/testing/impl/VitestAppTestSuiteRunner.js +24 -15
  45. package/dist/esm/uc/utils/rInput.js +3 -1
  46. package/dist/esm/utils/index.d.ts +0 -1
  47. package/dist/esm/utils/index.js +0 -1
  48. package/dist/esm/utils/ioc/ContainerPrinter.d.ts +5 -2
  49. package/dist/esm/utils/ioc/ContainerPrinter.js +3 -0
  50. package/package.json +15 -15
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v0.11.0 (2025-04-03)
4
+
5
+ **BREAKING**
6
+
7
+ - Remove `ContainerPrinter` : It was using internals of inversify v6. These internals are not present anymore in v7 and the maintainers were not convinced about adding something to list the bindings of a container. In prevision of the upgrade to v7, unfortunately, we remove it to keep things simple
8
+ - Upgrade to express 5 : Unless you extended `NodeExpressServerManager` and did some special stuff, this should be transparent to you. Except bumping the version to `5.1.0`, there should be nothing to do. Otherwise, check the excellent [migration guide](https://expressjs.com/en/guide/migrating-5.html)
9
+
10
+ **Fixed**
11
+
12
+ - Return early when parent data type validation is not ok
13
+ - Adjust the `npx libmodulor CreateProject` command
14
+
15
+ **Misc**
16
+
17
+ - Analyze the web bundle of `examples/supertrader` (`(cd examples/supertrader && yarn build:analyze:web)`)
18
+ - Include data-types tables in `llms.txt`
19
+ - Update UC input field when forcing the value in `rInput` (e.g. boolean set to false or array set to empty)
20
+
21
+ ## v0.10.0 (2025-03-28)
22
+
23
+ **Added**
24
+
25
+ - Introduce `UCOutputFieldValueFragment` in `target/react` and `UCOutputFieldValue` in `target/react-(native|web)-pure` to display uc values using the `fmt()` method of each data type. `TBoolean.fmt()` has been adapted to display `✔️` when `true`, instead of `true|false` which are not very user friendly in a UI
26
+ - Introduce `Year` data type
27
+
28
+ **Fixed**
29
+
30
+ - Adjust `fmt` of `THostPort` and `TTimestamp` (they shouldn't be formatted as numbers) and `tName` of `TEmbeddedObject`
31
+
32
+ **Misc**
33
+
34
+ - Update `examples/supertrader` to showcase type semantics and displaying UC output fields according to the definition
35
+ - In `target/react`, make `useUCOR` return a `Part0` always set : you can remove all the now obsolete patterns like `if (listItemsPart0)`, `listItemsPart0 &&`, `listItemsPart0?.`, `listItemsPart0!.` in your React components relying on `useUCOR`. Also expose the function signatures used by `useUC` and `useUCOR` to make them easily passable as children props
36
+ - List base and final data types directly in the documentation => https://libmodulor.c100k.eu/docs/references/data-types
37
+
3
38
  ## v0.9.0 (2025-03-20)
4
39
 
5
40
  **BREAKING**
@@ -10,7 +45,7 @@
10
45
  **Misc**
11
46
 
12
47
  - Make `buffer`, `fast-check` and `vitest` deps optional : basic uses of `libmodulor` do not require them (see https://libmodulor.c100k.eu/docs/examples/Basic as an example). You can remove them if you're not using the automated tests
13
- - Add `ListOrdersUCD` an `CancelOrderUCD` to `examples/supertrader` to showcase data fetching, aggregate building, sensitive use cases, test flows, etc.
48
+ - Add `ListOrdersUCD` and `CancelOrderUCD` to `examples/supertrader` to showcase data fetching, aggregate building, sensitive use cases, test flows, etc.
14
49
  - Remove linter config from examples (linting from repo root folder)
15
50
  - Use `debug` instead of `trace` in `ConsoleLogger`
16
51
 
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![npm version](https://img.shields.io/npm/v/libmodulor.svg?style=for-the-badge&color=blue)](https://www.npmjs.com/package/libmodulor)
4
4
  [![license](https://img.shields.io/badge/license-LGPL-green.svg?style=for-the-badge)](https://github.com/c100k/libmodulor/blob/master/LICENSE)
5
5
 
6
- An opinionated TypeScript library to create business oriented applications.
6
+ A TypeScript library to create business oriented applications.
7
7
 
8
8
  > [!WARNING]
9
9
  > The project is still in active development. Although already used in pilot projects, it's not suitable for all production scenarios yet.
@@ -23,4 +23,4 @@ If you think you can help in any way, feel free to contact me (cf. `author` in `
23
23
 
24
24
  ## ⚖️ License
25
25
 
26
- [LGPL-3.0](https://github.com/c100k/libmodulor/blob/v0.9.0/LICENSE)
26
+ [LGPL-3.0](https://github.com/c100k/libmodulor/blob/v0.11.0/LICENSE)
@@ -42,18 +42,18 @@ export const PACKAGE_JSON = (name) => `{
42
42
  },
43
43
  "devDependencies": {
44
44
  "@biomejs/biome": "^1.9.4",
45
- "@types/node": "^22.13.10",
46
- "@vitest/coverage-v8": "^3.0.8",
45
+ "@types/node": "^22.13.14",
46
+ "@vitest/coverage-v8": "^3.0.9",
47
47
  "buffer": "^6.0.3",
48
48
  "cookie-parser": "^1.4.7",
49
- "express": "^4.21.2",
49
+ "express": "^5.1.0",
50
50
  "express-fileupload": "^1.5.1",
51
- "fast-check": "^4.0.0",
52
- "helmet": "^8.0.0",
51
+ "fast-check": "^4.0.1",
52
+ "helmet": "^8.1.0",
53
53
  "jose": "^6.0.10",
54
54
  "typescript": "^5.8.2",
55
- "vite": "^6.2.2",
56
- "vitest": "^3.0.8"
55
+ "vite": "^6.2.3",
56
+ "vitest": "^3.0.9"
57
57
  }
58
58
  }
59
59
  `;
@@ -63,6 +63,8 @@ import type { URL } from './final/TURL.js';
63
63
  import type { URLPath } from './final/TURLPath.js';
64
64
  import type { UUID } from './final/TUUID.js';
65
65
  import type { Username } from './final/TUsername.js';
66
+ import type { Year } from './final/TYear.js';
67
+ import type { YesNo } from './final/TYesNo.js';
66
68
  type Primitive = string | number | boolean;
67
- export type DataType = Primitive | Address | Amount | ApiKey | BarCode | CSS | Color | ColorRGBA | CompanyName | CountryISO3166Alpha2 | CurrencyISO4217 | DateISO8601 | DateTimeFormat | DirPath | DomainName | Email | EmbeddedObject | Emoji | EncryptionKey | ErrorMessage | ExternalServiceId | File | FileExtension | FileMimeType | FileName | FilePath | FreeTextLong | FreeTextShort | Geolocation | GitSSHURL | HTML | HTTPContentType | HTTPMethod | HTTPStatusNumber | HostAddress | HostPort | IPv4 | IPv6 | JSONString | JWT | JavaScript | JobTitle | Markdown | NumIndex | Password | Percentage | PersonFirstname | PersonFullname | PersonInitials | PersonLastname | QRCode | SQLQuery | SSHPrivateKey | SSHPublicKey | SearchQuery | SemVerVersion | ShellCommand | Slug | Time | Timestamp | UIntDuration | UIntQuantity | URL | URLPath | UUID | Username;
69
+ export type DataType = Primitive | Address | Amount | ApiKey | BarCode | CSS | Color | ColorRGBA | CompanyName | CountryISO3166Alpha2 | CurrencyISO4217 | DateISO8601 | DateTimeFormat | DirPath | DomainName | Email | EmbeddedObject | Emoji | EncryptionKey | ErrorMessage | ExternalServiceId | File | FileExtension | FileMimeType | FileName | FilePath | FreeTextLong | FreeTextShort | Geolocation | GitSSHURL | HTML | HTTPContentType | HTTPMethod | HTTPStatusNumber | HostAddress | HostPort | IPv4 | IPv6 | JSONString | JWT | JavaScript | JobTitle | Markdown | NumIndex | Password | Percentage | PersonFirstname | PersonFullname | PersonInitials | PersonLastname | QRCode | SQLQuery | SSHPrivateKey | SSHPublicKey | SearchQuery | SemVerVersion | ShellCommand | Slug | Time | Timestamp | UIntDuration | UIntQuantity | URL | URLPath | UUID | Username | Year | YesNo;
68
70
  export {};
@@ -64,5 +64,6 @@ export const DataTypes = [
64
64
  'URLPath',
65
65
  'UUID',
66
66
  'Username',
67
+ 'Year',
67
68
  'YesNo',
68
69
  ];
@@ -5,6 +5,7 @@ export declare class TBoolean extends TBase<boolean> {
5
5
  tName(): TName;
6
6
  assign(raw: unknown): this;
7
7
  example(): boolean;
8
+ fmt(ifNullOrUndefined?: string | undefined): string;
8
9
  htmlInputType(): HTMLInputType;
9
10
  validate(): Validation;
10
11
  }
@@ -24,11 +24,20 @@ export class TBoolean extends TBase {
24
24
  example() {
25
25
  return true;
26
26
  }
27
+ fmt(ifNullOrUndefined) {
28
+ if (typeof this.raw !== 'boolean' || this.raw !== true) {
29
+ return super.fmt(ifNullOrUndefined);
30
+ }
31
+ return '✔️';
32
+ }
27
33
  htmlInputType() {
28
34
  return 'checkbox';
29
35
  }
30
36
  validate() {
31
37
  const validation = super.validate();
38
+ if (!validation.isOK()) {
39
+ return validation;
40
+ }
32
41
  if (typeof this.raw !== 'boolean') {
33
42
  validation.add({
34
43
  constraint: 'type',
@@ -45,6 +45,9 @@ export class TInt extends TNumber {
45
45
  }
46
46
  validate() {
47
47
  const validation = super.validate();
48
+ if (!validation.isOK()) {
49
+ return validation;
50
+ }
48
51
  if (typeof this.raw === 'number' && this.raw.toString().includes('.')) {
49
52
  validation.add({
50
53
  constraint: 'type',
@@ -60,6 +60,9 @@ export class TNumber extends TBase {
60
60
  }
61
61
  validate() {
62
62
  const validation = super.validate();
63
+ if (!validation.isOK()) {
64
+ return validation;
65
+ }
63
66
  if (typeof this.raw !== 'number' || Number.isNaN(this.raw)) {
64
67
  validation.add({
65
68
  constraint: 'type',
@@ -39,6 +39,9 @@ export class TObject extends TBase {
39
39
  }
40
40
  validate() {
41
41
  const validation = super.validate();
42
+ if (!validation.isOK()) {
43
+ return validation;
44
+ }
42
45
  // typeof this.raw is 'object', hence the check for nullity
43
46
  if (this.raw === null || typeof this.raw !== 'object') {
44
47
  validation.add({
@@ -27,6 +27,9 @@ export class TString extends TBase {
27
27
  }
28
28
  validate() {
29
29
  const validation = super.validate();
30
+ if (!validation.isOK()) {
31
+ return validation;
32
+ }
30
33
  if (typeof this.raw !== 'string') {
31
34
  validation.add({
32
35
  constraint: 'type',
@@ -17,6 +17,9 @@ export class TDateISO8601 extends TString {
17
17
  }
18
18
  validate() {
19
19
  const validation = super.validate();
20
+ if (!validation.isOK()) {
21
+ return validation;
22
+ }
20
23
  const parsed = Date.parse(this.raw);
21
24
  if (Number.isNaN(parsed)) {
22
25
  validation.add({
@@ -1,6 +1,8 @@
1
+ import type { TName } from '../base/TBase.js';
1
2
  import { TObject, type TObjectConstraints } from '../base/TObject.js';
2
3
  export type EmbeddedObject<E extends object = object> = E;
3
4
  export declare class TEmbeddedObject<E extends object = object> extends TObject<EmbeddedObject<E>> {
4
5
  protected constraints: TObjectConstraints;
5
6
  constructor(constraints?: TObjectConstraints);
7
+ tName(): TName;
6
8
  }
@@ -7,4 +7,7 @@ export class TEmbeddedObject extends TObject {
7
7
  super(constraints);
8
8
  this.constraints = constraints;
9
9
  }
10
+ tName() {
11
+ return 'EmbeddedObject';
12
+ }
10
13
  }
@@ -5,4 +5,5 @@ export declare class THostPort extends TUInt<HostPort> {
5
5
  constructor(constraints?: TUIntConstraints);
6
6
  tName(): TName;
7
7
  example(): HostPort;
8
+ fmt(ifNullOrUndefined?: string | undefined): string;
8
9
  }
@@ -12,4 +12,11 @@ export class THostPort extends TUInt {
12
12
  example() {
13
13
  return 443;
14
14
  }
15
+ fmt(ifNullOrUndefined) {
16
+ if (typeof this.raw !== 'number') {
17
+ return super.fmt(ifNullOrUndefined);
18
+ }
19
+ // Not using `fmtNumber` to avoid weird formatting (e.g. comma used as a thousands separator)
20
+ return this.raw.toString();
21
+ }
15
22
  }
@@ -11,6 +11,9 @@ export class TJSONString extends TString {
11
11
  }
12
12
  validate() {
13
13
  const validation = super.validate();
14
+ if (!validation.isOK()) {
15
+ return validation;
16
+ }
14
17
  try {
15
18
  JSON.parse(this.raw);
16
19
  }
@@ -14,6 +14,9 @@ export class TJWT extends TString {
14
14
  }
15
15
  validate() {
16
16
  const validation = super.validate();
17
+ if (!validation.isOK()) {
18
+ return validation;
19
+ }
17
20
  try {
18
21
  const parts = this.raw.split('.');
19
22
  if (parts.length !== 3) {
@@ -4,4 +4,5 @@ export type Timestamp = UInt;
4
4
  export declare class TTimestamp extends TUInt {
5
5
  tName(): TName;
6
6
  example(): Timestamp;
7
+ fmt(ifNullOrUndefined?: string | undefined): string;
7
8
  }
@@ -6,4 +6,11 @@ export class TTimestamp extends TUInt {
6
6
  example() {
7
7
  return 1_628_359_209;
8
8
  }
9
+ fmt(ifNullOrUndefined) {
10
+ if (typeof this.raw !== 'number') {
11
+ return super.fmt(ifNullOrUndefined);
12
+ }
13
+ // Not using `fmtNumber` to avoid weird formatting (e.g. comma used as a thousands separator)
14
+ return this.raw.toString();
15
+ }
9
16
  }
@@ -0,0 +1,9 @@
1
+ import type { TName } from '../base/TBase.js';
2
+ import { TUInt, type TUIntConstraints, type UInt } from '../base/TUInt.js';
3
+ export type Year = UInt;
4
+ export declare class TYear extends TUInt<Year> {
5
+ constructor(constraints?: TUIntConstraints);
6
+ tName(): TName;
7
+ example(): Year;
8
+ fmt(ifNullOrUndefined?: string | undefined): string;
9
+ }
@@ -0,0 +1,22 @@
1
+ import { TUInt } from '../base/TUInt.js';
2
+ export class TYear extends TUInt {
3
+ constructor(constraints) {
4
+ super({
5
+ ...constraints,
6
+ min: 0,
7
+ });
8
+ }
9
+ tName() {
10
+ return 'Year';
11
+ }
12
+ example() {
13
+ return 2025;
14
+ }
15
+ fmt(ifNullOrUndefined) {
16
+ if (typeof this.raw !== 'number') {
17
+ return super.fmt(ifNullOrUndefined);
18
+ }
19
+ // Not using `fmtNumber` to avoid weird formatting (e.g. comma used as a thousands separator)
20
+ return this.raw.toString();
21
+ }
22
+ }
@@ -70,6 +70,7 @@ export { type Username, TUsername } from './final/TUsername.js';
70
70
  export { type URL, TURL } from './final/TURL.js';
71
71
  export { type URLPath, TURLPath } from './final/TURLPath.js';
72
72
  export { type UUID, TUUID } from './final/TUUID.js';
73
+ export { type Year, TYear } from './final/TYear.js';
73
74
  export { type YesNo, TYesNo } from './final/TYesNo.js';
74
75
  export type { RNInputMode } from './targets/rn.js';
75
76
  export type { HTMLInputType } from './targets/web.js';
@@ -70,6 +70,7 @@ export { TUsername } from './final/TUsername.js';
70
70
  export { TURL } from './final/TURL.js';
71
71
  export { TURLPath } from './final/TURLPath.js';
72
72
  export { TUUID } from './final/TUUID.js';
73
+ export { TYear } from './final/TYear.js';
73
74
  export { TYesNo } from './final/TYesNo.js';
74
75
  export { DataTypes } from './DataTypes.js';
75
76
  export { Validation, } from './Validation.js';
@@ -8,3 +8,4 @@ export { UCFormFieldDesc } from './target/react-native-pure/UCFormFieldDesc.js';
8
8
  export { UCFormFieldErr } from './target/react-native-pure/UCFormFieldErr.js';
9
9
  export { UCFormFieldLabel } from './target/react-native-pure/UCFormFieldLabel.js';
10
10
  export { UCFormSubmitControl } from './target/react-native-pure/UCFormSubmitControl.js';
11
+ export { UCOutputFieldValue } from './target/react-native-pure/UCOutputFieldValue.js';
@@ -8,3 +8,4 @@ export { UCFormFieldDesc } from './target/react-native-pure/UCFormFieldDesc.js';
8
8
  export { UCFormFieldErr } from './target/react-native-pure/UCFormFieldErr.js';
9
9
  export { UCFormFieldLabel } from './target/react-native-pure/UCFormFieldLabel.js';
10
10
  export { UCFormSubmitControl } from './target/react-native-pure/UCFormSubmitControl.js';
11
+ export { UCOutputFieldValue } from './target/react-native-pure/UCOutputFieldValue.js';
@@ -8,3 +8,4 @@ export { UCFormFieldDesc } from './target/react-web-pure/UCFormFieldDesc.js';
8
8
  export { UCFormFieldErr } from './target/react-web-pure/UCFormFieldErr.js';
9
9
  export { UCFormFieldLabel } from './target/react-web-pure/UCFormFieldLabel.js';
10
10
  export { UCFormSubmitControl } from './target/react-web-pure/UCFormSubmitControl.js';
11
+ export { UCOutputFieldValue } from './target/react-web-pure/UCOutputFieldValue.js';
@@ -8,3 +8,4 @@ export { UCFormFieldDesc } from './target/react-web-pure/UCFormFieldDesc.js';
8
8
  export { UCFormFieldErr } from './target/react-web-pure/UCFormFieldErr.js';
9
9
  export { UCFormFieldLabel } from './target/react-web-pure/UCFormFieldLabel.js';
10
10
  export { UCFormSubmitControl } from './target/react-web-pure/UCFormSubmitControl.js';
11
+ export { UCOutputFieldValue } from './target/react-web-pure/UCOutputFieldValue.js';
@@ -4,6 +4,7 @@ export type { UCPanelCtx, UCPanelOnDone, UCPanelOnError, UCPanelOnInit, UCPanelO
4
4
  export type { UCEntrypointTouchableProps, RenderUCEntrypointTouchable, RenderUCExecTouchable, UCExecTouchableProps, } from './target/lib/react/touchable.js';
5
5
  export { UCContainer } from './target/lib/react/UCContainer.js';
6
6
  export { UCEntrypoint } from './target/lib/react/UCEntrypoint.js';
7
+ export { type Props as UCOutputFieldValueFragmentProps, UCOutputFieldValueFragment, } from './target/lib/react/UCOutputFieldValueFragment.js';
7
8
  export { UCPanel } from './target/lib/react/UCPanel.js';
8
- export { useUC } from './target/lib/react/useUC.js';
9
- export { useUCOR } from './target/lib/react/useUCOR.js';
9
+ export { type CloneFunc, type DivertFunc, type RefillFunc, useUC, } from './target/lib/react/useUC.js';
10
+ export { type AppendFunc, type RemoveFunc, type UpdateFunc, useUCOR, } from './target/lib/react/useUCOR.js';
@@ -1,6 +1,7 @@
1
1
  export { DIContext, useDIContext, DIContextProvider, } from './target/lib/react/DIContextProvider.js';
2
2
  export { UCContainer } from './target/lib/react/UCContainer.js';
3
3
  export { UCEntrypoint } from './target/lib/react/UCEntrypoint.js';
4
+ export { UCOutputFieldValueFragment, } from './target/lib/react/UCOutputFieldValueFragment.js';
4
5
  export { UCPanel } from './target/lib/react/UCPanel.js';
5
- export { useUC } from './target/lib/react/useUC.js';
6
- export { useUCOR } from './target/lib/react/useUCOR.js';
6
+ export { useUC, } from './target/lib/react/useUC.js';
7
+ export { useUCOR, } from './target/lib/react/useUCOR.js';
@@ -0,0 +1,8 @@
1
+ import { type ReactElement } from 'react';
2
+ import type { DataType } from '../../../dt/index.js';
3
+ import type { UCOPIBase, UCOutputField } from '../../../uc/index.js';
4
+ export interface Props<OPI extends UCOPIBase, T extends DataType> {
5
+ field: UCOutputField<OPI, T>;
6
+ value: T;
7
+ }
8
+ export declare function UCOutputFieldValueFragment<OPI extends UCOPIBase, T extends DataType>({ field, value }: Props<OPI, T>): ReactElement;
@@ -0,0 +1,6 @@
1
+ import React, {} from 'react';
2
+ export function UCOutputFieldValueFragment({ field, value }) {
3
+ const { def: { type }, } = field;
4
+ type.assign(value);
5
+ return React.createElement(React.Fragment, null, type.fmt());
6
+ }
@@ -1,7 +1,7 @@
1
1
  import { type ArgsRecord, UC, type UCDef, type UCInput, type UCOPIBase } from '../../../uc/index.js';
2
- type CloneFunc<I extends UCInput | undefined = undefined, OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCOPIBase | undefined = undefined> = (i: Partial<I>) => UC<I, OPI0, OPI1>;
3
- type DivertFunc<II extends UCInput | undefined = undefined, OOPI0 extends UCOPIBase | undefined = undefined, OOPI1 extends UCOPIBase | undefined = undefined> = (siblingUCD: UCDef) => UC<II, OOPI0, OOPI1>;
4
- type RefillFunc<I extends UCInput | undefined = undefined> = (i: Partial<I>) => void;
2
+ export type CloneFunc<I extends UCInput | undefined = undefined, OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCOPIBase | undefined = undefined> = (i: Partial<I>) => UC<I, OPI0, OPI1>;
3
+ export type DivertFunc<II extends UCInput | undefined = undefined, OOPI0 extends UCOPIBase | undefined = undefined, OOPI1 extends UCOPIBase | undefined = undefined> = (siblingUCD: UCDef) => UC<II, OOPI0, OOPI1>;
4
+ export type RefillFunc<I extends UCInput | undefined = undefined> = (i: Partial<I>) => void;
5
5
  /**
6
6
  * This hook provides utilities to init a use case and perform actions on it in a React way
7
7
  * @param appManifest
@@ -20,4 +20,3 @@ export declare function useUC<I extends UCInput | undefined = undefined, OPI0 ex
20
20
  refill: RefillFunc<I>;
21
21
  }
22
22
  ];
23
- export {};
@@ -1,7 +1,7 @@
1
1
  import type { UCInput, UCOPIBase, UCOutputReader, UCOutputReaderPart } from '../../../uc/index.js';
2
- type AppendFunc<OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCOPIBase | undefined = undefined> = (ucor: UCOutputReader<any, OPI0, OPI1>) => void;
3
- type RemoveFunc<OPI extends UCOPIBase> = (item: OPI) => void;
4
- type UpdateFunc<OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCOPIBase | undefined = undefined> = (ucor: UCOutputReader<any, OPI0, OPI1>) => void;
2
+ export type AppendFunc<OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCOPIBase | undefined = undefined> = (ucor: UCOutputReader<any, OPI0, OPI1>) => void;
3
+ export type RemoveFunc<OPI extends UCOPIBase> = (item: OPI) => void;
4
+ export type UpdateFunc<OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCOPIBase | undefined = undefined> = (ucor: UCOutputReader<any, OPI0, OPI1>) => void;
5
5
  /**
6
6
  * This hook provides utilities to act on a {@link UCOutputReader} in a React way
7
7
  *
@@ -18,7 +18,7 @@ type UpdateFunc<OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCO
18
18
  * @returns
19
19
  */
20
20
  export declare function useUCOR<I extends UCInput | undefined = undefined, OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCOPIBase | undefined = undefined>(ucor: UCOutputReader<I, OPI0, OPI1>): [
21
- UCOutputReaderPart<NonNullable<OPI0>> | undefined,
21
+ UCOutputReaderPart<NonNullable<OPI0>>,
22
22
  UCOutputReaderPart<NonNullable<OPI1>> | undefined,
23
23
  {
24
24
  append0: AppendFunc<OPI0, OPI1>;
@@ -29,4 +29,3 @@ export declare function useUCOR<I extends UCInput | undefined = undefined, OPI0
29
29
  update1: UpdateFunc<OPI0, OPI1>;
30
30
  }
31
31
  ];
32
- export {};
@@ -39,21 +39,15 @@ let AuthenticationCheckerMiddlewareBuilder = class AuthenticationCheckerMiddlewa
39
39
  auth: null,
40
40
  def: ucd,
41
41
  });
42
- try {
43
- const { auth } = await this.authenticationChecker.exec({
44
- authCookie,
45
- authorizationHeader,
46
- uc,
47
- });
48
- if (auth) {
49
- req.auth = auth;
50
- }
51
- nextFn();
52
- }
53
- catch (err) {
54
- // Always catch otherwise it breaks the middleware chain and hangs forever
55
- nextFn(err);
42
+ const { auth } = await this.authenticationChecker.exec({
43
+ authCookie,
44
+ authorizationHeader,
45
+ uc,
46
+ });
47
+ if (auth) {
48
+ req.auth = auth;
56
49
  }
50
+ nextFn();
57
51
  };
58
52
  }
59
53
  };
@@ -26,18 +26,12 @@ let PublicApiKeyCheckerMiddlewareBuilder = class PublicApiKeyCheckerMiddlewareBu
26
26
  }
27
27
  exec({ checkType }) {
28
28
  return async (req, _res, nextFn) => {
29
- try {
30
- const value = req.header(this.s().server_public_api_key_header_name);
31
- await this.publicApiKeyChecker.exec({
32
- checkType,
33
- value,
34
- });
35
- nextFn();
36
- }
37
- catch (err) {
38
- // Always catch otherwise it breaks the middleware chain and hangs forever
39
- nextFn(err);
40
- }
29
+ const value = req.header(this.s().server_public_api_key_header_name);
30
+ await this.publicApiKeyChecker.exec({
31
+ checkType,
32
+ value,
33
+ });
34
+ nextFn();
41
35
  };
42
36
  }
43
37
  };
@@ -19,18 +19,12 @@ let RequestCheckerMiddlewareBuilder = class RequestCheckerMiddlewareBuilder {
19
19
  }
20
20
  exec(_input) {
21
21
  return (req, _res, nextFn) => {
22
- try {
23
- this.requestChecker.exec({
24
- secure: req.secure,
25
- url: req.url,
26
- xForwardedProtoHeader: req.get('X-Forwarded-Proto'),
27
- });
28
- nextFn();
29
- }
30
- catch (err) {
31
- // Always catch otherwise it breaks the middleware chain and hangs forever
32
- nextFn(err);
33
- }
22
+ this.requestChecker.exec({
23
+ secure: req.secure,
24
+ url: req.url,
25
+ xForwardedProtoHeader: req.get('X-Forwarded-Proto'),
26
+ });
27
+ nextFn();
34
28
  };
35
29
  }
36
30
  };
@@ -29,56 +29,50 @@ let RequestHandlerMiddlewareBuilder = class RequestHandlerMiddlewareBuilder {
29
29
  };
30
30
  }
31
31
  exec({ appManifest, envelope, ucd, ucManager, }) {
32
- return async (req, res, nextFn) => {
33
- try {
34
- const uc = this.ucBuilder.exec({
35
- appManifest,
36
- auth: null,
37
- def: ucd,
38
- });
39
- if (isReqAuthenticated(req)) {
40
- uc.auth = req.auth;
32
+ return async (req, res) => {
33
+ const uc = this.ucBuilder.exec({
34
+ appManifest,
35
+ auth: null,
36
+ def: ucd,
37
+ });
38
+ if (isReqAuthenticated(req)) {
39
+ uc.auth = req.auth;
40
+ }
41
+ this.fillUCFromReq(req, envelope, uc);
42
+ const output = await ucManager.execServer(uc);
43
+ const { ext, io } = ucd;
44
+ const sideEffects = io.o?.sideEffects;
45
+ if (sideEffects) {
46
+ const ucor = new UCOutputReader(uc.def, output ?? undefined);
47
+ let item;
48
+ if (ucor.canItem00()) {
49
+ item = ucor.item00().item;
41
50
  }
42
- this.fillUCFromReq(req, envelope, uc);
43
- const output = await ucManager.execServer(uc);
44
- const { ext, io } = ucd;
45
- const sideEffects = io.o?.sideEffects;
46
- if (sideEffects) {
47
- const ucor = new UCOutputReader(uc.def, output ?? undefined);
48
- let item;
49
- if (ucor.canItem00()) {
50
- item = ucor.item00().item;
51
- }
52
- // Be careful with this, as some are incompatible.
53
- // For instance, if there is a REDIRECT and then a CLEAR_AUTH, the latter won't be executed as we return after the redirect.
54
- for (const se of sideEffects) {
55
- const { type } = se;
56
- switch (type) {
57
- case UCOutputSideEffectType.CLEAR_AUTH:
58
- await this.handleClearAuth(res);
59
- break;
60
- case UCOutputSideEffectType.REDIRECT:
61
- await this.handleRedirect(res, item);
62
- return;
63
- case UCOutputSideEffectType.SET_AUTH:
64
- await this.handleSetAuth(res, item);
65
- break;
66
- default:
67
- ((_) => { })(type);
68
- }
51
+ // Be careful with this, as some are incompatible.
52
+ // For instance, if there is a REDIRECT and then a CLEAR_AUTH, the latter won't be executed as we return after the redirect.
53
+ for (const se of sideEffects) {
54
+ const { type } = se;
55
+ switch (type) {
56
+ case UCOutputSideEffectType.CLEAR_AUTH:
57
+ await this.handleClearAuth(res);
58
+ break;
59
+ case UCOutputSideEffectType.REDIRECT:
60
+ await this.handleRedirect(res, item);
61
+ return;
62
+ case UCOutputSideEffectType.SET_AUTH:
63
+ await this.handleSetAuth(res, item);
64
+ break;
65
+ default:
66
+ ((_) => { })(type);
69
67
  }
70
68
  }
71
- if (!output) {
72
- res.status(204).send();
73
- return;
74
- }
75
- const transform = ext?.http?.transform;
76
- res.send(output && transform ? transform(output) : output);
77
69
  }
78
- catch (err) {
79
- // Always catch otherwise it breaks the middleware chain and hangs forever
80
- nextFn(err);
70
+ if (!output) {
71
+ res.status(204).send();
72
+ return;
81
73
  }
74
+ const transform = ext?.http?.transform;
75
+ res.send(output && transform ? transform(output) : output);
82
76
  };
83
77
  }
84
78
  fillUCFromReq(req, envelope, uc) {
@@ -0,0 +1,5 @@
1
+ import { type ReactElement } from 'react';
2
+ import type { DataType } from '../../dt/index.js';
3
+ import type { UCOPIBase } from '../../uc/index.js';
4
+ import { type Props } from '../lib/react/UCOutputFieldValueFragment.js';
5
+ export declare function UCOutputFieldValue<OPI extends UCOPIBase, T extends DataType>(props: Props<OPI, T>): ReactElement;
@@ -0,0 +1,7 @@
1
+ import React, {} from 'react';
2
+ import { Text } from 'react-native';
3
+ import { UCOutputFieldValueFragment, } from '../lib/react/UCOutputFieldValueFragment.js';
4
+ export function UCOutputFieldValue(props) {
5
+ return (React.createElement(Text, null,
6
+ React.createElement(UCOutputFieldValueFragment, { ...props })));
7
+ }
@@ -0,0 +1,5 @@
1
+ import { type ReactElement } from 'react';
2
+ import type { DataType } from '../../dt/index.js';
3
+ import type { UCOPIBase } from '../../uc/index.js';
4
+ import { type Props } from '../lib/react/UCOutputFieldValueFragment.js';
5
+ export declare function UCOutputFieldValue<OPI extends UCOPIBase, T extends DataType>(props: Props<OPI, T>): ReactElement;
@@ -0,0 +1,6 @@
1
+ import React, {} from 'react';
2
+ import { UCOutputFieldValueFragment, } from '../lib/react/UCOutputFieldValueFragment.js';
3
+ export function UCOutputFieldValue(props) {
4
+ return (React.createElement("span", null,
5
+ React.createElement(UCOutputFieldValueFragment, { ...props })));
6
+ }
@@ -1,9 +1,11 @@
1
1
  import type { FilePath } from '../../dt/index.js';
2
- import type { FSManager } from '../../std/index.js';
2
+ import type { FSManager, Logger, ShellCommandExecutor } from '../../std/index.js';
3
3
  import type { AppTestSuiteRunner, Input } from '../workers/AppTestSuiteRunner.js';
4
4
  export declare class VitestAppTestSuiteRunner implements AppTestSuiteRunner {
5
5
  private fsManager;
6
- constructor(fsManager: FSManager);
6
+ private logger;
7
+ private shellCommandExecutor;
8
+ constructor(fsManager: FSManager, logger: Logger, shellCommandExecutor: ShellCommandExecutor);
7
9
  exec({ appPath, skipCoverage, updateSnapshots, }: Input): Promise<void>;
8
10
  coverageReportEntrypointPath(appPath: FilePath): Promise<FilePath>;
9
11
  private coverageReportPath;
@@ -11,29 +11,36 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
11
  return function (target, key) { decorator(target, key, paramIndex); }
12
12
  };
13
13
  import { inject, injectable } from 'inversify';
14
- import { createVitest } from 'vitest/node';
15
14
  import { APP_TEST_DIR_NAME, APP_TEST_REPORTS_DIR_NAME, } from '../../convention.js';
16
15
  let VitestAppTestSuiteRunner = class VitestAppTestSuiteRunner {
17
16
  fsManager;
18
- constructor(fsManager) {
17
+ logger;
18
+ shellCommandExecutor;
19
+ constructor(fsManager, logger, shellCommandExecutor) {
19
20
  this.fsManager = fsManager;
21
+ this.logger = logger;
22
+ this.shellCommandExecutor = shellCommandExecutor;
20
23
  }
21
24
  async exec({ appPath, skipCoverage, updateSnapshots, }) {
22
25
  const testPath = this.fsManager.path(appPath, APP_TEST_DIR_NAME);
23
- const excludePath = this.fsManager.path(testPath);
24
- const runner = await createVitest('test', {
25
- coverage: {
26
- enabled: !skipCoverage,
27
- exclude: [excludePath],
28
- include: [appPath],
29
- reportsDirectory: this.coverageReportPath(appPath),
30
- reportOnFailure: false,
26
+ const args = [
27
+ 'run',
28
+ '--dir',
29
+ './examples/apps/Spotify',
30
+ ];
31
+ if (!skipCoverage) {
32
+ args.push('--coverage.enabled', '--coverage.exclude', testPath, '--coverage.include', appPath, '--coverage.reportsDirectory', this.coverageReportPath(appPath));
33
+ }
34
+ if (updateSnapshots) {
35
+ args.push('--update');
36
+ }
37
+ const output = await this.shellCommandExecutor.exec({
38
+ bin: './node_modules/.bin/vitest',
39
+ opts: {
40
+ args,
31
41
  },
32
- dir: appPath,
33
- update: updateSnapshots,
34
- watch: false,
35
42
  });
36
- await runner.start();
43
+ this.logger.info(output);
37
44
  }
38
45
  async coverageReportEntrypointPath(appPath) {
39
46
  return this.fsManager.path(this.coverageReportPath(appPath), 'index.html');
@@ -46,6 +53,8 @@ let VitestAppTestSuiteRunner = class VitestAppTestSuiteRunner {
46
53
  VitestAppTestSuiteRunner = __decorate([
47
54
  injectable(),
48
55
  __param(0, inject('FSManager')),
49
- __metadata("design:paramtypes", [Object])
56
+ __param(1, inject('Logger')),
57
+ __param(2, inject('ShellCommandExecutor')),
58
+ __metadata("design:paramtypes", [Object, Object, Object])
50
59
  ], VitestAppTestSuiteRunner);
51
60
  export { VitestAppTestSuiteRunner };
@@ -1,5 +1,5 @@
1
1
  import { TBoolean } from '../../dt/index.js';
2
- import { ucifIsMandatory, ucifRepeatability } from '../input-field.js';
2
+ import { UCInputFieldChangeOperator, ucifIsMandatory, ucifRepeatability, } from '../input-field.js';
3
3
  const DEFAULT_OPTS = {
4
4
  forceArrayAsEmpty: false,
5
5
  forceBooleanAsFalse: true,
@@ -24,12 +24,14 @@ export function rInput(uc, opts) {
24
24
  ucifIsMandatory(f.def) &&
25
25
  (value === null || value === undefined)) {
26
26
  value = false;
27
+ f.setValue(UCInputFieldChangeOperator.SET, value);
27
28
  }
28
29
  const [isRepeatable] = ucifRepeatability(f.def);
29
30
  if (forceArrayAsEmpty &&
30
31
  isRepeatable &&
31
32
  (value === null || value === undefined)) {
32
33
  value = [];
34
+ f.setValue(UCInputFieldChangeOperator.SET, value);
33
35
  }
34
36
  if (!ignoreUndefined || (ignoreUndefined && value !== undefined)) {
35
37
  // Useful when we get the input before persisting for example.
@@ -6,7 +6,6 @@ export { HTTPRequestBuilder } from './http/HTTPRequestBuilder.js';
6
6
  export type { HTTPDataEnvelope, HTTPReqData } from './http/types.js';
7
7
  export { bindProvider } from './ioc/bindProvider.js';
8
8
  export { CONTAINER_OPTS } from './ioc/container.js';
9
- export { ContainerPrinter } from './ioc/ContainerPrinter.js';
10
9
  export type { Class } from './ioc/types.js';
11
10
  export { fmt as fmtNumber } from './numbers/fmt.js';
12
11
  export { type CoreUnit, type SquareUnit, type SquareableUnit, type Unit, baseFromSquareUnit, isSquareUnit, } from './numbers/units.js';
@@ -2,7 +2,6 @@ export { sleep } from './async/sleep.js';
2
2
  export { HTTPRequestBuilder } from './http/HTTPRequestBuilder.js';
3
3
  export { bindProvider } from './ioc/bindProvider.js';
4
4
  export { CONTAINER_OPTS } from './ioc/container.js';
5
- export { ContainerPrinter } from './ioc/ContainerPrinter.js';
6
5
  export { fmt as fmtNumber } from './numbers/fmt.js';
7
6
  export { baseFromSquareUnit, isSquareUnit, } from './numbers/units.js';
8
7
  export { capitalize, isCapitalized } from './strings/capitalize.js';
@@ -3,10 +3,13 @@ import type { Logger, Worker } from '../../std/index.js';
3
3
  interface Input {
4
4
  container: Container;
5
5
  }
6
- export declare class ContainerPrinter implements Worker<Input, Promise<void>> {
6
+ interface Output {
7
+ bindingLines: string[];
8
+ }
9
+ export declare class ContainerPrinter implements Worker<Input, Promise<Output>> {
7
10
  private logger;
8
11
  constructor(logger: Logger);
9
- exec({ container }: Input): Promise<void>;
12
+ exec({ container }: Input): Promise<Output>;
10
13
  private symToString;
11
14
  }
12
15
  export {};
@@ -42,6 +42,9 @@ let ContainerPrinter = class ContainerPrinter {
42
42
  for (const e of sorted) {
43
43
  this.logger.debug(e);
44
44
  }
45
+ return {
46
+ bindingLines: sorted,
47
+ };
45
48
  }
46
49
  symToString(sym) {
47
50
  return typeof sym === 'string' ? sym : (sym.name ?? 'UnnamedClass');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "libmodulor",
3
- "description": "An opinionated TypeScript library to create business oriented applications",
4
- "version": "0.9.0",
3
+ "description": "A TypeScript library to create business oriented applications",
4
+ "version": "0.11.0",
5
5
  "license": "LGPL-3.0",
6
6
  "author": "Chafik H'nini <chafik.hnini@gmail.com>",
7
7
  "homepage": "https://github.com/c100k/libmodulor#readme",
@@ -75,28 +75,28 @@
75
75
  "@biomejs/biome": "^1.9.4"
76
76
  },
77
77
  "peerDependencies": {
78
- "@hono/node-server": "^1.13.8",
79
- "@modelcontextprotocol/sdk": "^1.7.0",
80
- "@stricli/core": "^1.1.1",
78
+ "@hono/node-server": "^1.14.0",
79
+ "@modelcontextprotocol/sdk": "^1.8.0",
80
+ "@stricli/core": "^1.1.2",
81
81
  "buffer": "^6.0.3",
82
82
  "cookie-parser": "^1.4.7",
83
- "express": "^4.21.2",
83
+ "express": "^5.1.0",
84
84
  "express-fileupload": "^1.5.1",
85
- "fast-check": "^4.0.0",
86
- "helmet": "^8.0.0",
87
- "hono": "^4.7.4",
85
+ "fast-check": "^4.0.1",
86
+ "helmet": "^8.1.0",
87
+ "hono": "^4.7.5",
88
88
  "inversify": "^6.2.2",
89
89
  "jose": "^6.0.10",
90
90
  "knex": "^3.1.0",
91
- "pg": "^8.14.0",
92
- "react": "^19.0.0",
93
- "react-dom": "^19.0.0",
94
- "react-native": "^0.78.0",
91
+ "pg": "^8.14.1",
92
+ "react": "^19.1.0",
93
+ "react-dom": "^19.1.0",
94
+ "react-native": "^0.78.1",
95
95
  "reflect-metadata": "^0.2.2",
96
96
  "sqlite3": "^5.1.7",
97
97
  "typescript": "^5.8.2",
98
- "vite": "^6.2.2",
99
- "vitest": "^3.0.8"
98
+ "vite": "^6.2.3",
99
+ "vitest": "^3.0.9"
100
100
  },
101
101
  "peerDependenciesMeta": {
102
102
  "@hono/node-server": {