libmodulor 0.3.0 → 0.5.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 +29 -1
- package/README.md +7 -182
- package/dist/esm/app/workers/AppSrcFilePathBuilder.d.ts +16 -0
- package/dist/esm/app/workers/AppSrcFilePathBuilder.js +6 -4
- package/dist/esm/apps/Helper/index.js +1 -0
- package/dist/esm/apps/Helper/src/ucds/GenerateAppsTestsUCD.js +3 -2
- package/dist/esm/bundlers/vite/StripUCDLifecycleServerPlugin.js +3 -0
- package/dist/esm/convention.d.ts +1 -0
- package/dist/esm/convention.js +17 -4
- package/dist/esm/dt/Validation.d.ts +8 -0
- package/dist/esm/dt/Validation.js +8 -0
- package/dist/esm/dt/base/TBase.d.ts +2 -1
- package/dist/esm/dt/base/TBoolean.js +2 -0
- package/dist/esm/dt/base/TInt.js +3 -0
- package/dist/esm/dt/base/TNumber.js +2 -0
- package/dist/esm/dt/base/TObject.d.ts +15 -0
- package/dist/esm/dt/base/TObject.js +14 -0
- package/dist/esm/dt/base/TString.js +1 -1
- package/dist/esm/dt/final/TAmount.js +1 -0
- package/dist/esm/dt/final/TCountryISO3166Alpha2.js +1 -0
- package/dist/esm/dt/final/TCurrencyISO4217.js +1 -0
- package/dist/esm/dt/final/TDateTimeFormat.js +1 -0
- package/dist/esm/dt/final/TEmail.js +2 -0
- package/dist/esm/dt/final/TEmoji.js +4 -0
- package/dist/esm/dt/final/TFile.js +3 -0
- package/dist/esm/dt/final/THostAddress.js +2 -0
- package/dist/esm/dt/final/TIPv6.js +1 -0
- package/dist/esm/dt/final/TJWT.js +8 -0
- package/dist/esm/dt/final/TPercentage.js +5 -0
- package/dist/esm/dt/final/TSQLQuery.js +1 -0
- package/dist/esm/dt/final/TSSHPrivateKey.js +3 -1
- package/dist/esm/dt/final/TSemVerVersion.js +1 -0
- package/dist/esm/dt/final/TShellCommand.js +1 -0
- package/dist/esm/dt/final/TURL.js +2 -0
- package/dist/esm/dt/final/TUUID.js +1 -0
- package/dist/esm/dt/final/TYesNo.js +1 -1
- package/dist/esm/i18n/WordingManager.d.ts +16 -0
- package/dist/esm/i18n/types.d.ts +5 -0
- package/dist/esm/icon/Icon.d.ts +7 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.react-native-pure.d.ts +10 -0
- package/dist/esm/index.react-native-pure.js +10 -0
- package/dist/esm/product/manifest.d.ts +15 -0
- package/dist/esm/products/Helper/index.js +3 -0
- package/dist/esm/products/Helper/manifest.d.ts +6 -1
- package/dist/esm/std/BufferManager.d.ts +18 -0
- package/dist/esm/std/ClockManager.d.ts +5 -0
- package/dist/esm/std/EnvironmentManager.d.ts +10 -0
- package/dist/esm/std/HTTPAPICaller.d.ts +6 -0
- package/dist/esm/std/I18nManager.d.ts +26 -0
- package/dist/esm/std/JWTManager.d.ts +26 -0
- package/dist/esm/std/JobManager.d.ts +6 -0
- package/dist/esm/std/LLMManager.d.ts +25 -0
- package/dist/esm/std/LLMManager.js +1 -0
- package/dist/esm/std/PromptManager.d.ts +8 -0
- package/dist/esm/std/SettingsManager.d.ts +19 -0
- package/dist/esm/std/SettingsManager.js +9 -0
- package/dist/esm/std/impl/ConsoleLogger.js +7 -1
- package/dist/esm/std/impl/FakeEmailManager.js +1 -0
- package/dist/esm/std/impl/FakeJobManager.js +1 -0
- package/dist/esm/std/impl/FetchHTTPAPICallExecutor.d.ts +9 -0
- package/dist/esm/std/impl/FetchHTTPAPICallExecutor.js +11 -0
- package/dist/esm/std/impl/MistralAILLMManager.d.ts +17 -0
- package/dist/esm/std/impl/MistralAILLMManager.js +56 -0
- package/dist/esm/std/impl/NodeCryptoManager.js +6 -1
- package/dist/esm/std/impl/NodeDeterministicCryptoManager.d.ts +14 -0
- package/dist/esm/std/impl/NodeDeterministicCryptoManager.js +17 -3
- package/dist/esm/std/impl/NodeFSManager.js +10 -0
- package/dist/esm/std/impl/NodeHTTPAPICallExecutorAgentBuilder.js +2 -0
- package/dist/esm/std/impl/NodePromptManager.js +3 -0
- package/dist/esm/std/impl/OllamaLLMManager.d.ts +20 -0
- package/dist/esm/std/impl/OllamaLLMManager.js +56 -0
- package/dist/esm/std/impl/OpenAILLMManager.d.ts +17 -0
- package/dist/esm/std/impl/OpenAILLMManager.js +51 -0
- package/dist/esm/std/impl/SimpleHTTPAPICaller.js +14 -0
- package/dist/esm/std/impl/SimpleMapI18nManager.js +4 -2
- package/dist/esm/std/impl/StdDateClockManager.js +3 -0
- package/dist/esm/std/impl/UCDataStoreExternalResourceManager.js +3 -0
- package/dist/esm/std/impl/WebCryptoManager.js +9 -0
- package/dist/esm/std/index.d.ts +1 -0
- package/dist/esm/std/index.js +1 -0
- package/dist/esm/target/lib/cli/renderer.js +3 -0
- package/dist/esm/target/lib/client/consts.d.ts +3 -0
- package/dist/esm/target/lib/client/consts.js +3 -0
- package/dist/esm/target/lib/mcp-server/MCPServerBooter.js +1 -0
- package/dist/esm/target/lib/react/UCContainer.js +1 -0
- package/dist/esm/target/lib/react/UCPanel.js +4 -0
- package/dist/esm/target/lib/react/entrypoint.d.ts +2 -0
- package/dist/esm/target/lib/react/useUC.d.ts +8 -0
- package/dist/esm/target/lib/react/useUC.js +22 -0
- package/dist/esm/target/lib/react/useUCOR.d.ts +15 -0
- package/dist/esm/target/lib/react/useUCOR.js +45 -0
- package/dist/esm/target/lib/rn/input.d.ts +15 -0
- package/dist/esm/target/lib/rn/input.js +28 -0
- package/dist/esm/target/lib/server/AuthenticationChecker.js +2 -1
- package/dist/esm/target/lib/server/BasicAuthenticationChecker.js +1 -0
- package/dist/esm/target/lib/server/CSPDirectivesBuilder.js +13 -0
- package/dist/esm/target/lib/server/CustomerFacingErrorBuilder.js +3 -0
- package/dist/esm/target/lib/server/PrivateApiKeyAuthenticationChecker.js +1 -0
- package/dist/esm/target/lib/server/PublicApiKeyChecker.js +1 -1
- package/dist/esm/target/lib/server/RequestChecker.js +5 -4
- package/dist/esm/target/lib/server/RequestHandler.d.ts +5 -0
- package/dist/esm/target/lib/server/RequestLogger.js +5 -0
- package/dist/esm/target/lib/server/ServerManager.d.ts +19 -0
- package/dist/esm/target/lib/server/consts.d.ts +3 -0
- package/dist/esm/target/lib/server/consts.js +3 -0
- package/dist/esm/target/lib/web/input.d.ts +21 -0
- package/dist/esm/target/lib/web/input.js +4 -0
- package/dist/esm/target/node-core-cli/NodeCoreCLIManager.js +2 -2
- package/dist/esm/target/node-express-server/NodeExpressServerManager.js +5 -0
- package/dist/esm/target/node-express-server/lib/AuthCookieCreator.js +1 -1
- package/dist/esm/target/node-express-server/middlewares/AuthenticationCheckerMiddlewareBuilder.js +1 -0
- package/dist/esm/target/node-express-server/middlewares/PublicApiKeyCheckerMiddlewareBuilder.js +1 -0
- package/dist/esm/target/node-express-server/middlewares/RequestCheckerMiddlewareBuilder.js +1 -0
- package/dist/esm/target/node-express-server/middlewares/RequestHandlerMiddlewareBuilder.js +8 -0
- package/dist/esm/target/node-express-server/middlewares/RequestLoggerMiddlewareBuilder.js +1 -0
- package/dist/esm/target/node-mcp-server/NodeLocalStdioMCPServerManager.d.ts +10 -0
- package/dist/esm/target/node-mcp-server/NodeLocalStdioMCPServerManager.js +14 -0
- package/dist/esm/target/react-native-pure/UCAutoExecLoader.d.ts +2 -0
- package/dist/esm/target/react-native-pure/UCAutoExecLoader.js +5 -0
- package/dist/esm/target/react-native-pure/UCEntrypointTouchable.d.ts +4 -0
- package/dist/esm/target/react-native-pure/UCEntrypointTouchable.js +6 -0
- package/dist/esm/target/react-native-pure/UCExecTouchable.d.ts +4 -0
- package/dist/esm/target/react-native-pure/UCExecTouchable.js +9 -0
- package/dist/esm/target/react-native-pure/UCForm.d.ts +4 -0
- package/dist/esm/target/react-native-pure/UCForm.js +13 -0
- package/dist/esm/target/react-native-pure/UCFormField.d.ts +11 -0
- package/dist/esm/target/react-native-pure/UCFormField.js +32 -0
- package/dist/esm/target/react-native-pure/UCFormFieldControl.d.ts +11 -0
- package/dist/esm/target/react-native-pure/UCFormFieldControl.js +37 -0
- package/dist/esm/target/react-native-pure/UCFormFieldDesc.d.ts +7 -0
- package/dist/esm/target/react-native-pure/UCFormFieldDesc.js +11 -0
- package/dist/esm/target/react-native-pure/UCFormFieldErr.d.ts +7 -0
- package/dist/esm/target/react-native-pure/UCFormFieldErr.js +5 -0
- package/dist/esm/target/react-native-pure/UCFormFieldLabel.d.ts +7 -0
- package/dist/esm/target/react-native-pure/UCFormFieldLabel.js +8 -0
- package/dist/esm/target/react-native-pure/UCFormSubmitControl.d.ts +9 -0
- package/dist/esm/target/react-native-pure/UCFormSubmitControl.js +8 -0
- package/dist/esm/target/react-web-pure/UCEntrypointTouchable.d.ts +1 -1
- package/dist/esm/target/react-web-pure/UCExecTouchable.d.ts +1 -1
- package/dist/esm/target/react-web-pure/UCExecTouchable.js +1 -1
- package/dist/esm/target/react-web-pure/UCFormFieldControl.d.ts +1 -1
- package/dist/esm/target/react-web-pure/UCFormFieldControl.js +3 -3
- package/dist/esm/testing/AppTester.d.ts +4 -0
- package/dist/esm/testing/AppTester.js +16 -0
- package/dist/esm/testing/AppTesterConfigurator.d.ts +68 -0
- package/dist/esm/testing/UCDataStoreTester.d.ts +9 -0
- package/dist/esm/testing/UCDataStoreTester.js +13 -0
- package/dist/esm/testing/impl/SimpleAppDocsEmitter.js +22 -2
- package/dist/esm/testing/impl/SimpleAppTesterConfigurator.js +1 -0
- package/dist/esm/testing/impl/SimpleHTMLAppTestReportEmitter.js +9 -3
- package/dist/esm/testing/impl/TypeScriptLibUCDefASTParser.js +12 -4
- package/dist/esm/testing/impl/VitestAppTestSuiteEmitter.js +6 -0
- package/dist/esm/testing/opts.d.ts +38 -0
- package/dist/esm/testing/opts.js +1 -1
- package/dist/esm/testing/uc-input.js +2 -0
- package/dist/esm/testing/workers/AppTesterCtxInitializer.js +7 -0
- package/dist/esm/testing/workers/UCExecutor.js +1 -0
- package/dist/esm/testing/workers/checkers/AppIndexChecker.js +1 -0
- package/dist/esm/testing/workers/checkers/UCDefSourcesChecker.js +4 -0
- package/dist/esm/uc/UC.js +19 -1
- package/dist/esm/uc/UCInputField.d.ts +28 -0
- package/dist/esm/uc/UCInputField.js +42 -0
- package/dist/esm/uc/data.d.ts +3 -0
- package/dist/esm/uc/def.d.ts +7 -0
- package/dist/esm/uc/exec.d.ts +39 -0
- package/dist/esm/uc/exec.js +29 -0
- package/dist/esm/uc/ext.d.ts +30 -1
- package/dist/esm/uc/helpers/UCOutputBuilder.js +5 -0
- package/dist/esm/uc/helpers/UCOutputReader.js +3 -1
- package/dist/esm/uc/impl/HTTPUCTransporter.js +4 -0
- package/dist/esm/uc/impl/InMemoryUCDataStore.js +7 -0
- package/dist/esm/uc/impl/KnexUCDataStore.d.ts +4 -0
- package/dist/esm/uc/impl/KnexUCDataStore.js +14 -0
- package/dist/esm/uc/impl/SimpleUCManager.js +6 -0
- package/dist/esm/uc/input-field.d.ts +60 -0
- package/dist/esm/uc/input-field.js +33 -0
- package/dist/esm/uc/input.d.ts +24 -0
- package/dist/esm/uc/lifecycle/client/IdleClientMain.js +1 -0
- package/dist/esm/uc/lifecycle/server/IdleServerMain.js +2 -0
- package/dist/esm/uc/manager.d.ts +11 -0
- package/dist/esm/uc/metadata.d.ts +10 -0
- package/dist/esm/uc/opi-layout.d.ts +3 -0
- package/dist/esm/uc/opi.d.ts +8 -0
- package/dist/esm/uc/output-field.d.ts +9 -0
- package/dist/esm/uc/output-part.d.ts +22 -0
- package/dist/esm/uc/output.d.ts +3 -0
- package/dist/esm/uc/policies/RoleRegularUCPolicy.js +1 -0
- package/dist/esm/uc/policies/funcs.js +1 -0
- package/dist/esm/uc/policy.d.ts +22 -0
- package/dist/esm/uc/sec.d.ts +9 -0
- package/dist/esm/uc/server.d.ts +10 -0
- package/dist/esm/uc/settings.d.ts +25 -0
- package/dist/esm/uc/side-effect.d.ts +16 -0
- package/dist/esm/uc/side-effect.js +16 -0
- package/dist/esm/uc/utils/rInput.d.ts +12 -0
- package/dist/esm/uc/utils/rInput.js +2 -0
- package/dist/esm/uc/utils/rVal.d.ts +25 -0
- package/dist/esm/uc/utils/rVal.js +27 -0
- package/dist/esm/uc/utils/recIs.d.ts +9 -0
- package/dist/esm/uc/utils/recIs.js +12 -1
- package/dist/esm/uc/utils/stripUCDLifecycleServer.d.ts +13 -0
- package/dist/esm/uc/utils/stripUCDLifecycleServer.js +17 -0
- package/dist/esm/uc/utils/ucifcoIsForArray.d.ts +6 -0
- package/dist/esm/uc/utils/ucifcoIsForArray.js +6 -0
- package/dist/esm/uc/workers/SimpleAggregateFinder.d.ts +12 -0
- package/dist/esm/uc/workers/SimpleAggregateFinder.js +12 -0
- package/dist/esm/uc/workers/UCBuilder.d.ts +7 -0
- package/dist/esm/uc/workers/UCBuilder.js +7 -0
- package/dist/esm/uc/workers/UCExecChecker.js +2 -0
- package/dist/esm/uc/workers/UCInputFilesProcessor.js +10 -4
- package/dist/esm/uc/workers/UCOutputFilesProcessor.js +6 -2
- package/dist/esm/utils/async/sleep.d.ts +10 -0
- package/dist/esm/utils/async/sleep.js +10 -0
- package/dist/esm/utils/http/appendData.js +5 -1
- package/dist/esm/utils/ioc/ContainerPrinter.js +2 -0
- package/dist/esm/utils/ioc/bindCommon.js +4 -0
- package/dist/esm/utils/ioc/bindNodeCLI.js +2 -0
- package/dist/esm/utils/ioc/bindNodeCore.js +1 -0
- package/dist/esm/utils/ioc/bindProduct.js +2 -0
- package/dist/esm/utils/ioc/bindRN.js +1 -0
- package/dist/esm/utils/ioc/bindServer.js +1 -0
- package/dist/esm/utils/ioc/bindWeb.js +2 -0
- package/dist/esm/utils/ioc/container.js +6 -0
- package/dist/esm/utils/numbers/units.js +3 -0
- package/dist/esm/utils/types/funcs.d.ts +35 -0
- package/dist/esm/utils/types/funcs.js +35 -0
- package/dist/esm/utils/types/utility-types.d.ts +17 -0
- package/dist/esm/utils/types/utility-types.js +1 -0
- package/package.json +16 -9
- package/docs/assets/trading-buy-asset-sequence-diagram.png +0 -0
- package/docs/assets/trading-target-mcp-server.png +0 -0
- package/docs/assets/trading-target-web-human.png +0 -0
- package/docs/assets/trading-target-web.png +0 -0
- package/docs/getting-started/001_Create_the_project.md +0 -168
- package/docs/getting-started/002_Create_the_App.md +0 -49
- package/docs/getting-started/003_Create_the_UseCase.md +0 -205
- package/docs/getting-started/004_Test_the_App.md +0 -114
- package/docs/getting-started/005_Create_the_Product.md +0 -46
- package/docs/getting-started/006_Create_the_server_Target.md +0 -130
- package/docs/getting-started/007_Create_the_web_Target.md +0 -262
- package/docs/getting-started/008_Switch_to_a_persistent_data_storage.md +0 -55
- package/docs/getting-started/009_Define_wording_for_humans.md +0 -42
- package/docs/getting-started/010_Create_the_cli_Target.md +0 -102
- package/docs/getting-started/011_Create_the_mcp_server_Target.md +0 -157
- package/docs/getting-started/012_Summary.md +0 -29
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libmodulor",
|
|
3
3
|
"description": "An opinionated TypeScript library to create business oriented applications",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"license": "LGPL-3.0",
|
|
6
6
|
"author": "Chafik H'nini <chafik.hnini@gmail.com>",
|
|
7
7
|
"homepage": "https://github.com/c100k/libmodulor#readme",
|
|
@@ -31,6 +31,9 @@
|
|
|
31
31
|
"./node": {
|
|
32
32
|
"import": "./dist/esm/index.node.js"
|
|
33
33
|
},
|
|
34
|
+
"./react-native-pure": {
|
|
35
|
+
"import": "./dist/esm/index.react-native-pure.js"
|
|
36
|
+
},
|
|
34
37
|
"./react-web-pure": {
|
|
35
38
|
"import": "./dist/esm/index.react-web-pure.js"
|
|
36
39
|
},
|
|
@@ -54,26 +57,27 @@
|
|
|
54
57
|
}
|
|
55
58
|
},
|
|
56
59
|
"peerDependencies": {
|
|
57
|
-
"@hono/node-server": "^1.13.
|
|
58
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
59
|
-
"@stricli/core": "^1.1.
|
|
60
|
+
"@hono/node-server": "^1.13.8",
|
|
61
|
+
"@modelcontextprotocol/sdk": "^1.5.0",
|
|
62
|
+
"@stricli/core": "^1.1.1",
|
|
60
63
|
"buffer": "^6.0.3",
|
|
61
64
|
"cookie-parser": "^1.4.7",
|
|
62
65
|
"express": "^4.21.2",
|
|
63
66
|
"express-fileupload": "^1.5.1",
|
|
64
67
|
"fast-check": "^3.23.2",
|
|
65
68
|
"helmet": "^8.0.0",
|
|
66
|
-
"hono": "^4.
|
|
67
|
-
"inversify": "^6.2.
|
|
68
|
-
"jose": "^
|
|
69
|
+
"hono": "^4.7.2",
|
|
70
|
+
"inversify": "^6.2.2",
|
|
71
|
+
"jose": "^6.0.6",
|
|
69
72
|
"knex": "^3.1.0",
|
|
70
|
-
"pg": "^8.13.
|
|
73
|
+
"pg": "^8.13.3",
|
|
71
74
|
"react": "^18.3.1",
|
|
72
75
|
"react-dom": "^18.3.1",
|
|
76
|
+
"react-native": "^0.78.0",
|
|
73
77
|
"reflect-metadata": "^0.2.2",
|
|
74
78
|
"sqlite3": "^5.1.7",
|
|
75
79
|
"typescript": "^5.7.3",
|
|
76
|
-
"vite": "^6.
|
|
80
|
+
"vite": "^6.1.1"
|
|
77
81
|
},
|
|
78
82
|
"peerDependenciesMeta": {
|
|
79
83
|
"@hono/node-server": {
|
|
@@ -103,6 +107,9 @@
|
|
|
103
107
|
"react-dom": {
|
|
104
108
|
"optional": true
|
|
105
109
|
},
|
|
110
|
+
"react-native": {
|
|
111
|
+
"optional": true
|
|
112
|
+
},
|
|
106
113
|
"sqlite3": {
|
|
107
114
|
"optional": true
|
|
108
115
|
},
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
# Create the project
|
|
2
|
-
|
|
3
|
-
Assuming you have the following installed (otherwise, install them or adapt the commands) :
|
|
4
|
-
|
|
5
|
-
- `node` >= 22
|
|
6
|
-
- `yarn` >= 1.x
|
|
7
|
-
- `wget` and `curl`
|
|
8
|
-
|
|
9
|
-
If you're on macOS, for the `sed` commands, add a `''` after `-i` ([Explanation](https://stackoverflow.com/a/4247319/1259118)).
|
|
10
|
-
|
|
11
|
-
```sh
|
|
12
|
-
# Create the directory
|
|
13
|
-
mkdir libmodulor-tuto && cd libmodulor-tuto # Note how the repository is generic to contain multiple apps and products
|
|
14
|
-
|
|
15
|
-
# Initialize git
|
|
16
|
-
git init
|
|
17
|
-
|
|
18
|
-
# Initialize config files
|
|
19
|
-
touch .gitignore biome.json package.json README.md tsconfig.json vitest.config.ts
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## .gitignore
|
|
23
|
-
|
|
24
|
-
```.gitignore
|
|
25
|
-
coverage
|
|
26
|
-
dist
|
|
27
|
-
node_modules
|
|
28
|
-
src/apps/**/test/reports
|
|
29
|
-
.env
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## biome.json
|
|
33
|
-
|
|
34
|
-
```json
|
|
35
|
-
{
|
|
36
|
-
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
|
37
|
-
"files": {
|
|
38
|
-
"ignore": ["coverage", "dist", "node_modules"],
|
|
39
|
-
"ignoreUnknown": true
|
|
40
|
-
},
|
|
41
|
-
"formatter": {
|
|
42
|
-
"indentStyle": "space",
|
|
43
|
-
"indentWidth": 4
|
|
44
|
-
},
|
|
45
|
-
"javascript": {
|
|
46
|
-
"formatter": {
|
|
47
|
-
"quoteStyle": "single"
|
|
48
|
-
},
|
|
49
|
-
"parser": {
|
|
50
|
-
"unsafeParameterDecoratorsEnabled": true
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## package.json
|
|
57
|
-
|
|
58
|
-
```json
|
|
59
|
-
{
|
|
60
|
-
"name": "libmodulor-tuto",
|
|
61
|
-
"version": "0.1.0",
|
|
62
|
-
"author": "Chafik H'nini <chafik.hnini@gmail.com>",
|
|
63
|
-
"type": "module",
|
|
64
|
-
"private": true,
|
|
65
|
-
"scripts": {
|
|
66
|
-
"helper": "node ./node_modules/libmodulor/dist/esm/products/Helper/index.js",
|
|
67
|
-
"lint": "biome check --write .",
|
|
68
|
-
"test": "tsc && vitest run"
|
|
69
|
-
},
|
|
70
|
-
"dependencies": {
|
|
71
|
-
"inversify": "^6.2.1",
|
|
72
|
-
"libmodulor": "^0.3.0",
|
|
73
|
-
"reflect-metadata": "^0.2.2"
|
|
74
|
-
},
|
|
75
|
-
"devDependencies": {
|
|
76
|
-
"@biomejs/biome": "^1.9.4",
|
|
77
|
-
"@types/node": "^22.10.7",
|
|
78
|
-
"@vitest/coverage-v8": "^3.0.2",
|
|
79
|
-
"buffer": "^6.0.3",
|
|
80
|
-
"cookie-parser": "^1.4.7",
|
|
81
|
-
"express": "^4.21.2",
|
|
82
|
-
"express-fileupload": "^1.5.1",
|
|
83
|
-
"fast-check": "^3.23.2",
|
|
84
|
-
"helmet": "^8.0.0",
|
|
85
|
-
"jose": "^5.9.6",
|
|
86
|
-
"typescript": "^5.7.3",
|
|
87
|
-
"vite": "^6.0.11",
|
|
88
|
-
"vitest": "^3.0.3"
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## README.md
|
|
94
|
-
|
|
95
|
-
```md
|
|
96
|
-
# libmodulor-tuto
|
|
97
|
-
|
|
98
|
-
🚀🚀🚀
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## tsconfig.json
|
|
102
|
-
|
|
103
|
-
```json
|
|
104
|
-
{
|
|
105
|
-
"compilerOptions": {
|
|
106
|
-
"allowSyntheticDefaultImports": true,
|
|
107
|
-
"declaration": true,
|
|
108
|
-
"lib": ["dom", "esnext"],
|
|
109
|
-
"module": "NodeNext",
|
|
110
|
-
"moduleResolution": "NodeNext",
|
|
111
|
-
"noEmit": true,
|
|
112
|
-
"removeComments": true,
|
|
113
|
-
"skipLibCheck": true,
|
|
114
|
-
"sourceMap": true,
|
|
115
|
-
"target": "ESNext",
|
|
116
|
-
|
|
117
|
-
"strict": true,
|
|
118
|
-
"allowUnreachableCode": false,
|
|
119
|
-
"allowUnusedLabels": false,
|
|
120
|
-
"exactOptionalPropertyTypes": true,
|
|
121
|
-
"noFallthroughCasesInSwitch": true,
|
|
122
|
-
"noPropertyAccessFromIndexSignature": true,
|
|
123
|
-
"noImplicitOverride": true,
|
|
124
|
-
"noImplicitReturns": true,
|
|
125
|
-
"noUncheckedIndexedAccess": true,
|
|
126
|
-
"noUnusedLocals": true,
|
|
127
|
-
"noUnusedParameters": true,
|
|
128
|
-
"verbatimModuleSyntax": true,
|
|
129
|
-
|
|
130
|
-
"emitDecoratorMetadata": true,
|
|
131
|
-
"experimentalDecorators": true,
|
|
132
|
-
|
|
133
|
-
"jsx": "react"
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## vitest.config.ts
|
|
139
|
-
|
|
140
|
-
```typescript
|
|
141
|
-
import { defineConfig } from 'vitest/config';
|
|
142
|
-
|
|
143
|
-
export default defineConfig({
|
|
144
|
-
test: {
|
|
145
|
-
coverage: {
|
|
146
|
-
enabled: true,
|
|
147
|
-
exclude: ['src/apps/**/test', 'src/**/*.test.ts'],
|
|
148
|
-
include: ['src'],
|
|
149
|
-
reporter: ['html', 'lcov', 'text'],
|
|
150
|
-
},
|
|
151
|
-
reporters: ['verbose'],
|
|
152
|
-
},
|
|
153
|
-
});
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
## Install
|
|
157
|
-
|
|
158
|
-
```sh
|
|
159
|
-
yarn install
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
```sh
|
|
163
|
-
yarn lint && git add . && git commit -m "chore: init source code"
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
Optionally, you can create a remote repository (e.g. on GitHub) and push it.
|
|
167
|
-
|
|
168
|
-
Now that's done, let's [Create the App](./002_Create_the_App.md).
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# Create the App
|
|
2
|
-
|
|
3
|
-
An app is composed of three main files : `i18n.ts`, `manifest.ts` and `index.ts`.
|
|
4
|
-
|
|
5
|
-
```sh
|
|
6
|
-
mkdir -p src/apps/Trading/src/ucds
|
|
7
|
-
touch src/apps/Trading/src/{i18n.ts,manifest.ts}
|
|
8
|
-
touch src/apps/Trading/index.ts
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
> [!NOTE]
|
|
12
|
-
> There is a lot of controversy about barrel files. In this specific context, they are useful to only expose the necessary things to the upper layers and keep the app isolated.
|
|
13
|
-
|
|
14
|
-
## i18n.ts
|
|
15
|
-
|
|
16
|
-
```typescript
|
|
17
|
-
import type { AppI18n } from 'libmodulor';
|
|
18
|
-
|
|
19
|
-
export const I18n: AppI18n = {
|
|
20
|
-
en: {},
|
|
21
|
-
};
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## manifest.ts
|
|
25
|
-
|
|
26
|
-
```typescript
|
|
27
|
-
import type { AppManifest } from 'libmodulor';
|
|
28
|
-
|
|
29
|
-
export const Manifest = {
|
|
30
|
-
languageCodes: ['en'],
|
|
31
|
-
name: 'Trading',
|
|
32
|
-
ucReg: {},
|
|
33
|
-
} satisfies AppManifest;
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## index.ts
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
// Expose only what's necessary
|
|
40
|
-
|
|
41
|
-
export { I18n } from './src/i18n.js';
|
|
42
|
-
export { Manifest } from './src/manifest.js';
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
```sh
|
|
46
|
-
yarn lint && git add . && git commit -m "feat: add the app"
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Now that's done, let's [Create the UseCase](./003_Create_the_UseCase.md).
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
# Create the UseCase
|
|
2
|
-
|
|
3
|
-
> [!NOTE]
|
|
4
|
-
> Starting now, you'll see `UC` or `uc` a lot. It's the abbreviation of `UseCase`. Acronyms are not good in codebases, except those that are commonly used ([debate](https://stackoverflow.com/questions/2236807/java-naming-convention-with-acronyms)). In any case, when you write `UseCase` hundreds of times, you're happy to be able to write `UC` instead. Thus, `UCD` stands for `Use Case Definition`, `UCIF` stands for `Use Case Input Field` and so on.
|
|
5
|
-
|
|
6
|
-
The app manifest registers all the use cases metadata. Use cases can depend on each other as we'll see later, and this dependency must go through the manifest. Never directly.
|
|
7
|
-
|
|
8
|
-
Update `manifest.ts` to register the new use case.
|
|
9
|
-
|
|
10
|
-
```typescript
|
|
11
|
-
// ...
|
|
12
|
-
ucReg: {
|
|
13
|
-
BuyAsset: {
|
|
14
|
-
action: 'Create',
|
|
15
|
-
icon: 'plus',
|
|
16
|
-
name: 'BuyAsset',
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
// ...
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
If you're using an IDE with auto-complete, you might have noticed the other properties like `beta`, `new`, `sensitive`. We'll come back to them later.
|
|
23
|
-
|
|
24
|
-
```sh
|
|
25
|
-
mkdir src/apps/Trading/src/dt
|
|
26
|
-
touch src/apps/Trading/src/dt/TISIN.ts
|
|
27
|
-
touch src/apps/Trading/src/ucds/{BuyAssetServerMain.ts,BuyAssetUCD.ts}
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## TISIN.ts
|
|
31
|
-
|
|
32
|
-
An asset is usually identified by a unique code called [ISIN](https://www.isin.org). This is a typical business data type that has specific rules and that is not simply a `string`.
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
import { type TName, TString, type TStringConstraints } from 'libmodulor';
|
|
36
|
-
|
|
37
|
-
export type ISIN = Capitalize<string>;
|
|
38
|
-
|
|
39
|
-
export class TISIN extends TString<ISIN, 'ISIN'> {
|
|
40
|
-
public static readonly FORMAT: RegExp = /^[A-Z]{2}[A-Z0-9]{9}[0-9]$/;
|
|
41
|
-
|
|
42
|
-
constructor(constraints?: TStringConstraints) {
|
|
43
|
-
super({
|
|
44
|
-
...constraints,
|
|
45
|
-
format: { f: 'ISIN', regexp: TISIN.FORMAT },
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
public override tName(): TName {
|
|
50
|
-
return 'ISIN';
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
public override example(): ISIN {
|
|
54
|
-
return 'US02079K3059';
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## BuyAssetUCD.ts
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
import {
|
|
63
|
-
type AggregateOPI0,
|
|
64
|
-
type Amount,
|
|
65
|
-
EverybodyUCPolicy,
|
|
66
|
-
TAmount,
|
|
67
|
-
TBoolean,
|
|
68
|
-
TUIntQuantity,
|
|
69
|
-
type UCDef,
|
|
70
|
-
type UCInput,
|
|
71
|
-
type UCInputFieldValue,
|
|
72
|
-
type UCMain,
|
|
73
|
-
type UCMainInput,
|
|
74
|
-
type UCOutputOrNothing,
|
|
75
|
-
type UCTransporter,
|
|
76
|
-
type UIntQuantity,
|
|
77
|
-
} from 'libmodulor';
|
|
78
|
-
import { inject, injectable } from 'inversify';
|
|
79
|
-
|
|
80
|
-
import { Manifest } from '../manifest.js';
|
|
81
|
-
|
|
82
|
-
import { type ISIN, TISIN } from '../dt/TISIN.js';
|
|
83
|
-
import { BuyAssetServerMain } from './BuyAssetServerMain.js';
|
|
84
|
-
|
|
85
|
-
export interface BuyAssetInput extends UCInput {
|
|
86
|
-
isin: UCInputFieldValue<ISIN>;
|
|
87
|
-
limit: UCInputFieldValue<Amount>;
|
|
88
|
-
qty: UCInputFieldValue<UIntQuantity>;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export interface BuyAssetOPI0 extends AggregateOPI0 {
|
|
92
|
-
executedDirectly: boolean;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
@injectable()
|
|
96
|
-
class BuyAssetClientMain implements UCMain<BuyAssetInput, BuyAssetOPI0> {
|
|
97
|
-
constructor(
|
|
98
|
-
@inject('UCTransporter')
|
|
99
|
-
private ucTransporter: UCTransporter,
|
|
100
|
-
) {}
|
|
101
|
-
|
|
102
|
-
public async exec({
|
|
103
|
-
uc,
|
|
104
|
-
}: UCMainInput<BuyAssetInput, BuyAssetOPI0>): Promise<
|
|
105
|
-
UCOutputOrNothing<BuyAssetOPI0>
|
|
106
|
-
> {
|
|
107
|
-
return this.ucTransporter.send(uc);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export const BuyAssetUCD: UCDef<BuyAssetInput, BuyAssetOPI0> = {
|
|
112
|
-
io: {
|
|
113
|
-
i: {
|
|
114
|
-
fields: {
|
|
115
|
-
isin: {
|
|
116
|
-
type: new TISIN(),
|
|
117
|
-
},
|
|
118
|
-
limit: {
|
|
119
|
-
type: new TAmount('USD'),
|
|
120
|
-
},
|
|
121
|
-
qty: {
|
|
122
|
-
type: new TUIntQuantity(),
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
o: {
|
|
127
|
-
parts: {
|
|
128
|
-
_0: {
|
|
129
|
-
fields: {
|
|
130
|
-
executedDirectly: {
|
|
131
|
-
type: new TBoolean(),
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
lifecycle: {
|
|
139
|
-
client: {
|
|
140
|
-
main: BuyAssetClientMain,
|
|
141
|
-
policy: EverybodyUCPolicy,
|
|
142
|
-
},
|
|
143
|
-
server: {
|
|
144
|
-
main: BuyAssetServerMain,
|
|
145
|
-
policy: EverybodyUCPolicy,
|
|
146
|
-
},
|
|
147
|
-
},
|
|
148
|
-
metadata: Manifest.ucReg.BuyAsset,
|
|
149
|
-
};
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## BuyAssetServerMain.ts
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
import {
|
|
156
|
-
type UCMain,
|
|
157
|
-
type UCMainInput,
|
|
158
|
-
type UCManager,
|
|
159
|
-
type UCOutput,
|
|
160
|
-
UCOutputBuilder,
|
|
161
|
-
} from 'libmodulor';
|
|
162
|
-
import { inject, injectable } from 'inversify';
|
|
163
|
-
|
|
164
|
-
import type { BuyAssetInput, BuyAssetOPI0 } from './BuyAssetUCD.js';
|
|
165
|
-
|
|
166
|
-
@injectable()
|
|
167
|
-
export class BuyAssetServerMain implements UCMain<BuyAssetInput, BuyAssetOPI0> {
|
|
168
|
-
constructor(@inject('UCManager') private ucManager: UCManager) {}
|
|
169
|
-
|
|
170
|
-
public async exec({
|
|
171
|
-
uc,
|
|
172
|
-
}: UCMainInput<BuyAssetInput, BuyAssetOPI0>): Promise<
|
|
173
|
-
UCOutput<BuyAssetOPI0>
|
|
174
|
-
> {
|
|
175
|
-
// >=> Persist the order
|
|
176
|
-
const { aggregateId } = await this.ucManager.persist(uc);
|
|
177
|
-
|
|
178
|
-
// >=> TODO : Check the user has enough funds to place the order
|
|
179
|
-
|
|
180
|
-
// >=> TODO : Send the order to a queue for processing
|
|
181
|
-
const executedDirectly: BuyAssetOPI0['executedDirectly'] = false;
|
|
182
|
-
|
|
183
|
-
return new UCOutputBuilder<BuyAssetOPI0>()
|
|
184
|
-
.add({
|
|
185
|
-
executedDirectly,
|
|
186
|
-
id: aggregateId,
|
|
187
|
-
})
|
|
188
|
-
.get();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
For now, we won't detail all this code but take the time to read it and understand how it works. Hopefully it's clear enough and self-explanatory.
|
|
194
|
-
|
|
195
|
-
> [!TIP]
|
|
196
|
-
> Using a comment following the pattern `// >=> ` in `ClientMain` and `ServerMain` has a specific meaning as we'll see a little bit later.
|
|
197
|
-
|
|
198
|
-
> [!NOTE]
|
|
199
|
-
> Unlike `ClientMain`, `ServerMain` is put in another file for "historical" reasons, mainly for stripping and tree shaking reasons. More on this later.
|
|
200
|
-
|
|
201
|
-
```sh
|
|
202
|
-
yarn lint && git add . && git commit -m "feat: add the use case"
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
Now that's done, let's [Test the App](./004_Test_the_App.md).
|
|
@@ -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).
|