libmodulor 0.23.0 → 0.25.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 (132) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +154 -2
  3. package/dist/esm/apps/Helper/src/lib/project.js +6 -6
  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/Validation.d.ts +2 -2
  10. package/dist/esm/dt/base/TBase.d.ts +2 -0
  11. package/dist/esm/dt/base/TBase.js +3 -0
  12. package/dist/esm/dt/base/TObject.d.ts +6 -4
  13. package/dist/esm/dt/base/TObject.js +5 -6
  14. package/dist/esm/dt/base/TString.d.ts +2 -1
  15. package/dist/esm/dt/base/TString.js +22 -0
  16. package/dist/esm/dt/final/TFile.d.ts +13 -4
  17. package/dist/esm/dt/final/TFile.js +70 -8
  18. package/dist/esm/dt/final/TFileExtension.d.ts +1 -1
  19. package/dist/esm/dt/final/TFileMimeType.d.ts +2 -6
  20. package/dist/esm/dt/final/TFileMimeType.js +0 -6
  21. package/dist/esm/dt/final/TFilePath.d.ts +7 -0
  22. package/dist/esm/dt/final/TFilePath.js +5 -1
  23. package/dist/esm/dt/index.d.ts +1 -1
  24. package/dist/esm/i18n/WordingManager.d.ts +2 -1
  25. package/dist/esm/i18n/WordingManager.js +23 -2
  26. package/dist/esm/i18n/index.d.ts +1 -1
  27. package/dist/esm/i18n/locales/de.d.ts +2 -0
  28. package/dist/esm/i18n/locales/de.js +62 -0
  29. package/dist/esm/i18n/locales/en.js +8 -0
  30. package/dist/esm/i18n/locales/es.d.ts +2 -0
  31. package/dist/esm/i18n/locales/es.js +62 -0
  32. package/dist/esm/i18n/locales/fr.js +8 -0
  33. package/dist/esm/i18n/types.d.ts +5 -4
  34. package/dist/esm/index.babel.d.ts +1 -0
  35. package/dist/esm/index.babel.js +1 -0
  36. package/dist/esm/index.node-stricli-cli.d.ts +2 -0
  37. package/dist/esm/index.node-stricli-cli.js +2 -0
  38. package/dist/esm/index.vite.d.ts +1 -1
  39. package/dist/esm/index.vite.js +1 -1
  40. package/dist/esm/index.webpack.d.ts +1 -0
  41. package/dist/esm/index.webpack.js +1 -0
  42. package/dist/esm/std/FSManager.d.ts +7 -5
  43. package/dist/esm/std/FSManager.js +5 -6
  44. package/dist/esm/std/FormDataBuilder.d.ts +2 -1
  45. package/dist/esm/std/I18nManager.d.ts +12 -0
  46. package/dist/esm/std/impl/NodeFSManager.js +4 -3
  47. package/dist/esm/std/impl/SimpleMapI18nManager.d.ts +6 -1
  48. package/dist/esm/std/impl/SimpleMapI18nManager.js +41 -12
  49. package/dist/esm/target/lib/react/StyleContextProvider.d.ts +1 -0
  50. package/dist/esm/target/lib/react/form.d.ts +1 -1
  51. package/dist/esm/target/lib/react/form.js +1 -0
  52. package/dist/esm/target/lib/react/useAction.d.ts +1 -1
  53. package/dist/esm/target/lib/react/useAction.js +13 -12
  54. package/dist/esm/target/lib/server/AuthenticationChecker.js +1 -1
  55. package/dist/esm/target/lib/server/PublicApiKeyChecker.js +1 -1
  56. package/dist/esm/target/lib/server/ServerRequestHandler.js +2 -2
  57. package/dist/esm/target/lib/server-express/funcs.js +4 -3
  58. package/dist/esm/target/lib/server-hono/funcs.js +2 -2
  59. package/dist/esm/target/lib/server-node/funcs.js +1 -1
  60. package/dist/esm/target/lib/web/input.d.ts +1 -0
  61. package/dist/esm/target/lib/web/input.js +5 -1
  62. package/dist/esm/target/node-stricli-cli/NodeStricliCLIManager.d.ts +12 -0
  63. package/dist/esm/target/node-stricli-cli/NodeStricliCLIManager.js +118 -0
  64. package/dist/esm/target/react-native-pure/UCFormField.js +2 -1
  65. package/dist/esm/target/react-native-pure/UCFormFieldControl.js +6 -4
  66. package/dist/esm/target/react-native-pure/UCFormFieldErr.d.ts +1 -1
  67. package/dist/esm/target/react-native-pure/UCFormFieldErr.js +4 -1
  68. package/dist/esm/target/react-native-pure/UCFormFieldHelp.d.ts +4 -0
  69. package/dist/esm/target/react-native-pure/UCFormFieldHelp.js +15 -0
  70. package/dist/esm/target/react-web-pure/UCFormField.js +2 -1
  71. package/dist/esm/target/react-web-pure/UCFormFieldErr.d.ts +1 -1
  72. package/dist/esm/target/react-web-pure/UCFormFieldErr.js +4 -1
  73. package/dist/esm/target/react-web-pure/UCFormFieldHelp.d.ts +4 -0
  74. package/dist/esm/target/react-web-pure/UCFormFieldHelp.js +14 -0
  75. package/dist/esm/testing/AppTester.js +4 -5
  76. package/dist/esm/testing/UCDefASTParser.d.ts +24 -6
  77. package/dist/esm/testing/impl/SimpleAppDocsEmitter/SimpleAppDocsEmitter.d.ts +7 -0
  78. package/dist/esm/testing/impl/SimpleAppDocsEmitter/SimpleAppDocsEmitter.js +59 -0
  79. package/dist/esm/testing/impl/SimpleAppDocsEmitter/markdown.d.ts +2 -0
  80. package/dist/esm/testing/impl/SimpleAppDocsEmitter/markdown.js +10 -0
  81. package/dist/esm/testing/impl/SimpleAppDocsEmitter/sequence-diagram.d.ts +2 -0
  82. package/dist/esm/testing/impl/SimpleAppDocsEmitter/sequence-diagram.js +92 -0
  83. package/dist/esm/testing/impl/SimpleAppDocsEmitter/tech-summary.d.ts +2 -0
  84. package/dist/esm/testing/impl/SimpleAppDocsEmitter/tech-summary.js +27 -0
  85. package/dist/esm/testing/impl/SimpleAppDocsEmitter/uc-summary.d.ts +2 -0
  86. package/dist/esm/testing/impl/SimpleAppDocsEmitter/uc-summary.js +63 -0
  87. package/dist/esm/testing/impl/SimpleAppDocsEmitter.js +4 -5
  88. package/dist/esm/testing/impl/TypeScriptLibUCDefASTParser.js +38 -11
  89. package/dist/esm/testing/impl/VitestAppTestSuiteRunner.d.ts +1 -1
  90. package/dist/esm/testing/impl/VitestAppTestSuiteRunner.js +17 -2
  91. package/dist/esm/testing/impl/newNodeAppTester.js +1 -1
  92. package/dist/esm/testing/opts.js +1 -1
  93. package/dist/esm/testing/uc-input.js +5 -2
  94. package/dist/esm/testing/workers/AppTestSuiteRunner.d.ts +2 -0
  95. package/dist/esm/testing/workers/UCExecutor.js +1 -1
  96. package/dist/esm/testing/workers/checkers/AppI18nChecker.d.ts +8 -2
  97. package/dist/esm/testing/workers/checkers/AppI18nChecker.js +44 -2
  98. package/dist/esm/testing/workers/checkers/UCDefSourcesChecker.js +12 -12
  99. package/dist/esm/uc/exec.d.ts +42 -21
  100. package/dist/esm/uc/exec.js +48 -13
  101. package/dist/esm/uc/impl/HTTPUCTransporter.js +2 -2
  102. package/dist/esm/uc/impl/KnexUCDataStore.js +2 -2
  103. package/dist/esm/uc/index.d.ts +0 -1
  104. package/dist/esm/uc/index.js +0 -1
  105. package/dist/esm/uc/input-field.d.ts +6 -4
  106. package/dist/esm/uc/input-field.js +4 -5
  107. package/dist/esm/uc/side-effect.d.ts +10 -8
  108. package/dist/esm/uc/side-effect.js +5 -6
  109. package/dist/esm/uc/workers/UCExecChecker.js +1 -1
  110. package/dist/esm/uc/workers/UCInputFilesProcessor.js +3 -3
  111. package/dist/esm/uc/workers/UCInputValidator.js +2 -1
  112. package/dist/esm/uc/workers/UCOutputFilesProcessor.js +1 -1
  113. package/dist/esm/utils/bundling/babel/plugin.d.ts +2 -0
  114. package/dist/esm/utils/bundling/babel/plugin.js +38 -0
  115. package/dist/esm/utils/bundling/funcs.d.ts +6 -0
  116. package/dist/esm/utils/bundling/funcs.js +20 -0
  117. package/dist/esm/utils/bundling/typescript.d.ts +3 -0
  118. package/dist/esm/utils/bundling/typescript.js +61 -0
  119. package/dist/esm/utils/bundling/vite/plugin.d.ts +6 -0
  120. package/dist/esm/utils/bundling/vite/plugin.js +17 -0
  121. package/dist/esm/utils/bundling/webpack/loader.d.ts +2 -0
  122. package/dist/esm/utils/bundling/webpack/loader.js +12 -0
  123. package/dist/esm/utils/http/HTTPRequestBuilder.js +1 -1
  124. package/dist/esm/utils/index.d.ts +1 -1
  125. package/dist/esm/utils/ioc/bindCommon.js +1 -1
  126. package/dist/esm/utils/terminal/fmt.js +1 -1
  127. package/dist/esm/utils/types/utility-types.d.ts +4 -0
  128. package/package.json +36 -12
  129. package/pnpm-workspace.yaml +1 -4
  130. package/tsconfig.build.examples.json +8 -0
  131. package/tsconfig.json +1 -0
  132. package/vitest.config.ts +16 -0
