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
package/README.md ADDED
@@ -0,0 +1,1121 @@
1
+ # grafeo-ogm
2
+
3
+ **A type-safe Object-Graph Mapper for Neo4j, driven by GraphQL SDL.**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/grafeo-ogm.svg)](https://www.npmjs.com/package/grafeo-ogm)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-3178C6.svg)](https://www.typescriptlang.org/)
8
+ [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-339933.svg)](https://nodejs.org/)
9
+ [![Neo4j](https://img.shields.io/badge/Neo4j-5.x-008CC1.svg)](https://neo4j.com/)
10
+
11
+ Define your graph model once in `.graphql` and get fully typed CRUD operations, optimized Cypher compilation, and TypeScript code generation -- no boilerplate, no Cartesian products, no injection vulnerabilities.
12
+
13
+ > **grafeo** (*/ˈɡra.fe.o/*) -- from Greek *grapho* (write, draw), the root behind "graph." Because your data model should be written once and understood everywhere.
14
+
15
+ ---
16
+
17
+ ## Table of Contents
18
+
19
+ - [Why grafeo-ogm?](#why-grafeo-ogm)
20
+ - [Quick Start](#quick-start)
21
+ - [Features Overview](#features-overview)
22
+ - [Schema Definition](#schema-definition)
23
+ - [Query API](#query-api)
24
+ - [Mutation API](#mutation-api)
25
+ - [Advanced Features](#advanced-features)
26
+ - [Type Generation](#type-generation)
27
+ - [Testing Utilities](#testing-utilities)
28
+ - [Security](#security)
29
+ - [Migration from @neo4j/graphql-ogm](#migration-from-neo4jgraphql-ogm)
30
+ - [API Reference](#api-reference)
31
+ - [Contributing](#contributing)
32
+ - [License](#license)
33
+
34
+ ---
35
+
36
+ ## Why grafeo-ogm?
37
+
38
+ The official [`@neo4j/graphql-ogm`](https://github.com/neo4j/graphql/tree/changeset-release/lts/packages/ogm/) was deprecated with no development path beyond v5. grafeo-ogm is a community continuation that maintains **full backward compatibility** while addressing five fundamental problems the original library could never patch:
39
+
40
+ | Problem | @neo4j/graphql-ogm | grafeo-ogm |
41
+ |---|---|---|
42
+ | **Deprecation** | Abandoned by Neo4j; no path beyond v5 | Actively maintained; evolves with your schema |
43
+ | **Query efficiency** | `OPTIONAL MATCH` chains that create Cartesian products across relationships | Pattern comprehensions scoped to matched nodes -- O(1) rows per node regardless of relationship count |
44
+ | **Security** | No identifier validation, no injection prevention, no parameterization enforcement | 7 security controls: parameterization, identifier regex, label validation, prototype pollution prevention, Lucene escaping, depth limiting, sort direction validation |
45
+ | **Type generation** | Monolithic multi-MB output; required a running Neo4j driver at build time | Modular emitter pipeline; no driver needed at build time; output sized proportionally to schema |
46
+ | **Customization** | No union type support, no subquery isolation, no edge property queries | Full union/interface support, `CALL` subquery isolation for mutations, connection fields with edge properties |
47
+
48
+ ### Pattern Comprehensions vs. OPTIONAL MATCH
49
+
50
+ The most impactful difference is how relationships are queried. The old OGM chains `OPTIONAL MATCH` clauses that multiply intermediate rows:
51
+
52
+ ```
53
+ 10 variants x 5 categories x 3 tags = 150 intermediate rows per node
54
+ Query 200 nodes = 30,000 rows processed
55
+ ```
56
+
57
+ grafeo-ogm uses pattern comprehensions that evaluate each relationship independently inside the RETURN projection:
58
+
59
+ ```cypher
60
+ RETURN n {
61
+ .id, .name,
62
+ variants: [(n)-[:HAS_VARIANT]->(n0:Variant) | n0 { .id, .value }],
63
+ status: head([(n)-[:HAS_STATUS]->(n0:Status) | n0 { .id, .name }])
64
+ }
65
+ -- Always 1 row per node. Adding relationships adds list expressions, never rows.
66
+ ```
67
+
68
+ | Query | Old OGM (rows) | grafeo-ogm (rows) | Reduction |
69
+ |---|---|---|---|
70
+ | 1 node, 10+5+3 rels | 150 | 1 | 150x |
71
+ | 50 nodes, same rels | 7,500 | 50 | 150x |
72
+ | 200 nodes, same rels | 30,000 | 200 | 150x |
73
+
74
+ ### CALL Subquery Isolation
75
+
76
+ Mutations with multiple connect/disconnect operations are wrapped in `CALL { ... }` subqueries. Each operation runs in isolation -- no Cartesian products, no duplicate relationships, no silently dropped operations when a disconnect target doesn't exist.
77
+
78
+ ---
79
+
80
+ ## Quick Start
81
+
82
+ ### Installation
83
+
84
+ ```bash
85
+ # npm
86
+ npm install grafeo-ogm neo4j-driver graphql
87
+
88
+ # pnpm
89
+ pnpm add grafeo-ogm neo4j-driver graphql
90
+
91
+ # yarn
92
+ yarn add grafeo-ogm neo4j-driver graphql
93
+ ```
94
+
95
+ ### 1. Define your schema
96
+
97
+ ```graphql
98
+ # schema.graphql
99
+ type Book @node
100
+ @fulltext(indexes: [{ name: "BookSearch", fields: ["title"] }]) {
101
+ id: ID! @id @unique
102
+ title: String!
103
+ isbn: String @unique
104
+ published: DateTime
105
+ author: Author! @relationship(type: "WRITTEN_BY", direction: OUT)
106
+ categories: [Category!]! @relationship(type: "IN_CATEGORY", direction: OUT)
107
+ }
108
+
109
+ type Author @node {
110
+ id: ID! @id @unique
111
+ name: String!
112
+ books: [Book!]! @relationship(type: "WRITTEN_BY", direction: IN)
113
+ }
114
+
115
+ type Category @node {
116
+ id: ID! @id @unique
117
+ name: String!
118
+ }
119
+ ```
120
+
121
+ ### 2. Initialize the OGM
122
+
123
+ ```typescript
124
+ import { OGM } from 'grafeo-ogm';
125
+ import neo4j from 'neo4j-driver';
126
+ import fs from 'fs';
127
+
128
+ const driver = neo4j.driver(
129
+ 'bolt://localhost:7687',
130
+ neo4j.auth.basic('neo4j', 'password'),
131
+ );
132
+ const typeDefs = fs.readFileSync('./schema.graphql', 'utf-8');
133
+
134
+ const ogm = new OGM({ typeDefs, driver });
135
+ ```
136
+
137
+ ### 3. Basic CRUD
138
+
139
+ ```typescript
140
+ const Book = ogm.model('Book');
141
+
142
+ // Create
143
+ const result = await Book.create({
144
+ input: [{
145
+ title: 'Graph Databases',
146
+ isbn: '978-1491930892',
147
+ author: { connect: { where: { node: { name: 'Jim Webber' } } } },
148
+ }],
149
+ });
150
+
151
+ // Read
152
+ const books = await Book.find({
153
+ where: { title_CONTAINS: 'Graph' },
154
+ select: {
155
+ id: true,
156
+ title: true,
157
+ author: { select: { name: true } },
158
+ },
159
+ });
160
+
161
+ // Update
162
+ await Book.update({
163
+ where: { isbn: '978-1491930892' },
164
+ update: { title: 'Graph Databases, 2nd Edition' },
165
+ });
166
+
167
+ // Delete
168
+ await Book.delete({ where: { isbn: '978-1491930892' } });
169
+ ```
170
+
171
+ ### 4. Transactions
172
+
173
+ ```typescript
174
+ await ogm.$transaction(async (ctx) => {
175
+ await Book.create({ input: [{ title: 'Book A' }] }, { context: ctx });
176
+ await Book.create({ input: [{ title: 'Book B' }] }, { context: ctx });
177
+ // Both committed atomically, or both rolled back on error
178
+ });
179
+ ```
180
+
181
+ ---
182
+
183
+ ## Features Overview
184
+
185
+ | Feature | Description |
186
+ |---|---|
187
+ | **Prisma-like query API** | `find`, `findFirst`, `findUnique`, `findFirstOrThrow`, `findUniqueOrThrow`, `create`, `createMany`, `update`, `updateMany`, `delete`, `deleteMany`, `upsert`, `count`, `aggregate` |
188
+ | **GraphQL SDL schemas** | Define nodes, relationships, interfaces, unions, enums, and fulltext indexes using Neo4j GraphQL directives |
189
+ | **Full TypeScript type safety** | Code generation produces typed models, where inputs, create/update inputs, select fields, and connection types |
190
+ | **Pattern comprehensions** | Relationship traversal without Cartesian products -- O(1) rows per node |
191
+ | **Typed `select` API** | `select: { id: true, author: { select: { name: true } } }` with compile-time type checking |
192
+ | **Fulltext search** | Node and relationship indexes with phrase matching, score thresholds, and logical operators (`AND`, `OR`, `NOT`) |
193
+ | **Subgraph operations** | Clone and delete entire subgraphs via APOC with reference relationship re-attachment |
194
+ | **Runtime multi-label** | Add/remove/filter by labels at query time: `labels: ['Active']`, `setLabels()` |
195
+ | **Interface models** | Polymorphic read queries across all types implementing a shared interface, with `__typename` discrimination |
196
+ | **Connection types** | Cursor pagination with edge properties via `*Connection` fields |
197
+ | **Nested mutations** | Create, connect, disconnect, and cascade-delete related nodes in a single operation |
198
+ | **Transaction support** | `$transaction()` for atomic multi-operation writes with automatic commit/rollback |
199
+ | **Raw Cypher** | `$queryRaw()` and `$executeRaw()` for escape hatches |
200
+ | **7 security controls** | Parameterization, identifier validation, label validation, prototype pollution prevention, Lucene escaping, depth limiting, sort direction validation |
201
+ | **Testing utilities** | `CypherAssert`, `Neo4jRecordFactory`, `SelectionSetFactory` for unit testing without a database |
202
+ | **Backward compatible** | Drop-in replacement for `@neo4j/graphql-ogm` |
203
+
204
+ ---
205
+
206
+ ## Schema Definition
207
+
208
+ grafeo-ogm uses standard GraphQL SDL with Neo4j directives to define your graph model.
209
+
210
+ ### Supported Directives
211
+
212
+ | Directive | Target | Description |
213
+ |---|---|---|
214
+ | `@node` | Type | Marks a type as a Neo4j node. Optional `labels` argument for multi-label nodes |
215
+ | `@relationship` | Field | Defines a relationship with `type`, `direction` (`IN`/`OUT`), and optional `properties` |
216
+ | `@relationshipProperties` | Type | Marks a type as relationship properties (edge data) |
217
+ | `@id` | Field | Auto-generates a UUID on create |
218
+ | `@unique` | Field | Creates a uniqueness constraint in Neo4j |
219
+ | `@cypher` | Field | Computed field resolved via a custom Cypher statement |
220
+ | `@default` | Field | Sets a default value on create |
221
+ | `@fulltext` | Type | Defines fulltext search indexes with `name` and `fields` |
222
+
223
+ ### Supported Scalar Types
224
+
225
+ | Type | Neo4j Mapping | Notes |
226
+ |---|---|---|
227
+ | `String` | String | |
228
+ | `Int` | Integer | |
229
+ | `Float` | Float | |
230
+ | `Boolean` | Boolean | |
231
+ | `ID` | String | Typically used with `@id` for UUID generation |
232
+ | `DateTime` | DateTime | ISO 8601 format |
233
+ | `Date` | Date | |
234
+ | `Time` | Time | |
235
+ | `LocalTime` | LocalTime | |
236
+ | `LocalDateTime` | LocalDateTime | |
237
+ | `Duration` | Duration | |
238
+ | `BigInt` | Integer | 64-bit integer |
239
+ | `Point` | Point | Geographical coordinates (WGS-84) |
240
+ | `CartesianPoint` | Point | 2D/3D Cartesian coordinates |
241
+
242
+ ### Relationships
243
+
244
+ ```graphql
245
+ type Book @node {
246
+ # Singular relationship (one author per book)
247
+ author: Author! @relationship(type: "WRITTEN_BY", direction: OUT)
248
+
249
+ # Array relationship (many categories)
250
+ categories: [Category!]! @relationship(type: "IN_CATEGORY", direction: OUT)
251
+
252
+ # Relationship with edge properties
253
+ reviews: [Review!]! @relationship(
254
+ type: "HAS_REVIEW",
255
+ direction: OUT,
256
+ properties: "HasReview"
257
+ )
258
+ }
259
+
260
+ type HasReview @relationshipProperties {
261
+ createdAt: DateTime
262
+ rating: Int
263
+ }
264
+ ```
265
+
266
+ ### Interfaces
267
+
268
+ ```graphql
269
+ interface Entity @node {
270
+ id: ID! @id
271
+ name: String!
272
+ }
273
+
274
+ type User @node implements Entity {
275
+ id: ID! @id
276
+ name: String!
277
+ email: String!
278
+ }
279
+
280
+ type Organization @node implements Entity {
281
+ id: ID! @id
282
+ name: String!
283
+ website: String
284
+ }
285
+ ```
286
+
287
+ Interface types can be queried polymorphically via `ogm.interfaceModel('Entity')`. Results include `__typename` to discriminate concrete types.
288
+
289
+ ### Enums
290
+
291
+ ```graphql
292
+ enum Status {
293
+ DRAFT
294
+ PUBLISHED
295
+ ARCHIVED
296
+ }
297
+
298
+ type Book @node {
299
+ status: Status
300
+ }
301
+ ```
302
+
303
+ ### Fulltext Indexes
304
+
305
+ ```graphql
306
+ type Book @node
307
+ @fulltext(indexes: [
308
+ { name: "BookSearch", fields: ["title", "description"] }
309
+ ]) {
310
+ title: String!
311
+ description: String
312
+ }
313
+ ```
314
+
315
+ Relationship fulltext indexes are defined on `@relationshipProperties` types:
316
+
317
+ ```graphql
318
+ type WrittenBy @relationshipProperties
319
+ @fulltext(indexes: [{ name: "AuthorRoleSearch", fields: ["role"] }]) {
320
+ role: String
321
+ }
322
+ ```
323
+
324
+ ---
325
+
326
+ ## Query API
327
+
328
+ ### Find Methods
329
+
330
+ ```typescript
331
+ const Book = ogm.model('Book');
332
+
333
+ // find — returns all matching nodes
334
+ const books = await Book.find({
335
+ where: { title_CONTAINS: 'Graph' },
336
+ select: { id: true, title: true },
337
+ options: { sort: [{ title: 'ASC' }], limit: 10, offset: 0 },
338
+ });
339
+
340
+ // findFirst — returns first match or null
341
+ const book = await Book.findFirst({
342
+ where: { title_CONTAINS: 'Graph' },
343
+ select: { id: true, title: true },
344
+ });
345
+
346
+ // findUnique — find by unique identifier or null
347
+ const book = await Book.findUnique({
348
+ where: { isbn: '978-1491930892' },
349
+ select: { id: true, title: true },
350
+ });
351
+
352
+ // findFirstOrThrow — throws RecordNotFoundError if no match
353
+ const book = await Book.findFirstOrThrow({
354
+ where: { title: 'Exact Title' },
355
+ });
356
+
357
+ // findUniqueOrThrow — throws RecordNotFoundError if not found
358
+ const book = await Book.findUniqueOrThrow({
359
+ where: { id: 'book-1' },
360
+ });
361
+ ```
362
+
363
+ ### WHERE Operators
364
+
365
+ **Comparison operators:**
366
+
367
+ | Operator | Cypher | Example |
368
+ |---|---|---|
369
+ | *(equality)* | `=` | `{ name: 'Wireless Mouse' }` |
370
+ | `_NOT` | `<>` | `{ name_NOT: 'x' }` |
371
+ | `_IN` | `IN` | `{ id_IN: ['a', 'b'] }` |
372
+ | `_NOT_IN` | `NOT IN` | `{ id_NOT_IN: ['a'] }` |
373
+ | `_GT` | `>` | `{ price_GT: 10 }` |
374
+ | `_GTE` | `>=` | `{ price_GTE: 10 }` |
375
+ | `_LT` | `<` | `{ price_LT: 50 }` |
376
+ | `_LTE` | `<=` | `{ price_LTE: 50 }` |
377
+
378
+ **String operators:**
379
+
380
+ | Operator | Cypher | Example |
381
+ |---|---|---|
382
+ | `_CONTAINS` | `CONTAINS` | `{ name_CONTAINS: 'alb' }` |
383
+ | `_NOT_CONTAINS` | `NOT CONTAINS` | `{ name_NOT_CONTAINS: 'x' }` |
384
+ | `_STARTS_WITH` | `STARTS WITH` | `{ name_STARTS_WITH: 'A' }` |
385
+ | `_NOT_STARTS_WITH` | `NOT STARTS WITH` | `{ name_NOT_STARTS_WITH: 'X' }` |
386
+ | `_ENDS_WITH` | `ENDS WITH` | `{ name_ENDS_WITH: 'ol' }` |
387
+ | `_NOT_ENDS_WITH` | `NOT ENDS WITH` | `{ name_NOT_ENDS_WITH: 'x' }` |
388
+ | `_MATCHES` | `=~` (regex) | `{ name_MATCHES: '^A.*' }` |
389
+
390
+ **Relationship quantifiers:**
391
+
392
+ | Operator | Meaning | Example |
393
+ |---|---|---|
394
+ | *(bare key)* or `_SOME` | At least one related node matches | `{ categories_SOME: { name: 'Tech' } }` |
395
+ | `_NONE` | No related node matches | `{ categories_NONE: { name: 'Fiction' } }` |
396
+ | `_ALL` | Every related node matches | `{ categories_ALL: { name: 'Tech' } }` |
397
+ | `_SINGLE` | Exactly one related node matches | `{ categories_SINGLE: { name: 'Tech' } }` |
398
+
399
+ **Logical operators:**
400
+
401
+ ```typescript
402
+ await Book.find({
403
+ where: {
404
+ OR: [
405
+ { title_CONTAINS: 'Graph' },
406
+ { title_CONTAINS: 'Neo4j' },
407
+ ],
408
+ NOT: { status: 'ARCHIVED' },
409
+ },
410
+ });
411
+ ```
412
+
413
+ **Null handling:**
414
+
415
+ ```typescript
416
+ // Scalar null check
417
+ await Book.find({ where: { isbn: null } }); // WHERE n.isbn IS NULL
418
+
419
+ // Relationship existence
420
+ await Book.find({ where: { author: null } }); // NOT EXISTS pattern
421
+ ```
422
+
423
+ **Connection filters (with edge properties):**
424
+
425
+ ```typescript
426
+ await Book.find({
427
+ where: {
428
+ categoriesConnection_SOME: {
429
+ node: { name: 'Technology' },
430
+ edge: { isPrimary: true },
431
+ },
432
+ },
433
+ });
434
+ ```
435
+
436
+ ### Selection Modes
437
+
438
+ grafeo-ogm supports two mutually exclusive selection modes. Providing both `select` and `selectionSet` throws an error.
439
+
440
+ **Typed `select` API (recommended):**
441
+
442
+ ```typescript
443
+ const books = await Book.find({
444
+ select: {
445
+ id: true,
446
+ title: true,
447
+ author: { select: { name: true } }, // nested selection
448
+ categories: { where: { name_STARTS_WITH: 'Tech' }, select: { name: true } }, // with filter
449
+ },
450
+ });
451
+
452
+ // Simple relationship: true returns all scalar fields
453
+ const books = await Book.find({
454
+ select: { id: true, author: true },
455
+ });
456
+ ```
457
+
458
+ **String `selectionSet` (legacy, fully supported):**
459
+
460
+ ```typescript
461
+ const books = await Book.find({
462
+ selectionSet: `{
463
+ id
464
+ title
465
+ author { name }
466
+ categories { id name }
467
+ }`,
468
+ });
469
+
470
+ // Or set a default on the model instance
471
+ Book.selectionSet = `{ id title }`;
472
+ ```
473
+
474
+ **Default (no selection):** Returns all scalar fields of the matched node.
475
+
476
+ ### Sorting and Pagination
477
+
478
+ ```typescript
479
+ const books = await Book.find({
480
+ where: { status: 'PUBLISHED' },
481
+ options: {
482
+ sort: [{ title: 'ASC' }, { published: 'DESC' }],
483
+ limit: 20,
484
+ offset: 40,
485
+ },
486
+ });
487
+ ```
488
+
489
+ ---
490
+
491
+ ## Mutation API
492
+
493
+ ### Create
494
+
495
+ ```typescript
496
+ const Book = ogm.model('Book');
497
+
498
+ // Create one or more nodes
499
+ const result = await Book.create({
500
+ input: [{
501
+ title: 'Graph Databases',
502
+ isbn: '978-1491930892',
503
+ author: { connect: { where: { node: { name: 'Jim Webber' } } } },
504
+ categories: {
505
+ create: [{ node: { name: 'Technology' }, edge: { isPrimary: true } }],
506
+ },
507
+ }],
508
+ });
509
+ // result: { info: { nodesCreated, relationshipsCreated }, books: [...] }
510
+ ```
511
+
512
+ ### Create Many
513
+
514
+ ```typescript
515
+ // Bulk create (scalar properties only, no nested ops)
516
+ const { count } = await Book.createMany({
517
+ data: [
518
+ { title: 'Book A', isbn: '111' },
519
+ { title: 'Book B', isbn: '222' },
520
+ ],
521
+ skipDuplicates: true, // uses MERGE on unique fields
522
+ });
523
+ ```
524
+
525
+ ### Update
526
+
527
+ ```typescript
528
+ const result = await Book.update({
529
+ where: { id: 'book-1' },
530
+ update: { title: 'Updated Title' },
531
+ connect: {
532
+ categories: [{ where: { node: { name: 'Science' } } }],
533
+ },
534
+ disconnect: {
535
+ categories: { where: { node: { name: 'Fiction' } } },
536
+ },
537
+ });
538
+ ```
539
+
540
+ ### Update Many
541
+
542
+ ```typescript
543
+ // Bulk update (returns count only, no connect/disconnect)
544
+ const { count } = await Book.updateMany({
545
+ where: { published_LT: '2020-01-01' },
546
+ data: { isbn: null },
547
+ });
548
+ ```
549
+
550
+ ### Delete
551
+
552
+ ```typescript
553
+ // Simple delete
554
+ await Book.delete({ where: { id: 'book-1' } });
555
+
556
+ // Cascade delete related nodes
557
+ await Book.delete({
558
+ where: { id: 'book-1' },
559
+ delete: {
560
+ categories: [{ where: { node: { name: 'Temp Category' } } }],
561
+ },
562
+ });
563
+ ```
564
+
565
+ ### Delete Many
566
+
567
+ ```typescript
568
+ const { count } = await Book.deleteMany({
569
+ where: { title_CONTAINS: 'Draft' },
570
+ });
571
+ ```
572
+
573
+ ### Upsert
574
+
575
+ ```typescript
576
+ // Create if not found, update if exists (scalar properties only)
577
+ const book = await Book.upsert({
578
+ where: { isbn: '978-1491930892' },
579
+ create: { title: 'Graph Databases', isbn: '978-1491930892' },
580
+ update: { title: 'Graph Databases, Updated' },
581
+ select: { id: true, title: true },
582
+ });
583
+ ```
584
+
585
+ ### Label Management
586
+
587
+ ```typescript
588
+ // Set, add, or remove labels on existing nodes
589
+ await Book.setLabels({
590
+ where: { id: 'book-1' },
591
+ addLabels: ['Published', 'Featured'],
592
+ removeLabels: ['Draft'],
593
+ });
594
+
595
+ // Query with additional runtime labels
596
+ const activeBooks = await Book.find({
597
+ where: { title_CONTAINS: 'Graph' },
598
+ labels: ['Active'], // MATCH (n:`Book`:`Active`)
599
+ });
600
+ ```
601
+
602
+ ### Nested Mutations
603
+
604
+ Create, connect, disconnect, and cascade-delete related nodes in a single operation:
605
+
606
+ ```typescript
607
+ await Book.create({
608
+ input: [{
609
+ title: 'Graph Databases',
610
+ categories: {
611
+ create: [{
612
+ node: { name: 'Technology' },
613
+ edge: { isPrimary: true }, // relationship properties
614
+ }],
615
+ },
616
+ author: {
617
+ connect: {
618
+ where: { node: { name: 'Jim Webber' } },
619
+ edge: { role: 'Primary Author' },
620
+ },
621
+ },
622
+ }],
623
+ });
624
+ ```
625
+
626
+ ---
627
+
628
+ ## Advanced Features
629
+
630
+ ### Fulltext Search
631
+
632
+ Requires a `@fulltext` directive on the node type and the index to exist in Neo4j. Use `assertIndexesAndConstraints` to create indexes automatically:
633
+
634
+ ```typescript
635
+ await ogm.assertIndexesAndConstraints({ options: { create: true } });
636
+
637
+ const Book = ogm.model('Book');
638
+
639
+ // Basic search
640
+ const results = await Book.find({
641
+ fulltext: {
642
+ BookSearch: { phrase: 'graph databases' },
643
+ },
644
+ select: { id: true, title: true },
645
+ });
646
+
647
+ // With score threshold
648
+ const highRelevance = await Book.find({
649
+ fulltext: {
650
+ BookSearch: { phrase: 'graph', score: 1.0 },
651
+ },
652
+ });
653
+
654
+ // Combined with where filters
655
+ const affordable = await Book.find({
656
+ fulltext: {
657
+ BookSearch: { phrase: 'art' },
658
+ },
659
+ where: { price_LT: 10 },
660
+ });
661
+
662
+ // Logical composition
663
+ const results = await Book.find({
664
+ fulltext: {
665
+ OR: [
666
+ { BookSearch: { phrase: 'graph' } },
667
+ { BookSearch: { phrase: 'database' } },
668
+ ],
669
+ },
670
+ });
671
+ ```
672
+
673
+ ### Subgraph Operations
674
+
675
+ Clone or delete entire subgraphs using APOC procedures. Requires the APOC plugin installed in Neo4j.
676
+
677
+ ```typescript
678
+ import { cloneSubgraph, deleteSubgraph } from 'grafeo-ogm';
679
+
680
+ // Clone a subgraph rooted at a node
681
+ const cloneResult = await cloneSubgraph(
682
+ 'source-node-id',
683
+ {
684
+ ownedLabels: ['Book', 'Chapter', 'Section'],
685
+ ownedRelationships: ['HAS_CHAPTER', 'HAS_SECTION'],
686
+ referenceRelationships: [
687
+ { fromLabel: 'Book', relationshipType: 'WRITTEN_BY', direction: 'OUT' },
688
+ ],
689
+ },
690
+ transaction,
691
+ );
692
+ // cloneResult: { clonedRootId, idMapping: Map<original, cloned> }
693
+
694
+ // Delete a subgraph
695
+ const deleteResult = await deleteSubgraph(
696
+ 'root-node-id',
697
+ {
698
+ ownedLabels: ['Book', 'Chapter'],
699
+ ownedRelationships: ['HAS_CHAPTER'],
700
+ referenceRelationships: [],
701
+ },
702
+ transaction,
703
+ );
704
+ ```
705
+
706
+ ### Raw Cypher
707
+
708
+ ```typescript
709
+ // Read query
710
+ const results = await ogm.$queryRaw<{ name: string; count: number }>(
711
+ 'MATCH (a:Author)-[:WRITTEN_BY]-(b:Book) RETURN a.name AS name, count(b) AS count',
712
+ );
713
+
714
+ // Write operation
715
+ const { recordsAffected } = await ogm.$executeRaw(
716
+ 'MATCH (b:Book) WHERE b.published < $cutoff DELETE b',
717
+ { cutoff: '2020-01-01' },
718
+ );
719
+ ```
720
+
721
+ ### Transactions
722
+
723
+ ```typescript
724
+ // Callback style — interactive transaction
725
+ await ogm.$transaction(async (ctx) => {
726
+ await Book.create({ input: [{ title: 'Book A' }] }, { context: ctx });
727
+ await Book.update(
728
+ { where: { id: 'book-1' }, update: { title: 'Updated' } },
729
+ { context: ctx },
730
+ );
731
+ // Committed on success, rolled back on error
732
+ });
733
+
734
+ // Sequential style — array of operations
735
+ const [books, authors] = await ogm.$transaction([
736
+ (ctx) => Book.find({ select: { id: true } }, { context: ctx }),
737
+ (ctx) => Author.find({ select: { id: true } }, { context: ctx }),
738
+ ]);
739
+ ```
740
+
741
+ ### Interface Models
742
+
743
+ Query across all types implementing a shared interface:
744
+
745
+ ```typescript
746
+ const Entity = ogm.interfaceModel('Entity');
747
+
748
+ // Returns both Users and Organizations with __typename
749
+ const entities = await Entity.find({
750
+ where: { name_CONTAINS: 'Acme' },
751
+ selectionSet: `{ id name }`,
752
+ });
753
+ // [{ id: '1', name: 'Acme Corp', __typename: 'Organization' }, ...]
754
+
755
+ // Count across all implementing types
756
+ const total = await Entity.count();
757
+
758
+ // Aggregate across all implementing types
759
+ const agg = await Entity.aggregate({
760
+ aggregate: { count: true },
761
+ });
762
+ ```
763
+
764
+ Interface models are **read-only**. Use `ogm.model('User')` or `ogm.model('Organization')` for mutations.
765
+
766
+ ### Aggregation
767
+
768
+ ```typescript
769
+ const Book = ogm.model('Book');
770
+
771
+ // Simple count
772
+ const total = await Book.count();
773
+ const published = await Book.count({ where: { status: 'PUBLISHED' } });
774
+
775
+ // Field aggregation (min, max, average)
776
+ const priceStats = await Book.aggregate({
777
+ aggregate: { count: true, price: true },
778
+ });
779
+ // { count: 42, price: { min: 9.99, max: 59.99, average: 24.50 } }
780
+
781
+ // With where filter
782
+ const publishedStats = await Book.aggregate({
783
+ where: { status: 'PUBLISHED' },
784
+ aggregate: { count: true, price: true },
785
+ });
786
+ ```
787
+
788
+ ---
789
+
790
+ ## Type Generation
791
+
792
+ Generate TypeScript types from your schema for compile-time safety. **No Neo4j driver connection needed at build time.**
793
+
794
+ ### Usage
795
+
796
+ ```typescript
797
+ import { generateTypes } from 'grafeo-ogm';
798
+ import fs from 'fs';
799
+
800
+ const result = await generateTypes({
801
+ typeDefs: fs.readFileSync('./schema.graphql', 'utf-8'),
802
+ outFile: './src/generated/ogm-types.ts',
803
+ });
804
+
805
+ console.log(`Generated ${result.typeCount} types in ${result.durationMs}ms`);
806
+ ```
807
+
808
+ ### Using Generated Types
809
+
810
+ ```typescript
811
+ import { OGM } from 'grafeo-ogm';
812
+ import type { ModelMap } from './generated/ogm-types';
813
+
814
+ const ogm = new OGM<ModelMap>({ typeDefs, driver });
815
+
816
+ // ogm.model('Book') is now fully typed:
817
+ const Book = ogm.model('Book');
818
+
819
+ // Book.find() returns typed Book objects
820
+ // Book.create() accepts typed BookCreateInput
821
+ // Book.find({ where: ... }) validates BookWhere
822
+ // Book.find({ select: ... }) validates BookSelectFields
823
+ ```
824
+
825
+ ### Configuration
826
+
827
+ | Option | Type | Default | Description |
828
+ |---|---|---|---|
829
+ | `typeDefs` | `string` | *(required)* | Raw GraphQL schema string |
830
+ | `outFile` | `string` | *(required)* | Output file path |
831
+ | `config.packageName` | `string` | `'grafeo-ogm'` | Package name in generated import statements |
832
+ | `config.stringMatchesFilter` | `boolean` | `true` | Enable `_MATCHES` regex operator in where types |
833
+ | `config.formatOutput` | `boolean` | `true` | Format output with Prettier |
834
+ | `config.prettierConfig` | `object` | `undefined` | Custom Prettier configuration |
835
+ | `config.header` | `string` | `undefined` | Custom header comment prepended to the file |
836
+
837
+ ### Custom Package Name
838
+
839
+ If you install grafeo-ogm under an npm alias:
840
+
841
+ ```typescript
842
+ await generateTypes({
843
+ typeDefs,
844
+ outFile: './src/generated/ogm-types.ts',
845
+ config: { packageName: '@myorg/ogm' },
846
+ });
847
+ ```
848
+
849
+ ### What Gets Generated
850
+
851
+ The generator runs 16 specialized emitters to produce:
852
+
853
+ - **Node types** -- TypeScript interfaces for each `@node` type (e.g., `Book`, `Author`)
854
+ - **Where types** -- All operator suffixes per field (e.g., `BookWhere` with `title_CONTAINS`, `id_IN`)
855
+ - **CreateInput / UpdateInput** -- Typed mutation inputs with nested relationship operations
856
+ - **ConnectInput / DisconnectInput** -- Relationship operation inputs
857
+ - **SelectFields** -- Types for the programmatic `select: {}` API
858
+ - **SelectResult** -- Return types narrowed by selection
859
+ - **ConnectionWhere** -- Edge property filtering types
860
+ - **SortInput** -- Sort options with direction constraints
861
+ - **AggregateSelection** -- Aggregation return types
862
+ - **MutationResponse** -- Typed mutation return shapes
863
+ - **FulltextInput** -- Fulltext search input types
864
+ - **ModelMap / InterfaceModelMap** -- Generic maps for `ogm.model()` and `ogm.interfaceModel()`
865
+
866
+ ---
867
+
868
+ ## Testing Utilities
869
+
870
+ Import from `grafeo-ogm/testing`:
871
+
872
+ ```typescript
873
+ import {
874
+ CypherAssert,
875
+ Neo4jRecordFactory,
876
+ SelectionSetFactory,
877
+ } from 'grafeo-ogm/testing';
878
+ ```
879
+
880
+ ### CypherAssert
881
+
882
+ Assert Cypher queries contain expected patterns:
883
+
884
+ ```typescript
885
+ // Check that a Cypher string contains a fragment
886
+ CypherAssert.assertContains(cypher, 'MATCH (n:`Book`)');
887
+
888
+ // Check parameter values
889
+ CypherAssert.assertParams(params, { param0: 'Graph Databases' });
890
+ ```
891
+
892
+ ### Neo4jRecordFactory
893
+
894
+ Build mock Neo4j records for unit tests without a running database:
895
+
896
+ ```typescript
897
+ const records = Neo4jRecordFactory.create([
898
+ { id: '1', title: 'Test Book', published: '2024-01-01' },
899
+ { id: '2', title: 'Another Book', published: '2024-06-15' },
900
+ ]);
901
+ ```
902
+
903
+ ### SelectionSetFactory
904
+
905
+ Build selection sets programmatically:
906
+
907
+ ```typescript
908
+ const selectionSet = SelectionSetFactory.build(`{
909
+ id
910
+ title
911
+ author { name }
912
+ }`);
913
+ ```
914
+
915
+ ---
916
+
917
+ ## Security
918
+
919
+ grafeo-ogm implements 7 layered security controls. Every user-provided value passes through validation before it reaches the Cypher string.
920
+
921
+ ### 1. Parameterization
922
+
923
+ All user values become `$paramN` references. No value is ever string-interpolated into Cypher:
924
+
925
+ ```typescript
926
+ // Input: { name: "'; DROP DATABASE neo4j; //" }
927
+ // Cypher: n.name = $param0
928
+ // Params: { param0: "'; DROP DATABASE neo4j; //" }
929
+ ```
930
+
931
+ ### 2. Identifier Validation
932
+
933
+ Field names, sort fields, and aggregate fields are validated against `/^[a-zA-Z_][a-zA-Z0-9_]*$/`. Any identifier containing special characters is rejected before query compilation.
934
+
935
+ ### 3. Label Validation
936
+
937
+ Labels used in `MATCH`, `SET`, and `REMOVE` are validated with the same regex, preventing label injection attacks.
938
+
939
+ ### 4. Prototype Pollution Prevention
940
+
941
+ Keys `__proto__`, `constructor`, and `prototype` are blocked in all `where` and `update` inputs. The ResultMapper uses `Object.create(null)` for all output objects to prevent prototype chain attacks.
942
+
943
+ ### 5. Lucene Escaping
944
+
945
+ Special Lucene characters (`+ - && || ! ( ) { } [ ] ^ " ~ * ? : \ /`) are escaped before being passed to fulltext queries.
946
+
947
+ ### 6. Depth Limiting
948
+
949
+ - WhereCompiler: max recursion depth of 10
950
+ - SelectionCompiler: configurable max relationship traversal depth (default 5)
951
+
952
+ Prevents denial-of-service via deeply nested or circular inputs.
953
+
954
+ ### 7. Sort Direction Validation
955
+
956
+ Sort directions are strictly validated as `"ASC"` or `"DESC"`. Any other value -- including injected Cypher fragments -- is rejected.
957
+
958
+ ### Best Practices
959
+
960
+ - Always use the OGM's query methods instead of raw Cypher when possible
961
+ - Use `generateTypes()` for compile-time validation of inputs
962
+ - Set appropriate depth limits for your schema's relationship density
963
+ - Use `$transaction()` for multi-step operations to maintain consistency
964
+ - Keep `@unique` constraints on fields used for `findUnique` lookups
965
+
966
+ ---
967
+
968
+ ## Migration from @neo4j/graphql-ogm
969
+
970
+ grafeo-ogm is a **drop-in replacement** for the deprecated `@neo4j/graphql-ogm`. Existing code works without changes, and you can adopt new features incrementally.
971
+
972
+ ### Step 1: Replace the dependency
973
+
974
+ ```bash
975
+ npm uninstall @neo4j/graphql-ogm
976
+ npm install grafeo-ogm
977
+ ```
978
+
979
+ ### Step 2: Update imports
980
+
981
+ ```typescript
982
+ // Before
983
+ import { OGM } from '@neo4j/graphql-ogm';
984
+
985
+ // After
986
+ import { OGM } from 'grafeo-ogm';
987
+ ```
988
+
989
+ ### Step 3: Update type generation
990
+
991
+ Replace your existing type generation script to use `generateTypes()` from grafeo-ogm. No Neo4j driver connection is needed at build time.
992
+
993
+ ### Step 4 (optional): Adopt new features
994
+
995
+ These are incremental improvements you can adopt at your own pace:
996
+
997
+ | Old Pattern | New Alternative | Benefit |
998
+ |---|---|---|
999
+ | `selectionSet` strings | `select: { field: true }` | Compile-time type checking |
1000
+ | `find()` + manual null check | `findFirstOrThrow()` | Throws `RecordNotFoundError` automatically |
1001
+ | `find({ where, options: { limit: 1 } })` | `findFirst({ where })` | Cleaner API, returns `T \| null` |
1002
+ | Manual bulk loops | `createMany()`, `updateMany()`, `deleteMany()` | Single query, returns `{ count }` |
1003
+ | Manual label management | `setLabels()`, `labels` parameter | Built-in label add/remove/filter |
1004
+
1005
+ ### What's the Same
1006
+
1007
+ - `OGM` constructor signature: `{ typeDefs, driver }` -- identical
1008
+ - `ogm.model('Name')` -- same API
1009
+ - `model.find()`, `model.create()`, `model.update()`, `model.delete()` -- same signatures
1010
+ - `selectionSet` -- fully supported
1011
+ - `init()` -- still works (now a no-op since schema parsing is synchronous)
1012
+ - `assertIndexesAndConstraints()` -- same behavior
1013
+
1014
+ ### What Changed (Under the Hood)
1015
+
1016
+ - Queries use **pattern comprehensions** instead of `OPTIONAL MATCH` -- better performance, same results
1017
+ - Mutations use **CALL subquery isolation** -- prevents Cartesian products in connect/disconnect
1018
+ - All identifiers are **validated and escaped** -- injection-safe by default
1019
+ - Type generation is **modular and driver-free** -- smaller output, faster builds
1020
+
1021
+ ---
1022
+
1023
+ ## API Reference
1024
+
1025
+ ### OGM
1026
+
1027
+ | Method | Description |
1028
+ |---|---|
1029
+ | `new OGM(config)` | Create an OGM instance with `{ typeDefs, driver, logger?, features? }` |
1030
+ | `model(name)` | Get a typed Model for a node type |
1031
+ | `interfaceModel(name)` | Get a typed InterfaceModel for an interface type |
1032
+ | `$queryRaw(cypher, params?)` | Execute a read query and return mapped results |
1033
+ | `$executeRaw(cypher, params?)` | Execute a write operation and return affected counts |
1034
+ | `$transaction(fn)` | Execute operations in an atomic transaction |
1035
+ | `assertIndexesAndConstraints(options?)` | Create fulltext indexes and uniqueness constraints |
1036
+ | `close()` | Clear caches and release references |
1037
+
1038
+ ### Model
1039
+
1040
+ | Method | Returns | Description |
1041
+ |---|---|---|
1042
+ | `find(options?)` | `T[]` | Find all nodes matching filters |
1043
+ | `findFirst(options?)` | `T \| null` | Find first matching node |
1044
+ | `findUnique(options)` | `T \| null` | Find by unique identifier |
1045
+ | `findFirstOrThrow(options?)` | `T` | Find first match or throw `RecordNotFoundError` |
1046
+ | `findUniqueOrThrow(options)` | `T` | Find by unique identifier or throw |
1047
+ | `create(options)` | `MutationResponse` | Create one or more nodes with optional relationships |
1048
+ | `createMany(options)` | `{ count }` | Bulk create nodes |
1049
+ | `update(options)` | `MutationResponse` | Update matched nodes with connect/disconnect |
1050
+ | `updateMany(options)` | `{ count }` | Bulk update matched nodes |
1051
+ | `delete(options)` | `DeleteInfo` | Delete matched nodes with optional cascade |
1052
+ | `deleteMany(options)` | `{ count }` | Bulk delete matched nodes |
1053
+ | `upsert(options)` | `T` | Create if not found, update if exists |
1054
+ | `count(options?)` | `number` | Count matching nodes |
1055
+ | `aggregate(options)` | `AggregateResult` | Aggregate values (min, max, avg, count) |
1056
+ | `setLabels(options)` | `void` | Add or remove labels on matching nodes |
1057
+
1058
+ ### InterfaceModel
1059
+
1060
+ | Method | Returns | Description |
1061
+ |---|---|---|
1062
+ | `find(options?)` | `T[]` | Find nodes across all implementing types |
1063
+ | `findFirst(options?)` | `T \| null` | Find first match |
1064
+ | `findUnique(options)` | `T \| null` | Find by unique identifier |
1065
+ | `findFirstOrThrow(options?)` | `T` | Find first match or throw |
1066
+ | `findUniqueOrThrow(options)` | `T` | Find by unique identifier or throw |
1067
+ | `count(options?)` | `number` | Count matching nodes |
1068
+ | `aggregate(options)` | `AggregateResult` | Aggregate across implementing types |
1069
+
1070
+ ### generateTypes(options)
1071
+
1072
+ | Option | Type | Default | Description |
1073
+ |---|---|---|---|
1074
+ | `typeDefs` | `string` | *(required)* | Raw GraphQL schema string |
1075
+ | `outFile` | `string` | *(required)* | Output file path |
1076
+ | `config.packageName` | `string` | `'grafeo-ogm'` | Package name in generated imports |
1077
+ | `config.stringMatchesFilter` | `boolean` | `true` | Enable `_MATCHES` operator |
1078
+ | `config.formatOutput` | `boolean` | `true` | Format with Prettier |
1079
+ | `config.prettierConfig` | `object` | — | Custom Prettier config |
1080
+ | `config.header` | `string` | — | Custom header comment |
1081
+
1082
+ Returns `GenerateTypesResult`:
1083
+
1084
+ | Field | Type | Description |
1085
+ |---|---|---|
1086
+ | `outputPath` | `string` | Absolute path of the written file |
1087
+ | `typeCount` | `number` | Number of exported types/interfaces/enums |
1088
+ | `fileSize` | `number` | File size in bytes |
1089
+ | `durationMs` | `number` | Generation duration in milliseconds |
1090
+ | `warnings` | `GeneratorWarning[]` | Non-fatal warnings encountered |
1091
+
1092
+ For detailed usage examples, see the [`examples/`](./examples/) directory.
1093
+
1094
+ ---
1095
+
1096
+ ## Contributing
1097
+
1098
+ Contributions are welcome. Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
1099
+
1100
+ ```bash
1101
+ # Clone and install
1102
+ git clone https://github.com/aleguzman/grafeo-ogm.git
1103
+ cd grafeo-ogm
1104
+ pnpm install
1105
+
1106
+ # Run tests
1107
+ pnpm test
1108
+
1109
+ # Lint and format
1110
+ pnpm run lint
1111
+ pnpm run format
1112
+
1113
+ # Build
1114
+ pnpm run build
1115
+ ```
1116
+
1117
+ ---
1118
+
1119
+ ## License
1120
+
1121
+ [MIT](LICENSE)