libmodulor 0.23.0 → 0.24.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 (70) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +154 -2
  3. package/dist/esm/apps/Helper/src/lib/project.js +3 -3
  4. package/dist/esm/apps/Helper/src/ucds/TestAppUCD.d.ts +2 -1
  5. package/dist/esm/apps/Helper/src/ucds/TestAppUCD.js +8 -0
  6. package/dist/esm/convention.d.ts +3 -0
  7. package/dist/esm/convention.js +3 -0
  8. package/dist/esm/dt/DataTypes.d.ts +1 -2
  9. package/dist/esm/dt/base/TObject.js +1 -1
  10. package/dist/esm/i18n/index.d.ts +1 -1
  11. package/dist/esm/i18n/locales/de.d.ts +2 -0
  12. package/dist/esm/i18n/locales/de.js +54 -0
  13. package/dist/esm/i18n/locales/es.d.ts +2 -0
  14. package/dist/esm/i18n/locales/es.js +54 -0
  15. package/dist/esm/i18n/types.d.ts +3 -2
  16. package/dist/esm/index.babel.d.ts +1 -0
  17. package/dist/esm/index.babel.js +1 -0
  18. package/dist/esm/index.node-stricli-cli.d.ts +2 -0
  19. package/dist/esm/index.node-stricli-cli.js +2 -0
  20. package/dist/esm/index.vite.d.ts +1 -1
  21. package/dist/esm/index.vite.js +1 -1
  22. package/dist/esm/index.webpack.d.ts +1 -0
  23. package/dist/esm/index.webpack.js +1 -0
  24. package/dist/esm/std/I18nManager.d.ts +12 -0
  25. package/dist/esm/std/impl/NodeFSManager.js +1 -1
  26. package/dist/esm/std/impl/SimpleMapI18nManager.d.ts +6 -1
  27. package/dist/esm/std/impl/SimpleMapI18nManager.js +41 -12
  28. package/dist/esm/target/lib/server/AuthenticationChecker.js +1 -1
  29. package/dist/esm/target/lib/server/PublicApiKeyChecker.js +1 -1
  30. package/dist/esm/target/lib/server/ServerRequestHandler.js +2 -2
  31. package/dist/esm/target/lib/server-express/funcs.js +2 -2
  32. package/dist/esm/target/lib/server-hono/funcs.js +2 -2
  33. package/dist/esm/target/lib/server-node/funcs.js +1 -1
  34. package/dist/esm/target/node-stricli-cli/NodeStricliCLIManager.d.ts +12 -0
  35. package/dist/esm/target/node-stricli-cli/NodeStricliCLIManager.js +118 -0
  36. package/dist/esm/testing/AppTester.js +4 -5
  37. package/dist/esm/testing/UCDefASTParser.d.ts +24 -6
  38. package/dist/esm/testing/impl/SimpleAppDocsEmitter.js +4 -5
  39. package/dist/esm/testing/impl/TypeScriptLibUCDefASTParser.js +38 -11
  40. package/dist/esm/testing/impl/VitestAppTestSuiteRunner.d.ts +1 -1
  41. package/dist/esm/testing/impl/VitestAppTestSuiteRunner.js +17 -2
  42. package/dist/esm/testing/opts.js +1 -1
  43. package/dist/esm/testing/workers/AppTestSuiteRunner.d.ts +2 -0
  44. package/dist/esm/testing/workers/UCExecutor.js +1 -1
  45. package/dist/esm/testing/workers/checkers/AppI18nChecker.d.ts +8 -2
  46. package/dist/esm/testing/workers/checkers/AppI18nChecker.js +44 -2
  47. package/dist/esm/testing/workers/checkers/UCDefSourcesChecker.js +12 -12
  48. package/dist/esm/uc/impl/HTTPUCTransporter.js +2 -2
  49. package/dist/esm/uc/impl/KnexUCDataStore.js +2 -2
  50. package/dist/esm/uc/index.d.ts +0 -1
  51. package/dist/esm/uc/index.js +0 -1
  52. package/dist/esm/uc/workers/UCExecChecker.js +1 -1
  53. package/dist/esm/utils/bundling/babel/plugin.d.ts +2 -0
  54. package/dist/esm/utils/bundling/babel/plugin.js +38 -0
  55. package/dist/esm/utils/bundling/funcs.d.ts +6 -0
  56. package/dist/esm/utils/bundling/funcs.js +20 -0
  57. package/dist/esm/utils/bundling/typescript.d.ts +3 -0
  58. package/dist/esm/utils/bundling/typescript.js +61 -0
  59. package/dist/esm/utils/bundling/vite/plugin.d.ts +6 -0
  60. package/dist/esm/utils/bundling/vite/plugin.js +17 -0
  61. package/dist/esm/utils/bundling/webpack/loader.d.ts +2 -0
  62. package/dist/esm/utils/bundling/webpack/loader.js +12 -0
  63. package/dist/esm/utils/http/HTTPRequestBuilder.js +1 -1
  64. package/dist/esm/utils/ioc/bindCommon.js +1 -1
  65. package/dist/esm/utils/terminal/fmt.js +1 -1
  66. package/package.json +30 -8
  67. package/pnpm-workspace.yaml +1 -4
  68. package/tsconfig.build.examples.json +8 -0
  69. package/tsconfig.json +1 -0
  70. package/vitest.config.ts +16 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v0.24.0 (2025-12-07)
