libmodulor 0.4.0 → 0.6.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 (246) hide show
  1. package/.github/dependabot.yml +19 -0
  2. package/CHANGELOG.md +35 -1
  3. package/README.md +10 -4
  4. package/dist/esm/app/workers/AppSrcFilePathBuilder.d.ts +16 -0
  5. package/dist/esm/app/workers/AppSrcFilePathBuilder.js +6 -4
  6. package/dist/esm/apps/Helper/index.js +1 -0
  7. package/dist/esm/apps/Helper/src/i18n.js +4 -0
  8. package/dist/esm/apps/Helper/src/lib/project.d.ts +2 -0
  9. package/dist/esm/apps/Helper/src/lib/project.js +120 -0
  10. package/dist/esm/apps/Helper/src/manifest.d.ts +5 -0
  11. package/dist/esm/apps/Helper/src/manifest.js +5 -0
  12. package/dist/esm/apps/Helper/src/ucds/CreateProjectUCD.d.ts +15 -0
  13. package/dist/esm/apps/Helper/src/ucds/CreateProjectUCD.js +118 -0
  14. package/dist/esm/apps/Helper/src/ucds/GenerateAppsTestsUCD.js +3 -2
  15. package/dist/esm/bundlers/vite/StripUCDLifecycleServerPlugin.js +3 -0
  16. package/dist/esm/convention.d.ts +1 -0
  17. package/dist/esm/convention.js +17 -4
  18. package/dist/esm/dt/Validation.d.ts +8 -0
  19. package/dist/esm/dt/Validation.js +8 -0
  20. package/dist/esm/dt/base/TBase.d.ts +2 -1
  21. package/dist/esm/dt/base/TBoolean.js +2 -0
  22. package/dist/esm/dt/base/TInt.js +3 -0
  23. package/dist/esm/dt/base/TNumber.js +2 -0
  24. package/dist/esm/dt/base/TObject.d.ts +15 -0
  25. package/dist/esm/dt/base/TObject.js +14 -0
  26. package/dist/esm/dt/base/TString.js +1 -1
  27. package/dist/esm/dt/final/TAmount.js +1 -0
  28. package/dist/esm/dt/final/TCountryISO3166Alpha2.js +1 -0
  29. package/dist/esm/dt/final/TCurrencyISO4217.js +1 -0
  30. package/dist/esm/dt/final/TDateTimeFormat.js +1 -0
  31. package/dist/esm/dt/final/TEmail.js +2 -0
  32. package/dist/esm/dt/final/TEmoji.js +4 -0
  33. package/dist/esm/dt/final/TFile.js +3 -0
  34. package/dist/esm/dt/final/THostAddress.js +2 -0
  35. package/dist/esm/dt/final/TIPv6.js +1 -0
  36. package/dist/esm/dt/final/TJWT.js +8 -0
  37. package/dist/esm/dt/final/TPercentage.js +5 -0
  38. package/dist/esm/dt/final/TSQLQuery.js +1 -0
  39. package/dist/esm/dt/final/TSSHPrivateKey.js +3 -1
  40. package/dist/esm/dt/final/TSemVerVersion.js +1 -0
  41. package/dist/esm/dt/final/TShellCommand.js +1 -0
  42. package/dist/esm/dt/final/TURL.js +2 -0
  43. package/dist/esm/dt/final/TUUID.js +1 -0
  44. package/dist/esm/dt/final/TYesNo.js +1 -1
  45. package/dist/esm/dt/index.d.ts +1 -1
  46. package/dist/esm/i18n/WordingManager.d.ts +16 -0
  47. package/dist/esm/i18n/types.d.ts +5 -0
  48. package/dist/esm/icon/Icon.d.ts +7 -0
  49. package/dist/esm/index.d.ts +3 -0
  50. package/dist/esm/index.js +4 -0
  51. package/dist/esm/index.node.d.ts +1 -0
  52. package/dist/esm/index.node.js +1 -0
  53. package/dist/esm/index.react.d.ts +1 -1
  54. package/dist/esm/product/manifest.d.ts +15 -0
  55. package/dist/esm/products/Helper/container.js +4 -0
  56. package/dist/esm/products/Helper/index.js +5 -0
  57. package/dist/esm/products/Helper/manifest.d.ts +6 -1
  58. package/dist/esm/std/BufferManager.d.ts +18 -0
  59. package/dist/esm/std/ClockManager.d.ts +5 -0
  60. package/dist/esm/std/EnvironmentManager.d.ts +10 -0
  61. package/dist/esm/std/HTTPAPICaller.d.ts +6 -0
  62. package/dist/esm/std/I18nManager.d.ts +26 -0
  63. package/dist/esm/std/JWTManager.d.ts +26 -0
  64. package/dist/esm/std/JobManager.d.ts +6 -0
  65. package/dist/esm/std/LLMManager.d.ts +25 -0
  66. package/dist/esm/std/LLMManager.js +1 -0
  67. package/dist/esm/std/PromptManager.d.ts +8 -0
  68. package/dist/esm/std/SettingsManager.d.ts +19 -0
  69. package/dist/esm/std/SettingsManager.js +9 -0
  70. package/dist/esm/std/ShellCommandExecutor.d.ts +19 -0
  71. package/dist/esm/std/ShellCommandExecutor.js +1 -0
  72. package/dist/esm/std/impl/ConsoleLogger.js +7 -1
  73. package/dist/esm/std/impl/FakeEmailManager.js +1 -0
  74. package/dist/esm/std/impl/FakeJobManager.js +1 -0
  75. package/dist/esm/std/impl/FetchHTTPAPICallExecutor.d.ts +9 -0
  76. package/dist/esm/std/impl/FetchHTTPAPICallExecutor.js +11 -0
  77. package/dist/esm/std/impl/MistralAILLMManager.d.ts +17 -0
  78. package/dist/esm/std/impl/MistralAILLMManager.js +56 -0
  79. package/dist/esm/std/impl/NodeCryptoManager.js +6 -1
  80. package/dist/esm/std/impl/NodeDeterministicCryptoManager.d.ts +14 -0
  81. package/dist/esm/std/impl/NodeDeterministicCryptoManager.js +17 -3
  82. package/dist/esm/std/impl/NodeFSManager.js +10 -0
  83. package/dist/esm/std/impl/NodeHTTPAPICallExecutorAgentBuilder.js +2 -0
  84. package/dist/esm/std/impl/NodePromptManager.js +3 -0
  85. package/dist/esm/std/impl/NodeSpawnShellCommandExecutor.d.ts +4 -0
  86. package/dist/esm/std/impl/NodeSpawnShellCommandExecutor.js +41 -0
  87. package/dist/esm/std/impl/OllamaLLMManager.d.ts +20 -0
  88. package/dist/esm/std/impl/OllamaLLMManager.js +56 -0
  89. package/dist/esm/std/impl/OpenAILLMManager.d.ts +17 -0
  90. package/dist/esm/std/impl/OpenAILLMManager.js +51 -0
  91. package/dist/esm/std/impl/SimpleHTTPAPICaller.js +14 -0
  92. package/dist/esm/std/impl/SimpleMapI18nManager.js +4 -2
  93. package/dist/esm/std/impl/StdDateClockManager.js +3 -0
  94. package/dist/esm/std/impl/UCDataStoreExternalResourceManager.js +3 -0
  95. package/dist/esm/std/impl/WebCryptoManager.js +9 -0
  96. package/dist/esm/std/index.d.ts +2 -0
  97. package/dist/esm/std/index.js +2 -0
  98. package/dist/esm/target/lib/cli/renderer.js +3 -0
  99. package/dist/esm/target/lib/client/consts.d.ts +3 -0
  100. package/dist/esm/target/lib/client/consts.js +3 -0
  101. package/dist/esm/target/lib/mcp-server/MCPServerBooter.js +1 -0
  102. package/dist/esm/target/lib/react/UCContainer.js +1 -0
  103. package/dist/esm/target/lib/react/UCPanel.js +4 -0
  104. package/dist/esm/target/lib/react/form.d.ts +26 -2
  105. package/dist/esm/target/lib/react/form.js +18 -1
  106. package/dist/esm/target/lib/react/useUC.d.ts +8 -0
  107. package/dist/esm/target/lib/react/useUC.js +22 -0
  108. package/dist/esm/target/lib/react/useUCOR.d.ts +15 -0
  109. package/dist/esm/target/lib/react/useUCOR.js +45 -0
  110. package/dist/esm/target/lib/rn/input.d.ts +7 -0
  111. package/dist/esm/target/lib/rn/input.js +2 -0
  112. package/dist/esm/target/lib/server/AuthenticationChecker.js +2 -1
  113. package/dist/esm/target/lib/server/BasicAuthenticationChecker.js +1 -0
  114. package/dist/esm/target/lib/server/CSPDirectivesBuilder.js +13 -0
  115. package/dist/esm/target/lib/server/CustomerFacingErrorBuilder.js +3 -0
  116. package/dist/esm/target/lib/server/PrivateApiKeyAuthenticationChecker.js +1 -0
  117. package/dist/esm/target/lib/server/PublicApiKeyChecker.js +1 -1
  118. package/dist/esm/target/lib/server/RequestChecker.js +5 -4
  119. package/dist/esm/target/lib/server/RequestHandler.d.ts +5 -0
  120. package/dist/esm/target/lib/server/RequestLogger.js +5 -0
  121. package/dist/esm/target/lib/server/ServerManager.d.ts +19 -0
  122. package/dist/esm/target/lib/server/consts.d.ts +3 -0
  123. package/dist/esm/target/lib/server/consts.js +3 -0
  124. package/dist/esm/target/lib/web/input.d.ts +21 -0
  125. package/dist/esm/target/lib/web/input.js +4 -0
  126. package/dist/esm/target/node-core-cli/NodeCoreCLIManager.js +2 -2
  127. package/dist/esm/target/node-express-server/NodeExpressServerManager.js +5 -0
  128. package/dist/esm/target/node-express-server/lib/AuthCookieCreator.js +1 -1
  129. package/dist/esm/target/node-express-server/middlewares/AuthenticationCheckerMiddlewareBuilder.js +1 -0
  130. package/dist/esm/target/node-express-server/middlewares/PublicApiKeyCheckerMiddlewareBuilder.js +1 -0
  131. package/dist/esm/target/node-express-server/middlewares/RequestCheckerMiddlewareBuilder.js +1 -0
  132. package/dist/esm/target/node-express-server/middlewares/RequestHandlerMiddlewareBuilder.js +8 -0
  133. package/dist/esm/target/node-express-server/middlewares/RequestLoggerMiddlewareBuilder.js +1 -0
  134. package/dist/esm/target/node-mcp-server/NodeLocalStdioMCPServerManager.d.ts +10 -0
  135. package/dist/esm/target/node-mcp-server/NodeLocalStdioMCPServerManager.js +14 -0
  136. package/dist/esm/target/react-native-pure/UCForm.js +2 -2
  137. package/dist/esm/target/react-native-pure/UCFormField.d.ts +2 -9
  138. package/dist/esm/target/react-native-pure/UCFormField.js +9 -20
  139. package/dist/esm/target/react-native-pure/UCFormFieldControl.d.ts +3 -10
  140. package/dist/esm/target/react-native-pure/UCFormFieldControl.js +9 -8
  141. package/dist/esm/target/react-native-pure/UCFormFieldDesc.d.ts +2 -5
  142. package/dist/esm/target/react-native-pure/UCFormFieldDesc.js +2 -2
  143. package/dist/esm/target/react-native-pure/UCFormFieldErr.d.ts +2 -6
  144. package/dist/esm/target/react-native-pure/UCFormFieldLabel.d.ts +2 -5
  145. package/dist/esm/target/react-native-pure/UCFormFieldLabel.js +2 -2
  146. package/dist/esm/target/react-native-pure/UCFormSubmitControl.d.ts +2 -7
  147. package/dist/esm/target/react-native-pure/UCFormSubmitControl.js +1 -1
  148. package/dist/esm/target/react-web-pure/UCForm.js +1 -1
  149. package/dist/esm/target/react-web-pure/UCFormField.d.ts +2 -9
  150. package/dist/esm/target/react-web-pure/UCFormField.js +9 -20
  151. package/dist/esm/target/react-web-pure/UCFormFieldControl.d.ts +3 -10
  152. package/dist/esm/target/react-web-pure/UCFormFieldControl.js +20 -5
  153. package/dist/esm/target/react-web-pure/UCFormFieldDesc.d.ts +2 -5
  154. package/dist/esm/target/react-web-pure/UCFormFieldDesc.js +2 -2
  155. package/dist/esm/target/react-web-pure/UCFormFieldErr.d.ts +2 -6
  156. package/dist/esm/target/react-web-pure/UCFormFieldLabel.d.ts +2 -5
  157. package/dist/esm/target/react-web-pure/UCFormFieldLabel.js +3 -3
  158. package/dist/esm/target/react-web-pure/UCFormSubmitControl.d.ts +2 -4
  159. package/dist/esm/target/react-web-pure/UCFormSubmitControl.js +1 -1
  160. package/dist/esm/testing/AppTester.d.ts +4 -0
  161. package/dist/esm/testing/AppTester.js +16 -0
  162. package/dist/esm/testing/AppTesterConfigurator.d.ts +68 -0
  163. package/dist/esm/testing/UCDataStoreTester.d.ts +9 -0
  164. package/dist/esm/testing/UCDataStoreTester.js +13 -0
  165. package/dist/esm/testing/impl/SimpleAppDocsEmitter.js +22 -2
  166. package/dist/esm/testing/impl/SimpleAppTesterConfigurator.js +1 -0
  167. package/dist/esm/testing/impl/SimpleHTMLAppTestReportEmitter.js +9 -3
  168. package/dist/esm/testing/impl/TypeScriptLibUCDefASTParser.js +12 -4
  169. package/dist/esm/testing/impl/VitestAppTestSuiteEmitter.js +6 -0
  170. package/dist/esm/testing/opts.d.ts +38 -0
  171. package/dist/esm/testing/opts.js +1 -1
  172. package/dist/esm/testing/uc-input.js +2 -0
  173. package/dist/esm/testing/workers/AppTesterCtxInitializer.js +7 -0
  174. package/dist/esm/testing/workers/UCExecutor.js +1 -0
  175. package/dist/esm/testing/workers/checkers/AppIndexChecker.js +1 -0
  176. package/dist/esm/testing/workers/checkers/UCDefSourcesChecker.js +4 -0
  177. package/dist/esm/uc/UC.js +19 -1
  178. package/dist/esm/uc/UCInputField.d.ts +28 -0
  179. package/dist/esm/uc/UCInputField.js +42 -0
  180. package/dist/esm/uc/data.d.ts +3 -0
  181. package/dist/esm/uc/def.d.ts +7 -0
  182. package/dist/esm/uc/exec.d.ts +39 -0
  183. package/dist/esm/uc/exec.js +29 -0
  184. package/dist/esm/uc/ext.d.ts +30 -1
  185. package/dist/esm/uc/helpers/UCOutputBuilder.js +5 -0
  186. package/dist/esm/uc/helpers/UCOutputReader.js +3 -1
  187. package/dist/esm/uc/impl/HTTPUCTransporter.js +4 -0
  188. package/dist/esm/uc/impl/InMemoryUCDataStore.js +7 -0
  189. package/dist/esm/uc/impl/KnexUCDataStore.d.ts +4 -0
  190. package/dist/esm/uc/impl/KnexUCDataStore.js +14 -0
  191. package/dist/esm/uc/impl/SimpleUCManager.js +6 -0
  192. package/dist/esm/uc/input-field.d.ts +60 -0
  193. package/dist/esm/uc/input-field.js +33 -0
  194. package/dist/esm/uc/input.d.ts +24 -0
  195. package/dist/esm/uc/lifecycle/client/IdleClientMain.js +1 -0
  196. package/dist/esm/uc/lifecycle/server/IdleServerMain.js +2 -0
  197. package/dist/esm/uc/manager.d.ts +11 -0
  198. package/dist/esm/uc/metadata.d.ts +10 -0
  199. package/dist/esm/uc/opi-layout.d.ts +3 -0
  200. package/dist/esm/uc/opi.d.ts +8 -0
  201. package/dist/esm/uc/output-field.d.ts +9 -0
  202. package/dist/esm/uc/output-part.d.ts +22 -0
  203. package/dist/esm/uc/output.d.ts +3 -0
  204. package/dist/esm/uc/policies/RoleRegularUCPolicy.js +1 -0
  205. package/dist/esm/uc/policies/funcs.js +1 -0
  206. package/dist/esm/uc/policy.d.ts +22 -0
  207. package/dist/esm/uc/sec.d.ts +9 -0
  208. package/dist/esm/uc/server.d.ts +10 -0
  209. package/dist/esm/uc/settings.d.ts +25 -0
  210. package/dist/esm/uc/side-effect.d.ts +16 -0
  211. package/dist/esm/uc/side-effect.js +16 -0
  212. package/dist/esm/uc/utils/rInput.d.ts +12 -0
  213. package/dist/esm/uc/utils/rInput.js +2 -0
  214. package/dist/esm/uc/utils/rVal.d.ts +25 -0
  215. package/dist/esm/uc/utils/rVal.js +27 -0
  216. package/dist/esm/uc/utils/recIs.d.ts +9 -0
  217. package/dist/esm/uc/utils/recIs.js +12 -1
  218. package/dist/esm/uc/utils/stripUCDLifecycleServer.d.ts +13 -0
  219. package/dist/esm/uc/utils/stripUCDLifecycleServer.js +17 -0
  220. package/dist/esm/uc/utils/ucifcoIsForArray.d.ts +6 -0
  221. package/dist/esm/uc/utils/ucifcoIsForArray.js +6 -0
  222. package/dist/esm/uc/workers/SimpleAggregateFinder.d.ts +12 -0
  223. package/dist/esm/uc/workers/SimpleAggregateFinder.js +12 -0
  224. package/dist/esm/uc/workers/UCBuilder.d.ts +7 -0
  225. package/dist/esm/uc/workers/UCBuilder.js +7 -0
  226. package/dist/esm/uc/workers/UCExecChecker.js +2 -0
  227. package/dist/esm/uc/workers/UCInputFilesProcessor.js +10 -4
  228. package/dist/esm/uc/workers/UCOutputFilesProcessor.js +6 -2
  229. package/dist/esm/utils/async/sleep.d.ts +10 -0
  230. package/dist/esm/utils/async/sleep.js +10 -0
  231. package/dist/esm/utils/http/appendData.js +5 -1
  232. package/dist/esm/utils/ioc/ContainerPrinter.js +2 -0
  233. package/dist/esm/utils/ioc/bindCommon.js +4 -0
  234. package/dist/esm/utils/ioc/bindNodeCLI.js +2 -0
  235. package/dist/esm/utils/ioc/bindNodeCore.js +1 -0
  236. package/dist/esm/utils/ioc/bindProduct.js +2 -0
  237. package/dist/esm/utils/ioc/bindRN.js +1 -0
  238. package/dist/esm/utils/ioc/bindServer.js +1 -0
  239. package/dist/esm/utils/ioc/bindWeb.js +2 -0
  240. package/dist/esm/utils/ioc/container.js +6 -0
  241. package/dist/esm/utils/numbers/units.js +3 -0
  242. package/dist/esm/utils/types/funcs.d.ts +35 -0
  243. package/dist/esm/utils/types/funcs.js +35 -0
  244. package/dist/esm/utils/types/utility-types.d.ts +17 -0
  245. package/dist/esm/utils/types/utility-types.js +1 -0
  246. package/package.json +13 -14
