libmodulor 0.4.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.
Files changed (204) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/README.md +3 -3
  3. package/dist/esm/app/workers/AppSrcFilePathBuilder.d.ts +16 -0
  4. package/dist/esm/app/workers/AppSrcFilePathBuilder.js +6 -4
  5. package/dist/esm/apps/Helper/index.js +1 -0
  6. package/dist/esm/apps/Helper/src/ucds/GenerateAppsTestsUCD.js +3 -2
  7. package/dist/esm/bundlers/vite/StripUCDLifecycleServerPlugin.js +3 -0
  8. package/dist/esm/convention.d.ts +1 -0
  9. package/dist/esm/convention.js +17 -4
  10. package/dist/esm/dt/Validation.d.ts +8 -0
  11. package/dist/esm/dt/Validation.js +8 -0
  12. package/dist/esm/dt/base/TBase.d.ts +2 -1
  13. package/dist/esm/dt/base/TBoolean.js +2 -0
  14. package/dist/esm/dt/base/TInt.js +3 -0
  15. package/dist/esm/dt/base/TNumber.js +2 -0
  16. package/dist/esm/dt/base/TObject.d.ts +15 -0
  17. package/dist/esm/dt/base/TObject.js +14 -0
  18. package/dist/esm/dt/base/TString.js +1 -1
  19. package/dist/esm/dt/final/TAmount.js +1 -0
  20. package/dist/esm/dt/final/TCountryISO3166Alpha2.js +1 -0
  21. package/dist/esm/dt/final/TCurrencyISO4217.js +1 -0
  22. package/dist/esm/dt/final/TDateTimeFormat.js +1 -0
  23. package/dist/esm/dt/final/TEmail.js +2 -0
  24. package/dist/esm/dt/final/TEmoji.js +4 -0
  25. package/dist/esm/dt/final/TFile.js +3 -0
  26. package/dist/esm/dt/final/THostAddress.js +2 -0
  27. package/dist/esm/dt/final/TIPv6.js +1 -0
  28. package/dist/esm/dt/final/TJWT.js +8 -0
  29. package/dist/esm/dt/final/TPercentage.js +5 -0
  30. package/dist/esm/dt/final/TSQLQuery.js +1 -0
  31. package/dist/esm/dt/final/TSSHPrivateKey.js +3 -1
  32. package/dist/esm/dt/final/TSemVerVersion.js +1 -0
  33. package/dist/esm/dt/final/TShellCommand.js +1 -0
  34. package/dist/esm/dt/final/TURL.js +2 -0
  35. package/dist/esm/dt/final/TUUID.js +1 -0
  36. package/dist/esm/dt/final/TYesNo.js +1 -1
  37. package/dist/esm/i18n/WordingManager.d.ts +16 -0
  38. package/dist/esm/i18n/types.d.ts +5 -0
  39. package/dist/esm/icon/Icon.d.ts +7 -0
  40. package/dist/esm/index.d.ts +3 -0
  41. package/dist/esm/index.js +4 -0
  42. package/dist/esm/product/manifest.d.ts +15 -0
  43. package/dist/esm/products/Helper/index.js +3 -0
  44. package/dist/esm/products/Helper/manifest.d.ts +6 -1
  45. package/dist/esm/std/BufferManager.d.ts +18 -0
  46. package/dist/esm/std/ClockManager.d.ts +5 -0
  47. package/dist/esm/std/EnvironmentManager.d.ts +10 -0
  48. package/dist/esm/std/HTTPAPICaller.d.ts +6 -0
  49. package/dist/esm/std/I18nManager.d.ts +26 -0
  50. package/dist/esm/std/JWTManager.d.ts +26 -0
  51. package/dist/esm/std/JobManager.d.ts +6 -0
  52. package/dist/esm/std/LLMManager.d.ts +25 -0
  53. package/dist/esm/std/LLMManager.js +1 -0
  54. package/dist/esm/std/PromptManager.d.ts +8 -0
  55. package/dist/esm/std/SettingsManager.d.ts +19 -0
  56. package/dist/esm/std/SettingsManager.js +9 -0
  57. package/dist/esm/std/impl/ConsoleLogger.js +7 -1
  58. package/dist/esm/std/impl/FakeEmailManager.js +1 -0
  59. package/dist/esm/std/impl/FakeJobManager.js +1 -0
  60. package/dist/esm/std/impl/FetchHTTPAPICallExecutor.d.ts +9 -0
  61. package/dist/esm/std/impl/FetchHTTPAPICallExecutor.js +11 -0
  62. package/dist/esm/std/impl/MistralAILLMManager.d.ts +17 -0
  63. package/dist/esm/std/impl/MistralAILLMManager.js +56 -0
  64. package/dist/esm/std/impl/NodeCryptoManager.js +6 -1
  65. package/dist/esm/std/impl/NodeDeterministicCryptoManager.d.ts +14 -0
  66. package/dist/esm/std/impl/NodeDeterministicCryptoManager.js +17 -3
  67. package/dist/esm/std/impl/NodeFSManager.js +10 -0
  68. package/dist/esm/std/impl/NodeHTTPAPICallExecutorAgentBuilder.js +2 -0
  69. package/dist/esm/std/impl/NodePromptManager.js +3 -0
  70. package/dist/esm/std/impl/OllamaLLMManager.d.ts +20 -0
  71. package/dist/esm/std/impl/OllamaLLMManager.js +56 -0
  72. package/dist/esm/std/impl/OpenAILLMManager.d.ts +17 -0
  73. package/dist/esm/std/impl/OpenAILLMManager.js +51 -0
  74. package/dist/esm/std/impl/SimpleHTTPAPICaller.js +14 -0
  75. package/dist/esm/std/impl/SimpleMapI18nManager.js +4 -2
  76. package/dist/esm/std/impl/StdDateClockManager.js +3 -0
  77. package/dist/esm/std/impl/UCDataStoreExternalResourceManager.js +3 -0
  78. package/dist/esm/std/impl/WebCryptoManager.js +9 -0
  79. package/dist/esm/std/index.d.ts +1 -0
  80. package/dist/esm/std/index.js +1 -0
  81. package/dist/esm/target/lib/cli/renderer.js +3 -0
  82. package/dist/esm/target/lib/client/consts.d.ts +3 -0
  83. package/dist/esm/target/lib/client/consts.js +3 -0
  84. package/dist/esm/target/lib/mcp-server/MCPServerBooter.js +1 -0
  85. package/dist/esm/target/lib/react/UCContainer.js +1 -0
  86. package/dist/esm/target/lib/react/UCPanel.js +4 -0
  87. package/dist/esm/target/lib/react/useUC.d.ts +8 -0
  88. package/dist/esm/target/lib/react/useUC.js +22 -0
  89. package/dist/esm/target/lib/react/useUCOR.d.ts +15 -0
  90. package/dist/esm/target/lib/react/useUCOR.js +45 -0
  91. package/dist/esm/target/lib/rn/input.d.ts +7 -0
  92. package/dist/esm/target/lib/rn/input.js +2 -0
  93. package/dist/esm/target/lib/server/AuthenticationChecker.js +2 -1
  94. package/dist/esm/target/lib/server/BasicAuthenticationChecker.js +1 -0
  95. package/dist/esm/target/lib/server/CSPDirectivesBuilder.js +13 -0
  96. package/dist/esm/target/lib/server/CustomerFacingErrorBuilder.js +3 -0
  97. package/dist/esm/target/lib/server/PrivateApiKeyAuthenticationChecker.js +1 -0
  98. package/dist/esm/target/lib/server/PublicApiKeyChecker.js +1 -1
  99. package/dist/esm/target/lib/server/RequestChecker.js +5 -4
  100. package/dist/esm/target/lib/server/RequestHandler.d.ts +5 -0
  101. package/dist/esm/target/lib/server/RequestLogger.js +5 -0
  102. package/dist/esm/target/lib/server/ServerManager.d.ts +19 -0
  103. package/dist/esm/target/lib/server/consts.d.ts +3 -0
  104. package/dist/esm/target/lib/server/consts.js +3 -0
  105. package/dist/esm/target/lib/web/input.d.ts +21 -0
  106. package/dist/esm/target/lib/web/input.js +4 -0
  107. package/dist/esm/target/node-core-cli/NodeCoreCLIManager.js +2 -2
  108. package/dist/esm/target/node-express-server/NodeExpressServerManager.js +5 -0
  109. package/dist/esm/target/node-express-server/lib/AuthCookieCreator.js +1 -1
  110. package/dist/esm/target/node-express-server/middlewares/AuthenticationCheckerMiddlewareBuilder.js +1 -0
  111. package/dist/esm/target/node-express-server/middlewares/PublicApiKeyCheckerMiddlewareBuilder.js +1 -0
  112. package/dist/esm/target/node-express-server/middlewares/RequestCheckerMiddlewareBuilder.js +1 -0
  113. package/dist/esm/target/node-express-server/middlewares/RequestHandlerMiddlewareBuilder.js +8 -0
  114. package/dist/esm/target/node-express-server/middlewares/RequestLoggerMiddlewareBuilder.js +1 -0
  115. package/dist/esm/target/node-mcp-server/NodeLocalStdioMCPServerManager.d.ts +10 -0
  116. package/dist/esm/target/node-mcp-server/NodeLocalStdioMCPServerManager.js +14 -0
  117. package/dist/esm/target/react-native-pure/UCFormFieldControl.js +1 -0
  118. package/dist/esm/testing/AppTester.d.ts +4 -0
  119. package/dist/esm/testing/AppTester.js +16 -0
  120. package/dist/esm/testing/AppTesterConfigurator.d.ts +68 -0
  121. package/dist/esm/testing/UCDataStoreTester.d.ts +9 -0
  122. package/dist/esm/testing/UCDataStoreTester.js +13 -0
  123. package/dist/esm/testing/impl/SimpleAppDocsEmitter.js +22 -2
  124. package/dist/esm/testing/impl/SimpleAppTesterConfigurator.js +1 -0
  125. package/dist/esm/testing/impl/SimpleHTMLAppTestReportEmitter.js +9 -3
  126. package/dist/esm/testing/impl/TypeScriptLibUCDefASTParser.js +12 -4
  127. package/dist/esm/testing/impl/VitestAppTestSuiteEmitter.js +6 -0
  128. package/dist/esm/testing/opts.d.ts +38 -0
  129. package/dist/esm/testing/opts.js +1 -1
  130. package/dist/esm/testing/uc-input.js +2 -0
  131. package/dist/esm/testing/workers/AppTesterCtxInitializer.js +7 -0
  132. package/dist/esm/testing/workers/UCExecutor.js +1 -0
  133. package/dist/esm/testing/workers/checkers/AppIndexChecker.js +1 -0
  134. package/dist/esm/testing/workers/checkers/UCDefSourcesChecker.js +4 -0
  135. package/dist/esm/uc/UC.js +19 -1
  136. package/dist/esm/uc/UCInputField.d.ts +28 -0
  137. package/dist/esm/uc/UCInputField.js +42 -0
  138. package/dist/esm/uc/data.d.ts +3 -0
  139. package/dist/esm/uc/def.d.ts +7 -0
  140. package/dist/esm/uc/exec.d.ts +39 -0
  141. package/dist/esm/uc/exec.js +29 -0
  142. package/dist/esm/uc/ext.d.ts +30 -1
  143. package/dist/esm/uc/helpers/UCOutputBuilder.js +5 -0
  144. package/dist/esm/uc/helpers/UCOutputReader.js +3 -1
  145. package/dist/esm/uc/impl/HTTPUCTransporter.js +4 -0
  146. package/dist/esm/uc/impl/InMemoryUCDataStore.js +7 -0
  147. package/dist/esm/uc/impl/KnexUCDataStore.d.ts +4 -0
  148. package/dist/esm/uc/impl/KnexUCDataStore.js +14 -0
  149. package/dist/esm/uc/impl/SimpleUCManager.js +6 -0
  150. package/dist/esm/uc/input-field.d.ts +60 -0
  151. package/dist/esm/uc/input-field.js +33 -0
  152. package/dist/esm/uc/input.d.ts +24 -0
  153. package/dist/esm/uc/lifecycle/client/IdleClientMain.js +1 -0
  154. package/dist/esm/uc/lifecycle/server/IdleServerMain.js +2 -0
  155. package/dist/esm/uc/manager.d.ts +11 -0
  156. package/dist/esm/uc/metadata.d.ts +10 -0
  157. package/dist/esm/uc/opi-layout.d.ts +3 -0
  158. package/dist/esm/uc/opi.d.ts +8 -0
  159. package/dist/esm/uc/output-field.d.ts +9 -0
  160. package/dist/esm/uc/output-part.d.ts +22 -0
  161. package/dist/esm/uc/output.d.ts +3 -0
  162. package/dist/esm/uc/policies/RoleRegularUCPolicy.js +1 -0
  163. package/dist/esm/uc/policies/funcs.js +1 -0
  164. package/dist/esm/uc/policy.d.ts +22 -0
  165. package/dist/esm/uc/sec.d.ts +9 -0
  166. package/dist/esm/uc/server.d.ts +10 -0
  167. package/dist/esm/uc/settings.d.ts +25 -0
  168. package/dist/esm/uc/side-effect.d.ts +16 -0
  169. package/dist/esm/uc/side-effect.js +16 -0
  170. package/dist/esm/uc/utils/rInput.d.ts +12 -0
  171. package/dist/esm/uc/utils/rInput.js +2 -0
  172. package/dist/esm/uc/utils/rVal.d.ts +25 -0
  173. package/dist/esm/uc/utils/rVal.js +27 -0
  174. package/dist/esm/uc/utils/recIs.d.ts +9 -0
  175. package/dist/esm/uc/utils/recIs.js +12 -1
  176. package/dist/esm/uc/utils/stripUCDLifecycleServer.d.ts +13 -0
  177. package/dist/esm/uc/utils/stripUCDLifecycleServer.js +17 -0
  178. package/dist/esm/uc/utils/ucifcoIsForArray.d.ts +6 -0
  179. package/dist/esm/uc/utils/ucifcoIsForArray.js +6 -0
  180. package/dist/esm/uc/workers/SimpleAggregateFinder.d.ts +12 -0
  181. package/dist/esm/uc/workers/SimpleAggregateFinder.js +12 -0
  182. package/dist/esm/uc/workers/UCBuilder.d.ts +7 -0
  183. package/dist/esm/uc/workers/UCBuilder.js +7 -0
  184. package/dist/esm/uc/workers/UCExecChecker.js +2 -0
  185. package/dist/esm/uc/workers/UCInputFilesProcessor.js +10 -4
  186. package/dist/esm/uc/workers/UCOutputFilesProcessor.js +6 -2
  187. package/dist/esm/utils/async/sleep.d.ts +10 -0
  188. package/dist/esm/utils/async/sleep.js +10 -0
  189. package/dist/esm/utils/http/appendData.js +5 -1
  190. package/dist/esm/utils/ioc/ContainerPrinter.js +2 -0
  191. package/dist/esm/utils/ioc/bindCommon.js +4 -0
  192. package/dist/esm/utils/ioc/bindNodeCLI.js +2 -0
  193. package/dist/esm/utils/ioc/bindNodeCore.js +1 -0
  194. package/dist/esm/utils/ioc/bindProduct.js +2 -0
  195. package/dist/esm/utils/ioc/bindRN.js +1 -0
  196. package/dist/esm/utils/ioc/bindServer.js +1 -0
  197. package/dist/esm/utils/ioc/bindWeb.js +2 -0
  198. package/dist/esm/utils/ioc/container.js +6 -0
  199. package/dist/esm/utils/numbers/units.js +3 -0
  200. package/dist/esm/utils/types/funcs.d.ts +35 -0
  201. package/dist/esm/utils/types/funcs.js +35 -0
  202. package/dist/esm/utils/types/utility-types.d.ts +17 -0
  203. package/dist/esm/utils/types/utility-types.js +1 -0
  204. package/package.json +9 -9
