joist-codegen 2.3.0-next.12 → 2.3.0-next.120

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 (428) hide show
  1. package/build/EntityDbMetadata.cjs +672 -0
  2. package/build/EntityDbMetadata.cjs.map +1 -0
  3. package/build/EntityDbMetadata.d.cts +301 -0
  4. package/build/EntityDbMetadata.d.cts.map +1 -0
  5. package/build/EntityDbMetadata.d.mts +301 -0
  6. package/build/EntityDbMetadata.d.mts.map +1 -0
  7. package/build/EntityDbMetadata.js +550 -664
  8. package/build/EntityDbMetadata.js.map +1 -1
  9. package/build/_virtual/_rolldown/runtime.cjs +23 -0
  10. package/build/_virtual/_rolldown/runtime.js +5 -0
  11. package/build/assignTags.cjs +45 -0
  12. package/build/assignTags.cjs.map +1 -0
  13. package/build/{assignTags.d.ts → assignTags.d.cts} +8 -4
  14. package/build/assignTags.d.cts.map +1 -0
  15. package/build/assignTags.d.mts +16 -0
  16. package/build/assignTags.d.mts.map +1 -0
  17. package/build/assignTags.js +37 -59
  18. package/build/assignTags.js.map +1 -1
  19. package/build/codemods/Codemod.cjs +2 -0
  20. package/build/codemods/Codemod.d.cts +17 -0
  21. package/build/codemods/Codemod.d.cts.map +1 -0
  22. package/build/codemods/Codemod.d.mts +17 -0
  23. package/build/codemods/Codemod.d.mts.map +1 -0
  24. package/build/codemods/Codemod.js +2 -3
  25. package/build/codemods/JscodeshiftMod.cjs +42 -0
  26. package/build/codemods/JscodeshiftMod.cjs.map +1 -0
  27. package/build/codemods/JscodeshiftMod.d.cts +14 -0
  28. package/build/codemods/JscodeshiftMod.d.cts.map +1 -0
  29. package/build/codemods/JscodeshiftMod.d.mts +14 -0
  30. package/build/codemods/JscodeshiftMod.d.mts.map +1 -0
  31. package/build/codemods/JscodeshiftMod.js +33 -33
  32. package/build/codemods/JscodeshiftMod.js.map +1 -1
  33. package/build/codemods/codemod_0001-rename_has_async_property.cjs +30 -0
  34. package/build/codemods/codemod_0001-rename_has_async_property.cjs.map +1 -0
  35. package/build/codemods/codemod_0001-rename_has_async_property.d.cts +9 -0
  36. package/build/codemods/codemod_0001-rename_has_async_property.d.cts.map +1 -0
  37. package/build/codemods/codemod_0001-rename_has_async_property.d.mts +9 -0
  38. package/build/codemods/codemod_0001-rename_has_async_property.d.mts.map +1 -0
  39. package/build/codemods/codemod_0001-rename_has_async_property.js +21 -25
  40. package/build/codemods/codemod_0001-rename_has_async_property.js.map +1 -1
  41. package/build/codemods/codemod_0002-rename_async_query_fields.cjs +50 -0
  42. package/build/codemods/codemod_0002-rename_async_query_fields.cjs.map +1 -0
  43. package/build/codemods/codemod_0002-rename_async_query_fields.d.cts +9 -0
  44. package/build/codemods/codemod_0002-rename_async_query_fields.d.cts.map +1 -0
  45. package/build/codemods/codemod_0002-rename_async_query_fields.d.mts +9 -0
  46. package/build/codemods/codemod_0002-rename_async_query_fields.d.mts.map +1 -0
  47. package/build/codemods/codemod_0002-rename_async_query_fields.js +38 -52
  48. package/build/codemods/codemod_0002-rename_async_query_fields.js.map +1 -1
  49. package/build/codemods/codemod_0003-rename_find_paginated.cjs +25 -0
  50. package/build/codemods/codemod_0003-rename_find_paginated.cjs.map +1 -0
  51. package/build/codemods/codemod_0003-rename_find_paginated.d.cts +9 -0
  52. package/build/codemods/codemod_0003-rename_find_paginated.d.cts.map +1 -0
  53. package/build/codemods/codemod_0003-rename_find_paginated.d.mts +9 -0
  54. package/build/codemods/codemod_0003-rename_find_paginated.d.mts.map +1 -0
  55. package/build/codemods/codemod_0003-rename_find_paginated.js +17 -26
  56. package/build/codemods/codemod_0003-rename_find_paginated.js.map +1 -1
  57. package/build/codemods/index.cjs +49 -0
  58. package/build/codemods/index.cjs.map +1 -0
  59. package/build/codemods/index.d.cts +12 -0
  60. package/build/codemods/index.d.cts.map +1 -0
  61. package/build/codemods/index.d.mts +12 -0
  62. package/build/codemods/index.d.mts.map +1 -0
  63. package/build/codemods/index.js +34 -42
  64. package/build/codemods/index.js.map +1 -1
  65. package/build/config.cjs +377 -0
  66. package/build/config.cjs.map +1 -0
  67. package/build/config.d.cts +181 -0
  68. package/build/config.d.cts.map +1 -0
  69. package/build/config.d.mts +181 -0
  70. package/build/config.d.mts.map +1 -0
  71. package/build/config.js +306 -356
  72. package/build/config.js.map +1 -1
  73. package/build/docs/cache.cjs +62 -0
  74. package/build/docs/cache.cjs.map +1 -0
  75. package/build/docs/cache.d.cts +17 -0
  76. package/build/docs/cache.d.cts.map +1 -0
  77. package/build/docs/cache.d.mts +17 -0
  78. package/build/docs/cache.d.mts.map +1 -0
  79. package/build/docs/cache.js +52 -53
  80. package/build/docs/cache.js.map +1 -1
  81. package/build/docs/generate-metadata-docs.cjs +32 -0
  82. package/build/docs/generate-metadata-docs.cjs.map +1 -0
  83. package/build/docs/generate-metadata-docs.d.cts +9 -0
  84. package/build/docs/generate-metadata-docs.d.cts.map +1 -0
  85. package/build/docs/generate-metadata-docs.d.mts +9 -0
  86. package/build/docs/generate-metadata-docs.d.mts.map +1 -0
  87. package/build/docs/generate-metadata-docs.js +19 -24
  88. package/build/docs/generate-metadata-docs.js.map +1 -1
  89. package/build/docs/index.cjs +90 -0
  90. package/build/docs/index.cjs.map +1 -0
  91. package/build/docs/index.d.cts +16 -0
  92. package/build/docs/index.d.cts.map +1 -0
  93. package/build/docs/index.d.mts +16 -0
  94. package/build/docs/index.d.mts.map +1 -0
  95. package/build/docs/index.js +75 -80
  96. package/build/docs/index.js.map +1 -1
  97. package/build/docs/markdown.cjs +181 -0
  98. package/build/docs/markdown.cjs.map +1 -0
  99. package/build/docs/{markdown.d.ts → markdown.d.cts} +14 -12
  100. package/build/docs/markdown.d.cts.map +1 -0
  101. package/build/docs/markdown.d.mts +32 -0
  102. package/build/docs/markdown.d.mts.map +1 -0
  103. package/build/docs/markdown.js +152 -220
  104. package/build/docs/markdown.js.map +1 -1
  105. package/build/docs/parsing.cjs +117 -0
  106. package/build/docs/parsing.cjs.map +1 -0
  107. package/build/docs/parsing.d.cts +48 -0
  108. package/build/docs/parsing.d.cts.map +1 -0
  109. package/build/docs/parsing.d.mts +48 -0
  110. package/build/docs/parsing.d.mts.map +1 -0
  111. package/build/docs/parsing.js +89 -88
  112. package/build/docs/parsing.js.map +1 -1
  113. package/build/findEntityScopes.cjs +119 -0
  114. package/build/findEntityScopes.cjs.map +1 -0
  115. package/build/findEntityScopes.d.cts +14 -0
  116. package/build/findEntityScopes.d.cts.map +1 -0
  117. package/build/findEntityScopes.d.mts +14 -0
  118. package/build/findEntityScopes.d.mts.map +1 -0
  119. package/build/findEntityScopes.js +83 -111
  120. package/build/findEntityScopes.js.map +1 -1
  121. package/build/foreignKeyOrdering.cjs +52 -0
  122. package/build/foreignKeyOrdering.cjs.map +1 -0
  123. package/build/foreignKeyOrdering.d.cts +8 -0
  124. package/build/foreignKeyOrdering.d.cts.map +1 -0
  125. package/build/foreignKeyOrdering.d.mts +8 -0
  126. package/build/foreignKeyOrdering.d.mts.map +1 -0
  127. package/build/foreignKeyOrdering.js +46 -55
  128. package/build/foreignKeyOrdering.js.map +1 -1
  129. package/build/generate.cjs +128 -0
  130. package/build/generate.cjs.map +1 -0
  131. package/build/generate.d.cts +17 -0
  132. package/build/generate.d.cts.map +1 -0
  133. package/build/generate.d.mts +17 -0
  134. package/build/generate.d.mts.map +1 -0
  135. package/build/generate.js +111 -124
  136. package/build/generate.js.map +1 -1
  137. package/build/generateEntitiesFile.cjs +46 -0
  138. package/build/generateEntitiesFile.cjs.map +1 -0
  139. package/build/generateEntitiesFile.d.cts +11 -0
  140. package/build/generateEntitiesFile.d.cts.map +1 -0
  141. package/build/generateEntitiesFile.d.mts +11 -0
  142. package/build/generateEntitiesFile.d.mts.map +1 -0
  143. package/build/generateEntitiesFile.js +28 -25
  144. package/build/generateEntitiesFile.js.map +1 -1
  145. package/build/generateEntityCodegenFile.cjs +1011 -0
  146. package/build/generateEntityCodegenFile.cjs.map +1 -0
  147. package/build/generateEntityCodegenFile.d.cts +16 -0
  148. package/build/generateEntityCodegenFile.d.cts.map +1 -0
  149. package/build/generateEntityCodegenFile.d.mts +16 -0
  150. package/build/generateEntityCodegenFile.d.mts.map +1 -0
  151. package/build/generateEntityCodegenFile.js +794 -827
  152. package/build/generateEntityCodegenFile.js.map +1 -1
  153. package/build/generateEntityFile.cjs +23 -0
  154. package/build/generateEntityFile.cjs.map +1 -0
  155. package/build/generateEntityFile.d.cts +9 -0
  156. package/build/generateEntityFile.d.cts.map +1 -0
  157. package/build/generateEntityFile.d.mts +9 -0
  158. package/build/generateEntityFile.d.mts.map +1 -0
  159. package/build/generateEntityFile.js +11 -8
  160. package/build/generateEntityFile.js.map +1 -1
  161. package/build/generateEntityTestFile.cjs +25 -0
  162. package/build/generateEntityTestFile.cjs.map +1 -0
  163. package/build/generateEntityTestFile.d.cts +9 -0
  164. package/build/generateEntityTestFile.d.cts.map +1 -0
  165. package/build/generateEntityTestFile.d.mts +9 -0
  166. package/build/generateEntityTestFile.d.mts.map +1 -0
  167. package/build/generateEntityTestFile.js +10 -7
  168. package/build/generateEntityTestFile.js.map +1 -1
  169. package/build/generateEnumFile.cjs +73 -0
  170. package/build/generateEnumFile.cjs.map +1 -0
  171. package/build/generateEnumFile.d.cts +9 -0
  172. package/build/generateEnumFile.d.cts.map +1 -0
  173. package/build/generateEnumFile.d.mts +9 -0
  174. package/build/generateEnumFile.d.mts.map +1 -0
  175. package/build/generateEnumFile.js +33 -43
  176. package/build/generateEnumFile.js.map +1 -1
  177. package/build/generateFactoriesFiles.cjs +28 -0
  178. package/build/generateFactoriesFiles.cjs.map +1 -0
  179. package/build/generateFactoriesFiles.d.cts +7 -0
  180. package/build/generateFactoriesFiles.d.cts.map +1 -0
  181. package/build/generateFactoriesFiles.d.mts +7 -0
  182. package/build/generateFactoriesFiles.d.mts.map +1 -0
  183. package/build/generateFactoriesFiles.js +21 -16
  184. package/build/generateFactoriesFiles.js.map +1 -1
  185. package/build/generateFlushFunction.cjs +61 -0
  186. package/build/generateFlushFunction.cjs.map +1 -0
  187. package/build/{generateFlushFunction.d.ts → generateFlushFunction.d.cts} +8 -5
  188. package/build/generateFlushFunction.d.cts.map +1 -0
  189. package/build/generateFlushFunction.d.mts +23 -0
  190. package/build/generateFlushFunction.d.mts.map +1 -0
  191. package/build/generateFlushFunction.js +32 -78
  192. package/build/generateFlushFunction.js.map +1 -1
  193. package/build/generateMetadataFile.cjs +315 -0
  194. package/build/generateMetadataFile.cjs.map +1 -0
  195. package/build/generateMetadataFile.d.cts +10 -0
  196. package/build/generateMetadataFile.d.cts.map +1 -0
  197. package/build/generateMetadataFile.d.mts +10 -0
  198. package/build/generateMetadataFile.d.mts.map +1 -0
  199. package/build/generateMetadataFile.js +151 -157
  200. package/build/generateMetadataFile.js.map +1 -1
  201. package/build/generatePgEnumFile.cjs +21 -0
  202. package/build/generatePgEnumFile.cjs.map +1 -0
  203. package/build/generatePgEnumFile.d.cts +9 -0
  204. package/build/generatePgEnumFile.d.cts.map +1 -0
  205. package/build/generatePgEnumFile.d.mts +9 -0
  206. package/build/generatePgEnumFile.d.mts.map +1 -0
  207. package/build/generatePgEnumFile.js +12 -9
  208. package/build/generatePgEnumFile.js.map +1 -1
  209. package/build/index.cjs +135 -0
  210. package/build/index.cjs.map +1 -0
  211. package/build/index.d.cts +12 -0
  212. package/build/index.d.cts.map +1 -0
  213. package/build/index.d.mts +12 -0
  214. package/build/index.d.mts.map +1 -0
  215. package/build/index.js +109 -127
  216. package/build/index.js.map +1 -1
  217. package/build/inheritance.cjs +258 -0
  218. package/build/inheritance.cjs.map +1 -0
  219. package/build/inheritance.d.cts +13 -0
  220. package/build/inheritance.d.cts.map +1 -0
  221. package/build/inheritance.d.mts +13 -0
  222. package/build/inheritance.d.mts.map +1 -0
  223. package/build/inheritance.js +238 -247
  224. package/build/inheritance.js.map +1 -1
  225. package/build/installSkills.cjs +68 -0
  226. package/build/installSkills.cjs.map +1 -0
  227. package/build/installSkills.d.cts +16 -0
  228. package/build/installSkills.d.cts.map +1 -0
  229. package/build/installSkills.d.mts +16 -0
  230. package/build/installSkills.d.mts.map +1 -0
  231. package/build/installSkills.js +67 -0
  232. package/build/installSkills.js.map +1 -0
  233. package/build/keywords.cjs +7 -0
  234. package/build/keywords.cjs.map +1 -0
  235. package/build/keywords.d.cts +5 -0
  236. package/build/keywords.d.cts.map +1 -0
  237. package/build/keywords.d.mts +5 -0
  238. package/build/keywords.d.mts.map +1 -0
  239. package/build/keywords.js +5 -4
  240. package/build/keywords.js.map +1 -1
  241. package/build/loadMetadata.cjs +39 -0
  242. package/build/loadMetadata.cjs.map +1 -0
  243. package/build/loadMetadata.d.cts +31 -0
  244. package/build/loadMetadata.d.cts.map +1 -0
  245. package/build/loadMetadata.d.mts +31 -0
  246. package/build/loadMetadata.d.mts.map +1 -0
  247. package/build/loadMetadata.js +32 -39
  248. package/build/loadMetadata.js.map +1 -1
  249. package/build/logger.cjs +28 -0
  250. package/build/logger.cjs.map +1 -0
  251. package/build/logger.d.cts +13 -0
  252. package/build/logger.d.cts.map +1 -0
  253. package/build/logger.d.mts +13 -0
  254. package/build/logger.d.mts.map +1 -0
  255. package/build/logger.js +17 -16
  256. package/build/logger.js.map +1 -1
  257. package/build/pgMetadata.cjs +332 -0
  258. package/build/pgMetadata.cjs.map +1 -0
  259. package/build/pgMetadata.d.cts +128 -0
  260. package/build/pgMetadata.d.cts.map +1 -0
  261. package/build/pgMetadata.d.mts +128 -0
  262. package/build/pgMetadata.d.mts.map +1 -0
  263. package/build/pgMetadata.js +320 -0
  264. package/build/pgMetadata.js.map +1 -0
  265. package/build/scanEntityFiles.cjs +46 -0
  266. package/build/scanEntityFiles.cjs.map +1 -0
  267. package/build/scanEntityFiles.d.cts +8 -0
  268. package/build/scanEntityFiles.d.cts.map +1 -0
  269. package/build/scanEntityFiles.d.mts +8 -0
  270. package/build/scanEntityFiles.d.mts.map +1 -0
  271. package/build/scanEntityFiles.js +37 -42
  272. package/build/scanEntityFiles.js.map +1 -1
  273. package/build/sortForeignKeys.cjs +99 -0
  274. package/build/sortForeignKeys.cjs.map +1 -0
  275. package/build/{sortForeignKeys.d.ts → sortForeignKeys.d.cts} +8 -5
  276. package/build/sortForeignKeys.d.cts.map +1 -0
  277. package/build/sortForeignKeys.d.mts +35 -0
  278. package/build/sortForeignKeys.d.mts.map +1 -0
  279. package/build/sortForeignKeys.js +88 -117
  280. package/build/sortForeignKeys.js.map +1 -1
  281. package/build/symbols.cjs +238 -0
  282. package/build/symbols.cjs.map +1 -0
  283. package/build/symbols.d.cts +120 -0
  284. package/build/symbols.d.cts.map +1 -0
  285. package/build/symbols.d.mts +120 -0
  286. package/build/symbols.d.mts.map +1 -0
  287. package/build/symbols.js +121 -113
  288. package/build/symbols.js.map +1 -1
  289. package/build/tsdoc.cjs +22 -0
  290. package/build/tsdoc.cjs.map +1 -0
  291. package/build/tsdoc.d.cts +15 -0
  292. package/build/tsdoc.d.cts.map +1 -0
  293. package/build/tsdoc.d.mts +15 -0
  294. package/build/tsdoc.d.mts.map +1 -0
  295. package/build/tsdoc.js +18 -15
  296. package/build/tsdoc.js.map +1 -1
  297. package/build/utils.cjs +175 -0
  298. package/build/utils.cjs.map +1 -0
  299. package/build/utils.d.cts +36 -0
  300. package/build/utils.d.cts.map +1 -0
  301. package/build/utils.d.mts +36 -0
  302. package/build/utils.d.mts.map +1 -0
  303. package/build/utils.js +113 -166
  304. package/build/utils.js.map +1 -1
  305. package/build/write-json-schema.cjs +23 -0
  306. package/build/write-json-schema.cjs.map +1 -0
  307. package/build/write-json-schema.d.cts +1 -0
  308. package/build/write-json-schema.d.mts +1 -0
  309. package/build/write-json-schema.js +19 -13
  310. package/build/write-json-schema.js.map +1 -1
  311. package/package.json +47 -18
  312. package/skills/joist-docs/SKILL.md +142 -0
  313. package/skills/joist-em-basics/SKILL.md +114 -0
  314. package/skills/joist-partial-updates/SKILL.md +352 -0
  315. package/skills/joist-reactive-hints/SKILL.md +150 -0
  316. package/skills/joist-test-factories/SKILL.md +397 -0
  317. package/skills/joist-upsert/SKILL.md +95 -0
  318. package/build/EntityDbMetadata.d.ts +0 -271
  319. package/build/EntityDbMetadata.d.ts.map +0 -1
  320. package/build/EntityDbMetadata.test.d.ts +0 -2
  321. package/build/EntityDbMetadata.test.d.ts.map +0 -1
  322. package/build/EntityDbMetadata.test.js +0 -211
  323. package/build/EntityDbMetadata.test.js.map +0 -1
  324. package/build/assignTags.d.ts.map +0 -1
  325. package/build/codemods/Codemod.d.ts +0 -14
  326. package/build/codemods/Codemod.d.ts.map +0 -1
  327. package/build/codemods/Codemod.js.map +0 -1
  328. package/build/codemods/JscodeshiftMod.d.ts +0 -11
  329. package/build/codemods/JscodeshiftMod.d.ts.map +0 -1
  330. package/build/codemods/__tests__/codemod_0001-rename_has_async_property.test.d.ts +0 -2
  331. package/build/codemods/__tests__/codemod_0001-rename_has_async_property.test.d.ts.map +0 -1
  332. package/build/codemods/__tests__/codemod_0001-rename_has_async_property.test.js +0 -9
  333. package/build/codemods/__tests__/codemod_0001-rename_has_async_property.test.js.map +0 -1
  334. package/build/codemods/__tests__/codemod_0002-rename_async_query_fields.test.d.ts +0 -2
  335. package/build/codemods/__tests__/codemod_0002-rename_async_query_fields.test.d.ts.map +0 -1
  336. package/build/codemods/__tests__/codemod_0002-rename_async_query_fields.test.js +0 -9
  337. package/build/codemods/__tests__/codemod_0002-rename_async_query_fields.test.js.map +0 -1
  338. package/build/codemods/__tests__/codemod_0003-rename_find_paginated.test.d.ts +0 -2
  339. package/build/codemods/__tests__/codemod_0003-rename_find_paginated.test.d.ts.map +0 -1
  340. package/build/codemods/__tests__/codemod_0003-rename_find_paginated.test.js +0 -9
  341. package/build/codemods/__tests__/codemod_0003-rename_find_paginated.test.js.map +0 -1
  342. package/build/codemods/codemod_0001-rename_has_async_property.d.ts +0 -7
  343. package/build/codemods/codemod_0001-rename_has_async_property.d.ts.map +0 -1
  344. package/build/codemods/codemod_0002-rename_async_query_fields.d.ts +0 -7
  345. package/build/codemods/codemod_0002-rename_async_query_fields.d.ts.map +0 -1
  346. package/build/codemods/codemod_0003-rename_find_paginated.d.ts +0 -7
  347. package/build/codemods/codemod_0003-rename_find_paginated.d.ts.map +0 -1
  348. package/build/codemods/index.d.ts +0 -9
  349. package/build/codemods/index.d.ts.map +0 -1
  350. package/build/codemods/index.test.d.ts +0 -2
  351. package/build/codemods/index.test.d.ts.map +0 -1
  352. package/build/codemods/index.test.js +0 -24
  353. package/build/codemods/index.test.js.map +0 -1
  354. package/build/config.d.ts +0 -172
  355. package/build/config.d.ts.map +0 -1
  356. package/build/config.test.d.ts +0 -2
  357. package/build/config.test.d.ts.map +0 -1
  358. package/build/config.test.js +0 -78
  359. package/build/config.test.js.map +0 -1
  360. package/build/docs/cache.d.ts +0 -14
  361. package/build/docs/cache.d.ts.map +0 -1
  362. package/build/docs/cache.test.d.ts +0 -2
  363. package/build/docs/cache.test.d.ts.map +0 -1
  364. package/build/docs/cache.test.js +0 -47
  365. package/build/docs/cache.test.js.map +0 -1
  366. package/build/docs/generate-metadata-docs.d.ts +0 -6
  367. package/build/docs/generate-metadata-docs.d.ts.map +0 -1
  368. package/build/docs/index.d.ts +0 -9
  369. package/build/docs/index.d.ts.map +0 -1
  370. package/build/docs/index.test.d.ts +0 -2
  371. package/build/docs/index.test.d.ts.map +0 -1
  372. package/build/docs/index.test.js +0 -100
  373. package/build/docs/index.test.js.map +0 -1
  374. package/build/docs/markdown.d.ts.map +0 -1
  375. package/build/docs/markdown.test.d.ts +0 -2
  376. package/build/docs/markdown.test.d.ts.map +0 -1
  377. package/build/docs/markdown.test.js +0 -280
  378. package/build/docs/markdown.test.js.map +0 -1
  379. package/build/docs/parsing.d.ts +0 -42
  380. package/build/docs/parsing.d.ts.map +0 -1
  381. package/build/docs/parsing.test.d.ts +0 -2
  382. package/build/docs/parsing.test.d.ts.map +0 -1
  383. package/build/docs/parsing.test.js +0 -190
  384. package/build/docs/parsing.test.js.map +0 -1
  385. package/build/findEntityScopes.d.ts +0 -11
  386. package/build/findEntityScopes.d.ts.map +0 -1
  387. package/build/foreignKeyOrdering.d.ts +0 -3
  388. package/build/foreignKeyOrdering.d.ts.map +0 -1
  389. package/build/generate.d.ts +0 -12
  390. package/build/generate.d.ts.map +0 -1
  391. package/build/generateEntitiesFile.d.ts +0 -7
  392. package/build/generateEntitiesFile.d.ts.map +0 -1
  393. package/build/generateEntityCodegenFile.d.ts +0 -11
  394. package/build/generateEntityCodegenFile.d.ts.map +0 -1
  395. package/build/generateEntityFile.d.ts +0 -6
  396. package/build/generateEntityFile.d.ts.map +0 -1
  397. package/build/generateEntityTestFile.d.ts +0 -6
  398. package/build/generateEntityTestFile.d.ts.map +0 -1
  399. package/build/generateEnumFile.d.ts +0 -5
  400. package/build/generateEnumFile.d.ts.map +0 -1
  401. package/build/generateFactoriesFiles.d.ts +0 -4
  402. package/build/generateFactoriesFiles.d.ts.map +0 -1
  403. package/build/generateFlushFunction.d.ts.map +0 -1
  404. package/build/generateMetadataFile.d.ts +0 -5
  405. package/build/generateMetadataFile.d.ts.map +0 -1
  406. package/build/generatePgEnumFile.d.ts +0 -5
  407. package/build/generatePgEnumFile.d.ts.map +0 -1
  408. package/build/index.d.ts +0 -11
  409. package/build/index.d.ts.map +0 -1
  410. package/build/inheritance.d.ts +0 -10
  411. package/build/inheritance.d.ts.map +0 -1
  412. package/build/keywords.d.ts +0 -2
  413. package/build/keywords.d.ts.map +0 -1
  414. package/build/loadMetadata.d.ts +0 -26
  415. package/build/loadMetadata.d.ts.map +0 -1
  416. package/build/logger.d.ts +0 -10
  417. package/build/logger.d.ts.map +0 -1
  418. package/build/scanEntityFiles.d.ts +0 -5
  419. package/build/scanEntityFiles.d.ts.map +0 -1
  420. package/build/sortForeignKeys.d.ts.map +0 -1
  421. package/build/symbols.d.ts +0 -108
  422. package/build/symbols.d.ts.map +0 -1
  423. package/build/tsdoc.d.ts +0 -12
  424. package/build/tsdoc.d.ts.map +0 -1
  425. package/build/utils.d.ts +0 -31
  426. package/build/utils.d.ts.map +0 -1
  427. package/build/write-json-schema.d.ts +0 -2
  428. package/build/write-json-schema.d.ts.map +0 -1
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: entity-docs
3
+ description: Read the `src/entities/<Entity>.md` business-docs when working on an entity, its resolvers, jobs, or tests. Add only durable, non-obvious domain knowledge that helps readers understand a business rule or avoid a likely mistake; prioritize succinctness, clarity, and discretion. Also covers how `## Overview` and `## Fields`/`### fieldName` sections sync to JSDocs via `yarn joist-codegen`.
4
+ ---
5
+
6
+ # Entity docs (`src/entities/<Entity>.md`)
7
+
8
+ Many Joist entities have a sibling Markdown doc — `src/entities/Author.ts` ↔
9
+ `src/entities/Author.md` (same basename; enums under `src/entities/enums/` too). These files hold the
10
+ **"why"** that code can't: business rationale, worked scenarios, gotchas, domain vocabulary, and
11
+ cross-entity workflows. They are written for **both humans and agents** — you are a primary audience.
12
+
13
+ Read routinely; write selectively:
14
+
15
+ 1. **Read** the sibling `.md` before/while you work on an entity, to get domain context.
16
+ 2. **Write** only when a concise addition would give future readers important context they cannot
17
+ readily get from the code or existing docs. Leaving the docs unchanged is often the right outcome.
18
+
19
+ ## 1. Read it first
20
+
21
+ Before editing an entity — its `src/entities/<Entity>.ts`, its resolvers, a job that touches it, or its
22
+ tests — open `src/entities/<Entity>.md` if one exists. It routinely explains things you would otherwise
23
+ have to reverse-engineer or would get subtly wrong:
24
+
25
+ - **Business rationale** — *why* a field/entity exists and what real-world thing it models
26
+ (e.g. `PurchaseOrder.md` on tracking "what and why" a project's cost changed).
27
+ - **Worked scenarios** — concrete `I.e.` walkthroughs with real values ("when an `Employee` is termined, the status workflow is...").
28
+ - **Gotchas & edge cases** — footguns, "this looks stale but isn't", exemptions
29
+ - **Domain vocabulary** — statuses, flags, and other "esoteric unless you know it" domain concepts
30
+
31
+ Follow relevant cross-links (`[Book](Book.md)`, sometimes with anchors like
32
+ `Book.md#Definitions`) into related docs. A missing or thin doc is not by itself a reason to add content.
33
+
34
+ ## 2. How the file maps to code (the codegen doc-sync)
35
+
36
+ `joist-config.json` has `"docs": true`, so `yarn joist-codegen` keeps these `.md` files and the entity
37
+ JSDocs in sync. Understanding this is what makes your writing land in the right place.
38
+
39
+ **Only two heading shapes are parsed and synced; everything else is free-form narrative:**
40
+
41
+ | `.md` section | Syncs to | Notes |
42
+ |---|---|---|
43
+ | `## Overview` (body) | the entity **class** JSDoc | exact heading `## Overview` |
44
+ | `## Fields` → `### <fieldName>` (body) | that field/property/relation's JSDoc | `### ` heading must be the **exact member name** |
45
+ | any other `## Section` (e.g. `## Hard vs. Soft Costs`, `## DB Constraints`, mermaid) | **nothing** | free-form; lives only in the `.md`, preserved verbatim |
46
+
47
+ Synced JSDocs are tagged `@generated <Entity>.md` in the `.ts` — that tag means "this comment came from
48
+ the `.md`; edit the `.md`, not here."
49
+
50
+ **Sync is bidirectional, but the `.md` wins.** `yarn joist-codegen`:
51
+
52
+ 1. **Backfills** the `.md` from any hand-written `.ts` JSDoc that isn't in the `.md` yet (so a jsdoc you
53
+ write directly on a field gets pulled into `## Fields` for you), then
54
+ 2. **Writes the `.md` back into the `.ts`** JSDocs — so for anything already in the `.md`, the `.md` is
55
+ the source of truth and will overwrite a divergent `.ts` comment.
56
+
57
+ Net rule: **to durably document an entity or field, edit the `.md`.** Hand-editing a `@generated` JSDoc
58
+ in the `.ts` gets stomped on the next codegen.
59
+
60
+ ### Field-heading constraints (easy to get wrong)
61
+
62
+ - `### <fieldName>` must be a **single word** — the parser matches `\w+` only. `### excludeFromBudget`
63
+ works; `### is excluded`, `` ### `excludeFromBudget` ``, or `### excludeFromBudget (derived)` are **not**
64
+ recognized as field docs.
65
+ - It must be the **exact** name of a field, relation, or property on the entity, and must sit **under the
66
+ `## Fields` heading**. Any other `##` heading ends the fields section — so keep all `### fieldName`
67
+ entries together under the one `## Fields` section (conventionally at the bottom of the file).
68
+ - **Methods don't sync** — only fields/getters/properties/relations get their JSDoc updated.
69
+
70
+ ## 3. Choose what is worth documenting
71
+
72
+ Add a note only when the knowledge is durable, non-obvious, and useful for a future domain decision or
73
+ for avoiding a likely mistake. Possible candidates, not a checklist to fill:
74
+
75
+ - You just figured out **why** an entity or field exists, or what it really models.
76
+ - A **non-obvious business rule**, invariant, or "this only applies to X clients" exemption.
77
+ - A **gotcha / footgun** — a value that looks stale but isn't, an ordering requirement, a special-cased
78
+ type.
79
+ - A **cross-entity interaction** or workflow that isn't visible from one file.
80
+ - **Domain vocabulary** a newcomer (human or agent) wouldn't know.
81
+ - A **worked scenario** needed to resolve an ambiguity that a short explanation cannot.
82
+
83
+ **Omit** what the code already says plainly, transient implementation details, exhaustive edge-case
84
+ lists, and a recap of the work just completed. Do not repeat knowledge already documented elsewhere;
85
+ link to it when readers need it. Correct outdated guidance rather than appending a competing explanation.
86
+
87
+ **Where to put it:**
88
+
89
+ - Something specific to one field → a `### <fieldName>` block under `## Fields`. It syncs to that
90
+ member's JSDoc, so it shows on hover/`@generated` in the `.ts`.
91
+ - The entity's purpose → a short `## Overview`, which syncs to the class JSDoc.
92
+ - Necessary longer explanations or cross-entity workflows → a free-form `##` section, linked from the
93
+ field doc if needed. Keep synced JSDocs small enough to read comfortably on hover.
94
+
95
+ **Style** (prioritize succinctness and clarity):
96
+
97
+ - Lead with business meaning and *why*. Use plain language and state each fact once.
98
+ - Default to one sentence for a field and one short paragraph for an overview. Add detail only when
99
+ shortening it would hide an important rule or make the meaning ambiguous.
100
+ - Use a single brief `I.e. ...` example with concrete values only when it clarifies a difficult rule.
101
+ Do not add examples, diagrams, or sections merely to make the doc look complete.
102
+ - Cross-link related entities with relative links: `[Book](Book.md)`.
103
+ - Reference field/entity names in backticks.
104
+ - Before finishing, remove any sentence that does not add necessary meaning. Match existing terminology,
105
+ but do not copy existing verbosity.
106
+
107
+ ## 4. Workflow to add or update a doc
108
+
109
+ 1. If an addition meets the criteria above, edit `src/entities/<Entity>.md` (create it only if needed — basename must equal the entity, e.g.
110
+ `Author.md`). For field docs, use the exact member name as a `### ` heading under `## Fields`.
111
+ 2. Sync into the JSDocs:
112
+ ```bash
113
+ mise exec -- yarn joist-codegen
114
+ ```
115
+ (or `mise exec -- yarn codegen` to also run graphql-codegen.)
116
+ 3. Verify the `@generated <Entity>.md` JSDoc now appears on the class / field in `src/entities/<Entity>.ts`.
117
+ 4. Commit the `.md` **and** the regenerated `.ts` JSDoc changes together.
118
+
119
+ Never hand-edit a `@generated <Entity>.md` JSDoc in the `.ts` — edit the `.md` and re-run codegen. Do not
120
+ touch files under `src/entities/codegen/` (see the repo's generated-files rules).
121
+
122
+ ## Skeleton for a new doc
123
+
124
+ Include only the sections and fields that need explanation; this is a structure, not a checklist.
125
+
126
+ ```markdown
127
+ # <Entity>
128
+
129
+ ## Overview
130
+
131
+ <What this entity models and *why* it exists, in business terms. One short paragraph.>
132
+
133
+ ## <Some Business Concept> <!-- free-form: rationale, scenarios, gotchas, diagrams; NOT synced -->
134
+
135
+ <Only if needed: a concise explanation of a non-obvious business concept.>
136
+
137
+ ## Fields <!-- each ### below syncs into that member's JSDoc -->
138
+
139
+ ### someField
140
+
141
+ <Usually one sentence: why it exists or the important non-obvious rule.>
142
+ ```
@@ -0,0 +1,114 @@
1
+ ---
2
+ name: joist-em-basics
3
+ description: Create, load, find, mutate, and save entities with Joist's EntityManager. Use when writing data-access or business logic in a Joist project — anything using em.create, em.load, em.find, em.flush, or walking the entity graph.
4
+ ---
5
+
6
+ <!-- Managed by joist-codegen. Do not edit by hand; re-run codegen to update. -->
7
+
8
+ # Joist EntityManager basics
9
+
10
+ The `EntityManager` (`em`) is how entities are loaded from and saved to the
11
+ database. It is a Unit of Work: each request gets its own `em`, which tracks
12
+ the entities it has loaded/created and writes them all out on `em.flush()`.
13
+
14
+ Entities are always loaded/created _through_ the `em`, never via static methods
15
+ on the class (Joist is not ActiveRecord). IDs are tagged strings like `"a:1"`.
16
+
17
+ ## Creating
18
+
19
+ `em.create` is synchronous and returns the new entity immediately:
20
+
21
+ ```ts
22
+ const author = em.create(Author, { firstName: "a1" });
23
+ // Nested/related entities can be created inline
24
+ const author = em.create(Author, { firstName: "a1", address: { street: "123 Main" } });
25
+ ```
26
+
27
+ ## Loading by id
28
+
29
+ Use tagged ids. These throw if the id does not exist (except the `IfExists`
30
+ variants):
31
+
32
+ ```ts
33
+ const a = await em.load(Author, "a:1");
34
+ const as = await em.loadAll(Author, ["a:1", "a:2"]);
35
+ const as = await em.loadAllIfExists(Author, ["a:1", "a:2"]); // skips missing ids
36
+ ```
37
+
38
+ To eagerly load relations, pass a populate hint as the 3rd argument; the
39
+ returned entity is typed as "loaded" so the relations can be read synchronously:
40
+
41
+ ```ts
42
+ const a = await em.load(Author, "a:1", { books: "reviews" });
43
+ a.books.get.flatMap((b) => b.reviews.get);
44
+ ```
45
+
46
+ ## Walking the graph
47
+
48
+ ~90% of reads are just navigating relations from an entity you already have.
49
+ These are guaranteed N+1-safe, even in a loop:
50
+
51
+ ```ts
52
+ const author = await book.author.load(); // load a single relation
53
+ const reviews = await publisher.load((p) => p.books.reviews); // lens
54
+ const loaded = await author.populate({ books: "reviews" }); // populate + .get
55
+ ```
56
+
57
+ ## Finding (filtered queries)
58
+
59
+ `em.find` issues a `SELECT` with a "join literal" of nested relations plus
60
+ inline `WHERE` conditions. It is batch/N+1-safe.
61
+
62
+ ```ts
63
+ const books = await em.find(Book, { author: { firstName: "a1" } });
64
+ const recent = await em.find(Book, { publishedAt: { gte: jan1 } });
65
+ const some = await em.find(Author, { firstName: { in: ["a1", "a2"] } });
66
+
67
+ const one = await em.findOne(Book, { title: "b1" }); // undefined if none
68
+ const one = await em.findOneOrFail(Book, { title: "b1" }); // throws if none
69
+ const one = await em.findOrCreate(Author, { email: "a@b.com" });
70
+ ```
71
+
72
+ `undefined` values are pruned (the condition and any now-unused join are
73
+ dropped), so filters compose cleanly. To filter for null, pass `null`
74
+ explicitly, e.g. `{ firstName: null }`. For `OR` / nested boolean logic, use
75
+ `alias`/`aliases` with a `conditions` argument. For aggregates or group-bys,
76
+ drop down to Knex/Kysely — Joist's `find` only returns whole entities.
77
+
78
+ ## Mutating
79
+
80
+ Assign fields directly, or use `.set` for a batch of changes:
81
+
82
+ ```ts
83
+ author.firstName = "a2";
84
+ author.set({ firstName: "a2", lastName: "b2" });
85
+ ```
86
+
87
+ For partial/RPC-style updates (treating `null` as "unset"), and for
88
+ incrementally updating children, see the `joist-upsert` skill.
89
+
90
+ ## Deleting
91
+
92
+ ```ts
93
+ const a = await em.load(Author, "a:1");
94
+ em.delete(a);
95
+ ```
96
+
97
+ ## Saving with `em.flush()`
98
+
99
+ `em.flush()` is where everything happens. It is async and:
100
+
101
+ 1. Runs lifecycle hooks and validation rules,
102
+ 2. Opens a transaction,
103
+ 3. Issues batched `INSERT`/`UPDATE`/`DELETE` (one batch per entity type),
104
+ 4. Commits.
105
+
106
+ You don't write individual SQL statements — let Joist batch them. `flush` can
107
+ be called multiple times as you do more work:
108
+
109
+ ```ts
110
+ const author = em.create(Author, { firstName: "a1" });
111
+ await em.flush();
112
+ author.firstName = "a2";
113
+ await em.flush();
114
+ ```
@@ -0,0 +1,352 @@
1
+ ---
2
+ name: joist-partial-updates
3
+ description: Joist ORM partial updates, em.upsert, setPartial, nested collection op semantics, and GraphQL/REST/gRPC save inputs. Use when designing, implementing, reviewing, or debugging Joist partial-update or save-input APIs, especially parent-child graphs, id-less nested rows, required-parent validation errors, or accidental collection removal.
4
+ ---
5
+
6
+ # Joist Partial Update APIs
7
+
8
+ Use Joist's native partial-update contract from the transport through to `EntityManager.upsert` or `Entity.setPartial`. Do not load an existing graph and write custom reconciliation code when IDs and Joist collection operations can express the update.
9
+
10
+ Authoritative documentation: <https://joist-orm.io/features/partial-update-apis/>
11
+
12
+ ## Non-Negotiable Rules
13
+
14
+ 1. Preserve entity IDs across read, edit, and write boundaries.
15
+ 2. Decide whether every supplied collection is exhaustive or incremental before writing the payload.
16
+ 3. Omit a field to leave it unchanged. Do not replace omission with `null`, `[]`, or a generated default.
17
+ 4. A collection array is exhaustive by default. Omitted existing children are removed from the relation.
18
+ 5. A collection becomes incremental when its child objects use `op`.
19
+ 6. If any child has `op`, every child in that collection must have `op`, including newly created children.
20
+ 7. An empty array is always exhaustive and clears the collection. It never means "no changes."
21
+ 8. Use `{ op: "incremental" }` only as a no-op sentinel when the collection key must be present. Prefer omitting the collection key.
22
+ 9. Use `include`, `delete`, and `remove` according to their actual relationship semantics.
23
+ 10. Test updates against an already-persisted graph and flush the unit of work. Mapper-only tests cannot catch detached required children.
24
+ 11. Authorize every supplied ID and operation. Joist enforces persistence rules, not API permissions or tenant boundaries.
25
+
26
+ ## Partial Field Semantics
27
+
28
+ Joist distinguishes omission, `undefined`, and `null`:
29
+
30
+ | Input | Scalar or relation behavior | Collection behavior |
31
+ | --- | --- | --- |
32
+ | Omitted | Leave unchanged | Leave unchanged |
33
+ | `undefined` | Leave unchanged | Leave unchanged |
34
+ | `null` | Unset optional value; required value fails validation | Clear the collection |
35
+ | Value or array | Set/update | Exhaustively replace unless `op` enables incremental behavior |
36
+
37
+ Do not globally normalize `null` to `undefined`. That destroys the caller's ability to explicitly unset an optional field. Adapt only fields whose transport representation differs from the entity graph.
38
+
39
+ Transport-specific presence rules:
40
+
41
+ - GraphQL: preserve the difference between an omitted input field and an explicit `null`. Codegen commonly produces `T | null | undefined`; this is what `DeepPartialOrNull` and the partial APIs are designed to accept.
42
+ - REST/JSON: omission means no change; JSON has no `undefined`, so do not synthesize absent keys while decoding or mapping.
43
+ - gRPC/Protobuf: scalar presence can use `optional`, wrappers, or `oneof`, but ordinary repeated fields do not distinguish omitted from empty. Wrap repeated fields in a present message, use a `oneof`, or use a field mask so "unchanged" cannot decode to `[]`.
44
+
45
+ ## Choose the Collection Contract
46
+
47
+ ### Exhaustive Replacement
48
+
49
+ Use an exhaustive collection only when the caller intentionally sends the complete desired membership:
50
+
51
+ ```ts
52
+ await em.upsert(Author, {
53
+ id: "a:1",
54
+ books: [
55
+ { id: "b:1", title: "Retained and updated" },
56
+ { title: "New book" },
57
+ ],
58
+ });
59
+ ```
60
+
61
+ This removes every omitted existing book from `author.books`. It does not imply hard deletion. If the inverse relation is required, detaching an omitted child can produce validation errors such as `Book author is required`.
62
+
63
+ Do not use exhaustive replacement when:
64
+
65
+ - The client does not know every existing child.
66
+ - The UI discarded child IDs.
67
+ - Omitted children should remain untouched.
68
+ - Removed children must be hard-deleted rather than merely detached.
69
+
70
+ ### Incremental Update
71
+
72
+ Use incremental operations when the request describes additions, updates, removals, or deletions without replacing the whole collection:
73
+
74
+ ```ts
75
+ await em.upsert(Author, {
76
+ id: "a:1",
77
+ books: [
78
+ { id: "b:1", op: "include", title: "Updated title" },
79
+ { op: "include", title: "New book" },
80
+ { id: "b:2", op: "delete" },
81
+ ],
82
+ });
83
+ ```
84
+
85
+ Operation meanings:
86
+
87
+ | Operation | Behavior |
88
+ | --- | --- |
89
+ | `include` | Create or update the child, and ensure it belongs to the collection |
90
+ | `delete` | Remove the child from the collection and call `EntityManager.delete` |
91
+ | `remove` | Detach the child without deleting it |
92
+ | `incremental` | No-op sentinel that enables incremental semantics for an otherwise empty list |
93
+
94
+ Use `remove` only when the child can validly exist without this parent or is being reparented in the same unit of work. For a required parent relation, `remove` alone usually creates an invalid entity. Use `delete` when the child should cease to exist.
95
+
96
+ An actual empty array still clears:
97
+
98
+ ```ts
99
+ await em.upsert(Author, { id: "a:1", books: [] });
100
+ ```
101
+
102
+ For an incremental no-op, prefer:
103
+
104
+ ```ts
105
+ await em.upsert(Author, { id: "a:1" });
106
+ ```
107
+
108
+ If a generic payload builder must emit `books`, use:
109
+
110
+ ```ts
111
+ await em.upsert(Author, {
112
+ id: "a:1",
113
+ books: [{ op: "incremental" }],
114
+ });
115
+ ```
116
+
117
+ ## Stable Identity End to End
118
+
119
+ IDs are the reliable identity contract for active nested updates. A nested object without an ID is normally a request to create a new entity.
120
+
121
+ For an editable parent-child graph:
122
+
123
+ 1. Query IDs for the parent and every editable nested entity.
124
+ 2. Include those IDs in API response types.
125
+ 3. Store IDs in form or draft state.
126
+ 4. Send retained rows as `{ id, op: "include", ...changes }`.
127
+ 5. Mark deleted persisted rows in place by switching their `op` to `delete` (or `remove`); keep them in the same array rather than moving their IDs to a separate deletion list.
128
+ 6. Send new rows without an ID but with `op: "include"`.
129
+ 7. Keep the same discipline recursively for grandchildren.
130
+
131
+ A robust UI row model carries `op` on the row itself, so a deleted book flips to `op: "delete"`/`"remove"` in place instead of moving to a separate collection:
132
+
133
+ ```ts
134
+ interface EditableBook {
135
+ id?: string;
136
+ op?: "include" | "remove" | "delete";
137
+ title: string;
138
+ }
139
+
140
+ interface AuthorDraft {
141
+ books: EditableBook[];
142
+ }
143
+ ```
144
+
145
+ Because each row already mirrors `SaveBookInput` (`id`, `op`, scalars), the draft state can go directly onto the wire as the GraphQL input, with no separate deletion list to reconcile back in. Keep the collection incremental (every row has `op`) once any row does.
146
+
147
+ Do not key editable rows only by array index. Reordering or deleting an earlier row can assign an existing ID to the wrong logical row. Prefer structured row state with stable IDs. If a textarea represents multiple database rows, either maintain line identity explicitly or document why positional identity is safe for that domain.
148
+
149
+ Do not infer identity from `sortOrder`, names, unique constraints, or array position. Joist may use configured uniqueness for specific upsert cases such as soft-deleted rows, but do not assume `uniqueBy` will match active id-less children. Send the ID unless the current Joist behavior is intentionally relied on and covered by an integration test.
150
+
151
+ ## GraphQL Shape
152
+
153
+ Make GraphQL inputs mirror Joist's native graph shape:
154
+
155
+ ```graphql
156
+ enum UpsertOp {
157
+ include
158
+ remove
159
+ delete
160
+ incremental
161
+ }
162
+
163
+ input SaveAuthorInput {
164
+ id: ID
165
+ firstName: String
166
+ books: [SaveBookInput!]
167
+ }
168
+
169
+ input SaveBookInput {
170
+ id: ID
171
+ op: UpsertOp
172
+ title: String
173
+ }
174
+ ```
175
+
176
+ The corresponding output query must expose nested IDs:
177
+
178
+ ```graphql
179
+ query EditAuthor($id: ID!) {
180
+ author(id: $id) {
181
+ id
182
+ firstName
183
+ books {
184
+ id
185
+ title
186
+ }
187
+ }
188
+ }
189
+ ```
190
+
191
+ Keep the resolver thin when the schema already matches the entity graph:
192
+
193
+ ```ts
194
+ import { type DeepPartialOrNull } from "joist-orm";
195
+
196
+ async function saveAuthor(input: SaveAuthorInput, em: EntityManager): Promise<Author> {
197
+ return em.upsert(Author, input as DeepPartialOrNull<Author>);
198
+ }
199
+ ```
200
+
201
+ The cast is appropriate only after checking that relation names, IDs, scalar fields, and nested collection operations align with Joist's expected graph. A cast does not make an incompatible payload safe.
202
+
203
+ Thin does not mean unchecked. Before calling `upsert`, authorize the parent and every nested ID for the caller's tenant/scope, and validate that each requested attach, reparent, remove, or delete operation is allowed. This security validation is separate from persistence reconciliation.
204
+
205
+ If the API also accepts convenience fields, such as a textarea that expands into child rows, use a small boundary mapper:
206
+
207
+ ```ts
208
+ type SaveAuthorConvenienceInput = SaveAuthorInput & { booksText?: string | null };
209
+
210
+ function toAuthorUpsertInput(input: SaveAuthorConvenienceInput): DeepPartialOrNull<Author> {
211
+ return {
212
+ id: input.id,
213
+ firstName: input.firstName,
214
+ books: mapBooks(input),
215
+ } as DeepPartialOrNull<Author>;
216
+ }
217
+
218
+ function mapBooks(input: SaveAuthorConvenienceInput) {
219
+ if (input.books !== undefined) return input.books;
220
+ if (input.booksText === undefined) return undefined;
221
+ return parseLegacyBooks(input.booksText ?? "");
222
+ }
223
+ ```
224
+
225
+ The mapper should transform representation, not reconcile persistence. It must preserve `books: null` as an explicit clear and preserve omission as `undefined`; do not use `??` when `null` and omission have different meanings.
226
+
227
+ ## REST and gRPC Shapes
228
+
229
+ Use the same graph contract regardless of transport. For example, a REST PATCH body can be:
230
+
231
+ ```json
232
+ {
233
+ "id": "a:1",
234
+ "books": [
235
+ { "id": "b:1", "op": "include", "title": "Updated title" },
236
+ { "id": "b:2", "op": "delete" }
237
+ ]
238
+ }
239
+ ```
240
+
241
+ A gRPC message should model the same operations with an enum and explicit collection presence. A plain `repeated SaveBookInput books` field is unsafe for a partial update because its decoded empty list cannot distinguish "omitted" from "clear." Use a wrapper message inside a `oneof`, or an equivalent field-mask convention:
242
+
243
+ ```proto
244
+ message BookChanges {
245
+ repeated SaveBookInput values = 1;
246
+ }
247
+
248
+ message SaveAuthorRequest {
249
+ optional string id = 1;
250
+ oneof books_update {
251
+ BookChanges books = 2;
252
+ bool clear_books = 3;
253
+ }
254
+ }
255
+ ```
256
+
257
+ An unset `books_update` means unchanged; a present `books` wrapper carries incremental or exhaustive values; `clear_books: true` explicitly clears. Convert generated enum values to Joist's lowercase strings at the boundary if necessary.
258
+
259
+ Do not invent transport-specific reconciliation semantics. GraphQL, REST, and gRPC should all produce the same `DeepPartialOrNull<Entity>` graph before calling Joist.
260
+
261
+ ## Implementation Workflow
262
+
263
+ 1. Read the entity metadata and identify required inverse relations and delete behavior.
264
+ 2. Trace the complete read-edit-write path, not only the resolver.
265
+ 3. Verify every editable output type and query includes stable nested IDs.
266
+ 4. Verify client state retains those IDs after parsing, normalization, and form edits.
267
+ 5. Choose exhaustive or incremental semantics independently for every nested collection.
268
+ 6. Make transport inputs match Joist's graph and expose `op` where incremental updates are needed.
269
+ 7. Add only the smallest representation mapper required by the transport.
270
+ 8. Call `em.upsert`, `entity.setPartial`, or a thin shared helper that delegates to them.
271
+ 9. Flush in an integration test and assert identity, membership, deletion, and untouched fields.
272
+
273
+ ## Failure Signatures
274
+
275
+ ### `<Child> <parent> is required` after updating a parent
276
+
277
+ Likely cause: an id-less or incomplete child array was interpreted as exhaustive replacement. Joist created new children and detached omitted persisted children, whose required parent relation then failed validation.
278
+
279
+ Fix: preserve child IDs and either send the complete exhaustive graph or use `op: "include"`/`"delete"` incrementally.
280
+
281
+ ### Duplicate children after each save
282
+
283
+ Likely cause: existing child IDs are missing from the write payload, so each row is treated as new.
284
+
285
+ Fix: query, retain, and resend IDs. Do not match by sort order or content on the server.
286
+
287
+ ### All children disappear on a no-change save
288
+
289
+ Likely cause: a payload builder emitted `children: []`, which is an exhaustive clear.
290
+
291
+ Fix: omit `children`, or emit `[{ op: "incremental" }]` only if the key is mandatory.
292
+
293
+ ### Incremental update is rejected or behaves exhaustively
294
+
295
+ Likely cause: only some children have `op`.
296
+
297
+ Fix: give every child in that collection an operation, including new children.
298
+
299
+ ### GraphQL input does not type-check against Joist
300
+
301
+ Likely cause: generated nullable input types do not directly match strict entity setters, or the API has convenience fields not present on the entity.
302
+
303
+ Fix: target `DeepPartialOrNull<Entity>` and use a narrow boundary mapper. Do not weaken unrelated types or write a persistence reconciliation layer.
304
+
305
+ ## Anti-Patterns
306
+
307
+ Never solve a partial-update mismatch by defaulting to these approaches:
308
+
309
+ - Loading all existing children in the resolver to assign IDs by array position.
310
+ - Diffing old and new child arrays manually.
311
+ - Appending custom delete markers after comparing database state.
312
+ - Matching active children by `sortOrder`, name, or another mutable field.
313
+ - Assuming a database unique constraint supplies identity to an id-less upsert.
314
+ - Dropping nested IDs from API output, DTOs, parsers, or form state.
315
+ - Mapping an omitted collection to `[]`.
316
+ - Mixing children with and without `op` in one collection.
317
+ - Using `remove` for a child whose parent relation is required.
318
+ - Treating an empty array as an incremental no-op.
319
+ - Converting every GraphQL `null` to `undefined`.
320
+ - Adding backward-compatibility reconciliation without a concrete shipped caller that requires it.
321
+
322
+ Custom reconciliation is justified only when the API contract fundamentally cannot carry stable identity or Joist operations, and changing that contract is impossible. Document that constraint before adding such code.
323
+
324
+ ## Regression Tests
325
+
326
+ For a nested update bug, build a persisted graph before invoking the real API boundary:
327
+
328
+ 1. Create a parent with at least three children.
329
+ 2. Give one child at least two grandchildren.
330
+ 3. Update retained entities using their IDs and `op: "include"`.
331
+ 4. Add one new id-less entity with `op: "include"`.
332
+ 5. Delete one persisted entity with `op: "delete"`.
333
+ 6. Omit another relation and prove it remains unchanged.
334
+ 7. Flush and reload.
335
+ 8. Assert retained IDs are unchanged, new IDs were created, deleted entities are gone, and no duplicates exist.
336
+
337
+ Do not re-test `em.upsert`'s own collection semantics (omitted vs `[]` vs `[{ op: "incremental" }]`, `remove` vs `delete`, mixed-`op` rejection) — Joist owns and tests those. Test what is yours: that nested IDs and operations survive the transport boundary and reach the resolver. Do not stop at payload snapshots; the original class of failure often appears only during validation or flush.
338
+
339
+ ## Review Checklist
340
+
341
+ - Are IDs selected for every editable nested entity?
342
+ - Are IDs retained in client or caller state?
343
+ - Does each supplied collection intentionally use exhaustive or incremental semantics?
344
+ - If incremental, does every child have `op`?
345
+ - Are new children marked `include`?
346
+ - Are deleted persisted IDs sent as `delete`?
347
+ - Is `remove` valid for the inverse relation's nullability?
348
+ - Can any no-change path emit `[]` accidentally?
349
+ - Are omitted fields preserved as omitted or `undefined`?
350
+ - Are explicit `null` values preserved where they mean unset?
351
+ - Is the resolver a thin `upsert`/`setPartial` boundary instead of a reconciliation engine?
352
+ - Does an integration test update a persisted graph and verify stable IDs after flush?