libmodulor 0.24.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 (73) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +1 -1
  3. package/dist/esm/apps/Helper/src/lib/project.js +6 -6
  4. package/dist/esm/dt/Validation.d.ts +2 -2
  5. package/dist/esm/dt/base/TBase.d.ts +2 -0
  6. package/dist/esm/dt/base/TBase.js +3 -0
  7. package/dist/esm/dt/base/TObject.d.ts +6 -4
  8. package/dist/esm/dt/base/TObject.js +4 -5
  9. package/dist/esm/dt/base/TString.d.ts +2 -1
  10. package/dist/esm/dt/base/TString.js +22 -0
  11. package/dist/esm/dt/final/TFile.d.ts +13 -4
  12. package/dist/esm/dt/final/TFile.js +70 -8
  13. package/dist/esm/dt/final/TFileExtension.d.ts +1 -1
  14. package/dist/esm/dt/final/TFileMimeType.d.ts +2 -6
  15. package/dist/esm/dt/final/TFileMimeType.js +0 -6
  16. package/dist/esm/dt/final/TFilePath.d.ts +7 -0
  17. package/dist/esm/dt/final/TFilePath.js +5 -1
  18. package/dist/esm/dt/index.d.ts +1 -1
  19. package/dist/esm/i18n/WordingManager.d.ts +2 -1
  20. package/dist/esm/i18n/WordingManager.js +23 -2
  21. package/dist/esm/i18n/locales/de.js +8 -0
  22. package/dist/esm/i18n/locales/en.js +8 -0
  23. package/dist/esm/i18n/locales/es.js +8 -0
  24. package/dist/esm/i18n/locales/fr.js +8 -0
  25. package/dist/esm/i18n/types.d.ts +2 -2
  26. package/dist/esm/std/FSManager.d.ts +7 -5
  27. package/dist/esm/std/FSManager.js +5 -6
  28. package/dist/esm/std/FormDataBuilder.d.ts +2 -1
  29. package/dist/esm/std/impl/NodeFSManager.js +3 -2
  30. package/dist/esm/target/lib/react/StyleContextProvider.d.ts +1 -0
  31. package/dist/esm/target/lib/react/form.d.ts +1 -1
  32. package/dist/esm/target/lib/react/form.js +1 -0
  33. package/dist/esm/target/lib/react/useAction.d.ts +1 -1
  34. package/dist/esm/target/lib/react/useAction.js +13 -12
  35. package/dist/esm/target/lib/server-express/funcs.js +2 -1
  36. package/dist/esm/target/lib/web/input.d.ts +1 -0
  37. package/dist/esm/target/lib/web/input.js +5 -1
  38. package/dist/esm/target/react-native-pure/UCFormField.js +2 -1
  39. package/dist/esm/target/react-native-pure/UCFormFieldControl.js +6 -4
  40. package/dist/esm/target/react-native-pure/UCFormFieldErr.d.ts +1 -1
  41. package/dist/esm/target/react-native-pure/UCFormFieldErr.js +4 -1
  42. package/dist/esm/target/react-native-pure/UCFormFieldHelp.d.ts +4 -0
  43. package/dist/esm/target/react-native-pure/UCFormFieldHelp.js +15 -0
  44. package/dist/esm/target/react-web-pure/UCFormField.js +2 -1
  45. package/dist/esm/target/react-web-pure/UCFormFieldErr.d.ts +1 -1
  46. package/dist/esm/target/react-web-pure/UCFormFieldErr.js +4 -1
  47. package/dist/esm/target/react-web-pure/UCFormFieldHelp.d.ts +4 -0
  48. package/dist/esm/target/react-web-pure/UCFormFieldHelp.js +14 -0
  49. package/dist/esm/testing/impl/SimpleAppDocsEmitter/SimpleAppDocsEmitter.d.ts +7 -0
  50. package/dist/esm/testing/impl/SimpleAppDocsEmitter/SimpleAppDocsEmitter.js +59 -0
  51. package/dist/esm/testing/impl/SimpleAppDocsEmitter/markdown.d.ts +2 -0
  52. package/dist/esm/testing/impl/SimpleAppDocsEmitter/markdown.js +10 -0
  53. package/dist/esm/testing/impl/SimpleAppDocsEmitter/sequence-diagram.d.ts +2 -0
  54. package/dist/esm/testing/impl/SimpleAppDocsEmitter/sequence-diagram.js +92 -0
  55. package/dist/esm/testing/impl/SimpleAppDocsEmitter/tech-summary.d.ts +2 -0
  56. package/dist/esm/testing/impl/SimpleAppDocsEmitter/tech-summary.js +27 -0
  57. package/dist/esm/testing/impl/SimpleAppDocsEmitter/uc-summary.d.ts +2 -0
  58. package/dist/esm/testing/impl/SimpleAppDocsEmitter/uc-summary.js +63 -0
  59. package/dist/esm/testing/impl/newNodeAppTester.js +1 -1
  60. package/dist/esm/testing/uc-input.js +5 -2
  61. package/dist/esm/uc/exec.d.ts +42 -21
  62. package/dist/esm/uc/exec.js +48 -13
  63. package/dist/esm/uc/input-field.d.ts +6 -4
  64. package/dist/esm/uc/input-field.js +4 -5
  65. package/dist/esm/uc/side-effect.d.ts +10 -8
  66. package/dist/esm/uc/side-effect.js +5 -6
  67. package/dist/esm/uc/workers/UCInputFilesProcessor.js +3 -3
  68. package/dist/esm/uc/workers/UCInputValidator.js +2 -1
  69. package/dist/esm/uc/workers/UCOutputFilesProcessor.js +1 -1
  70. package/dist/esm/utils/bundling/webpack/loader.js +1 -1
  71. package/dist/esm/utils/index.d.ts +1 -1
  72. package/dist/esm/utils/types/utility-types.d.ts +4 -0
  73. package/package.json +16 -14
