@proseql/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/LICENSE +21 -0
  2. package/dist/errors/crud-errors.d.ts +98 -0
  3. package/dist/errors/crud-errors.d.ts.map +1 -0
  4. package/dist/errors/crud-errors.js +23 -0
  5. package/dist/errors/crud-errors.js.map +1 -0
  6. package/dist/errors/index.d.ts +16 -0
  7. package/dist/errors/index.d.ts.map +1 -0
  8. package/dist/errors/index.js +12 -0
  9. package/dist/errors/index.js.map +1 -0
  10. package/dist/errors/migration-errors.d.ts +22 -0
  11. package/dist/errors/migration-errors.d.ts.map +1 -0
  12. package/dist/errors/migration-errors.js +14 -0
  13. package/dist/errors/migration-errors.js.map +1 -0
  14. package/dist/errors/plugin-errors.d.ts +15 -0
  15. package/dist/errors/plugin-errors.d.ts.map +1 -0
  16. package/dist/errors/plugin-errors.js +11 -0
  17. package/dist/errors/plugin-errors.js.map +1 -0
  18. package/dist/errors/query-errors.d.ts +31 -0
  19. package/dist/errors/query-errors.d.ts.map +1 -0
  20. package/dist/errors/query-errors.js +11 -0
  21. package/dist/errors/query-errors.js.map +1 -0
  22. package/dist/errors/storage-errors.d.ts +30 -0
  23. package/dist/errors/storage-errors.d.ts.map +1 -0
  24. package/dist/errors/storage-errors.js +11 -0
  25. package/dist/errors/storage-errors.js.map +1 -0
  26. package/dist/factories/crud-factory-with-relationships.d.ts +28 -0
  27. package/dist/factories/crud-factory-with-relationships.d.ts.map +1 -0
  28. package/dist/factories/crud-factory-with-relationships.js +8 -0
  29. package/dist/factories/crud-factory-with-relationships.js.map +1 -0
  30. package/dist/factories/crud-factory.d.ts +25 -0
  31. package/dist/factories/crud-factory.d.ts.map +1 -0
  32. package/dist/factories/crud-factory.js +8 -0
  33. package/dist/factories/crud-factory.js.map +1 -0
  34. package/dist/factories/database-effect.d.ts +241 -0
  35. package/dist/factories/database-effect.d.ts.map +1 -0
  36. package/dist/factories/database-effect.js +859 -0
  37. package/dist/factories/database-effect.js.map +1 -0
  38. package/dist/hooks/hook-runner.d.ts +60 -0
  39. package/dist/hooks/hook-runner.d.ts.map +1 -0
  40. package/dist/hooks/hook-runner.js +107 -0
  41. package/dist/hooks/hook-runner.js.map +1 -0
  42. package/dist/index.d.ts +84 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +110 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/indexes/index-lookup.d.ts +33 -0
  47. package/dist/indexes/index-lookup.d.ts.map +1 -0
  48. package/dist/indexes/index-lookup.js +180 -0
  49. package/dist/indexes/index-lookup.js.map +1 -0
  50. package/dist/indexes/index-manager.d.ts +118 -0
  51. package/dist/indexes/index-manager.d.ts.map +1 -0
  52. package/dist/indexes/index-manager.js +345 -0
  53. package/dist/indexes/index-manager.js.map +1 -0
  54. package/dist/indexes/search-index.d.ts +179 -0
  55. package/dist/indexes/search-index.d.ts.map +1 -0
  56. package/dist/indexes/search-index.js +405 -0
  57. package/dist/indexes/search-index.js.map +1 -0
  58. package/dist/migrations/migration-runner.d.ts +70 -0
  59. package/dist/migrations/migration-runner.d.ts.map +1 -0
  60. package/dist/migrations/migration-runner.js +271 -0
  61. package/dist/migrations/migration-runner.js.map +1 -0
  62. package/dist/migrations/migration-types.d.ts +63 -0
  63. package/dist/migrations/migration-types.d.ts.map +1 -0
  64. package/dist/migrations/migration-types.js +5 -0
  65. package/dist/migrations/migration-types.js.map +1 -0
  66. package/dist/operations/crud/create-with-relationships.d.ts +44 -0
  67. package/dist/operations/crud/create-with-relationships.d.ts.map +1 -0
  68. package/dist/operations/crud/create-with-relationships.js +483 -0
  69. package/dist/operations/crud/create-with-relationships.js.map +1 -0
  70. package/dist/operations/crud/create.d.ts +48 -0
  71. package/dist/operations/crud/create.d.ts.map +1 -0
  72. package/dist/operations/crud/create.js +333 -0
  73. package/dist/operations/crud/create.js.map +1 -0
  74. package/dist/operations/crud/delete-with-relationships.d.ts +63 -0
  75. package/dist/operations/crud/delete-with-relationships.d.ts.map +1 -0
  76. package/dist/operations/crud/delete-with-relationships.js +395 -0
  77. package/dist/operations/crud/delete-with-relationships.js.map +1 -0
  78. package/dist/operations/crud/delete.d.ts +58 -0
  79. package/dist/operations/crud/delete.d.ts.map +1 -0
  80. package/dist/operations/crud/delete.js +267 -0
  81. package/dist/operations/crud/delete.js.map +1 -0
  82. package/dist/operations/crud/unique-check.d.ts +114 -0
  83. package/dist/operations/crud/unique-check.d.ts.map +1 -0
  84. package/dist/operations/crud/unique-check.js +383 -0
  85. package/dist/operations/crud/unique-check.js.map +1 -0
  86. package/dist/operations/crud/update-with-relationships.d.ts +45 -0
  87. package/dist/operations/crud/update-with-relationships.d.ts.map +1 -0
  88. package/dist/operations/crud/update-with-relationships.js +516 -0
  89. package/dist/operations/crud/update-with-relationships.js.map +1 -0
  90. package/dist/operations/crud/update.d.ts +91 -0
  91. package/dist/operations/crud/update.d.ts.map +1 -0
  92. package/dist/operations/crud/update.js +505 -0
  93. package/dist/operations/crud/update.js.map +1 -0
  94. package/dist/operations/crud/upsert.d.ts +52 -0
  95. package/dist/operations/crud/upsert.d.ts.map +1 -0
  96. package/dist/operations/crud/upsert.js +386 -0
  97. package/dist/operations/crud/upsert.js.map +1 -0
  98. package/dist/operations/query/aggregate.d.ts +30 -0
  99. package/dist/operations/query/aggregate.d.ts.map +1 -0
  100. package/dist/operations/query/aggregate.js +227 -0
  101. package/dist/operations/query/aggregate.js.map +1 -0
  102. package/dist/operations/query/cursor-stream.d.ts +18 -0
  103. package/dist/operations/query/cursor-stream.d.ts.map +1 -0
  104. package/dist/operations/query/cursor-stream.js +199 -0
  105. package/dist/operations/query/cursor-stream.js.map +1 -0
  106. package/dist/operations/query/filter-stream.d.ts +12 -0
  107. package/dist/operations/query/filter-stream.d.ts.map +1 -0
  108. package/dist/operations/query/filter-stream.js +167 -0
  109. package/dist/operations/query/filter-stream.js.map +1 -0
  110. package/dist/operations/query/filter.d.ts +13 -0
  111. package/dist/operations/query/filter.d.ts.map +1 -0
  112. package/dist/operations/query/filter.js +267 -0
  113. package/dist/operations/query/filter.js.map +1 -0
  114. package/dist/operations/query/paginate-stream.d.ts +11 -0
  115. package/dist/operations/query/paginate-stream.d.ts.map +1 -0
  116. package/dist/operations/query/paginate-stream.js +22 -0
  117. package/dist/operations/query/paginate-stream.js.map +1 -0
  118. package/dist/operations/query/query-helpers.d.ts +14 -0
  119. package/dist/operations/query/query-helpers.d.ts.map +1 -0
  120. package/dist/operations/query/query-helpers.js +22 -0
  121. package/dist/operations/query/query-helpers.js.map +1 -0
  122. package/dist/operations/query/resolve-computed.d.ts +142 -0
  123. package/dist/operations/query/resolve-computed.d.ts.map +1 -0
  124. package/dist/operations/query/resolve-computed.js +197 -0
  125. package/dist/operations/query/resolve-computed.js.map +1 -0
  126. package/dist/operations/query/search.d.ts +110 -0
  127. package/dist/operations/query/search.d.ts.map +1 -0
  128. package/dist/operations/query/search.js +188 -0
  129. package/dist/operations/query/search.js.map +1 -0
  130. package/dist/operations/query/select-stream.d.ts +27 -0
  131. package/dist/operations/query/select-stream.d.ts.map +1 -0
  132. package/dist/operations/query/select-stream.js +88 -0
  133. package/dist/operations/query/select-stream.js.map +1 -0
  134. package/dist/operations/query/select.d.ts +54 -0
  135. package/dist/operations/query/select.d.ts.map +1 -0
  136. package/dist/operations/query/select.js +159 -0
  137. package/dist/operations/query/select.js.map +1 -0
  138. package/dist/operations/query/sort-stream.d.ts +46 -0
  139. package/dist/operations/query/sort-stream.d.ts.map +1 -0
  140. package/dist/operations/query/sort-stream.js +158 -0
  141. package/dist/operations/query/sort-stream.js.map +1 -0
  142. package/dist/operations/query/sort.d.ts +9 -0
  143. package/dist/operations/query/sort.d.ts.map +1 -0
  144. package/dist/operations/query/sort.js +58 -0
  145. package/dist/operations/query/sort.js.map +1 -0
  146. package/dist/operations/relationships/populate-stream.d.ts +29 -0
  147. package/dist/operations/relationships/populate-stream.d.ts.map +1 -0
  148. package/dist/operations/relationships/populate-stream.js +159 -0
  149. package/dist/operations/relationships/populate-stream.js.map +1 -0
  150. package/dist/operations/relationships/populate.d.ts +15 -0
  151. package/dist/operations/relationships/populate.d.ts.map +1 -0
  152. package/dist/operations/relationships/populate.js +228 -0
  153. package/dist/operations/relationships/populate.js.map +1 -0
  154. package/dist/plugins/plugin-hooks.d.ts +25 -0
  155. package/dist/plugins/plugin-hooks.d.ts.map +1 -0
  156. package/dist/plugins/plugin-hooks.js +64 -0
  157. package/dist/plugins/plugin-hooks.js.map +1 -0
  158. package/dist/plugins/plugin-registry.d.ts +26 -0
  159. package/dist/plugins/plugin-registry.d.ts.map +1 -0
  160. package/dist/plugins/plugin-registry.js +150 -0
  161. package/dist/plugins/plugin-registry.js.map +1 -0
  162. package/dist/plugins/plugin-types.d.ts +95 -0
  163. package/dist/plugins/plugin-types.d.ts.map +1 -0
  164. package/dist/plugins/plugin-types.js +6 -0
  165. package/dist/plugins/plugin-types.js.map +1 -0
  166. package/dist/plugins/plugin-validation.d.ts +49 -0
  167. package/dist/plugins/plugin-validation.d.ts.map +1 -0
  168. package/dist/plugins/plugin-validation.js +295 -0
  169. package/dist/plugins/plugin-validation.js.map +1 -0
  170. package/dist/reactive/change-event.d.ts +44 -0
  171. package/dist/reactive/change-event.d.ts.map +1 -0
  172. package/dist/reactive/change-event.js +49 -0
  173. package/dist/reactive/change-event.js.map +1 -0
  174. package/dist/reactive/change-pubsub.d.ts +32 -0
  175. package/dist/reactive/change-pubsub.d.ts.map +1 -0
  176. package/dist/reactive/change-pubsub.js +31 -0
  177. package/dist/reactive/change-pubsub.js.map +1 -0
  178. package/dist/reactive/evaluate-query.d.ts +62 -0
  179. package/dist/reactive/evaluate-query.d.ts.map +1 -0
  180. package/dist/reactive/evaluate-query.js +57 -0
  181. package/dist/reactive/evaluate-query.js.map +1 -0
  182. package/dist/reactive/watch-by-id.d.ts +53 -0
  183. package/dist/reactive/watch-by-id.d.ts.map +1 -0
  184. package/dist/reactive/watch-by-id.js +55 -0
  185. package/dist/reactive/watch-by-id.js.map +1 -0
  186. package/dist/reactive/watch.d.ts +78 -0
  187. package/dist/reactive/watch.d.ts.map +1 -0
  188. package/dist/reactive/watch.js +133 -0
  189. package/dist/reactive/watch.js.map +1 -0
  190. package/dist/serializers/codecs/hjson.d.ts +33 -0
  191. package/dist/serializers/codecs/hjson.d.ts.map +1 -0
  192. package/dist/serializers/codecs/hjson.js +40 -0
  193. package/dist/serializers/codecs/hjson.js.map +1 -0
  194. package/dist/serializers/codecs/json.d.ts +22 -0
  195. package/dist/serializers/codecs/json.d.ts.map +1 -0
  196. package/dist/serializers/codecs/json.js +28 -0
  197. package/dist/serializers/codecs/json.js.map +1 -0
  198. package/dist/serializers/codecs/json5.d.ts +26 -0
  199. package/dist/serializers/codecs/json5.d.ts.map +1 -0
  200. package/dist/serializers/codecs/json5.js +33 -0
  201. package/dist/serializers/codecs/json5.js.map +1 -0
  202. package/dist/serializers/codecs/jsonc.d.ts +29 -0
  203. package/dist/serializers/codecs/jsonc.d.ts.map +1 -0
  204. package/dist/serializers/codecs/jsonc.js +38 -0
  205. package/dist/serializers/codecs/jsonc.js.map +1 -0
  206. package/dist/serializers/codecs/jsonl.d.ts +17 -0
  207. package/dist/serializers/codecs/jsonl.d.ts.map +1 -0
  208. package/dist/serializers/codecs/jsonl.js +31 -0
  209. package/dist/serializers/codecs/jsonl.js.map +1 -0
  210. package/dist/serializers/codecs/prose.d.ts +419 -0
  211. package/dist/serializers/codecs/prose.d.ts.map +1 -0
  212. package/dist/serializers/codecs/prose.js +1060 -0
  213. package/dist/serializers/codecs/prose.js.map +1 -0
  214. package/dist/serializers/codecs/toml.d.ts +23 -0
  215. package/dist/serializers/codecs/toml.d.ts.map +1 -0
  216. package/dist/serializers/codecs/toml.js +66 -0
  217. package/dist/serializers/codecs/toml.js.map +1 -0
  218. package/dist/serializers/codecs/toon.d.ts +20 -0
  219. package/dist/serializers/codecs/toon.d.ts.map +1 -0
  220. package/dist/serializers/codecs/toon.js +33 -0
  221. package/dist/serializers/codecs/toon.js.map +1 -0
  222. package/dist/serializers/codecs/yaml.d.ts +24 -0
  223. package/dist/serializers/codecs/yaml.d.ts.map +1 -0
  224. package/dist/serializers/codecs/yaml.js +31 -0
  225. package/dist/serializers/codecs/yaml.js.map +1 -0
  226. package/dist/serializers/format-codec.d.ts +53 -0
  227. package/dist/serializers/format-codec.d.ts.map +1 -0
  228. package/dist/serializers/format-codec.js +148 -0
  229. package/dist/serializers/format-codec.js.map +1 -0
  230. package/dist/serializers/presets.d.ts +48 -0
  231. package/dist/serializers/presets.d.ts.map +1 -0
  232. package/dist/serializers/presets.js +72 -0
  233. package/dist/serializers/presets.js.map +1 -0
  234. package/dist/serializers/serializer-service.d.ts +11 -0
  235. package/dist/serializers/serializer-service.d.ts.map +1 -0
  236. package/dist/serializers/serializer-service.js +4 -0
  237. package/dist/serializers/serializer-service.js.map +1 -0
  238. package/dist/state/collection-state.d.ts +19 -0
  239. package/dist/state/collection-state.d.ts.map +1 -0
  240. package/dist/state/collection-state.js +15 -0
  241. package/dist/state/collection-state.js.map +1 -0
  242. package/dist/state/state-operations.d.ts +38 -0
  243. package/dist/state/state-operations.d.ts.map +1 -0
  244. package/dist/state/state-operations.js +65 -0
  245. package/dist/state/state-operations.js.map +1 -0
  246. package/dist/storage/in-memory-adapter-layer.d.ts +16 -0
  247. package/dist/storage/in-memory-adapter-layer.d.ts.map +1 -0
  248. package/dist/storage/in-memory-adapter-layer.js +81 -0
  249. package/dist/storage/in-memory-adapter-layer.js.map +1 -0
  250. package/dist/storage/persistence-effect.d.ts +244 -0
  251. package/dist/storage/persistence-effect.d.ts.map +1 -0
  252. package/dist/storage/persistence-effect.js +551 -0
  253. package/dist/storage/persistence-effect.js.map +1 -0
  254. package/dist/storage/storage-service.d.ts +22 -0
  255. package/dist/storage/storage-service.d.ts.map +1 -0
  256. package/dist/storage/storage-service.js +4 -0
  257. package/dist/storage/storage-service.js.map +1 -0
  258. package/dist/storage/transforms.d.ts +183 -0
  259. package/dist/storage/transforms.d.ts.map +1 -0
  260. package/dist/storage/transforms.js +263 -0
  261. package/dist/storage/transforms.js.map +1 -0
  262. package/dist/transactions/transaction.d.ts +87 -0
  263. package/dist/transactions/transaction.d.ts.map +1 -0
  264. package/dist/transactions/transaction.js +240 -0
  265. package/dist/transactions/transaction.js.map +1 -0
  266. package/dist/types/aggregate-types.d.ts +73 -0
  267. package/dist/types/aggregate-types.d.ts.map +1 -0
  268. package/dist/types/aggregate-types.js +14 -0
  269. package/dist/types/aggregate-types.js.map +1 -0
  270. package/dist/types/computed-types.d.ts +71 -0
  271. package/dist/types/computed-types.d.ts.map +1 -0
  272. package/dist/types/computed-types.js +8 -0
  273. package/dist/types/computed-types.js.map +1 -0
  274. package/dist/types/crud-relationship-types.d.ts +180 -0
  275. package/dist/types/crud-relationship-types.d.ts.map +1 -0
  276. package/dist/types/crud-relationship-types.js +17 -0
  277. package/dist/types/crud-relationship-types.js.map +1 -0
  278. package/dist/types/crud-types.d.ts +343 -0
  279. package/dist/types/crud-types.d.ts.map +1 -0
  280. package/dist/types/crud-types.js +43 -0
  281. package/dist/types/crud-types.js.map +1 -0
  282. package/dist/types/cursor-types.d.ts +52 -0
  283. package/dist/types/cursor-types.d.ts.map +1 -0
  284. package/dist/types/cursor-types.js +2 -0
  285. package/dist/types/cursor-types.js.map +1 -0
  286. package/dist/types/database-config-types.d.ts +196 -0
  287. package/dist/types/database-config-types.d.ts.map +1 -0
  288. package/dist/types/database-config-types.js +11 -0
  289. package/dist/types/database-config-types.js.map +1 -0
  290. package/dist/types/hook-types.d.ts +158 -0
  291. package/dist/types/hook-types.d.ts.map +1 -0
  292. package/dist/types/hook-types.js +6 -0
  293. package/dist/types/hook-types.js.map +1 -0
  294. package/dist/types/index-types.d.ts +42 -0
  295. package/dist/types/index-types.d.ts.map +1 -0
  296. package/dist/types/index-types.js +8 -0
  297. package/dist/types/index-types.js.map +1 -0
  298. package/dist/types/operators.d.ts +5 -0
  299. package/dist/types/operators.d.ts.map +1 -0
  300. package/dist/types/operators.js +297 -0
  301. package/dist/types/operators.js.map +1 -0
  302. package/dist/types/query-overloads.d.ts +54 -0
  303. package/dist/types/query-overloads.d.ts.map +1 -0
  304. package/dist/types/query-overloads.js +3 -0
  305. package/dist/types/query-overloads.js.map +1 -0
  306. package/dist/types/reactive-types.d.ts +75 -0
  307. package/dist/types/reactive-types.d.ts.map +1 -0
  308. package/dist/types/reactive-types.js +7 -0
  309. package/dist/types/reactive-types.js.map +1 -0
  310. package/dist/types/schema-types.d.ts +56 -0
  311. package/dist/types/schema-types.d.ts.map +1 -0
  312. package/dist/types/schema-types.js +8 -0
  313. package/dist/types/schema-types.js.map +1 -0
  314. package/dist/types/search-types.d.ts +82 -0
  315. package/dist/types/search-types.d.ts.map +1 -0
  316. package/dist/types/search-types.js +110 -0
  317. package/dist/types/search-types.js.map +1 -0
  318. package/dist/types/types.d.ts +286 -0
  319. package/dist/types/types.d.ts.map +1 -0
  320. package/dist/types/types.js +2 -0
  321. package/dist/types/types.js.map +1 -0
  322. package/dist/utils/id-generator.d.ts +97 -0
  323. package/dist/utils/id-generator.d.ts.map +1 -0
  324. package/dist/utils/id-generator.js +247 -0
  325. package/dist/utils/id-generator.js.map +1 -0
  326. package/dist/utils/nested-path.d.ts +56 -0
  327. package/dist/utils/nested-path.d.ts.map +1 -0
  328. package/dist/utils/nested-path.js +119 -0
  329. package/dist/utils/nested-path.js.map +1 -0
  330. package/dist/utils/path.d.ts +16 -0
  331. package/dist/utils/path.d.ts.map +1 -0
  332. package/dist/utils/path.js +24 -0
  333. package/dist/utils/path.js.map +1 -0
  334. package/dist/validators/foreign-key.d.ts +49 -0
  335. package/dist/validators/foreign-key.d.ts.map +1 -0
  336. package/dist/validators/foreign-key.js +153 -0
  337. package/dist/validators/foreign-key.js.map +1 -0
  338. package/dist/validators/schema-validator.d.ts +19 -0
  339. package/dist/validators/schema-validator.d.ts.map +1 -0
  340. package/dist/validators/schema-validator.js +34 -0
  341. package/dist/validators/schema-validator.js.map +1 -0
  342. package/package.json +57 -0