@@ -0,0 +1,19 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "npm"
9
+ directory: "/"
10
+ schedule:
11
+ interval: "weekly"
12
+ ignore:
13
+ - dependency-name: "inversify"
14
+ versions: ["7.x"]
15
+ groups:
16
+ dev:
17
+ dependency-type: "development"
18
+ prod:
19
+ dependency-type: "production"
package/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v0.6.0 (2025-02-28)
4
+
5
+ **BREAKING**
6
+
7
+ - Extract common react elements on web and rn, renaming some of the props : check the new props names and the new `validateFormField` to simplify your overrides
8
+ - Remove `helper` from the exports map : it makes no sense to expose it as it is an executable (see `npx libmodulor` below)
9
+
10
+ **Added**
11
+
12
+ - Introduce the `npx libmodulor CreateProject` command
13
+ - Introduce `select` to target `react-web-pure` : it now renders an HTML `select` when the UC input field `hasOptions()`
14
+
15
+ **Misc**
16
+
17
+ - Bump `react` to `19.0.0`
18
+ - Add a new "Style the web Target" tutorial step showing how to use `tailwindcss` and `daisyUI` to provide custom components
19
+
20
+ ## v0.5.0 (2025-02-24)
21
+
22
+ **BREAKING**
23
+
24
+ - Replace `.js` by `.ts` for `ProductManifest`
25
+
26
+ **Added**
27
+
28
+ - Introduce a 3<sup>rd</sup> implementation of `LLMManager` (`OllamaLLMManager`) to run models locally
29
+ - Introduce `LLMManager` with 2 implementations (`MistralAILLMManager` and `OpenAILLMManager`)
30
+
31
+ **Misc**
32
+
33
+ - Simplify the signature of `UCDef.ext.http.transform` removing the generic `T` return
34
+ - Add JSDocs and comments for a better in-place documentation (i.e. in .d.ts files)
35
+ - Add the Tutorial code in the repo at `examples/libmodulor-tuto` and add a new "Expose a rn Target" step
36
+
3
37
  ## v0.4.0 (2025-01-31)