@@ -5,12 +5,41 @@ import type { UCMountingPoint } from './utils/ucMountingPoint.js';
5
5
  export type UCHTTPMountingPoint = `/${URLPath}`;
6
6
  export interface UCExt<OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCOPIBase | undefined = undefined> {
7
7
  cmd?: {
8
+ /**
9
+ * The command on which the use case is mounted at
10
+ *
11
+ * By default, it's mounted at `${fqucn}`.
12
+ */
8
13
  mountAt?: UCMountingPoint;
9
14
  };
10
15
  http?: {
16
+ /**
17
+ * The verb on which the use case is mounted at
18
+ *
19
+ * By default, it's computed from the {@link UCMetadata.action} with some specific heuristics.
20
+ *
21
+ * For instance, you can set `POST` even for a `List` use case, which usually defaults to `GET`.
22
+ * This can be useful in case you want the input to "travel" through the body of the request, and not the query params.
23
+ */
11
24
  method?: HTTPMethod;
25
+ /**
26
+ * The path on which the use case should mounted at
27
+ *
28
+ * By default, it's mounted at `/api/v1/${fqucn}`.
29
+ */
12
30
  mountAt?: UCHTTPMountingPoint;
31
+ /**
32
+ * The path on which the use case should also mounted at
33
+ *
34
+ * This is typically used when the mounting point is changed and you want to maintain a "legacy" endpoint for clients having
35
+ * a different release cycle than the server (e.g. a mobile app), who are still calling the old endpoint.
36
+ */
13
37
  mountAlsoAt?: UCHTTPMountingPoint[];
14
- transform?: <T extends object>(output: UCOutput<OPI0, OPI1>) => T;
38
+ /**
39
+ * Transform the output received to fit with some specific cases where the endpoint is expected to respect a certain contract
40
+ * @param output
41
+ * @returns
42
+ */
43
+ transform?: (output: UCOutput<OPI0, OPI1>) => object;
15
44
  };
16
45
  }