@@ -0,0 +1,197 @@
1
+ import { Stream } from "effect";
2
+ /**
3
+ * Resolve computed fields for a single entity.
4
+ *
5
+ * Iterates over the computed config, calling each derivation function with the entity,
6
+ * and returns a new object with the original entity fields plus computed field values.
7
+ *
8
+ * @template T - The entity type (stored fields, possibly with populated relationships)
9
+ * @template C - The computed fields config type
10
+ *
11
+ * @param entity - The entity to resolve computed fields for
12
+ * @param config - The computed fields configuration (field name → derivation function)
13
+ * @returns A new object with entity fields plus computed fields attached
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const book = { id: "1", title: "Dune", year: 1965 }
18
+ * const config = {
19
+ * displayName: (b) => `${b.title} (${b.year})`,
20
+ * isClassic: (b) => b.year < 1980,
21
+ * }
22
+ * const result = resolveComputedFields(book, config)
23
+ * // { id: "1", title: "Dune", year: 1965, displayName: "Dune (1965)", isClassic: true }
24
+ * ```
25
+ */
26
+ export const resolveComputedFields = (entity, config) => {
27
+ const computedValues = {};
28
+ for (const key of Object.keys(config)) {
29
+ const derivationFn = config[key];
30
+ if (typeof derivationFn === "function") {
31
+ computedValues[key] = derivationFn(entity);
32
+ }
33
+ }
34
+ return { ...entity, ...computedValues };
35
+ };
36
+ /**
37
+ * Apply computed field resolution as a Stream combinator.
38
+ * Returns a function that transforms Stream<T> → Stream<WithComputed<T, C>>,
39
+ * mapping the resolution function over each entity.
40
+ *
41
+ * When the config is undefined or has no keys, returns the stream unchanged
42
+ * (no resolution overhead).
43
+ *
44
+ * @template T - The entity type (stored fields, possibly with populated relationships)
45
+ * @template C - The computed fields config type
46
+ *
47
+ * @param config - The computed fields configuration (field name → derivation function), or undefined
48
+ * @returns A stream combinator function
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const config = {
53
+ * displayName: (b) => `${b.title} (${b.year})`,
54
+ * isClassic: (b) => b.year < 1980,
55
+ * }
56
+ * const enrichedStream = stream.pipe(resolveComputedStream(config))
57
+ * // Each entity in the resulting stream has displayName and isClassic attached
58
+ * ```
59
+ */
60
+ export const resolveComputedStream = (config) => (stream) => {
61
+ // When config is empty or undefined, return stream unchanged
62
+ if (config === undefined || Object.keys(config).length === 0) {
63
+ return stream;
64
+ }
65
+ return Stream.map(stream, (entity) => resolveComputedFields(entity, config));
66
+ };
67
+ /**
68
+ * Strip computed field keys from an entity object.
69
+ * Used as a safety net before persistence to ensure computed fields
70
+ * are never written to storage.
71
+ *
72
+ * @template T - The original entity type (stored fields only)
73
+ * @template C - The computed fields config type
74
+ *
75
+ * @param entity - The entity (possibly with computed fields attached)
76
+ * @param config - The computed fields configuration that defines which keys to strip
77
+ * @returns A new object with only stored fields (computed fields removed)
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * const entityWithComputed = {
82
+ * id: "1",
83
+ * title: "Dune",
84
+ * year: 1965,
85
+ * displayName: "Dune (1965)", // computed
86
+ * isClassic: true, // computed
87
+ * }
88
+ * const config = {
89
+ * displayName: (b) => `${b.title} (${b.year})`,
90
+ * isClassic: (b) => b.year < 1980,
91
+ * }
92
+ * const stored = stripComputedFields(entityWithComputed, config)
93
+ * // { id: "1", title: "Dune", year: 1965 }
94
+ * ```
95
+ */
96
+ export const stripComputedFields = (entity, config) => {
97
+ // When config is empty or undefined, return entity unchanged
98
+ if (config === undefined || Object.keys(config).length === 0) {
99
+ return entity;
100
+ }
101
+ const computedKeys = new Set(Object.keys(config));
102
+ const result = {};
103
+ for (const key of Object.keys(entity)) {
104
+ if (!computedKeys.has(key)) {
105
+ result[key] = entity[key];
106
+ }
107
+ }
108
+ return result;
109
+ };
110
+ /**
111
+ * Check if any computed fields are selected based on the select configuration.
112
+ *
113
+ * @param computedConfig - The computed fields configuration
114
+ * @param select - The select configuration (object with true values for selected fields)
115
+ * @returns true if any computed field is selected (or select is undefined meaning all fields)
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * const computedConfig = {
120
+ * displayName: (b) => `${b.title} (${b.year})`,
121
+ * isClassic: (b) => b.year < 1980,
122
+ * }
123
+ *
124
+ * // No select = all fields including computed
125
+ * hasSelectedComputedFields(computedConfig, undefined) // true
126
+ *
127
+ * // Select includes computed field
128
+ * hasSelectedComputedFields(computedConfig, { title: true, displayName: true }) // true
129
+ *
130
+ * // Select excludes all computed fields
131
+ * hasSelectedComputedFields(computedConfig, { title: true, year: true }) // false
132
+ * ```
133
+ */
134
+ export const hasSelectedComputedFields = (computedConfig, select) => {
135
+ // No computed config means no computed fields to select
136
+ if (computedConfig === undefined ||
137
+ Object.keys(computedConfig).length === 0) {
138
+ return false;
139
+ }
140
+ // No select means all fields are selected (including computed)
141
+ if (select === undefined) {
142
+ return true;
143
+ }
144
+ // Check if any computed field key is in the select config
145
+ const computedKeys = Object.keys(computedConfig);
146
+ const selectKeys = Object.keys(select);
147
+ for (const computedKey of computedKeys) {
148
+ if (selectKeys.includes(computedKey) && select[computedKey] === true) {
149
+ return true;
150
+ }
151
+ }
152
+ return false;
153
+ };
154
+ /**
155
+ * Apply computed field resolution with lazy skip optimization.
156
+ *
157
+ * When `select` is provided and has no intersection with computed field keys,
158
+ * bypasses resolution entirely by returning the stream unchanged. This avoids
159
+ * unnecessary computation when only stored fields are needed.
160
+ *
161
+ * @template T - The entity type (stored fields, possibly with populated relationships)
162
+ * @template C - The computed fields config type
163
+ *
164
+ * @param config - The computed fields configuration (field name → derivation function), or undefined
165
+ * @param select - The select configuration (object with true values for selected fields), or undefined
166
+ * @returns A stream combinator function
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * const config = {
171
+ * displayName: (b) => `${b.title} (${b.year})`,
172
+ * isClassic: (b) => b.year < 1980,
173
+ * }
174
+ *
175
+ * // When select includes computed fields, resolution is applied
176
+ * const enrichedStream = stream.pipe(
177
+ * resolveComputedStreamWithLazySkip(config, { title: true, displayName: true })
178
+ * )
179
+ *
180
+ * // When select excludes all computed fields, resolution is skipped
181
+ * const storedOnlyStream = stream.pipe(
182
+ * resolveComputedStreamWithLazySkip(config, { title: true, year: true })
183
+ * )
184
+ * // → stream returned unchanged, no resolution overhead
185
+ * ```
186
+ */
187
+ export const resolveComputedStreamWithLazySkip = (config, select) => (stream) => {
188
+ // Check if any computed fields are selected
189
+ // Cast to unknown first to bypass contravariance check - we're only checking keys
190
+ if (!hasSelectedComputedFields(config, select)) {
191
+ // No computed fields selected, return stream unchanged
192
+ return stream;
193
+ }
194
+ // Computed fields are selected, apply resolution
195
+ return Stream.map(stream, (entity) => resolveComputedFields(entity, config));
196
+ };
197
+ //# sourceMappingURL=resolve-computed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-computed.js","sourceRoot":"","sources":["../../../src/operations/query/resolve-computed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Full-text search operations: tokenization, matching, and relevance scoring.
3
+ *
4
+ * Provides pure functions for text search functionality including
5
+ * tokenization, stop word filtering, and relevance-based ranking.
6
+ */
7
+ /**
8
+ * Tokenize text into normalized tokens.
9
+ *
10
+ * Splits on whitespace, strips leading/trailing punctuation characters,
11
+ * lowercases all tokens, and filters out empty strings.
12
+ *
13
+ * @param text - The text to tokenize
14
+ * @returns Array of normalized tokens
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * tokenize("Gibson, William")
19
+ * // => ["gibson", "william"]
20
+ *
21
+ * tokenize("The Left Hand of Darkness")
22
+ * // => ["the", "left", "hand", "of", "darkness"]
23
+ *
24
+ * tokenize(" ")
25
+ * // => []
26
+ *
27
+ * tokenize("")
28
+ * // => []
29
+ * ```
30
+ */
31
+ export declare function tokenize(text: string): ReadonlyArray<string>;
32
+ /**
33
+ * Tokenize text with optional stop word filtering.
34
+ *
35
+ * Calls `tokenize` then optionally filters out common stop words
36
+ * (articles, prepositions, conjunctions, etc.) that typically don't
37
+ * contribute to search relevance.
38
+ *
39
+ * @param text - The text to tokenize
40
+ * @param removeStopWords - Whether to filter out stop words
41
+ * @returns Array of normalized tokens, optionally without stop words
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * tokenizeWithStopWords("The Left Hand of Darkness", false)
46
+ * // => ["the", "left", "hand", "of", "darkness"]
47
+ *
48
+ * tokenizeWithStopWords("The Left Hand of Darkness", true)
49
+ * // => ["left", "hand", "darkness"]
50
+ * ```
51
+ */
52
+ export declare function tokenizeWithStopWords(text: string, removeStopWords: boolean): ReadonlyArray<string>;
53
+ /**
54
+ * Compute the relevance score for a single field value against query tokens.
55
+ *
56
+ * Scoring uses three factors combined multiplicatively:
57
+ * 1. **Term coverage**: Fraction of query tokens that matched (0..1)
58
+ * 2. **Term frequency (TF)**: How many times query tokens appear in the field
59
+ * 3. **Field length normalization**: Shorter fields score higher for same match
60
+ *
61
+ * Prefix matches (via startsWith) count toward matching but score slightly lower
62
+ * than exact matches to preserve ranking quality.
63
+ *
64
+ * @param fieldValue - The field value to score
65
+ * @param queryTokens - The tokenized search query
66
+ * @returns Relevance score (0 if no tokens match)
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * computeFieldScore("Dune", ["dune"])
71
+ * // => ~1.44 (exact match, short field)
72
+ *
73
+ * computeFieldScore("The Left Hand of Darkness", ["left", "hand"])
74
+ * // => ~0.29 (2/2 coverage, longer field)
75
+ *
76
+ * computeFieldScore("Neuromancer", ["neuro"])
77
+ * // => ~0.72 (prefix match, reduced score)
78
+ *
79
+ * computeFieldScore("Dune", ["xyz"])
80
+ * // => 0 (no match)
81
+ * ```
82
+ */
83
+ export declare function computeFieldScore(fieldValue: string, queryTokens: ReadonlyArray<string>): number;
84
+ /**
85
+ * Compute the total relevance score for an entity across multiple fields.
86
+ *
87
+ * Sums the field scores for all specified fields. Fields that are not
88
+ * present on the entity or are not strings are skipped (score 0).
89
+ *
90
+ * @param entity - The entity to score
91
+ * @param queryTokens - The tokenized search query
92
+ * @param fields - The fields to search across
93
+ * @returns Total relevance score (0 if no fields match)
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * const book = { title: "Dune", author: "Frank Herbert", year: 1965 }
98
+ *
99
+ * computeSearchScore(book, ["dune"], ["title", "author"])
100
+ * // => ~1.44 (matches in title only)
101
+ *
102
+ * computeSearchScore(book, ["herbert", "dune"], ["title", "author"])
103
+ * // => ~2.88 (matches in both fields)
104
+ *
105
+ * computeSearchScore(book, ["xyz"], ["title", "author"])
106
+ * // => 0 (no matches)
107
+ * ```
108
+ */
109
+ export declare function computeSearchScore(entity: Record<string, unknown>, queryTokens: ReadonlyArray<string>, fields: ReadonlyArray<string>): number;
110
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/operations/query/search.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAM5D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACpC,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,OAAO,GACtB,aAAa,CAAC,MAAM,CAAC,CAMvB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,iBAAiB,CAChC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,aAAa,CAAC,MAAM,CAAC,GAChC,MAAM,CAqDR;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,kBAAkB,CACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,WAAW,EAAE,aAAa,CAAC,MAAM,CAAC,EAClC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,GAC3B,MAAM,CAoBR"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Full-text search operations: tokenization, matching, and relevance scoring.
3
+ *
4
+ * Provides pure functions for text search functionality including
5
+ * tokenization, stop word filtering, and relevance-based ranking.
6
+ */
7
+ import { STOP_WORDS } from "../../types/search-types.js";
8
+ // ============================================================================
9
+ // Tokenization
10
+ // ============================================================================
11
+ /**
12
+ * Tokenize text into normalized tokens.
13
+ *
14
+ * Splits on whitespace, strips leading/trailing punctuation characters,
15
+ * lowercases all tokens, and filters out empty strings.
16
+ *
17
+ * @param text - The text to tokenize
18
+ * @returns Array of normalized tokens
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * tokenize("Gibson, William")
23
+ * // => ["gibson", "william"]
24
+ *
25
+ * tokenize("The Left Hand of Darkness")
26
+ * // => ["the", "left", "hand", "of", "darkness"]
27
+ *
28
+ * tokenize(" ")
29
+ * // => []
30
+ *
31
+ * tokenize("")
32
+ * // => []
33
+ * ```
34
+ */
35
+ export function tokenize(text) {
36
+ return text
37
+ .toLowerCase()
38
+ .split(/\s+/)
39
+ .map((t) => t.replace(/^[^\w]+|[^\w]+$/g, ""))
40
+ .filter((t) => t.length > 0);
41
+ }
42
+ /**
43
+ * Tokenize text with optional stop word filtering.
44
+ *
45
+ * Calls `tokenize` then optionally filters out common stop words
46
+ * (articles, prepositions, conjunctions, etc.) that typically don't
47
+ * contribute to search relevance.
48
+ *
49
+ * @param text - The text to tokenize
50
+ * @param removeStopWords - Whether to filter out stop words
51
+ * @returns Array of normalized tokens, optionally without stop words
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * tokenizeWithStopWords("The Left Hand of Darkness", false)
56
+ * // => ["the", "left", "hand", "of", "darkness"]
57
+ *
58
+ * tokenizeWithStopWords("The Left Hand of Darkness", true)
59
+ * // => ["left", "hand", "darkness"]
60
+ * ```
61
+ */
62
+ export function tokenizeWithStopWords(text, removeStopWords) {
63
+ const tokens = tokenize(text);
64
+ if (!removeStopWords) {
65
+ return tokens;
66
+ }
67
+ return tokens.filter((token) => !STOP_WORDS.has(token));
68
+ }
69
+ // ============================================================================
70
+ // Relevance Scoring
71
+ // ============================================================================
72
+ /**
73
+ * Compute the relevance score for a single field value against query tokens.
74
+ *
75
+ * Scoring uses three factors combined multiplicatively:
76
+ * 1. **Term coverage**: Fraction of query tokens that matched (0..1)
77
+ * 2. **Term frequency (TF)**: How many times query tokens appear in the field
78
+ * 3. **Field length normalization**: Shorter fields score higher for same match
79
+ *
80
+ * Prefix matches (via startsWith) count toward matching but score slightly lower
81
+ * than exact matches to preserve ranking quality.
82
+ *
83
+ * @param fieldValue - The field value to score
84
+ * @param queryTokens - The tokenized search query
85
+ * @returns Relevance score (0 if no tokens match)
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * computeFieldScore("Dune", ["dune"])
90
+ * // => ~1.44 (exact match, short field)
91
+ *
92
+ * computeFieldScore("The Left Hand of Darkness", ["left", "hand"])
93
+ * // => ~0.29 (2/2 coverage, longer field)
94
+ *
95
+ * computeFieldScore("Neuromancer", ["neuro"])
96
+ * // => ~0.72 (prefix match, reduced score)
97
+ *
98
+ * computeFieldScore("Dune", ["xyz"])
99
+ * // => 0 (no match)
100
+ * ```
101
+ */
102
+ export function computeFieldScore(fieldValue, queryTokens) {
103
+ // Handle edge cases
104
+ if (queryTokens.length === 0) {
105
+ return 0;
106
+ }
107
+ const fieldTokens = tokenize(fieldValue);
108
+ if (fieldTokens.length === 0) {
109
+ return 0;
110
+ }
111
+ // Count matched tokens and compute term frequency
112
+ let matchedTermCount = 0;
113
+ let termFrequencySum = 0;
114
+ for (const queryToken of queryTokens) {
115
+ let tokenMatched = false;
116
+ let tokenFrequency = 0;
117
+ for (const fieldToken of fieldTokens) {
118
+ if (fieldToken === queryToken) {
119
+ // Exact match: full weight
120
+ tokenFrequency += 1;
121
+ tokenMatched = true;
122
+ }
123
+ else if (fieldToken.startsWith(queryToken)) {
124
+ // Prefix match: reduced weight (0.5x)
125
+ tokenFrequency += 0.5;
126
+ tokenMatched = true;
127
+ }
128
+ }
129
+ if (tokenMatched) {
130
+ matchedTermCount += 1;
131
+ termFrequencySum += tokenFrequency;
132
+ }
133
+ }
134
+ // No matches means score of 0
135
+ if (matchedTermCount === 0) {
136
+ return 0;
137
+ }
138
+ // Compute score using the three factors:
139
+ // 1. Coverage: fraction of query tokens that matched
140
+ const coverage = matchedTermCount / queryTokens.length;
141
+ // 2. TF boost: more occurrences = higher score
142
+ const tfBoost = 1 + termFrequencySum / fieldTokens.length;
143
+ // 3. Length normalization: shorter fields score higher
144
+ const lengthNorm = 1 / Math.log(1 + fieldTokens.length);
145
+ return coverage * tfBoost * lengthNorm;
146
+ }
147
+ /**
148
+ * Compute the total relevance score for an entity across multiple fields.
149
+ *
150
+ * Sums the field scores for all specified fields. Fields that are not
151
+ * present on the entity or are not strings are skipped (score 0).
152
+ *
153
+ * @param entity - The entity to score
154
+ * @param queryTokens - The tokenized search query
155
+ * @param fields - The fields to search across
156
+ * @returns Total relevance score (0 if no fields match)
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * const book = { title: "Dune", author: "Frank Herbert", year: 1965 }
161
+ *
162
+ * computeSearchScore(book, ["dune"], ["title", "author"])
163
+ * // => ~1.44 (matches in title only)
164
+ *
165
+ * computeSearchScore(book, ["herbert", "dune"], ["title", "author"])
166
+ * // => ~2.88 (matches in both fields)
167
+ *
168
+ * computeSearchScore(book, ["xyz"], ["title", "author"])
169
+ * // => 0 (no matches)
170
+ * ```
171
+ */
172
+ export function computeSearchScore(entity, queryTokens, fields) {
173
+ // Handle edge cases
174
+ if (queryTokens.length === 0 || fields.length === 0) {
175
+ return 0;
176
+ }
177
+ let totalScore = 0;
178
+ for (const field of fields) {
179
+ const fieldValue = entity[field];
180
+ // Skip non-string fields
181
+ if (typeof fieldValue !== "string") {
182
+ continue;
183
+ }
184
+ totalScore += computeFieldScore(fieldValue, queryTokens);
185
+ }
186
+ return totalScore;
187
+ }
188
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../../src/operations/query/search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { Stream } from "effect";
2
+ /**
3
+ * A select configuration can be either:
4
+ * - Object-based: `{ name: true, email: true, company: { name: true } }`
5
+ * - Array-based: `["name", "email"]` (picks those fields)
6
+ */
7
+ type SelectConfig = Record<string, unknown> | ReadonlyArray<string>;
8
+ /**
9
+ * Apply a field selection as a Stream combinator.
10
+ * Returns a function that transforms Stream<T> → Stream<T>, projecting each entity
11
+ * to only the selected fields.
12
+ *
13
+ * Supports both object-based (`{ name: true, email: true }`) and
14
+ * array-based (`["name", "email"]`) selection configs.
15
+ * Nested selection on populated relationships is supported via object-based config:
16
+ * `{ name: true, company: { name: true } }`.
17
+ */
18
+ export declare const applySelect: <T extends Record<string, unknown>>(select: SelectConfig | undefined) => <E, R>(stream: Stream.Stream<T, E, R>) => Stream.Stream<T, E, R>;
19
+ /**
20
+ * Apply a field selection to an array of items.
21
+ *
22
+ * This is used by cursor pagination where items are collected before selection.
23
+ * Supports both object-based and array-based selection configs.
24
+ */
25
+ export declare const applySelectToArray: <T extends Record<string, unknown>>(items: ReadonlyArray<T>, select: SelectConfig | undefined) => ReadonlyArray<T>;
26
+ export {};
27
+ //# sourceMappingURL=select-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-stream.d.ts","sourceRoot":"","sources":["../../../src/operations/query/select-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC;;;;GAIG;AACH,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;AAsDpE;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,GACtB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,YAAY,GAAG,SAAS,MACnE,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAmB5D,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnE,OAAO,aAAa,CAAC,CAAC,CAAC,EACvB,QAAQ,YAAY,GAAG,SAAS,KAC9B,aAAa,CAAC,CAAC,CAcjB,CAAC"}
@@ -0,0 +1,88 @@
1
+ import { Stream } from "effect";
2
+ /**
3
+ * Type guard to check if a value is a record (non-null, non-array object).
4
+ */
5
+ function isRecord(value) {
6
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7
+ }
8
+ /**
9
+ * Apply object-based field selection to a single item.
10
+ * Handles nested selection for populated relationships (both objects and arrays).
11
+ */
12
+ function applyObjectSelect(item, selection) {
13
+ const result = {};
14
+ for (const [key, value] of Object.entries(selection)) {
15
+ if (!(key in item))
16
+ continue;
17
+ if (value === true) {
18
+ result[key] = item[key];
19
+ }
20
+ else if (isRecord(value)) {
21
+ // Nested selection for populated relationships
22
+ const nestedData = item[key];
23
+ if (Array.isArray(nestedData)) {
24
+ result[key] = nestedData
25
+ .filter(isRecord)
26
+ .map((nested) => applyObjectSelect(nested, value));
27
+ }
28
+ else if (isRecord(nestedData)) {
29
+ result[key] = applyObjectSelect(nestedData, value);
30
+ }
31
+ }
32
+ }
33
+ return result;
34
+ }
35
+ /**
36
+ * Convert an array-based select config to an object-based one.
37
+ * `["name", "email"]` becomes `{ name: true, email: true }`.
38
+ */
39
+ function arraySelectToObject(fields) {
40
+ const result = {};
41
+ for (const field of fields) {
42
+ result[field] = true;
43
+ }
44
+ return result;
45
+ }
46
+ /**
47
+ * Apply a field selection as a Stream combinator.
48
+ * Returns a function that transforms Stream<T> → Stream<T>, projecting each entity
49
+ * to only the selected fields.
50
+ *
51
+ * Supports both object-based (`{ name: true, email: true }`) and
52
+ * array-based (`["name", "email"]`) selection configs.
53
+ * Nested selection on populated relationships is supported via object-based config:
54
+ * `{ name: true, company: { name: true } }`.
55
+ */
56
+ export const applySelect = (select) => (stream) => {
57
+ if (select === undefined || select === null)
58
+ return stream;
59
+ if (Array.isArray(select)) {
60
+ if (select.length === 0)
61
+ return stream;
62
+ const objectSelect = arraySelectToObject(select);
63
+ return Stream.map(stream, (item) => applyObjectSelect(item, objectSelect));
64
+ }
65
+ if (isRecord(select) && Object.keys(select).length === 0)
66
+ return stream;
67
+ return Stream.map(stream, (item) => applyObjectSelect(item, select));
68
+ };
69
+ /**
70
+ * Apply a field selection to an array of items.
71
+ *
72
+ * This is used by cursor pagination where items are collected before selection.
73
+ * Supports both object-based and array-based selection configs.
74
+ */
75
+ export const applySelectToArray = (items, select) => {
76
+ if (select === undefined || select === null)
77
+ return items;
78
+ if (Array.isArray(select)) {
79
+ if (select.length === 0)
80
+ return items;
81
+ const objectSelect = arraySelectToObject(select);
82
+ return items.map((item) => applyObjectSelect(item, objectSelect));
83
+ }
84
+ if (isRecord(select) && Object.keys(select).length === 0)
85
+ return items;
86
+ return items.map((item) => applyObjectSelect(item, select));
87
+ };
88
+ //# sourceMappingURL=select-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-stream.js","sourceRoot":"","sources":["../../../src/operations/query/select-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAShC;;GAEG;AACH,SAAS,QAAQ,CAAC,KAAc;IAC/B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CACzB,IAA6B,EAC7B,SAAkC;IAElC,MAAM,MAAM,GAA4B,EAAE,CAAC;IAE3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACtD,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC;YAAE,SAAS;QAE7B,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACpB,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;aAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,+CAA+C;YAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC/B,MAAM,CAAC,GAAG,CAAC,GAAG,UAAU;qBACtB,MAAM,CAAC,QAAQ,CAAC;qBAChB,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;YACrD,CAAC;iBAAM,IAAI,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjC,MAAM,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;YACpD,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAC3B,MAA6B;IAE7B,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IACtB,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,WAAW,GACvB,CAAoC,MAAgC,EAAE,EAAE,CACxE,CAAO,MAA8B,EAA0B,EAAE;IAChE,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAE3D,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC;QACvC,MAAM,YAAY,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;QACjD,OAAO,MAAM,CAAC,GAAG,CAChB,MAAM,EACN,CAAC,IAAO,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,EAAE,YAAY,CAAM,CACvD,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IAExE,OAAO,MAAM,CAAC,GAAG,CAChB,MAAM,EACN,CAAC,IAAO,EAAE,EAAE,CACX,iBAAiB,CAAC,IAAI,EAAE,MAAiC,CAAM,CAChE,CAAC;AACH,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CACjC,KAAuB,EACvB,MAAgC,EACb,EAAE;IACrB,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAE1D,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACtC,MAAM,YAAY,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;QACjD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,EAAE,YAAY,CAAM,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAEvE,OAAO,KAAK,CAAC,GAAG,CACf,CAAC,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,EAAE,MAAiC,CAAM,CACzE,CAAC;AACH,CAAC,CAAC"}