4
38
 
5
39
  **feat(target): introduce react-native-pure**
@@ -15,7 +49,7 @@ To help with the creation of specific targets, we've added a new one : `react-na
15
49
 
16
50
  **feat(uc): introduce alternate mounting point**
17
51
 
18
- Added a new property `UcDef.ext.http.mountAlsoAt` to be able to define path aliases. See the comment below to understand why.
52
+ Added a new property `UCDef.ext.http.mountAlsoAt` to be able to define path aliases. See the comment below to understand why.
19
53
 
20
54
  ```typescript
21
55
  /**
package/README.md CHANGED
@@ -27,11 +27,17 @@ Applications created with `libmodulor` have **6 main properties** :
27
27
 
28
28
  ## 🚀 Getting Started
29
29
 
30
- To get started, we recommend reading the [📖 Introduction](https://github.com/c100k/libmodulor/blob/v0.4.0/docs/Introduction.md) first. It will give you an overview of what `libmodulor` is and how it works.
30
+ If you're discovering `libmodulor`, we recommend reading the [📖 Introduction](https://github.com/c100k/libmodulor/blob/v0.6.0/docs/Introduction.md) first. It will give you an overview of what `libmodulor` is and how it works.
31
31
 
32
- Then, you can follow the [🚀 Tutorial](https://github.com/c100k/libmodulor/blob/v0.4.0/docs/Tutorial.md) that will show you all the main notions by building something real.
32
+ If you want to learn by doing, you can follow the [🚀 Tutorial](https://github.com/c100k/libmodulor/blob/v0.6.0/docs/Tutorial.md). It contains multiple steps showing you the basics of `libmodulor`. We'll build a small Trading app that will allow us to buy an asset from `curl`, a SPA, a CLI, Claude Desktop, an Android mobile app and an iOS mobile app !
33
33
 
34
- Finally, for more advanced usages, go to the [📜 Guides](https://github.com/c100k/libmodulor/blob/v0.4.0/docs/Guides.md).
34
+ If you're felling adventurous, create a project and start building your own application, with your own apps, use cases, products, targets :
35
+
36
+ ```sh
37
+ npx libmodulor CreateProject --outPath ~/Downloads --projectName libmodulor-projx
38
+ ```
39
+
40
+ Finally, for more advanced uses, check out the [📜 Guides](https://github.com/c100k/libmodulor/blob/v0.6.0/docs/Guides.md).
35
41
 
36
42
  ## 👨‍💻 Contribute
37
43
 
@@ -39,4 +45,4 @@ If you think you can help in any way, feel free to contact me (cf. `author` in `
39
45
 