@@ -19,13 +19,17 @@ export class UCOutputBuilder {
19
19
  }
20
20
  addAll1(items) {
21
21
  this.init1IfNecessary();
22
+ // biome-ignore lint/style/noNonNullAssertion: set in the call above
22
23
  this.output.parts._1.items.push(...items);
24
+ // biome-ignore lint/style/noNonNullAssertion: set in the call above
23
25
  this.output.parts._1.total += items.length;
24
26
  return this;
25
27
  }
26
28
  add1(item) {
27
29
  this.init1IfNecessary();
30
+ // biome-ignore lint/style/noNonNullAssertion: set in the call above
28
31
  this.output.parts._1.items.push(item);
32
+ // biome-ignore lint/style/noNonNullAssertion: set in the call above
29
33
  this.output.parts._1.total += 1;
30
34
  return this;
31
35
  }
@@ -34,6 +38,7 @@ export class UCOutputBuilder {
34
38
  }
35
39
  count1() {
36
40
  this.init1IfNecessary();
41
+ // biome-ignore lint/style/noNonNullAssertion: set in the call above
37
42
  return this.output.parts._1.total;
38
43
  }
39
44
  has(predicate) {
@@ -75,7 +75,9 @@ export class UCOutputReader {
75
75
  total = part.total;
76
76
  }
77
77
  return {
78
- fields: allFieldsKeys.map((fieldKey) => new UCOutputField(fieldKey, allFields[fieldKey])),
78
+ fields: allFieldsKeys.map((fieldKey) =>
79
+ // biome-ignore lint/suspicious/noExplicitAny: can be anything
80
+ new UCOutputField(fieldKey, allFields[fieldKey])),
79
81
  idx,
80
82
  items,
81
83
  key,
@@ -40,6 +40,7 @@ let HTTPUCTransporter = class HTTPUCTransporter {
40
40
  const publicApiKeyCheckType = sec?.publicApiKeyCheckType ?? DEFAULT_UC_SEC_PAKCT;
41
41
  switch (publicApiKeyCheckType) {
42
42
  case 'off':
43
+ // Nothing to do
43
44
  break;
44
45
  case 'on': {
45
46
  const publicApiKey = await this.serverClientManager.publicApiKey();
@@ -102,6 +103,9 @@ let HTTPUCTransporter = class HTTPUCTransporter {
102
103
  },
103
104
  urlBuilder: async () => `${baseURL}${path}`,
104
105
  });
106
+ // In case of 204, we get an empty object.
107
+ // Even though it makes deserializing easier, this is not considered a valid output.
108
+ // So we handle the case here.
105
109
  if (!res || Object.keys(res).length === 0) {
106
110
  return;
107
111
  }
@@ -19,6 +19,7 @@ function predicate(key, filter) {
19
19
  return (r) => r[key] === filter;
20
20
  }
21
21
  let InMemoryUCDataStore = class InMemoryUCDataStore {
22
+ // biome-ignore lint/suspicious/noExplicitAny: can be anything
22
23
  entries;
23
24
  tx;
24
25
  constructor() {
@@ -46,10 +47,13 @@ let InMemoryUCDataStore = class InMemoryUCDataStore {
46
47
  };
47
48
  }
48
49
  async install() {
50
+ // Nothing to do
49
51
  }
50
52
  async read(opts) {
51
53
  let items = this
52
54
  .entries;
55
+ // Filter
56
+ // Of course it handles only simple cases (eq X, is null, in) with the AND operator
53
57
  if (opts?.filters) {
54
58
  const { aggregateId, appName, name, organizationId, userId } = opts.filters;
55
59
  if (aggregateId !== undefined) {
@@ -68,6 +72,8 @@ let InMemoryUCDataStore = class InMemoryUCDataStore {
68
72
  items = items.filter(predicate('userId', userId));
69
73
  }
70
74
  }
75
+ // Sort
76
+ // => No need, we are processing them by order of insertion in the Map
71
77
  return {
72
78
  records: items,
73
79
  };
@@ -79,6 +85,7 @@ let InMemoryUCDataStore = class InMemoryUCDataStore {
79
85
  return [];
80
86
  }
81
87
  async testKey() {
88
+ // Nothing to do
82
89
  }
83
90
  async write(record) {
84
91
  this.writeBulk([record]);
@@ -5,6 +5,10 @@ import type { UCDataStore, UCDataStoreReadOpts, UCDataStoreReadProjectionOpts, U
5
5
  import type { UCData } from '../data.js';
6
6
  import type { UCInput } from '../input.js';
7
7
  import type { UCSettings } from '../settings.js';
8
+ /**
9
+ * @see https://knexjs.org/guide/#configuration-options
10
+ * @see https://knexjs.org/guide/#pool
11
+ */
8
12
  export interface KnexUCDataStoreSettings extends Settings {
9
13
  knex_uc_data_store_conn_string: `postgresql://${string}`;
10
14
  knex_uc_data_store_file_path: FilePath | ':memory:';
@@ -58,6 +58,7 @@ let KnexUCDataStore = class KnexUCDataStore {
58
58
  }
59
59
  async read(opts) {
60
60
  const query = this.client(this.s().uc_data_store_ucs_dataset_name);
61
+ // Filter
61
62
  if (opts?.filters) {
62
63
  const { aggregateId, appName, idWithinInput, name, organizationId, userId, } = opts.filters;
63
64
  this.filter(query, aggregateId, 'aggregateId');
@@ -67,10 +68,17 @@ let KnexUCDataStore = class KnexUCDataStore {
67
68
  this.filter(query, userId, 'userId');
68
69
  if (idWithinInput !== undefined) {
69
70
  for (const [k, v] of Object.entries(idWithinInput)) {
71
+ // Not comfortable with the key here but it seems to escape correctly :
72
+ // select * from "d2efe54a-ce85-437c-958e-440f54c0743d"
73
+ // where jsonb_path_query_first("input", ?) #>> '{}' = ?
74
+ // and "name" = ?
75
+ // and "organization_id" = ?
76
+ // order by "created_at" asc
70
77
  query.whereJsonPath('input', `$.${k}`, '=', v);
71
78
  }
72
79
  }
73
80
  }
81
+ // Sort
74
82
  query.orderBy('created_at', 'asc');
75
83
  const records = (await query).map((r) => this.mapRowToRecord(r));
76
84
  return {
@@ -79,11 +87,13 @@ let KnexUCDataStore = class KnexUCDataStore {
79
87
  }
80
88
  async readProjection(name, opts) {
81
89
  const query = this.client(name);
90
+ // Sort
82
91
  if (opts?.orderBy) {
83
92
  for (const [k, v] of Object.entries(opts.orderBy)) {
84
93
  query.orderBy(k, v);
85
94
  }
86
95
  }
96
+ // Paginate
87
97
  if (opts?.limit) {
88
98
  query.limit(opts.limit);
89
99
  }
@@ -201,9 +211,13 @@ let KnexUCDataStore = class KnexUCDataStore {
201
211
  };
202
212
  }
203
213
  parseJSONColIfNecessary(value) {
214
+ // In some DBs, the json type is returned as string (e.g. sqlite3)
204
215
  return typeof value === 'string' ? JSON.parse(value) : value;
205
216
  }
217
+ //#region migrations
206
218
  async migration001CreateMainTable() {
219
+ // Using hasTable and then createTable because createTableIfNotExists has been deprecated
220
+ // See https://github.com/knex/knex/issues/1303#issuecomment-594489136
207
221
  const exists = await this.client.schema.hasTable(this.s().uc_data_store_ucs_dataset_name);
208
222
  if (exists) {
209
223
  return;
@@ -29,6 +29,7 @@ let SimpleUCManager = class SimpleUCManager {
29
29
  ucInputValidator;
30
30
  ucInitProvider;
31
31
  ucMainProvider;
32
+ // WARNING : This property makes this class "thread unsafe". Be careful how you inject it into your components.
32
33
  tx;
33
34
  constructor(ucClientConfirmManager, clockManager, cryptoManager, logger, ucDataStore, ucExecChecker, ucInputFilesProcessor, ucInputValidator, ucInitProvider, ucMainProvider) {
34
35
  this.ucClientConfirmManager = ucClientConfirmManager;
@@ -64,6 +65,7 @@ let SimpleUCManager = class SimpleUCManager {
64
65
  if (!client) {
65
66
  throw new Error(`The use case ${metadata.name} has no client lifecycle`);
66
67
  }
68
+ // Always check the right to execute, even if done when displaying the control
67
69
  const { allowed } = await this.ucExecChecker.exec({
68
70
  lifecycle: 'client',
69
71
  uc,
@@ -72,6 +74,9 @@ let SimpleUCManager = class SimpleUCManager {
72
74
  throw new ForbiddenError();
73
75
  }
74
76
  this.ucInputValidator.exec({ uc });
77
+ // Process the file client side only if it is not sent to the server
78
+ // Be careful with some edge cases where the file needs to be uploaded somewhere else.
79
+ // Note that we cannot check server !== true because in some cases, the server is not stripped (e.g. tests, node cli client, etc.).
75
80
  if (server === undefined) {
76
81
  await this.ucInputFilesProcessor.exec({ uc });
77
82
  }
@@ -85,6 +90,7 @@ let SimpleUCManager = class SimpleUCManager {
85
90
  if (typeof server !== 'object') {
86
91
  throw new Error(`The use case ${metadata.name} has no server lifecycle`);
87
92
  }
93
+ // Always check the right to execute, even if done when displaying the control
88
94
  const { allowed } = await this.ucExecChecker.exec({
89
95
  lifecycle: 'server',
90
96
  uc,
@@ -2,30 +2,90 @@ import type { DataType, TBase, UIntQuantity } from '../dt/index.js';
2
2
  import type { UCFieldKey } from './def.js';
3
3
  import type { Value } from './value.js';
4
4
  export declare enum UCInputFieldChangeOperator {
5
+ /**
6
+ * Considering the cardinality of the field (min > 1), add a new value
7
+ */
5
8
  ADD = "ADD",
9
+ /**
10
+ * Considering the cardinality of the field (min > 1), remove a value
11
+ */
6
12
  REMOVE = "REMOVE",
13
+ /**
14
+ * Reset the value of the field
15
+ */
7
16
  RESET = "RESET",
17
+ /**
18
+ * Considering the cardinality of the field (max <= 1), set the value
19
+ */
8
20
  SET = "SET"
9
21
  }
10
22
  export declare enum UCInputFieldFillingMode {
23
+ /**
24
+ * Set programmatically on behalf of the user (e.g. a foreign key id for a given object)
25
+ */
11
26
  AUTO_PRE = "AUTO_PRE",
27
+ /**
28
+ * Set manually by the user (e.g. a form field, a cli flag, etc.)
29
+ */
12
30
  MANUAL = "MANUAL"
13
31
  }
14
32
  export interface UCInputFieldDefCardinality {
15
33
  max?: UIntQuantity;
16
34
  min?: UIntQuantity;
17
35
  }
36
+ /**
37
+ * Definition of a use case input field
38
+ */
18
39
  export interface UCInputFieldDef<T extends DataType> {
40
+ /**
41
+ * A field can have 0, 1 or n values. This field defines the rules.
42
+ *
43
+ * @defaultValue { max: 1, min: 1 } => the user must absolutely provide one value only
44
+ *
45
+ * @example { max: 5, min: 0 } => the user must provide at most 5 values or none
46
+ * @example { min: 0 } => the user must provide 0 values or none
47
+ */
19
48
  cardinality?: UCInputFieldDefCardinality;
49
+ /**
50
+ * @defaultValue {@link UCInputFieldFillingMode.MANUAL}
51
+ */
20
52
  fillingMode?: UCInputFieldFillingMode;
53
+ /**
54
+ * @defaultValue false
55
+ */
21
56
  readOnly?: boolean;
57
+ /**
58
+ * Some types are automatically considered sensitive (e.g. {@link Password}).
59
+ *
60
+ * But you can force the behavior with this setting for any other type of field.
61
+ *
62
+ * @defaultValue false
63
+ */
22
64
  sensitive?: boolean;
65
+ /**
66
+ * When you persist a use case, all the input fields are automatically persisted as well.
67
+ *
68
+ * Sometimes this is not wanted. Setting the field as transient will exclude it from being persisted.
69
+ *
70
+ * @defaultValue false
71
+ */
23
72
  transient?: boolean;
24
73
  type: TBase<T>;
25
74
  }
26
75
  export type UCInputFieldValue<T extends DataType> = Value<T>;
27
76
  export declare function ucifExamples<T extends DataType>(def: UCInputFieldDef<T>): T[] | undefined;
28
77
  export declare function ucifHint<T extends DataType>(def: UCInputFieldDef<T>): string | undefined;
78
+ /**
79
+ * The unique id associated to this field
80
+ *
81
+ * By default it would return `inputfield-myField`.
82
+ * If you have the same field multiple times in one context (e.g. a web page), pass a unique element to the `prefix`.
83
+ * For example `uc1-inputfield-myField` and `uc2-inputfield-myField`
84
+ *
85
+ * @param prefix
86
+ * @param separator
87
+ * @returns
88
+ */
29
89
  export declare function ucifId(key: UCFieldKey, prefix?: string, separator?: string): string;
30
90
  export declare function ucifIsMandatory<T extends DataType>(def: UCInputFieldDef<T>): boolean;
31
91
  export declare function ucifRepeatability<T extends DataType>(def: UCInputFieldDef<T>): [boolean, UIntQuantity];
@@ -1,21 +1,41 @@
1
1
  export var UCInputFieldChangeOperator;
2
2
  (function (UCInputFieldChangeOperator) {
3
+ /**
4
+ * Considering the cardinality of the field (min > 1), add a new value
5
+ */
3
6
  UCInputFieldChangeOperator["ADD"] = "ADD";
7
+ /**
8
+ * Considering the cardinality of the field (min > 1), remove a value
9
+ */
4
10
  UCInputFieldChangeOperator["REMOVE"] = "REMOVE";
11
+ /**
12
+ * Reset the value of the field
13
+ */
5
14
  UCInputFieldChangeOperator["RESET"] = "RESET";
15
+ /**
16
+ * Considering the cardinality of the field (max <= 1), set the value
17
+ */
6
18
  UCInputFieldChangeOperator["SET"] = "SET";
7
19
  })(UCInputFieldChangeOperator || (UCInputFieldChangeOperator = {}));
8
20
  export var UCInputFieldFillingMode;
9
21
  (function (UCInputFieldFillingMode) {
22
+ /**
23
+ * Set programmatically on behalf of the user (e.g. a foreign key id for a given object)
24
+ */
10
25
  UCInputFieldFillingMode["AUTO_PRE"] = "AUTO_PRE";
26
+ /**
27
+ * Set manually by the user (e.g. a form field, a cli flag, etc.)
28
+ */
11
29
  UCInputFieldFillingMode["MANUAL"] = "MANUAL";
12
30
  })(UCInputFieldFillingMode || (UCInputFieldFillingMode = {}));
13
31
  export function ucifExamples(def) {
14
32
  const { type } = def;
15
33
  const examples = type.getExamples();
34
+ // Leaving the value `undefined` means you want the default value
16
35
  if (examples === undefined) {
17
36
  return [type.example()];
18
37
  }
38
+ // Setting the examples to `[]` means you don't want them
19
39
  if (examples.length === 0) {
20
40
  return undefined;
21
41
  }
@@ -37,6 +57,17 @@ export function ucifHint(def) {
37
57
  }
38
58
  return examples.join(', ');
39
59
  }
60
+ /**
61
+ * The unique id associated to this field
62
+ *
63
+ * By default it would return `inputfield-myField`.
64
+ * If you have the same field multiple times in one context (e.g. a web page), pass a unique element to the `prefix`.
65
+ * For example `uc1-inputfield-myField` and `uc2-inputfield-myField`
66
+ *
67
+ * @param prefix
68
+ * @param separator
69
+ * @returns
70
+ */
40
71
  export function ucifId(key, prefix = 'inputfield', separator = '-') {
41
72
  return `${prefix}${separator}${key}`;
42
73
  }
@@ -60,12 +91,14 @@ export function ucifIsSensitive(def) {
60
91
  }
61
92
  export function ucifMustBeFilledManually(def, opts) {
62
93
  const { fillingMode } = def;
94
+ // If there is no mode, we consider it as UCInputFieldFillingMode.MANUAL
63
95
  if (!fillingMode) {
64
96
  return true;
65
97
  }
66
98
  const fillingModes = [
67
99
  UCInputFieldFillingMode.MANUAL,
68
100
  ];
101
+ // If there is no context, it means that the field cannot be set via UCInputFieldFillingMode.AUTO_PRE (e.g. in CLI target)
69
102
  if (opts?.noContext) {
70
103
  fillingModes.push(UCInputFieldFillingMode.AUTO_PRE);
71
104
  }
@@ -1,11 +1,35 @@
1
1
  import type { StringKeys } from '../utils/index.js';
2
2
  import type { UCInputFieldDef } from './input-field.js';
3
+ /**
4
+ * Base interface all the use case input interfaces must extend
5
+ */
3
6
  export interface UCInput {
4
7
  }
8
+ /**
9
+ * Definition of a use case input
10
+ */
5
11
  export interface UCInputDef<I extends UCInput> {
12
+ /**
13
+ * This is useful when you want to render a given field only after its dependent fields have been set.
14
+ */
6
15
  dependencies?: Partial<Record<StringKeys<I>, StringKeys<I>[]>>;
16
+ /**
17
+ * It must follow strictly the shape of the corresponding {@link UCInput} with fields sorted alphabetically.
18
+ */
7
19
  fields: Record<StringKeys<I>, UCInputFieldDef<any>>;
20
+ /**
21
+ * By default, the fields are displayed in the same order as in {@link fields} (i.e. alphabetically).
22
+ *
23
+ * You can customize this by specifying the desired order here.
24
+ *
25
+ * For example, when rendering a form, this is used.
26
+ */
8
27
  order?: StringKeys<I>[];
28
+ /**
29
+ * Cross-field validation rules
30
+ *
31
+ * For example, if you want the user to provide `a` OR `b` but not both.
32
+ */
9
33
  validation?: {
10
34
  or?: StringKeys<I>[];
11
35
  };
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  import { injectable } from 'inversify';
8
8
  let IdleClientMain = class IdleClientMain {
9
9
  async exec(_props) {
10
+ // NOOP
10
11
  }
11
12
  };
12
13
  IdleClientMain = __decorate([
@@ -7,6 +7,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  import { injectable } from 'inversify';
8
8
  let IdleServerMain = class IdleServerMain {
9
9
  async exec(_props) {
10
+ // Right now it's the same as IdleClientMain, but it's better to have 2 impls in case they need to diverge
11
+ // NOOP
10
12
  }
11
13
  };
12
14
  IdleServerMain = __decorate([
@@ -8,8 +8,19 @@ import type { UCInput } from './input.js';
8
8
  import type { UCOPIBase } from './opi.js';
9
9
  import type { UCOutputOrNothing } from './output.js';
10
10
  export interface UCManagerPersistOpts {
11
+ /**
12
+ * In case the use case relates to an existing aggregate, pass its `aggregateId` in this field.
13
+ * This is typically used in a `Delete` use case in order to link it to the `Create` one executed in the past.
14
+ */
11
15
  aggregateId?: UUID;
16
+ /**
17
+ * @defaultValue {@link UCExecMode.USER}
18
+ */
12
19
  executionMode?: UCExecMode;
20
+ /**
21
+ * In case the use case is performed by an anonymous user or a user outside the organization, pass the actual `organizationId` in this field.
22
+ * This is typically used in a use case where someone anonymous posts something for a specific organization in a multi-tenant architecture.
23
+ */
13
24
  organizationId?: UUID;
14
25
  }
15
26
  export interface UCManager {
@@ -1,8 +1,18 @@
1
1
  import type { AppName } from '../app/index.js';
2
2
  import type { IconCode } from '../icon/index.js';
3
+ /**
4
+ * The type of action the use case performs
5
+ *
6
+ * This impacts the HTTP verb used in the transport layer for example.
7
+ */
3
8
  export type UCAction = 'Create' | 'Delete' | 'List' | 'Search' | 'Update' | 'View';
4
9
  export type UCName = Capitalize<string>;
5
10
  export type FQUCNameSeparator = '_';
11
+ /**
12
+ * Fully qualified use case name
13
+ *
14
+ * It's made with the {@link AppName} and the {@link UCName} linked by {@link FQUCNameSeparator}.
15
+ */
6
16
  export type FQUCName = `${AppName}${FQUCNameSeparator}${UCName}`;
7
17
  export interface UCMetadata {
8
18
  action: UCAction;
@@ -7,6 +7,9 @@ export interface UCOPILayoutContext {
7
7
  availableWidthInPx: UIntQuantity;
8
8
  target: 'cli' | 'mobile' | 'web';
9
9
  }
10
+ /**
11
+ * @alpha This feature is not fully ready yet.
12
+ */
10
13
  export type UCOPILayout<OPI extends UCOPIBase, LT extends UCOPILayoutType = UCOPILayoutType, LI extends UCOPILayoutInput = UCOPILayoutInput> = {
11
14
  get: (item: OPI, context: UCOPILayoutContext) => LI;
12
15
  type: LT;
@@ -1,6 +1,14 @@
1
1
  import type { DataType, UUID } from '../dt/index.js';
2
2
  import type { Value } from './value.js';
3
+ /**
4
+ * Base interface all the use case OPI interfaces must extend
5
+ */
3
6
  export interface UCOPIBase {
4
7
  id: UUID;
5
8
  }
9
+ /**
10
+ * A value returned as part of an OPI
11
+ *
12
+ * Unlike {@link UCInputFieldValue}, it cannot be `undefined` because it is not serializable in JSON.
13
+ */
6
14
  export type UCOPIValue<T extends DataType> = Exclude<Value<T>, undefined>;
@@ -1,8 +1,17 @@
1
1
  import type { DataType, TBase } from '../dt/index.js';
2
2
  import type { StringKeys } from '../utils/index.js';
3
3
  import type { UCOPIBase } from './opi.js';
4
+ /**
5
+ * Definition of a use case output field
6
+ */
4
7
  export interface UCOutputFieldDef<OPI extends UCOPIBase, T extends DataType> {
8
+ /**
9
+ * Indicates another field of the output this field can potentially link to (in the `<a></a>` way).
10
+ */
5
11
  linksTo?: StringKeys<OPI>;
12
+ /**
13
+ * The aggregation method to use to present the summary of this field across all the corresponding items.
14
+ */
6
15
  totalType?: 'sum';
7
16
  type: TBase<T>;
8
17
  }
@@ -6,14 +6,36 @@ import type { UCOPILayout } from './opi-layout.js';
6
6
  import type { UCOPIBase } from './opi.js';
7
7
  import type { UCOutputFieldDef } from './output-field.js';
8
8
  export interface UCOutputPart<OPI extends UCOPIBase> {
9
+ /**
10
+ * The items making the part
11
+ */
9
12
  items: OPI[];
13
+ /**
14
+ * Pagination to pass to the next call of the use case.
15
+ */
10
16
  pagination?: ListInput;
17
+ /**
18
+ * The total number of items regardless pagination.
19
+ */
11
20
  total: UIntQuantity;
12
21
  }
13
22
  export type UCOutputPartDefFields<OPI extends UCOPIBase> = Record<StringKeys<OPI>, UCOutputFieldDef<OPI, any>>;
23
+ /**
24
+ * Definition of a use case output part
25
+ */
14
26
  export interface UCOutputPartDef<OPI extends UCOPIBase> {
27
+ /**
28
+ * It must follow strictly the shape of the corresponding {@link UCOPIBase} with fields sorted alphabetically.
29
+ */
15
30
  fields: Omit<UCOutputPartDefFields<OPI>, 'id'>;
16
31
  layout?: UCOPILayout<OPI>;
32
+ /**
33
+ * By default, the fields are displayed in the same order as in {@link fields} (i.e. alphabetically).
34
+ *
35
+ * You can customize this by specifying the desired order here.
36
+ *
37
+ * For example, when rendering a card with the data, this is used.
38
+ */
17
39
  order?: StringKeys<OPI>[];
18
40
  related?: {
19
41
  global: UCDef<any, any, any>[];
@@ -10,6 +10,9 @@ export interface UCOutput<OPI0 extends UCOPIBase | undefined = undefined, OPI1 e
10
10
  };
11
11
  }
12
12
  export type UCOutputOrNothing<OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCOPIBase | undefined = undefined> = UCOutput<OPI0, OPI1> | void;
13
+ /**
14
+ * Definition of a use case output
15
+ */
13
16
  export interface UCOutputDef<OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCOPIBase | undefined = undefined> {
14
17
  parts?: {
15
18
  _0: UCOutputPartDef<NonNullable<OPI0>>;
@@ -12,6 +12,7 @@ let RoleRegularUCPolicy = class RoleRegularUCPolicy {
12
12
  }
13
13
  async exec({ uc, }) {
14
14
  const out = defaultUCPolicyOutput();
15
+ // Note that this excludes admin as policies have no hierarchy.
15
16
  out.allowed = uc.auth?.role === 'regular';
16
17
  return out;
17
18
  }
@@ -1,5 +1,6 @@
1
1
  export function defaultUCPolicyOutput() {
2
2
  const out = {
3
+ // The stricter, the better for security
3
4
  allowed: false,
4
5
  };
5
6
  return out;
@@ -8,6 +8,28 @@ export interface UCPolicyInput<I extends UCInput | undefined = undefined, OPI0 e
8
8
  export interface UCPolicyOutput {
9
9
  allowed: boolean;
10
10
  }
11
+ /**
12
+ * The policy defines the type(s) of user(s) who can perform the use case
13
+ *
14
+ * It corresponds more or less to RBAC (Resource Based Access Control).
15
+ *
16
+ * At this moment, for simplicity, ABAC (Attribute Based Access Control) must be done in the `main` of a use case.
17
+ * For example, if you have to check that a user must be the owner of a resource.
18
+ *
19
+ * The main reason for this choice at this moment, is to avoid "double-fetching" in the `policy` and in `main`.
20
+ * 1. Fetch the resource (e.g. by id) to check if the user is the owner => Accept
21
+ * 2. Fetch the resource again to perform actions on it
22
+ *
23
+ * A good solution for this would be to pass the resources fetched from the `policy` to `main` but that is,
24
+ * at least for the moment, out of scope.
25
+ */
11
26
  export interface UCPolicy<I extends UCInput | undefined = undefined, OPI0 extends UCOPIBase | undefined = undefined, OPI1 extends UCOPIBase | undefined = undefined> extends Worker<UCPolicyInput<I, OPI0, OPI1>, Promise<UCPolicyOutput>> {
27
+ /**
28
+ * Determines whether the policy check can be executed before checking the auth
29
+ *
30
+ * For example, for {@link NobodyUCPolicy}, we know that there is no need to check the auth,
31
+ * as the policy will always return false. The same goes for {@link EverybodyUCPolicy} but
32
+ * in the opposite way.
33
+ */
12
34
  canBeExecutedPreAuth(): Promise<boolean>;
13
35
  }
@@ -1,6 +1,15 @@
1
1
  export type UCSecAuthType = 'apiKey' | 'basic' | 'jwt';
2
2
  export type UCSecPublicApiKeyCheckType = 'off' | 'on';
3
+ /**
4
+ * Definition of the security scheme of a use case
5
+ */
3
6
  export interface UCSec {
7
+ /**
8
+ * @defaultValue {@link DEFAULT_UC_SEC_AT}
9
+ */
4
10
  authType?: UCSecAuthType;
11
+ /**
12
+ * @defaultValue {@link DEFAULT_UC_SEC_PAKCT}
13
+ */
5
14
  publicApiKeyCheckType?: UCSecPublicApiKeyCheckType;
6
15
  }