@@ -0,0 +1,10 @@
1
+ export function pre(val) {
2
+ if (!val) {
3
+ return '-';
4
+ }
5
+ return `\`${val}\``;
6
+ }
7
+ export function thead(cols) {
8
+ return `|#|${cols.join('|')}|
9
+ |---|${cols.map(() => '---').join('|')}|`;
10
+ }
@@ -0,0 +1,2 @@
1
+ import type { OutputItem } from '../../UCDefASTParser.js';
2
+ export declare function ucSequenceDiagram(item: OutputItem): string;
@@ -0,0 +1,92 @@
1
+ import { UC_MAIN_STEP_PREFIX_REGULAR } from '../../../convention.js';
2
+ const CHECK_POLICY = '🔐 Check policy';
3
+ const CHECK_POLICY_COND = 'when any validation fails';
4
+ const CHECK_POLICY_COND_ACTION = 'show failure';
5
+ const CLIENT_CONFIRM_N_COND = 'when does not confirm';
6
+ const CLIENT_CONFIRM_N_COND_ACTION = 'stop everything';
7
+ const CLIENT_CONFIRM_Q = '❓ Sure';
8
+ const CLIENT_CONFIRM_Y = 'Yes';
9
+ const FILL = '✏️ Fill';
10
+ const LB = '<br/>';
11
+ const OK = '👍 OK';
12
+ const SEND = '📤 Send';
13
+ const SUBMIT = '↩️ Submit';
14
+ const TRIGGER = '⤴️ Trigger';
15
+ export function ucSequenceDiagram(item) {
16
+ // Debugger : https://mermaid.live/edit
17
+ // Messages : https://mermaid.js.org/syntax/sequenceDiagram.html#messages
18
+ const client = 'Client';
19
+ const server = 'Server';
20
+ const user = 'User';
21
+ const lines = [`actor ${user}`];
22
+ const { ioIFields, ioOPI0Fields, ioOPI1Fields, lifecycleClientPolicy, lifecycleClientSteps, lifecycleServerPolicy, lifecycleServerSteps, metadataSensitive, } = item;
23
+ let req = TRIGGER;
24
+ if (ioIFields && ioIFields.length > 0) {
25
+ // TODO : Include only fields to fill manually ?
26
+ // Not sure though, as for CLI for example (i.e. noContext), one needs to provide all of them
27
+ req = `${FILL}${LB}${fields(item.ioIFields)}`;
28
+ }
29
+ lines.push(`${user}->>+${client}: ${req}`);
30
+ lines.push(`${user}->>${client}: ${SUBMIT}`);
31
+ if (metadataSensitive?.value) {
32
+ lines.push(...clientConfirm(client, user));
33
+ }
34
+ if (lifecycleClientPolicy) {
35
+ lines.push(...policy(client, user, lifecycleClientPolicy));
36
+ }
37
+ if (lifecycleClientSteps) {
38
+ lines.push(...mainSteps(client, lifecycleClientSteps));
39
+ }
40
+ // This is an approximation. Might need to improve it.
41
+ const hasServer = item.lifecycleServerPolicy?.value;
42
+ if (hasServer) {
43
+ req = SEND;
44
+ if (ioIFields && ioIFields.length > 0) {
45
+ req = `${req}${LB}${fields(item.ioIFields)}`;
46
+ }
47
+ lines.push(`${client}->>+${server}: ${req}`);
48
+ if (lifecycleServerPolicy) {
49
+ lines.push(...policy(server, user, lifecycleServerPolicy));
50
+ }
51
+ if (lifecycleServerSteps) {
52
+ lines.push(...mainSteps(server, lifecycleServerSteps));
53
+ }
54
+ let res = '';
55
+ if (ioOPI0Fields && ioOPI0Fields.length > 0) {
56
+ res += `${res}${LB}${fields(item.ioOPI0Fields)}`;
57
+ }
58
+ if (ioOPI1Fields && ioOPI1Fields?.length > 0) {
59
+ res += `${res}${LB}${fields(item.ioOPI1Fields)}`;
60
+ }
61
+ lines.push(`${server}-->>-${client}: ${OK}${res}`);
62
+ }
63
+ lines.push(`${client}-->>-${user}: ${OK}`);
64
+ return `\`\`\`mermaid
65
+ sequenceDiagram
66
+ ${lines.join('\n ')}
67
+ \`\`\``;
68
+ }
69
+ function clientConfirm(participant, caller) {
70
+ return [
71
+ `${participant}->>${caller}: ${CLIENT_CONFIRM_Q}`,
72
+ `${caller}->>${participant}: ${CLIENT_CONFIRM_Y}`,
73
+ `break ${CLIENT_CONFIRM_N_COND}`,
74
+ ` ${participant}-->${caller}: ${CLIENT_CONFIRM_N_COND_ACTION}`,
75
+ 'end',
76
+ ];
77
+ }
78
+ function fields(fields) {
79
+ return (fields?.map((f) => `${f.value.name}: ${f.value.dataType}`).join(LB) ||
80
+ '');
81
+ }
82
+ function mainSteps(participant, field) {
83
+ return field.map((f) => `${participant}->>${participant}: ${f.value.replace(UC_MAIN_STEP_PREFIX_REGULAR, '').trim()}`);
84
+ }
85
+ function policy(participant, caller, lifecyclePolicyField) {
86
+ return [
87
+ `${participant}->>${participant}: ${CHECK_POLICY} "${lifecyclePolicyField.value}"`,
88
+ `break ${CHECK_POLICY_COND}`,
89
+ ` ${participant}-->${caller}: ${CHECK_POLICY_COND_ACTION}`,
90
+ 'end',
91
+ ];
92
+ }
@@ -0,0 +1,2 @@
1
+ import { type OutputItem } from '../../UCDefASTParser.js';
2
+ export declare function techSummary(items: OutputItem[]): string;
@@ -0,0 +1,27 @@
1
+ import { OUTPUT_ITEM_FIELDS, } from '../../UCDefASTParser.js';
2
+ import { thead } from './markdown.js';
3
+ export function techSummary(items) {
4
+ return `${thead(OUTPUT_ITEM_FIELDS)}
5
+ ${items
6
+ .map((item, idx) => ['', idx + 1, ...OUTPUT_ITEM_FIELDS.map((f) => val(item[f])), ''].join('|'))
7
+ .join('\n')}`;
8
+ }
9
+ function val(value) {
10
+ if (!value) {
11
+ return '';
12
+ }
13
+ const values = Array.isArray(value) ? value : [value];
14
+ // NOTE : <br> won't work for every markdown renderer.
15
+ // See https://stackoverflow.com/questions/11700487/how-do-i-add-a-newline-in-a-markdown-table
16
+ return values.map(fmtVal).join('<br>');
17
+ }
18
+ function fmtVal(field) {
19
+ const { err, value } = field;
20
+ let res = (typeof value === 'string' ? value : value.raw) ?? '';
21
+ if (err) {
22
+ res += `❌ ${err}`;
23
+ }
24
+ res = res.replace(/[\u00A0-\u9999<>&]/g, (i) => `&#${i.charCodeAt(0)};`); // TS generics considered as HTML
25
+ res = res.replaceAll('|', '\\|'); // TS intersection vs Markdown table column
26
+ return res;
27
+ }
@@ -0,0 +1,2 @@
1
+ import type { OutputItem } from '../../UCDefASTParser.js';
2
+ export declare function ucSummary(item: OutputItem): string;
@@ -0,0 +1,63 @@
1
+ import { humanize } from '../../../utils/index.js';
2
+ import { pre, thead } from './markdown.js';
3
+ import { ucSequenceDiagram } from './sequence-diagram.js';
4
+ export function ucSummary(item) {
5
+ return `### ${item.metadataName?.value}
6
+
7
+ ${lifecycle(item)}
8
+
9
+ ${fields(item)}
10
+
11
+ #### Sequence Diagram
12
+
13
+ ${ucSequenceDiagram(item)}`;
14
+ }
15
+ const UC_FIELDS_TABLES_COLS = ['name', 'humanized', 'dataType'];
16
+ function fields(item) {
17
+ const { ioIFields, ioOPI0Fields, ioOPI1Fields } = item;
18
+ return `#### Input (I)
19
+
20
+ ${ucFieldsTable(ioIFields)}
21
+
22
+ #### Output (O)
23
+
24
+ ##### Part 0 (OPI0)
25
+
26
+ ${ucFieldsTable(ioOPI0Fields)}
27
+
28
+ ##### Part 1 (OPI1)
29
+
30
+ ${ucFieldsTable(ioOPI1Fields)}`;
31
+ }
32
+ function ucFieldsTable(fields) {
33
+ if (!fields || fields.length === 0) {
34
+ return 'None';
35
+ }
36
+ return `${thead(UC_FIELDS_TABLES_COLS)}
37
+ ${fields
38
+ ?.map(({ value: { dataType, name } }, idx) => [
39
+ '',
40
+ idx + 1,
41
+ pre(name),
42
+ name ? humanize(name) : '',
43
+ pre(dataType),
44
+ '',
45
+ ].join('|'))
46
+ .join('\n')}`;
47
+ }
48
+ function lifecycle(item) {
49
+ const { lifecycleClientPolicy, lifecycleServerPolicy } = item;
50
+ return `- **Type** : ${pre(lifecycleType(item))}
51
+ - **Client Policy** : ${pre(lifecycleClientPolicy?.value)}
52
+ - **Server Policy** : ${pre(lifecycleServerPolicy?.value)}`;
53
+ }
54
+ function lifecycleType(item) {
55
+ const { lifecycleClientPolicy, lifecycleServerPolicy } = item;
56
+ if (lifecycleClientPolicy && lifecycleServerPolicy) {
57
+ return 'Client / Server';
58
+ }
59
+ if (lifecycleClientPolicy) {
60
+ return 'Client only';
61
+ }
62
+ return 'Server only';
63
+ }
@@ -11,7 +11,7 @@ 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 { APP_DOCS_FILE_NAME, UC_INPUT_FIELD_PATTERN, UC_MAIN_STEP_PREFIX_REGULAR, } from '../../convention.js';
14
+ import { APP_DOCS_FILE_NAME, UC_MAIN_STEP_PREFIX_REGULAR, } from '../../convention.js';
15
15
  import { OUTPUT_ITEM_FIELDS, } from '../UCDefASTParser.js';