@@ -1,6 +1,5 @@
1
- export var FSManagerItemInfoType;
2
- (function (FSManagerItemInfoType) {
3
- FSManagerItemInfoType["DIR"] = "DIR";
4
- FSManagerItemInfoType["FILE"] = "FILE";
5
- FSManagerItemInfoType["OTHER"] = "OTHER";
6
- })(FSManagerItemInfoType || (FSManagerItemInfoType = {}));
1
+ export const FSManagerItemInfoType = {
2
+ DIR: 'DIR',
3
+ FILE: 'FILE',
4
+ OTHER: 'OTHER',
5
+ };
@@ -1,5 +1,6 @@
1
- import type { FileMimeType, FilePath } from '../dt/index.js';
1
+ import type { FileMimeType, FileName, FilePath } from '../dt/index.js';
2
2
  export interface BlobLike {
3
+ name: FileName;
3
4
  type: FileMimeType;
4
5
  uri: FilePath;
5
6
  }
@@ -94,15 +94,16 @@ let NodeFSManager = class NodeFSManager {
94
94
  }
95
95
  switch (source) {
96
96
  case 'path': {
97
- const { base: name, mimeType: type } = await this.info(path);
97
+ const { base: name, mimeType: type, size, } = await this.info(path);
98
98
  if (!type) {
99
99
  return [];
100
100
  }
101
101
  return [
102
102
  {
103
103
  name,
104
- path,
104
+ size,
105
105
  type,
106
+ uri: path,
106
107
  },
107
108
  ];
108
109
  }
@@ -18,6 +18,7 @@ export interface StyleContextT {
18
18
  formField?: StyleDef | undefined;
19
19
  formFieldControl?: Record<SelectiveStyleDefKey, StyleDef | undefined>;
20
20
  formFieldDesc?: StyleDef | undefined;
21
+ formFieldHelp?: StyleDef | undefined;
21
22
  formFieldErr?: StyleDef | undefined;
22
23
  formFieldLabel?: StyleDef | undefined;
23
24
  formSubmitControl?: StyleDef | undefined;
@@ -18,7 +18,7 @@ export interface UCFormFieldErrProps {
18
18
  export interface UCFormFieldLabelProps<T extends DataType> {
19
19
  f: UCInputField<T>;
20
20
  }
21
- export declare const UC_FORM_FIELD_ELEMENTS: readonly ["control", "desc", "err", "label"];
21
+ export declare const UC_FORM_FIELD_ELEMENTS: readonly ["control", "desc", "err", "help", "label"];
22
22
  export type UCFormFieldElement = (typeof UC_FORM_FIELD_ELEMENTS)[number];
23
23
  export type UCFormFieldProps<T extends DataType> = Omit<UCFormFieldControlProps<T>, 'onChange'> & {
24
24
  only?: UCFormFieldElement[];
@@ -3,6 +3,7 @@ export const UC_FORM_FIELD_ELEMENTS = [
3
3
  'control',
4
4
  'desc',
5
5
  'err',
6
+ 'help',
6
7
  'label',
7
8
  ];
8
9
  export function validateFormField(i18nManager, f) {
@@ -1,5 +1,5 @@
1
1
  import type { ErrorMessage, UIntDuration } from '../../../dt/index.js';
2
- import type { UCExecRes, UCExecState } from '../../../uc/index.js';
2
+ import { UCExecRes, UCExecState } from '../../../uc/index.js';
3
3
  export type UseActionAction = () => Promise<void>;
4
4
  export type UseActionConfirm = () => Promise<boolean>;
5
5
  export type UseActionExec = () => Promise<UCExecRes>;
@@ -1,54 +1,55 @@
1
1
  import { useEffect, useState } from 'react';
2
+ import { UCExecRes, UCExecState } from '../../../uc/index.js';
2
3
  import { sleep } from '../../../utils/index.js';
3
4
  export function useAction({ action, autoExec = false, confirm, onError, onInit, onStart, sleepInMs, }) {
4
5
  const [errMsg, setErrMsg] = useState(null);
5
6
  const [execRes, setExecRes] = useState(null);
6
- const [execState, setExecState] = useState(onInit ? 'initializing' : 'idle');
7
+ const [execState, setExecState] = useState(onInit ? UCExecState.INITIALIZING : UCExecState.IDLE);
7
8
  // biome-ignore lint/correctness/useExhaustiveDependencies : must run only once
8
9
  useEffect(() => {
9
10
  (async () => {
10
- if (execState === 'initializing') {
11
+ if (execState === UCExecState.INITIALIZING) {
11
12
  await onInit?.();
12
13
  }
13
14
  if (autoExec) {
14
15
  await exec();
15
16
  }
16
17
  else {
17
- setExecState('idle');
18
+ setExecState(UCExecState.IDLE);
18
19
  }
19
20
  })();
20
21
  }, []);
21
22
  const exec = async () => {
22
23
  setErrMsg(null);
23
24
  setExecRes(null);
24
- setExecState('submitting');
25
+ setExecState(UCExecState.SUBMITTING);
25
26
  await onStart?.();
26
27
  const confirmed = confirm ? await confirm?.() : true;
27
28
  if (!confirmed) {
28
- setExecState('idle');
29
- setExecRes('aborted');
30
- return 'aborted';
29
+ setExecState(UCExecState.IDLE);
30
+ setExecRes(UCExecRes.ABORTED);
31
+ return UCExecRes.ABORTED;
31
32
  }
32
33
  if (sleepInMs !== undefined) {
33
34
  await sleep(sleepInMs);
34
35
  }
35
36
  try {
36
37
  await action();
37
- setExecRes('succeeded');
38
- return 'succeeded';
38
+ setExecRes(UCExecRes.SUCCEEDED);
39
+ return UCExecRes.SUCCEEDED;
39
40
  }
40
41
  catch (err) {
41
- setExecRes('failed');
42
+ setExecRes(UCExecRes.FAILED);
42
43
  if (onError) {
43
44
  await onError?.(err);
44
45
  }
45
46
  else {
46
47
  setErrMsg(err.message);
47
48
  }
48
- return 'failed';
49
+ return UCExecRes.FAILED;
49
50
  }
50
51
  finally {
51
- setExecState('idle');
52
+ setExecState(UCExecState.IDLE);
52
53
  }
53
54
  };
54
55
  return { errMsg, exec, execRes, execState };
@@ -97,8 +97,9 @@ export function mountHandler(contract, express, handler) {
97
97
  export function toFile(f) {
98
98
  return {
99
99
  name: f.name,
100
- path: f.tempFilePath,
100
+ size: f.size,
101
101
  type: f.mimetype,
102
+ uri: f.tempFilePath,
102
103
  };
103
104
  }
104
105
  export function toReq(req) {
@@ -34,6 +34,7 @@ export interface HTMLInputDef {
34
34
  * @see node_modules/@types/react/index.d.ts `InputHTMLAttributes`
35
35
  */
36
36
  spec?: {
37
+ accept?: string | undefined;
37
38
  'aria-errormessage'?: string | undefined;
38
39
  'aria-invalid'?: boolean | undefined;
39
40
  disabled?: boolean | undefined;
@@ -1,4 +1,4 @@
1
- import { TBoolean, TNumber, TString, } from '../../../dt/index.js';
1
+ import { TBoolean, TFile, TNumber, TString, } from '../../../dt/index.js';
2
2
  import { ucifHint, ucifId, ucifIsMandatory, } from '../../../uc/index.js';
3
3
  export function htmlInputDef(field, disabled, errMsg) {
4
4
  const def = {
@@ -34,6 +34,10 @@ export function htmlInputDef(field, disabled, errMsg) {
34
34
  else if (fType instanceof TBoolean) {
35
35
  def.internal.checked = fType.getInitialValue() === true;
36
36
  }
37
+ else if (fType instanceof TFile) {
38
+ const constraints = fType.getFileConstraints();
39
+ def.spec.accept = constraints.accept.join(',');
40
+ }
37
41
  if (!(fType instanceof TBoolean)) {
38
42
  def.internal.value = fType.getInitialValue()?.toString() || '';
39
43
  }
@@ -7,6 +7,7 @@ import { useStyleContext } from '../lib/react/StyleContextProvider.js';
7
7
  import { UCFormFieldControl } from './UCFormFieldControl.js';
8
8
  import { UCFormFieldDesc } from './UCFormFieldDesc.js';
9
9
  import { UCFormFieldErr } from './UCFormFieldErr.js';
10
+ import { UCFormFieldHelp } from './UCFormFieldHelp.js';
10
11
  import { UCFormFieldLabel } from './UCFormFieldLabel.js';
11
12
  export function UCFormField({ disabled, execState, f, only, }) {
12
13
  const { i18nManager } = useDIContext();
@@ -16,5 +17,5 @@ export function UCFormField({ disabled, execState, f, only, }) {
16
17
  setErrMsg(validateFormField(i18nManager, f));
17
18
  };
18
19
  const elements = only ?? UC_FORM_FIELD_ELEMENTS;
19
- return (_jsxs(View, { style: formField?.style, children: [elements.includes('label') && _jsx(UCFormFieldLabel, { f: f }), elements.includes('control') && (_jsx(UCFormFieldControl, { disabled: disabled, execState: execState, f: f, onChange: onChange })), elements.includes('err') && errMsg && (_jsx(UCFormFieldErr, { errMsg: errMsg })), elements.includes('desc') && _jsx(UCFormFieldDesc, { f: f })] }));
20
+ return (_jsxs(View, { style: formField?.style, children: [elements.includes('label') && _jsx(UCFormFieldLabel, { f: f }), elements.includes('control') && (_jsx(UCFormFieldControl, { disabled: disabled, execState: execState, f: f, onChange: onChange })), elements.includes('desc') && _jsx(UCFormFieldDesc, { f: f }), elements.includes('help') && _jsx(UCFormFieldHelp, { f: f }), elements.includes('err') && _jsx(UCFormFieldErr, { errMsg: errMsg })] }));
20
21
  }
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
3
  import { FlatList, Pressable, StyleSheet, Switch, Text, TextInput, } from 'react-native';
4
- import { TBoolean } from '../../dt/index.js';
4
+ import { TBoolean, TFile } from '../../dt/index.js';
5
5
  import { ucifRepeatability } from '../../uc/index.js';
6
6
  import { isBlank } from '../../utils/index.js';
7
7
  import { styleDef, useStyleContext, } from '../lib/react/StyleContextProvider.js';
@@ -11,7 +11,7 @@ const MULTIPLE_VALUES_SEPARATOR = ',';
11
11
  export function UCFormFieldControl({ disabled, errMsg = null, execState, f, onChange: onChangeBase, }) {
12
12
  const { colors, formFieldControl, renderFormFieldControl } = useStyleContext();
13
13
  const [internalValue, setInternalValue] = useState(f.getValue());
14
- // biome-ignore lint/correctness/useExhaustiveDependencies: false positive : It is actually necessary (only `f` does not trigger the effect)
14
+ // biome-ignore lint/correctness/useExhaustiveDependencies: It is actually necessary because only `f` or `f.getValue` does not trigger the effect
15
15
  useEffect(() => {
16
16
  setInternalValue(f.getValue());
17
17
  }, [f.getValue()]);
@@ -25,6 +25,10 @@ export function UCFormFieldControl({ disabled, errMsg = null, execState, f, onCh
25
25
  if (component) {
26
26
  return component;
27
27
  }
28
+ const { type } = f.def;
29
+ if (type instanceof TFile) {
30
+ return (_jsx(Text, { style: { color: 'red' }, children: "Generic file picker not available in RN" }));
31
+ }
28
32
  const onChangeText = (value) => {
29
33
  const [isRepeatable] = ucifRepeatability(f.def);
30
34
  if (isRepeatable && typeof value === 'string') {
@@ -58,7 +62,6 @@ export function UCFormFieldControl({ disabled, errMsg = null, execState, f, onCh
58
62
  setInternalValue(value);
59
63
  };
60
64
  const attrs = rnInputDef(f, disabled, errMsg);
61
- const { type } = f.def;
62
65
  const options = type.getOptions();
63
66
  if (options) {
64
67
  // TODO : Handle type.hasStrictOptions() => display an input text alongside the options
@@ -72,7 +75,6 @@ export function UCFormFieldControl({ disabled, errMsg = null, execState, f, onCh
72
75
  },
73
76
  ], children: _jsx(Text, { children: item.label }) })), showsHorizontalScrollIndicator: false, style: styles.select }));
74
77
  }
75
- // TODO : Implement picker for TFile (requires a dependency...)
76
78
  if (type instanceof TBoolean) {
77
79
  const { style } = styleDef(formFieldControl, 'Switch');
78
80
  return (_jsx(Switch, { disabled: !attrs.spec?.editable, onValueChange: onValueChange, style: style, trackColor: { true: colors?.primary }, value: internalValue }));
@@ -1,3 +1,3 @@
1
1
  import type { ReactElement } from 'react';
2
2
  import type { UCFormFieldErrProps } from '../lib/react/form.js';
3
- export declare function UCFormFieldErr({ errMsg }: UCFormFieldErrProps): ReactElement;
3
+ export declare function UCFormFieldErr({ errMsg, }: UCFormFieldErrProps): ReactElement | null;
@@ -1,7 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Text } from 'react-native';
3
3
  import { useStyleContext } from '../lib/react/StyleContextProvider.js';
4
- export function UCFormFieldErr({ errMsg }) {
4
+ export function UCFormFieldErr({ errMsg, }) {
5
5
  const { formFieldErr } = useStyleContext();
6
+ if (!errMsg) {
7
+ return null;
8
+ }
6
9
  return _jsx(Text, { style: formFieldErr?.style, children: errMsg });
7
10
  }
@@ -0,0 +1,4 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { DataType } from '../../dt/index.js';
3
+ import type { UCFormFieldDescProps } from '../lib/react/form.js';
4
+ export declare function UCFormFieldHelp<T extends DataType>({ f, }: UCFormFieldDescProps<T>): ReactElement | null;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Text } from 'react-native';
3
+ import { useDIContext } from '../../index.react.js';
4
+ import { useStyleContext } from '../lib/react/StyleContextProvider.js';
5
+ export function UCFormFieldHelp({ f, }) {
6
+ const { wordingManager } = useDIContext();
7
+ const { formFieldHelp } = useStyleContext();
8
+ const { def: { type }, } = f;
9
+ const parts = wordingManager.dtConstr(type);
10
+ if (!parts) {
11
+ return null;
12
+ }
13
+ const text = parts.join(' - ');
14
+ return _jsx(Text, { style: formFieldHelp?.style, children: text });
15
+ }
@@ -6,6 +6,7 @@ import { useStyleContext } from '../lib/react/StyleContextProvider.js';
6
6
  import { UCFormFieldControl } from './UCFormFieldControl.js';
7
7
  import { UCFormFieldDesc } from './UCFormFieldDesc.js';
8
8
  import { UCFormFieldErr } from './UCFormFieldErr.js';
9
+ import { UCFormFieldHelp } from './UCFormFieldHelp.js';
9
10
  import { UCFormFieldLabel } from './UCFormFieldLabel.js';
10
11
  export function UCFormField({ disabled, execState, f, only, }) {
11
12
  const { i18nManager } = useDIContext();
@@ -15,5 +16,5 @@ export function UCFormField({ disabled, execState, f, only, }) {
15
16
  setErrMsg(validateFormField(i18nManager, f));
16
17
  };
17
18
  const elements = only ?? UC_FORM_FIELD_ELEMENTS;
18
- return (_jsxs("div", { className: formField?.className, style: formField?.style, children: [elements.includes('label') && _jsx(UCFormFieldLabel, { f: f }), elements.includes('control') && (_jsx(UCFormFieldControl, { disabled: disabled, execState: execState, f: f, onChange: onChange })), elements.includes('err') && errMsg && (_jsx(UCFormFieldErr, { errMsg: errMsg })), elements.includes('desc') && _jsx(UCFormFieldDesc, { f: f })] }));
19
+ return (_jsxs("div", { className: formField?.className, style: formField?.style, children: [elements.includes('label') && _jsx(UCFormFieldLabel, { f: f }), elements.includes('control') && (_jsx(UCFormFieldControl, { disabled: disabled, execState: execState, f: f, onChange: onChange })), elements.includes('desc') && _jsx(UCFormFieldDesc, { f: f }), elements.includes('help') && _jsx(UCFormFieldHelp, { f: f }), elements.includes('err') && _jsx(UCFormFieldErr, { errMsg: errMsg })] }));
19
20
  }
@@ -1,3 +1,3 @@
1
1
  import type { ReactElement } from 'react';
2
2
  import type { UCFormFieldErrProps } from '../lib/react/form.js';
3
- export declare function UCFormFieldErr({ errMsg }: UCFormFieldErrProps): ReactElement;
3
+ export declare function UCFormFieldErr({ errMsg, }: UCFormFieldErrProps): ReactElement | null;
@@ -1,6 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useStyleContext } from '../lib/react/StyleContextProvider.js';
3
- export function UCFormFieldErr({ errMsg }) {
3
+ export function UCFormFieldErr({ errMsg, }) {
4
4
  const { formFieldErr } = useStyleContext();
5
+ if (!errMsg) {
6
+ return null;
7
+ }
5
8
  return (_jsx("div", { className: formFieldErr?.className, style: formFieldErr?.style, children: errMsg }));
6
9
  }
@@ -0,0 +1,4 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { DataType } from '../../dt/index.js';
3
+ import type { UCFormFieldDescProps } from '../lib/react/form.js';
4
+ export declare function UCFormFieldHelp<T extends DataType>({ f, }: UCFormFieldDescProps<T>): ReactElement | null;
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useDIContext } from '../../index.react.js';
3
+ import { useStyleContext } from '../lib/react/StyleContextProvider.js';
4
+ export function UCFormFieldHelp({ f, }) {
5
+ const { wordingManager } = useDIContext();
6
+ const { formFieldHelp } = useStyleContext();
7
+ const { def: { type }, } = f;
8
+ const parts = wordingManager.dtConstr(type);
9
+ if (!parts) {
10
+ return null;
11
+ }
12
+ const text = parts.join(' - ');
13
+ return (_jsx("div", { className: formFieldHelp?.className, style: formFieldHelp?.style, children: text }));
14
+ }
@@ -0,0 +1,7 @@
1
+ import type { FSManager } from '../../../std/index.js';
2
+ import type { AppDocsEmitter, Input, Output } from '../../workers/AppDocsEmitter.js';
3
+ export declare class SimpleAppDocsEmitter implements AppDocsEmitter {
4
+ private fsManager;
5
+ constructor(fsManager: FSManager);
6
+ exec({ appPath, ucDefSourcesCheckerOutput, }: Input): Promise<Output>;
7
+ }
@@ -0,0 +1,59 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { inject, injectable } from 'inversify';
14
+ import { APP_DOCS_FILE_NAME } from '../../../convention.js';
15
+ import { techSummary } from './tech-summary.js';
16
+ import { ucSummary } from './uc-summary.js';
17
+ let SimpleAppDocsEmitter = class SimpleAppDocsEmitter {
18
+ fsManager;
19
+ constructor(fsManager) {
20
+ this.fsManager = fsManager;
21
+ }
22
+ async exec({ appPath, ucDefSourcesCheckerOutput, }) {
23
+ const outPath = this.fsManager.path(appPath, APP_DOCS_FILE_NAME);
24
+ const tpl = template(ucDefSourcesCheckerOutput.items);
25
+ await this.fsManager.touch(outPath, tpl);
26
+ return {
27
+ outPath,
28
+ };
29
+ }
30
+ };
31
+ SimpleAppDocsEmitter = __decorate([
32
+ injectable(),
33
+ __param(0, inject('FSManager')),
34
+ __metadata("design:paramtypes", [Object])
35
+ ], SimpleAppDocsEmitter);
36
+ export { SimpleAppDocsEmitter };
37
+ // For now, we can have it here. When it becomes harder to maintain, we can introduce some kind of template engine.
38
+ // Be aware that this will introduce complexities on building the lib.
39
+ // We'll need to include these templates in the build and make them accessible via package.json "exports" or any other mechanism.
40
+ // Hence the choice to keep it simple for now.
41
+ // Defined it as function in case we need to pass args.
42
+ // Using --- for the comment to make it compatible with pandoc
43
+ // See https://stackoverflow.com/a/4829998/1259118
44
+ const template = (items) => `<!---
45
+ All this code has been auto generated.
46
+ DO NOT EDIT.
47
+ Or be prepared to see all your changes erased at the next generation.
48
+ -->
49
+
50
+ # App
51
+
52
+ ## Use Cases
53
+
54
+ ${items.map(ucSummary).join('\n\n')}
55
+
56
+ ## Technical Summary
57
+
58
+ ${techSummary(items)}
59
+ `;
@@ -0,0 +1,2 @@
1
+ export declare function pre(val: string | null | undefined): string;
2
+ export declare function thead(cols: string[]): string;
@@ -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
+ }
@@ -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;