@deterministic-code/datasources 0.0.0-stage → 0.1.1

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 (508) hide show
  1. package/dist/ArrayReader.d.ts +12 -0
  2. package/dist/BatchCommandIBulkWriteStrategy.d.ts +33 -0
  3. package/dist/BuiltRepository.d.ts +16 -0
  4. package/dist/BulkCRUDRepository.d.ts +17 -0
  5. package/dist/BulkCommand.d.ts +5 -0
  6. package/dist/BulkWriteEnrichment.d.ts +4 -0
  7. package/dist/CRUDRepository.d.ts +40 -0
  8. package/dist/CRUDRepositoryBuilder.d.ts +19 -0
  9. package/dist/CRUDRepositoryOptions.d.ts +7 -0
  10. package/dist/CRUDRepositoryStatements.d.ts +17 -0
  11. package/dist/CascadeDeleteEnrichmentStrategy.d.ts +7 -0
  12. package/dist/CascadeDeleteUpdateEnrichmentStrategy.d.ts +8 -0
  13. package/dist/CommandMiddlewareHost.d.ts +22 -0
  14. package/dist/ConsoleCommandMiddleware.d.ts +14 -0
  15. package/dist/CreateEnrichmentStrategy.d.ts +14 -0
  16. package/dist/Datasource.d.ts +30 -0
  17. package/dist/DatasourceError.d.ts +3 -0
  18. package/dist/DatasourceFactory.d.ts +12 -0
  19. package/dist/DatasourceOptions.d.ts +1 -0
  20. package/dist/DatasourceUrl.d.ts +12 -0
  21. package/dist/DatasourceUrlParser.d.ts +4 -0
  22. package/dist/DefaultCreateEnrichmentStrategy.d.ts +7 -0
  23. package/dist/DefaultReadonlyEnrichmentStrategy.d.ts +19 -0
  24. package/dist/DefaultUpdateEnrichmentStrategy.d.ts +7 -0
  25. package/dist/DefaultWriteEnrichmentStrategy.d.ts +21 -0
  26. package/dist/DeleteEnrichmentStrategy.d.ts +11 -0
  27. package/dist/DriverLoader.d.ts +5 -0
  28. package/dist/Enrichment.d.ts +13 -0
  29. package/dist/EnrichmentPropertyType.d.ts +7 -0
  30. package/dist/ExpectConsoleMiddleware.d.ts +15 -0
  31. package/dist/FirstResultReader.d.ts +19 -0
  32. package/dist/HashsetOptimizedReadonlyEnrichmentStrategy.d.ts +9 -0
  33. package/dist/IBulkWriteStrategy.d.ts +12 -0
  34. package/dist/ICRUDRepository.d.ts +12 -0
  35. package/dist/ICRUDRepositoryFactory.d.ts +7 -0
  36. package/dist/ICommand.d.ts +20 -0
  37. package/dist/ICommandMiddleware.d.ts +12 -0
  38. package/dist/IConnection.d.ts +12 -0
  39. package/dist/IDatasource.d.ts +18 -0
  40. package/dist/IDatasourceFactory.d.ts +5 -0
  41. package/dist/IDriver.d.ts +19 -0
  42. package/dist/IEnriched.d.ts +6 -0
  43. package/dist/IReader.d.ts +8 -0
  44. package/dist/IReadonlyEnrichmentStrategy.d.ts +7 -0
  45. package/dist/IReadonlyRepository.d.ts +11 -0
  46. package/dist/IRepository.d.ts +4 -0
  47. package/dist/IRepositoryFactory.d.ts +7 -0
  48. package/dist/IRepositoryTree.d.ts +4 -0
  49. package/dist/IRepositoryTreeNode.d.ts +7 -0
  50. package/dist/ITransaction.d.ts +8 -0
  51. package/dist/IUnitOfWork.d.ts +5 -0
  52. package/dist/IWriteEnrichmentStrategy.d.ts +7 -0
  53. package/dist/Information.d.ts +5 -0
  54. package/dist/IsolationLevel.d.ts +7 -0
  55. package/dist/LazyWriteEnrichmentStrategy.d.ts +4 -0
  56. package/dist/NestedToObjectEnrichmentStrategy.d.ts +10 -0
  57. package/dist/Parameter.d.ts +7 -0
  58. package/dist/ParameterDirection.d.ts +7 -0
  59. package/dist/ProviderCatalog.d.ts +7 -0
  60. package/dist/ProviderNotImplementedError.d.ts +4 -0
  61. package/dist/ProviderNotInstalledError.d.ts +4 -0
  62. package/dist/ReadonlyRepository.d.ts +59 -0
  63. package/dist/ReadonlyRepositoryBuilder.d.ts +12 -0
  64. package/dist/ReadonlyRepositoryMappingOptions.d.ts +14 -0
  65. package/dist/ReadonlyRepositoryStatements.d.ts +8 -0
  66. package/dist/RepositoryAccess.d.ts +11 -0
  67. package/dist/RepositoryFactory.d.ts +23 -0
  68. package/dist/RepositoryNotRegisteredError.d.ts +4 -0
  69. package/dist/RepositoryTree.d.ts +6 -0
  70. package/dist/RepositoryTreeNode.d.ts +9 -0
  71. package/dist/Response.d.ts +7 -0
  72. package/dist/Row.d.ts +2 -0
  73. package/dist/SingleCommandIBulkWriteStrategy.d.ts +11 -0
  74. package/dist/StatementMappers.d.ts +16 -0
  75. package/dist/StoredEnrichment.d.ts +11 -0
  76. package/dist/StoredProcedureBulkWriteStrategy.d.ts +27 -0
  77. package/dist/StreamingReader.d.ts +16 -0
  78. package/dist/UnitOfWorkTransaction.d.ts +12 -0
  79. package/dist/UpdateEnrichmentStrategy.d.ts +18 -0
  80. package/dist/WriteMode.d.ts +1 -0
  81. package/dist/builders/Field.d.ts +5 -0
  82. package/dist/builders/Scalar.d.ts +1 -0
  83. package/dist/builders/index.d.ts +2 -0
  84. package/dist/collectAsync.d.ts +1 -0
  85. package/dist/converters/Cell.d.ts +2 -0
  86. package/dist/converters/ChangeCasingConverter.d.ts +7 -0
  87. package/dist/converters/Constructable.d.ts +1 -0
  88. package/dist/converters/MetaDataRowTypeConverter.d.ts +14 -0
  89. package/dist/converters/PluralizerConverter.d.ts +4 -0
  90. package/dist/converters/RegexConverter.d.ts +7 -0
  91. package/dist/converters/ReplaceConverter.d.ts +7 -0
  92. package/dist/converters/RowTypeConverter.d.ts +10 -0
  93. package/dist/converters/ToLowerCaseConverter.d.ts +4 -0
  94. package/dist/converters/ToUpperCaseConverter.d.ts +4 -0
  95. package/dist/converters/TypeConverter.d.ts +5 -0
  96. package/dist/createCRUDRepositoryOptions.d.ts +7 -0
  97. package/dist/createReadonlyRepositoryMappingOptions.d.ts +6 -0
  98. package/dist/dotEnv.d.ts +5 -0
  99. package/dist/index.d.ts +119 -0
  100. package/dist/mappers/ChangeCasingMapper.d.ts +9 -0
  101. package/dist/mappers/DictionaryMapper.d.ts +8 -0
  102. package/dist/mappers/FlatToNestedMapper.d.ts +20 -0
  103. package/dist/mappers/IFlatToNestedMapper.d.ts +6 -0
  104. package/dist/mappers/IMapper.d.ts +4 -0
  105. package/dist/mappers/IdentityMapper.d.ts +5 -0
  106. package/dist/mappers/PluralizerMapper.d.ts +6 -0
  107. package/dist/mappers/RowTypeMapper.d.ts +6 -0
  108. package/dist/mappers/TransformConverterMapper.d.ts +5 -0
  109. package/dist/mappers/TransformMapper.d.ts +6 -0
  110. package/dist/mappers/TypeMap.d.ts +8 -0
  111. package/dist/mappers/TypeMapGroup.d.ts +5 -0
  112. package/dist/mocks/index.d.ts +9 -0
  113. package/dist/mocks/mock-command.d.ts +28 -0
  114. package/dist/mocks/mock-connection.d.ts +22 -0
  115. package/dist/mocks/mock-datasource.d.ts +24 -0
  116. package/dist/mocks/mock-driver.d.ts +15 -0
  117. package/dist/mocks/mock-reader.d.ts +14 -0
  118. package/dist/mocks/mock-repository.d.ts +42 -0
  119. package/dist/mocks/mock-select-statement.d.ts +10 -0
  120. package/dist/mocks/mock-transaction.d.ts +16 -0
  121. package/dist/mocks/mock-where.d.ts +8 -0
  122. package/dist/parametersToRecord.d.ts +2 -0
  123. package/dist/readerToAsyncIterable.d.ts +2 -0
  124. package/dist/splitStatements.d.ts +1 -0
  125. package/dist/statementParameters.d.ts +1 -0
  126. package/dist/statementUsesParameter.d.ts +1 -0
  127. package/dist/statements/Aggregate.d.ts +6 -0
  128. package/dist/statements/ArrayExpression.d.ts +5 -0
  129. package/dist/statements/BulkDeleteStatement.d.ts +11 -0
  130. package/dist/statements/BulkDeleteStatementTextRenderer.d.ts +4 -0
  131. package/dist/statements/BulkInsertStatement.d.ts +12 -0
  132. package/dist/statements/BulkInsertStatementTextRenderer.d.ts +4 -0
  133. package/dist/statements/BulkUpdateStatement.d.ts +16 -0
  134. package/dist/statements/BulkUpdateStatementTextRenderer.d.ts +8 -0
  135. package/dist/statements/CheckConstraint.d.ts +7 -0
  136. package/dist/statements/Cmp.d.ts +9 -0
  137. package/dist/statements/Column.d.ts +9 -0
  138. package/dist/statements/ColumnDefault.d.ts +5 -0
  139. package/dist/statements/Condition.d.ts +3 -0
  140. package/dist/statements/ConditionRenderer.d.ts +12 -0
  141. package/dist/statements/CreateIndexStatement.d.ts +10 -0
  142. package/dist/statements/CreateIndexStatementTextRenderer.d.ts +5 -0
  143. package/dist/statements/CreateTableStatement.d.ts +11 -0
  144. package/dist/statements/CreateTableStatementTextRenderer.d.ts +18 -0
  145. package/dist/statements/DefaultStatementTextRenderer.d.ts +5 -0
  146. package/dist/statements/DeleteStatement.d.ts +9 -0
  147. package/dist/statements/DeleteStatementTextRenderer.d.ts +7 -0
  148. package/dist/statements/ExecuteStoredProcedureStatement.d.ts +9 -0
  149. package/dist/statements/ExecuteStoredProcedureStatementTextRenderer.d.ts +4 -0
  150. package/dist/statements/ExpressionEvaluator.d.ts +5 -0
  151. package/dist/statements/ExpressionRenderer.d.ts +8 -0
  152. package/dist/statements/ForeignKey.d.ts +19 -0
  153. package/dist/statements/IBulkDeleteStatement.d.ts +3 -0
  154. package/dist/statements/IBulkInsertStatement.d.ts +3 -0
  155. package/dist/statements/IBulkUpdateStatement.d.ts +3 -0
  156. package/dist/statements/ICondition.d.ts +4 -0
  157. package/dist/statements/IConditionVisitor.d.ts +6 -0
  158. package/dist/statements/ICreateIndexStatement.d.ts +3 -0
  159. package/dist/statements/ICreateTableStatement.d.ts +3 -0
  160. package/dist/statements/IDeleteStatement.d.ts +5 -0
  161. package/dist/statements/IExecuteStoredProcedureStatement.d.ts +3 -0
  162. package/dist/statements/IExpression.d.ts +2 -0
  163. package/dist/statements/IInsertStatement.d.ts +3 -0
  164. package/dist/statements/IReturnStatement.d.ts +3 -0
  165. package/dist/statements/ISelectStatement.d.ts +5 -0
  166. package/dist/statements/IStatement.d.ts +4 -0
  167. package/dist/statements/IStatementVisitor.d.ts +26 -0
  168. package/dist/statements/ITableConstraint.d.ts +5 -0
  169. package/dist/statements/IUpdateStatement.d.ts +5 -0
  170. package/dist/statements/IWhere.d.ts +6 -0
  171. package/dist/statements/Identifier.d.ts +5 -0
  172. package/dist/statements/InsertStatement.d.ts +9 -0
  173. package/dist/statements/InsertStatementTextRenderer.d.ts +4 -0
  174. package/dist/statements/Join.d.ts +14 -0
  175. package/dist/statements/Literal.d.ts +6 -0
  176. package/dist/statements/Native.d.ts +5 -0
  177. package/dist/statements/NotNull.d.ts +4 -0
  178. package/dist/statements/Operator.d.ts +107 -0
  179. package/dist/statements/OrderBy.d.ts +10 -0
  180. package/dist/statements/PrimaryKey.d.ts +7 -0
  181. package/dist/statements/ReturnStatement.d.ts +9 -0
  182. package/dist/statements/ReturnStatementTextRenderer.d.ts +4 -0
  183. package/dist/statements/SelectInsertedIdStatement.d.ts +8 -0
  184. package/dist/statements/SelectInsertedIdStatementTextRenderer.d.ts +4 -0
  185. package/dist/statements/SelectStatement.d.ts +20 -0
  186. package/dist/statements/SelectStatementOptions.d.ts +15 -0
  187. package/dist/statements/SelectStatementTextRenderer.d.ts +19 -0
  188. package/dist/statements/StatementEvaluator.d.ts +4 -0
  189. package/dist/statements/StatementParser.d.ts +4 -0
  190. package/dist/statements/StatementRenderer.d.ts +42 -0
  191. package/dist/statements/StatementTextRenderer.d.ts +14 -0
  192. package/dist/statements/Subquery.d.ts +6 -0
  193. package/dist/statements/UniqueConstraint.d.ts +7 -0
  194. package/dist/statements/UpdateStatement.d.ts +14 -0
  195. package/dist/statements/UpdateStatementTextRenderer.d.ts +14 -0
  196. package/dist/statements/Where.d.ts +16 -0
  197. package/dist/statements/WhereText.d.ts +8 -0
  198. package/dist/statements/index.d.ts +75 -0
  199. package/dist/stubs/StubCommand.d.ts +15 -0
  200. package/dist/stubs/StubConnection.d.ts +13 -0
  201. package/dist/stubs/StubDatasource.d.ts +20 -0
  202. package/dist/stubs/StubReader.d.ts +11 -0
  203. package/dist/stubs/StubRepository.d.ts +22 -0
  204. package/dist/stubs/StubTransaction.d.ts +10 -0
  205. package/dist/stubs/createStubDatasource.d.ts +2 -0
  206. package/dist/stubs/index.d.ts +7 -0
  207. package/dist/stubs/notImplemented.d.ts +1 -0
  208. package/package.json +24 -4
  209. package/src/ArrayReader.test.ts +23 -0
  210. package/src/ArrayReader.ts +32 -0
  211. package/src/BatchCommandIBulkWriteStrategy.ts +228 -0
  212. package/src/BuiltRepository.ts +17 -0
  213. package/src/BulkCRUDRepository.test.ts +183 -0
  214. package/src/BulkCRUDRepository.ts +84 -0
  215. package/src/BulkCommand.ts +6 -0
  216. package/src/BulkWriteEnrichment.ts +19 -0
  217. package/src/BulkWriteStrategy.test.ts +293 -0
  218. package/src/CRUDRepository.test.ts +420 -0
  219. package/src/CRUDRepository.ts +264 -0
  220. package/src/CRUDRepositoryBuilder.ts +53 -0
  221. package/src/CRUDRepositoryOptions.test.ts +76 -0
  222. package/src/CRUDRepositoryOptions.ts +8 -0
  223. package/src/CRUDRepositoryStatements.ts +43 -0
  224. package/src/CascadeDeleteEnrichmentStrategy.ts +51 -0
  225. package/src/CascadeDeleteUpdateEnrichmentStrategy.ts +28 -0
  226. package/src/Command.test.ts +105 -0
  227. package/src/CommandMiddlewareHost.test.ts +98 -0
  228. package/src/CommandMiddlewareHost.ts +113 -0
  229. package/src/Connection.test.ts +34 -0
  230. package/src/ConsoleCommandMiddleware.test.ts +149 -0
  231. package/src/ConsoleCommandMiddleware.ts +64 -0
  232. package/src/CreateEnrichmentStrategy.ts +82 -0
  233. package/src/Datasource.test.ts +63 -0
  234. package/src/Datasource.ts +152 -0
  235. package/src/DatasourceError.test.ts +10 -0
  236. package/src/DatasourceError.ts +6 -0
  237. package/src/DatasourceFactory.test.ts +159 -0
  238. package/src/DatasourceFactory.ts +86 -0
  239. package/src/DatasourceOptions.ts +1 -0
  240. package/src/DatasourceUrl.ts +37 -0
  241. package/src/DatasourceUrlParser.test.ts +173 -0
  242. package/src/DatasourceUrlParser.ts +188 -0
  243. package/src/DefaultCreateEnrichmentStrategy.ts +21 -0
  244. package/src/DefaultReadonlyEnrichmentStrategy.ts +164 -0
  245. package/src/DefaultUpdateEnrichmentStrategy.ts +40 -0
  246. package/src/DefaultWriteEnrichmentStrategy.ts +151 -0
  247. package/src/DeleteEnrichmentStrategy.ts +56 -0
  248. package/src/DriverLoader.ts +97 -0
  249. package/src/Enrichment.ts +15 -0
  250. package/src/EnrichmentPropertyType.ts +9 -0
  251. package/src/ExpectConsoleMiddleware.test.ts +79 -0
  252. package/src/ExpectConsoleMiddleware.ts +47 -0
  253. package/src/FirstResultReader.test.ts +43 -0
  254. package/src/FirstResultReader.ts +49 -0
  255. package/src/HashsetOptimizedReadonlyEnrichmentStrategy.ts +80 -0
  256. package/src/IBulkWriteStrategy.ts +13 -0
  257. package/src/ICRUDRepository.ts +13 -0
  258. package/src/ICRUDRepositoryFactory.ts +8 -0
  259. package/src/ICommand.ts +45 -0
  260. package/src/ICommandMiddleware.ts +20 -0
  261. package/src/IConnection.ts +13 -0
  262. package/src/IDatasource.ts +27 -0
  263. package/src/IDatasourceFactory.ts +6 -0
  264. package/src/IDriver.ts +24 -0
  265. package/src/IEnriched.ts +11 -0
  266. package/src/IReader.ts +8 -0
  267. package/src/IReadonlyEnrichmentStrategy.ts +19 -0
  268. package/src/IReadonlyRepository.ts +21 -0
  269. package/src/IRepository.ts +5 -0
  270. package/src/IRepositoryFactory.ts +8 -0
  271. package/src/IRepositoryTree.ts +5 -0
  272. package/src/IRepositoryTreeNode.ts +8 -0
  273. package/src/ITransaction.ts +9 -0
  274. package/src/IUnitOfWork.ts +5 -0
  275. package/src/IWriteEnrichmentStrategy.ts +8 -0
  276. package/src/Information.ts +5 -0
  277. package/src/IsolationLevel.test.ts +13 -0
  278. package/src/IsolationLevel.ts +8 -0
  279. package/src/LazyWriteEnrichmentStrategy.ts +5 -0
  280. package/src/NestedToObjectEnrichmentStrategy.ts +33 -0
  281. package/src/Parameter.ts +8 -0
  282. package/src/ParameterDirection.ts +9 -0
  283. package/src/ProviderCatalog.test.ts +110 -0
  284. package/src/ProviderCatalog.ts +21 -0
  285. package/src/ProviderNotImplementedError.test.ts +11 -0
  286. package/src/ProviderNotImplementedError.ts +9 -0
  287. package/src/ProviderNotInstalledError.test.ts +11 -0
  288. package/src/ProviderNotInstalledError.ts +9 -0
  289. package/src/ReadonlyRepository.test.ts +406 -0
  290. package/src/ReadonlyRepository.ts +276 -0
  291. package/src/ReadonlyRepositoryBuilder.ts +53 -0
  292. package/src/ReadonlyRepositoryMappingOptions.ts +15 -0
  293. package/src/ReadonlyRepositoryStatements.ts +13 -0
  294. package/src/RepositoryAccess.ts +14 -0
  295. package/src/RepositoryFactory.test.ts +94 -0
  296. package/src/RepositoryFactory.ts +115 -0
  297. package/src/RepositoryNotRegisteredError.ts +9 -0
  298. package/src/RepositoryTree.ts +6 -0
  299. package/src/RepositoryTreeNode.ts +11 -0
  300. package/src/Response.ts +15 -0
  301. package/src/Row.ts +3 -0
  302. package/src/SingleCommandIBulkWriteStrategy.ts +35 -0
  303. package/src/StatementMappers.test.ts +221 -0
  304. package/src/StatementMappers.ts +110 -0
  305. package/src/StoredEnrichment.ts +25 -0
  306. package/src/StoredProcedureBulkWriteStrategy.ts +164 -0
  307. package/src/StreamingReader.test.ts +126 -0
  308. package/src/StreamingReader.ts +64 -0
  309. package/src/Transaction.test.ts +37 -0
  310. package/src/UnitOfWorkTransaction.test.ts +43 -0
  311. package/src/UnitOfWorkTransaction.ts +50 -0
  312. package/src/UpdateEnrichmentStrategy.ts +97 -0
  313. package/src/UserViewCRUDRepository.test.ts +323 -0
  314. package/src/WriteMode.ts +1 -0
  315. package/src/builders/Field.ts +6 -0
  316. package/src/builders/Scalar.ts +1 -0
  317. package/src/builders/builders.test.ts +404 -0
  318. package/src/builders/index.ts +2 -0
  319. package/src/collectAsync.ts +7 -0
  320. package/src/converters/Cell.ts +10 -0
  321. package/src/converters/ChangeCasingConverter.test.ts +34 -0
  322. package/src/converters/ChangeCasingConverter.ts +59 -0
  323. package/src/converters/Constructable.ts +1 -0
  324. package/src/converters/MetaDataRowTypeConverter.ts +80 -0
  325. package/src/converters/PluralizerConverter.test.ts +43 -0
  326. package/src/converters/PluralizerConverter.ts +67 -0
  327. package/src/converters/RegexConverter.test.ts +11 -0
  328. package/src/converters/RegexConverter.ts +14 -0
  329. package/src/converters/ReplaceConverter.test.ts +9 -0
  330. package/src/converters/ReplaceConverter.ts +6 -0
  331. package/src/converters/RowTypeConverter.test.ts +79 -0
  332. package/src/converters/RowTypeConverter.ts +33 -0
  333. package/src/converters/ToLowerCaseConverter.test.ts +9 -0
  334. package/src/converters/ToLowerCaseConverter.ts +4 -0
  335. package/src/converters/ToUpperCaseConverter.test.ts +9 -0
  336. package/src/converters/ToUpperCaseConverter.ts +4 -0
  337. package/src/converters/TypeConverter.ts +6 -0
  338. package/src/createCRUDRepositoryOptions.ts +21 -0
  339. package/src/createReadonlyRepositoryMappingOptions.ts +26 -0
  340. package/src/dotEnv.test.ts +21 -0
  341. package/src/dotEnv.ts +16 -0
  342. package/src/edgeCases.test.ts +485 -0
  343. package/src/index.ts +222 -0
  344. package/src/mappers/ChangeCasingMapper.test.ts +32 -0
  345. package/src/mappers/ChangeCasingMapper.ts +15 -0
  346. package/src/mappers/DictionaryMapper.test.ts +69 -0
  347. package/src/mappers/DictionaryMapper.ts +16 -0
  348. package/src/mappers/FlatToNestedMapper.test.ts +269 -0
  349. package/src/mappers/FlatToNestedMapper.ts +127 -0
  350. package/src/mappers/IFlatToNestedMapper.ts +7 -0
  351. package/src/mappers/IMapper.ts +4 -0
  352. package/src/mappers/IdentityMapper.test.ts +12 -0
  353. package/src/mappers/IdentityMapper.ts +6 -0
  354. package/src/mappers/PluralizerMapper.test.ts +40 -0
  355. package/src/mappers/PluralizerMapper.ts +9 -0
  356. package/src/mappers/RowTypeMapper.test.ts +12 -0
  357. package/src/mappers/RowTypeMapper.ts +13 -0
  358. package/src/mappers/TransformConverterMapper.test.ts +181 -0
  359. package/src/mappers/TransformConverterMapper.ts +11 -0
  360. package/src/mappers/TransformMapper.ts +8 -0
  361. package/src/mappers/TypeMap.ts +10 -0
  362. package/src/mappers/TypeMapGroup.ts +6 -0
  363. package/src/mappers/transformMapper.test.ts +66 -0
  364. package/src/mocks/MockCommand.test.ts +75 -0
  365. package/src/mocks/MockConnection.test.ts +34 -0
  366. package/src/mocks/MockDatasource.test.ts +40 -0
  367. package/src/mocks/MockReader.test.ts +17 -0
  368. package/src/mocks/MockRepository.test.ts +32 -0
  369. package/src/mocks/MockSelectStatement.test.ts +11 -0
  370. package/src/mocks/MockTransaction.test.ts +17 -0
  371. package/src/mocks/MockWhere.test.ts +11 -0
  372. package/src/mocks/index.ts +9 -0
  373. package/src/mocks/mock-command.ts +66 -0
  374. package/src/mocks/mock-connection.ts +57 -0
  375. package/src/mocks/mock-datasource.ts +65 -0
  376. package/src/mocks/mock-driver.ts +27 -0
  377. package/src/mocks/mock-reader.ts +38 -0
  378. package/src/mocks/mock-repository.ts +91 -0
  379. package/src/mocks/mock-select-statement.ts +17 -0
  380. package/src/mocks/mock-transaction.ts +31 -0
  381. package/src/mocks/mock-where.ts +13 -0
  382. package/src/parametersToRecord.test.ts +19 -0
  383. package/src/parametersToRecord.ts +11 -0
  384. package/src/public-surface.test.ts +23 -0
  385. package/src/readWithWhereClauseAndCustomFlatToNestedObjectMappingOptions.test.ts +126 -0
  386. package/src/readerToAsyncIterable.test.ts +39 -0
  387. package/src/readerToAsyncIterable.ts +11 -0
  388. package/src/repository.test.ts +410 -0
  389. package/src/repository.type.test.ts +29 -0
  390. package/src/splitStatements.ts +99 -0
  391. package/src/statementParameters.ts +22 -0
  392. package/src/statementUsesParameter.ts +4 -0
  393. package/src/statements/Aggregate.ts +9 -0
  394. package/src/statements/ArrayExpression.ts +5 -0
  395. package/src/statements/BulkDeleteStatement.ts +18 -0
  396. package/src/statements/BulkDeleteStatementTextRenderer.ts +16 -0
  397. package/src/statements/BulkInsertStatement.ts +37 -0
  398. package/src/statements/BulkInsertStatementTextRenderer.ts +10 -0
  399. package/src/statements/BulkUpdateStatement.ts +24 -0
  400. package/src/statements/BulkUpdateStatementTextRenderer.ts +29 -0
  401. package/src/statements/CheckConstraint.ts +9 -0
  402. package/src/statements/Cmp.ts +12 -0
  403. package/src/statements/Column.ts +10 -0
  404. package/src/statements/ColumnDefault.ts +6 -0
  405. package/src/statements/Condition.ts +3 -0
  406. package/src/statements/ConditionRenderer.ts +17 -0
  407. package/src/statements/CreateIndexStatement.ts +18 -0
  408. package/src/statements/CreateIndexStatementTextRenderer.ts +11 -0
  409. package/src/statements/CreateTableStatement.ts +18 -0
  410. package/src/statements/CreateTableStatementTextRenderer.ts +92 -0
  411. package/src/statements/DefaultStatementTextRenderer.ts +9 -0
  412. package/src/statements/DeleteStatement.ts +15 -0
  413. package/src/statements/DeleteStatementTextRenderer.ts +12 -0
  414. package/src/statements/ExecuteStoredProcedureStatement.ts +15 -0
  415. package/src/statements/ExecuteStoredProcedureStatementTextRenderer.ts +15 -0
  416. package/src/statements/ExpressionEvaluator.ts +166 -0
  417. package/src/statements/ExpressionRenderer.ts +161 -0
  418. package/src/statements/ForeignKey.ts +23 -0
  419. package/src/statements/IBulkDeleteStatement.ts +3 -0
  420. package/src/statements/IBulkInsertStatement.ts +3 -0
  421. package/src/statements/IBulkUpdateStatement.ts +3 -0
  422. package/src/statements/ICondition.ts +5 -0
  423. package/src/statements/IConditionVisitor.ts +7 -0
  424. package/src/statements/ICreateIndexStatement.ts +3 -0
  425. package/src/statements/ICreateTableStatement.ts +3 -0
  426. package/src/statements/IDeleteStatement.ts +6 -0
  427. package/src/statements/IExecuteStoredProcedureStatement.ts +3 -0
  428. package/src/statements/IExpression.ts +1 -0
  429. package/src/statements/IInsertStatement.ts +3 -0
  430. package/src/statements/IReturnStatement.ts +3 -0
  431. package/src/statements/ISelectStatement.ts +6 -0
  432. package/src/statements/IStatement.ts +5 -0
  433. package/src/statements/IStatementVisitor.ts +27 -0
  434. package/src/statements/ITableConstraint.ts +6 -0
  435. package/src/statements/IUpdateStatement.ts +6 -0
  436. package/src/statements/IWhere.ts +7 -0
  437. package/src/statements/Identifier.ts +5 -0
  438. package/src/statements/InsertStatement.ts +15 -0
  439. package/src/statements/InsertStatementTextRenderer.ts +7 -0
  440. package/src/statements/Join.ts +26 -0
  441. package/src/statements/Literal.ts +6 -0
  442. package/src/statements/Native.ts +5 -0
  443. package/src/statements/NotNull.ts +3 -0
  444. package/src/statements/Operator.ts +234 -0
  445. package/src/statements/OrderBy.ts +19 -0
  446. package/src/statements/PrimaryKey.ts +9 -0
  447. package/src/statements/ReturnStatement.ts +15 -0
  448. package/src/statements/ReturnStatementTextRenderer.ts +15 -0
  449. package/src/statements/SelectInsertedIdStatement.test.ts +11 -0
  450. package/src/statements/SelectInsertedIdStatement.ts +11 -0
  451. package/src/statements/SelectInsertedIdStatementTextRenderer.ts +7 -0
  452. package/src/statements/SelectStatement.ts +34 -0
  453. package/src/statements/SelectStatementOptions.ts +16 -0
  454. package/src/statements/SelectStatementTextRenderer.ts +71 -0
  455. package/src/statements/StatementCompile.test.ts +231 -0
  456. package/src/statements/StatementEvaluator.ts +5 -0
  457. package/src/statements/StatementParser.test.ts +304 -0
  458. package/src/statements/StatementParser.ts +461 -0
  459. package/src/statements/StatementRenderer.ts +120 -0
  460. package/src/statements/StatementTextRenderer.ts +60 -0
  461. package/src/statements/Subquery.ts +6 -0
  462. package/src/statements/UniqueConstraint.ts +9 -0
  463. package/src/statements/UpdateStatement.ts +34 -0
  464. package/src/statements/UpdateStatementTextRenderer.ts +63 -0
  465. package/src/statements/Where.ts +250 -0
  466. package/src/statements/WhereText.ts +10 -0
  467. package/src/statements/index.ts +75 -0
  468. package/src/stubs/StubCommand.ts +30 -0
  469. package/src/stubs/StubConnection.ts +21 -0
  470. package/src/stubs/StubDatasource.test.ts +99 -0
  471. package/src/stubs/StubDatasource.ts +60 -0
  472. package/src/stubs/StubReader.ts +25 -0
  473. package/src/stubs/StubRepository.ts +42 -0
  474. package/src/stubs/StubTransaction.ts +17 -0
  475. package/src/stubs/createStubDatasource.ts +4 -0
  476. package/src/stubs/index.ts +7 -0
  477. package/src/stubs/notImplemented.ts +5 -0
  478. package/src/tests/contact/Address.ts +14 -0
  479. package/src/tests/contact/AddressType.ts +6 -0
  480. package/src/tests/contact/Base.ts +7 -0
  481. package/src/tests/contact/Contact.ts +12 -0
  482. package/src/tests/contact/ContactBase.ts +8 -0
  483. package/src/tests/contact/ContactChangeLog.ts +8 -0
  484. package/src/tests/contact/ContactGroup.ts +6 -0
  485. package/src/tests/contact/ContactGroupBase.ts +6 -0
  486. package/src/tests/contact/ContactGroupMember.ts +6 -0
  487. package/src/tests/contact/ContactSource.ts +6 -0
  488. package/src/tests/contact/ContactType.ts +6 -0
  489. package/src/tests/contact/ContactsService.test.ts +118 -0
  490. package/src/tests/contact/ContactsService.ts +32 -0
  491. package/src/tests/contact/ImportContactRequest.ts +4 -0
  492. package/src/tests/contact/ImportContactResult.ts +5 -0
  493. package/src/tests/contact/LegacyContact.ts +7 -0
  494. package/src/tests/contact/LegacyMigrationResult.ts +5 -0
  495. package/src/tests/contact/Phone.ts +9 -0
  496. package/src/tests/contact/PhoneType.ts +6 -0
  497. package/src/tests/contact/asContactsView.ts +63 -0
  498. package/src/tests/contact/contactColumns.ts +40 -0
  499. package/src/tests/contact/contactLookupIds.ts +32 -0
  500. package/src/tests/contact/contactLookups.ts +6 -0
  501. package/src/tests/contact/contactsParity.ts +63 -0
  502. package/src/tests/contact/contactsServiceOf.ts +38 -0
  503. package/src/tests/contact/contactsView.ts +31 -0
  504. package/src/tests/contact/expectContactsView.ts +23 -0
  505. package/src/tests/contact/extraContact.ts +61 -0
  506. package/src/tests/contact/logMapped.ts +5 -0
  507. package/src/tests/contact/seedContacts.ts +31 -0
  508. package/README.md +0 -3
