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.
- package/CHANGELOG.md +34 -0
- package/README.md +154 -2
- package/dist/esm/apps/Helper/src/lib/project.js +6 -6
- package/dist/esm/apps/Helper/src/ucds/TestAppUCD.d.ts +2 -1
- package/dist/esm/apps/Helper/src/ucds/TestAppUCD.js +8 -0
- package/dist/esm/convention.d.ts +3 -0
- package/dist/esm/convention.js +3 -0
- package/dist/esm/dt/DataTypes.d.ts +1 -2
- package/dist/esm/dt/Validation.d.ts +2 -2
- package/dist/esm/dt/base/TBase.d.ts +2 -0
- package/dist/esm/dt/base/TBase.js +3 -0
- package/dist/esm/dt/base/TObject.d.ts +6 -4
- package/dist/esm/dt/base/TObject.js +5 -6
- package/dist/esm/dt/base/TString.d.ts +2 -1
- package/dist/esm/dt/base/TString.js +22 -0
- package/dist/esm/dt/final/TFile.d.ts +13 -4
- package/dist/esm/dt/final/TFile.js +70 -8
- package/dist/esm/dt/final/TFileExtension.d.ts +1 -1
- package/dist/esm/dt/final/TFileMimeType.d.ts +2 -6
- package/dist/esm/dt/final/TFileMimeType.js +0 -6
- package/dist/esm/dt/final/TFilePath.d.ts +7 -0
- package/dist/esm/dt/final/TFilePath.js +5 -1
- package/dist/esm/dt/index.d.ts +1 -1
- package/dist/esm/i18n/WordingManager.d.ts +2 -1
- package/dist/esm/i18n/WordingManager.js +23 -2
- package/dist/esm/i18n/index.d.ts +1 -1
- package/dist/esm/i18n/locales/de.d.ts +2 -0
- package/dist/esm/i18n/locales/de.js +62 -0
- package/dist/esm/i18n/locales/en.js +8 -0
- package/dist/esm/i18n/locales/es.d.ts +2 -0
- package/dist/esm/i18n/locales/es.js +62 -0
- package/dist/esm/i18n/locales/fr.js +8 -0
- package/dist/esm/i18n/types.d.ts +5 -4
- package/dist/esm/index.babel.d.ts +1 -0
- package/dist/esm/index.babel.js +1 -0
- package/dist/esm/index.node-stricli-cli.d.ts +2 -0
- package/dist/esm/index.node-stricli-cli.js +2 -0
- package/dist/esm/index.vite.d.ts +1 -1
- package/dist/esm/index.vite.js +1 -1
- package/dist/esm/index.webpack.d.ts +1 -0
- package/dist/esm/index.webpack.js +1 -0
- package/dist/esm/std/FSManager.d.ts +7 -5
- package/dist/esm/std/FSManager.js +5 -6
- package/dist/esm/std/FormDataBuilder.d.ts +2 -1
- package/dist/esm/std/I18nManager.d.ts +12 -0
- package/dist/esm/std/impl/NodeFSManager.js +4 -3
- package/dist/esm/std/impl/SimpleMapI18nManager.d.ts +6 -1
- package/dist/esm/std/impl/SimpleMapI18nManager.js +41 -12
- package/dist/esm/target/lib/react/StyleContextProvider.d.ts +1 -0
- package/dist/esm/target/lib/react/form.d.ts +1 -1
- package/dist/esm/target/lib/react/form.js +1 -0
- package/dist/esm/target/lib/react/useAction.d.ts +1 -1
- package/dist/esm/target/lib/react/useAction.js +13 -12
- package/dist/esm/target/lib/server/AuthenticationChecker.js +1 -1
- package/dist/esm/target/lib/server/PublicApiKeyChecker.js +1 -1
- package/dist/esm/target/lib/server/ServerRequestHandler.js +2 -2
- package/dist/esm/target/lib/server-express/funcs.js +4 -3
- package/dist/esm/target/lib/server-hono/funcs.js +2 -2
- package/dist/esm/target/lib/server-node/funcs.js +1 -1
- package/dist/esm/target/lib/web/input.d.ts +1 -0
- package/dist/esm/target/lib/web/input.js +5 -1
- package/dist/esm/target/node-stricli-cli/NodeStricliCLIManager.d.ts +12 -0
- package/dist/esm/target/node-stricli-cli/NodeStricliCLIManager.js +118 -0
- package/dist/esm/target/react-native-pure/UCFormField.js +2 -1
- package/dist/esm/target/react-native-pure/UCFormFieldControl.js +6 -4
- package/dist/esm/target/react-native-pure/UCFormFieldErr.d.ts +1 -1
- package/dist/esm/target/react-native-pure/UCFormFieldErr.js +4 -1
- package/dist/esm/target/react-native-pure/UCFormFieldHelp.d.ts +4 -0
- package/dist/esm/target/react-native-pure/UCFormFieldHelp.js +15 -0
- package/dist/esm/target/react-web-pure/UCFormField.js +2 -1
- package/dist/esm/target/react-web-pure/UCFormFieldErr.d.ts +1 -1
- package/dist/esm/target/react-web-pure/UCFormFieldErr.js +4 -1
- package/dist/esm/target/react-web-pure/UCFormFieldHelp.d.ts +4 -0
- package/dist/esm/target/react-web-pure/UCFormFieldHelp.js +14 -0
- package/dist/esm/testing/AppTester.js +4 -5
- package/dist/esm/testing/UCDefASTParser.d.ts +24 -6
- package/dist/esm/testing/impl/SimpleAppDocsEmitter/SimpleAppDocsEmitter.d.ts +7 -0
- package/dist/esm/testing/impl/SimpleAppDocsEmitter/SimpleAppDocsEmitter.js +59 -0
- package/dist/esm/testing/impl/SimpleAppDocsEmitter/markdown.d.ts +2 -0
- package/dist/esm/testing/impl/SimpleAppDocsEmitter/markdown.js +10 -0
- package/dist/esm/testing/impl/SimpleAppDocsEmitter/sequence-diagram.d.ts +2 -0
- package/dist/esm/testing/impl/SimpleAppDocsEmitter/sequence-diagram.js +92 -0
- package/dist/esm/testing/impl/SimpleAppDocsEmitter/tech-summary.d.ts +2 -0
- package/dist/esm/testing/impl/SimpleAppDocsEmitter/tech-summary.js +27 -0
- package/dist/esm/testing/impl/SimpleAppDocsEmitter/uc-summary.d.ts +2 -0
- package/dist/esm/testing/impl/SimpleAppDocsEmitter/uc-summary.js +63 -0
- package/dist/esm/testing/impl/SimpleAppDocsEmitter.js +4 -5
- package/dist/esm/testing/impl/TypeScriptLibUCDefASTParser.js +38 -11
- package/dist/esm/testing/impl/VitestAppTestSuiteRunner.d.ts +1 -1
- package/dist/esm/testing/impl/VitestAppTestSuiteRunner.js +17 -2
- package/dist/esm/testing/impl/newNodeAppTester.js +1 -1
- package/dist/esm/testing/opts.js +1 -1
- package/dist/esm/testing/uc-input.js +5 -2
- package/dist/esm/testing/workers/AppTestSuiteRunner.d.ts +2 -0
- package/dist/esm/testing/workers/UCExecutor.js +1 -1
- package/dist/esm/testing/workers/checkers/AppI18nChecker.d.ts +8 -2
- package/dist/esm/testing/workers/checkers/AppI18nChecker.js +44 -2
- package/dist/esm/testing/workers/checkers/UCDefSourcesChecker.js +12 -12
- package/dist/esm/uc/exec.d.ts +42 -21
- package/dist/esm/uc/exec.js +48 -13
- package/dist/esm/uc/impl/HTTPUCTransporter.js +2 -2
- package/dist/esm/uc/impl/KnexUCDataStore.js +2 -2
- package/dist/esm/uc/index.d.ts +0 -1
- package/dist/esm/uc/index.js +0 -1
- package/dist/esm/uc/input-field.d.ts +6 -4
- package/dist/esm/uc/input-field.js +4 -5
- package/dist/esm/uc/side-effect.d.ts +10 -8
- package/dist/esm/uc/side-effect.js +5 -6
- package/dist/esm/uc/workers/UCExecChecker.js +1 -1
- package/dist/esm/uc/workers/UCInputFilesProcessor.js +3 -3
- package/dist/esm/uc/workers/UCInputValidator.js +2 -1
- package/dist/esm/uc/workers/UCOutputFilesProcessor.js +1 -1
- package/dist/esm/utils/bundling/babel/plugin.d.ts +2 -0
- package/dist/esm/utils/bundling/babel/plugin.js +38 -0
- package/dist/esm/utils/bundling/funcs.d.ts +6 -0
- package/dist/esm/utils/bundling/funcs.js +20 -0
- package/dist/esm/utils/bundling/typescript.d.ts +3 -0
- package/dist/esm/utils/bundling/typescript.js +61 -0
- package/dist/esm/utils/bundling/vite/plugin.d.ts +6 -0
- package/dist/esm/utils/bundling/vite/plugin.js +17 -0
- package/dist/esm/utils/bundling/webpack/loader.d.ts +2 -0
- package/dist/esm/utils/bundling/webpack/loader.js +12 -0
- package/dist/esm/utils/http/HTTPRequestBuilder.js +1 -1
- package/dist/esm/utils/index.d.ts +1 -1
- package/dist/esm/utils/ioc/bindCommon.js +1 -1
- package/dist/esm/utils/terminal/fmt.js +1 -1
- package/dist/esm/utils/types/utility-types.d.ts +4 -0
- package/package.json +36 -12
- package/pnpm-workspace.yaml +1 -4
- package/tsconfig.build.examples.json +8 -0
- package/tsconfig.json +1 -0
- package/vitest.config.ts +16 -0
|
@@ -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,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,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,
|
|
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
|
-
|
|
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
|
|
103
|
+
const fields = type
|
|
104
|
+
.getProperties()
|
|
105
|
+
.map((p) => {
|
|
103
106
|
const field = {
|
|
104
107
|
err: null,
|
|
105
|
-
value:
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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(
|
|
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;
|
package/dist/esm/testing/opts.js
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
97
|
-
if (
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
|
|
180
|
+
lifecycle;
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
async processFiles(appPath, paths) {
|
package/dist/esm/uc/exec.d.ts
CHANGED
|
@@ -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
|
|
5
|
+
export declare const UCExecMode: {
|
|
8
6
|
/**
|
|
9
7
|
* The use case has been executed programmatically
|
|
10
8
|
*/
|
|
11
|
-
AUTO
|
|
9
|
+
readonly AUTO: "auto";
|
|
12
10
|
/**
|
|
13
11
|
* The use case has been executed explicitly by a user
|
|
14
12
|
*/
|
|
15
|
-
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
|
|
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
|
|
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 ===
|
|
49
|
-
* Note that it's not for `
|
|
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
|