40
46
  ## ⚖️ License
41
47
 
42
- [LGPL-3.0](./LICENSE)
48
+ [LGPL-3.0](https://github.com/c100k/libmodulor/blob/v0.6.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 });
@@ -1,2 +1,3 @@
1
+ // Expose only what's necessary
1
2
  export { I18n } from './src/i18n.js';
2
3
  export { Manifest } from './src/manifest.js';
@@ -1,6 +1,8 @@
1
1
  export const I18n = {
2
2
  en: {
3
3
  err_unknown_app: 'Unknown app : {{appPath}}',
4
+ uc_CreateProject_desc: 'Create a project by initializing a git repo, creating basic config files and installing the required dependencies',
5
+ uc_CreateProject_label: 'Create a project',
4
6
  uc_DeleteGeneratedAppsTests_desc: 'Delete the automated test suite generated for each app',
5
7
  uc_DeleteGeneratedAppsTests_label: 'Delete generated apps tests',
6
8
  uc_GenerateAppsTests_desc: 'Generate an automated test suite for each app',
@@ -9,6 +11,8 @@ export const I18n = {
9
11
  uc_TestApp_label: 'Test app',
10
12
  ucif_appPath_desc: 'The path of the app',
11
13
  ucif_appsPath_desc: 'The path to the directory containing all the apps',
14
+ ucif_projectName_desc: "Name of the project conforming to the package.json's spec (i.e. a-z, 0-9, -)",
15
+ ucif_outPath_desc: 'Path to a directory where to create the project. Do not include the project name in it. It is created recursively if missing.',
12
16
  ucif_monkeyTestingTimeoutInMs_desc: 'These tests can take longer than the usual default of 5000ms because they try lots of possibilities',
13
17
  ucif_depsMapping_desc: 'The mapping of dependencies in case some of them need a specific pattern (e.g. one directory above the default)',
14
18
  ucif_serverPortRangeStart_desc: 'The port number to start with when generating the server to test (incremented by 1) for each app',
@@ -0,0 +1,2 @@
1
+ import type { FileName, Slug } from '../../../../dt/index.js';
2
+ export declare function projectFiles(name: Slug): Map<FileName, string>;
@@ -0,0 +1,120 @@
1
+ const GITIGNORE = `coverage
2
+ dist
3
+ node_modules
4
+ src/apps/**/test/reports
5
+ src/apps/**/test/*.test.ts
6
+ src/products/**/rn/.expo
7
+ .env
8
+ `;
9
+ const BIOME_JSON = `{
10
+ "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
11
+ "files": {
12
+ "ignore": ["coverage", "dist", "node_modules"],
13
+ "ignoreUnknown": true
14
+ },
15
+ "formatter": {
16
+ "indentStyle": "space",
17
+ "indentWidth": 4
18
+ },
19
+ "javascript": {
20
+ "formatter": {
21
+ "quoteStyle": "single"
22
+ },
23
+ "parser": {
24
+ "unsafeParameterDecoratorsEnabled": true
25
+ }
26
+ }
27
+ }
28
+ `;
29
+ const PACKAGE_JSON = (name) => `{
30
+ "name": "${name}",
31
+ "version": "0.1.0",
32
+ "type": "module",
33
+ "private": true,
34
+ "scripts": {
35
+ "lint": "biome check --write .",
36
+ "test": "tsc && vitest run --passWithNoTests"
37
+ },
38
+ "dependencies": {
39
+ "inversify": "^6.2.2",
40
+ "libmodulor": "^0.5.0",
41
+ "reflect-metadata": "^0.2.2"
42
+ },
43
+ "devDependencies": {
44
+ "@biomejs/biome": "^1.9.4",
45
+ "@types/node": "^22.13.5",
46
+ "@vitest/coverage-v8": "^3.0.7",
47
+ "buffer": "^6.0.3",
48
+ "cookie-parser": "^1.4.7",
49
+ "express": "^4.21.2",
50
+ "express-fileupload": "^1.5.1",
51
+ "fast-check": "^3.23.2",
52
+ "helmet": "^8.0.0",
53
+ "jose": "^6.0.8",
54
+ "typescript": "^5.7.3",
55
+ "vite": "^6.2.0",
56
+ "vitest": "^3.0.7"
57
+ }
58
+ }
59
+ `;
60
+ const README_MD = (name) => `# ${name}
61
+
62
+ 🚀🚀🚀
63
+ `;
64
+ const TSCONFIG_JSON = `{
65
+ "compilerOptions": {
66
+ "allowSyntheticDefaultImports": true,
67
+ "declaration": true,
68
+ "lib": ["dom", "esnext"],
69
+ "module": "NodeNext",
70
+ "moduleResolution": "NodeNext",
71
+ "noEmit": true,
72
+ "removeComments": true,
73
+ "skipLibCheck": true,
74
+ "sourceMap": true,
75
+ "target": "ESNext",
76
+
77
+ "strict": true,
78
+ "allowUnreachableCode": false,
79
+ "allowUnusedLabels": false,
80
+ "exactOptionalPropertyTypes": true,
81
+ "noFallthroughCasesInSwitch": true,
82
+ "noPropertyAccessFromIndexSignature": true,
83
+ "noImplicitOverride": true,
84
+ "noImplicitReturns": true,
85
+ "noUncheckedIndexedAccess": true,
86
+ "noUnusedLocals": true,
87
+ "noUnusedParameters": true,
88
+ "verbatimModuleSyntax": true,
89
+
90
+ "emitDecoratorMetadata": true,
91
+ "experimentalDecorators": true,
92
+
93
+ "jsx": "react"
94
+ }
95
+ }
96
+ `;
97
+ const VITEST_CONFIG_TS = `import { defineConfig } from 'vitest/config';
98
+
99
+ export default defineConfig({
100
+ test: {
101
+ coverage: {
102
+ enabled: true,
103
+ exclude: ['src/apps/**/test', 'src/**/*.test.ts'],
104
+ include: ['src'],
105
+ reporter: ['html', 'lcov', 'text'],
106
+ },
107
+ reporters: ['verbose'],
108
+ },
109
+ });
110
+ `;
111
+ export function projectFiles(name) {
112
+ return new Map([
113
+ ['.gitignore', GITIGNORE],
114
+ ['biome.json', BIOME_JSON],
115
+ ['package.json', PACKAGE_JSON(name)],
116
+ ['README.md', README_MD(name)],
117
+ ['tsconfig.json', TSCONFIG_JSON],
118
+ ['vitest.config.ts', VITEST_CONFIG_TS],
119
+ ]);
120
+ }
@@ -2,6 +2,11 @@ export declare const Manifest: {
2
2
  languageCodes: "en"[];
3
3
  name: "Helper";
4
4
  ucReg: {
5
+ CreateProject: {
6
+ action: "Create";
7
+ icon: string;
8
+ name: "CreateProject";
9
+ };
5
10
  DeleteGeneratedAppsTests: {
6
11
  action: "Delete";
7
12
  icon: string;
@@ -2,6 +2,11 @@ export const Manifest = {
2
2
  languageCodes: ['en'],
3
3
  name: 'Helper',
4
4
  ucReg: {
5
+ CreateProject: {
6
+ action: 'Create',
7
+ icon: 'plus',
8
+ name: 'CreateProject',
9
+ },
5
10
  DeleteGeneratedAppsTests: {
6
11
  action: 'Delete',
7
12
  icon: 'trash-can',
@@ -0,0 +1,15 @@
1
+ import { type DirPath, type Slug } from '../../../../dt/index.js';
2
+ import type { FSManager, Logger, ShellCommandExecutor } from '../../../../std/index.js';
3
+ import { type UCDef, type UCInput, type UCInputFieldValue, type UCMain, type UCMainInput } from '../../../../uc/index.js';
4
+ export interface CreateProjectInput extends UCInput {
5
+ outPath: UCInputFieldValue<DirPath>;
6
+ projectName: UCInputFieldValue<Slug>;
7
+ }
8
+ export declare class CreateProjectClientMain implements UCMain<CreateProjectInput> {
9
+ private fsManager;
10
+ private logger;
11
+ private shellCommandExecutor;
12
+ constructor(fsManager: FSManager, logger: Logger, shellCommandExecutor: ShellCommandExecutor);
13
+ exec({ uc }: UCMainInput<CreateProjectInput>): Promise<void>;
14
+ }
15
+ export declare const CreateProjectUCD: UCDef<CreateProjectInput>;
@@ -0,0 +1,118 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { inject, injectable } from 'inversify';
14
+ import { APPS_ROOT_PATH, PRODUCTS_ROOT_PATH } from '../../../../convention.js';
15
+ import { TDirPath, TSlug, } from '../../../../dt/index.js';
16
+ import { EverybodyUCPolicy, } from '../../../../uc/index.js';
17
+ import { projectFiles } from '../lib/project.js';
18
+ import { Manifest } from '../manifest.js';
19
+ let CreateProjectClientMain = class CreateProjectClientMain {
20
+ fsManager;
21
+ logger;
22
+ shellCommandExecutor;
23
+ constructor(fsManager, logger, shellCommandExecutor) {
24
+ this.fsManager = fsManager;
25
+ this.logger = logger;
26
+ this.shellCommandExecutor = shellCommandExecutor;
27
+ }
28
+ async exec({ uc }) {
29
+ const outPath = uc.reqVal0('outPath');
30
+ const projectName = uc.reqVal0('projectName');
31
+ const cwd = this.fsManager.path(outPath, projectName);
32
+ this.logger.info('Creating root dir : %s', cwd);
33
+ await this.fsManager.mkdir(cwd, { recursive: true });
34
+ this.logger.info('Initializing git repository');
35
+ await this.shellCommandExecutor.exec({
36
+ bin: 'git',
37
+ opts: { args: ['init'], cwd },
38
+ });
39
+ this.logger.info('Creating config files');
40
+ const files = projectFiles(projectName);
41
+ for await (const [fileName, content] of files) {
42
+ const path = this.fsManager.path(cwd, fileName);
43
+ await this.fsManager.touch(path, content);
44
+ }
45
+ const dirs = [APPS_ROOT_PATH, PRODUCTS_ROOT_PATH];
46
+ this.logger.info('Creating apps and products directories');
47
+ for await (const dirPath of dirs) {
48
+ const path = this.fsManager.path(cwd, ...dirPath);
49
+ await this.fsManager.mkdir(path, { recursive: true });
50
+ await this.fsManager.touch(this.fsManager.path(path, '.gitkeep'), '');
51
+ }
52
+ this.logger.info('Installing dependencies');
53
+ await this.shellCommandExecutor.exec({
54
+ bin: 'yarn',
55
+ opts: { args: ['install'], cwd },
56
+ });
57
+ this.logger.info('Committing');
58
+ const cmdArgs = [
59
+ ['branch', '-M', 'master'],
60
+ ['add', '.'],
61
+ ['commit', '-am', '"chore: initial commit"'],
62
+ ];
63
+ for await (const args of cmdArgs) {
64
+ await this.shellCommandExecutor.exec({
65
+ bin: 'git',
66
+ opts: { args, cwd },
67
+ });
68
+ }
69
+ const devCmds = ['lint', 'test'];
70
+ for await (const cmd of devCmds) {
71
+ this.logger.info('Testing dev command : yarn %s', cmd);
72
+ await this.shellCommandExecutor.exec({
73
+ bin: 'yarn',
74
+ opts: { args: [cmd], cwd },
75
+ });
76
+ }
77
+ this.logger.info('Done ! Project ready ! ✅ 🚀');
78
+ }
79
+ };
80
+ CreateProjectClientMain = __decorate([
81
+ injectable(),
82
+ __param(0, inject('FSManager')),
83
+ __param(1, inject('Logger')),
84
+ __param(2, inject('ShellCommandExecutor')),
85
+ __metadata("design:paramtypes", [Object, Object, Object])
86
+ ], CreateProjectClientMain);
87
+ export { CreateProjectClientMain };
88
+ export const CreateProjectUCD = {
89
+ ext: {
90
+ cmd: {
91
+ mountAt: 'CreateProject',
92
+ },
93
+ },
94
+ io: {
95
+ i: {
96
+ fields: {
97
+ outPath: {
98
+ cardinality: {
99
+ min: 0,
100
+ },
101
+ type: new TDirPath()
102
+ .setDefaultValue('./')
103
+ .setExamples([['~', 'Desktop'].join('/')]),
104
+ },
105
+ projectName: {
106
+ type: new TSlug(),
107
+ },
108
+ },
109
+ },
110
+ },
111
+ lifecycle: {
112
+ client: {
113
+ main: CreateProjectClientMain,
114
+ policy: EverybodyUCPolicy,
115
+ },
116
+ },
117
+ metadata: Manifest.ucReg.CreateProject,
118
+ };
@@ -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) => {
@@ -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;
@@ -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()}.ts`;
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 PRODUCT_MANIFEST_FILE_NAME = `${PRODUCT_MANIFEST_NAME.toLowerCase()}.js`;
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, SemanticsValue>;
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;
@@ -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;