libmodulor 0.2.0 → 0.4.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 (57) hide show
  1. package/.github/ISSUE_TEMPLATE/generic-issue.md +16 -0
  2. package/CHANGELOG.md +31 -0
  3. package/README.md +7 -182
  4. package/dist/esm/index.react-native-pure.d.ts +10 -0
  5. package/dist/esm/index.react-native-pure.js +10 -0
  6. package/dist/esm/target/lib/react/entrypoint.d.ts +2 -0
  7. package/dist/esm/target/lib/rn/input.d.ts +8 -0
  8. package/dist/esm/target/lib/rn/input.js +26 -0
  9. package/dist/esm/target/node-express-server/NodeExpressServerManager.js +4 -1
  10. package/dist/esm/target/react-native-pure/UCAutoExecLoader.d.ts +2 -0
  11. package/dist/esm/target/react-native-pure/UCAutoExecLoader.js +5 -0
  12. package/dist/esm/target/react-native-pure/UCEntrypointTouchable.d.ts +4 -0
  13. package/dist/esm/target/react-native-pure/UCEntrypointTouchable.js +6 -0
  14. package/dist/esm/target/react-native-pure/UCExecTouchable.d.ts +4 -0
  15. package/dist/esm/target/react-native-pure/UCExecTouchable.js +9 -0
  16. package/dist/esm/target/react-native-pure/UCForm.d.ts +4 -0
  17. package/dist/esm/target/react-native-pure/UCForm.js +13 -0
  18. package/dist/esm/target/react-native-pure/UCFormField.d.ts +11 -0
  19. package/dist/esm/target/react-native-pure/UCFormField.js +32 -0
  20. package/dist/esm/target/react-native-pure/UCFormFieldControl.d.ts +11 -0
  21. package/dist/esm/target/react-native-pure/UCFormFieldControl.js +36 -0
  22. package/dist/esm/target/react-native-pure/UCFormFieldDesc.d.ts +7 -0
  23. package/dist/esm/target/react-native-pure/UCFormFieldDesc.js +11 -0
  24. package/dist/esm/target/react-native-pure/UCFormFieldErr.d.ts +7 -0
  25. package/dist/esm/target/react-native-pure/UCFormFieldErr.js +5 -0
  26. package/dist/esm/target/react-native-pure/UCFormFieldLabel.d.ts +7 -0
  27. package/dist/esm/target/react-native-pure/UCFormFieldLabel.js +8 -0
  28. package/dist/esm/target/react-native-pure/UCFormSubmitControl.d.ts +9 -0
  29. package/dist/esm/target/react-native-pure/UCFormSubmitControl.js +8 -0
  30. package/dist/esm/target/react-web-pure/UCEntrypointTouchable.d.ts +1 -1
  31. package/dist/esm/target/react-web-pure/UCExecTouchable.d.ts +1 -1
  32. package/dist/esm/target/react-web-pure/UCExecTouchable.js +1 -1
  33. package/dist/esm/target/react-web-pure/UCFormFieldControl.d.ts +1 -1
  34. package/dist/esm/target/react-web-pure/UCFormFieldControl.js +3 -3
  35. package/dist/esm/uc/ext.d.ts +3 -1
  36. package/dist/esm/uc/helpers/UCOutputReader.d.ts +1 -1
  37. package/dist/esm/uc/helpers/UCOutputReader.js +4 -2
  38. package/dist/esm/uc/output-part.d.ts +2 -1
  39. package/dist/esm/uc/utils/ucHTTPContract.d.ts +1 -0
  40. package/dist/esm/uc/utils/ucHTTPContract.js +5 -0
  41. package/package.json +12 -5
  42. package/docs/assets/trading-buy-asset-sequence-diagram.png +0 -0
  43. package/docs/assets/trading-target-mcp-server.png +0 -0
  44. package/docs/assets/trading-target-web-human.png +0 -0
  45. package/docs/assets/trading-target-web.png +0 -0
  46. package/docs/getting-started/001_Create_the_project.md +0 -168
  47. package/docs/getting-started/002_Create_the_App.md +0 -49
  48. package/docs/getting-started/003_Create_the_UseCase.md +0 -205
  49. package/docs/getting-started/004_Test_the_App.md +0 -114
  50. package/docs/getting-started/005_Create_the_Product.md +0 -46
  51. package/docs/getting-started/006_Create_the_server_Target.md +0 -130
  52. package/docs/getting-started/007_Create_the_web_Target.md +0 -262
  53. package/docs/getting-started/008_Switch_to_a_persistent_data_storage.md +0 -52
  54. package/docs/getting-started/009_Define_wording_for_humans.md +0 -42
  55. package/docs/getting-started/010_Create_the_cli_Target.md +0 -102
  56. package/docs/getting-started/011_Create_the_mcp_server_Target.md +0 -157
  57. package/docs/getting-started/012_Summary.md +0 -29
