grafeo-ogm 1.0.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 (333) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1121 -0
  3. package/dist/cjs/compilers/fulltext.compiler.d.ts +67 -0
  4. package/dist/cjs/compilers/fulltext.compiler.d.ts.map +1 -0
  5. package/dist/cjs/compilers/fulltext.compiler.js +254 -0
  6. package/dist/cjs/compilers/fulltext.compiler.js.map +1 -0
  7. package/dist/cjs/compilers/mutation.compiler.d.ts +126 -0
  8. package/dist/cjs/compilers/mutation.compiler.d.ts.map +1 -0
  9. package/dist/cjs/compilers/mutation.compiler.js +1239 -0
  10. package/dist/cjs/compilers/mutation.compiler.js.map +1 -0
  11. package/dist/cjs/compilers/select-normalizer.d.ts +50 -0
  12. package/dist/cjs/compilers/select-normalizer.d.ts.map +1 -0
  13. package/dist/cjs/compilers/select-normalizer.js +225 -0
  14. package/dist/cjs/compilers/select-normalizer.js.map +1 -0
  15. package/dist/cjs/compilers/selection.compiler.d.ts +113 -0
  16. package/dist/cjs/compilers/selection.compiler.d.ts.map +1 -0
  17. package/dist/cjs/compilers/selection.compiler.js +410 -0
  18. package/dist/cjs/compilers/selection.compiler.js.map +1 -0
  19. package/dist/cjs/compilers/where.compiler.d.ts +39 -0
  20. package/dist/cjs/compilers/where.compiler.d.ts.map +1 -0
  21. package/dist/cjs/compilers/where.compiler.js +456 -0
  22. package/dist/cjs/compilers/where.compiler.js.map +1 -0
  23. package/dist/cjs/errors.d.ts +28 -0
  24. package/dist/cjs/errors.d.ts.map +1 -0
  25. package/dist/cjs/errors.js +42 -0
  26. package/dist/cjs/errors.js.map +1 -0
  27. package/dist/cjs/execution/executor.d.ts +38 -0
  28. package/dist/cjs/execution/executor.d.ts.map +1 -0
  29. package/dist/cjs/execution/executor.js +50 -0
  30. package/dist/cjs/execution/executor.js.map +1 -0
  31. package/dist/cjs/execution/result-mapper.d.ts +26 -0
  32. package/dist/cjs/execution/result-mapper.d.ts.map +1 -0
  33. package/dist/cjs/execution/result-mapper.js +92 -0
  34. package/dist/cjs/execution/result-mapper.js.map +1 -0
  35. package/dist/cjs/generator/generate-types.d.ts +55 -0
  36. package/dist/cjs/generator/generate-types.d.ts.map +1 -0
  37. package/dist/cjs/generator/generate-types.js +181 -0
  38. package/dist/cjs/generator/generate-types.js.map +1 -0
  39. package/dist/cjs/generator/index.d.ts +3 -0
  40. package/dist/cjs/generator/index.d.ts.map +1 -0
  41. package/dist/cjs/generator/index.js +6 -0
  42. package/dist/cjs/generator/index.js.map +1 -0
  43. package/dist/cjs/generator/type-emitters/aggregation-emitter.d.ts +7 -0
  44. package/dist/cjs/generator/type-emitters/aggregation-emitter.d.ts.map +1 -0
  45. package/dist/cjs/generator/type-emitters/aggregation-emitter.js +196 -0
  46. package/dist/cjs/generator/type-emitters/aggregation-emitter.js.map +1 -0
  47. package/dist/cjs/generator/type-emitters/connection-emitter.d.ts +22 -0
  48. package/dist/cjs/generator/type-emitters/connection-emitter.d.ts.map +1 -0
  49. package/dist/cjs/generator/type-emitters/connection-emitter.js +270 -0
  50. package/dist/cjs/generator/type-emitters/connection-emitter.js.map +1 -0
  51. package/dist/cjs/generator/type-emitters/enum-emitter.d.ts +7 -0
  52. package/dist/cjs/generator/type-emitters/enum-emitter.d.ts.map +1 -0
  53. package/dist/cjs/generator/type-emitters/enum-emitter.js +45 -0
  54. package/dist/cjs/generator/type-emitters/enum-emitter.js.map +1 -0
  55. package/dist/cjs/generator/type-emitters/fulltext-emitter.d.ts +15 -0
  56. package/dist/cjs/generator/type-emitters/fulltext-emitter.d.ts.map +1 -0
  57. package/dist/cjs/generator/type-emitters/fulltext-emitter.js +80 -0
  58. package/dist/cjs/generator/type-emitters/fulltext-emitter.js.map +1 -0
  59. package/dist/cjs/generator/type-emitters/helpers.d.ts +32 -0
  60. package/dist/cjs/generator/type-emitters/helpers.d.ts.map +1 -0
  61. package/dist/cjs/generator/type-emitters/helpers.js +69 -0
  62. package/dist/cjs/generator/type-emitters/helpers.js.map +1 -0
  63. package/dist/cjs/generator/type-emitters/input-emitter.d.ts +6 -0
  64. package/dist/cjs/generator/type-emitters/input-emitter.d.ts.map +1 -0
  65. package/dist/cjs/generator/type-emitters/input-emitter.js +560 -0
  66. package/dist/cjs/generator/type-emitters/input-emitter.js.map +1 -0
  67. package/dist/cjs/generator/type-emitters/interface-emitter.d.ts +10 -0
  68. package/dist/cjs/generator/type-emitters/interface-emitter.d.ts.map +1 -0
  69. package/dist/cjs/generator/type-emitters/interface-emitter.js +88 -0
  70. package/dist/cjs/generator/type-emitters/interface-emitter.js.map +1 -0
  71. package/dist/cjs/generator/type-emitters/model-map-emitter.d.ts +32 -0
  72. package/dist/cjs/generator/type-emitters/model-map-emitter.d.ts.map +1 -0
  73. package/dist/cjs/generator/type-emitters/model-map-emitter.js +154 -0
  74. package/dist/cjs/generator/type-emitters/model-map-emitter.js.map +1 -0
  75. package/dist/cjs/generator/type-emitters/mutation-response-emitter.d.ts +9 -0
  76. package/dist/cjs/generator/type-emitters/mutation-response-emitter.d.ts.map +1 -0
  77. package/dist/cjs/generator/type-emitters/mutation-response-emitter.js +69 -0
  78. package/dist/cjs/generator/type-emitters/mutation-response-emitter.js.map +1 -0
  79. package/dist/cjs/generator/type-emitters/node-type-emitter.d.ts +13 -0
  80. package/dist/cjs/generator/type-emitters/node-type-emitter.d.ts.map +1 -0
  81. package/dist/cjs/generator/type-emitters/node-type-emitter.js +123 -0
  82. package/dist/cjs/generator/type-emitters/node-type-emitter.js.map +1 -0
  83. package/dist/cjs/generator/type-emitters/select-fields-emitter.d.ts +8 -0
  84. package/dist/cjs/generator/type-emitters/select-fields-emitter.d.ts.map +1 -0
  85. package/dist/cjs/generator/type-emitters/select-fields-emitter.js +169 -0
  86. package/dist/cjs/generator/type-emitters/select-fields-emitter.js.map +1 -0
  87. package/dist/cjs/generator/type-emitters/select-result-emitter.d.ts +9 -0
  88. package/dist/cjs/generator/type-emitters/select-result-emitter.d.ts.map +1 -0
  89. package/dist/cjs/generator/type-emitters/select-result-emitter.js +61 -0
  90. package/dist/cjs/generator/type-emitters/select-result-emitter.js.map +1 -0
  91. package/dist/cjs/generator/type-emitters/sort-options-emitter.d.ts +8 -0
  92. package/dist/cjs/generator/type-emitters/sort-options-emitter.d.ts.map +1 -0
  93. package/dist/cjs/generator/type-emitters/sort-options-emitter.js +47 -0
  94. package/dist/cjs/generator/type-emitters/sort-options-emitter.js.map +1 -0
  95. package/dist/cjs/generator/type-emitters/utility-types.d.ts +10 -0
  96. package/dist/cjs/generator/type-emitters/utility-types.d.ts.map +1 -0
  97. package/dist/cjs/generator/type-emitters/utility-types.js +93 -0
  98. package/dist/cjs/generator/type-emitters/utility-types.js.map +1 -0
  99. package/dist/cjs/generator/type-emitters/where-emitter.d.ts +15 -0
  100. package/dist/cjs/generator/type-emitters/where-emitter.d.ts.map +1 -0
  101. package/dist/cjs/generator/type-emitters/where-emitter.js +189 -0
  102. package/dist/cjs/generator/type-emitters/where-emitter.js.map +1 -0
  103. package/dist/cjs/index.d.ts +30 -0
  104. package/dist/cjs/index.d.ts.map +1 -0
  105. package/dist/cjs/index.js +59 -0
  106. package/dist/cjs/index.js.map +1 -0
  107. package/dist/cjs/interface-model.d.ts +158 -0
  108. package/dist/cjs/interface-model.d.ts.map +1 -0
  109. package/dist/cjs/interface-model.js +214 -0
  110. package/dist/cjs/interface-model.js.map +1 -0
  111. package/dist/cjs/model.d.ts +354 -0
  112. package/dist/cjs/model.d.ts.map +1 -0
  113. package/dist/cjs/model.js +513 -0
  114. package/dist/cjs/model.js.map +1 -0
  115. package/dist/cjs/ogm.d.ts +97 -0
  116. package/dist/cjs/ogm.d.ts.map +1 -0
  117. package/dist/cjs/ogm.js +224 -0
  118. package/dist/cjs/ogm.js.map +1 -0
  119. package/dist/cjs/package.json +1 -0
  120. package/dist/cjs/schema/parser.d.ts +12 -0
  121. package/dist/cjs/schema/parser.d.ts.map +1 -0
  122. package/dist/cjs/schema/parser.js +331 -0
  123. package/dist/cjs/schema/parser.js.map +1 -0
  124. package/dist/cjs/schema/types.d.ts +66 -0
  125. package/dist/cjs/schema/types.d.ts.map +1 -0
  126. package/dist/cjs/schema/types.js +3 -0
  127. package/dist/cjs/schema/types.js.map +1 -0
  128. package/dist/cjs/schema/utils.d.ts +23 -0
  129. package/dist/cjs/schema/utils.d.ts.map +1 -0
  130. package/dist/cjs/schema/utils.js +92 -0
  131. package/dist/cjs/schema/utils.js.map +1 -0
  132. package/dist/cjs/subgraph/index.d.ts +3 -0
  133. package/dist/cjs/subgraph/index.d.ts.map +1 -0
  134. package/dist/cjs/subgraph/index.js +7 -0
  135. package/dist/cjs/subgraph/index.js.map +1 -0
  136. package/dist/cjs/subgraph/subgraph-operations.d.ts +34 -0
  137. package/dist/cjs/subgraph/subgraph-operations.d.ts.map +1 -0
  138. package/dist/cjs/subgraph/subgraph-operations.js +225 -0
  139. package/dist/cjs/subgraph/subgraph-operations.js.map +1 -0
  140. package/dist/cjs/subgraph/types.d.ts +23 -0
  141. package/dist/cjs/subgraph/types.d.ts.map +1 -0
  142. package/dist/cjs/subgraph/types.js +3 -0
  143. package/dist/cjs/subgraph/types.js.map +1 -0
  144. package/dist/cjs/testing/cypher-assert.d.ts +35 -0
  145. package/dist/cjs/testing/cypher-assert.d.ts.map +1 -0
  146. package/dist/cjs/testing/cypher-assert.js +72 -0
  147. package/dist/cjs/testing/cypher-assert.js.map +1 -0
  148. package/dist/cjs/testing/index.d.ts +4 -0
  149. package/dist/cjs/testing/index.d.ts.map +1 -0
  150. package/dist/cjs/testing/index.js +10 -0
  151. package/dist/cjs/testing/index.js.map +1 -0
  152. package/dist/cjs/testing/neo4j-record-factory.d.ts +25 -0
  153. package/dist/cjs/testing/neo4j-record-factory.d.ts.map +1 -0
  154. package/dist/cjs/testing/neo4j-record-factory.js +43 -0
  155. package/dist/cjs/testing/neo4j-record-factory.js.map +1 -0
  156. package/dist/cjs/testing/selection-set-factory.d.ts +24 -0
  157. package/dist/cjs/testing/selection-set-factory.d.ts.map +1 -0
  158. package/dist/cjs/testing/selection-set-factory.js +44 -0
  159. package/dist/cjs/testing/selection-set-factory.js.map +1 -0
  160. package/dist/cjs/utils/lucene.d.ts +9 -0
  161. package/dist/cjs/utils/lucene.d.ts.map +1 -0
  162. package/dist/cjs/utils/lucene.js +25 -0
  163. package/dist/cjs/utils/lucene.js.map +1 -0
  164. package/dist/cjs/utils/validation.d.ts +25 -0
  165. package/dist/cjs/utils/validation.d.ts.map +1 -0
  166. package/dist/cjs/utils/validation.js +53 -0
  167. package/dist/cjs/utils/validation.js.map +1 -0
  168. package/dist/esm/compilers/fulltext.compiler.d.ts +67 -0
  169. package/dist/esm/compilers/fulltext.compiler.d.ts.map +1 -0
  170. package/dist/esm/compilers/fulltext.compiler.js +250 -0
  171. package/dist/esm/compilers/fulltext.compiler.js.map +1 -0
  172. package/dist/esm/compilers/mutation.compiler.d.ts +126 -0
  173. package/dist/esm/compilers/mutation.compiler.d.ts.map +1 -0
  174. package/dist/esm/compilers/mutation.compiler.js +1235 -0
  175. package/dist/esm/compilers/mutation.compiler.js.map +1 -0
  176. package/dist/esm/compilers/select-normalizer.d.ts +50 -0
  177. package/dist/esm/compilers/select-normalizer.d.ts.map +1 -0
  178. package/dist/esm/compilers/select-normalizer.js +221 -0
  179. package/dist/esm/compilers/select-normalizer.js.map +1 -0
  180. package/dist/esm/compilers/selection.compiler.d.ts +113 -0
  181. package/dist/esm/compilers/selection.compiler.d.ts.map +1 -0
  182. package/dist/esm/compilers/selection.compiler.js +406 -0
  183. package/dist/esm/compilers/selection.compiler.js.map +1 -0
  184. package/dist/esm/compilers/where.compiler.d.ts +39 -0
  185. package/dist/esm/compilers/where.compiler.d.ts.map +1 -0
  186. package/dist/esm/compilers/where.compiler.js +452 -0
  187. package/dist/esm/compilers/where.compiler.js.map +1 -0
  188. package/dist/esm/errors.d.ts +28 -0
  189. package/dist/esm/errors.d.ts.map +1 -0
  190. package/dist/esm/errors.js +36 -0
  191. package/dist/esm/errors.js.map +1 -0
  192. package/dist/esm/execution/executor.d.ts +38 -0
  193. package/dist/esm/execution/executor.d.ts.map +1 -0
  194. package/dist/esm/execution/executor.js +46 -0
  195. package/dist/esm/execution/executor.js.map +1 -0
  196. package/dist/esm/execution/result-mapper.d.ts +26 -0
  197. package/dist/esm/execution/result-mapper.d.ts.map +1 -0
  198. package/dist/esm/execution/result-mapper.js +85 -0
  199. package/dist/esm/execution/result-mapper.js.map +1 -0
  200. package/dist/esm/generator/generate-types.d.ts +55 -0
  201. package/dist/esm/generator/generate-types.d.ts.map +1 -0
  202. package/dist/esm/generator/generate-types.js +141 -0
  203. package/dist/esm/generator/generate-types.js.map +1 -0
  204. package/dist/esm/generator/index.d.ts +3 -0
  205. package/dist/esm/generator/index.d.ts.map +1 -0
  206. package/dist/esm/generator/index.js +2 -0
  207. package/dist/esm/generator/index.js.map +1 -0
  208. package/dist/esm/generator/type-emitters/aggregation-emitter.d.ts +7 -0
  209. package/dist/esm/generator/type-emitters/aggregation-emitter.d.ts.map +1 -0
  210. package/dist/esm/generator/type-emitters/aggregation-emitter.js +193 -0
  211. package/dist/esm/generator/type-emitters/aggregation-emitter.js.map +1 -0
  212. package/dist/esm/generator/type-emitters/connection-emitter.d.ts +22 -0
  213. package/dist/esm/generator/type-emitters/connection-emitter.d.ts.map +1 -0
  214. package/dist/esm/generator/type-emitters/connection-emitter.js +266 -0
  215. package/dist/esm/generator/type-emitters/connection-emitter.js.map +1 -0
  216. package/dist/esm/generator/type-emitters/enum-emitter.d.ts +7 -0
  217. package/dist/esm/generator/type-emitters/enum-emitter.d.ts.map +1 -0
  218. package/dist/esm/generator/type-emitters/enum-emitter.js +42 -0
  219. package/dist/esm/generator/type-emitters/enum-emitter.js.map +1 -0
  220. package/dist/esm/generator/type-emitters/fulltext-emitter.d.ts +15 -0
  221. package/dist/esm/generator/type-emitters/fulltext-emitter.d.ts.map +1 -0
  222. package/dist/esm/generator/type-emitters/fulltext-emitter.js +77 -0
  223. package/dist/esm/generator/type-emitters/fulltext-emitter.js.map +1 -0
  224. package/dist/esm/generator/type-emitters/helpers.d.ts +32 -0
  225. package/dist/esm/generator/type-emitters/helpers.d.ts.map +1 -0
  226. package/dist/esm/generator/type-emitters/helpers.js +63 -0
  227. package/dist/esm/generator/type-emitters/helpers.js.map +1 -0
  228. package/dist/esm/generator/type-emitters/input-emitter.d.ts +6 -0
  229. package/dist/esm/generator/type-emitters/input-emitter.d.ts.map +1 -0
  230. package/dist/esm/generator/type-emitters/input-emitter.js +557 -0
  231. package/dist/esm/generator/type-emitters/input-emitter.js.map +1 -0
  232. package/dist/esm/generator/type-emitters/interface-emitter.d.ts +10 -0
  233. package/dist/esm/generator/type-emitters/interface-emitter.d.ts.map +1 -0
  234. package/dist/esm/generator/type-emitters/interface-emitter.js +85 -0
  235. package/dist/esm/generator/type-emitters/interface-emitter.js.map +1 -0
  236. package/dist/esm/generator/type-emitters/model-map-emitter.d.ts +32 -0
  237. package/dist/esm/generator/type-emitters/model-map-emitter.d.ts.map +1 -0
  238. package/dist/esm/generator/type-emitters/model-map-emitter.js +149 -0
  239. package/dist/esm/generator/type-emitters/model-map-emitter.js.map +1 -0
  240. package/dist/esm/generator/type-emitters/mutation-response-emitter.d.ts +9 -0
  241. package/dist/esm/generator/type-emitters/mutation-response-emitter.d.ts.map +1 -0
  242. package/dist/esm/generator/type-emitters/mutation-response-emitter.js +66 -0
  243. package/dist/esm/generator/type-emitters/mutation-response-emitter.js.map +1 -0
  244. package/dist/esm/generator/type-emitters/node-type-emitter.d.ts +13 -0
  245. package/dist/esm/generator/type-emitters/node-type-emitter.d.ts.map +1 -0
  246. package/dist/esm/generator/type-emitters/node-type-emitter.js +120 -0
  247. package/dist/esm/generator/type-emitters/node-type-emitter.js.map +1 -0
  248. package/dist/esm/generator/type-emitters/select-fields-emitter.d.ts +8 -0
  249. package/dist/esm/generator/type-emitters/select-fields-emitter.d.ts.map +1 -0
  250. package/dist/esm/generator/type-emitters/select-fields-emitter.js +166 -0
  251. package/dist/esm/generator/type-emitters/select-fields-emitter.js.map +1 -0
  252. package/dist/esm/generator/type-emitters/select-result-emitter.d.ts +9 -0
  253. package/dist/esm/generator/type-emitters/select-result-emitter.d.ts.map +1 -0
  254. package/dist/esm/generator/type-emitters/select-result-emitter.js +58 -0
  255. package/dist/esm/generator/type-emitters/select-result-emitter.js.map +1 -0
  256. package/dist/esm/generator/type-emitters/sort-options-emitter.d.ts +8 -0
  257. package/dist/esm/generator/type-emitters/sort-options-emitter.d.ts.map +1 -0
  258. package/dist/esm/generator/type-emitters/sort-options-emitter.js +44 -0
  259. package/dist/esm/generator/type-emitters/sort-options-emitter.js.map +1 -0
  260. package/dist/esm/generator/type-emitters/utility-types.d.ts +10 -0
  261. package/dist/esm/generator/type-emitters/utility-types.d.ts.map +1 -0
  262. package/dist/esm/generator/type-emitters/utility-types.js +88 -0
  263. package/dist/esm/generator/type-emitters/utility-types.js.map +1 -0
  264. package/dist/esm/generator/type-emitters/where-emitter.d.ts +15 -0
  265. package/dist/esm/generator/type-emitters/where-emitter.d.ts.map +1 -0
  266. package/dist/esm/generator/type-emitters/where-emitter.js +186 -0
  267. package/dist/esm/generator/type-emitters/where-emitter.js.map +1 -0
  268. package/dist/esm/index.d.ts +30 -0
  269. package/dist/esm/index.d.ts.map +1 -0
  270. package/dist/esm/index.js +29 -0
  271. package/dist/esm/index.js.map +1 -0
  272. package/dist/esm/interface-model.d.ts +158 -0
  273. package/dist/esm/interface-model.d.ts.map +1 -0
  274. package/dist/esm/interface-model.js +210 -0
  275. package/dist/esm/interface-model.js.map +1 -0
  276. package/dist/esm/model.d.ts +354 -0
  277. package/dist/esm/model.d.ts.map +1 -0
  278. package/dist/esm/model.js +507 -0
  279. package/dist/esm/model.js.map +1 -0
  280. package/dist/esm/ogm.d.ts +97 -0
  281. package/dist/esm/ogm.d.ts.map +1 -0
  282. package/dist/esm/ogm.js +220 -0
  283. package/dist/esm/ogm.js.map +1 -0
  284. package/dist/esm/package.json +1 -0
  285. package/dist/esm/schema/parser.d.ts +12 -0
  286. package/dist/esm/schema/parser.d.ts.map +1 -0
  287. package/dist/esm/schema/parser.js +324 -0
  288. package/dist/esm/schema/parser.js.map +1 -0
  289. package/dist/esm/schema/types.d.ts +66 -0
  290. package/dist/esm/schema/types.d.ts.map +1 -0
  291. package/dist/esm/schema/types.js +2 -0
  292. package/dist/esm/schema/types.js.map +1 -0
  293. package/dist/esm/schema/utils.d.ts +23 -0
  294. package/dist/esm/schema/utils.d.ts.map +1 -0
  295. package/dist/esm/schema/utils.js +87 -0
  296. package/dist/esm/schema/utils.js.map +1 -0
  297. package/dist/esm/subgraph/index.d.ts +3 -0
  298. package/dist/esm/subgraph/index.d.ts.map +1 -0
  299. package/dist/esm/subgraph/index.js +2 -0
  300. package/dist/esm/subgraph/index.js.map +1 -0
  301. package/dist/esm/subgraph/subgraph-operations.d.ts +34 -0
  302. package/dist/esm/subgraph/subgraph-operations.d.ts.map +1 -0
  303. package/dist/esm/subgraph/subgraph-operations.js +221 -0
  304. package/dist/esm/subgraph/subgraph-operations.js.map +1 -0
  305. package/dist/esm/subgraph/types.d.ts +23 -0
  306. package/dist/esm/subgraph/types.d.ts.map +1 -0
  307. package/dist/esm/subgraph/types.js +2 -0
  308. package/dist/esm/subgraph/types.js.map +1 -0
  309. package/dist/esm/testing/cypher-assert.d.ts +35 -0
  310. package/dist/esm/testing/cypher-assert.d.ts.map +1 -0
  311. package/dist/esm/testing/cypher-assert.js +68 -0
  312. package/dist/esm/testing/cypher-assert.js.map +1 -0
  313. package/dist/esm/testing/index.d.ts +4 -0
  314. package/dist/esm/testing/index.d.ts.map +1 -0
  315. package/dist/esm/testing/index.js +4 -0
  316. package/dist/esm/testing/index.js.map +1 -0
  317. package/dist/esm/testing/neo4j-record-factory.d.ts +25 -0
  318. package/dist/esm/testing/neo4j-record-factory.d.ts.map +1 -0
  319. package/dist/esm/testing/neo4j-record-factory.js +36 -0
  320. package/dist/esm/testing/neo4j-record-factory.js.map +1 -0
  321. package/dist/esm/testing/selection-set-factory.d.ts +24 -0
  322. package/dist/esm/testing/selection-set-factory.d.ts.map +1 -0
  323. package/dist/esm/testing/selection-set-factory.js +40 -0
  324. package/dist/esm/testing/selection-set-factory.js.map +1 -0
  325. package/dist/esm/utils/lucene.d.ts +9 -0
  326. package/dist/esm/utils/lucene.d.ts.map +1 -0
  327. package/dist/esm/utils/lucene.js +22 -0
  328. package/dist/esm/utils/lucene.js.map +1 -0
  329. package/dist/esm/utils/validation.d.ts +25 -0
  330. package/dist/esm/utils/validation.d.ts.map +1 -0
  331. package/dist/esm/utils/validation.js +46 -0
  332. package/dist/esm/utils/validation.js.map +1 -0
  333. package/package.json +88 -0
