@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,383 @@
1
+ /**
2
+ * Unique constraint checking utilities for CRUD operations.
3
+ *
4
+ * Handles both single-field constraints (e.g., "email") and compound constraints
5
+ * (e.g., ["userId", "settingKey"]). All constraints are normalized to arrays internally.
6
+ */
7
+ // ============================================================================
8
+ // Constraint Normalization
9
+ // ============================================================================
10
+ /**
11
+ * Normalize unique fields configuration to a consistent array-of-arrays format.
12
+ *
13
+ * Converts:
14
+ * ["email", ["userId", "settingKey"]]
15
+ * To:
16
+ * [["email"], ["userId", "settingKey"]]
17
+ *
18
+ * This allows a single code path to handle both single and compound constraints.
19
+ *
20
+ * @param uniqueFields - Configuration from CollectionConfig.uniqueFields
21
+ * @returns Normalized constraints where each constraint is an array of field names
22
+ */
23
+ export const normalizeConstraints = (uniqueFields) => {
24
+ if (!uniqueFields || uniqueFields.length === 0) {
25
+ return [];
26
+ }
27
+ return uniqueFields.map((constraint) => typeof constraint === "string" ? [constraint] : constraint);
28
+ };
29
+ // ============================================================================
30
+ // Unique Constraint Checking
31
+ // ============================================================================
32
+ import { Effect } from "effect";
33
+ import { UniqueConstraintError, ValidationError, } from "../../errors/crud-errors.js";
34
+ /**
35
+ * Check for unique constraint violations when creating or updating an entity.
36
+ *
37
+ * For each normalized constraint (array of field names):
38
+ * 1. Extract the values for all fields in the constraint from the entity
39
+ * 2. Skip if any field value is null or undefined (nulls are not unique-checked)
40
+ * 3. Check if any existing entity (excluding the same ID) has matching values for ALL fields
41
+ * 4. Fail-fast on first violation with UniqueConstraintError
42
+ *
43
+ * @param entity - The entity being created or updated
44
+ * @param existingMap - Current state of the collection
45
+ * @param constraints - Normalized constraints (array of field name arrays)
46
+ * @param collectionName - Name of the collection for error messages
47
+ * @returns Effect that succeeds with void or fails with UniqueConstraintError
48
+ */
49
+ export const checkUniqueConstraints = (entity, existingMap, constraints, collectionName) => {
50
+ // No constraints configured — nothing to check
51
+ if (constraints.length === 0) {
52
+ return Effect.void;
53
+ }
54
+ const entityRecord = entity;
55
+ for (const constraintFields of constraints) {
56
+ // Extract values for this constraint
57
+ const constraintValues = {};
58
+ let hasNullOrUndefined = false;
59
+ for (const field of constraintFields) {
60
+ const value = entityRecord[field];
61
+ if (value === null || value === undefined) {
62
+ hasNullOrUndefined = true;
63
+ break;
64
+ }
65
+ constraintValues[field] = value;
66
+ }
67
+ // Skip constraint if any field is null/undefined
68
+ if (hasNullOrUndefined) {
69
+ continue;
70
+ }
71
+ // Check against existing entities
72
+ for (const [existingId, existing] of existingMap) {
73
+ // Exclude the entity itself (for updates)
74
+ if (existingId === entity.id) {
75
+ continue;
76
+ }
77
+ const existingRecord = existing;
78
+ // Check if ALL fields in the constraint match
79
+ let allFieldsMatch = true;
80
+ for (const field of constraintFields) {
81
+ if (existingRecord[field] !== constraintValues[field]) {
82
+ allFieldsMatch = false;
83
+ break;
84
+ }
85
+ }
86
+ if (allFieldsMatch) {
87
+ // Generate constraint name: "unique_" + fields joined by "_"
88
+ const constraintName = `unique_${constraintFields.join("_")}`;
89
+ return Effect.fail(new UniqueConstraintError({
90
+ collection: collectionName,
91
+ constraint: constraintName,
92
+ fields: constraintFields,
93
+ values: constraintValues,
94
+ existingId,
95
+ message: `Unique constraint violation on ${collectionName}: ${constraintName} (${constraintFields.join(", ")}) = ${JSON.stringify(constraintValues)} already exists (id: ${existingId})`,
96
+ }));
97
+ }
98
+ }
99
+ }
100
+ return Effect.void;
101
+ };
102
+ /**
103
+ * Check for unique constraint violations when creating multiple entities in a batch.
104
+ *
105
+ * This function performs two types of checks:
106
+ * 1. Each entity against the existing map (same as checkUniqueConstraints)
107
+ * 2. Each entity against other entities in the batch (inter-batch check)
108
+ *
109
+ * The inter-batch check ensures that if entities at index 3 and 7 both have
110
+ * email: "alice@example.com", entity 7 fails (the later one in the batch).
111
+ *
112
+ * @param entities - The entities being created in the batch
113
+ * @param existingMap - Current state of the collection
114
+ * @param constraints - Normalized constraints (array of field name arrays)
115
+ * @param collectionName - Name of the collection for error messages
116
+ * @returns Effect that succeeds with void or fails with UniqueConstraintError
117
+ */
118
+ export const checkBatchUniqueConstraints = (entities, existingMap, constraints, collectionName) => {
119
+ // No constraints configured — nothing to check
120
+ if (constraints.length === 0) {
121
+ return Effect.void;
122
+ }
123
+ // No entities to check
124
+ if (entities.length === 0) {
125
+ return Effect.void;
126
+ }
127
+ // Build a lookup index from entities processed so far in the batch
128
+ // Key: constraintName + ":" + serialized values, Value: entity id
129
+ const batchIndex = new Map();
130
+ for (const entity of entities) {
131
+ const entityRecord = entity;
132
+ for (const constraintFields of constraints) {
133
+ // Extract values for this constraint
134
+ const constraintValues = {};
135
+ let hasNullOrUndefined = false;
136
+ for (const field of constraintFields) {
137
+ const value = entityRecord[field];
138
+ if (value === null || value === undefined) {
139
+ hasNullOrUndefined = true;
140
+ break;
141
+ }
142
+ constraintValues[field] = value;
143
+ }
144
+ // Skip constraint if any field is null/undefined
145
+ if (hasNullOrUndefined) {
146
+ continue;
147
+ }
148
+ const constraintName = `unique_${constraintFields.join("_")}`;
149
+ // Create a stable key for this constraint+values combination
150
+ const valuesKey = constraintFields
151
+ .map((f) => constraintValues[f])
152
+ .join("\0");
153
+ const indexKey = `${constraintName}:${valuesKey}`;
154
+ // Check against existing entities in the collection
155
+ for (const [existingId, existing] of existingMap) {
156
+ // Exclude the entity itself (for updates)
157
+ if (existingId === entity.id) {
158
+ continue;
159
+ }
160
+ const existingRecord = existing;
161
+ // Check if ALL fields in the constraint match
162
+ let allFieldsMatch = true;
163
+ for (const field of constraintFields) {
164
+ if (existingRecord[field] !== constraintValues[field]) {
165
+ allFieldsMatch = false;
166
+ break;
167
+ }
168
+ }
169
+ if (allFieldsMatch) {
170
+ return Effect.fail(new UniqueConstraintError({
171
+ collection: collectionName,
172
+ constraint: constraintName,
173
+ fields: constraintFields,
174
+ values: constraintValues,
175
+ existingId,
176
+ message: `Unique constraint violation on ${collectionName}: ${constraintName} (${constraintFields.join(", ")}) = ${JSON.stringify(constraintValues)} already exists (id: ${existingId})`,
177
+ }));
178
+ }
179
+ }
180
+ // Check against entities already processed in this batch
181
+ const conflictingId = batchIndex.get(indexKey);
182
+ if (conflictingId !== undefined && conflictingId !== entity.id) {
183
+ return Effect.fail(new UniqueConstraintError({
184
+ collection: collectionName,
185
+ constraint: constraintName,
186
+ fields: constraintFields,
187
+ values: constraintValues,
188
+ existingId: conflictingId,
189
+ message: `Unique constraint violation on ${collectionName}: ${constraintName} (${constraintFields.join(", ")}) = ${JSON.stringify(constraintValues)} already exists in batch (id: ${conflictingId})`,
190
+ }));
191
+ }
192
+ // Add this entity to the batch index
193
+ batchIndex.set(indexKey, entity.id);
194
+ }
195
+ }
196
+ return Effect.void;
197
+ };
198
+ // ============================================================================
199
+ // Per-Entity Unique Constraint Checking (for skipDuplicates support)
200
+ // ============================================================================
201
+ /**
202
+ * Check a single entity against unique constraints, including a batch index of
203
+ * previously-checked entities. Used by createMany with skipDuplicates.
204
+ *
205
+ * @param entity - The entity being checked
206
+ * @param entityRecord - The entity as a record for field access
207
+ * @param existingMap - Current state of the collection
208
+ * @param constraints - Normalized constraints
209
+ * @param collectionName - Name of the collection for error messages
210
+ * @param batchIndex - Map of constraint keys to entity IDs from prior entities in the batch
211
+ * @returns Effect that succeeds with void or fails with UniqueConstraintError
212
+ */
213
+ export const checkEntityUniqueConstraints = (entity, entityRecord, existingMap, constraints, collectionName, batchIndex) => {
214
+ // No constraints configured — nothing to check
215
+ if (constraints.length === 0) {
216
+ return Effect.void;
217
+ }
218
+ for (const constraintFields of constraints) {
219
+ // Extract values for this constraint
220
+ const constraintValues = {};
221
+ let hasNullOrUndefined = false;
222
+ for (const field of constraintFields) {
223
+ const value = entityRecord[field];
224
+ if (value === null || value === undefined) {
225
+ hasNullOrUndefined = true;
226
+ break;
227
+ }
228
+ constraintValues[field] = value;
229
+ }
230
+ // Skip constraint if any field is null/undefined
231
+ if (hasNullOrUndefined) {
232
+ continue;
233
+ }
234
+ const constraintName = `unique_${constraintFields.join("_")}`;
235
+ // Create a stable key for this constraint+values combination
236
+ const valuesKey = constraintFields
237
+ .map((f) => constraintValues[f])
238
+ .join("\0");
239
+ const indexKey = `${constraintName}:${valuesKey}`;
240
+ // Check against existing entities in the collection
241
+ for (const [existingId, existing] of existingMap) {
242
+ // Exclude the entity itself (for updates)
243
+ if (existingId === entity.id) {
244
+ continue;
245
+ }
246
+ const existingRecord = existing;
247
+ // Check if ALL fields in the constraint match
248
+ let allFieldsMatch = true;
249
+ for (const field of constraintFields) {
250
+ if (existingRecord[field] !== constraintValues[field]) {
251
+ allFieldsMatch = false;
252
+ break;
253
+ }
254
+ }
255
+ if (allFieldsMatch) {
256
+ return Effect.fail(new UniqueConstraintError({
257
+ collection: collectionName,
258
+ constraint: constraintName,
259
+ fields: constraintFields,
260
+ values: constraintValues,
261
+ existingId,
262
+ message: `Unique constraint violation on ${collectionName}: ${constraintName} (${constraintFields.join(", ")}) = ${JSON.stringify(constraintValues)} already exists (id: ${existingId})`,
263
+ }));
264
+ }
265
+ }
266
+ // Check against entities already processed in this batch
267
+ const conflictingId = batchIndex.get(indexKey);
268
+ if (conflictingId !== undefined && conflictingId !== entity.id) {
269
+ return Effect.fail(new UniqueConstraintError({
270
+ collection: collectionName,
271
+ constraint: constraintName,
272
+ fields: constraintFields,
273
+ values: constraintValues,
274
+ existingId: conflictingId,
275
+ message: `Unique constraint violation on ${collectionName}: ${constraintName} (${constraintFields.join(", ")}) = ${JSON.stringify(constraintValues)} already exists in batch (id: ${conflictingId})`,
276
+ }));
277
+ }
278
+ }
279
+ return Effect.void;
280
+ };
281
+ /**
282
+ * Add an entity's unique constraint values to the batch index.
283
+ * Called after an entity passes unique constraint checks.
284
+ *
285
+ * @param entity - The entity to add
286
+ * @param entityRecord - The entity as a record for field access
287
+ * @param constraints - Normalized constraints
288
+ * @param batchIndex - Map to populate with constraint keys -> entity IDs
289
+ */
290
+ export const addEntityToBatchIndex = (entity, entityRecord, constraints, batchIndex) => {
291
+ for (const constraintFields of constraints) {
292
+ // Extract values for this constraint
293
+ const constraintValues = {};
294
+ let hasNullOrUndefined = false;
295
+ for (const field of constraintFields) {
296
+ const value = entityRecord[field];
297
+ if (value === null || value === undefined) {
298
+ hasNullOrUndefined = true;
299
+ break;
300
+ }
301
+ constraintValues[field] = value;
302
+ }
303
+ // Skip constraint if any field is null/undefined
304
+ if (hasNullOrUndefined) {
305
+ continue;
306
+ }
307
+ const constraintName = `unique_${constraintFields.join("_")}`;
308
+ const valuesKey = constraintFields
309
+ .map((f) => constraintValues[f])
310
+ .join("\0");
311
+ const indexKey = `${constraintName}:${valuesKey}`;
312
+ // Add this entity to the batch index
313
+ batchIndex.set(indexKey, entity.id);
314
+ }
315
+ };
316
+ // ============================================================================
317
+ // Upsert Where Clause Validation
318
+ // ============================================================================
319
+ /**
320
+ * Validate that an upsert where clause targets a declared unique field or id.
321
+ *
322
+ * The where clause must fully cover at least one declared constraint:
323
+ * - `{ id: "..." }` — always valid (id is implicitly unique)
324
+ * - `{ email: "..." }` — valid if `[["email"]]` is in constraints
325
+ * - `{ userId: "u1", settingKey: "theme" }` — valid if `[["userId", "settingKey"]]` is in constraints
326
+ * - Extra fields beyond the constraint are allowed (for additional filtering)
327
+ *
328
+ * If no constraint is fully covered by the where clause, fail with ValidationError
329
+ * listing the valid unique fields.
330
+ *
331
+ * @param where - The where clause object from upsert
332
+ * @param constraints - Normalized constraints (array of field name arrays)
333
+ * @param collectionName - Name of the collection for error messages
334
+ * @returns Effect that succeeds with void or fails with ValidationError
335
+ */
336
+ export const validateUpsertWhere = (where, constraints, collectionName) => {
337
+ const whereKeys = Object.keys(where);
338
+ // `id` is always a valid constraint (implicitly unique)
339
+ if (whereKeys.includes("id")) {
340
+ return Effect.void;
341
+ }
342
+ // Check if where keys cover at least one declared constraint
343
+ for (const constraintFields of constraints) {
344
+ // All fields in the constraint must be present in where keys
345
+ const coversConstraint = constraintFields.every((field) => whereKeys.includes(field));
346
+ if (coversConstraint) {
347
+ return Effect.void;
348
+ }
349
+ }
350
+ // No constraint is covered — build error message
351
+ const validFields = buildValidFieldsDescription(constraints);
352
+ return Effect.fail(new ValidationError({
353
+ message: `Upsert where clause must target a unique field or id`,
354
+ issues: [
355
+ {
356
+ field: "where",
357
+ message: `Where clause does not match any declared unique field in collection '${collectionName}'. Valid unique fields: ${validFields}`,
358
+ value: where,
359
+ },
360
+ ],
361
+ }));
362
+ };
363
+ /**
364
+ * Build a human-readable description of valid unique fields for error messages.
365
+ *
366
+ * @param constraints - Normalized constraints
367
+ * @returns String like "email, username" or "(userId, settingKey)" for compounds
368
+ */
369
+ const buildValidFieldsDescription = (constraints) => {
370
+ if (constraints.length === 0) {
371
+ return "id";
372
+ }
373
+ const descriptions = constraints.map((constraintFields) => {
374
+ if (constraintFields.length === 1) {
375
+ return constraintFields[0];
376
+ }
377
+ // Compound constraint: show as tuple notation
378
+ return `(${constraintFields.join(", ")})`;
379
+ });
380
+ // Always include id as valid
381
+ return [...descriptions, "id"].join(", ");
382
+ };
383
+ //# sourceMappingURL=unique-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unique-check.js","sourceRoot":"","sources":["../../../src/operations/crud/unique-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Effect-based update operations with relationship support.
3
+ *
4
+ * Uses Ref<ReadonlyMap> for atomic state mutation, Effect Schema for validation,
5
+ * and typed errors. Supports $disconnect, $connect, $update, $delete, and $set
6
+ * operations for both ref (single) and inverse (many) relationship types.
7
+ */
8
+ import { Effect, PubSub, Ref, type Schema } from "effect";
9
+ import { ForeignKeyError, NotFoundError, type OperationError, ValidationError } from "../../errors/crud-errors.js";
10
+ import type { ComputedFieldsConfig } from "../../types/computed-types.js";
11
+ import type { UpdateWithRelationshipsInput } from "../../types/crud-relationship-types.js";
12
+ import type { ChangeEvent } from "../../types/reactive-types.js";
13
+ import type { RelationshipDef } from "../../types/types.js";
14
+ type HasId = {
15
+ readonly id: string;
16
+ };
17
+ type RelationshipConfig = {
18
+ readonly type: "ref" | "inverse";
19
+ readonly target?: string;
20
+ readonly __targetCollection?: string;
21
+ readonly foreignKey?: string;
22
+ };
23
+ type CollectionConfig = {
24
+ readonly schema: Schema.Schema<HasId, unknown>;
25
+ readonly relationships: Record<string, RelationshipConfig>;
26
+ };
27
+ type DatabaseConfig = Record<string, CollectionConfig>;
28
+ /**
29
+ * Update a single entity with relationship support.
30
+ *
31
+ * Steps:
32
+ * 1. Strip computed field keys from input (they are derived, not stored)
33
+ * 2. Look up existing entity by ID
34
+ * 3. Parse relationship operations from input
35
+ * 4. Extract base entity updates (non-relationship fields)
36
+ * 5. Process $disconnect: set FK to null (ref) or update inverse entities
37
+ * 6. Process $connect: set FK (ref) or update inverse entity FKs
38
+ * 7. Process $update: update related entities in target collections
39
+ * 8. Process $delete: disconnect specific inverse entities
40
+ * 9. Process $set: replace all inverse relationships
41
+ * 10. Merge base updates, validate, and update the entity
42
+ */
43
+ export declare const updateWithRelationships: <T extends HasId, I = T>(collectionName: string, schema: Schema.Schema<T, I>, relationships: Record<string, RelationshipConfig>, ref: Ref.Ref<ReadonlyMap<string, T>>, stateRefs: Record<string, Ref.Ref<ReadonlyMap<string, HasId>>>, dbConfig: DatabaseConfig, computed?: ComputedFieldsConfig<unknown>, changePubSub?: PubSub.PubSub<ChangeEvent>) => (id: string, input: UpdateWithRelationshipsInput<T, Record<string, RelationshipDef>>) => Effect.Effect<T, ValidationError | NotFoundError | ForeignKeyError | OperationError>;
44
+ export {};
45
+ //# sourceMappingURL=update-with-relationships.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-with-relationships.d.ts","sourceRoot":"","sources":["../../../src/operations/crud/update-with-relationships.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EACN,eAAe,EACf,aAAa,EACb,KAAK,cAAc,EACnB,eAAe,EACf,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,KAAK,EAIX,4BAA4B,EAC5B,MAAM,wCAAwC,CAAC;AAGhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAQ5D,KAAK,KAAK,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC,KAAK,kBAAkB,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC/C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CAC3D,CAAC;AAEF,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AA0avD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,GAClC,CAAC,SAAS,KAAK,EAAE,CAAC,GAAG,CAAC,EACtB,gBAAgB,MAAM,EACtB,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAC3B,eAAe,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,EACjD,KAAK,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EACpC,WAAW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAC9D,UAAU,cAAc,EACxB,WAAW,oBAAoB,CAAC,OAAO,CAAC,EACxC,eAAe,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,MAGzC,IAAI,MAAM,EACV,OAAO,4BAA4B,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,KACrE,MAAM,CAAC,MAAM,CACf,CAAC,EACD,eAAe,GAAG,aAAa,GAAG,eAAe,GAAG,cAAc,CAqThE,CAAC"}