@@ -1,114 +0,0 @@
1
- # Test the App
2
-
3
- ## Preliminary test
4
-
5
- By default, we rely on [vitest](https://vitest.dev) to run the tests and [@vitest/coverage-v8](https://vitest.dev/guide/coverage) for the coverage.
6
-
7
- ```sh
8
- yarn test
9
- ```
10
-
11
- ```sh
12
- % Coverage report from v8
13
- ------------------------|---------|----------|---------|---------|-------------------
14
- File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
15
- ------------------------|---------|----------|---------|---------|-------------------
16
- All files | 0 | 40 | 40 | 0 |
17
- Trading | 0 | 0 | 0 | 0 |
18
- index.ts | 0 | 0 | 0 | 0 | 1
19
- Trading/src | 0 | 100 | 100 | 0 |
20
- i18n.ts | 0 | 100 | 100 | 0 | 3-5
21
- manifest.ts | 0 | 100 | 100 | 0 | 3-14
22
- Trading/src/ucds | 0 | 0 | 0 | 0 |
23
- BuyAssetServerMain.ts | 0 | 0 | 0 | 0 | 1-28
24
- BuyAssetUCD.ts | 0 | 0 | 0 | 0 | 1-92
25
- ------------------------|---------|----------|---------|---------|-------------------
26
- ```
27
-
28
- As expected, the coverage report is pretty lame. Which is understandable, since we haven't written any tests yet.
29
-
30
- ```sh
31
- mkdir src/apps/Trading/test
32
- touch src/apps/Trading/test/Configurator.ts
33
- ```
34
-
35
- ## Configurator.ts
36
-
37
- ```typescript
38
- import { type AppTesterCtx, type CryptoManager, bindCommon } from 'libmodulor';
39
- import {
40
- NodeDeterministicCryptoManager,
41
- bindNodeCore,
42
- bindServer,
43
- } from 'libmodulor/node';
44
- import { SimpleAppTesterConfigurator } from 'libmodulor/node-test';
45
- import { injectable } from 'inversify';
46
-
47
- @injectable()
48
- export class Configurator extends SimpleAppTesterConfigurator {
49
- public override async bindImplementations(
50
- ctx: AppTesterCtx,
51
- ): Promise<void> {
52
- await super.bindImplementations(ctx);
53
-
54
- const { container } = ctx;
55
-
56
- bindCommon(container);
57
- bindNodeCore(container);
58
- bindServer(container);
59
-
60
- container
61
- .rebind<CryptoManager>('CryptoManager')
62
- .to(NodeDeterministicCryptoManager);
63
- }
64
- }
65
- ```
66
-
67
- ## Automated test
68
-
69
- Generate the automated tests and execute them with the CLI (it does more than a simple `yarn test`).
70
-
71
- ```sh
72
- yarn helper GenerateAppsTests
73
- yarn helper TestApp --appName Trading
74
- ```
75
-
76
- ```sh
77
- % Coverage report from v8
78
- ------------------------|---------|----------|---------|---------|-------------------
79
- File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
80
- ------------------------|---------|----------|---------|---------|-------------------
81
- All files | 98.83 | 80 | 80 | 98.83 |
82
- Trading | 0 | 0 | 0 | 0 |
83
- index.ts | 0 | 0 | 0 | 0 | 1
84
- Trading/src | 100 | 100 | 100 | 100 |
85
- i18n.ts | 100 | 100 | 100 | 100 |
86
- manifest.ts | 100 | 100 | 100 | 100 |
87
- Trading/src/ucds | 100 | 100 | 100 | 100 |
88
- BuyAssetServerMain.ts | 100 | 100 | 100 | 100 |
89
- BuyAssetUCD.ts | 100 | 100 | 100 | 100 |
90
- ------------------------|---------|----------|---------|---------|-------------------
91
- 2024-12-29T11:00:53.178Z [info] Coverage Report => open src/apps/Trading/test/reports/coverage/index.html
92
- 2024-12-29T11:00:53.178Z [info] Simple HTML Report => open src/apps/Trading/test/reports/simple-html/index.html
93
- ```
94
-
95
- That's much better. Without writing any line of test code, we've reached almost 100% coverage. Although coverage is a vanity metric in some way, it still is a valuable one.
96
-
97
- Note another important thing : auto documentation. Check out the generated `src/apps/Trading/README.md` that shows a mermaid chart for each use case and a technical summary. This is very valuable to whoever wants to understand what the app does.
98
-
99
- To vizualize the mermaid chart, you can copy/paste it [here](https://mermaid.live) or if you've published your repository to GitHub, they are displayed out of the box.
100
-
101
- <img src="/docs/assets/trading-buy-asset-sequence-diagram.png" width="600px">
102
-
103
- You can see how the special comments we've mentioned earlier show up in this chart. It's nice in order to describe with more details what happens at each step.
104
-
105
- Note also the generated "Coverage Report" and the "Simple HTML Report". The former is provided by `c8` while the other is built by `libmodulor`. It gives a great overview of the test scenarios.
106
-
107
- > [!NOTE]
108
- > You might have seen that `Configurator` is extensible. It allows you to define specific flows to test a suite of use cases, define specific assertions, etc.
109
-
110
- ```sh
111
- yarn lint && git add . && git commit -m "test: init app tests"
112
- ```
113
-
114
- Now that's done, let's [Create the Product](./005_Create_the_Product.md).
@@ -1,46 +0,0 @@
1
- # Create the Product
2
-
3
- Like the app, the product has a `i18n.ts` and `manifest.ts`.
4
-
5
- ```sh
6
- mkdir -p src/products/SuperTrader
7
- touch src/products/SuperTrader/{i18n.ts,manifest.ts}
8
- ```
9
-
10
- ## i18n.ts
11
-
12
- ```typescript
13
- import type { ProductI18n } from 'libmodulor';
14
- import { I18nEN } from 'libmodulor/locales/en';
15
-
16
- import { I18n as TradingI18n } from '../../apps/Trading/index.js';
17
-
18
- export const I18n: ProductI18n = {
19
- en: {
20
- ...I18nEN,
21
- ...TradingI18n.en,
22
- p_desc: 'A simple app to trade crypto, shares and other assets',
23
- p_slogan: 'Trading made simple',
24
- },
25
- };
26
- ```
27
-
28
- ## manifest.ts
29
-
30
- ```typescript
31
- import type { ProductManifest } from 'libmodulor';
32
-
33
- export const Manifest: ProductManifest = {
34
- appReg: [{ name: 'Trading' }],
35
- name: 'SuperTrader',
36
- };
37
- ```
38
-
39
- > [!NOTE]
40
- > The same way we register use cases in an app, we register apps in a product. Since apps are reusable across products, you can even exclude some use cases for a given product.
41
-
42
- ```sh
43
- yarn lint && git add . && git commit -m "feat: add the product"
44
- ```
45
-
46
- Now that's done, let's [Create the server Target](./006_Create_the_server_Target.md).
@@ -1,130 +0,0 @@
1
- # Create the server Target
2
-
3
- We'll use the pre-built [express](https://expressjs.com) `ServerManager`.
4
-
5
- ```sh
6
- mkdir src/products/SuperTrader/server
7
- touch src/products/SuperTrader/server/{container.ts,index.ts}
8
- touch tsconfig.build.json
9
- touch .env
10
- ```
11
-
12
- ## container.ts
13
-
14
- ```typescript
15
- import {
16
- CONTAINER_OPTS,
17
- EnvSettingsManager,
18
- type ServerManager,
19
- type ServerManagerSettings,
20
- type SettingsManager,
21
- TARGET_DEFAULT_SERVER_MANAGER_SETTINGS,
22
- bindCommon,
23
- bindProduct,
24
- } from 'libmodulor';
25
- import {
26
- NodeExpressServerManager,
27
- bindNodeCore,
28
- bindServer,
29
- } from 'libmodulor/node';
30
- import { Container } from 'inversify';
31
-
32
- import { I18n } from '../i18n.js';
33
- import { Manifest } from '../manifest.js';
34
-
35
- type S = ServerManagerSettings;
36
-
37
- const container = new Container(CONTAINER_OPTS);
38
-
39
- bindCommon<S>(container, () => ({
40
- ...TARGET_DEFAULT_SERVER_MANAGER_SETTINGS,
41
- }));
42
- bindNodeCore(container);
43
- bindServer(container);
44
- bindProduct(container, Manifest, I18n);
45
-
46
- container.rebind<SettingsManager>('SettingsManager').to(EnvSettingsManager);
47
-
48
- container.bind<ServerManager>('ServerManager').to(NodeExpressServerManager);
49
-
50
- export default container;
51
- ```
52
-
53
- ## index.ts
54
-
55
- ```typescript
56
- import {
57
- APPS_ROOT_DIR_NAME,
58
- type FSManager,
59
- type I18nManager,
60
- ServerBooter,
61
- } from 'libmodulor';
62
-
63
- import container from './container.js';
64
-
65
- await container.get<I18nManager>('I18nManager').init();
66
-
67
- await container.resolve(ServerBooter).exec({
68
- appsRootPath: container
69
- .get<FSManager>('FSManager')
70
- .path('..', '..', '..', APPS_ROOT_DIR_NAME),
71
- srcImporter: (path) => import(path),
72
- });
73
- ```
74
-
75
- ## tsconfig.build.json
76
-
77
- ```json
78
- {
79
- "extends": "./tsconfig.json",
80
- "compilerOptions": {
81
- "noEmit": false,
82
- "outDir": "dist"
83
- },
84
- "include": ["src"]
85
- }
86
- ```
87
-
88
- ## .env
89
-
90
- ```properties
91
- app_logger_level=trace # the default is 'debug'
92
- ```
93
-
94
- > [!TIP]
95
- > A setting named `my_setting` in the code can be overriden with an environment variable called `app_my_setting`.
96
-
97
- ## Build & Run
98
-
99
- Update `package.json` to add new entries to the `scripts`.
100
-
101
- ```json
102
- "build": "tsc --project tsconfig.build.json && cp .env dist/products/SuperTrader/server/.env",
103
- "run:server": "cd dist/products/SuperTrader/server && node --env-file .env index.js",
104
- ```
105
-
106
- ```sh
107
- yarn build && yarn run:server
108
- ```
109
-
110
- Et voilà ! The server is running !
111
-
112
- ```sh
113
- curl -X POST -H "Content-Type: application/json" http://localhost:7443/api/v1/Trading_BuyAsset
114
- # ❌ {"message":"Invalid credentials"}
115
- curl -X POST -H "Content-Type: application/json" -H "X-API-Key: PublicApiKeyToBeChangedWhenDeploying" http://localhost:7443/api/v1/Trading_BuyAsset
116
- # ❌ {"message":"isin must be filled"}
117
- curl -X POST -H "Content-Type: application/json" -H "X-API-Key: PublicApiKeyToBeChangedWhenDeploying" -d '{"isin":"US02079K3059","limit":123.5,"qty":150}' http://localhost:7443/api/v1/Trading_BuyAsset
118
- # ✅ {"parts":{"_0":{"items":[{"executedDirectly":false,"id":"95dddca5-5e9d-48ac-a90c-71a58d4e8554"}],"total":1}}}
119
- ```
120
-
121
- As you can see, validation comes out of the box. Later we'll see how to add even more precise rules to the data types.
122
-
123
- > [!NOTE]
124
- > The `public_api_key` is just a first layer of security to "authenticate" the client apps calling the server. Hopefully this is not the only security mechanism because of course, this key must be present in clear client side (web, cli, curl...). We'll dive deeper in security when we study the policies.
125
-
126
- ```sh
127
- yarn lint && git add . && git commit -m "feat: add the server target"
128
- ```
129
-
130
- Now that's done, let's [Create the web Target](./007_Create_the_web_Target.md).
@@ -1,262 +0,0 @@
1
- # Create the web Target
2
-
3
- We'll use the pre-built [React](https://react.dev) components to build a SPA (Single Page Application), bundled with [vite](https://vite.dev) and served with the server defined above.
4
-
5
- > [!WARNING]
6
- > For readers used to "beautiful" websites à la Linear, Vercel and related, your eyes will burn. You're going to discover the simple and pure CSS-less Web. The most beautiful one.
7
- > Of course, feel free to add CSS if you want to. The main goal here is to focus on the essence of the UI and not the UI design.
8
-
9
- ```sh
10
- yarn add --dev "@types/react@^18.3.17" "@types/react-dom@^18.3.5"
11
- yarn add "react@^18.3.1" "react-dom@^18.3.1"
12
-
13
- mkdir -p src/products/SuperTrader/web/components
14
- touch src/products/SuperTrader/vite.config.web.ts
15
- touch src/products/SuperTrader/web/{container.ts,index.html,index.tsx}
16
- touch src/products/SuperTrader/web/components/App.tsx
17
- ```
18
-
19
- ## vite.config.web.ts
20
-
21
- ```typescript
22
- import { join } from 'node:path';
23
-
24
- import { StripUCDLifecycleServerPlugin } from 'libmodulor/vite';
25
- import { defineConfig } from 'vite';
26
-
27
- const base = process.cwd();
28
- const root = join('src', 'products', 'SuperTrader', 'web');
29
- const outDir = join(
30
- base,
31
- 'dist',
32
- 'products',
33
- 'SuperTrader',
34
- 'server',
35
- 'public',
36
- );
37
-
38
- export default defineConfig({
39
- build: {
40
- emptyOutDir: true,
41
- outDir,
42
- },
43
- plugins: [StripUCDLifecycleServerPlugin],
44
- root,
45
- });
46
- ```
47
-
48
- ## container.ts
49
-
50
- ```typescript
51
- import {
52
- CONTAINER_OPTS,
53
- type ServerClientManagerSettings,
54
- TARGET_DEFAULT_SERVER_CLIENT_MANAGER_SETTINGS,
55
- bindCommon,
56
- bindProduct,
57
- } from 'libmodulor';
58
- import { bindWeb } from 'libmodulor/web';
59
- import { Container } from 'inversify';
60
-
61
- import { I18n } from '../i18n.js';
62
- import { Manifest } from '../manifest.js';
63
-
64
- type S = ServerClientManagerSettings;
65
-
66
- const container = new Container(CONTAINER_OPTS);
67
-
68
- bindCommon<S>(container, () => ({
69
- ...TARGET_DEFAULT_SERVER_CLIENT_MANAGER_SETTINGS,
70
- }));
71
- bindWeb(container);
72
- bindProduct(container, Manifest, I18n);
73
-
74
- export default container;
75
- ```
76
-
77
- ## index.html
78
-
79
- ```html
80
- <!DOCTYPE html>
81
- <html lang="en">
82
- <head>
83
- <meta charset="utf-8" />
84
- <meta content="width=device-width, initial-scale=1" name="viewport"
85
- />
86
- </head>
87
- <body>
88
- <div id="root"></div>
89
- <script type="module" src="/index.tsx"></script>
90
- </body>
91
- </html>
92
- ```
93
-
94
- ## App.tsx
95
-
96
- Update `src/apps/Trading/index.ts` to expose the use case.
97
-
98
- ```typescript
99
- export { BuyAssetUCD } from './src/ucds/BuyAssetUCD.js';
100
- ```
101
-
102
- Naturally, in real life scenarios, we would never have such a bloated `App.tsx` and we could create fine-grained components. Everybody does that, right ?
103
-
104
- ```tsx
105
- import { type Logger, type ProductManifest, UCOutputReader } from 'libmodulor';
106
- import {
107
- UCPanel,
108
- type UCPanelOnError,
109
- useDIContext,
110
- useUC,
111
- useUCOR,
112
- } from 'libmodulor/react';
113
- import {
114
- UCAutoExecLoader,
115
- UCExecTouchable,
116
- UCForm,
117
- } from 'libmodulor/react-web-pure';
118
- import React, { useEffect, useState, type ReactElement } from 'react';
119
-
120
- import { BuyAssetUCD, Manifest } from '../../../../apps/Trading/index.js';
121
-
122
- export default function App(): ReactElement {
123
- const { container, i18nManager, wordingManager } = useDIContext();
124
- const [logger] = useState(container.get<Logger>('Logger'));
125
- const [productManifest] = useState(
126
- container.get<ProductManifest>('ProductManifest'),
127
- );
128
-
129
- const [buyAssetUC] = useUC(Manifest, BuyAssetUCD, null);
130
- const [buyAssetPart0, _buyAssetPart1, { append0 }] = useUCOR(
131
- new UCOutputReader(BuyAssetUCD, undefined),
132
- );
133
-
134
- const [loading, setLoading] = useState(true);
135
-
136
- useEffect(() => {
137
- (async () => {
138
- logger.debug('Initializing i18n');
139
- await i18nManager.init();
140
- logger.debug('Done initializing i18n');
141
- setLoading(false);
142
- })();
143
-
144
- const { slogan } = wordingManager.p();
145
- document.title = `${productManifest.name} : ${slogan}`;
146
- }, [i18nManager, logger, productManifest, wordingManager]);
147
-
148
- const onError: UCPanelOnError = async (err) => alert(err.message);
149
-
150
- const { slogan } = wordingManager.p();
151
- const { label } = wordingManager.uc(buyAssetUC.def);
152
- const { label: idLabel } = wordingManager.ucof('id');
153
- const { label: executedDirectlyLabel } =
154
- wordingManager.ucof('executedDirectly');
155
-
156
- return (
157
- <div>
158
- {loading && 'Loading...'}
159
-
160
- {!loading && (
161
- <>
162
- <h1>
163
- {productManifest.name} : {slogan}
164
- </h1>
165
-
166
- <h2>{label}</h2>
167
-
168
- <UCPanel
169
- clearAfterExec={false}
170
- onDone={async (ucor) => append0(ucor)}
171
- onError={onError}
172
- renderAutoExecLoader={UCAutoExecLoader}
173
- renderExecTouchable={UCExecTouchable}
174
- renderForm={UCForm}
175
- sleepInMs={200} // Fake delay to see submit wording changing
176
- uc={buyAssetUC}
177
- />
178
-
179
- <table>
180
- <thead>
181
- <tr>
182
- <th>{idLabel}</th>
183
- <th>{executedDirectlyLabel}</th>
184
- </tr>
185
- </thead>
186
- <tbody>
187
- {buyAssetPart0?.items.map((i) => (
188
- <tr key={i.id}>
189
- <td>{i.id}</td>
190
- <td>{i.executedDirectly ? '✅' : '❌'}</td>
191
- </tr>
192
- ))}
193
- </tbody>
194
- <tfoot>
195
- <tr>
196
- <th>{i18nManager.t('total')}</th>
197
- <th>{buyAssetPart0?.pagination.total}</th>
198
- </tr>
199
- </tfoot>
200
- </table>
201
- </>
202
- )}
203
- </div>
204
- );
205
- }
206
- ```
207
-
208
- ## index.tsx
209
-
210
- ```typescript
211
- import { DIContextProvider } from 'libmodulor/react';
212
- import React, { StrictMode } from 'react';
213
- import ReactDOM from 'react-dom/client';
214
-
215
- import App from './components/App.js';
216
- import container from './container.js';
217
-
218
- const rootElt = document.getElementById('root');
219
- if (!rootElt) {
220
- throw new Error('Add a div#root in index.html');
221
- }
222
-
223
- ReactDOM.createRoot(rootElt).render(
224
- <StrictMode>
225
- <DIContextProvider container={container}>
226
- <App />
227
- </DIContextProvider>
228
- </StrictMode>,
229
- );
230
- ```
231
-
232
- ## Build & Run
233
-
234
- Update `package.json` to add the `web` build to the `build` command.
235
-
236
- ```json
237
- "build": "tsc --project tsconfig.build.json && cp .env dist/products/SuperTrader/server/.env && vite -c src/products/SuperTrader/vite.config.web.ts build",
238
- ```
239
-
240
- Update `src/products/SuperTrader/server/container.ts` to mount the `public` directory.
241
-
242
- ```diff
243
- ...TARGET_DEFAULT_SERVER_MANAGER_SETTINGS,
244
- +server_static_dir_path: 'public',
245
- ```
246
-
247
- Press <kbd>ctrl</kbd> + <kbd>C</kbd> to stop the server (we'll setup hot reload later).
248
-
249
- ```sh
250
- yarn build && yarn run:server
251
- open http://localhost:7443
252
- ```
253
-
254
- Et voilà ! The server is running ! Fill the form and see how it automatically submits to the server with client side and server side validation out of the box.
255
-
256
- <img src="/docs/assets/trading-target-web.png" width="600px">
257
-
258
- ```sh
259
- yarn lint && git add . && git commit -m "feat: add the web target"
260
- ```
261
-
262
- Now that's done, let's [Switch to a persistent data storage](./008_Switch_to_a_persistent_data_storage.md).
@@ -1,52 +0,0 @@
1
- # Switch to a persistent data storage
2
-
3
- By default, the data is stored in memory on the server. Therefore, whenever we restart it, we lose everything. That is not very practical in real life scenarios. Let's use SQLite instead.
4
-
5
- ```sh
6
- yarn add "knex@^3.1.0" "sqlite3@^5.1.7"
7
- ```
8
-
9
- Update `src/products/SuperTrader/server/container.ts` to change the implementation.
10
-
11
- ```diff
12
- [...]
13
- TARGET_DEFAULT_SERVER_MANAGER_SETTINGS,
14
- + type UCDataStore,
15
- [...]
16
- +import { KnexUCDataStore } from 'libmodulor/uc-data-store/knex';
17
- [...]
18
- +type S = KnexUCDataStoreSettings & ServerManagerSettings;
19
- [...]
20
- ...TARGET_DEFAULT_SERVER_MANAGER_SETTINGS,
21
- + knex_uc_data_store_conn_string: 'postgresql://toto',
22
- + knex_uc_data_store_file_path: 'uc-data-store.sqlite',
23
- + knex_uc_data_store_pool_max: 5,
24
- + knex_uc_data_store_pool_min: 0,
25
- + knex_uc_data_store_type: 'sqlite3',
26
- server_static_dir_path: 'public',
27
- [...]
28
- container.rebind<SettingsManager>('SettingsManager').to(EnvSettingsManager);
29
- +container.rebind<UCDataStore>('UCDataStore').to(KnexUCDataStore);
30
- ```
31
-
32
- Press <kbd>ctrl</kbd> + <kbd>C</kbd> to stop the server.
33
-
34
- ```sh
35
- yarn build && yarn run:server
36
- ```
37
-
38
- Fill and submit the use case multiple times.
39
-
40
- Open the SQLite database with you with your favorite DB editor (e.g. TablePlus, DBeaver...).
41
-
42
- ```sh
43
- open dist/products/SuperTrader/server/uc-data-store.sqlite
44
- ```
45
-
46
- You should see all your submissions stored in the database.
47
-
48
- ```sh
49
- yarn lint && git add . && git commit -m "feat: persist data in SQLite"
50
- ```
51
-
52
- Now that's done, let's [Define wording for humans](./009_Define_wording_for_humans.md).
@@ -1,42 +0,0 @@
1
- # Define wording for humans
2
-
3
- By default, the UI simply "humanizes" the use case field keys. It's fine for technical people, but not for humans.
4
-
5
- Update `src/apps/Trading/src/i18n.ts` and add the following keys to `en`.
6
-
7
- ```typescript
8
- uc_BuyAsset_label: 'Buy an asset',
9
- uc_BuyAsset_i_submit_idle: 'Send buy order',
10
- uc_BuyAsset_i_submit_submitting: 'Sending',
11
- ucif_isin_label: 'ISIN',
12
- ucif_qty_label: 'Quantity',
13
- ucof_executedDirectly_label: '🚀 Executed directly',
14
- ucof_id_label: 'Identifier',
15
- validation_format_ISIN:
16
- 'Must be 2 uppercase letters, followed by 9 alphanumeric characters and 1 digit',
17
- ```
18
-
19
- Update `src/products/SuperTrader/i18n.ts` and add the following keys to `en`.
20
-
21
- ```typescript
22
- total: 'Total',
23
- ```
24
-
25
- > [!NOTE]
26
- > We distinguish what's related to the app from what's related to the product. Usually, in the app's `i18n`, you'll add only translations following a certain convention like `dt_*` (data type choices), `err_*` (error messages), `uc_*` (use cases), `ucif_*` (use case input fields), `ucof_*` (use case output fields), `validation_*` (validation messages), etc.
27
-
28
- Press <kbd>ctrl</kbd> + <kbd>C</kbd> to stop the server.
29
-
30
- ```sh
31
- yarn build && yarn run:server
32
- ```
33
-
34
- Refresh the page. You should see a better wording. Try to type an invalid `ISIN` and see how the full validation message is displayed as well.
35
-
36
- <img src="/docs/assets/trading-target-web-human.png" width="600px">
37
-
38
- ```sh
39
- yarn lint && git add . && git commit -m "feat: define wording for humans"
40
- ```
41
-
42
- Now that's done, let's [Create the cli Target](./010_Create_the_cli_Target.md).