4
+
5
+ **Highlights**
6
+
7
+ - Introduced `babel`, `vite` plugins and `webpack` loader => https://libmodulor.c100k.eu/docs/guides/bundle-target
8
+ - Introduced the `node-stricli-cli` target using @bloomberg's `stricli` library
9
+ - Introduced the `Playground` example, runnable locally, to play with all the features of `libmodulor` => https://libmodulor.c100k.eu/docs/examples/Playground
10
+ - Improved apps testing by making sure non-english languages are fully translated
11
+ - Improved apps testing performance by optimizing use cases testing using the `stream` transport
12
+ - Introduced `de` 🇩🇪 and `es` 🇪🇸 locales (see them in action in the `Playground`)
13
+
14
+ Some breaking changes but at the very low level. If your apps break, just follow the TypeScript errors and you should be good to go.
15
+
16
+ Also bumped React versions for the infamous [React2Shell vulnerability](https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components).
17
+
18
+ See all the changes here : https://github.com/c100k/libmodulor/compare/v0.23.0...master
19
+
3
20
  ## v0.23.0 (2025-11-11)
4
21
 
5
22
  See all the changes here : https://github.com/c100k/libmodulor/compare/v0.22.0...master
package/README.md CHANGED
@@ -7,14 +7,166 @@ A TypeScript library to create platform-agnostic applications.
7
7
 
8
8
  ## 🚀 Getting Started
9
9
 
10
- If you're discovering `libmodulor`, we recommend reading the [📖 Documentation](https://libmodulor.c100k.eu/docs) first. You'll find everything you need to get started : Concepts, Examples and Guides.
10
+ If you're discovering `libmodulor`, we recommend reading the [📖 Documentation](https://libmodulor.c100k.eu/docs) first.
11
+ You'll find everything you need to get started : Concepts, Examples and Guides.
11
12
 
12
13
  When you're ready, [🚀 Create a project](https://libmodulor.c100k.eu/docs/guides/create-project) and build the awesome idea you have in mind.
13
14
 