@@ -0,0 +1,1239 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MutationCompiler = void 0;
4
+ const errors_1 = require("../errors");
5
+ const utils_1 = require("../schema/utils");
6
+ const validation_1 = require("../utils/validation");
7
+ /**
8
+ * Relationship filter suffixes that require EXISTS subquery patterns.
9
+ * These match related nodes via relationship traversal rather than scalar property comparison.
10
+ */
11
+ const RELATIONSHIP_SUFFIXES = ['_SOME', '_NONE', '_ALL', '_SINGLE'];
12
+ /**
13
+ * Compiles create, update, delete, and label mutations into Cypher + params.
14
+ */
15
+ class MutationCompiler {
16
+ constructor(schema) {
17
+ this.schema = schema;
18
+ /**
19
+ * Cache of computed label strings per type name.
20
+ * Bounded by the number of node types in the schema (~50-100 entries).
21
+ */
22
+ this.labelCache = new Map();
23
+ /** Counter for unique relationship filter variables within a single WHERE clause. */
24
+ this.relFilterCounter = 0;
25
+ }
26
+ /** Clear internal caches. Useful in tests to prevent cross-test pollution. */
27
+ clearCaches() {
28
+ this.labelCache.clear();
29
+ }
30
+ getCachedLabelString(nodeDef) {
31
+ const cached = this.labelCache.get(nodeDef.typeName);
32
+ if (cached)
33
+ return cached;
34
+ const labelStr = (0, utils_1.getTargetLabelString)(nodeDef);
35
+ this.labelCache.set(nodeDef.typeName, labelStr);
36
+ return labelStr;
37
+ }
38
+ /**
39
+ * Generate CREATE Cypher for one or more nodes.
40
+ * Handles scalar properties, nested relationship creates, and connects.
41
+ */
42
+ compileCreate(inputs, nodeDef, labels) {
43
+ this.relFilterCounter = 0;
44
+ const lines = [];
45
+ const params = {};
46
+ const createdVars = [];
47
+ for (let i = 0; i < inputs.length; i++) {
48
+ const input = inputs[i];
49
+ const prefix = `create${i}`;
50
+ const nodeVar = i === 0 ? 'n' : `n_${i}`;
51
+ const { propString, propParams } = this.buildCreateProperties(input, nodeDef, prefix);
52
+ const labelStr = this.getCachedLabelString(nodeDef);
53
+ lines.push(`CREATE (${nodeVar}:${labelStr} { ${propString} })`);
54
+ Object.assign(params, propParams);
55
+ createdVars.push(nodeVar);
56
+ // Process relationship operations (nested create / connect)
57
+ // Pass all previously created node vars as ancestors so WITH clauses
58
+ // don't drop them from scope (needed for RETURN n at the end)
59
+ const relLines = this.buildCreateRelationships(input, nodeDef, nodeVar, prefix, params, createdVars.slice(0, -1));
60
+ lines.push(...relLines);
61
+ }
62
+ // Add extra labels to all created nodes
63
+ if (labels && labels.length > 0) {
64
+ const validatedLabels = labels.map((l) => (0, validation_1.assertSafeLabel)(l)).join(':');
65
+ for (const nodeVar of createdVars)
66
+ lines.push(`SET ${nodeVar}:${validatedLabels}`);
67
+ }
68
+ lines.push('RETURN n');
69
+ return { cypher: lines.join('\n'), params };
70
+ }
71
+ /**
72
+ * Generate UPDATE Cypher (SET properties + connect/disconnect).
73
+ */
74
+ compileUpdate(_where, update, connect, disconnect, nodeDef, whereResult, labels, returnMode = 'node') {
75
+ this.relFilterCounter = 0;
76
+ const labelStr = this.getCachedLabelString(nodeDef);
77
+ const lines = [];
78
+ const params = { ...whereResult.params };
79
+ // Apply runtime labels to MATCH pattern (same as find())
80
+ if (labels && labels.length > 0) {
81
+ const extraLabels = labels.map((l) => (0, validation_1.assertSafeLabel)(l)).join(':');
82
+ lines.push(`MATCH (n:${labelStr}:${extraLabels})`);
83
+ }
84
+ else
85
+ lines.push(`MATCH (n:${labelStr})`);
86
+ if (whereResult.cypher)
87
+ lines.push(`WHERE ${whereResult.cypher}`);
88
+ // SET properties
89
+ if (update && Object.keys(update).length > 0) {
90
+ const setClauses = [];
91
+ for (const [key, value] of Object.entries(update)) {
92
+ // Skip relationship fields — handled separately below
93
+ if (nodeDef.relationships.has(key))
94
+ continue;
95
+ if (value === undefined)
96
+ continue;
97
+ (0, validation_1.assertSafeIdentifier)(key, 'update property');
98
+ const paramName = `update_${key}`;
99
+ setClauses.push(`n.${(0, validation_1.escapeIdentifier)(key)} = $${paramName}`);
100
+ params[paramName] = value;
101
+ }
102
+ if (setClauses.length > 0)
103
+ lines.push(`SET ${setClauses.join(', ')}`);
104
+ // Process nested relationship operations within update body
105
+ const relLines = this.buildUpdateRelationships(update, nodeDef, 'n', 'update', params);
106
+ lines.push(...relLines);
107
+ }
108
+ // Disconnect relationships (top-level) — must run before connects
109
+ // so that blanket disconnects don't remove newly-connected relationships.
110
+ if (disconnect) {
111
+ const disconnectLines = this.buildDisconnects(disconnect, nodeDef, params);
112
+ lines.push(...disconnectLines);
113
+ }
114
+ // Connect relationships (top-level)
115
+ if (connect) {
116
+ const connectLines = this.buildConnects(connect, nodeDef, params);
117
+ lines.push(...connectLines);
118
+ }
119
+ lines.push(returnMode === 'count' ? 'RETURN count(n) AS count' : 'RETURN n');
120
+ return { cypher: lines.join('\n'), params };
121
+ }
122
+ /**
123
+ * Generate DELETE Cypher with optional cascade.
124
+ */
125
+ compileDelete(nodeDef, whereResult, deleteInput) {
126
+ const labelStr = this.getCachedLabelString(nodeDef);
127
+ const lines = [];
128
+ const params = { ...whereResult.params };
129
+ lines.push(`MATCH (n:${labelStr})`);
130
+ if (whereResult.cypher)
131
+ lines.push(`WHERE ${whereResult.cypher}`);
132
+ if (deleteInput && Object.keys(deleteInput).length > 0) {
133
+ const deleteVars = [];
134
+ let varCounter = 0;
135
+ for (const [fieldName] of Object.entries(deleteInput)) {
136
+ const relDef = nodeDef.relationships.get(fieldName);
137
+ if (!relDef)
138
+ continue;
139
+ const cascadeVar = this.getCascadeVarName(varCounter);
140
+ varCounter++;
141
+ const pattern = this.buildRelPattern('n', relDef, cascadeVar, undefined, relDef.target);
142
+ lines.push(`OPTIONAL MATCH ${pattern}`);
143
+ deleteVars.push(cascadeVar);
144
+ }
145
+ lines.push(`DETACH DELETE ${[...deleteVars, 'n'].join(', ')}`);
146
+ }
147
+ else
148
+ lines.push('DETACH DELETE n');
149
+ return { cypher: lines.join('\n'), params };
150
+ }
151
+ /**
152
+ * Generate MERGE (upsert) Cypher with ON CREATE SET / ON MATCH SET.
153
+ * Scalar properties only — nested relationship ops are not supported.
154
+ */
155
+ compileMerge(where, create, update, nodeDef, labels) {
156
+ const labelStr = this.getCachedLabelString(nodeDef);
157
+ const lines = [];
158
+ const params = {};
159
+ // Build MERGE key properties from where clause (scalar only)
160
+ const mergeProps = [];
161
+ for (const [key, value] of Object.entries(where)) {
162
+ if (value === undefined || value === null)
163
+ continue;
164
+ // Skip relationship suffixes and operators
165
+ if (key === 'AND' || key === 'OR' || key === 'NOT')
166
+ continue;
167
+ if (key.includes('_') && !nodeDef.properties.has(key))
168
+ continue;
169
+ (0, validation_1.assertSafeIdentifier)(key, 'merge key');
170
+ const paramName = `merge_${key}`;
171
+ mergeProps.push(`${(0, validation_1.escapeIdentifier)(key)}: $${paramName}`);
172
+ params[paramName] = value;
173
+ }
174
+ if (mergeProps.length === 0)
175
+ throw new errors_1.OGMError('upsert requires at least one scalar property in "where" for MERGE key');
176
+ lines.push(`MERGE (n:${labelStr} { ${mergeProps.join(', ')} })`);
177
+ // ON CREATE SET — all properties from create input (scalar only)
178
+ const createSets = [];
179
+ for (const [key, value] of Object.entries(create)) {
180
+ if (value === undefined)
181
+ continue;
182
+ if (nodeDef.relationships.has(key))
183
+ continue;
184
+ (0, validation_1.assertSafeIdentifier)(key, 'create property');
185
+ const paramName = `onCreate_${key}`;
186
+ createSets.push(`n.${(0, validation_1.escapeIdentifier)(key)} = $${paramName}`);
187
+ params[paramName] = value;
188
+ }
189
+ if (createSets.length > 0)
190
+ lines.push(`ON CREATE SET ${createSets.join(', ')}`);
191
+ // ON MATCH SET — all properties from update input (scalar only)
192
+ const updateSets = [];
193
+ for (const [key, value] of Object.entries(update)) {
194
+ if (value === undefined)
195
+ continue;
196
+ if (nodeDef.relationships.has(key))
197
+ continue;
198
+ (0, validation_1.assertSafeIdentifier)(key, 'update property');
199
+ const paramName = `onMatch_${key}`;
200
+ updateSets.push(`n.${(0, validation_1.escapeIdentifier)(key)} = $${paramName}`);
201
+ params[paramName] = value;
202
+ }
203
+ if (updateSets.length > 0)
204
+ lines.push(`ON MATCH SET ${updateSets.join(', ')}`);
205
+ // Extra labels
206
+ if (labels && labels.length > 0) {
207
+ const validatedLabels = labels.map((l) => (0, validation_1.assertSafeLabel)(l)).join(':');
208
+ lines.push(`SET n:${validatedLabels}`);
209
+ }
210
+ lines.push('RETURN n');
211
+ return { cypher: lines.join('\n'), params };
212
+ }
213
+ /**
214
+ * Generate SET/REMOVE labels Cypher.
215
+ */
216
+ compileSetLabels(nodeDef, whereResult, addLabels, removeLabels) {
217
+ const labelStr = this.getCachedLabelString(nodeDef);
218
+ const lines = [];
219
+ const params = { ...whereResult.params };
220
+ lines.push(`MATCH (n:${labelStr})`);
221
+ if (whereResult.cypher)
222
+ lines.push(`WHERE ${whereResult.cypher}`);
223
+ if (addLabels && addLabels.length > 0)
224
+ lines.push(`SET n:${addLabels.map((l) => (0, validation_1.assertSafeLabel)(l)).join(':')}`);
225
+ if (removeLabels && removeLabels.length > 0)
226
+ for (const label of removeLabels)
227
+ lines.push(`REMOVE n:${(0, validation_1.assertSafeLabel)(label)}`);
228
+ return { cypher: lines.join('\n'), params };
229
+ }
230
+ /**
231
+ * Generate batch CREATE (or MERGE for skipDuplicates) Cypher via UNWIND.
232
+ * Scalar properties only — no nested relationship operations.
233
+ * Returns count of created nodes.
234
+ */
235
+ compileCreateMany(data, nodeDef, skipDuplicates, labels) {
236
+ if (data.length === 0)
237
+ return { cypher: 'RETURN 0 AS count', params: {} };
238
+ const labelStr = this.getCachedLabelString(nodeDef);
239
+ const lines = [];
240
+ const params = {};
241
+ // Validate and sanitize: only scalar properties allowed
242
+ const scalarKeys = [];
243
+ for (const key of Object.keys(data[0])) {
244
+ if (nodeDef.relationships.has(key))
245
+ throw new errors_1.OGMError(`createMany does not support relationship fields. Found: "${key}". Use create() for nested operations.`);
246
+ (0, validation_1.assertSafeIdentifier)(key, 'createMany property');
247
+ scalarKeys.push(key);
248
+ }
249
+ // Identify @id (generated) fields that are NOT provided in data
250
+ const generatedFields = [];
251
+ for (const [, propDef] of nodeDef.properties)
252
+ if (propDef.isGenerated && !scalarKeys.includes(propDef.name))
253
+ generatedFields.push(propDef.name);
254
+ // Build sanitized items (strip undefined values)
255
+ const sanitizedItems = data.map((item) => {
256
+ const sanitized = {};
257
+ for (const key of scalarKeys)
258
+ if (item[key] !== undefined)
259
+ sanitized[key] = item[key];
260
+ return sanitized;
261
+ });
262
+ params.items = sanitizedItems;
263
+ lines.push('UNWIND $items AS item');
264
+ if (skipDuplicates) {
265
+ // Find unique/id fields present in data for MERGE key
266
+ const mergeKeys = [];
267
+ for (const key of scalarKeys) {
268
+ const propDef = nodeDef.properties.get(key);
269
+ if (propDef && (propDef.isUnique || propDef.isGenerated))
270
+ mergeKeys.push(key);
271
+ }
272
+ if (mergeKeys.length === 0)
273
+ throw new errors_1.OGMError('createMany with skipDuplicates requires at least one @id or @unique field in data');
274
+ // MERGE on unique key(s)
275
+ const mergeProps = mergeKeys
276
+ .map((k) => `${(0, validation_1.escapeIdentifier)(k)}: item.${(0, validation_1.escapeIdentifier)(k)}`)
277
+ .join(', ');
278
+ lines.push(`MERGE (n:${labelStr} { ${mergeProps} })`);
279
+ // ON CREATE SET — remaining fields + generated IDs
280
+ const onCreateSets = [];
281
+ for (const key of scalarKeys)
282
+ if (!mergeKeys.includes(key))
283
+ onCreateSets.push(`n.${(0, validation_1.escapeIdentifier)(key)} = item.${(0, validation_1.escapeIdentifier)(key)}`);
284
+ for (const field of generatedFields)
285
+ onCreateSets.push(`n.${(0, validation_1.escapeIdentifier)(field)} = randomUUID()`);
286
+ if (onCreateSets.length > 0)
287
+ lines.push(`ON CREATE SET ${onCreateSets.join(', ')}`);
288
+ }
289
+ else {
290
+ // CREATE with all scalar properties + generated IDs
291
+ const propParts = [];
292
+ for (const field of generatedFields)
293
+ propParts.push(`${(0, validation_1.escapeIdentifier)(field)}: randomUUID()`);
294
+ for (const key of scalarKeys)
295
+ propParts.push(`${(0, validation_1.escapeIdentifier)(key)}: item.${(0, validation_1.escapeIdentifier)(key)}`);
296
+ lines.push(`CREATE (n:${labelStr} { ${propParts.join(', ')} })`);
297
+ }
298
+ // Extra labels
299
+ if (labels && labels.length > 0) {
300
+ const validatedLabels = labels.map((l) => (0, validation_1.assertSafeLabel)(l)).join(':');
301
+ lines.push(`SET n:${validatedLabels}`);
302
+ }
303
+ lines.push('RETURN count(n) AS count');
304
+ return { cypher: lines.join('\n'), params };
305
+ }
306
+ // ─── Private helpers ──────────────────────────────────────────────
307
+ buildCreateProperties(input, nodeDef, prefix) {
308
+ const parts = [];
309
+ const propParams = {};
310
+ // Auto-generate ID if property has isGenerated
311
+ for (const [, propDef] of nodeDef.properties)
312
+ if (propDef.isGenerated)
313
+ parts.push(`${(0, validation_1.escapeIdentifier)(propDef.name)}: randomUUID()`);
314
+ for (const [key, value] of Object.entries(input)) {
315
+ // Skip relationship fields and undefined values
316
+ if (nodeDef.relationships.has(key))
317
+ continue;
318
+ if (value === undefined)
319
+ continue;
320
+ (0, validation_1.assertSafeIdentifier)(key, 'create property');
321
+ const paramName = `${prefix}_${key}`;
322
+ parts.push(`${(0, validation_1.escapeIdentifier)(key)}: $${paramName}`);
323
+ propParams[paramName] = value;
324
+ }
325
+ return { propString: parts.join(', '), propParams };
326
+ }
327
+ buildCreateRelationships(input, nodeDef, nodeVar, prefix, params, ancestorVars = []) {
328
+ const lines = [];
329
+ let nestedCounter = 0;
330
+ // WITH clause must carry all ancestors + the node being created on
331
+ const allVars = [...new Set([...ancestorVars, nodeVar])];
332
+ const withClause = `WITH ${allVars.join(', ')}`;
333
+ for (const [key, value] of Object.entries(input)) {
334
+ const relDef = nodeDef.relationships.get(key);
335
+ if (!relDef)
336
+ continue;
337
+ const relInput = value;
338
+ // Check if target is a union type — if so, relInput uses per-member keys
339
+ // e.g., items: { Ebook: { create: [...] } }
340
+ const isUnionTarget = !this.schema.nodes.has(relDef.target) &&
341
+ this.schema.unions?.has(relDef.target);
342
+ if (isUnionTarget) {
343
+ const unionMembers = this.schema.unions.get(relDef.target);
344
+ for (const [memberKey, memberValue] of Object.entries(relInput)) {
345
+ if (!unionMembers.includes(memberKey))
346
+ continue;
347
+ const memberNodeDef = this.schema.nodes.get(memberKey);
348
+ if (!memberNodeDef)
349
+ continue;
350
+ const memberInput = memberValue;
351
+ // Process creates/connects for this union member
352
+ const memberLines = this.buildCreateRelationshipsForTarget(memberInput, memberNodeDef, relDef, nodeVar, `${prefix}_${key}_${memberKey}`, params, allVars, withClause, nestedCounter);
353
+ nestedCounter += memberLines.counterUsed;
354
+ lines.push(...memberLines.lines);
355
+ }
356
+ continue;
357
+ }
358
+ const targetNodeDef = this.schema.nodes.get(relDef.target);
359
+ if (!targetNodeDef)
360
+ continue;
361
+ const resultLines = this.buildCreateRelationshipsForTarget(relInput, targetNodeDef, relDef, nodeVar, `${prefix}_${key}`, params, allVars, withClause, nestedCounter);
362
+ nestedCounter += resultLines.counterUsed;
363
+ lines.push(...resultLines.lines);
364
+ }
365
+ return lines;
366
+ }
367
+ /**
368
+ * Process create/connect operations for a specific target node type.
369
+ * Used by buildCreateRelationships for both union-member and non-union targets.
370
+ */
371
+ buildCreateRelationshipsForTarget(relInput, targetNodeDef, relDef, nodeVar, prefix, params, allVars, withClause, startCounter) {
372
+ const lines = [];
373
+ let counter = 0;
374
+ const targetLabelStr = this.getCachedLabelString(targetNodeDef);
375
+ // --- create ---
376
+ if (relInput.create) {
377
+ const createItems = Array.isArray(relInput.create)
378
+ ? relInput.create
379
+ : [relInput.create];
380
+ for (let ci = 0; ci < createItems.length; ci++) {
381
+ const createSpec = createItems[ci];
382
+ const nodeSpec = (createSpec.node ?? createSpec);
383
+ const createVar = `${nodeVar}_c${startCounter + counter}`;
384
+ counter++;
385
+ const { propString, propParams } = this.buildCreateProperties(nodeSpec, targetNodeDef, `${prefix}_create${ci}`);
386
+ // Handle nested relationship creates within the created node
387
+ const nestedRelLines = this.buildCreateRelationships(nodeSpec, targetNodeDef, createVar, `${prefix}_create${ci}`, params, allVars);
388
+ const propsClause = propString.length > 0
389
+ ? ` { ${propString} }`
390
+ : ` { ${this.buildGeneratedIdClause(targetNodeDef)} }`;
391
+ const relPattern = this.buildRelPattern(nodeVar, relDef, createVar);
392
+ // Handle edge properties if present in createSpec
393
+ const edgeInput = createSpec.edge;
394
+ // Wrap in CALL subquery if there are nested ops so failed MATCHes don't kill pipeline
395
+ const hasNestedOps = nestedRelLines.length > 0;
396
+ if (hasNestedOps) {
397
+ lines.push(withClause);
398
+ lines.push(`CALL {`);
399
+ lines.push(withClause);
400
+ }
401
+ else
402
+ lines.push(withClause);
403
+ lines.push(`CREATE (${createVar}:${targetLabelStr}${propsClause})`);
404
+ Object.assign(params, propParams);
405
+ lines.push(`CREATE ${relPattern}`);
406
+ // Set edge properties if present
407
+ if (edgeInput && Object.keys(edgeInput).length > 0) {
408
+ const relVar = `r_edge_${startCounter + counter - 1}`;
409
+ // Re-match the relationship to set edge props
410
+ const relArrow = this.buildRelPattern(nodeVar, relDef, createVar, relVar, 'auto');
411
+ lines.push(`WITH ${[...allVars, createVar].join(', ')}`);
412
+ lines.push(`MATCH ${relArrow}`);
413
+ const setItems = [];
414
+ for (const [prop, val] of Object.entries(edgeInput)) {
415
+ if (val === undefined)
416
+ continue;
417
+ (0, validation_1.assertSafeIdentifier)(prop, 'edge property');
418
+ const paramName = `${prefix}_create${ci}_edge_${prop}`;
419
+ setItems.push(`${relVar}.${(0, validation_1.escapeIdentifier)(prop)} = $${paramName}`);
420
+ params[paramName] = val;
421
+ }
422
+ if (setItems.length > 0)
423
+ lines.push(`SET ${setItems.join(', ')}`);
424
+ }
425
+ if (hasNestedOps) {
426
+ lines.push(...nestedRelLines);
427
+ lines.push(`RETURN count(*) AS _cc_${prefix}_${ci}`);
428
+ lines.push(`}`);
429
+ }
430
+ }
431
+ }
432
+ // --- connect ---
433
+ if (relInput.connect) {
434
+ const connectItems = Array.isArray(relInput.connect)
435
+ ? relInput.connect
436
+ : [relInput.connect];
437
+ for (let ci = 0; ci < connectItems.length; ci++) {
438
+ const connectSpec = connectItems[ci];
439
+ const whereSpec = connectSpec.where;
440
+ const edgeInput = connectSpec.edge;
441
+ // Wrap in CALL subquery so a failed MATCH doesn't kill the outer pipeline
442
+ lines.push(withClause);
443
+ lines.push(`CALL {`);
444
+ lines.push(withClause);
445
+ const connectVar = `${nodeVar}_cn${startCounter + counter}`;
446
+ counter++;
447
+ lines.push(`MATCH (${connectVar}:${targetLabelStr})`);
448
+ const conditions = this.buildConnectionWhereConditions(whereSpec, connectVar, `${prefix}_conn${ci}`, params, targetNodeDef);
449
+ if (conditions.length > 0)
450
+ lines.push(`WHERE ${conditions.join(' AND ')}`);
451
+ if (edgeInput && Object.keys(edgeInput).length > 0) {
452
+ // Use bare target var (no label) since connectVar is already bound by MATCH.
453
+ // Including labels on a bound variable in MERGE causes Neo4j to reject it.
454
+ const relVar = `r_conn_${startCounter + counter - 1}`;
455
+ (0, validation_1.assertSafeIdentifier)(relDef.type, 'relationship type');
456
+ const escapedRelType = (0, validation_1.escapeIdentifier)(relDef.type);
457
+ const mergePattern = relDef.direction === 'OUT'
458
+ ? `(${nodeVar})-[${relVar}:${escapedRelType}]->(${connectVar})`
459
+ : `(${nodeVar})<-[${relVar}:${escapedRelType}]-(${connectVar})`;
460
+ lines.push(`MERGE ${mergePattern}`);
461
+ const setItems = [];
462
+ for (const [prop, val] of Object.entries(edgeInput)) {
463
+ if (val === undefined)
464
+ continue;
465
+ (0, validation_1.assertSafeIdentifier)(prop, 'edge property');
466
+ const paramName = `${prefix}_conn${ci}_edge_${prop}`;
467
+ setItems.push(`r_conn_${startCounter + counter - 1}.${(0, validation_1.escapeIdentifier)(prop)} = $${paramName}`);
468
+ params[paramName] = val;
469
+ }
470
+ if (setItems.length > 0)
471
+ lines.push(`SET ${setItems.join(', ')}`);
472
+ }
473
+ else {
474
+ const relPattern = this.buildRelPattern(nodeVar, relDef, connectVar);
475
+ lines.push(`MERGE ${relPattern}`);
476
+ }
477
+ lines.push(`RETURN count(*) AS _ccn_${prefix}_${ci}`);
478
+ lines.push(`}`);
479
+ }
480
+ }
481
+ return { lines, counterUsed: counter };
482
+ }
483
+ buildConnects(connect, nodeDef, params) {
484
+ const lines = [];
485
+ for (const [fieldName, spec] of Object.entries(connect)) {
486
+ const relDef = nodeDef.relationships.get(fieldName);
487
+ if (!relDef)
488
+ continue;
489
+ const targetNodeDef = (0, utils_1.resolveTargetDef)(relDef.target, this.schema);
490
+ if (!targetNodeDef)
491
+ continue;
492
+ const targetLabelStr = this.getCachedLabelString(targetNodeDef);
493
+ // Array connect
494
+ if (Array.isArray(spec)) {
495
+ if (spec.length === 0)
496
+ continue; // Empty array — nothing to connect
497
+ // Check if WHERE contains relationship filters (nested objects referencing
498
+ // relationships on the target node). UNWIND can't handle EXISTS subqueries
499
+ // with dynamic params, so we fall back to individual CALL subqueries.
500
+ const hasRelFilters = this.arrayConnectHasRelationshipFilters(spec, targetNodeDef);
501
+ if (hasRelFilters)
502
+ // Fall back to individual CALL subqueries per item — these use
503
+ // buildConnectionWhereConditions which handles relationship filters.
504
+ for (let ci = 0; ci < spec.length; ci++) {
505
+ const connectSpec = spec[ci];
506
+ const whereSpec = connectSpec.where;
507
+ const edgeInput = connectSpec.edge;
508
+ lines.push('WITH n');
509
+ lines.push(`CALL {`);
510
+ lines.push('WITH n');
511
+ const connectVar = `target_${fieldName}_${ci}`;
512
+ lines.push(`MATCH (${connectVar}:${targetLabelStr})`);
513
+ const conditions = this.buildConnectionWhereConditions(whereSpec, connectVar, `connect_${fieldName}_${ci}`, params, targetNodeDef);
514
+ if (conditions.length > 0)
515
+ lines.push(`WHERE ${conditions.join(' AND ')}`);
516
+ if (edgeInput && Object.keys(edgeInput).length > 0) {
517
+ (0, validation_1.assertSafeIdentifier)(relDef.type, 'relationship type');
518
+ const escapedRelType = (0, validation_1.escapeIdentifier)(relDef.type);
519
+ const relVar = `r_conn_${fieldName}_${ci}`;
520
+ const mergePattern = relDef.direction === 'OUT'
521
+ ? `(n)-[${relVar}:${escapedRelType}]->(${connectVar})`
522
+ : `(n)<-[${relVar}:${escapedRelType}]-(${connectVar})`;
523
+ lines.push(`MERGE ${mergePattern}`);
524
+ const setItems = [];
525
+ for (const [prop, val] of Object.entries(edgeInput)) {
526
+ if (val === undefined)
527
+ continue;
528
+ (0, validation_1.assertSafeIdentifier)(prop, 'edge property');
529
+ const paramName = `connect_${fieldName}_${ci}_edge_${prop}`;
530
+ setItems.push(`${relVar}.${(0, validation_1.escapeIdentifier)(prop)} = $${paramName}`);
531
+ params[paramName] = val;
532
+ }
533
+ if (setItems.length > 0)
534
+ lines.push(`SET ${setItems.join(', ')}`);
535
+ }
536
+ else {
537
+ const mergePattern = this.buildRelPattern('n', relDef, connectVar);
538
+ lines.push(`MERGE ${mergePattern}`);
539
+ }
540
+ lines.push(`RETURN count(*) AS _ccn_${fieldName}_${ci}`);
541
+ lines.push(`}`);
542
+ }
543
+ else {
544
+ // Scalar-only WHERE — use efficient UNWIND approach
545
+ const paramName = `connect_${fieldName}`;
546
+ // Strip undefined values from edge objects — Neo4j driver drops undefined,
547
+ // causing param mismatches when the Cypher references nested edge properties.
548
+ params[paramName] = spec.map((item) => {
549
+ const edge = item.edge;
550
+ if (!edge)
551
+ return item;
552
+ const cleaned = {};
553
+ for (const [k, v] of Object.entries(edge))
554
+ if (v !== undefined)
555
+ cleaned[k] = v;
556
+ return { ...item, edge: cleaned };
557
+ });
558
+ lines.push('WITH n');
559
+ lines.push(`UNWIND $${paramName} AS connItem`);
560
+ lines.push(`MATCH (target:${targetLabelStr})`);
561
+ // Build WHERE from the first item's structure to determine the path
562
+ const firstItem = spec[0];
563
+ const whereConditions = this.extractConnectWhereConditions(firstItem, 'target', 'connItem');
564
+ if (whereConditions.length > 0)
565
+ lines.push(`WHERE ${whereConditions.join(' AND ')}`);
566
+ const mergePattern = this.buildRelPattern('n', relDef, 'target');
567
+ lines.push(`MERGE ${mergePattern}`);
568
+ // Handle edge properties from first item structure
569
+ const edgeProps = this.extractEdgeProperties(firstItem);
570
+ if (edgeProps.length > 0) {
571
+ // Use bare target var (no label) since target is already bound by MATCH
572
+ (0, validation_1.assertSafeIdentifier)(relDef.type, 'relationship type');
573
+ const escapedRelType = (0, validation_1.escapeIdentifier)(relDef.type);
574
+ const mergeWithVar = relDef.direction === 'OUT'
575
+ ? `(n)-[r:${escapedRelType}]->(target)`
576
+ : `(n)<-[r:${escapedRelType}]-(target)`;
577
+ // Replace MERGE line with one that has a variable
578
+ lines[lines.length - 1] = `MERGE ${mergeWithVar}`;
579
+ // r.prop is relationship property (escape), connItem.edge.prop is parameter map (no escape)
580
+ const setItems = edgeProps.map((p) => `r.${(0, validation_1.escapeIdentifier)(p)} = connItem.edge.${p}`);
581
+ lines.push(`SET ${setItems.join(', ')}`);
582
+ }
583
+ }
584
+ }
585
+ else {
586
+ // Single connect
587
+ const connectSpec = spec;
588
+ const whereSpec = connectSpec.where;
589
+ const edgeInput = connectSpec.edge;
590
+ lines.push('WITH n');
591
+ lines.push(`MATCH (target:${targetLabelStr})`);
592
+ const matchConditions = this.buildConnectionWhereConditions(whereSpec, 'target', `connect_${fieldName}`, params, targetNodeDef);
593
+ if (matchConditions.length > 0)
594
+ lines.push(`WHERE ${matchConditions.join(' AND ')}`);
595
+ if (edgeInput && Object.keys(edgeInput).length > 0) {
596
+ // Use bare target var (no label) since target is already bound by MATCH
597
+ (0, validation_1.assertSafeIdentifier)(relDef.type, 'relationship type');
598
+ const escapedRelType = (0, validation_1.escapeIdentifier)(relDef.type);
599
+ const mergeWithVar = relDef.direction === 'OUT'
600
+ ? `(n)-[r:${escapedRelType}]->(target)`
601
+ : `(n)<-[r:${escapedRelType}]-(target)`;
602
+ lines.push(`MERGE ${mergeWithVar}`);
603
+ const setItems = [];
604
+ for (const [prop, val] of Object.entries(edgeInput)) {
605
+ if (val === undefined)
606
+ continue;
607
+ const paramName = `connect_${fieldName}_edge_${prop}`;
608
+ setItems.push(`r.${(0, validation_1.escapeIdentifier)(prop)} = $${paramName}`);
609
+ params[paramName] = val;
610
+ }
611
+ if (setItems.length > 0)
612
+ lines.push(`SET ${setItems.join(', ')}`);
613
+ }
614
+ else {
615
+ const mergePattern = this.buildRelPattern('n', relDef, 'target');
616
+ lines.push(`MERGE ${mergePattern}`);
617
+ }
618
+ }
619
+ }
620
+ return lines;
621
+ }
622
+ buildDisconnects(disconnect, nodeDef, params) {
623
+ const lines = [];
624
+ for (const [fieldName, spec] of Object.entries(disconnect)) {
625
+ const relDef = nodeDef.relationships.get(fieldName);
626
+ if (!relDef)
627
+ continue;
628
+ const targetNodeDef = (0, utils_1.resolveTargetDef)(relDef.target, this.schema) ?? undefined;
629
+ // Normalize spec to an array of disconnect items
630
+ const specItems = Array.isArray(spec) ? spec : [spec];
631
+ for (let si = 0; si < specItems.length; si++) {
632
+ const item = specItems[si];
633
+ const relVar = `r_${fieldName}_${si}`;
634
+ lines.push('WITH n');
635
+ if (item == null ||
636
+ (typeof item === 'object' && Object.keys(item).length === 0)) {
637
+ // Blanket disconnect — remove all relationships of this type
638
+ const pattern = this.buildRelPattern('n', relDef, '', relVar);
639
+ lines.push(`OPTIONAL MATCH ${pattern}`);
640
+ lines.push(`DELETE ${relVar}`);
641
+ }
642
+ else {
643
+ // Specific disconnect with conditions
644
+ const disconnectSpec = item;
645
+ const whereSpec = disconnectSpec.where;
646
+ const targetVar = `target_${fieldName}_${si}`;
647
+ const pattern = this.buildRelPattern('n', relDef, targetVar, relVar, 'auto');
648
+ lines.push(`OPTIONAL MATCH ${pattern}`);
649
+ const conditions = this.buildConnectionWhereConditions(whereSpec, targetVar, `disconnect_${fieldName}_${si}`, params, targetNodeDef);
650
+ if (conditions.length > 0)
651
+ lines.push(`WHERE ${conditions.join(' AND ')}`);
652
+ lines.push(`DELETE ${relVar}`);
653
+ }
654
+ }
655
+ }
656
+ return lines;
657
+ }
658
+ /**
659
+ * Process nested relationship operations within an update input.
660
+ * Handles: create, connect, disconnect, update, and arrays of these.
661
+ */
662
+ buildUpdateRelationships(update, nodeDef, sourceVar, prefix, params, depth = 0, ancestorVars = []) {
663
+ if (depth > 5)
664
+ throw new errors_1.OGMError(`[neo4j-ogm] buildUpdateRelationships: max nesting depth (5) exceeded for prefix "${prefix}". ` +
665
+ 'Nested relationship mutations beyond depth 5 are not supported.');
666
+ const lines = [];
667
+ let varCounter = 0;
668
+ // All WITH clauses must carry forward ancestor vars + current source
669
+ const allVars = [...new Set([...ancestorVars, sourceVar])];
670
+ const withClause = `WITH ${allVars.join(', ')}`;
671
+ for (const [key, value] of Object.entries(update)) {
672
+ const relDef = nodeDef.relationships.get(key);
673
+ if (!relDef || value == null)
674
+ continue;
675
+ // Check if target is a union type — if so, value uses per-member keys
676
+ // e.g., doseTypes: { InfusionStandard: [{ update: { node: { minValue: 1 } } }] }
677
+ const isUnionTarget = !this.schema.nodes.has(relDef.target) &&
678
+ this.schema.unions?.has(relDef.target);
679
+ if (isUnionTarget) {
680
+ const unionMembers = this.schema.unions.get(relDef.target);
681
+ const memberEntries = value;
682
+ for (const [memberKey, memberValue] of Object.entries(memberEntries)) {
683
+ if (!unionMembers.includes(memberKey))
684
+ continue;
685
+ const memberNodeDef = this.schema.nodes.get(memberKey);
686
+ if (!memberNodeDef || memberValue == null)
687
+ continue;
688
+ // Recurse: treat as if the relationship points directly to the member type
689
+ const memberRelDef = {
690
+ ...relDef,
691
+ target: memberKey,
692
+ };
693
+ // Build a pseudo-update object with just this relationship
694
+ // so we can reuse buildUpdateRelationships logic
695
+ const memberItems = Array.isArray(memberValue)
696
+ ? memberValue
697
+ : [memberValue];
698
+ const memberUpdate = { [key]: memberItems };
699
+ const memberNodeDef2 = {
700
+ ...nodeDef,
701
+ relationships: new Map([[key, memberRelDef]]),
702
+ };
703
+ const memberLines = this.buildUpdateRelationships(memberUpdate, memberNodeDef2, sourceVar, `${prefix}_${key}_${memberKey}`, params, depth + 1, ancestorVars);
704
+ lines.push(...memberLines);
705
+ }
706
+ continue;
707
+ }
708
+ const targetNodeDef = this.schema.nodes.get(relDef.target);
709
+ if (!targetNodeDef)
710
+ continue;
711
+ const targetLabelStr = this.getCachedLabelString(targetNodeDef);
712
+ const relPrefix = `${prefix}_${key}`;
713
+ // Value can be an array (e.g., chapters: [{ create: ... }, { update: ... }])
714
+ // or an object (e.g., author: { connect: ... })
715
+ const items = Array.isArray(value)
716
+ ? value
717
+ : [value];
718
+ for (let i = 0; i < items.length; i++) {
719
+ const item = items[i];
720
+ const itemPrefix = `${relPrefix}_${i}`;
721
+ // --- delete (detach delete target nodes) ---
722
+ // Wrapped in CALL subquery to prevent OPTIONAL MATCH from multiplying
723
+ // pipeline rows (N existing nodes → N rows → N subsequent CREATEs).
724
+ if (item.delete) {
725
+ const deleteItems = Array.isArray(item.delete)
726
+ ? item.delete
727
+ : [item.delete];
728
+ for (let di = 0; di < deleteItems.length; di++) {
729
+ const deleteSpec = deleteItems[di];
730
+ const whereSpec = deleteSpec.where;
731
+ const delTarget = `${sourceVar}_del${varCounter}`;
732
+ const delRel = `r_del_${key}_${i}_${di}`;
733
+ varCounter++;
734
+ lines.push(withClause);
735
+ lines.push(`CALL {`);
736
+ lines.push(withClause);
737
+ const pattern = this.buildRelPattern(sourceVar, relDef, delTarget, delRel, 'auto');
738
+ lines.push(`OPTIONAL MATCH ${pattern}`);
739
+ if (whereSpec && Object.keys(whereSpec).length > 0) {
740
+ const nodeWhere = (whereSpec.node ?? whereSpec);
741
+ const conditions = [];
742
+ for (const [prop, val] of Object.entries(nodeWhere)) {
743
+ (0, validation_1.assertSafeIdentifier)(prop, 'delete where property');
744
+ const paramName = `${itemPrefix}_del${di}_${prop}`;
745
+ conditions.push(`${delTarget}.${(0, validation_1.escapeIdentifier)(prop)} = $${paramName}`);
746
+ params[paramName] = val;
747
+ }
748
+ if (conditions.length > 0)
749
+ lines.push(`WHERE ${conditions.join(' AND ')}`);
750
+ }
751
+ lines.push(`DETACH DELETE ${delTarget}`);
752
+ lines.push(`RETURN count(*) AS _del_${key}_${i}_${di}`);
753
+ lines.push(`}`);
754
+ }
755
+ }
756
+ // --- create ---
757
+ if (item.create) {
758
+ const createItems = Array.isArray(item.create)
759
+ ? item.create
760
+ : [item.create];
761
+ for (let ci = 0; ci < createItems.length; ci++) {
762
+ const createSpec = createItems[ci];
763
+ const nodeSpec = (createSpec.node ?? createSpec);
764
+ const createVar = `${sourceVar}_cr${varCounter}`;
765
+ varCounter++;
766
+ const { propString, propParams } = this.buildCreateProperties(nodeSpec, targetNodeDef, `${itemPrefix}_create${ci}`);
767
+ // Handle nested relationship creates within the created node
768
+ const nestedRelLines = this.buildCreateRelationships(nodeSpec, targetNodeDef, createVar, `${itemPrefix}_create${ci}`, params, allVars);
769
+ const propsClause = propString.length > 0
770
+ ? ` { ${propString} }`
771
+ : ` { ${this.buildGeneratedIdClause(targetNodeDef)} }`;
772
+ const relPattern = this.buildRelPattern(sourceVar, relDef, createVar);
773
+ // Wrap in CALL subquery so failed connect MATCHes don't kill the outer pipeline
774
+ const hasNestedOps = nestedRelLines.length > 0;
775
+ if (hasNestedOps) {
776
+ lines.push(withClause);
777
+ lines.push(`CALL {`);
778
+ lines.push(withClause);
779
+ }
780
+ else
781
+ lines.push(withClause);
782
+ lines.push(`CREATE (${createVar}:${targetLabelStr}${propsClause})`);
783
+ Object.assign(params, propParams);
784
+ lines.push(`CREATE ${relPattern}`);
785
+ if (hasNestedOps) {
786
+ lines.push(...nestedRelLines);
787
+ lines.push(`RETURN count(*) AS _cc_${key}_${ci}`);
788
+ lines.push(`}`);
789
+ }
790
+ }
791
+ }
792
+ // --- disconnect (runs before connect so "disconnect all + connect new" works) ---
793
+ if (item.disconnect) {
794
+ const disconnectItems = Array.isArray(item.disconnect)
795
+ ? item.disconnect
796
+ : [item.disconnect];
797
+ for (let di = 0; di < disconnectItems.length; di++) {
798
+ const disconnectSpec = disconnectItems[di];
799
+ const whereSpec = disconnectSpec.where;
800
+ const relVar = `r_disc_${key}_${i}_${di}`;
801
+ lines.push(withClause);
802
+ if (!whereSpec || Object.keys(whereSpec).length === 0) {
803
+ const pattern = this.buildRelPattern(sourceVar, relDef, '', relVar);
804
+ lines.push(`OPTIONAL MATCH ${pattern}`);
805
+ lines.push(`DELETE ${relVar}`);
806
+ }
807
+ else {
808
+ const nodeWhere = (whereSpec.node ?? whereSpec);
809
+ const discTarget = `${sourceVar}_disc${varCounter}`;
810
+ varCounter++;
811
+ const pattern = this.buildRelPattern(sourceVar, relDef, discTarget, relVar, 'auto');
812
+ lines.push(`OPTIONAL MATCH ${pattern}`);
813
+ const conditions = this.buildNodeWhereConditions(nodeWhere, discTarget, `${itemPrefix}_disc_${di}`, params, targetNodeDef);
814
+ if (conditions.length > 0)
815
+ lines.push(`WHERE ${conditions.join(' AND ')}`);
816
+ lines.push(`DELETE ${relVar}`);
817
+ }
818
+ }
819
+ }
820
+ // --- connect ---
821
+ if (item.connect) {
822
+ const connectItems = Array.isArray(item.connect)
823
+ ? item.connect
824
+ : [item.connect];
825
+ for (let ci = 0; ci < connectItems.length; ci++) {
826
+ const connectSpec = connectItems[ci];
827
+ const whereSpec = connectSpec.where;
828
+ const nodeWhere = (whereSpec?.node ?? whereSpec ?? {});
829
+ const edgeInput = connectSpec.edge;
830
+ // Wrap in CALL subquery so a failed MATCH doesn't kill the outer pipeline
831
+ lines.push(withClause);
832
+ lines.push(`CALL {`);
833
+ lines.push(withClause);
834
+ const connectVar = `${sourceVar}_conn${varCounter}`;
835
+ varCounter++;
836
+ lines.push(`MATCH (${connectVar}:${targetLabelStr})`);
837
+ const conditions = this.buildNodeWhereConditions(nodeWhere, connectVar, `${itemPrefix}_conn${ci}`, params, targetNodeDef);
838
+ if (conditions.length > 0)
839
+ lines.push(`WHERE ${conditions.join(' AND ')}`);
840
+ if (edgeInput && Object.keys(edgeInput).length > 0) {
841
+ // Use bare target var (no label) since connectVar is already bound by MATCH
842
+ const relVar = `r_conn_${key}_${ci}`;
843
+ (0, validation_1.assertSafeIdentifier)(relDef.type, 'relationship type');
844
+ const escapedRelType = (0, validation_1.escapeIdentifier)(relDef.type);
845
+ const mergePattern = relDef.direction === 'OUT'
846
+ ? `(${sourceVar})-[${relVar}:${escapedRelType}]->(${connectVar})`
847
+ : `(${sourceVar})<-[${relVar}:${escapedRelType}]-(${connectVar})`;
848
+ lines.push(`MERGE ${mergePattern}`);
849
+ const setItems = [];
850
+ for (const [prop, val] of Object.entries(edgeInput)) {
851
+ if (val === undefined)
852
+ continue;
853
+ (0, validation_1.assertSafeIdentifier)(prop, 'edge property');
854
+ const paramName = `${itemPrefix}_conn${ci}_edge_${prop}`;
855
+ setItems.push(`r_conn_${key}_${ci}.${(0, validation_1.escapeIdentifier)(prop)} = $${paramName}`);
856
+ params[paramName] = val;
857
+ }
858
+ if (setItems.length > 0)
859
+ lines.push(`SET ${setItems.join(', ')}`);
860
+ }
861
+ else {
862
+ const relPattern = this.buildRelPattern(sourceVar, relDef, connectVar);
863
+ lines.push(`MERGE ${relPattern}`);
864
+ }
865
+ lines.push(`RETURN count(*) AS _cn_${key}_${ci}`);
866
+ lines.push(`}`);
867
+ }
868
+ }
869
+ // --- update (nested) ---
870
+ if (item.update) {
871
+ const updateSpec = item.update;
872
+ const edgeUpdate = updateSpec.edge;
873
+ const nodeUpdate = (updateSpec.node ??
874
+ (edgeUpdate ? {} : updateSpec));
875
+ const updateWhere = item.where;
876
+ const updateVar = `${sourceVar}_u${varCounter}`;
877
+ const relVar = `r_${key}_${i}`;
878
+ varCounter++;
879
+ // Pre-compute SET clauses for node properties
880
+ const setClauses = [];
881
+ const setParams = {};
882
+ for (const [prop, val] of Object.entries(nodeUpdate)) {
883
+ if (targetNodeDef.relationships.has(prop))
884
+ continue;
885
+ if (val === undefined)
886
+ continue;
887
+ (0, validation_1.assertSafeIdentifier)(prop, 'nested update property');
888
+ const paramName = `${itemPrefix}_set_${prop}`;
889
+ setClauses.push(`${updateVar}.${(0, validation_1.escapeIdentifier)(prop)} = $${paramName}`);
890
+ setParams[paramName] = val;
891
+ }
892
+ // Pre-compute SET clauses for edge (relationship) properties
893
+ const edgeClauses = [];
894
+ const edgeParams = {};
895
+ if (edgeUpdate)
896
+ for (const [prop, val] of Object.entries(edgeUpdate)) {
897
+ if (val === undefined)
898
+ continue;
899
+ (0, validation_1.assertSafeIdentifier)(prop, 'edge update property');
900
+ const paramName = `${itemPrefix}_edge_${prop}`;
901
+ edgeClauses.push(`${relVar}.${(0, validation_1.escapeIdentifier)(prop)} = $${paramName}`);
902
+ edgeParams[paramName] = val;
903
+ }
904
+ // Pre-compute nested relationship operations
905
+ const nestedParams = {};
906
+ const nestedRelLines = this.buildUpdateRelationships(nodeUpdate, targetNodeDef, updateVar, itemPrefix, nestedParams, depth + 1, allVars);
907
+ // Only emit the MATCH block if there's actual work to do
908
+ const hasWork = setClauses.length > 0 ||
909
+ edgeClauses.length > 0 ||
910
+ nestedRelLines.length > 0;
911
+ if (hasWork) {
912
+ // Wrap in CALL subquery so a failed MATCH doesn't kill the outer pipeline.
913
+ // RETURN count(*) ensures exactly 1 row is returned even if MATCH finds nothing.
914
+ // WITH is required before CALL (Neo4j syntax rule after CREATE/DELETE/SET).
915
+ lines.push(withClause);
916
+ lines.push(`CALL {`);
917
+ lines.push(withClause);
918
+ const relArrow = this.buildRelPattern(sourceVar, relDef, updateVar, relVar, 'auto');
919
+ lines.push(`MATCH ${relArrow}`);
920
+ // WHERE on the target
921
+ if (updateWhere) {
922
+ const nodeWhere = (updateWhere.node ?? updateWhere);
923
+ const conditions = [];
924
+ for (const [prop, val] of Object.entries(nodeWhere)) {
925
+ (0, validation_1.assertSafeIdentifier)(prop, 'update where property');
926
+ const paramName = `${itemPrefix}_where_${prop}`;
927
+ conditions.push(`${updateVar}.${(0, validation_1.escapeIdentifier)(prop)} = $${paramName}`);
928
+ params[paramName] = val;
929
+ }
930
+ if (conditions.length > 0)
931
+ lines.push(`WHERE ${conditions.join(' AND ')}`);
932
+ }
933
+ if (setClauses.length > 0)
934
+ lines.push(`SET ${setClauses.join(', ')}`);
935
+ Object.assign(params, setParams);
936
+ if (edgeClauses.length > 0)
937
+ lines.push(`SET ${edgeClauses.join(', ')}`);
938
+ Object.assign(params, edgeParams);
939
+ Object.assign(params, nestedParams);
940
+ lines.push(...nestedRelLines);
941
+ lines.push(`RETURN count(*) AS _uc_${key}_${i}`);
942
+ lines.push(`}`);
943
+ }
944
+ }
945
+ }
946
+ }
947
+ return lines;
948
+ }
949
+ /**
950
+ * Parse a property key that may contain an operator suffix (e.g. `id_IN`, `name_CONTAINS`).
951
+ * Returns the base property name and the Cypher expression template.
952
+ */
953
+ parseOperatorSuffix(key) {
954
+ for (const [suffix, template] of MutationCompiler.OPERATOR_SUFFIXES)
955
+ if (key.endsWith(suffix))
956
+ return { baseProp: key.slice(0, -suffix.length), template };
957
+ // No suffix — simple equality
958
+ return { baseProp: key, template: '%v = $%p' };
959
+ }
960
+ /**
961
+ * Build WHERE conditions for connect/disconnect operations.
962
+ * Handles operator suffixes (_IN, _NOT, _NOT_IN, _CONTAINS, etc.),
963
+ * NOT blocks, and relationship filters (_SOME, _NONE, _ALL, _SINGLE).
964
+ *
965
+ * @param targetNodeDef - Optional node definition for the target node.
966
+ * Required to resolve relationship filters like `configurations_SOME`.
967
+ * When omitted, relationship filters are silently skipped.
968
+ */
969
+ buildNodeWhereConditions(nodeWhere, targetVar, prefix, params, targetNodeDef) {
970
+ const conditions = [];
971
+ for (const [prop, val] of Object.entries(nodeWhere))
972
+ if (prop === 'NOT') {
973
+ const notSpec = val;
974
+ for (const [notProp, notVal] of Object.entries(notSpec)) {
975
+ const { baseProp, template } = this.parseOperatorSuffix(notProp);
976
+ (0, validation_1.assertSafeIdentifier)(baseProp, 'where property');
977
+ const paramName = `${prefix}_NOT_${notProp}`;
978
+ const escapedProp = (0, validation_1.escapeIdentifier)(baseProp);
979
+ let expr;
980
+ if (template === '%v = $%p')
981
+ // Simple equality → negate to <>
982
+ expr = `${targetVar}.${escapedProp} <> $${paramName}`;
983
+ else {
984
+ // Operator suffix — wrap with NOT if not already negated
985
+ const resolved = template
986
+ .replace('%v', `${targetVar}.${escapedProp}`)
987
+ .replace('$%p', `$${paramName}`);
988
+ expr = resolved.startsWith('NOT ') ? resolved : `NOT ${resolved}`;
989
+ }
990
+ conditions.push(expr);
991
+ params[paramName] = notVal;
992
+ }
993
+ }
994
+ else {
995
+ // Check for relationship filter suffixes (_SOME, _NONE, _ALL, _SINGLE)
996
+ const relFilter = this.tryBuildRelationshipFilter(prop, val, targetVar, prefix, params, targetNodeDef);
997
+ if (relFilter) {
998
+ conditions.push(relFilter);
999
+ continue;
1000
+ }
1001
+ const { baseProp, template } = this.parseOperatorSuffix(prop);
1002
+ (0, validation_1.assertSafeIdentifier)(baseProp, 'where property');
1003
+ const paramName = `${prefix}_${prop}`;
1004
+ conditions.push(template
1005
+ .replace('%v', `${targetVar}.${(0, validation_1.escapeIdentifier)(baseProp)}`)
1006
+ .replace('$%p', `$${paramName}`));
1007
+ params[paramName] = val;
1008
+ }
1009
+ return conditions;
1010
+ }
1011
+ /**
1012
+ * Build WHERE conditions from a connection-level WHERE spec.
1013
+ * Handles `node`, `NOT`, `AND`, `OR` at the connection level.
1014
+ *
1015
+ * Connection WHERE format: `{ node: {...}, NOT: { node: {...} }, AND: [...], OR: [...] }`
1016
+ */
1017
+ buildConnectionWhereConditions(whereSpec, targetVar, prefix, params, targetNodeDef) {
1018
+ if (!whereSpec || Object.keys(whereSpec).length === 0)
1019
+ return [];
1020
+ const conditions = [];
1021
+ // Direct node conditions
1022
+ if (whereSpec.node) {
1023
+ const nodeConditions = this.buildNodeWhereConditions(whereSpec.node, targetVar, prefix, params, targetNodeDef);
1024
+ conditions.push(...nodeConditions);
1025
+ }
1026
+ // NOT wrapper — negate the inner connection conditions
1027
+ if (whereSpec.NOT) {
1028
+ const notSpec = whereSpec.NOT;
1029
+ const innerConditions = this.buildConnectionWhereConditions(notSpec, targetVar, `${prefix}_NOT`, params, targetNodeDef);
1030
+ if (innerConditions.length > 0)
1031
+ conditions.push(`NOT (${innerConditions.join(' AND ')})`);
1032
+ }
1033
+ // AND — all sub-conditions must hold
1034
+ if (Array.isArray(whereSpec.AND))
1035
+ for (let i = 0; i < whereSpec.AND.length; i++) {
1036
+ const sub = this.buildConnectionWhereConditions(whereSpec.AND[i], targetVar, `${prefix}_AND${i}`, params, targetNodeDef);
1037
+ conditions.push(...sub);
1038
+ }
1039
+ // OR — any sub-condition must hold
1040
+ if (Array.isArray(whereSpec.OR)) {
1041
+ const orClauses = [];
1042
+ for (let i = 0; i < whereSpec.OR.length; i++) {
1043
+ const sub = this.buildConnectionWhereConditions(whereSpec.OR[i], targetVar, `${prefix}_OR${i}`, params, targetNodeDef);
1044
+ if (sub.length > 0)
1045
+ orClauses.push(`(${sub.join(' AND ')})`);
1046
+ }
1047
+ if (orClauses.length > 0)
1048
+ conditions.push(`(${orClauses.join(' OR ')})`);
1049
+ }
1050
+ // Fallback: if no recognized keys, treat the whole spec as node conditions
1051
+ // (backward compatibility for simple `{ id: '...' }` style)
1052
+ if (!whereSpec.node && !whereSpec.NOT && !whereSpec.AND && !whereSpec.OR) {
1053
+ // Reject unsupported connection WHERE keys (e.g., "edge")
1054
+ if (whereSpec.edge !== undefined)
1055
+ throw new errors_1.OGMError(`Connection WHERE with "edge" filters is not supported in mutations. ` +
1056
+ `Only "node", "NOT", "AND", "OR" keys or direct property conditions are allowed.`);
1057
+ const fallbackConditions = this.buildNodeWhereConditions(whereSpec, targetVar, prefix, params, targetNodeDef);
1058
+ conditions.push(...fallbackConditions);
1059
+ }
1060
+ return conditions;
1061
+ }
1062
+ /**
1063
+ * Try to compile a relationship filter condition (_SOME, _NONE, _ALL, _SINGLE).
1064
+ * Returns the Cypher expression string or null if the key is not a relationship filter.
1065
+ */
1066
+ tryBuildRelationshipFilter(key, value, nodeVar, prefix, params, targetNodeDef) {
1067
+ if (!targetNodeDef || typeof value !== 'object' || value === null)
1068
+ return null;
1069
+ // Detect suffix and extract field name
1070
+ let suffix = null;
1071
+ let fieldName = '';
1072
+ for (const s of RELATIONSHIP_SUFFIXES)
1073
+ if (key.endsWith(s)) {
1074
+ suffix = s;
1075
+ fieldName = key.slice(0, -s.length);
1076
+ break;
1077
+ }
1078
+ // If no suffix, check if bare key is a relationship (defaults to _SOME)
1079
+ if (!suffix) {
1080
+ const relDef = targetNodeDef.relationships.get(key);
1081
+ if (!relDef)
1082
+ return null;
1083
+ suffix = '_SOME';
1084
+ fieldName = key;
1085
+ }
1086
+ const relDef = targetNodeDef.relationships.get(fieldName);
1087
+ if (!relDef)
1088
+ return null;
1089
+ const relTargetDef = (0, utils_1.resolveTargetDef)(relDef.target, this.schema);
1090
+ if (!relTargetDef)
1091
+ return null;
1092
+ const relVar = `rf${this.relFilterCounter++}`;
1093
+ const innerWhere = value;
1094
+ // Build the MATCH pattern
1095
+ const edgePart = `[:${(0, validation_1.escapeIdentifier)(relDef.type)}]`;
1096
+ const escapedTarget = (0, validation_1.escapeIdentifier)(relDef.target);
1097
+ const pattern = relDef.direction === 'IN'
1098
+ ? `(${nodeVar})<-${edgePart}-(${relVar}:${escapedTarget})`
1099
+ : `(${nodeVar})-${edgePart}->(${relVar}:${escapedTarget})`;
1100
+ // Recursively compile inner conditions
1101
+ const innerConditions = this.buildNodeWhereConditions(innerWhere, relVar, `${prefix}_${fieldName}`, params, relTargetDef);
1102
+ const innerClause = innerConditions.length > 0
1103
+ ? ` WHERE ${innerConditions.join(' AND ')}`
1104
+ : '';
1105
+ switch (suffix) {
1106
+ case '_SOME':
1107
+ return `EXISTS { MATCH ${pattern}${innerClause} }`;
1108
+ case '_NONE':
1109
+ return `NOT EXISTS { MATCH ${pattern}${innerClause} }`;
1110
+ case '_ALL':
1111
+ // Double negation: all match ≡ none fail to match
1112
+ if (!innerClause)
1113
+ return '';
1114
+ return `NOT EXISTS { MATCH ${pattern} WHERE NOT (${innerConditions.join(' AND ')}) }`;
1115
+ case '_SINGLE':
1116
+ return `size([${relVar} IN [(${pattern}${innerClause} | ${relVar})] | ${relVar}]) = 1`;
1117
+ default:
1118
+ return null;
1119
+ }
1120
+ }
1121
+ /**
1122
+ * Build a Cypher relationship pattern string.
1123
+ *
1124
+ * @param sourceVar - Cypher variable for the source node
1125
+ * @param relDef - Relationship definition
1126
+ * @param targetVar - Cypher variable for the target node (empty string for anonymous)
1127
+ * @param relVar - Optional relationship variable (omit for anonymous `[:TYPE]`)
1128
+ * @param targetLabel - Target label override. Pass explicit label, `'auto'` to use
1129
+ * `relDef.target`, or omit for no label on the target node.
1130
+ */
1131
+ buildRelPattern(sourceVar, relDef, targetVar, relVar, targetLabel) {
1132
+ (0, validation_1.assertSafeIdentifier)(relDef.type, 'relationship type');
1133
+ const escapedType = (0, validation_1.escapeIdentifier)(relDef.type);
1134
+ const relPart = relVar ? `[${relVar}:${escapedType}]` : `[:${escapedType}]`;
1135
+ let targetPart;
1136
+ if (targetLabel) {
1137
+ const label = targetLabel === 'auto' ? relDef.target : targetLabel;
1138
+ const escapedLabel = (0, validation_1.assertSafeLabel)(label);
1139
+ targetPart = `(${targetVar}:${escapedLabel})`;
1140
+ }
1141
+ else if (targetVar)
1142
+ targetPart = `(${targetVar})`;
1143
+ else
1144
+ targetPart = `()`;
1145
+ if (relDef.direction === 'OUT')
1146
+ return `(${sourceVar})-${relPart}->${targetPart}`;
1147
+ return `(${sourceVar})<-${relPart}-${targetPart}`;
1148
+ }
1149
+ buildGeneratedIdClause(nodeDef) {
1150
+ const parts = [];
1151
+ for (const [, propDef] of nodeDef.properties)
1152
+ if (propDef.isGenerated)
1153
+ parts.push(`${(0, validation_1.escapeIdentifier)(propDef.name)}: randomUUID()`);
1154
+ return parts.join(', ');
1155
+ }
1156
+ getCascadeVarName(index) {
1157
+ return `cascade_${index}`;
1158
+ }
1159
+ /**
1160
+ * Check if any item in an array connect spec has relationship filters in its WHERE clause.
1161
+ * Relationship filters require EXISTS subqueries which can't use UNWIND param references.
1162
+ */
1163
+ arrayConnectHasRelationshipFilters(items, targetNodeDef) {
1164
+ for (const item of items) {
1165
+ const whereSpec = item.where;
1166
+ if (!whereSpec)
1167
+ continue;
1168
+ const nodeWhere = (whereSpec.node ?? whereSpec);
1169
+ for (const key of Object.keys(nodeWhere)) {
1170
+ // Strip operator suffixes to get the base field name
1171
+ let fieldName = key;
1172
+ for (const s of RELATIONSHIP_SUFFIXES)
1173
+ if (key.endsWith(s)) {
1174
+ fieldName = key.slice(0, -s.length);
1175
+ break;
1176
+ }
1177
+ // If the base field name is a relationship on the target, it's a rel filter
1178
+ if (targetNodeDef.relationships.has(fieldName))
1179
+ return true;
1180
+ // Also check if the value is an object (nested filter on a relationship)
1181
+ const val = nodeWhere[key];
1182
+ if (typeof val === 'object' &&
1183
+ val !== null &&
1184
+ !Array.isArray(val) &&
1185
+ targetNodeDef.relationships.has(key))
1186
+ return true;
1187
+ }
1188
+ }
1189
+ return false;
1190
+ }
1191
+ extractConnectWhereConditions(item, targetVar, itemVar) {
1192
+ const conditions = [];
1193
+ const whereSpec = item.where;
1194
+ if (!whereSpec)
1195
+ return conditions;
1196
+ const nodeWhere = (whereSpec.node ?? whereSpec);
1197
+ for (const key of Object.keys(nodeWhere)) {
1198
+ const { baseProp, template } = this.parseOperatorSuffix(key);
1199
+ (0, validation_1.assertSafeIdentifier)(baseProp, 'connect where property');
1200
+ // Replace template placeholders with dynamic UNWIND references
1201
+ // Note: itemVar map access is NOT escaped (parameter map keys, not Cypher identifiers)
1202
+ const valueRef = `${itemVar}.where.node.${key}`;
1203
+ conditions.push(template
1204
+ .replace('%v', `${targetVar}.${(0, validation_1.escapeIdentifier)(baseProp)}`)
1205
+ .replace('$%p', valueRef));
1206
+ }
1207
+ return conditions;
1208
+ }
1209
+ extractEdgeProperties(item) {
1210
+ const edgeSpec = item.edge;
1211
+ if (!edgeSpec)
1212
+ return [];
1213
+ const keys = Object.keys(edgeSpec).filter((k) => edgeSpec[k] !== undefined);
1214
+ for (const key of keys)
1215
+ (0, validation_1.assertSafeIdentifier)(key, 'edge property');
1216
+ return keys;
1217
+ }
1218
+ }
1219
+ exports.MutationCompiler = MutationCompiler;
1220
+ /**
1221
+ * Operator suffixes supported in connect/disconnect WHERE conditions.
1222
+ * Maps suffix → Cypher operator template (use %v for variable, %p for param).
1223
+ */
1224
+ MutationCompiler.OPERATOR_SUFFIXES = [
1225
+ ['_NOT_IN', 'NOT %v IN $%p'],
1226
+ ['_IN', '%v IN $%p'],
1227
+ ['_NOT_CONTAINS', 'NOT %v CONTAINS $%p'],
1228
+ ['_CONTAINS', '%v CONTAINS $%p'],
1229
+ ['_NOT_STARTS_WITH', 'NOT %v STARTS WITH $%p'],
1230
+ ['_STARTS_WITH', '%v STARTS WITH $%p'],
1231
+ ['_NOT_ENDS_WITH', 'NOT %v ENDS WITH $%p'],
1232
+ ['_ENDS_WITH', '%v ENDS WITH $%p'],
1233
+ ['_LTE', '%v <= $%p'],
1234
+ ['_LT', '%v < $%p'],
1235
+ ['_GTE', '%v >= $%p'],
1236
+ ['_GT', '%v > $%p'],
1237
+ ['_NOT', '%v <> $%p'],
1238
+ ];
1239
+ //# sourceMappingURL=mutation.compiler.js.map