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/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,38 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## v0.5.0 (2025-02-24)
|
|
4
|
+
|
|
5
|
+
**BREAKING**
|
|
6
|
+
|
|
7
|
+
- Replace `.js` by `.ts` for `ProductManifest`
|
|
8
|
+
|
|
9
|
+
**Added**
|
|
10
|
+
|
|
11
|
+
- Introduce a 3<sup>rd</sup> implementation of `LLMManager` (`OllamaLLMManager`) to run models locally
|
|
12
|
+
- Introduce `LLMManager` with 2 implementations (`MistralAILLMManager` and `OpenAILLMManager`)
|
|
13
|
+
|
|
14
|
+
**Misc**
|
|
15
|
+
|
|
16
|
+
- Simplify the signature of `UCDef.ext.http.transform` removing the generic `T` return
|
|
17
|
+
- Add JSDocs and comments for a better in-place documentation (i.e. in .d.ts files)
|
|
18
|
+
- Add the Tutorial code in the repo at `examples/libmodulor-tuto` and add a new "Expose a rn Target" step
|
|
19
|
+
|
|
20
|
+
## v0.4.0 (2025-01-31)
|
|
21
|
+
|
|
22
|
+
**feat(target): introduce react-native-pure**
|
|
23
|
+
|
|
24
|
+
To help with the creation of specific targets, we've added a new one : `react-native-pure`. It's as simple as `react-web-pure`, with no specific UI style. It's a good starting point to take inspiration to create your own GUI target, with your own style.
|
|
25
|
+
|
|
26
|
+
**Misc**
|
|
27
|
+
|
|
28
|
+
- Introduced Guides in docs for more advanced scenarios (e.g. Create a target) (https://github.com/c100k/libmodulor/pull/10)
|
|
29
|
+
- Improved the docs for a better readability (https://github.com/c100k/libmodulor/pull/10)
|
|
30
|
+
|
|
3
31
|
## v0.3.0 (2025-01-23)
|
|
4
32
|
|
|
5
33
|
**feat(uc): introduce alternate mounting point**
|
|
6
34
|
|
|
7
|
-
Added a new property `
|
|
35
|
+
Added a new property `UCDef.ext.http.mountAlsoAt` to be able to define path aliases. See the comment below to understand why.
|
|
8
36
|
|
|
9
37
|
```typescript
|
|
10
38
|
/**
|
package/README.md
CHANGED
|
@@ -25,193 +25,18 @@ Applications created with `libmodulor` have **6 main properties** :
|
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
-
## 📚 Philosophy
|
|
29
|
-
|
|
30
|
-
One might argue that, with so many "JS frameworks" on the market, there are already too many ways to build new applications today. And they would be right.
|
|
31
|
-
|
|
32
|
-
That's why the angle taken by `libmodulor` is different. Although opinionated about some things (see below), it is not, regarding the technical side. Instead, it focuses mainly on the "core" of your application.
|
|
33
|
-
|
|
34
|
-
Thus, you are free to use :
|
|
35
|
-
|
|
36
|
-
- the data store of your choice (PostgreSQL, MySQL, MariaDB, DynamoDB, SQLite, MongoDB...),
|
|
37
|
-
- the frontend framework of your choice (React, Svelte, Angular, Vue, Solid...),
|
|
38
|
-
- the server of your choice (Express, Fastify, Hono...),
|
|
39
|
-
- the meta framework of your choice (Next, Remix, Astro, Nuxt...),
|
|
40
|
-
- the runtime of your choice (Node, Deno, Bun...)
|
|
41
|
-
- the libraries of your choice (Lodash, React Query...)
|
|
42
|
-
- the tools of your choice (Biome, ESLint, Prettier...)
|
|
43
|
-
- the styling library of your choice for web (tailwind, shadcn, bootstrap, vanilla CSS...)
|
|
44
|
-
- the hosting of your choice (Cloud, IaaS, PaaS, On-Prem, RaspberryPi, your fridge...)
|
|
45
|
-
|
|
46
|
-
The main goal is to offer higher level primitives that make building business applications faster, without having to use a boilerplate or worse, no/low code, and thus, avoid vendor lock-in.
|
|
47
|
-
|
|
48
|
-
## 💡 How it works
|
|
49
|
-
|
|
50
|
-
The library defines a **4-layer architecture** composed of : `UseCase`, `App`, `Product`, `Target`.
|
|
51
|
-
|
|
52
|
-
```mermaid
|
|
53
|
-
block-beta
|
|
54
|
-
Target1:2
|
|
55
|
-
Target2:2
|
|
56
|
-
Target3:2
|
|
57
|
-
columns 6
|
|
58
|
-
Product1:6
|
|
59
|
-
App1:3
|
|
60
|
-
App2:3
|
|
61
|
-
UseCase1
|
|
62
|
-
UseCase2
|
|
63
|
-
UseCase3
|
|
64
|
-
UseCase4
|
|
65
|
-
UseCase6
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
_If you're not seeing the mermaid chart (e.g. on npm), head to GitHub._
|
|
69
|
-
|
|
70
|
-
### UseCase
|
|
71
|
-
|
|
72
|
-
A use case is the smallest unit. It defines the contract, mainly as an `Input` that goes into lifecycle methods (`client` and/or `server`) to finally give an `Output`. In the end, it constitutes a piece of business functionality.
|
|
73
|
-
|
|
74
|
-
Inspired by [UML's Use case diagram](https://en.wikipedia.org/wiki/Use_case_diagram) and [Event-driven architecture](https://en.wikipedia.org/wiki/Event-driven_architecture), schematically, it could be defined as follows :
|
|
75
|
-
|
|
76
|
-
```math
|
|
77
|
-
O = clientMain(serverMain(I))
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
_Examples : `SignIn`, `CreatePost`, `TransferAccount`, `InviteContacts`_...
|
|
81
|
-
|
|
82
|
-
Note how it always starts with a verb.
|
|
83
|
-
|
|
84
|
-
### App
|
|
85
|
-
|
|
86
|
-
An app is a logical group of use cases.
|
|
87
|
-
|
|
88
|
-
It's like a "module" (_whatever that means_), inspired by [Domain-driven design (DDD)](https://en.wikipedia.org/wiki/Domain-driven_design) bounded contexts.
|
|
89
|
-
|
|
90
|
-
_Examples : `Auth`, `Accounting`, `CMS`..._
|
|
91
|
-
|
|
92
|
-
### Product
|
|
93
|
-
|
|
94
|
-
A product is a logical group of apps that are assembled together.
|
|
95
|
-
|
|
96
|
-
Behind this barbaric definition, it's simply what end users know and use.
|
|
97
|
-
|
|
98
|
-
_Examples : `GitHub`, `Facebook`, `LinkedIn`, `Airbnb`..._
|
|
99
|
-
|
|
100
|
-
When defined correctly, apps are reusable across multiple products (e.g. `Auth`).
|
|
101
|
-
|
|
102
|
-
### Target
|
|
103
|
-
|
|
104
|
-
A target defines how a product is "exposed" to the end user. It's a combination of platform and runtime.
|
|
105
|
-
|
|
106
|
-
_Examples : `web-react`, `web-angular`, `server-node`, `cli-node`, `cli-stricli`..._
|
|
107
|
-
|
|
108
|
-
Note that it's the only place where the "infrastructure" choices are applied.
|
|
109
|
-
|
|
110
|
-
## 👀 At a glance
|
|
111
|
-
|
|
112
|
-
Here is what a typical use case looks like. For more details, please follow the Guide below.
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
import { UCInput, /* omitted for brevity */ } from 'libmodulor';
|
|
116
|
-
import { Manifest } from '../manifest.js';
|
|
117
|
-
import { SignInServerMain } from './SignInServerMain.js';
|
|
118
|
-
|
|
119
|
-
export interface SignInInput extends UCInput {
|
|
120
|
-
email: UCInputFieldValue<Email>;
|
|
121
|
-
password: UCInputFieldValue<Password>;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export interface SignInOPI0 extends UCOPIBase {
|
|
125
|
-
jwt: JWT;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export const SignInUCD: UCDef<SignInInput, SignInOPI0> = {
|
|
129
|
-
io: {
|
|
130
|
-
i: {
|
|
131
|
-
fields: {
|
|
132
|
-
email: {
|
|
133
|
-
type: new TEmail(),
|
|
134
|
-
},
|
|
135
|
-
password: {
|
|
136
|
-
type: new TPassword({ minLength: 10 }),
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
o: {
|
|
141
|
-
parts: {
|
|
142
|
-
_0: {
|
|
143
|
-
fields: {
|
|
144
|
-
jwt: {
|
|
145
|
-
type: new TJWT(),
|
|
146
|
-
},
|
|
147
|
-
},
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
lifecycle: {
|
|
153
|
-
client: {
|
|
154
|
-
main: SendClientMain,
|
|
155
|
-
policy: AnonymousUCPolicy,
|
|
156
|
-
},
|
|
157
|
-
server: {
|
|
158
|
-
main: SignInServerMain,
|
|
159
|
-
policy: AnonymousUCPolicy,
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
metadata: Manifest.ucReg.SignIn,
|
|
163
|
-
};
|
|
164
|
-
```
|
|
165
|
-
|
|
166
28
|
## 🚀 Getting Started
|
|
167
29
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
> [!NOTE]
|
|
171
|
-
> This Guide is voluntarily very verbose and not scripted so you can get a full overview of how things work. `npx` magic is good. But understanding what happens behind the scenes is good as well.
|
|
172
|
-
|
|
173
|
-
In this Guide, we'll init a repository (a repository can contain multiple apps and products) and create a real life application using the `libmodulor` primitives.
|
|
174
|
-
|
|
175
|
-
We'll build a small trading application. It will contain one `App` named `Trading`, which will contain one `UseCase` named `BuyAsset`. The `App` will be mounted in a `Product` called `SuperTrader` which will be exposed via a `server` `Target`, a `web` `Target`, a `cli` `Target` and finally, a `mcp-server` `Target`.
|
|
176
|
-
|
|
177
|
-
> [!NOTE]
|
|
178
|
-
> MCP stands for [Model Context Protocol](https://modelcontextprotocol.io) introduced recently by [@anthropics](https://github.com/anthropics).
|
|
179
|
-
|
|
180
|
-
If we adapt the abstract mermaid chart displayed above, concretely, it looks like this :
|
|
30
|
+
To get started, we recommend reading the [📖 Introduction](https://github.com/c100k/libmodulor/blob/v0.5.0/docs/Introduction.md) first. It will give you an overview of what `libmodulor` is and how it works.
|
|
181
31
|
|
|
182
|
-
|
|
183
|
-
block-beta
|
|
184
|
-
server
|
|
185
|
-
web
|
|
186
|
-
cli
|
|
187
|
-
mcp_server
|
|
188
|
-
columns 4
|
|
189
|
-
SuperTrader:4
|
|
190
|
-
Trading:2
|
|
191
|
-
Auth:2
|
|
192
|
-
BuyAsset
|
|
193
|
-
ListOrders
|
|
194
|
-
SignUp
|
|
195
|
-
SignIn
|
|
196
|
-
```
|
|
32
|
+
Then, you can follow the [🚀 Tutorial](https://github.com/c100k/libmodulor/blob/v0.5.0/docs/Tutorial.md) that will show you all the main notions by building something real. We'll build a small Trading app that will allow us to buy an asset from a web page, a Terminal, Claude Desktop, Android and iOS ! All within a single, simple codebase.
|
|
197
33
|
|
|
198
|
-
|
|
34
|
+
Finally, for more advanced usages, go to the [📜 Guides](https://github.com/c100k/libmodulor/blob/v0.5.0/docs/Guides.md).
|
|
199
35
|
|
|
200
|
-
|
|
36
|
+
## 👨💻 Contribute
|
|
201
37
|
|
|
202
|
-
|
|
38
|
+
If you think you can help in any way, feel free to contact me (cf. `author` in `package.json`). I'd love to chat.
|
|
203
39
|
|
|
204
|
-
|
|
205
|
-
1. [Create the App](https://github.com/c100k/libmodulor/blob/v0.3.0/docs/getting-started/002_Create_the_App.md)
|
|
206
|
-
1. [Create the UseCase](https://github.com/c100k/libmodulor/blob/v0.3.0/docs/getting-started/003_Create_the_UseCase.md)
|
|
207
|
-
1. [Test the App](https://github.com/c100k/libmodulor/blob/v0.3.0/docs/getting-started/004_Test_the_App.md)
|
|
208
|
-
1. [Create the Product](https://github.com/c100k/libmodulor/blob/v0.3.0/docs/getting-started/005_Create_the_Product.md)
|
|
209
|
-
1. [Create the server Target](https://github.com/c100k/libmodulor/blob/v0.3.0/docs/getting-started/006_Create_the_server_Target.md)
|
|
210
|
-
1. [Create the web Target](https://github.com/c100k/libmodulor/blob/v0.3.0/docs/getting-started/007_Create_the_web_Target.md)
|
|
211
|
-
1. [Switch to a persistent data storage](https://github.com/c100k/libmodulor/blob/v0.3.0/docs/getting-started/008_Switch_to_a_persistent_data_storage.md)
|
|
212
|
-
1. [Define wording for humans](https://github.com/c100k/libmodulor/blob/v0.3.0/docs/getting-started/009_Define_wording_for_humans.md)
|
|
213
|
-
1. [Create the cli Target](https://github.com/c100k/libmodulor/blob/v0.3.0/docs/getting-started/010_Create_the_cli_Target.md)
|
|
214
|
-
1. [Create the mcp-server Target](https://github.com/c100k/libmodulor/blob/v0.3.0/docs/getting-started/011_Create_the_mcp_server_Target.md)
|
|
215
|
-
1. [Summary](https://github.com/c100k/libmodulor/blob/v0.3.0/docs/getting-started/012_Summary.md)
|
|
40
|
+
## ⚖️ License
|
|
216
41
|
|
|
217
|
-
|
|
42
|
+
[LGPL-3.0](./LICENSE)
|
|
@@ -3,10 +3,26 @@ import type { FSManager, Logger, Worker } from '../../std/index.js';
|
|
|
3
3
|
import type { AppName } from '../manifest.js';
|
|
4
4
|
export interface Input {
|
|
5
5
|
appName: AppName;
|
|
6
|
+
/**
|
|
7
|
+
* If set, it uses this value to build the path. Otherwise, it fallbacks on {@link appsRootPath}.
|
|
8
|
+
* @see {@link APPS_ROOT_ALIAS}
|
|
9
|
+
*/
|
|
6
10
|
appsRootAlias?: FilePath | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* If `true`, it uses {@link APPS_ROOT_ALIAS} to build the path. Otherwise, it checks {@link appsRootAlias}.
|
|
13
|
+
*/
|
|
7
14
|
appsRootAliasUseDefault?: boolean | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* @defaultValue {@link APPS_ROOT_DIR_NAME}
|
|
17
|
+
*/
|
|
8
18
|
appsRootPath?: FilePath | undefined;
|
|
9
19
|
ext?: 'js' | 'ts' | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* For example, it can be `[APP_MANIFEST_NAME]`.
|
|
22
|
+
*
|
|
23
|
+
* Do not include the extension. This must be passed by the caller via {@link ext}.
|
|
24
|
+
* Indeed, depending on the context, the extension is not needed (e.g. in tests).
|
|
25
|
+
*/
|
|
10
26
|
filePathParts: FilePath[];
|
|
11
27
|
}
|
|
12
28
|
type Output = FilePath;
|
|
@@ -23,16 +23,18 @@ let AppSrcFilePathBuilder = class AppSrcFilePathBuilder {
|
|
|
23
23
|
const appPath = this.fsManager.path(appsRootPath ?? APPS_ROOT_DIR_NAME, appName);
|
|
24
24
|
let importPath = '';
|
|
25
25
|
if (appsRootAliasUseDefault) {
|
|
26
|
-
importPath = this.fsManager.path(APPS_ROOT_ALIAS, appName);
|
|
26
|
+
importPath = this.fsManager.path(APPS_ROOT_ALIAS, appName); // @apps/MyApp
|
|
27
27
|
}
|
|
28
28
|
else if (appsRootAlias) {
|
|
29
|
-
importPath = this.fsManager.path(appsRootAlias, appName);
|
|
29
|
+
importPath = this.fsManager.path(appsRootAlias, appName); // @myapps/MyApp
|
|
30
30
|
}
|
|
31
31
|
else {
|
|
32
|
-
importPath = this.fsManager.path(appPath);
|
|
33
|
-
importPath = `./${importPath}`;
|
|
32
|
+
importPath = this.fsManager.path(appPath); // src/apps/MyApp
|
|
33
|
+
importPath = `./${importPath}`; // ./src/apps/MyApp
|
|
34
34
|
}
|
|
35
35
|
const filePath = this.fsManager.path(APP_SRC_DIR_NAME, ...filePathParts);
|
|
36
|
+
// NOTE 1 : We don't check if the path exists because it wouldn't work when using an alias. So we let the caller do it. For example when using `import` it will fail by itself.
|
|
37
|
+
// NOTE 2 : Not using `path()` to join in order to avoid the initial `./` to be removed (this is the default behavior of node's join function).
|
|
36
38
|
const suffix = ext ? `.${ext}` : '';
|
|
37
39
|
const path = `${importPath}/${filePath}${suffix}`;
|
|
38
40
|
this.logger.trace('Resolving app src', { path });
|
|
@@ -36,6 +36,7 @@ let GenerateAppsTestsClientMain = class GenerateAppsTestsClientMain {
|
|
|
36
36
|
const { apps } = await this.appSrcBrowser.exec({
|
|
37
37
|
appsPath,
|
|
38
38
|
});
|
|
39
|
+
// Keeping it simple without any defensive programming. Be responsible !
|
|
39
40
|
const depsMappingParsed = new Map((depsMapping || []).map((dm) => dm.split(DEP_MAPPING_SEP)));
|
|
40
41
|
let idx = 0;
|
|
41
42
|
for await (const [appPath] of apps) {
|
|
@@ -70,7 +71,7 @@ export const GenerateAppsTestsUCD = {
|
|
|
70
71
|
...AppInputFieldsDef,
|
|
71
72
|
depsMapping: {
|
|
72
73
|
cardinality: {
|
|
73
|
-
max: 20,
|
|
74
|
+
max: 20, // A totally arbitrary number
|
|
74
75
|
min: 0,
|
|
75
76
|
},
|
|
76
77
|
type: new TDepMapping().setExamples([
|
|
@@ -91,7 +92,7 @@ export const GenerateAppsTestsUCD = {
|
|
|
91
92
|
},
|
|
92
93
|
type: new THostPort()
|
|
93
94
|
.setDefaultValue(14_000)
|
|
94
|
-
.setExamples([14_000]),
|
|
95
|
+
.setExamples([14_000]), // "Calvados ❤️"
|
|
95
96
|
},
|
|
96
97
|
},
|
|
97
98
|
},
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { UC_DEF_FILE_NAME_SUFFIX } from '../../convention.js';
|
|
2
2
|
import { stripUCDLifecycleServer } from '../../uc/index.js';
|
|
3
3
|
export const StripUCDLifecycleServerPlugin = {
|
|
4
|
+
// Why enforce ?
|
|
5
|
+
// Otherwise Rollup parses the file and strips trailing commas.
|
|
6
|
+
// We need them to strip correctly (although the implementation will be made more robust).
|
|
4
7
|
enforce: 'pre',
|
|
5
8
|
name: 'strip-ucd-lifecycle-server',
|
|
6
9
|
transform: (src, id) => {
|
package/dist/esm/convention.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export declare const APP_TEST_REPORTS_DIR_NAME: string;
|
|
|
23
23
|
export declare const PRODUCTS_ROOT_ALIAS: string;
|
|
24
24
|
export declare const PRODUCTS_ROOT_PATH: string[];
|
|
25
25
|
export declare const PRODUCT_MANIFEST_NAME: string;
|
|
26
|
+
export declare const PRODUCT_MANIFEST_FILE_EXT: string;
|
|
26
27
|
export declare const PRODUCT_MANIFEST_FILE_NAME: string;
|
|
27
28
|
export declare const PRODUCT_NAME_PLACEHOLDER: ProductName;
|
|
28
29
|
export declare const UC_DEF_SUFFIX: string;
|
package/dist/esm/convention.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Common
|
|
3
|
+
*/
|
|
1
4
|
export const SRC_DIR_NAME = 'src';
|
|
5
|
+
/*
|
|
6
|
+
* App
|
|
7
|
+
*/
|
|
2
8
|
export const APPS_ROOT_DIR_NAME = 'apps';
|
|
3
9
|
export const APPS_ROOT_ALIAS = `@${APPS_ROOT_DIR_NAME}`;
|
|
4
10
|
export const APPS_ROOT_PATH = [SRC_DIR_NAME, APPS_ROOT_DIR_NAME];
|
|
@@ -7,22 +13,29 @@ export const APP_I18N_NAME = 'I18n';
|
|
|
7
13
|
export const APP_I18N_FILE_NAME = `${APP_I18N_NAME.toLowerCase()}.ts`;
|
|
8
14
|
export const APP_INDEX_NAME = 'index';
|
|
9
15
|
export const APP_INDEX_FILE_NAME = `${APP_INDEX_NAME}.ts`;
|
|
10
|
-
export const APP_INDEX_FILE_NAME_FOR_IMPORT = 'index.js';
|
|
16
|
+
export const APP_INDEX_FILE_NAME_FOR_IMPORT = 'index.js'; // ESM
|
|
11
17
|
export const APP_MANIFEST_NAME = 'Manifest';
|
|
12
18
|
export const APP_MANIFEST_FILE_EXT = '.ts';
|
|
13
|
-
export const APP_MANIFEST_FILE_NAME = `${APP_MANIFEST_NAME.toLowerCase()}
|
|
19
|
+
export const APP_MANIFEST_FILE_NAME = `${APP_MANIFEST_NAME.toLowerCase()}${APP_MANIFEST_FILE_EXT}`;
|
|
14
20
|
export const APP_NAME_PLACEHOLDER = 'AppX';
|
|
15
|
-
export const APP_ROOT_FROM_UCD = ['..', '..', '..'];
|
|
21
|
+
export const APP_ROOT_FROM_UCD = ['..', '..', '..']; // $root/src/ucds/XyzUCD.ts
|
|
16
22
|
export const APP_SRC_DIR_NAME = SRC_DIR_NAME;
|
|
17
23
|
export const APP_SRC_UCDS_DIR_NAME = 'ucds';
|
|
18
24
|
export const APP_TEST_DIR_NAME = 'test';
|
|
19
25
|
export const APP_TEST_MAIN_FILE_NAME = 'App.test.ts';
|
|
20
26
|
export const APP_TEST_REPORTS_DIR_NAME = 'reports';
|
|
27
|
+
/*
|
|
28
|
+
* Product
|
|
29
|
+
*/
|
|
21
30
|
export const PRODUCTS_ROOT_ALIAS = '@p';
|
|
22
31
|
export const PRODUCTS_ROOT_PATH = [SRC_DIR_NAME, 'products'];
|
|
23
32
|
export const PRODUCT_MANIFEST_NAME = 'Manifest';
|
|
24
|
-
export const
|
|
33
|
+
export const PRODUCT_MANIFEST_FILE_EXT = '.ts';
|
|
34
|
+
export const PRODUCT_MANIFEST_FILE_NAME = `${PRODUCT_MANIFEST_NAME.toLowerCase()}${PRODUCT_MANIFEST_FILE_EXT}`;
|
|
25
35
|
export const PRODUCT_NAME_PLACEHOLDER = 'ProductX';
|
|
36
|
+
/*
|
|
37
|
+
* Use Case
|
|
38
|
+
*/
|
|
26
39
|
export const UC_DEF_SUFFIX = 'UCD';
|
|
27
40
|
export const UC_DEF_FILE_NAME_EXT = '.ts';
|
|
28
41
|
export const UC_DEF_FILE_NAME_SUFFIX = `${UC_DEF_SUFFIX}${UC_DEF_FILE_NAME_EXT}`;
|
|
@@ -19,6 +19,14 @@ export declare class Validation {
|
|
|
19
19
|
get(idx?: NumIndex): [ViolationI18nable, string] | null;
|
|
20
20
|
getViolations(): Violation[];
|
|
21
21
|
getViolationsAsI18nables(): ViolationI18nable[];
|
|
22
|
+
/**
|
|
23
|
+
* Check whether the validation has succeeded or not
|
|
24
|
+
*
|
|
25
|
+
* If you want to get a violation, use directly {@link get} and check if it's null.
|
|
26
|
+
* No need to check if `!validation.isOK()` and then call `validation.get()`.
|
|
27
|
+
*
|
|
28
|
+
* @returns
|
|
29
|
+
*/
|
|
22
30
|
isOK(): boolean;
|
|
23
31
|
private violationAsI18nable;
|
|
24
32
|
}
|
|
@@ -24,6 +24,14 @@ export class Validation {
|
|
|
24
24
|
getViolationsAsI18nables() {
|
|
25
25
|
return this.violations.map((v) => this.violationAsI18nable(v));
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Check whether the validation has succeeded or not
|
|
29
|
+
*
|
|
30
|
+
* If you want to get a violation, use directly {@link get} and check if it's null.
|
|
31
|
+
* No need to check if `!validation.isOK()` and then call `validation.get()`.
|
|
32
|
+
*
|
|
33
|
+
* @returns
|
|
34
|
+
*/
|
|
27
35
|
isOK() {
|
|
28
36
|
return this.violations.length === 0;
|
|
29
37
|
}
|
|
@@ -19,7 +19,8 @@ export interface SemanticsValue {
|
|
|
19
19
|
color?: Color;
|
|
20
20
|
variant?: SemanticsVariant;
|
|
21
21
|
}
|
|
22
|
-
export type SemanticsMapping = Record<string,
|
|
22
|
+
export type SemanticsMapping = Record<string, // corresponds to T.toString()
|
|
23
|
+
SemanticsValue>;
|
|
23
24
|
export type SemanticsPredicate<T> = (value: T) => SemanticsValue;
|
|
24
25
|
export interface OptionsOpts {
|
|
25
26
|
shouldTranslateLabels?: boolean;
|
|
@@ -4,10 +4,12 @@ export class TBoolean extends TBase {
|
|
|
4
4
|
return 'Boolean';
|
|
5
5
|
}
|
|
6
6
|
assign(raw) {
|
|
7
|
+
// It's not a string at all
|
|
7
8
|
if (typeof raw !== 'string') {
|
|
8
9
|
super.assign(raw);
|
|
9
10
|
return this;
|
|
10
11
|
}
|
|
12
|
+
// It's a string, let's try to parse it
|
|
11
13
|
if (raw === 'true') {
|
|
12
14
|
super.assign(true);
|
|
13
15
|
return this;
|
package/dist/esm/dt/base/TInt.js
CHANGED
|
@@ -9,15 +9,18 @@ export class TInt extends TNumber {
|
|
|
9
9
|
return 'Int';
|
|
10
10
|
}
|
|
11
11
|
assign(raw) {
|
|
12
|
+
// It's not a string at all
|
|
12
13
|
if (typeof raw !== 'string') {
|
|
13
14
|
super.assign(raw);
|
|
14
15
|
return this;
|
|
15
16
|
}
|
|
17
|
+
// It's a string, let's try to parse it
|
|
16
18
|
const parsed = Number.parseInt(raw, 10);
|
|
17
19
|
if (Number.isNaN(parsed)) {
|
|
18
20
|
super.assign(raw);
|
|
19
21
|
return this;
|
|
20
22
|
}
|
|
23
|
+
// It's been parsed correctly, let's make sure it's not a float that has been "truncated" into an int
|
|
21
24
|
if (parsed.toString() !== raw) {
|
|
22
25
|
super.assign(raw);
|
|
23
26
|
return this;
|
|
@@ -18,10 +18,12 @@ export class TNumber extends TBase {
|
|
|
18
18
|
return 'Number';
|
|
19
19
|
}
|
|
20
20
|
assign(raw) {
|
|
21
|
+
// It's not a string at all
|
|
21
22
|
if (typeof raw !== 'string') {
|
|
22
23
|
super.assign(raw);
|
|
23
24
|
return this;
|
|
24
25
|
}
|
|
26
|
+
// It's a string, let's try to parse it
|
|
25
27
|
const parsed = Number.parseFloat(raw);
|
|
26
28
|
super.assign(parsed);
|
|
27
29
|
return this;
|
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
import type { Validation } from '../Validation.js';
|
|
2
2
|
import { TBase, type TName } from './TBase.js';
|
|
3
3
|
export declare enum TObjectShapeValidationStrategy {
|
|
4
|
+
/**
|
|
5
|
+
* No shape validation is performed
|
|
6
|
+
*
|
|
7
|
+
* To be used when the object can have multiple shapes or that its shape is not important.
|
|
8
|
+
*
|
|
9
|
+
* Otherwise, you can still override {@link validate} in the `T*` class and do your own validation.
|
|
10
|
+
*/
|
|
4
11
|
NONE = "NONE",
|
|
12
|
+
/**
|
|
13
|
+
* Validate against the {@link TObject.example()}
|
|
14
|
+
*
|
|
15
|
+
* It checks that the keys of the value, sorted alphabetically, are the same as the example's keys.
|
|
16
|
+
*/
|
|
5
17
|
SAME_AS_EXAMPLE = "SAME_AS_EXAMPLE"
|
|
6
18
|
}
|
|
7
19
|
export interface TObjectConstraints {
|
|
20
|
+
/**
|
|
21
|
+
* @defaultValue {@link TObjectShapeValidationStrategy.SAME_AS_EXAMPLE}
|
|
22
|
+
*/
|
|
8
23
|
shapeValidationStrategy: TObjectShapeValidationStrategy;
|
|
9
24
|
}
|
|
10
25
|
export declare class TObject<T extends object> extends TBase<T> {
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { TBase } from './TBase.js';
|
|
2
2
|
export var TObjectShapeValidationStrategy;
|
|
3
3
|
(function (TObjectShapeValidationStrategy) {
|
|
4
|
+
/**
|
|
5
|
+
* No shape validation is performed
|
|
6
|
+
*
|
|
7
|
+
* To be used when the object can have multiple shapes or that its shape is not important.
|
|
8
|
+
*
|
|
9
|
+
* Otherwise, you can still override {@link validate} in the `T*` class and do your own validation.
|
|
10
|
+
*/
|
|
4
11
|
TObjectShapeValidationStrategy["NONE"] = "NONE";
|
|
12
|
+
/**
|
|
13
|
+
* Validate against the {@link TObject.example()}
|
|
14
|
+
*
|
|
15
|
+
* It checks that the keys of the value, sorted alphabetically, are the same as the example's keys.
|
|
16
|
+
*/
|
|
5
17
|
TObjectShapeValidationStrategy["SAME_AS_EXAMPLE"] = "SAME_AS_EXAMPLE";
|
|
6
18
|
})(TObjectShapeValidationStrategy || (TObjectShapeValidationStrategy = {}));
|
|
7
19
|
export class TObject extends TBase {
|
|
@@ -19,6 +31,7 @@ export class TObject extends TBase {
|
|
|
19
31
|
return {};
|
|
20
32
|
}
|
|
21
33
|
fmt(ifNullOrUndefined) {
|
|
34
|
+
// typeof this.raw is 'object', hence the check for nullity
|
|
22
35
|
if (this.raw === null || typeof this.raw !== 'object') {
|
|
23
36
|
return super.fmt(ifNullOrUndefined);
|
|
24
37
|
}
|
|
@@ -26,6 +39,7 @@ export class TObject extends TBase {
|
|
|
26
39
|
}
|
|
27
40
|
validate() {
|
|
28
41
|
const validation = super.validate();
|
|
42
|
+
// typeof this.raw is 'object', hence the check for nullity
|
|
29
43
|
if (this.raw === null || typeof this.raw !== 'object') {
|
|
30
44
|
validation.add({
|
|
31
45
|
constraint: 'type',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TBase } from './TBase.js';
|
|
2
2
|
export class TString extends TBase {
|
|
3
3
|
constraints;
|
|
4
|
-
static DEFAULT_MAX_LENGTH = 999_999;
|
|
4
|
+
static DEFAULT_MAX_LENGTH = 999_999; // This is totally arbitrary and will probably be changed
|
|
5
5
|
static DEFAULT_MIN_LENGTH = 0;
|
|
6
6
|
constructor(constraints) {
|
|
7
7
|
super();
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { TString } from '../base/TString.js';
|
|
2
2
|
export class TEmail extends TString {
|
|
3
|
+
// Inspired by https://github.com/jquense/yup/blob/master/src/string.ts#L19
|
|
4
|
+
// Which is inspired by https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
|
|
3
5
|
static FORMAT = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
4
6
|
constructor(constraints) {
|
|
5
7
|
super({
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { TString } from '../base/TString.js';
|
|
2
|
+
// It is too difficult to validate safely an emoji
|
|
3
|
+
// See : https://stackoverflow.com/questions/18862256/how-to-detect-emoji-using-javascript
|
|
4
|
+
// Plus it does not bring much
|
|
5
|
+
// At first, I used maxLength/minLength == 1 but it was naive. Composite ones' length is greater than 1.
|
|
2
6
|
export class TEmoji extends TString {
|
|
3
7
|
tName() {
|
|
4
8
|
return 'Emoji';
|
|
@@ -6,6 +6,8 @@ export class TFile extends TObject {
|
|
|
6
6
|
fileConstraints;
|
|
7
7
|
constructor(fileConstraints) {
|
|
8
8
|
super({
|
|
9
|
+
// We usually process instances of https://developer.mozilla.org/fr/docs/Web/API/File
|
|
10
|
+
// Therefore, it's not strictly the same as the example, with some extra fields that we don't control.
|
|
9
11
|
shapeValidationStrategy: TObjectShapeValidationStrategy.NONE,
|
|
10
12
|
});
|
|
11
13
|
this.fileConstraints = fileConstraints;
|
|
@@ -36,6 +38,7 @@ export class TFile extends TObject {
|
|
|
36
38
|
validation.concat(new TFileMimeType(this.fileConstraints.type)
|
|
37
39
|
.assign(val.type)
|
|
38
40
|
.validate());
|
|
41
|
+
// TODO : Add validation on file size
|
|
39
42
|
return validation;
|
|
40
43
|
}
|
|
41
44
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TString } from '../base/TString.js';
|
|
2
2
|
export class TIPv6 extends TString {
|
|
3
|
+
// Inspired by https://community.fortra.com/forums/intermapper/miscellaneous-topics/5acc4fcf-fa83-e511-80cf-0050568460e4?_ga=2.113564423.1432958022.1523882681-2146416484.1523557976
|
|
3
4
|
static FORMAT = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
|
|
4
5
|
constructor(constraints) {
|
|
5
6
|
super({
|