15
+ In the meantime, here is how to declare the four layers of `libmodulor`.
16
+
17
+ _These snippets are extracted from the [`Basic`](https://libmodulor.c100k.eu/docs/examples/Basic) example (check out the full example to get the full picture)._
18
+
19
+ ### App
20
+
21
+ ```ts
22
+ const appManifest = {
23
+ languageCodes: ['en', 'fr'],
24
+ name: 'Event',
25
+ ucReg: {
26
+ Register: {
27
+ action: 'Create',
28
+ icon: 'user',
29
+ name: 'Register',
30
+ },
31
+ },
32
+ } satisfies AppManifest;
33
+
34
+ const appI18n: AppI18n = {
35
+ en: {
36
+ ucif_email_label: 'Your email address',
37
+ ucif_firstname_label: 'Your firstname',
38
+ ucif_lastname_label: 'Your lastname',
39
+ ucof_id_label: 'Your registration #',
40
+ ucof_ticketNumber_label: 'Your ticket #',
41
+ },
42
+ fr: {
43
+ ucif_email_label: 'Votre adresse email',
44
+ ucif_firstname_label: 'Votre prénom',
45
+ ucif_lastname_label: 'Votre nom',
46
+ ucof_id_label: "Votre # d'inscription",
47
+ ucof_ticketNumber_label: 'Votre # de ticket',
48
+ },
49
+ };
50
+ ```
51
+
52
+ ### Use Case
53
+
54
+ ```ts
55
+ interface RegisterInput extends UCInput {
56
+ email: UCInputFieldValue<Email>;
57
+ firstname: UCInputFieldValue<PersonFirstname>;
58
+ lastname: UCInputFieldValue<PersonLastname>;
59
+ }
60
+
61
+ interface RegisterOPI0 extends AggregateOPI0 {
62
+ amount: Amount;
63
+ ticketNumber: TicketNumber;
64
+ }
65
+
66
+ @injectable()
67
+ class RegisterClientMain implements UCMain<RegisterInput, RegisterOPI0> {
68
+ constructor(@inject('UCManager') private ucManager: UCManager) {}
69
+
70
+ public async exec({
71
+ uc,
72
+ }: UCMainInput<RegisterInput, RegisterOPI0>): Promise<
73
+ UCOutput<RegisterOPI0>
74
+ > {
75
+ const { aggregateId } = await this.ucManager.persist(uc);
76
+
77
+ const amount: Amount = 99.99; // Should come from some catalog in a real application
78
+ const ticketNumber: TicketNumber = 1; // Should come from a safely auto-generated sequence in a real application
79
+
80
+ return new UCOutputBuilder<RegisterOPI0>()
81
+ .add({
82
+ amount,
83
+ id: aggregateId,
84
+ ticketNumber,
85
+ })
86
+ .get();
87
+ }
88
+ }
89
+
90
+ const RegisterUCD: UCDef<RegisterInput, RegisterOPI0> = {
91
+ io: {
92
+ i: {
93
+ fields: {
94
+ email: {
95
+ type: new TEmail(),
96
+ },
97
+ firstname: {
98
+ type: new TPersonFirstname(),
99
+ },
100
+ lastname: {
101
+ type: new TPersonLastname(),
102
+ },
103
+ },
104
+ },
105
+ o: {
106
+ parts: {
107
+ _0: {
108
+ fields: {
109
+ amount: {
110
+ type: new TAmount('EUR'),
111
+ },
112
+ ticketNumber: {
113
+ type: new TTicketNumber(),
114
+ },
115
+ },
116
+ order: ['ticketNumber', 'amount', 'id'],
117
+ },
118
+ },
119
+ },
120
+ },
121
+ lifecycle: {
122
+ client: {
123
+ main: RegisterClientMain,
124
+ policy: EverybodyUCPolicy,
125
+ },
126
+ },
127
+ metadata: {
128
+ action: 'Create',
129
+ icon: 'user',
130
+ name: 'Register',
131
+ },
132
+ };
133
+ ```
134
+
135
+ ### Product
136
+
137
+ ```ts
138
+ const productManifest: ProductManifest = {
139
+ appReg: [{ name: 'Event' }],
140
+ name: 'Eventer',
141
+ };
142
+
143
+ const productI18n: ProductI18n = {
144
+ en: {
145
+ ...I18nEN,
146
+ ...appI18n.en,
147
+ },
148
+ fr: {
149
+ ...I18nFR,
150
+ ...appI18n.fr,
151
+ },
152
+ };
153
+ ```
154
+
155
+ ### Target
156
+
157
+ ```ts
158
+ const container = new Container(CONTAINER_OPTS);
159
+
160
+ bindCommon(container);
161
+ bindNodeCore(container);
162
+ bindProduct(container, productManifest, productI18n);
163
+ ```
164
+
165
+
14
166
  ## 👨‍💻 Contribute
15
167
 
16
168
  If you think you can help in any way, feel free to contact me (cf. `author` in `package.json`). I'd love to chat.
17
169
 
18
170
  ## ⚖️ License
19
171
 
20
- [LGPL-3.0](https://github.com/c100k/libmodulor/blob/v0.23.0/LICENSE)
172
+ [LGPL-3.0](https://github.com/c100k/libmodulor/blob/v0.24.0/LICENSE)
@@ -86,8 +86,8 @@ export const PACKAGE_JSON = (name) => `{
86
86
  "reflect-metadata": "^0.2.2"
87
87
  },
88
88
  "devDependencies": {
89
- "@biomejs/biome": "^2.3.4",
90
- "@types/node": "^24.10.0",
89
+ "@biomejs/biome": "^2.3.8",
90
+ "@types/node": "^24.10.1",
91
91
  "@vitest/coverage-v8": "^3.2.4",
92
92
  "buffer": "^6.0.3",
93
93
  "cookie-parser": "^1.4.7",
@@ -95,7 +95,7 @@ export const PACKAGE_JSON = (name) => `{
95
95
  "express-fileupload": "^1.5.2",
96
96
  "fast-check": "^4.3.0",
97
97
  "helmet": "^8.1.0",
98
- "jose": "^6.1.0",
98
+ "jose": "^6.1.2",
99
99
  "typescript": "^5.9.3",
100
100
  "vite": "^6.4.1",
101
101
  "vitest": "^3.2.4"
@@ -1,8 +1,9 @@
1
1
  import type { AppName } from '../../../../app/index.js';
2
- import { type UCDef, type UCInputFieldValue } from '../../../../uc/index.js';
2
+ import { type UCDef, type UCInputFieldValue, type UCName } from '../../../../uc/index.js';
3
3
  import { type AppInput } from '../lib/app.js';
4
4
  export interface TestAppInput extends AppInput {
5
5
  appName: UCInputFieldValue<AppName>;
6
+ only: UCInputFieldValue<UCName>;
6
7
  skipCoverage: UCInputFieldValue<boolean>;
7
8
  updateSnapshots: UCInputFieldValue<boolean>;
8
9
  }
@@ -34,6 +34,7 @@ let TestAppClientMain = class TestAppClientMain {
34
34
  async exec({ uc }) {
35
35
  const appsPath = uc.reqVal0('appsPath');
36
36
  const appName = uc.reqVal0('appName');
37
+ const only = uc.rVal0('only');
37
38
  const skipCoverage = uc.reqVal0('skipCoverage');
38
39
  const updateSnapshots = uc.reqVal0('updateSnapshots');
39
40
  const appPath = this.fsManager.path(appsPath, appName);
@@ -42,6 +43,7 @@ let TestAppClientMain = class TestAppClientMain {
42
43
  }
43
44
  await this.appTestSuiteRunner.exec({
44
45
  appPath,
46
+ only,
45
47
  skipCoverage,
46
48
  updateSnapshots,
47
49
  });
@@ -84,6 +86,12 @@ export const TestAppUCD = {
84
86
  appName: {
85
87
  type: new TString().setExamples([APP_NAME_PLACEHOLDER]),
86
88
  },
89
+ only: {
90
+ cardinality: {
91
+ min: 0,
92
+ },
93
+ type: new TString().setExamples(['CreatePost']),
94
+ },
87
95
  skipCoverage: {
88
96
  type: new TBoolean().setDefaultValue(false),
89
97
  },
@@ -36,6 +36,9 @@ export declare const UC_DEF_TYPE: string;
36
36
  export declare const UC_INPUT_BASE: string;
37
37
  export declare const UC_INPUT_FIELD_PATTERN: string;
38
38
  export declare const UC_INPUT_SUFFIX: string;
39
+ export declare const UC_LIFECYCLE_PROP_NAME: string;
40
+ export declare const UC_LIFECYCLE_CLIENT_PROP_NAME: string;
41
+ export declare const UC_LIFECYCLE_SERVER_PROP_NAME: string;
39
42
  export declare const UC_MAIN_SUFFIX: string;
40
43
  export declare const UC_MAIN_CLIENT_SUFFIX: string;
41
44
  export declare const UC_MAIN_SERVER_SUFFIX: string;
@@ -46,6 +46,9 @@ export const UC_DEF_TYPE = 'UCDef';
46
46
  export const UC_INPUT_BASE = 'UCInput';
47
47
  export const UC_INPUT_FIELD_PATTERN = '^UCInputFieldValue<(.*)>$';
48
48
  export const UC_INPUT_SUFFIX = 'Input';
49
+ export const UC_LIFECYCLE_PROP_NAME = 'lifecycle';
50
+ export const UC_LIFECYCLE_CLIENT_PROP_NAME = 'client';
51
+ export const UC_LIFECYCLE_SERVER_PROP_NAME = 'server';
49
52
  export const UC_MAIN_SUFFIX = 'Main';
50
53
  export const UC_MAIN_CLIENT_SUFFIX = `Client${UC_MAIN_SUFFIX}`;
51
54
  export const UC_MAIN_SERVER_SUFFIX = `Server${UC_MAIN_SUFFIX}`;
@@ -1,2 +1 @@
1
- import type { DataType } from './DataType.js';
2
- export declare const DataTypes: DataType[];
1
+ export declare const DataTypes: readonly ["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", "TransportType", "UIntDuration", "UIntQuantity", "URL", "URLPath", "UUID", "Username", "Year", "YesNo"];
@@ -65,7 +65,7 @@ export class TObject extends TBase {
65
65
  break;
66
66
  }
67
67
  default:
68
- ((_) => { })(strategy);
68
+ strategy;
69
69
  }
70
70
  }
71
71
  return validation;
@@ -1,3 +1,3 @@
1
1
  export { I18N_DEFAULT_LANG } from './consts.js';
2
- export type { I18n, I18nCoreKey, I18nCoreTranslations, I18nLanguageCode, I18nSource, I18nSourceSafe, I18nTranslation, I18nTranslationKey, } from './types.js';
2
+ export type { I18n, I18nCoreKey, I18nCoreTranslations, I18nEntry, I18nLanguageCode, I18nSource, I18nSourceSafe, I18nTranslation, I18nTranslationKey, } from './types.js';
3
3
  export { WordingManager, type WordingManagerKey } from './WordingManager.js';
@@ -0,0 +1,2 @@
1
+ import type { I18nCoreTranslations } from '../types.js';
2
+ export declare const I18nDE: I18nCoreTranslations;
@@ -0,0 +1,54 @@
1
+ export const I18nDE = {
2
+ dt_YesNo_N_desc: '',
3
+ dt_YesNo_N_label: 'Nein',
4
+ dt_YesNo_Y_desc: '',
5
+ dt_YesNo_Y_label: 'Ja',
6
+ uc_client_confirm_cancel: 'Abbrechen',
7
+ uc_client_confirm_confirm: 'Ja',
8
+ uc_client_confirm_message: '',
9
+ uc_client_confirm_title: 'Sind Sie sicher?',
10
+ uc_i_submit_changing: 'Laden',
11
+ uc_i_submit_idle: 'Speichern',
12
+ uc_i_submit_initializing: 'Laden',
13
+ uc_i_submit_submitting: 'Speichern',
14
+ validation_fieldsOr: 'Mindestens eines dieser Felder muss ausgefüllt werden: {{expected}}',
15
+ validation_format_ColorRGBA: 'Muss eine gültige RGBA-Farbe sein',
16
+ validation_format_DateISO8601: 'Muss ein gültiges Datum im ISO8601-Format sein',
17
+ validation_format_DirPath: 'Muss ein gültiger Verzeichnispfad sein',
18
+ validation_format_DomainName: 'Muss ein gültiger Domainname sein',
19
+ validation_format_Email: 'Muss eine gültige E-Mail-Adresse sein',
20
+ validation_format_FilePath: 'Muss ein gültiger Dateipfad sein',
21
+ validation_format_GitSSHURL: 'Muss eine gültige Git-SSH-URL sein',
22
+ validation_format_IPv4: 'Muss eine gültige IPv4-Adresse sein',
23
+ validation_format_IPv6: 'Muss eine gültige IPv6-Adresse sein',
24
+ validation_format_JSON: 'Muss ein gültiger JSON-String sein',
25
+ validation_format_JWT: 'Muss ein gültiges JWT sein',
26
+ validation_format_PersonFirstname: 'Muss ein gültiger Vorname sein, beginnend mit einem Großbuchstaben',
27
+ validation_format_PersonFullname: 'Muss ein gültiger vollständiger Name sein, beginnend mit einem Großbuchstaben',
28
+ validation_format_PersonInitials: 'Muss gültige Initialen aus Großbuchstaben enthalten',
29
+ validation_format_PersonLastname: 'Muss ein gültiger Nachname sein, beginnend mit einem Großbuchstaben',
30
+ validation_format_QRCode: 'Muss ein gültiger QR-Code im Base64-Format sein',
31
+ validation_format_SemVerVersion: 'Muss eine gültige SemVer-Version sein (X.X.X)',
32
+ validation_format_Slug: 'Darf nur Kleinbuchstaben, Ziffern und Bindestriche (-) enthalten',
33
+ validation_format_SSHPrivateKey: 'Muss ein gültiger privater SSH-Schlüssel sein',
34
+ validation_format_SSHPublicKey: 'Muss ein gültiger öffentlicher SSH-Schlüssel sein',
35
+ validation_format_Time: 'Muss eine gültige Uhrzeit sein',
36
+ validation_format_URL: 'Muss eine gültige URL sein',
37
+ validation_format_UUID: 'Muss eine gültige UUID sein',
38
+ validation_mandatory: 'Muss ausgefüllt werden',
39
+ validation_max: 'Muss kleiner oder gleich {{expected}} sein',
40
+ validation_maxCount: 'Darf höchstens {{expected}} Element(e) enthalten',
41
+ validation_maxLength: 'Darf höchstens {{expected}} Zeichen enthalten',
42
+ validation_min: 'Muss größer oder gleich {{expected}} sein',
43
+ validation_minCount: 'Muss mindestens {{expected}} Element(e) enthalten',
44
+ validation_minLength: 'Muss mindestens {{expected}} Zeichen enthalten',
45
+ validation_oneOf: 'Muss eines der Elemente der Liste sein',
46
+ validation_shape: 'Muss der erwarteten Objektstruktur entsprechen',
47
+ validation_type_array: 'Muss ein Array sein',
48
+ validation_type_boolean: 'Muss ein Boolescher Wert sein',
49
+ validation_type_int: 'Muss eine ganze Zahl sein',
50
+ validation_type_number: 'Muss eine Zahl sein',
51
+ validation_type_object: 'Muss ein Objekt sein',
52
+ validation_type_scalar: 'Muss ein Skalar sein',
53
+ validation_type_string: 'Muss ein String sein',
54
+ };
@@ -0,0 +1,2 @@
1
+ import type { I18nCoreTranslations } from '../types.js';
2
+ export declare const I18nES: I18nCoreTranslations;
@@ -0,0 +1,54 @@
1
+ export const I18nES = {
2
+ dt_YesNo_N_desc: '',
3
+ dt_YesNo_N_label: 'No',
4
+ dt_YesNo_Y_desc: '',
5
+ dt_YesNo_Y_label: 'Sí',
6
+ uc_client_confirm_cancel: 'Cancelar',
7
+ uc_client_confirm_confirm: 'Sí',
8
+ uc_client_confirm_message: '',
9
+ uc_client_confirm_title: '¿Estás seguro?',
10
+ uc_i_submit_changing: 'Cargando',
11
+ uc_i_submit_idle: 'Guardar',
12
+ uc_i_submit_initializing: 'Cargando',
13
+ uc_i_submit_submitting: 'Guardando',
14
+ validation_fieldsOr: 'Al menos uno de estos campos debe estar completado: {{expected}}',
15
+ validation_format_ColorRGBA: 'Debe ser un color RGBA válido',
16
+ validation_format_DateISO8601: 'Debe ser una fecha válida con formato ISO8601',
17
+ validation_format_DirPath: 'Debe ser una ruta de directorio válida',
18
+ validation_format_DomainName: 'Debe ser un nombre de dominio válido',
19
+ validation_format_Email: 'Debe ser una dirección de correo válida',
20
+ validation_format_FilePath: 'Debe ser una ruta de archivo válida',
21
+ validation_format_GitSSHURL: 'Debe ser una URL SSH de Git válida',
22
+ validation_format_IPv4: 'Debe ser una dirección IPv4 válida',
23
+ validation_format_IPv6: 'Debe ser una dirección IPv6 válida',
24
+ validation_format_JSON: 'Debe ser una cadena JSON válida',
25
+ validation_format_JWT: 'Debe ser un JWT válido',
26
+ validation_format_PersonFirstname: 'Debe ser un nombre válido comenzando con mayúscula',
27
+ validation_format_PersonFullname: 'Debe ser un nombre completo válido comenzando con mayúscula',
28
+ validation_format_PersonInitials: 'Debe contener iniciales válidas compuestas por letras mayúsculas',
29
+ validation_format_PersonLastname: 'Debe ser un apellido válido comenzando con mayúscula',
30
+ validation_format_QRCode: 'Debe ser un código QR válido en formato base64',
31
+ validation_format_SemVerVersion: 'Debe ser una versión SemVer válida (X.X.X)',
32
+ validation_format_Slug: 'Debe contener solo letras minúsculas, dígitos y guiones (-)',
33
+ validation_format_SSHPrivateKey: 'Debe ser una clave privada SSH válida',
34
+ validation_format_SSHPublicKey: 'Debe ser una clave pública SSH válida',
35
+ validation_format_Time: 'Debe ser una hora válida',
36
+ validation_format_URL: 'Debe ser una URL válida',
37
+ validation_format_UUID: 'Debe ser un UUID válido',
38
+ validation_mandatory: 'Debe completarse',
39
+ validation_max: 'Debe ser menor o igual que {{expected}}',
40
+ validation_maxCount: 'Debe contener como máximo {{expected}} elemento(s)',
41
+ validation_maxLength: 'Debe contener como máximo {{expected}} carácter(es)',
42
+ validation_min: 'Debe ser mayor o igual que {{expected}}',
43
+ validation_minCount: 'Debe contener al menos {{expected}} elemento(s)',
44
+ validation_minLength: 'Debe contener al menos {{expected}} carácter(es)',
45
+ validation_oneOf: 'Debe ser uno de los elementos de la lista',
46
+ validation_shape: 'Debe respetar la estructura esperada del objeto',
47
+ validation_type_array: 'Debe ser un array',
48
+ validation_type_boolean: 'Debe ser un valor booleano',
49
+ validation_type_int: 'Debe ser un número entero',
50
+ validation_type_number: 'Debe ser un número',
51
+ validation_type_object: 'Debe ser un objeto',
52
+ validation_type_scalar: 'Debe ser un escalar',
53
+ validation_type_string: 'Debe ser una cadena',
54
+ };
@@ -1,6 +1,6 @@
1
1
  import type { ViolationI18nable, YesNo } from '../dt/index.js';
2
2
  import type { UCClientConfirmConfig, UCExecState, UCWording } from '../uc/index.js';
3
- export type I18nLanguageCode = 'en' | 'fr';
3
+ export type I18nLanguageCode = 'de' | 'en' | 'es' | 'fr';
4
4
  /**
5
5
  * A translation value
6
6
  *
@@ -14,6 +14,7 @@ export type I18nSource = any;
14
14
  export type I18nSourceSafe = {
15
15
  I18n: I18n;
16
16
  };
17
+ export type I18nEntry = Record<I18nTranslationKey, I18nTranslation>;
17
18
  export type I18n = {
18
- [key in I18nLanguageCode]?: Record<I18nTranslationKey, I18nTranslation>;
19
+ [key in I18nLanguageCode]?: I18nEntry;
19
20
  };
@@ -0,0 +1 @@
1
+ export { Plugin } from './utils/bundling/babel/plugin.js';
@@ -0,0 +1 @@
1
+ export { Plugin } from './utils/bundling/babel/plugin.js';
@@ -0,0 +1,2 @@
1
+ export { NodeStricliCLIManager } from './target/node-stricli-cli/NodeStricliCLIManager.js';
2
+ export { bindNodeCLI } from './utils/ioc/bindNodeCLI.js';
@@ -0,0 +1,2 @@
1
+ export { NodeStricliCLIManager } from './target/node-stricli-cli/NodeStricliCLIManager.js';
2
+ export { bindNodeCLI } from './utils/ioc/bindNodeCLI.js';
@@ -1 +1 @@
1
- export { StripUCDLifecycleServerPlugin } from './utils/bundling/vite/StripUCDLifecycleServerPlugin.js';
1
+ export { Plugin } from './utils/bundling/vite/plugin.js';
@@ -1 +1 @@
1
- export { StripUCDLifecycleServerPlugin } from './utils/bundling/vite/StripUCDLifecycleServerPlugin.js';
1
+ export { Plugin } from './utils/bundling/vite/plugin.js';
@@ -0,0 +1 @@
1
+ export { default } from './utils/bundling/webpack/loader.js';
@@ -0,0 +1 @@
1
+ export { default } from './utils/bundling/webpack/loader.js';
@@ -16,6 +16,18 @@ export interface I18nManager extends Initializable {
16
16
  * @param value
17
17
  */
18
18
  add<K extends I18nTranslationKey>(key: K, value: string): Promise<void>;
19
+ /**
20
+ * Get the list of available languages
21
+ */
22
+ availableLangs(): I18nLanguageCode[];
23
+ /**
24
+ * Change the current language
25
+ *
26
+ * It might not work for all the implementations, depending on how langs are managed.
27
+ *
28
+ * @param lang
29
+ */
30
+ changeLang(lang: I18nLanguageCode): Promise<void>;
19
31
  /**
20
32
  * Get the current lang code
21
33
  */
@@ -110,7 +110,7 @@ let NodeFSManager = class NodeFSManager {
110
110
  case 'library':
111
111
  return [];
112
112
  default:
113
- ((_) => { })(source);
113
+ source;
114
114
  return [];
115
115
  }
116
116
  }
@@ -1,18 +1,23 @@
1
- import { type I18n, type I18nLanguageCode, type I18nTranslationKey } from '../../i18n/index.js';
1
+ import type { I18n, I18nLanguageCode, I18nTranslationKey } from '../../i18n/index.js';
2
2
  import type { I18nManager, I18nManagerTOpts } from '../I18nManager.js';
3
3
  import type { Logger } from '../Logger.js';
4
4
  export declare class SimpleMapI18nManager implements I18nManager {
5
5
  private i18n;
6
6
  private logger;
7
7
  private static PLACEHOLDERS_REGEX;
8
+ private langs;
8
9
  private entries;
10
+ private currentLang;
9
11
  constructor(i18n: I18n, logger: Logger);
10
12
  add<K extends I18nTranslationKey>(key: K, value: string): Promise<void>;
13
+ availableLangs(): I18nLanguageCode[];
14
+ changeLang(lang: I18nLanguageCode): Promise<void>;
11
15
  init(): Promise<void>;
12
16
  initSync(): void;
13
17
  l(): I18nLanguageCode;
14
18
  t<K extends I18nTranslationKey>(key: K, opts?: I18nManagerTOpts): string;
15
19
  private initCommon;
16
20
  tOrNull<K extends I18nTranslationKey>(key: K, _opts?: I18nManagerTOpts): string | null;
21
+ private current;
17
22
  private replacePlaceholders;
18
23
  }
@@ -12,20 +12,34 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
12
  };
13
13
  var SimpleMapI18nManager_1;
14
14
  import { inject, injectable } from 'inversify';
15
- import { I18N_DEFAULT_LANG, } from '../../i18n/index.js';
16
15
  let SimpleMapI18nManager = class SimpleMapI18nManager {
17
16
  static { SimpleMapI18nManager_1 = this; }
18
17
  i18n;
19
18
  logger;
20
19
  static PLACEHOLDERS_REGEX = /{{([A-Z-a-z0-9]+)}}/g; // Note the 'g' so it can be used with `matchAll`
20
+ langs;
21
21
  entries;
22
+ currentLang;
22
23
  constructor(i18n, logger) {
23
24
  this.i18n = i18n;
24
25
  this.logger = logger;
26
+ this.langs = Object.keys(i18n);
27
+ if (this.langs.length === 0) {
28
+ throw new Error('I18n must define at least one lang');
29
+ }
25
30
  this.entries = new Map();
31
+ // biome-ignore lint/style/noNonNullAssertion: we want it
32
+ this.currentLang = this.langs[0];
26
33
  }
27
34
  async add(key, value) {
28
- this.entries.set(key, value);
35
+ this.current().set(key, value);
36
+ }
37
+ availableLangs() {
38
+ return this.langs;
39
+ }
40
+ async changeLang(lang) {
41
+ this.currentLang = lang;
42
+ this.initCommon();
29
43
  }
30
44
  async init() {
31
45
  this.initCommon();
@@ -34,10 +48,10 @@ let SimpleMapI18nManager = class SimpleMapI18nManager {
34
48
  this.initCommon();
35
49
  }
36
50
  l() {
37
- return I18N_DEFAULT_LANG;
51
+ return this.currentLang;
38
52
  }
39
53
  t(key, opts) {
40
- const v = this.entries.get(key);
54
+ const v = this.current().get(key);
41
55
  if (v) {
42
56
  return this.replacePlaceholders(v, opts);
43
57
  }
@@ -47,17 +61,32 @@ let SimpleMapI18nManager = class SimpleMapI18nManager {
47
61
  return key; // Mimic the behavior of some common libraries like i18next
48
62
  }
49
63
  initCommon() {
50
- const translations = this.i18n[this.l()];
51
- this.logger.trace('Initializing I18nManager', { translations });
52
- if (!translations) {
53
- return;
54
- }
55
- for (const [k, v] of Object.entries(translations)) {
56
- this.entries.set(k, v);
64
+ for (const lang of this.langs) {
65
+ const translations = this.i18n[lang];
66
+ this.logger.trace('Initializing I18nManager', {
67
+ lang,
68
+ translations,
69
+ });
70
+ if (!translations) {
71
+ return;
72
+ }
73
+ if (!this.entries.has(lang)) {
74
+ this.entries.set(lang, new Map());
75
+ }
76
+ for (const [k, v] of Object.entries(translations)) {
77
+ this.entries.get(lang)?.set(k, v);
78
+ }
57
79
  }
58
80
  }
59
81
  tOrNull(key, _opts) {
60
- return this.entries.get(key) || null;
82
+ return this.current().get(key) || null;
83
+ }
84
+ current() {
85
+ const entry = this.entries.get(this.currentLang);
86
+ if (!entry) {
87
+ throw new Error(`I18nManager must contain an entry for lang : ${this.currentLang}`);
88
+ }
89
+ return entry;
61
90
  }
62
91
  replacePlaceholders(v, opts) {
63
92
  // DO NOT USE THIS IN PRODUCTION
@@ -80,7 +80,7 @@ let AuthenticationChecker = class AuthenticationChecker {
80
80
  }
81
81
  break;
82
82
  default:
83
- ((_) => { })(authType);
83
+ authType;
84
84
  }
85
85
  uc.auth = output.auth;
86
86
  const { allowed } = await policy.exec({ uc });
@@ -39,7 +39,7 @@ let PublicApiKeyChecker = class PublicApiKeyChecker {
39
39
  }
40
40
  break;
41
41
  default:
42
- ((_) => { })(checkType);
42
+ checkType;
43
43
  }
44
44
  if (!allowed) {
45
45
  throw new UnauthorizedError();
@@ -127,7 +127,7 @@ let ServerRequestHandler = class ServerRequestHandler {
127
127
  uc.fill((await req.bodyFromQueryParams()));
128
128
  break;
129
129
  default:
130
- ((_) => { })(envelope);
130
+ envelope;
131
131
  }
132
132
  }
133
133
  async applySideEffects(res, ucd, output) {
@@ -154,7 +154,7 @@ let ServerRequestHandler = class ServerRequestHandler {
154
154
  await this.applySetAuthSideEffect(res, item);
155
155
  break;
156
156
  default:
157
- ((_) => { })(type);
157
+ (type);
158
158
  }
159
159
  }
160
160
  return { status: undefined };