@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,110 @@
1
+ import { afterAll, afterEach, beforeEach, describe, expect, it } from "vitest";
2
+ import { DriverLoader } from "./DriverLoader.ts";
3
+ import { Datasource, DatasourceError, DatasourceFactory, ProviderCatalog, ProviderNotInstalledError } from "./index.ts";
4
+
5
+ const envKeys = ["INSTALLED_PROVIDERS", "DATASOURCE_URL", "PROVIDER"] as const;
6
+ const savedEnv = Object.fromEntries(envKeys.map((key) => [key, process.env[key]]));
7
+
8
+ beforeEach(() => {
9
+ for (const key of envKeys) delete process.env[key];
10
+ DatasourceFactory.reset();
11
+ });
12
+
13
+ afterEach(() => DatasourceFactory.reset());
14
+
15
+ afterAll(() => {
16
+ for (const key of envKeys) {
17
+ if (savedEnv[key] === undefined) delete process.env[key];
18
+ else process.env[key] = savedEnv[key];
19
+ }
20
+ });
21
+
22
+ describe("ProviderCatalog", () => {
23
+ it("default stores location and module", () => {
24
+ const sqlite = ProviderCatalog.default.sqlite;
25
+ expect(sqlite?.location).toBe("@datasources/sqlite");
26
+ expect(sqlite?.module).toBe("SqliteDriver");
27
+ expect(ProviderCatalog.default.sqlserver?.module).toBe("SqlServerDriver");
28
+ expect(ProviderCatalog.default.mongodb?.module).toBe("MongoDbDriver");
29
+ expect(ProviderCatalog.default.dynamodb?.module).toBe("DynamoDbDriver");
30
+ });
31
+
32
+ it("full constructor stores location and module", () => {
33
+ const catalog = new ProviderCatalog("acme", "@acme/data", "AcmeDriver");
34
+ expect(catalog.name).toBe("acme");
35
+ expect(catalog.location).toBe("@acme/data");
36
+ expect(catalog.module).toBe("AcmeDriver");
37
+ });
38
+
39
+ it("default lists monorepo providers", () => {
40
+ expect(Object.keys(ProviderCatalog.default)).toEqual([
41
+ "json",
42
+ "memory",
43
+ "sqlite",
44
+ "xml",
45
+ "yaml",
46
+ "mysql",
47
+ "sqlserver",
48
+ "postgres",
49
+ "oracle",
50
+ "mongodb",
51
+ "dynamodb",
52
+ ]);
53
+ });
54
+
55
+ it("defaults registers installed providers", () => {
56
+ DatasourceFactory.defaults({
57
+ INSTALLED_PROVIDERS: "json,memory",
58
+ DATASOURCE_URL: "memory://",
59
+ });
60
+ expect(DatasourceFactory.registered()).toEqual(["json", "memory"]);
61
+ expect(
62
+ Datasource.create({ PROVIDER: "memory", DATASOURCE_URL: "memory://" }).provider,
63
+ ).toBe("memory");
64
+ });
65
+
66
+ it("create does not take installed providers", () => {
67
+ DatasourceFactory.defaults({ INSTALLED_PROVIDERS: "json,memory" });
68
+ expect(Datasource.create({ PROVIDER: "json", DATASOURCE_URL: "json://" }).provider).toBe(
69
+ "json",
70
+ );
71
+ expect(DatasourceFactory.registered()).toEqual(["json", "memory"]);
72
+ });
73
+
74
+ it("create reads connection from the environment", () => {
75
+ process.env.INSTALLED_PROVIDERS = "json,memory";
76
+ process.env.DATASOURCE_URL = "memory://";
77
+ process.env.PROVIDER = "memory";
78
+ expect(Datasource.create().provider).toBe("memory");
79
+ expect(Datasource.default.provider).toBe("memory");
80
+ expect(DatasourceFactory.registered()).toEqual(["json", "memory"]);
81
+ });
82
+
83
+ it("missing export throws", () => {
84
+ expect(() =>
85
+ DriverLoader.load(new ProviderCatalog("json", "@datasources/json", "MissingDriver")),
86
+ ).toThrow(ProviderNotInstalledError);
87
+ });
88
+
89
+ it("missing package or unknown name", () => {
90
+ expect(() => DatasourceFactory.defaults({ INSTALLED_PROVIDERS: "sqlite" })).toThrow(
91
+ ProviderNotInstalledError,
92
+ );
93
+ expect(() => DatasourceFactory.defaults({ INSTALLED_PROVIDERS: "nope" })).toThrow(
94
+ ProviderNotInstalledError,
95
+ );
96
+ });
97
+
98
+ it("default overrides env", () => {
99
+ process.env.INSTALLED_PROVIDERS = "json,memory";
100
+ process.env.PROVIDER = "json";
101
+ process.env.DATASOURCE_URL = "json://";
102
+ expect(Datasource.default.provider).toBe("json");
103
+ });
104
+
105
+ it("url without provider throws", () => {
106
+ expect(() => DatasourceFactory.defaults({ DATASOURCE_URL: "not-a-url" })).toThrow(
107
+ new DatasourceError("DATASOURCE_URL has no provider"),
108
+ );
109
+ });
110
+ });
@@ -0,0 +1,21 @@
1
+ export class ProviderCatalog {
2
+ constructor(
3
+ readonly name: string,
4
+ readonly location: string,
5
+ readonly module: string,
6
+ ) {}
7
+
8
+ static readonly default: Readonly<Record<string, ProviderCatalog>> = {
9
+ json: new ProviderCatalog("json", "@datasources/json", "JsonDriver"),
10
+ memory: new ProviderCatalog("memory", "@datasources/memory", "MemoryDriver"),
11
+ sqlite: new ProviderCatalog("sqlite", "@datasources/sqlite", "SqliteDriver"),
12
+ xml: new ProviderCatalog("xml", "@datasources/xml", "XmlDriver"),
13
+ yaml: new ProviderCatalog("yaml", "@datasources/yaml", "YamlDriver"),
14
+ mysql: new ProviderCatalog("mysql", "@datasources/mysql", "MysqlDriver"),
15
+ sqlserver: new ProviderCatalog("sqlserver", "@datasources/sqlserver", "SqlServerDriver"),
16
+ postgres: new ProviderCatalog("postgres", "@datasources/postgres", "PostgresDriver"),
17
+ oracle: new ProviderCatalog("oracle", "@datasources/oracle", "OracleDriver"),
18
+ mongodb: new ProviderCatalog("mongodb", "@datasources/mongodb", "MongoDbDriver"),
19
+ dynamodb: new ProviderCatalog("dynamodb", "@datasources/dynamodb", "DynamoDbDriver"),
20
+ };
21
+ }
@@ -0,0 +1,11 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { ProviderNotImplementedError } from "./index.ts";
3
+
4
+ describe("ProviderNotImplementedError", () => {
5
+ it("reports an unimplemented provider", () => {
6
+ const error = new ProviderNotImplementedError("oracle");
7
+ expect(error.name).toBe("ProviderNotImplementedError");
8
+ expect(error.provider).toBe("oracle");
9
+ expect(error.message).toContain("oracle");
10
+ });
11
+ });
@@ -0,0 +1,9 @@
1
+ export class ProviderNotImplementedError extends Error {
2
+ readonly provider: string;
3
+
4
+ constructor(provider: string) {
5
+ super(`Provider "${provider}" is not implemented`);
6
+ this.name = "ProviderNotImplementedError";
7
+ this.provider = provider;
8
+ }
9
+ }
@@ -0,0 +1,11 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { ProviderNotInstalledError } from "./index.ts";
3
+
4
+ describe("ProviderNotInstalledError", () => {
5
+ it("reports a missing provider", () => {
6
+ const error = new ProviderNotInstalledError("mysql");
7
+ expect(error.name).toBe("ProviderNotInstalledError");
8
+ expect(error.provider).toBe("mysql");
9
+ expect(error.message).toContain("mysql");
10
+ });
11
+ });
@@ -0,0 +1,9 @@
1
+ export class ProviderNotInstalledError extends Error {
2
+ readonly provider: string;
3
+
4
+ constructor(provider: string) {
5
+ super(`Provider "${provider}" is not installed`);
6
+ this.name = "ProviderNotInstalledError";
7
+ this.provider = provider;
8
+ }
9
+ }
@@ -0,0 +1,406 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { MemoryDriver } from "@datasources/memory";
3
+ import { collectAsync } from "./collectAsync.ts";
4
+ import type { Cells } from "./converters/Cell.ts";
5
+ import { TypeConverter } from "./converters/TypeConverter.ts";
6
+ import { DefaultReadonlyEnrichmentStrategy } from "./DefaultReadonlyEnrichmentStrategy.ts";
7
+ import { NestedToObjectEnrichmentStrategy } from "./NestedToObjectEnrichmentStrategy.ts";
8
+ import { HashsetOptimizedReadonlyEnrichmentStrategy } from "./HashsetOptimizedReadonlyEnrichmentStrategy.ts";
9
+ import { ReadonlyRepository } from "./ReadonlyRepository.ts";
10
+ import { RepositoryTreeNode } from "./RepositoryTreeNode.ts";
11
+ import { ChangeCasingMapper } from "./mappers/ChangeCasingMapper.ts";
12
+ import { DictionaryMapper } from "./mappers/DictionaryMapper.ts";
13
+ import type { IMapper } from "./mappers/IMapper.ts";
14
+ import { TransformMapper } from "./mappers/TransformMapper.ts";
15
+ import type { IConnection } from "./IConnection.ts";
16
+ import type { Scalar } from "./builders/Scalar.ts";
17
+ import { MockCommand } from "./mocks/mock-command.ts";
18
+ import { MockConnection } from "./mocks/mock-connection.ts";
19
+ import { MockReader } from "./mocks/mock-reader.ts";
20
+ import { Join, Operator, SelectStatement, Where } from "./statements/index.ts";
21
+ import { WhereText } from "./statements/WhereText.ts";
22
+
23
+ class UserType {
24
+ Id = 0;
25
+ Name = "";
26
+ }
27
+
28
+ class Child {
29
+ Id = 0;
30
+ ParentId = 0;
31
+ Type = new UserType();
32
+ TypeName = "";
33
+ Label = "";
34
+ }
35
+
36
+ class Parent {
37
+ Id = 0;
38
+ Type = new UserType();
39
+ TypeName = "";
40
+ Children: Child[] = [];
41
+ Child?: Child | string;
42
+ }
43
+
44
+ class View {
45
+ UserType = "";
46
+ }
47
+
48
+ type Seed = Record<string, Scalar>;
49
+
50
+ const snake = new ChangeCasingMapper("pascalCase", "snakeCase");
51
+ const references = new DictionaryMapper<string>([["Type", "type_id"]]);
52
+ const columns: IMapper<string, string> = new TransformMapper(
53
+ (name) => {
54
+ const mapped = references.map(name);
55
+ return mapped !== name ? mapped : snake.map(name);
56
+ },
57
+ (name) => {
58
+ const from = references.reverseMap(name);
59
+ return from !== name ? from : snake.reverseMap(name);
60
+ },
61
+ );
62
+
63
+ const repo = <T>(type: new () => T, connection: IConnection) =>
64
+ new ReadonlyRepository(connection, { type, propertyMappings: columns });
65
+
66
+ const setup = async (rows: Seed[] = []): Promise<IConnection> => {
67
+ const connection = new MemoryDriver().connection();
68
+ await connection.open();
69
+ for (const row of rows) {
70
+ const response = await connection.createCommand().execute(
71
+ "insert",
72
+ Object.entries(row).map(([name, value]) => ({ name, value })),
73
+ );
74
+ if (typeof response.result === "number") row.id = response.result;
75
+ }
76
+ return connection;
77
+ };
78
+
79
+ const remove = (connection: IConnection, row: Seed) =>
80
+ connection.createCommand().execute("delete", [{ name: "id", value: row.id }]);
81
+
82
+ describe("ReadonlyRepository", () => {
83
+ it("reads one row through a where clause", async () => {
84
+ const first: Seed = {};
85
+ const second: Seed = {};
86
+ const connection = await setup([first, second]);
87
+ const parentsRepo = repo(Parent, connection);
88
+ const byClause = await collectAsync(parentsRepo.read(new Where(new Operator.Equals("Id", first.id))));
89
+ const byWhere = await collectAsync(parentsRepo.read(new WhereText(JSON.stringify({ id: first.id }))));
90
+ expect(byClause.map((item) => item.Id)).toEqual([first.id]);
91
+ expect(byWhere.map((item) => item.Id)).toEqual([first.id]);
92
+ });
93
+
94
+ it("fills lookup select and leaves missing default", async () => {
95
+ const person: Seed = { name: "person" };
96
+ const gone: Seed = { name: "gone" };
97
+ const typesConnection = await setup([person, gone]);
98
+ await remove(typesConnection, gone);
99
+ const connection = await setup([{ type_id: person.id }, { type_id: gone.id }]);
100
+ const parentsRepo = repo(Parent, connection);
101
+ const typesRepo = repo(UserType, typesConnection);
102
+ parentsRepo.addEnriched({ destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" }, typesRepo);
103
+ const items = await collectAsync(parentsRepo.read());
104
+ expect(items[0]?.TypeName).toBe("person");
105
+ expect(items[1]?.TypeName).toBe("");
106
+ });
107
+
108
+ it("hashset fills lookup select and leaves missing default", async () => {
109
+ const person: Seed = { name: "person" };
110
+ const gone: Seed = { name: "gone" };
111
+ const typesConnection = await setup([person, gone]);
112
+ await remove(typesConnection, gone);
113
+ const connection = await setup([{ type_id: person.id }, { type_id: gone.id }]);
114
+ const parentsRepo = repo(Parent, connection);
115
+ const typesRepo = repo(UserType, typesConnection);
116
+ parentsRepo.addEnriched(
117
+ { destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" },
118
+ typesRepo,
119
+ new HashsetOptimizedReadonlyEnrichmentStrategy(),
120
+ );
121
+ const items = await collectAsync(parentsRepo.read());
122
+ expect(items[0]?.TypeName).toBe("person");
123
+ expect(items[1]?.TypeName).toBe("");
124
+ });
125
+
126
+ it("fills lists and readSingle is enriched", async () => {
127
+ const org: Seed = { name: "org" };
128
+ const typesConnection = await setup([org]);
129
+ const parent: Seed = { type_id: org.id };
130
+ const other: Seed = { type_id: org.id };
131
+ const connection = await setup([parent, other]);
132
+ await remove(connection, other);
133
+ const parentsRepo = repo(Parent, connection);
134
+ const typesRepo = repo(UserType, typesConnection);
135
+ const childrenConnection = await setup([
136
+ { parent_id: parent.id, label: "a" },
137
+ { parent_id: parent.id, label: "b" },
138
+ { parent_id: other.id, label: "x" },
139
+ ]);
140
+ const childrenRepo = repo(Child, childrenConnection);
141
+ parentsRepo.addEnriched({ destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" }, typesRepo);
142
+ parentsRepo.addEnriched({ destinationProperty: "Children", sourceProperty: "Id", relatedKey: "ParentId", type: "list" }, childrenRepo);
143
+ const item = await parentsRepo.readSingle();
144
+ expect(item?.TypeName).toBe("org");
145
+ expect(item?.Children.map((child) => child.Label)).toEqual(["a", "b"]);
146
+ });
147
+
148
+ it("hashset fills lists and readSingle is enriched", async () => {
149
+ const org: Seed = { name: "org" };
150
+ const typesConnection = await setup([org]);
151
+ const parent: Seed = { type_id: org.id };
152
+ const other: Seed = { type_id: org.id };
153
+ const connection = await setup([parent, other]);
154
+ await remove(connection, other);
155
+ const parentsRepo = repo(Parent, connection);
156
+ const typesRepo = repo(UserType, typesConnection);
157
+ const childrenConnection = await setup([
158
+ { parent_id: parent.id, label: "a" },
159
+ { parent_id: parent.id, label: "b" },
160
+ { parent_id: other.id, label: "x" },
161
+ ]);
162
+ const childrenRepo = repo(Child, childrenConnection);
163
+ const strategy = new HashsetOptimizedReadonlyEnrichmentStrategy<Parent>();
164
+ parentsRepo.addEnriched({ destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" }, typesRepo, strategy);
165
+ parentsRepo.addEnriched({ destinationProperty: "Children", sourceProperty: "Id", relatedKey: "ParentId", type: "list" }, childrenRepo, strategy);
166
+ const item = await parentsRepo.readSingle();
167
+ expect(item?.TypeName).toBe("org");
168
+ expect(item?.Children.map((child) => child.Label)).toEqual(["a", "b"]);
169
+ });
170
+
171
+ it("nests list lookup and keeps root sibling", async () => {
172
+ const org: Seed = { name: "org" };
173
+ const typesConnection = await setup([org]);
174
+ const home: Seed = { name: "home" };
175
+ const gone: Seed = { name: "gone" };
176
+ const childTypesConnection = await setup([home, gone]);
177
+ await remove(childTypesConnection, gone);
178
+ const parent: Seed = { type_id: org.id };
179
+ const connection = await setup([parent]);
180
+ const parentsRepo = repo(Parent, connection);
181
+ const typesRepo = repo(UserType, typesConnection);
182
+ const childTypesRepo = repo(UserType, childTypesConnection);
183
+ const childrenConnection = await setup([
184
+ { parent_id: parent.id, type_id: home.id, label: "a" },
185
+ { parent_id: parent.id, type_id: gone.id, label: "b" },
186
+ ]);
187
+ const childrenRepo = repo(Child, childrenConnection);
188
+ childrenRepo.addEnriched({ destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" }, childTypesRepo);
189
+ parentsRepo.addEnriched({ destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" }, typesRepo);
190
+ parentsRepo.addEnriched({ destinationProperty: "Children", sourceProperty: "Id", relatedKey: "ParentId", type: "list" }, childrenRepo);
191
+ const item = await parentsRepo.readSingle();
192
+ expect(item?.TypeName).toBe("org");
193
+ expect(item?.Children[0]?.TypeName).toBe("home");
194
+ expect(item?.Children[1]?.TypeName).toBe("");
195
+ });
196
+
197
+ it("hashset nests list lookup and keeps root sibling", async () => {
198
+ const org: Seed = { name: "org" };
199
+ const typesConnection = await setup([org]);
200
+ const home: Seed = { name: "home" };
201
+ const gone: Seed = { name: "gone" };
202
+ const childTypesConnection = await setup([home, gone]);
203
+ await remove(childTypesConnection, gone);
204
+ const parent: Seed = { type_id: org.id };
205
+ const connection = await setup([parent]);
206
+ const parentsRepo = repo(Parent, connection);
207
+ const typesRepo = repo(UserType, typesConnection);
208
+ const childTypesRepo = repo(UserType, childTypesConnection);
209
+ const childrenConnection = await setup([
210
+ { parent_id: parent.id, type_id: home.id, label: "a" },
211
+ { parent_id: parent.id, type_id: gone.id, label: "b" },
212
+ ]);
213
+ const childrenRepo = repo(Child, childrenConnection);
214
+ const strategy = new HashsetOptimizedReadonlyEnrichmentStrategy<Parent>();
215
+ childrenRepo.addEnriched({ destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" }, childTypesRepo);
216
+ parentsRepo.addEnriched({ destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" }, typesRepo, strategy);
217
+ parentsRepo.addEnriched({ destinationProperty: "Children", sourceProperty: "Id", relatedKey: "ParentId", type: "list" }, childrenRepo, strategy);
218
+ const item = await parentsRepo.readSingle();
219
+ expect(item?.TypeName).toBe("org");
220
+ expect(item?.Children[0]?.TypeName).toBe("home");
221
+ expect(item?.Children[1]?.TypeName).toBe("");
222
+ });
223
+
224
+ it("skips missing enrichment keys", async () => {
225
+ const connection = await setup([{ type_id: -1 }, { type_id: -1 }]);
226
+ const parentsRepo = repo(Parent, connection);
227
+ const typesRepo = repo(UserType, await setup([{ name: "person" }]));
228
+ const childrenRepo = repo(Child, await setup([{ label: "a" }]));
229
+ parentsRepo.addEnriched({ destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" }, typesRepo);
230
+ parentsRepo.addEnriched({ destinationProperty: "Children", sourceProperty: "Id", relatedKey: "ParentId", type: "list" }, childrenRepo);
231
+ const items = await collectAsync(parentsRepo.read());
232
+ expect(items[0]?.TypeName).toBe("");
233
+ expect(items[1]?.TypeName).toBe("");
234
+ expect(items[1]?.Children).toEqual([]);
235
+ });
236
+
237
+ it("readSingle is undefined when empty", async () => {
238
+ const parentsRepo = repo(Parent, await setup());
239
+ expect(await parentsRepo.readSingle()).toBeUndefined();
240
+ });
241
+
242
+ it("nests on a singular related object and skips strings", async () => {
243
+ const kept: Seed = { child: "skip" };
244
+ const nested: Seed = {};
245
+ const connection = await setup([kept, nested]);
246
+ const parentsRepo = repo(Parent, connection);
247
+ const home: Seed = { name: "home" };
248
+ const childTypesRepo = repo(UserType, await setup([home]));
249
+ const childrenRepo = repo(Child, await setup([
250
+ { parent_id: nested.id, type_id: home.id, label: "only" },
251
+ ]));
252
+ childrenRepo.addEnriched({ destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" }, childTypesRepo);
253
+ parentsRepo.addEnriched({ destinationProperty: "Child", sourceProperty: "Id", relatedKey: "ParentId" }, childrenRepo);
254
+ const items = await collectAsync(parentsRepo.read());
255
+ expect(items[0]?.Child).toBe("skip");
256
+ expect(items[1]?.Child && typeof items[1].Child === "object" ? items[1].Child.TypeName : undefined).toBe("home");
257
+ });
258
+
259
+ it("throws when an enrichment key is an object", async () => {
260
+ const typesRepo = repo(UserType, await setup());
261
+ await expect(
262
+ new DefaultReadonlyEnrichmentStrategy<Parent>().apply(
263
+ typesRepo,
264
+ { destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" },
265
+ [Object.assign(new Parent(), { Id: 1, Type: { Name: "x" } })],
266
+ ),
267
+ ).rejects.toThrow(/scalar/);
268
+ });
269
+
270
+ it("hashset assigns the related row when select is omitted", async () => {
271
+ const person: Seed = { name: "person" };
272
+ const typesConnection = await setup([person]);
273
+ const connection = await setup([{ type_id: person.id }]);
274
+ const parentsRepo = repo(Parent, connection);
275
+ const typesRepo = repo(UserType, typesConnection);
276
+ parentsRepo.addEnriched(
277
+ { destinationProperty: "Type", sourceProperty: "Type", relatedKey: "Id" },
278
+ typesRepo,
279
+ new HashsetOptimizedReadonlyEnrichmentStrategy(),
280
+ );
281
+ const items = await collectAsync(parentsRepo.read());
282
+ expect(items[0]?.Type).toMatchObject({ Name: "person" });
283
+ });
284
+
285
+ it("hashset skips missing keys and unmatched rows", async () => {
286
+ const person: Seed = { name: "person" };
287
+ const typesConnection = await setup([person]);
288
+ const matched: Seed = { type_id: person.id };
289
+ const missing: Seed = { type_id: Number(person.id) + 1 };
290
+ const blank: Seed = { type_id: -1 };
291
+ const connection = await setup([matched, missing, blank]);
292
+ const parentsRepo = repo(Parent, connection);
293
+ const typesRepo = repo(UserType, typesConnection);
294
+ const childrenConnection = await setup([
295
+ { label: "orphan" },
296
+ { parent_id: matched.id, label: "a" },
297
+ { parent_id: matched.id, label: "b" },
298
+ ]);
299
+ const childrenRepo = repo(Child, childrenConnection);
300
+ const strategy = new HashsetOptimizedReadonlyEnrichmentStrategy<Parent>();
301
+ parentsRepo.addEnriched({ destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" }, typesRepo, strategy);
302
+ parentsRepo.addEnriched({ destinationProperty: "Children", sourceProperty: "Id", relatedKey: "ParentId", type: "list" }, childrenRepo, strategy);
303
+ const items = await collectAsync(parentsRepo.read());
304
+ expect(items[0]?.TypeName).toBe("person");
305
+ expect(items[0]?.Children.map((child) => child.Label)).toEqual(["a", "b"]);
306
+ expect(items[1]?.TypeName).toBe("");
307
+ expect(items[1]?.Children).toEqual([]);
308
+ expect(items[2]?.Children).toEqual([]);
309
+ expect(new RepositoryTreeNode({}, "").value).toEqual({});
310
+ });
311
+
312
+ it("collapses join rows", async () => {
313
+ const person: Seed = { TypeName: "person" };
314
+ const org: Seed = { TypeName: "org" };
315
+ const connection = await setup([person, org]);
316
+ person.Id = person.id;
317
+ org.Id = org.id;
318
+ const parents: Parent[] = [];
319
+ const parentsRepo = repo(Parent, connection);
320
+ await new NestedToObjectEnrichmentStrategy<Parent>({
321
+ key: "Id",
322
+ fields: { Id: "Id", TypeName: "TypeName" },
323
+ }).apply(parentsRepo, { destinationProperty: "TypeName", sourceProperty: "Id", relatedKey: "Id" }, parents, [person, org]);
324
+ const items = parents;
325
+ expect(items.map((item) => item.Id)).toEqual([person.id, org.id]);
326
+ expect(items.map((item) => item.TypeName)).toEqual(["person", "org"]);
327
+ });
328
+
329
+ it("lookup key comes from renamed row", async () => {
330
+ const admin: Seed = { name: "admin" };
331
+ const typesConnection = await setup([admin]);
332
+ const typesRepo = repo(UserType, typesConnection);
333
+ const usersConnection = await setup([{ user_type_id: admin.id }]);
334
+ const usersRepo = new ReadonlyRepository(usersConnection, {
335
+ type: View,
336
+ propertyMappings: new DictionaryMapper([["UserType", "user_type_id"]]),
337
+ });
338
+ usersRepo.addEnriched({ destinationProperty: "UserType", sourceProperty: "UserType", relatedKey: "Id", relatedProperty: "Name" }, typesRepo);
339
+ const item = await usersRepo.readSingle();
340
+ expect(item?.UserType).toBe("admin");
341
+ });
342
+
343
+ it("uses per-call object and row converters when provided", async () => {
344
+ class Item {
345
+ Label = "";
346
+ }
347
+ class FixedLabel extends TypeConverter<Item, Cells> {
348
+ constructor(private readonly label: string) {
349
+ super();
350
+ }
351
+ convert = (_from: Cells): Item => Object.assign(new Item(), { Label: this.label });
352
+ reverseConvert = (to: Item): Cells => ({ Label: to.Label });
353
+ }
354
+ class TagLabel extends TypeConverter<Item, Cells> {
355
+ convert = (from: Cells): Item =>
356
+ Object.assign(new Item(), { Label: `tag:${String(from.Label)}` });
357
+ reverseConvert = (to: Item): Cells => ({ Label: to.Label });
358
+ }
359
+ class UpperLabel extends TypeConverter<Cells, Cells> {
360
+ convert = (from: Cells): Cells => ({
361
+ ...from,
362
+ Label: String(from.Label).toUpperCase(),
363
+ });
364
+ reverseConvert = (to: Cells): Cells => to;
365
+ }
366
+
367
+ const connection = new MockConnection();
368
+ const command = new MockCommand(connection);
369
+ connection.command = command;
370
+ command.reader = new MockReader([{ Label: "ada" }]);
371
+ const itemsRepo = new ReadonlyRepository(connection, {
372
+ type: Item,
373
+ entity: "items",
374
+ readProperties: ["Label"],
375
+ objectConverter: new FixedLabel("default"),
376
+ });
377
+ expect((await itemsRepo.readSingle())?.Label).toBe("default");
378
+
379
+ command.reader = new MockReader([{ Label: "ada" }]);
380
+ expect(
381
+ (
382
+ await itemsRepo.readSingle(undefined, undefined, {
383
+ objectConverter: new TagLabel(),
384
+ rowConverter: new UpperLabel(),
385
+ })
386
+ )?.Label,
387
+ ).toBe("tag:ADA");
388
+ });
389
+
390
+ it("public include uses the node binder", async () => {
391
+ const person: Seed = { name: "person" };
392
+ const typesConnection = await setup([person]);
393
+ const connection = await setup([{ type_id: person.id }]);
394
+ const parentsRepo = repo(Parent, connection);
395
+ const typesRepo = repo(UserType, typesConnection);
396
+ parentsRepo.addEnriched({ destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" }, typesRepo);
397
+ const items = await collectAsync(parentsRepo.read());
398
+ expect(items[0]?.TypeName).toBe("person");
399
+ await new DefaultReadonlyEnrichmentStrategy<Parent>().apply(
400
+ typesRepo,
401
+ { destinationProperty: "TypeName", sourceProperty: "Type", relatedKey: "Id", relatedProperty: "Name" },
402
+ items,
403
+ [{ Type: person.id }],
404
+ );
405
+ });
406
+ });