@@ -0,0 +1,64 @@
1
+ import type { Cells } from "./converters/Cell.ts";
2
+ import type { IReader } from "./IReader.ts";
3
+ import { readerToAsyncIterable } from "./readerToAsyncIterable.ts";
4
+
5
+ export class StreamingReader implements IReader<Cells> {
6
+ current: Cells | undefined = undefined;
7
+ private closed = false;
8
+
9
+ constructor(
10
+ private readonly pull: () => Promise<Cells | undefined>,
11
+ private readonly advance: () => Promise<boolean>,
12
+ private readonly close: () => Promise<void> = async () => {},
13
+ ) {}
14
+
15
+ async next(): Promise<boolean> {
16
+ this.current = await this.pull();
17
+ return this.current !== undefined;
18
+ }
19
+
20
+ read(): AsyncIterable<Cells> {
21
+ return readerToAsyncIterable(this);
22
+ }
23
+
24
+ async nextResult(): Promise<boolean> {
25
+ this.current = undefined;
26
+ return this.advance();
27
+ }
28
+
29
+ async dispose(): Promise<void> {
30
+ if (this.closed) return;
31
+ this.closed = true;
32
+ await this.close();
33
+ }
34
+
35
+ [Symbol.asyncDispose] = (): Promise<void> => this.dispose();
36
+ }
37
+
38
+ export const statementReader = (
39
+ statements: readonly string[],
40
+ load: (sql: string) => AsyncIterable<Cells>,
41
+ ): StreamingReader => {
42
+ let index = 0;
43
+ let rows: AsyncIterator<Cells> | undefined;
44
+ const closeRows = async () => {
45
+ await rows?.return?.();
46
+ rows = undefined;
47
+ };
48
+ return new StreamingReader(
49
+ async () => {
50
+ if (index >= statements.length) return undefined;
51
+ const sql = statements[index];
52
+ if (!sql) return undefined;
53
+ rows ??= load(sql)[Symbol.asyncIterator]();
54
+ const step = await rows.next();
55
+ return step.done ? undefined : step.value;
56
+ },
57
+ async () => {
58
+ await closeRows();
59
+ index += 1;
60
+ return index < statements.length;
61
+ },
62
+ closeRows,
63
+ );
64
+ };
@@ -0,0 +1,37 @@
1
+ import { afterAll, afterEach, beforeEach, describe, expect, it } from "vitest";
2
+ import { Datasource, DatasourceFactory, MockDriver } from "./index.ts";
3
+
4
+ const envKeys = [
5
+ "INSTALLED_PROVIDERS",
6
+ "DATASOURCE_URL",
7
+ "PROVIDER",
8
+ ] as const;
9
+ const savedEnv = Object.fromEntries(envKeys.map((key) => [key, process.env[key]]));
10
+
11
+ beforeEach(() => {
12
+ for (const key of envKeys) delete process.env[key];
13
+ DatasourceFactory.register("sqlite", new MockDriver());
14
+ process.env.PROVIDER = "sqlite";
15
+ });
16
+
17
+ afterEach(() => DatasourceFactory.reset());
18
+
19
+ afterAll(() => {
20
+ for (const key of envKeys) {
21
+ if (savedEnv[key] === undefined) delete process.env[key];
22
+ else process.env[key] = savedEnv[key];
23
+ }
24
+ });
25
+
26
+ describe("Transaction", () => {
27
+ it("commit or abort", async () => {
28
+ const ds = Datasource.default;
29
+ const connection = await ds.createConnection();
30
+ await connection.open();
31
+ const txn = await connection.startTransaction();
32
+ const saved = true;
33
+ if (saved) await txn.commit();
34
+ else await txn.abort();
35
+ expect(txn.isolation).toBe("readCommitted");
36
+ });
37
+ });
@@ -0,0 +1,43 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { DatasourceError, MockConnection, MockTransaction, UnitOfWorkTransaction } from "./index.ts";
3
+
4
+ describe("UnitOfWorkTransaction", () => {
5
+ it("joins a transaction already open on the connection", async () => {
6
+ const connection = new MockConnection();
7
+ const txn = connection.transaction as MockTransaction;
8
+ const outer = new UnitOfWorkTransaction(connection);
9
+ await outer.begin();
10
+ const inner = new UnitOfWorkTransaction(connection);
11
+ await inner.begin();
12
+ await inner.commit();
13
+ await outer.commit();
14
+ expect(connection.startTransactionCalls).toBe(1);
15
+ expect(txn.commitCalls).toBe(1);
16
+ expect(txn.abortCalls).toBe(0);
17
+ });
18
+
19
+ it("aborts the outer transaction once when a joined unit aborts", async () => {
20
+ const connection = new MockConnection();
21
+ const txn = connection.transaction as MockTransaction;
22
+ const outer = new UnitOfWorkTransaction(connection);
23
+ await outer.begin();
24
+ const inner = new UnitOfWorkTransaction(connection);
25
+ await inner.begin();
26
+ await inner.abort();
27
+ await outer.abort();
28
+ expect(connection.startTransactionCalls).toBe(1);
29
+ expect(txn.commitCalls).toBe(0);
30
+ expect(txn.abortCalls).toBe(1);
31
+ });
32
+
33
+ it("rejects a second begin and a unit that never began", async () => {
34
+ const failed = new MockConnection();
35
+ failed.startTransactionError = new Error("nope");
36
+ const broken = new UnitOfWorkTransaction(failed);
37
+ await expect(broken.begin()).rejects.toThrow("nope");
38
+ await expect(broken.begin()).rejects.toThrow(DatasourceError);
39
+ const idle = new UnitOfWorkTransaction(new MockConnection());
40
+ await expect(idle.commit()).rejects.toThrow("has not begun");
41
+ await expect(idle.abort()).rejects.toThrow("has not begun");
42
+ });
43
+ });
@@ -0,0 +1,50 @@
1
+ import { DatasourceError } from "./DatasourceError.ts";
2
+ import type { IConnection } from "./IConnection.ts";
3
+ import type { ITransaction } from "./ITransaction.ts";
4
+ import type { IUnitOfWork } from "./IUnitOfWork.ts";
5
+
6
+ const open = new WeakSet<IConnection>();
7
+
8
+ export class UnitOfWorkTransaction implements IUnitOfWork {
9
+ private begun = false;
10
+ private transaction: ITransaction | undefined;
11
+
12
+ constructor(private readonly connection: IConnection) {}
13
+
14
+ begin = async (): Promise<void> => {
15
+ if (this.begun) throw new DatasourceError("Unit of work already begun");
16
+ this.begun = true;
17
+ if (open.has(this.connection)) return;
18
+ open.add(this.connection);
19
+ try {
20
+ this.transaction = await this.connection.startTransaction();
21
+ } catch (error) {
22
+ open.delete(this.connection);
23
+ throw error;
24
+ }
25
+ };
26
+
27
+ commit = async (): Promise<void> => {
28
+ if (!this.begun) throw new DatasourceError("Unit of work has not begun");
29
+ const txn = this.transaction;
30
+ if (!txn) return;
31
+ await txn.commit();
32
+ this.release();
33
+ };
34
+
35
+ abort = async (): Promise<void> => {
36
+ if (!this.begun) throw new DatasourceError("Unit of work has not begun");
37
+ const txn = this.transaction;
38
+ if (!txn) return;
39
+ try {
40
+ await txn.abort();
41
+ } finally {
42
+ this.release();
43
+ }
44
+ };
45
+
46
+ private release(): void {
47
+ open.delete(this.connection);
48
+ this.transaction = undefined;
49
+ }
50
+ }
@@ -0,0 +1,97 @@
1
+ import type { Enrichment } from "./Enrichment.ts";
2
+ import type { ICRUDRepository } from "./ICRUDRepository.ts";
3
+ import type { Scalar } from "./builders/Scalar.ts";
4
+ import { defaultId } from "./createCRUDRepositoryOptions.ts";
5
+ import { DatasourceError } from "./DatasourceError.ts";
6
+ import { Operator, Where } from "./statements/index.ts";
7
+
8
+ type Props = Record<string, Scalar | object>;
9
+
10
+ export abstract class UpdateEnrichmentStrategy<T> {
11
+ abstract updateChildren<TChild extends object>(writer: ICRUDRepository<TChild>, enrichment: Enrichment, parent: T): Promise<void>;
12
+
13
+ abstract updateLinked<TChild extends object>(writer: ICRUDRepository<TChild>, enrichment: Enrichment, parent: T): Promise<void>;
14
+
15
+ protected getCell = <TItem>(item: TItem, name: string): Scalar | object | undefined =>
16
+ (item as never as Props)[name];
17
+
18
+ protected presentCell = (value: Scalar | object | undefined): value is Scalar =>
19
+ value !== undefined && value !== null && typeof value !== "object" && value !== 0 && value !== 0n && value !== "";
20
+
21
+ protected payloadOf = <TChild extends object, TParent>(
22
+ parent: TParent,
23
+ into: string,
24
+ enrichment?: Enrichment,
25
+ ): TChild[] => mapPayload(this.getCell(parent, into), enrichment) as TChild[];
26
+
27
+ protected stampCell = <TItem extends object>(related: TItem, by: string, key: Scalar | object | undefined): void => {
28
+ if (key === undefined || key === null || typeof key === "object") return;
29
+ const current = this.getCell(related, by);
30
+ if (current !== null && typeof current === "object" && !Array.isArray(current)) {
31
+ const target = current as Props;
32
+ const id = "Id" in target ? "Id" : "id";
33
+ target[id] = key;
34
+ return;
35
+ }
36
+ Object.assign(related, { [by]: key });
37
+ };
38
+
39
+ protected hasKey = <TItem extends object>(item: TItem, keys: readonly string[]): boolean =>
40
+ keys.length > 0 && keys.every((name) => this.presentCell(this.getCell(item, name)));
41
+
42
+ protected keyOf = <TItem extends object>(item: TItem, keys: readonly string[]): string =>
43
+ keys.map((name) => String(this.getCell(item, name) ?? "")).join("\u001f");
44
+
45
+ protected namesOf = <TChild extends object>(writer: ICRUDRepository<TChild>, item: TChild): readonly string[] =>
46
+ writer.ids.length > 0 ? writer.ids : defaultId(Object.keys(item));
47
+
48
+ protected linkWhere = (
49
+ association: NonNullable<Enrichment["association"]>,
50
+ parentKey: Scalar,
51
+ relatedKey: Scalar,
52
+ ): Where =>
53
+ new Where(
54
+ new Operator.And(
55
+ new Operator.Equals(association.parent, parentKey),
56
+ new Operator.Equals(association.related, relatedKey),
57
+ ),
58
+ );
59
+
60
+ protected relatedIds = <TParent>(parent: TParent, enrichment: Enrichment): Scalar[] => {
61
+ const ids: Scalar[] = [];
62
+ const seen = new Set<string>();
63
+ for (const related of this.payloadOf<object, TParent>(parent, enrichment.destinationProperty)) {
64
+ const id = this.getCell(related, enrichment.relatedKey);
65
+ if (!this.presentCell(id)) throw new DatasourceError(`${enrichment.destinationProperty} requires "${enrichment.relatedKey}"`);
66
+ const key = String(id);
67
+ if (seen.has(key)) continue;
68
+ seen.add(key);
69
+ ids.push(id);
70
+ }
71
+ return ids;
72
+ };
73
+
74
+ protected requiredKey = <TParent>(parent: TParent, enrichment: Enrichment): Scalar => {
75
+ const key = this.getCell(parent, enrichment.sourceProperty);
76
+ if (!this.presentCell(key)) throw new DatasourceError(`${enrichment.destinationProperty} requires "${enrichment.sourceProperty}"`);
77
+ return key;
78
+ };
79
+ }
80
+
81
+ const mapPayload = (value: Scalar | object | undefined, enrichment?: Enrichment): object[] => {
82
+ if (value === undefined || value === null || typeof value === "string") return [];
83
+ if (Array.isArray(value)) return value.filter((item) => item !== null && typeof item === "object");
84
+ if (enrichment?.type !== "dictionary" || typeof value !== "object") return [];
85
+ const keyName = mapKeyName(enrichment.relatedKey);
86
+ const valueName = enrichment.relatedProperty ?? (keyName === "Key" ? "Value" : "value");
87
+ return Object.entries(value as Record<string, Scalar | object>)
88
+ .sort(([left], [right]) => left.localeCompare(right))
89
+ .map(([key, cell]) =>
90
+ cell !== null && typeof cell === "object" && !Array.isArray(cell)
91
+ ? { [keyName]: key, ...(cell as object) }
92
+ : { [keyName]: key, [valueName]: cell },
93
+ );
94
+ };
95
+
96
+ const mapKeyName = (relatedKey: string): string =>
97
+ relatedKey.includes("_") || relatedKey[0] !== relatedKey[0]?.toUpperCase() ? "key" : "Key";
@@ -0,0 +1,323 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { MemoryDriver } from "@datasources/memory";
3
+ import { collectAsync } from "./collectAsync.ts";
4
+ import { CRUDRepository } from "./CRUDRepository.ts";
5
+ import { LazyWriteEnrichmentStrategy } from "./LazyWriteEnrichmentStrategy.ts";
6
+ import type { IConnection } from "./IConnection.ts";
7
+
8
+ class UserView {
9
+ id = 0;
10
+ username = "";
11
+ is_enabled = false;
12
+ user_type_id = 0;
13
+ user_type?: UserType;
14
+ roles: Role[] | null = null;
15
+ groups: Group[] | null = null;
16
+ }
17
+
18
+ class UserType {
19
+ id = 0;
20
+ name = "";
21
+ }
22
+
23
+ class Role {
24
+ id = 0;
25
+ name = "";
26
+ }
27
+
28
+ class Group {
29
+ id = 0;
30
+ name = "";
31
+ }
32
+
33
+ class UserRole {
34
+ user_id = 0;
35
+ role_id = 0;
36
+ }
37
+
38
+ class UserGroup {
39
+ user_id = 0;
40
+ group_id = 0;
41
+ }
42
+
43
+ type Graph = {
44
+ usersRepo: CRUDRepository<UserView>;
45
+ userTypesRepo: CRUDRepository<UserType>;
46
+ rolesRepo: CRUDRepository<Role>;
47
+ groupsRepo: CRUDRepository<Group>;
48
+ userRolesRepo: CRUDRepository<UserRole>;
49
+ userGroupsRepo: CRUDRepository<UserGroup>;
50
+ adminType: UserType;
51
+ adminRole: Role;
52
+ editorRole: Role;
53
+ engineering: Group;
54
+ ops: Group;
55
+ };
56
+
57
+ const setup = async (): Promise<IConnection> => {
58
+ const connection = new MemoryDriver().connection();
59
+ await connection.open();
60
+ return connection;
61
+ };
62
+
63
+ const ids = (values: number[]) => [...values].sort((left, right) => left - right);
64
+
65
+ const graph = async (write?: LazyWriteEnrichmentStrategy<UserView>): Promise<Graph> => {
66
+ const connection = await setup();
67
+ const usersRepo = new CRUDRepository(connection, { type: UserView, write });
68
+ const userTypesRepo = new CRUDRepository(UserType, connection);
69
+ const rolesRepo = new CRUDRepository(Role, connection);
70
+ const groupsRepo = new CRUDRepository(Group, connection);
71
+ const userRolesRepo = new CRUDRepository(UserRole, connection);
72
+ const userGroupsRepo = new CRUDRepository(UserGroup, connection);
73
+ const adminType = await userTypesRepo.create(Object.assign(new UserType(), { name: "admin" }));
74
+ const adminRole = await rolesRepo.create(Object.assign(new Role(), { name: "admin" }));
75
+ const editorRole = await rolesRepo.create(Object.assign(new Role(), { name: "editor" }));
76
+ const engineering = await groupsRepo.create(Object.assign(new Group(), { name: "engineering" }));
77
+ const ops = await groupsRepo.create(Object.assign(new Group(), { name: "ops" }));
78
+ return { usersRepo, userTypesRepo, rolesRepo, groupsRepo, userRolesRepo, userGroupsRepo, adminType, adminRole, editorRole, engineering, ops };
79
+ };
80
+
81
+ const link = (usersRepo: CRUDRepository<UserView>, seeded: Graph): void => {
82
+ usersRepo.addEnriched({ destinationProperty: "user_type", sourceProperty: "user_type_id", relatedKey: "id" }, seeded.userTypesRepo);
83
+ usersRepo.addLinked(
84
+ { destinationProperty: "roles", sourceProperty: "id", relatedKey: "id", association: { parent: "user_id", related: "role_id" } },
85
+ seeded.rolesRepo,
86
+ seeded.userRolesRepo,
87
+ );
88
+ usersRepo.addLinked(
89
+ { destinationProperty: "groups", sourceProperty: "id", relatedKey: "id", association: { parent: "user_id", related: "group_id" } },
90
+ seeded.groupsRepo,
91
+ seeded.userGroupsRepo,
92
+ );
93
+ };
94
+
95
+ const createAda = async (seeded: Graph, roles: Role[] | null, groups: Group[] | null): Promise<UserView> => {
96
+ link(seeded.usersRepo, seeded);
97
+ return seeded.usersRepo.create(Object.assign(new UserView(), {
98
+ username: "ada",
99
+ is_enabled: true,
100
+ user_type_id: seeded.adminType.id,
101
+ roles,
102
+ groups,
103
+ }));
104
+ };
105
+
106
+ describe("CRUDRepository user view", () => {
107
+ it("creates, updates, and deletes through link rows", async () => {
108
+ const seeded = await graph();
109
+ const usersRepo = seeded.usersRepo;
110
+ usersRepo.addEnriched({ destinationProperty: "user_type", sourceProperty: "user_type_id", relatedKey: "id" }, seeded.userTypesRepo);
111
+ usersRepo.addLinked(
112
+ { destinationProperty: "roles", sourceProperty: "id", relatedKey: "id", association: { parent: "user_id", related: "role_id" } },
113
+ seeded.rolesRepo,
114
+ seeded.userRolesRepo,
115
+ );
116
+ usersRepo.addLinked(
117
+ { destinationProperty: "groups", sourceProperty: "id", relatedKey: "id", association: { parent: "user_id", related: "group_id" } },
118
+ seeded.groupsRepo,
119
+ seeded.userGroupsRepo,
120
+ );
121
+ const created = await usersRepo.create(Object.assign(new UserView(), {
122
+ username: "ada",
123
+ is_enabled: true,
124
+ user_type_id: seeded.adminType.id,
125
+ roles: [seeded.adminRole, seeded.editorRole],
126
+ groups: [seeded.engineering],
127
+ }));
128
+ const createdRoleLinks = await collectAsync(seeded.userRolesRepo.read());
129
+ const createdGroupLinks = await collectAsync(seeded.userGroupsRepo.read());
130
+ const saved = await usersRepo.update(Object.assign(new UserView(), {
131
+ id: created.id,
132
+ username: "ada",
133
+ is_enabled: true,
134
+ user_type_id: seeded.adminType.id,
135
+ roles: [seeded.editorRole],
136
+ groups: [seeded.engineering, seeded.ops],
137
+ }));
138
+ const updated = await usersRepo.readSingleById(created.id);
139
+ const updatedRoleLinks = await collectAsync(seeded.userRolesRepo.read());
140
+ const updatedGroupLinks = await collectAsync(seeded.userGroupsRepo.read());
141
+ const removed = await usersRepo.delete(Object.assign(new UserView(), { id: created.id }));
142
+
143
+ expect(created.id > 0).toEqual(true);
144
+ expect(created.username).toEqual("ada");
145
+ expect(created.is_enabled).toEqual(true);
146
+ expect(created.user_type_id).toEqual(seeded.adminType.id);
147
+ expect(created.user_type?.name).toEqual("admin");
148
+ expect(ids(created.roles?.map((role) => role.id) ?? [])).toEqual(ids([seeded.adminRole.id, seeded.editorRole.id]));
149
+ expect(created.groups?.map((group) => group.name)).toEqual(["engineering"]);
150
+ expect(createdRoleLinks.map((linkRow) => linkRow.user_id)).toEqual([created.id, created.id]);
151
+ expect(ids(createdRoleLinks.map((linkRow) => linkRow.role_id))).toEqual(ids([seeded.adminRole.id, seeded.editorRole.id]));
152
+ expect(createdGroupLinks.map((linkRow) => linkRow.user_id)).toEqual([created.id]);
153
+ expect(createdGroupLinks.map((linkRow) => linkRow.group_id)).toEqual([seeded.engineering.id]);
154
+ expect(saved.rowsAffected).toEqual(1);
155
+ expect(updated?.id).toEqual(created.id);
156
+ expect(updated?.username).toEqual("ada");
157
+ expect(updated?.is_enabled).toEqual(true);
158
+ expect(updated?.user_type_id).toEqual(seeded.adminType.id);
159
+ expect(updated?.roles?.map((role) => role.id)).toEqual([seeded.editorRole.id]);
160
+ expect(ids(updated?.groups?.map((group) => group.id) ?? [])).toEqual(ids([seeded.engineering.id, seeded.ops.id]));
161
+ expect(updatedRoleLinks.map((linkRow) => linkRow.user_id)).toEqual([created.id]);
162
+ expect(updatedRoleLinks.map((linkRow) => linkRow.role_id)).toEqual([seeded.editorRole.id]);
163
+ expect(updatedGroupLinks.map((linkRow) => linkRow.user_id)).toEqual([created.id, created.id]);
164
+ expect(ids(updatedGroupLinks.map((linkRow) => linkRow.group_id))).toEqual(ids([seeded.engineering.id, seeded.ops.id]));
165
+ expect(removed.rowsAffected).toEqual(1);
166
+ expect(await collectAsync(usersRepo.read())).toEqual([]);
167
+ expect(await collectAsync(seeded.userRolesRepo.read())).toEqual([]);
168
+ expect(await collectAsync(seeded.userGroupsRepo.read())).toEqual([]);
169
+ expect((await collectAsync(seeded.rolesRepo.read())).map((role) => role.name).sort()).toEqual(["admin", "editor"]);
170
+ expect((await collectAsync(seeded.groupsRepo.read())).map((group) => group.name).sort()).toEqual(["engineering", "ops"]);
171
+ expect((await collectAsync(seeded.userTypesRepo.read())).map((type) => type.name)).toEqual(["admin"]);
172
+ });
173
+
174
+ it("clears one association when the list is null", async () => {
175
+ const seeded = await graph();
176
+ const created = await createAda(seeded, [seeded.adminRole, seeded.editorRole], [seeded.engineering]);
177
+ const saved = await seeded.usersRepo.update(Object.assign(new UserView(), {
178
+ id: created.id,
179
+ username: "ada",
180
+ is_enabled: true,
181
+ user_type_id: seeded.adminType.id,
182
+ roles: null,
183
+ groups: [seeded.engineering],
184
+ }));
185
+ const loaded = await seeded.usersRepo.readSingleById(created.id);
186
+ const userRoles = await collectAsync(seeded.userRolesRepo.read());
187
+ const userGroups = await collectAsync(seeded.userGroupsRepo.read());
188
+
189
+ expect(created.id > 0).toEqual(true);
190
+ expect(saved.rowsAffected).toEqual(1);
191
+ expect(loaded?.id).toEqual(created.id);
192
+ expect(loaded?.username).toEqual("ada");
193
+ expect(loaded?.user_type_id).toEqual(seeded.adminType.id);
194
+ expect(loaded?.user_type?.name).toEqual("admin");
195
+ expect(loaded?.roles ?? []).toEqual([]);
196
+ expect(loaded?.groups?.map((group) => group.id)).toEqual([seeded.engineering.id]);
197
+ expect(userRoles).toEqual([]);
198
+ expect(userGroups.map((linkRow) => linkRow.user_id)).toEqual([created.id]);
199
+ expect(userGroups.map((linkRow) => linkRow.group_id)).toEqual([seeded.engineering.id]);
200
+ expect((await collectAsync(seeded.rolesRepo.read())).map((role) => role.name).sort()).toEqual(["admin", "editor"]);
201
+ expect((await collectAsync(seeded.groupsRepo.read())).map((group) => group.name).sort()).toEqual(["engineering", "ops"]);
202
+ });
203
+
204
+ it("clears one association when the list is empty", async () => {
205
+ const seeded = await graph();
206
+ const created = await createAda(seeded, [seeded.adminRole, seeded.editorRole], [seeded.engineering]);
207
+ const saved = await seeded.usersRepo.update(Object.assign(new UserView(), {
208
+ id: created.id,
209
+ username: "ada",
210
+ is_enabled: true,
211
+ user_type_id: seeded.adminType.id,
212
+ roles: [],
213
+ groups: [seeded.engineering],
214
+ }));
215
+ const loaded = await seeded.usersRepo.readSingleById(created.id);
216
+ const userRoles = await collectAsync(seeded.userRolesRepo.read());
217
+ const userGroups = await collectAsync(seeded.userGroupsRepo.read());
218
+
219
+ expect(created.id > 0).toEqual(true);
220
+ expect(saved.rowsAffected).toEqual(1);
221
+ expect(loaded?.id).toEqual(created.id);
222
+ expect(loaded?.username).toEqual("ada");
223
+ expect(loaded?.user_type_id).toEqual(seeded.adminType.id);
224
+ expect(loaded?.user_type?.name).toEqual("admin");
225
+ expect(loaded?.roles ?? []).toEqual([]);
226
+ expect(loaded?.groups?.map((group) => group.id)).toEqual([seeded.engineering.id]);
227
+ expect(userRoles).toEqual([]);
228
+ expect(userGroups.map((linkRow) => linkRow.user_id)).toEqual([created.id]);
229
+ expect(userGroups.map((linkRow) => linkRow.group_id)).toEqual([seeded.engineering.id]);
230
+ expect((await collectAsync(seeded.rolesRepo.read())).map((role) => role.name).sort()).toEqual(["admin", "editor"]);
231
+ expect((await collectAsync(seeded.groupsRepo.read())).map((group) => group.name).sort()).toEqual(["engineering", "ops"]);
232
+ });
233
+
234
+ it("inserts one junction row for a duplicate related id", async () => {
235
+ const seeded = await graph();
236
+ const created = await createAda(seeded, [seeded.adminRole, seeded.adminRole], [seeded.engineering]);
237
+ const roleLinks = await collectAsync(seeded.userRolesRepo.read());
238
+ const groupLinks = await collectAsync(seeded.userGroupsRepo.read());
239
+
240
+ expect(created.id > 0).toEqual(true);
241
+ expect(roleLinks.map((linkRow) => linkRow.user_id)).toEqual([created.id]);
242
+ expect(roleLinks.map((linkRow) => linkRow.role_id)).toEqual([seeded.adminRole.id]);
243
+ expect(groupLinks.map((linkRow) => linkRow.user_id)).toEqual([created.id]);
244
+ expect(groupLinks.map((linkRow) => linkRow.group_id)).toEqual([seeded.engineering.id]);
245
+ expect((await collectAsync(seeded.rolesRepo.read())).map((role) => role.name).sort()).toEqual(["admin", "editor"]);
246
+ });
247
+
248
+ it("lazy write skips unloaded links", async () => {
249
+ const seeded = await graph(new LazyWriteEnrichmentStrategy());
250
+ link(seeded.usersRepo, seeded);
251
+ const created = await seeded.usersRepo.create(Object.assign(new UserView(), {
252
+ username: "ada",
253
+ is_enabled: true,
254
+ user_type_id: seeded.adminType.id,
255
+ roles: null,
256
+ groups: [seeded.engineering],
257
+ }));
258
+ const createdRoleLinks = await collectAsync(seeded.userRolesRepo.read());
259
+ const createdGroupLinks = await collectAsync(seeded.userGroupsRepo.read());
260
+ const skipped = await seeded.usersRepo.update(Object.assign(new UserView(), {
261
+ id: created.id,
262
+ username: "ada",
263
+ is_enabled: false,
264
+ user_type_id: seeded.adminType.id,
265
+ }));
266
+ const afterSkip = await seeded.usersRepo.readSingleById(created.id);
267
+ const skippedRoleLinks = await collectAsync(seeded.userRolesRepo.read());
268
+ const skippedGroupLinks = await collectAsync(seeded.userGroupsRepo.read());
269
+ const linked = await seeded.usersRepo.update(Object.assign(new UserView(), {
270
+ id: created.id,
271
+ username: "ada",
272
+ is_enabled: false,
273
+ user_type_id: seeded.adminType.id,
274
+ roles: [seeded.editorRole],
275
+ }));
276
+ const linkedRoleLinks = await collectAsync(seeded.userRolesRepo.read());
277
+ const linkedGroupLinks = await collectAsync(seeded.userGroupsRepo.read());
278
+ const removed = await seeded.usersRepo.delete(Object.assign(new UserView(), { id: created.id, roles: null, groups: null }));
279
+
280
+ expect(created.id > 0).toEqual(true);
281
+ expect(createdRoleLinks).toEqual([]);
282
+ expect(createdGroupLinks.map((linkRow) => linkRow.user_id)).toEqual([created.id]);
283
+ expect(createdGroupLinks.map((linkRow) => linkRow.group_id)).toEqual([seeded.engineering.id]);
284
+ expect(skipped.rowsAffected).toEqual(1);
285
+ expect(afterSkip?.id).toEqual(created.id);
286
+ expect(afterSkip?.username).toEqual("ada");
287
+ expect(afterSkip?.is_enabled).toEqual(false);
288
+ expect(skippedRoleLinks).toEqual([]);
289
+ expect(skippedGroupLinks.map((linkRow) => linkRow.user_id)).toEqual([created.id]);
290
+ expect(skippedGroupLinks.map((linkRow) => linkRow.group_id)).toEqual([seeded.engineering.id]);
291
+ expect(linked.rowsAffected).toEqual(1);
292
+ expect(linkedRoleLinks.map((linkRow) => linkRow.user_id)).toEqual([created.id]);
293
+ expect(linkedRoleLinks.map((linkRow) => linkRow.role_id)).toEqual([seeded.editorRole.id]);
294
+ expect(linkedGroupLinks.map((linkRow) => linkRow.user_id)).toEqual([created.id]);
295
+ expect(linkedGroupLinks.map((linkRow) => linkRow.group_id)).toEqual([seeded.engineering.id]);
296
+ expect((await collectAsync(seeded.rolesRepo.read())).map((role) => role.name).sort()).toEqual(["admin", "editor"]);
297
+ expect((await collectAsync(seeded.groupsRepo.read())).map((group) => group.name).sort()).toEqual(["engineering", "ops"]);
298
+ expect(removed.rowsAffected).toEqual(1);
299
+ expect(await collectAsync(seeded.usersRepo.read())).toEqual([]);
300
+ expect(await collectAsync(seeded.userRolesRepo.read())).toEqual([]);
301
+ expect(await collectAsync(seeded.userGroupsRepo.read())).toEqual([]);
302
+ });
303
+
304
+ it("rejects a link without an id", async () => {
305
+ const seeded = await graph();
306
+ seeded.usersRepo.addLinked(
307
+ { destinationProperty: "roles", sourceProperty: "id", relatedKey: "id", association: { parent: "user_id", related: "role_id" } },
308
+ seeded.rolesRepo,
309
+ seeded.userRolesRepo,
310
+ );
311
+ await expect(seeded.usersRepo.create(Object.assign(new UserView(), {
312
+ username: "ada",
313
+ user_type_id: seeded.adminType.id,
314
+ is_enabled: true,
315
+ roles: [new Role()],
316
+ }))).rejects.toThrow(/requires "id"/);
317
+ const users = await collectAsync(seeded.usersRepo.read());
318
+ expect(users.length).toEqual(1);
319
+ expect(users[0]!.id > 0).toEqual(true);
320
+ expect(users[0]!.username).toEqual("ada");
321
+ expect(await collectAsync(seeded.userRolesRepo.read())).toEqual([]);
322
+ });
323
+ });
@@ -0,0 +1 @@
1
+ export type WriteMode = "create" | "update" | "delete";
@@ -0,0 +1,6 @@
1
+ import type { Scalar } from "./Scalar.ts";
2
+
3
+ export interface Field {
4
+ name: string;
5
+ value: Scalar;
6
+ }
@@ -0,0 +1 @@
1
+ export type Scalar = string | number | boolean | bigint | null;