16
16
  let SimpleAppDocsEmitter = class SimpleAppDocsEmitter {
17
17
  fsManager;
@@ -142,9 +142,8 @@ function diagramUCClientConfirm(participant, caller) {
142
142
  ];
143
143
  }
144
144
  function diagramUCFields(fields) {
145
- return (fields
146
- ?.map((f) => f.value.replace(new RegExp(UC_INPUT_FIELD_PATTERN.slice(1)), '$1'))
147
- .join(LB) || '');
145
+ return (fields?.map((f) => `${f.value.name}: ${f.value.dataType}`).join(LB) ||
146
+ '');
148
147
  }
149
148
  function diagramUCMainSteps(participant, field) {
150
149
  return field.map((f) => `${participant}->>${participant}: ${f.value.replace(UC_MAIN_STEP_PREFIX_REGULAR, '').trim()}`);
@@ -171,7 +170,7 @@ function fmtTechSummaryField(field) {
171
170
  }
172
171
  function fmtTechSummaryFieldVal(field) {
173
172
  const { err, value } = field;
174
- let res = value;
173
+ let res = (typeof value === 'string' ? value : value.raw) ?? '';
175
174
  if (err) {
176
175
  res += `❌ ${err}`;
177
176
  }
@@ -11,7 +11,8 @@ 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 typescript, { isClassDeclaration, isObjectLiteralExpression, isPropertyAssignment, } from 'typescript';
14
+ import typescript, { isClassDeclaration, isObjectLiteralExpression, isPropertyAssignment, SyntaxKind, } from 'typescript';
15
+ // https://ts-ast-viewer.com
15
16
  // To avoid the following error when used in a consumer :
16
17
  // SyntaxError: Named export 'ModuleKind' not found. The requested module 'typescript' is a CommonJS module, which may not support all module.exports as named exports.
17
18
  // CommonJS modules can always be imported via the default export
@@ -99,20 +100,46 @@ let TypeScriptLibUCDefASTParser = class TypeScriptLibUCDefASTParser {
99
100
  }
100
101
  getTypeFields(node) {
101
102
  const type = this.typeChecker.getTypeAtLocation(node);
102
- const fields = type.getProperties().map((p) => {
103
+ const fields = type
104
+ .getProperties()
105
+ .map((p) => {
103
106
  const field = {
104
107
  err: null,
105
- value: p.getName(),
108
+ value: {
109
+ dataType: null,
110
+ name: null,
111
+ raw: null,
112
+ type: null,
113
+ },
106
114
  };
107
115
  const declarations = p.getDeclarations();
108
- if (declarations) {
109
- const [first] = declarations;
110
- if (first && isPropertySignature(first)) {
111
- field.value = first
112
- .getText()
113
- .replaceAll('\n', '')
114
- .replaceAll(';', '');
115
- }
116
+ if (!declarations) {
117
+ return field;
118
+ }
119
+ const [first] = declarations;
120
+ if (first && isPropertySignature(first)) {
121
+ field.value.raw = first
122
+ .getText()
123
+ .replaceAll('\n', '')
124
+ .replaceAll(';', '')
125
+ .replaceAll(/\s+/g, ' ');
126
+ first.forEachChild((node1) => {
127
+ if (isIdentifier(node1)) {
128
+ field.value.name = node1.getText();
129
+ }
130
+ else if (isTypeReferenceNode(node1)) {
131
+ field.value.type = node1.getText();
132
+ node1.forEachChild((node2) => {
133
+ if (isIdentifier(node2) ||
134
+ isTypeReferenceNode(node2) ||
135
+ node2.kind === SyntaxKind.BooleanKeyword ||
136
+ node2.kind === SyntaxKind.NumberKeyword ||
137
+ node2.kind === SyntaxKind.StringKeyword) {
138
+ field.value.dataType = node2.getText();
139
+ }
140
+ });
141
+ }
142
+ });
116
143
  }
117
144
  return field;
118
145
  });
@@ -5,7 +5,7 @@ export declare class VitestAppTestSuiteRunner implements AppTestSuiteRunner {
5
5
  private fsManager;
6
6
  private shellCommandExecutor;
7
7
  constructor(fsManager: FSManager, shellCommandExecutor: ShellCommandExecutor);
8
- exec({ appPath, skipCoverage, updateSnapshots, }: Input): Promise<void>;
8
+ exec({ appPath, only, skipCoverage, updateSnapshots, }: Input): Promise<void>;
9
9
  coverageReportEntrypointPath(appPath: FilePath): Promise<FilePath>;
10
10
  private coverageReportPath;
11
11
  }
@@ -19,18 +19,33 @@ let VitestAppTestSuiteRunner = class VitestAppTestSuiteRunner {
19
19
  this.fsManager = fsManager;
20
20
  this.shellCommandExecutor = shellCommandExecutor;
21
21
  }
22
- async exec({ appPath, skipCoverage, updateSnapshots, }) {
22
+ async exec({ appPath, only, skipCoverage, updateSnapshots, }) {
23
23
  const testPath = this.fsManager.path(appPath, APP_TEST_DIR_NAME);
24
24
  const args = [
25
25
  'run',
26
26
  '--color',
27
+ // https://vitest.dev/guide/cli.html#dir
27
28
  '--dir',
28
29
  appPath,
29
30
  ];
31
+ if (only) {
32
+ args.push(
33
+ // https://vitest.dev/guide/cli.html#testnamepattern
34
+ '--testNamePattern', only);
35
+ }
30
36
  if (!skipCoverage) {
31
- args.push('--coverage.enabled', '--coverage.exclude', testPath, '--coverage.include', appPath, '--coverage.reportsDirectory', this.coverageReportPath(appPath));
37
+ args.push(
38
+ // https://vitest.dev/guide/cli.html#coverage-enabled
39
+ '--coverage.enabled',
40
+ // https://vitest.dev/guide/cli.html#coverage-exclude
41
+ '--coverage.exclude', testPath,
42
+ // https://vitest.dev/guide/cli.html#coverage-include
43
+ '--coverage.include', appPath,
44
+ // https://vitest.dev/guide/cli.html#coverage-reportsdirectory
45
+ '--coverage.reportsDirectory', this.coverageReportPath(appPath));
32
46
  }
33
47
  if (updateSnapshots) {
48
+ // https://vitest.dev/guide/cli.html#update
34
49
  args.push('--update');
35
50
  }
36
51
  await this.shellCommandExecutor.exec({
@@ -10,7 +10,7 @@ import { bindNodeCore } from '../../utils/ioc/bindNodeCore.js';
10
10
  import { bindServer } from '../../utils/ioc/bindServer.js';
11
11
  import { AppTester } from '../AppTester.js';
12
12
  import { optsAllSet } from '../opts.js';
13
- import { SimpleAppDocsEmitter } from './SimpleAppDocsEmitter.js';
13
+ import { SimpleAppDocsEmitter } from './SimpleAppDocsEmitter/SimpleAppDocsEmitter.js';
14
14
  import { TypeScriptLibUCDefASTParser } from './TypeScriptLibUCDefASTParser.js';
15
15
  export async function newNodeAppTester(serverPortRangeStart, idx, args) {
16
16
  const { configurator } = args;
@@ -4,7 +4,7 @@ export const DEFAULT_APP_TESTER_OPTS = {
4
4
  imports: {
5
5
  external: {
6
6
  aliasPrefix: '@',
7
- allowed: ['inversify', 'libmodulor'],
7
+ allowed: ['libmodulor', 'inversify'],
8
8
  },
9
9
  internal: {
10
10
  maxDepth: '../../',
@@ -1,5 +1,6 @@
1
- import { TFile } from '../dt/index.js';
1
+ import { TFile, TFilePath } from '../dt/index.js';
2
2
  import { ucifIsMandatory, ucifMustBeFilledManually, ucifRepeatability, } from '../uc/index.js';
3
+ import { range } from '../utils/index.js';
3
4
  export const DEFAULT_UC_INPUT_FILLERS = [
4
5
  'ALL_WITH_EXAMPLES',
5
6
  'ONLY_MANDATORY_WITH_EXAMPLES',
@@ -39,7 +40,9 @@ function fillWithExample(f) {
39
40
  if (type instanceof TFile) {
40
41
  const example = type.getExamples()?.[0] ?? type.example();
41
42
  // TODO : Consider building a real file with real data (e.g. image, pdf, txt, etc.)
42
- const val = new File(['01010101010101010101010101010101'], example.path, {
43
+ // A fake file filled with 0s and 1s
44
+ const blob = range(TFilePath.FILE_SIZE).map((i) => (i % 2).toString());
45
+ const val = new File(blob, example.uri, {
43
46
  type: example.type,
44
47
  });
45
48
  const [isRepeatable] = ucifRepeatability(f.def);
@@ -1,7 +1,9 @@
1
1
  import type { FilePath } from '../../dt/index.js';
2
2
  import type { Worker } from '../../std/index.js';
3
+ import type { UCName } from '../../uc/index.js';
3
4
  export interface Input {
4
5
  appPath: FilePath;
6
+ only: UCName | null;
5
7
  skipCoverage: boolean;
6
8
  updateSnapshots: boolean;
7
9
  }
@@ -104,7 +104,7 @@ let UCExecutor = class UCExecutor {
104
104
  break;
105
105
  }
106
106
  default:
107
- ((_) => { })(transportType);
107
+ transportType;
108
108
  }
109
109
  if (!ucor) {
110
110
  throw new Error(ERR_CLIENT_EXPECTED_UCOR(name));
@@ -1,10 +1,12 @@
1
1
  import type { AppManifest } from '../../../app/index.js';
2
2
  import type { ErrorMessage } from '../../../dt/index.js';
3
- import type { I18n } from '../../../i18n/index.js';
3
+ import { type I18n } from '../../../i18n/index.js';
4
4
  import type { Worker } from '../../../std/index.js';
5
+ import type { Output as UCDefSourcesCheckerOutput } from './UCDefSourcesChecker.js';
5
6
  export interface Input {
6
7
  appI18n: I18n;
7
8
  appManifest: Pick<AppManifest, 'languageCodes'>;
9
+ ucDefSourcesCheckerOutput: UCDefSourcesCheckerOutput;
8
10
  }
9
11
  export interface Output {
10
12
  errors: ErrorMessage[];
@@ -13,7 +15,11 @@ export declare class AppI18nChecker implements Worker<Input, Promise<Output>> {
13
15
  private static I18N_KEY_PATTERN;
14
16
  private output;
15
17
  constructor();
16
- exec({ appI18n, appManifest }: Input): Promise<Output>;
18
+ exec({ appI18n, appManifest, ucDefSourcesCheckerOutput, }: Input): Promise<Output>;
17
19
  private makeSureLanguagesAreConsistent;
18
20
  private makeSureKeysAndLabelsAreValid;
21
+ private makeSureNonDefaultLanguagesAreFullyTranslated;
22
+ private checkUC;
23
+ private checkIOFields;
24
+ private checkKey;
19
25
  }
@@ -9,9 +9,11 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  };
10
10
  var AppI18nChecker_1;
11
11
  import { injectable } from 'inversify';
12
+ import { I18N_DEFAULT_LANG, } from '../../../i18n/index.js';
12
13
  const ERR_I18N_MISMATCH = () => 'The languageCodes in app manifest must match with the keys in i18n';
13
14
  const ERR_I18N_KEY_INVALID = (key) => `The i18n key '${key}' must respect the i18n key pattern`;
14
- const ERR_I18N_LABEL_NO_DOT = (key) => `The i18n key '${key}' is a label, thus should not ends with a dot`;
15
+ const ERR_I18N_LABEL_NO_DOT = (key) => `The i18n key '${key}' is a label, thus it should not end with a dot`;
16
+ const ERR_I18N_MISSING_TRANS = (key, lang) => `The i18n key '${key}' is missing for lang '${lang}'`;
15
17
  let AppI18nChecker = class AppI18nChecker {
16
18
  static { AppI18nChecker_1 = this; }
17
19
  static I18N_KEY_PATTERN = /(dt_([A-Z][A-Za-z0-9]+)_([A-Za-z0-9]+)_(desc|label))|(err_([A-Za-z_]+))|(uc_([A-Z][A-Za-z0-9]+)_(client_confirm_(cancel|confirm|message|title)|desc|label|i_submit_(changing|idle|initializing|submitting)|op_(0|1)_(empty|label)))|(ucif_([a-z]([A-Za-z0-9]+)?)_(desc|label))|(ucof_([a-z]([A-Za-z0-9]+)?)_(desc|label))|(validation_([a-z]+)_([A-Z][A-Za-z0-9]+))/;
@@ -19,9 +21,10 @@ let AppI18nChecker = class AppI18nChecker {
19
21
  constructor() {
20
22
  this.output = { errors: [] };
21
23
  }
22
- async exec({ appI18n, appManifest }) {
24
+ async exec({ appI18n, appManifest, ucDefSourcesCheckerOutput, }) {
23
25
  this.makeSureLanguagesAreConsistent(appI18n, appManifest);
24
26
  this.makeSureKeysAndLabelsAreValid(appI18n);
27
+ this.makeSureNonDefaultLanguagesAreFullyTranslated(appI18n, ucDefSourcesCheckerOutput);
25
28
  return this.output;
26
29
  }
27
30
  makeSureLanguagesAreConsistent(appI18n, appManifest) {
@@ -44,6 +47,45 @@ let AppI18nChecker = class AppI18nChecker {
44
47
  }
45
48
  }
46
49
  }
50
+ makeSureNonDefaultLanguagesAreFullyTranslated(i18n, ucDefSourcesCheckerOutput) {
51
+ for (const [lang, translations] of Object.entries(i18n)) {
52
+ if (lang === I18N_DEFAULT_LANG) {
53
+ // We assume the code is in English so it's fine not to have all the translations in this language.
54
+ // For instance, a field named "name" will be humanized as "Name", which is totally ok.
55
+ continue;
56
+ }
57
+ for (const { metadataName, ioIFields, ioOPI0Fields, ioOPI1Fields, } of ucDefSourcesCheckerOutput.items) {
58
+ this.checkUC(lang, translations, 'label', metadataName);
59
+ for (const [prefix, fields] of [
60
+ ['ucif', ioIFields],
61
+ ['ucof', ioOPI0Fields],
62
+ ['ucof', ioOPI1Fields],
63
+ ]) {
64
+ this.checkIOFields(lang, translations, prefix, 'label', fields);
65
+ }
66
+ }
67
+ }
68
+ }
69
+ checkUC(lang, translations, suffix, metadataName) {
70
+ if (!metadataName) {
71
+ return;
72
+ }
73
+ this.checkKey(lang, translations, `uc_${metadataName.value}_${suffix}`);
74
+ }
75
+ checkIOFields(lang, translations, prefix, suffix, fields) {
76
+ if (!fields) {
77
+ return;
78
+ }
79
+ for (const f of fields) {
80
+ this.checkKey(lang, translations, `${prefix}_${f.value.name}_${suffix}`);
81
+ }
82
+ }
83
+ checkKey(lang, translations, key) {
84
+ if (key in translations) {
85
+ return;
86
+ }
87
+ this.output.errors.push(ERR_I18N_MISSING_TRANS(key, lang));
88
+ }
47
89
  };
48
90
  AppI18nChecker = AppI18nChecker_1 = __decorate([
49
91
  injectable(),
@@ -18,7 +18,7 @@ const ERR_UCD_CONST_NAME = (name) => `The UCD const name '${name}' must follow t
18
18
  const ERR_UCD_FILE_SUFFIX = (fileName) => `The file '${fileName}' must end with ${UC_DEF_FILE_NAME_SUFFIX}`;
19
19
  const ERR_UCD_IMPORTS_EXTERNAL_ALLOWED = (aliasPrefix, allowed, text) => `External imports must be an alias '${aliasPrefix}*' or be one of ${allowed} (Got ${text})`;
20
20
  const ERR_UCD_IMPORTS_INTERNAL_MAX_DEPTH = (maxDepth) => `Internal imports must not be deeper than ${maxDepth}`;
21
- const ERR_UCD_INPUT_FIELD_PATTERN = () => `The input field def must match ${UC_INPUT_FIELD_PATTERN}`;
21
+ const ERR_UCD_INPUT_FIELD_DECLARATION = (raw) => `The input field def must match 'name: UCInputFieldValue<DataType>' (Got '${raw}')`;
22
22
  const ERR_UCD_INPUT_TYPE_NAME = (ucName) => `The input type must be named ${ucName}${UC_INPUT_SUFFIX}`;
23
23
  const ERR_UCD_OPI_TYPE_NAME = (ucName, idx) => `The OPI must be named ${ucName}${UC_OPI_SUFFIX}${idx}`;
24
24
  // TODO : Improve the check* methods that look a little bit hacky
@@ -93,16 +93,16 @@ let UCDefSourcesChecker = class UCDefSourcesChecker {
93
93
  };
94
94
  item.ioIFields = fields;
95
95
  for (const f of item.ioIFields) {
96
- const parts = f.value.split(':');
97
- if (parts.length !== 2) {
98
- continue;
96
+ const { dataType, name, raw, type } = f.value;
97
+ if (dataType &&
98
+ name &&
99
+ raw &&
100
+ type &&
101
+ type.match(new RegExp(UC_INPUT_FIELD_PATTERN)) !== null) {
102
+ }
103
+ else {
104
+ f.err = ERR_UCD_INPUT_FIELD_DECLARATION(raw);
99
105
  }
100
- const [_name, type] = parts;
101
- // trim because we process `name: Type` if the code is linted correctly
102
- f.err =
103
- type?.trim().match(new RegExp(UC_INPUT_FIELD_PATTERN)) !== null
104
- ? null
105
- : ERR_UCD_INPUT_FIELD_PATTERN();
106
106
  }
107
107
  }
108
108
  checkMainStep(lifecycle, step, item) {
@@ -120,7 +120,7 @@ let UCDefSourcesChecker = class UCDefSourcesChecker {
120
120
  item.lifecycleServerSteps.push({ err: null, value: step });
121
121
  break;
122
122
  default:
123
- ((_) => { })(lifecycle);
123
+ lifecycle;
124
124
  }
125
125
  }
126
126
  checkMetadata(metadata, item) {
@@ -177,7 +177,7 @@ let UCDefSourcesChecker = class UCDefSourcesChecker {
177
177
  };
178
178
  break;
179
179
  default:
180
- ((_) => { })(lifecycle);
180
+ lifecycle;
181
181
  }
182
182
  }
183
183
  async processFiles(appPath, paths) {
@@ -1,38 +1,59 @@
1
+ import type { EnumOf } from '../utils/index.js';
1
2
  /**
2
3
  * Mode of execution of a use case
3
- *
4
- * Unlike all the other enums, the values are lowercased for compatibility reasons.
5
- * Indeed, this value is persisted in the database so we need to support all existing deployments.
6
4
  */
7
- export declare enum UCExecMode {
5
+ export declare const UCExecMode: {
8
6
  /**
9
7
  * The use case has been executed programmatically
10
8
  */
11
- AUTO = "auto",
9
+ readonly AUTO: "auto";
12
10
  /**
13
11
  * The use case has been executed explicitly by a user
14
12
  */
15
- USER = "user"
16
- }
13
+ readonly USER: "user";
14
+ };
15
+ export type UCExecMode = EnumOf<typeof UCExecMode>;
17
16
  /**
18
17
  * Result of execution of a use case
19
- *
20
- * - `aborted` : The user aborted the exec (e.g. by not confirming)
21
- * - `failed` : The exec failed
22
- * - `succeeded` : The exec succeeded
23
18
  */
24
- export type UCExecRes = 'aborted' | 'failed' | 'succeeded';
19
+ export declare const UCExecRes: {
20
+ /**
21
+ * The user aborted the exec (e.g. by not confirming)
22
+ */
23
+ readonly ABORTED: "aborted";
24
+ /**
25
+ * The execution failed
26
+ */
27
+ readonly FAILED: "failed";
28
+ /**
29
+ * The execution succeeded
30
+ */
31
+ readonly SUCCEEDED: "succeeded";
32
+ };
33
+ export type UCExecRes = EnumOf<typeof UCExecRes>;
25
34
  /**
26
35
  * State of execution of a use case
27
- *
28
- * - `changing` : An action triggered a change, fields should be disabled
29
- * - `idle` : It can be touched and filled by the user
30
- * - `initializing` : It's initializing in some way, fields should be disabled
31
- * - `submitting` : It's submitting, fields should be disabled and some indicator should show the processing
32
- *
33
36
  * It applies to a form, but it can be applied and generalized to any other interaction mechanism (i.e. cli, voice...).
34
37
  */
35
- export type UCExecState = 'changing' | 'idle' | 'initializing' | 'submitting';
38
+ export declare const UCExecState: {
39
+ /**
40
+ * An action triggered a change, fields should be disabled
41
+ */
42
+ readonly CHANGING: "changing";
43
+ /**
44
+ * It can be touched and filled by the user
45
+ */
46
+ readonly IDLE: "idle";
47
+ /**
48
+ * It's initializing in some way, fields should be disabled
49
+ */
50
+ readonly INITIALIZING: "initializing";
51
+ /**
52
+ * It's submitting, fields should be disabled and some indicator should show the processing
53
+ */
54
+ readonly SUBMITTING: "submitting";
55
+ };
56
+ export type UCExecState = EnumOf<typeof UCExecState>;
36
57
  /**
37
58
  * Check whether the execution corresponds to a "disabled" state
38
59
  *
@@ -45,8 +66,8 @@ export declare function ucIsDisabled(execState: UCExecState): boolean;
45
66
  /**
46
67
  * Check whether the execution corresponds to a "loading" state
47
68
  *
48
- * Typically, this is used to show a `loading` indicator. Thus, it's `true` when `execState === 'submitting'`.
49
- * Note that it's not for `changing` or `initializing` because these are not triggered by the user per sé.
69
+ * Typically, this is used to show a `loading` indicator. Thus, it's `true` when `execState === UCExecState.SUBMITTING`.
70
+ * Note that it's not for `UCExecState.CHANGING` or `UCExecState.INITIALIZING` because these are not triggered by the user per sé.
50
71
  *
51
72
  * @param execState
52
73
  * @returns