velocious 1.0.448 → 1.0.450

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 (645) hide show
  1. package/build/application.js +4 -4
  2. package/build/authorization/ability.js +8 -8
  3. package/build/authorization/base-resource.js +4 -4
  4. package/build/background-jobs/cron-expression.js +1 -1
  5. package/build/background-jobs/forked-runner-child.js +1 -1
  6. package/build/background-jobs/job-registry.js +1 -1
  7. package/build/background-jobs/job-runner.js +1 -1
  8. package/build/background-jobs/job.js +1 -1
  9. package/build/background-jobs/json-socket.js +4 -4
  10. package/build/background-jobs/main.js +21 -21
  11. package/build/background-jobs/scheduler.js +7 -7
  12. package/build/background-jobs/store.js +7 -7
  13. package/build/background-jobs/web/controller.js +2 -2
  14. package/build/background-jobs/worker.js +12 -12
  15. package/build/beacon/client.js +11 -8
  16. package/build/beacon/in-process-broker.js +2 -2
  17. package/build/beacon/in-process-client.js +2 -2
  18. package/build/beacon/server.js +3 -3
  19. package/build/cli/browser-cli.js +1 -1
  20. package/build/cli/commands/db/base-command.js +2 -2
  21. package/build/cli/index.js +2 -2
  22. package/build/cli/tenant-database-command-helper.js +3 -3
  23. package/build/cli/use-browser-cli.js +1 -1
  24. package/build/configuration.js +38 -38
  25. package/build/controller.js +8 -8
  26. package/build/current-configuration.js +1 -1
  27. package/build/database/annotations-async-hooks.js +2 -2
  28. package/build/database/annotations.js +3 -3
  29. package/build/database/drivers/base-column.js +1 -1
  30. package/build/database/drivers/base-foreign-key.js +1 -1
  31. package/build/database/drivers/base-table.js +1 -1
  32. package/build/database/drivers/base.js +10 -10
  33. package/build/database/drivers/mssql/index.js +1 -1
  34. package/build/database/drivers/mssql/table.js +1 -1
  35. package/build/database/drivers/mysql/index.js +3 -3
  36. package/build/database/drivers/mysql/query.js +1 -1
  37. package/build/database/drivers/mysql/table.js +1 -1
  38. package/build/database/drivers/pgsql/index.js +2 -2
  39. package/build/database/drivers/sqlite/base.js +16 -16
  40. package/build/database/drivers/sqlite/index.js +11 -11
  41. package/build/database/drivers/sqlite/index.native.js +1 -1
  42. package/build/database/drivers/sqlite/index.web.js +3 -3
  43. package/build/database/drivers/sqlite/query.js +1 -1
  44. package/build/database/drivers/sqlite/query.web.js +1 -1
  45. package/build/database/drivers/sqlite/sql/alter-table.js +2 -2
  46. package/build/database/drivers/sqlite/table.js +1 -1
  47. package/build/database/migrator/files-finder.js +1 -1
  48. package/build/database/migrator.js +6 -6
  49. package/build/database/pool/async-tracked-multi-connection.js +27 -27
  50. package/build/database/pool/base-methods-forward.js +4 -4
  51. package/build/database/pool/base.js +8 -8
  52. package/build/database/query/from-base.js +1 -1
  53. package/build/database/query/index.js +10 -10
  54. package/build/database/query/join-object.js +3 -3
  55. package/build/database/query/model-class-query.js +39 -39
  56. package/build/database/query/preloader/belongs-to.js +22 -22
  57. package/build/database/query/preloader/has-many.js +20 -20
  58. package/build/database/query/preloader/has-one.js +8 -8
  59. package/build/database/query/preloader/selection.js +3 -3
  60. package/build/database/query/preloader.js +8 -8
  61. package/build/database/query/query-data.js +9 -9
  62. package/build/database/query/where-model-class-hash.js +7 -7
  63. package/build/database/query/with-count.js +9 -9
  64. package/build/database/record/acts-as-list.js +15 -15
  65. package/build/database/record/attachments/handle.js +2 -2
  66. package/build/database/record/attachments/normalize-input.js +12 -12
  67. package/build/database/record/attachments/storage-drivers/s3.js +6 -6
  68. package/build/database/record/attachments/store.js +13 -13
  69. package/build/database/record/index.js +127 -128
  70. package/build/database/record/instance-relationships/base.js +11 -11
  71. package/build/database/record/instance-relationships/belongs-to.js +3 -3
  72. package/build/database/record/instance-relationships/has-many.js +8 -8
  73. package/build/database/record/instance-relationships/has-one.js +7 -7
  74. package/build/database/record/relationships/base.js +2 -2
  75. package/build/database/record/state-machine.js +7 -7
  76. package/build/database/record/validators/presence.js +1 -1
  77. package/build/database/record/validators/uniqueness.js +5 -5
  78. package/build/database/table-data/index.js +4 -4
  79. package/build/environment-handlers/base.js +2 -2
  80. package/build/environment-handlers/browser.js +7 -7
  81. package/build/environment-handlers/node/cli/commands/cli-command-context.js +1 -1
  82. package/build/environment-handlers/node/cli/commands/console.js +4 -4
  83. package/build/environment-handlers/node/cli/commands/db/schema/dump.js +1 -1
  84. package/build/environment-handlers/node/cli/commands/db/schema/load.js +3 -3
  85. package/build/environment-handlers/node/cli/commands/generate/base-models.js +1 -16
  86. package/build/environment-handlers/node/cli/commands/generate/frontend-models.js +179 -89
  87. package/build/environment-handlers/node.js +6 -6
  88. package/build/frontend-model-controller.js +90 -82
  89. package/build/frontend-model-resource/base-resource.js +343 -167
  90. package/build/frontend-models/base.js +362 -205
  91. package/build/frontend-models/clear-pending-debounced-callback.js +1 -1
  92. package/build/frontend-models/event-hook-models.js +2 -2
  93. package/build/frontend-models/outgoing-event-buffer.js +2 -2
  94. package/build/frontend-models/preloader.js +8 -7
  95. package/build/frontend-models/query.js +43 -38
  96. package/build/frontend-models/resource-definition.js +6 -6
  97. package/build/frontend-models/transport-serialization.js +11 -11
  98. package/build/frontend-models/use-destroyed-event.js +10 -10
  99. package/build/frontend-models/use-model-class-event.js +11 -11
  100. package/build/frontend-models/use-updated-event.js +11 -11
  101. package/build/frontend-models/websocket-channel.js +26 -17
  102. package/build/frontend-models/websocket-publishers.js +9 -9
  103. package/build/http-client/response.js +2 -2
  104. package/build/http-server/client/index.js +8 -8
  105. package/build/http-server/client/params-to-object.js +10 -10
  106. package/build/http-server/client/request-buffer/form-data-part.js +2 -2
  107. package/build/http-server/client/request-buffer/index.js +8 -8
  108. package/build/http-server/client/request-parser.js +3 -3
  109. package/build/http-server/client/request-runner.js +6 -6
  110. package/build/http-server/client/request-timing.js +5 -5
  111. package/build/http-server/client/request.js +1 -1
  112. package/build/http-server/client/response.js +5 -5
  113. package/build/http-server/client/websocket-request.js +4 -4
  114. package/build/http-server/client/websocket-session.js +18 -18
  115. package/build/http-server/development-reloader.js +5 -5
  116. package/build/http-server/index.js +12 -12
  117. package/build/http-server/remote-address.js +4 -4
  118. package/build/http-server/server-client.js +3 -3
  119. package/build/http-server/server-lock.js +4 -4
  120. package/build/http-server/websocket-channel-subscribers.js +3 -3
  121. package/build/http-server/websocket-channel.js +1 -1
  122. package/build/http-server/websocket-connection.js +1 -1
  123. package/build/http-server/websocket-event-log-store.js +6 -6
  124. package/build/http-server/websocket-events-host.js +2 -2
  125. package/build/http-server/worker-handler/in-process.js +7 -7
  126. package/build/http-server/worker-handler/index.js +4 -4
  127. package/build/http-server/worker-handler/worker-thread.js +3 -3
  128. package/build/logger/outputs/array-output.js +3 -3
  129. package/build/logger/outputs/console-output.js +1 -1
  130. package/build/logger/outputs/file-output.js +4 -4
  131. package/build/logger/outputs/stdout-output.js +1 -1
  132. package/build/logger.js +10 -10
  133. package/build/mailer/backends/smtp.js +3 -3
  134. package/build/mailer/base.js +8 -8
  135. package/build/mailer/delivery.js +4 -4
  136. package/build/plugins/sqljs-wasm-route-controller.js +1 -1
  137. package/build/routes/base-route.js +6 -6
  138. package/build/routes/basic-route.js +1 -1
  139. package/build/routes/hooks/frontend-model-command-route-hook.js +1 -1
  140. package/build/routes/index.js +1 -1
  141. package/build/routes/plugin-routes.js +1 -1
  142. package/build/routes/resolver.js +11 -11
  143. package/build/routes/resource-route.js +1 -1
  144. package/build/src/application.d.ts +3 -3
  145. package/build/src/application.d.ts.map +1 -1
  146. package/build/src/application.js +5 -5
  147. package/build/src/authorization/ability.d.ts +7 -7
  148. package/build/src/authorization/ability.d.ts.map +1 -1
  149. package/build/src/authorization/ability.js +9 -9
  150. package/build/src/authorization/base-resource.d.ts +1 -1
  151. package/build/src/authorization/base-resource.d.ts.map +1 -1
  152. package/build/src/authorization/base-resource.js +5 -5
  153. package/build/src/background-jobs/cron-expression.js +2 -2
  154. package/build/src/background-jobs/forked-runner-child.js +2 -2
  155. package/build/src/background-jobs/job-record.d.ts +2 -1
  156. package/build/src/background-jobs/job-record.d.ts.map +1 -1
  157. package/build/src/background-jobs/job-registry.d.ts +1 -1
  158. package/build/src/background-jobs/job-registry.d.ts.map +1 -1
  159. package/build/src/background-jobs/job-registry.js +2 -2
  160. package/build/src/background-jobs/job-runner.js +2 -2
  161. package/build/src/background-jobs/job.js +2 -2
  162. package/build/src/background-jobs/json-socket.d.ts +4 -4
  163. package/build/src/background-jobs/json-socket.d.ts.map +1 -1
  164. package/build/src/background-jobs/json-socket.js +5 -5
  165. package/build/src/background-jobs/main.d.ts +18 -18
  166. package/build/src/background-jobs/main.d.ts.map +1 -1
  167. package/build/src/background-jobs/main.js +22 -22
  168. package/build/src/background-jobs/scheduler.d.ts +5 -5
  169. package/build/src/background-jobs/scheduler.d.ts.map +1 -1
  170. package/build/src/background-jobs/scheduler.js +8 -8
  171. package/build/src/background-jobs/store.js +8 -8
  172. package/build/src/background-jobs/web/controller.js +3 -3
  173. package/build/src/background-jobs/worker.d.ts +6 -6
  174. package/build/src/background-jobs/worker.d.ts.map +1 -1
  175. package/build/src/background-jobs/worker.js +13 -13
  176. package/build/src/beacon/client.d.ts +4 -4
  177. package/build/src/beacon/client.d.ts.map +1 -1
  178. package/build/src/beacon/client.js +12 -9
  179. package/build/src/beacon/in-process-broker.d.ts.map +1 -1
  180. package/build/src/beacon/in-process-broker.js +3 -3
  181. package/build/src/beacon/in-process-client.d.ts +1 -1
  182. package/build/src/beacon/in-process-client.d.ts.map +1 -1
  183. package/build/src/beacon/in-process-client.js +3 -3
  184. package/build/src/beacon/server.d.ts +2 -2
  185. package/build/src/beacon/server.d.ts.map +1 -1
  186. package/build/src/beacon/server.js +4 -4
  187. package/build/src/cli/browser-cli.js +2 -2
  188. package/build/src/cli/commands/db/base-command.d.ts +2 -2
  189. package/build/src/cli/commands/db/base-command.d.ts.map +1 -1
  190. package/build/src/cli/commands/db/base-command.js +3 -3
  191. package/build/src/cli/index.js +3 -3
  192. package/build/src/cli/tenant-database-command-helper.d.ts +1 -1
  193. package/build/src/cli/tenant-database-command-helper.d.ts.map +1 -1
  194. package/build/src/cli/tenant-database-command-helper.js +4 -4
  195. package/build/src/cli/use-browser-cli.js +2 -2
  196. package/build/src/configuration.d.ts +14 -14
  197. package/build/src/configuration.d.ts.map +1 -1
  198. package/build/src/configuration.js +39 -39
  199. package/build/src/controller.js +10 -10
  200. package/build/src/current-configuration.js +2 -2
  201. package/build/src/database/annotations-async-hooks.js +3 -3
  202. package/build/src/database/annotations.d.ts.map +1 -1
  203. package/build/src/database/annotations.js +4 -4
  204. package/build/src/database/drivers/base-column.d.ts +1 -1
  205. package/build/src/database/drivers/base-column.d.ts.map +1 -1
  206. package/build/src/database/drivers/base-column.js +2 -2
  207. package/build/src/database/drivers/base-foreign-key.d.ts +1 -1
  208. package/build/src/database/drivers/base-foreign-key.d.ts.map +1 -1
  209. package/build/src/database/drivers/base-foreign-key.js +2 -2
  210. package/build/src/database/drivers/base-table.d.ts +1 -1
  211. package/build/src/database/drivers/base-table.d.ts.map +1 -1
  212. package/build/src/database/drivers/base-table.js +2 -2
  213. package/build/src/database/drivers/base.d.ts +6 -6
  214. package/build/src/database/drivers/base.d.ts.map +1 -1
  215. package/build/src/database/drivers/base.js +11 -11
  216. package/build/src/database/drivers/mssql/index.js +2 -2
  217. package/build/src/database/drivers/mssql/table.js +2 -2
  218. package/build/src/database/drivers/mysql/index.js +4 -4
  219. package/build/src/database/drivers/mysql/query.js +2 -2
  220. package/build/src/database/drivers/mysql/table.js +2 -2
  221. package/build/src/database/drivers/pgsql/index.js +3 -3
  222. package/build/src/database/drivers/sqlite/base.d.ts +3 -3
  223. package/build/src/database/drivers/sqlite/base.d.ts.map +1 -1
  224. package/build/src/database/drivers/sqlite/base.js +17 -17
  225. package/build/src/database/drivers/sqlite/index.d.ts +4 -4
  226. package/build/src/database/drivers/sqlite/index.d.ts.map +1 -1
  227. package/build/src/database/drivers/sqlite/index.js +12 -12
  228. package/build/src/database/drivers/sqlite/index.native.js +2 -2
  229. package/build/src/database/drivers/sqlite/index.web.d.ts +2 -2
  230. package/build/src/database/drivers/sqlite/index.web.d.ts.map +1 -1
  231. package/build/src/database/drivers/sqlite/index.web.js +4 -4
  232. package/build/src/database/drivers/sqlite/query.js +2 -2
  233. package/build/src/database/drivers/sqlite/query.web.js +2 -2
  234. package/build/src/database/drivers/sqlite/sql/alter-table.js +3 -3
  235. package/build/src/database/drivers/sqlite/table.js +2 -2
  236. package/build/src/database/migrator/files-finder.js +2 -2
  237. package/build/src/database/migrator.d.ts +1 -1
  238. package/build/src/database/migrator.d.ts.map +1 -1
  239. package/build/src/database/migrator.js +7 -7
  240. package/build/src/database/pool/async-tracked-multi-connection.d.ts +9 -9
  241. package/build/src/database/pool/async-tracked-multi-connection.d.ts.map +1 -1
  242. package/build/src/database/pool/async-tracked-multi-connection.js +28 -28
  243. package/build/src/database/pool/base-methods-forward.js +7 -7
  244. package/build/src/database/pool/base.d.ts +1 -1
  245. package/build/src/database/pool/base.d.ts.map +1 -1
  246. package/build/src/database/pool/base.js +9 -9
  247. package/build/src/database/query/from-base.d.ts +1 -1
  248. package/build/src/database/query/from-base.d.ts.map +1 -1
  249. package/build/src/database/query/from-base.js +2 -2
  250. package/build/src/database/query/index.d.ts +4 -4
  251. package/build/src/database/query/index.d.ts.map +1 -1
  252. package/build/src/database/query/index.js +11 -11
  253. package/build/src/database/query/join-object.js +4 -4
  254. package/build/src/database/query/model-class-query.d.ts +4 -4
  255. package/build/src/database/query/model-class-query.d.ts.map +1 -1
  256. package/build/src/database/query/model-class-query.js +40 -40
  257. package/build/src/database/query/preloader/belongs-to.d.ts +2 -2
  258. package/build/src/database/query/preloader/belongs-to.d.ts.map +1 -1
  259. package/build/src/database/query/preloader/belongs-to.js +23 -23
  260. package/build/src/database/query/preloader/has-many.d.ts +1 -1
  261. package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
  262. package/build/src/database/query/preloader/has-many.js +21 -21
  263. package/build/src/database/query/preloader/has-one.d.ts +2 -2
  264. package/build/src/database/query/preloader/has-one.d.ts.map +1 -1
  265. package/build/src/database/query/preloader/has-one.js +9 -9
  266. package/build/src/database/query/preloader/selection.js +4 -4
  267. package/build/src/database/query/preloader.d.ts +1 -1
  268. package/build/src/database/query/preloader.d.ts.map +1 -1
  269. package/build/src/database/query/preloader.js +9 -9
  270. package/build/src/database/query/query-data.js +10 -10
  271. package/build/src/database/query/where-model-class-hash.js +8 -8
  272. package/build/src/database/query/with-count.js +10 -10
  273. package/build/src/database/record/acts-as-list.js +20 -20
  274. package/build/src/database/record/attachments/handle.d.ts +2 -2
  275. package/build/src/database/record/attachments/handle.d.ts.map +1 -1
  276. package/build/src/database/record/attachments/handle.js +3 -3
  277. package/build/src/database/record/attachments/normalize-input.js +15 -15
  278. package/build/src/database/record/attachments/storage-drivers/s3.js +7 -7
  279. package/build/src/database/record/attachments/store.d.ts +2 -2
  280. package/build/src/database/record/attachments/store.d.ts.map +1 -1
  281. package/build/src/database/record/attachments/store.js +14 -14
  282. package/build/src/database/record/index.d.ts +35 -32
  283. package/build/src/database/record/index.d.ts.map +1 -1
  284. package/build/src/database/record/index.js +147 -148
  285. package/build/src/database/record/instance-relationships/base.d.ts +3 -3
  286. package/build/src/database/record/instance-relationships/base.d.ts.map +1 -1
  287. package/build/src/database/record/instance-relationships/base.js +13 -13
  288. package/build/src/database/record/instance-relationships/belongs-to.js +4 -4
  289. package/build/src/database/record/instance-relationships/has-many.js +9 -9
  290. package/build/src/database/record/instance-relationships/has-one.d.ts +1 -1
  291. package/build/src/database/record/instance-relationships/has-one.d.ts.map +1 -1
  292. package/build/src/database/record/instance-relationships/has-one.js +8 -8
  293. package/build/src/database/record/relationships/base.js +4 -4
  294. package/build/src/database/record/state-machine.js +8 -8
  295. package/build/src/database/record/validators/presence.js +2 -2
  296. package/build/src/database/record/validators/uniqueness.js +6 -6
  297. package/build/src/database/table-data/index.d.ts +4 -4
  298. package/build/src/database/table-data/index.d.ts.map +1 -1
  299. package/build/src/database/table-data/index.js +5 -5
  300. package/build/src/environment-handlers/base.js +3 -3
  301. package/build/src/environment-handlers/browser.d.ts +3 -3
  302. package/build/src/environment-handlers/browser.d.ts.map +1 -1
  303. package/build/src/environment-handlers/browser.js +8 -8
  304. package/build/src/environment-handlers/node/cli/commands/cli-command-context.js +2 -2
  305. package/build/src/environment-handlers/node/cli/commands/console.d.ts.map +1 -1
  306. package/build/src/environment-handlers/node/cli/commands/console.js +5 -5
  307. package/build/src/environment-handlers/node/cli/commands/db/schema/dump.d.ts +1 -1
  308. package/build/src/environment-handlers/node/cli/commands/db/schema/dump.d.ts.map +1 -1
  309. package/build/src/environment-handlers/node/cli/commands/db/schema/dump.js +2 -2
  310. package/build/src/environment-handlers/node/cli/commands/db/schema/load.d.ts +1 -1
  311. package/build/src/environment-handlers/node/cli/commands/db/schema/load.d.ts.map +1 -1
  312. package/build/src/environment-handlers/node/cli/commands/db/schema/load.js +4 -4
  313. package/build/src/environment-handlers/node/cli/commands/generate/base-models.d.ts.map +1 -1
  314. package/build/src/environment-handlers/node/cli/commands/generate/base-models.js +2 -15
  315. package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +105 -33
  316. package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts.map +1 -1
  317. package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +172 -87
  318. package/build/src/environment-handlers/node.d.ts +2 -2
  319. package/build/src/environment-handlers/node.d.ts.map +1 -1
  320. package/build/src/environment-handlers/node.js +7 -7
  321. package/build/src/frontend-model-controller.d.ts +5 -6
  322. package/build/src/frontend-model-controller.d.ts.map +1 -1
  323. package/build/src/frontend-model-controller.js +95 -89
  324. package/build/src/frontend-model-resource/base-resource.d.ts +239 -91
  325. package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
  326. package/build/src/frontend-model-resource/base-resource.js +302 -172
  327. package/build/src/frontend-models/base.d.ts +186 -115
  328. package/build/src/frontend-models/base.d.ts.map +1 -1
  329. package/build/src/frontend-models/base.js +347 -218
  330. package/build/src/frontend-models/clear-pending-debounced-callback.js +2 -2
  331. package/build/src/frontend-models/event-hook-models.d.ts.map +1 -1
  332. package/build/src/frontend-models/event-hook-models.js +3 -3
  333. package/build/src/frontend-models/outgoing-event-buffer.d.ts +1 -1
  334. package/build/src/frontend-models/outgoing-event-buffer.d.ts.map +1 -1
  335. package/build/src/frontend-models/outgoing-event-buffer.js +3 -3
  336. package/build/src/frontend-models/preloader.d.ts.map +1 -1
  337. package/build/src/frontend-models/preloader.js +9 -8
  338. package/build/src/frontend-models/query.d.ts +16 -8
  339. package/build/src/frontend-models/query.d.ts.map +1 -1
  340. package/build/src/frontend-models/query.js +43 -39
  341. package/build/src/frontend-models/resource-definition.js +7 -7
  342. package/build/src/frontend-models/transport-serialization.js +12 -12
  343. package/build/src/frontend-models/use-destroyed-event.d.ts.map +1 -1
  344. package/build/src/frontend-models/use-destroyed-event.js +11 -11
  345. package/build/src/frontend-models/use-model-class-event.d.ts.map +1 -1
  346. package/build/src/frontend-models/use-model-class-event.js +12 -12
  347. package/build/src/frontend-models/use-updated-event.d.ts.map +1 -1
  348. package/build/src/frontend-models/use-updated-event.js +12 -12
  349. package/build/src/frontend-models/websocket-channel.d.ts +6 -1
  350. package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
  351. package/build/src/frontend-models/websocket-channel.js +26 -18
  352. package/build/src/frontend-models/websocket-publishers.js +11 -11
  353. package/build/src/http-client/response.d.ts +2 -2
  354. package/build/src/http-client/response.d.ts.map +1 -1
  355. package/build/src/http-client/response.js +3 -3
  356. package/build/src/http-server/client/index.d.ts +1 -1
  357. package/build/src/http-server/client/index.d.ts.map +1 -1
  358. package/build/src/http-server/client/index.js +9 -9
  359. package/build/src/http-server/client/params-to-object.js +11 -11
  360. package/build/src/http-server/client/request-buffer/form-data-part.d.ts +2 -2
  361. package/build/src/http-server/client/request-buffer/form-data-part.d.ts.map +1 -1
  362. package/build/src/http-server/client/request-buffer/form-data-part.js +3 -3
  363. package/build/src/http-server/client/request-buffer/index.d.ts +4 -4
  364. package/build/src/http-server/client/request-buffer/index.d.ts.map +1 -1
  365. package/build/src/http-server/client/request-buffer/index.js +9 -9
  366. package/build/src/http-server/client/request-parser.d.ts +1 -1
  367. package/build/src/http-server/client/request-parser.d.ts.map +1 -1
  368. package/build/src/http-server/client/request-parser.js +4 -4
  369. package/build/src/http-server/client/request-runner.js +7 -7
  370. package/build/src/http-server/client/request-timing.d.ts +5 -5
  371. package/build/src/http-server/client/request-timing.d.ts.map +1 -1
  372. package/build/src/http-server/client/request-timing.js +6 -6
  373. package/build/src/http-server/client/request.js +2 -2
  374. package/build/src/http-server/client/response.d.ts +3 -3
  375. package/build/src/http-server/client/response.d.ts.map +1 -1
  376. package/build/src/http-server/client/response.js +6 -6
  377. package/build/src/http-server/client/websocket-request.d.ts +3 -3
  378. package/build/src/http-server/client/websocket-request.d.ts.map +1 -1
  379. package/build/src/http-server/client/websocket-request.js +5 -5
  380. package/build/src/http-server/client/websocket-session.d.ts +6 -6
  381. package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
  382. package/build/src/http-server/client/websocket-session.js +19 -19
  383. package/build/src/http-server/development-reloader.d.ts +3 -3
  384. package/build/src/http-server/development-reloader.d.ts.map +1 -1
  385. package/build/src/http-server/development-reloader.js +6 -6
  386. package/build/src/http-server/index.d.ts +7 -7
  387. package/build/src/http-server/index.d.ts.map +1 -1
  388. package/build/src/http-server/index.js +13 -13
  389. package/build/src/http-server/remote-address.js +6 -6
  390. package/build/src/http-server/server-client.js +4 -4
  391. package/build/src/http-server/server-lock.js +5 -5
  392. package/build/src/http-server/websocket-channel-subscribers.d.ts +1 -1
  393. package/build/src/http-server/websocket-channel-subscribers.d.ts.map +1 -1
  394. package/build/src/http-server/websocket-channel-subscribers.js +4 -4
  395. package/build/src/http-server/websocket-channel.d.ts +1 -1
  396. package/build/src/http-server/websocket-channel.d.ts.map +1 -1
  397. package/build/src/http-server/websocket-channel.js +2 -2
  398. package/build/src/http-server/websocket-connection.d.ts +1 -1
  399. package/build/src/http-server/websocket-connection.d.ts.map +1 -1
  400. package/build/src/http-server/websocket-connection.js +2 -2
  401. package/build/src/http-server/websocket-event-log-store.d.ts +1 -1
  402. package/build/src/http-server/websocket-event-log-store.d.ts.map +1 -1
  403. package/build/src/http-server/websocket-event-log-store.js +7 -7
  404. package/build/src/http-server/websocket-events-host.d.ts +1 -1
  405. package/build/src/http-server/websocket-events-host.d.ts.map +1 -1
  406. package/build/src/http-server/websocket-events-host.js +3 -3
  407. package/build/src/http-server/worker-handler/in-process.d.ts +3 -3
  408. package/build/src/http-server/worker-handler/in-process.d.ts.map +1 -1
  409. package/build/src/http-server/worker-handler/in-process.js +9 -9
  410. package/build/src/http-server/worker-handler/index.d.ts +4 -4
  411. package/build/src/http-server/worker-handler/index.d.ts.map +1 -1
  412. package/build/src/http-server/worker-handler/index.js +5 -5
  413. package/build/src/http-server/worker-handler/worker-thread.d.ts +2 -2
  414. package/build/src/http-server/worker-handler/worker-thread.d.ts.map +1 -1
  415. package/build/src/http-server/worker-handler/worker-thread.js +4 -4
  416. package/build/src/logger/outputs/array-output.d.ts +3 -3
  417. package/build/src/logger/outputs/array-output.d.ts.map +1 -1
  418. package/build/src/logger/outputs/array-output.js +4 -4
  419. package/build/src/logger/outputs/console-output.d.ts +1 -1
  420. package/build/src/logger/outputs/console-output.d.ts.map +1 -1
  421. package/build/src/logger/outputs/console-output.js +2 -2
  422. package/build/src/logger/outputs/file-output.d.ts +4 -4
  423. package/build/src/logger/outputs/file-output.d.ts.map +1 -1
  424. package/build/src/logger/outputs/file-output.js +5 -5
  425. package/build/src/logger/outputs/stdout-output.d.ts.map +1 -1
  426. package/build/src/logger/outputs/stdout-output.js +2 -2
  427. package/build/src/logger.d.ts.map +1 -1
  428. package/build/src/logger.js +11 -11
  429. package/build/src/mailer/backends/smtp.d.ts.map +1 -1
  430. package/build/src/mailer/backends/smtp.js +5 -5
  431. package/build/src/mailer/base.js +11 -11
  432. package/build/src/mailer/delivery.d.ts +3 -3
  433. package/build/src/mailer/delivery.d.ts.map +1 -1
  434. package/build/src/mailer/delivery.js +5 -5
  435. package/build/src/plugins/sqljs-wasm-route-controller.js +2 -2
  436. package/build/src/routes/base-route.d.ts +6 -6
  437. package/build/src/routes/base-route.d.ts.map +1 -1
  438. package/build/src/routes/base-route.js +7 -7
  439. package/build/src/routes/basic-route.js +2 -2
  440. package/build/src/routes/hooks/frontend-model-command-route-hook.js +2 -2
  441. package/build/src/routes/index.js +2 -2
  442. package/build/src/routes/plugin-routes.js +2 -2
  443. package/build/src/routes/resolver.d.ts +1 -1
  444. package/build/src/routes/resolver.d.ts.map +1 -1
  445. package/build/src/routes/resolver.js +13 -13
  446. package/build/src/routes/resource-route.d.ts +1 -1
  447. package/build/src/routes/resource-route.d.ts.map +1 -1
  448. package/build/src/routes/resource-route.js +2 -2
  449. package/build/src/testing/browser-frontend-model-event-hook-scenarios.d.ts.map +1 -1
  450. package/build/src/testing/browser-frontend-model-event-hook-scenarios.js +9 -9
  451. package/build/src/testing/expect-utils.js +13 -13
  452. package/build/src/testing/expect.d.ts +7 -1
  453. package/build/src/testing/expect.d.ts.map +1 -1
  454. package/build/src/testing/expect.js +17 -9
  455. package/build/src/testing/test-files-finder.d.ts +8 -8
  456. package/build/src/testing/test-files-finder.d.ts.map +1 -1
  457. package/build/src/testing/test-files-finder.js +9 -9
  458. package/build/src/testing/test-filter-parser.js +3 -3
  459. package/build/src/testing/test-runner.d.ts +2 -2
  460. package/build/src/testing/test-runner.d.ts.map +1 -1
  461. package/build/src/testing/test-runner.js +17 -17
  462. package/build/src/testing/test-suite-splitter.js +2 -2
  463. package/build/src/testing/test.d.ts +2 -2
  464. package/build/src/testing/test.d.ts.map +1 -1
  465. package/build/src/testing/test.js +16 -16
  466. package/build/src/utils/backtrace-cleaner.d.ts +1 -1
  467. package/build/src/utils/backtrace-cleaner.d.ts.map +1 -1
  468. package/build/src/utils/backtrace-cleaner.js +2 -2
  469. package/build/src/utils/format-value.js +2 -2
  470. package/build/src/utils/model-scope.js +2 -2
  471. package/build/src/utils/ransack.js +27 -27
  472. package/build/src/utils/with-tracked-stack-async-hooks.js +8 -8
  473. package/build/src/utils/with-tracked-stack.js +4 -4
  474. package/build/testing/browser-frontend-model-event-hook-scenarios.js +8 -8
  475. package/build/testing/expect-utils.js +11 -11
  476. package/build/testing/expect.js +16 -7
  477. package/build/testing/test-files-finder.js +8 -8
  478. package/build/testing/test-filter-parser.js +2 -2
  479. package/build/testing/test-runner.js +16 -16
  480. package/build/testing/test-suite-splitter.js +1 -1
  481. package/build/testing/test.js +15 -15
  482. package/build/utils/backtrace-cleaner.js +1 -1
  483. package/build/utils/format-value.js +1 -1
  484. package/build/utils/model-scope.js +1 -1
  485. package/build/utils/ransack.js +26 -26
  486. package/build/utils/with-tracked-stack-async-hooks.js +7 -7
  487. package/build/utils/with-tracked-stack.js +3 -3
  488. package/package.json +2 -1
  489. package/src/application.js +4 -4
  490. package/src/authorization/ability.js +8 -8
  491. package/src/authorization/base-resource.js +4 -4
  492. package/src/background-jobs/cron-expression.js +1 -1
  493. package/src/background-jobs/forked-runner-child.js +1 -1
  494. package/src/background-jobs/job-registry.js +1 -1
  495. package/src/background-jobs/job-runner.js +1 -1
  496. package/src/background-jobs/job.js +1 -1
  497. package/src/background-jobs/json-socket.js +4 -4
  498. package/src/background-jobs/main.js +21 -21
  499. package/src/background-jobs/scheduler.js +7 -7
  500. package/src/background-jobs/store.js +7 -7
  501. package/src/background-jobs/web/controller.js +2 -2
  502. package/src/background-jobs/worker.js +12 -12
  503. package/src/beacon/client.js +11 -8
  504. package/src/beacon/in-process-broker.js +2 -2
  505. package/src/beacon/in-process-client.js +2 -2
  506. package/src/beacon/server.js +3 -3
  507. package/src/cli/browser-cli.js +1 -1
  508. package/src/cli/commands/db/base-command.js +2 -2
  509. package/src/cli/index.js +2 -2
  510. package/src/cli/tenant-database-command-helper.js +3 -3
  511. package/src/cli/use-browser-cli.js +1 -1
  512. package/src/configuration.js +38 -38
  513. package/src/controller.js +8 -8
  514. package/src/current-configuration.js +1 -1
  515. package/src/database/annotations-async-hooks.js +2 -2
  516. package/src/database/annotations.js +3 -3
  517. package/src/database/drivers/base-column.js +1 -1
  518. package/src/database/drivers/base-foreign-key.js +1 -1
  519. package/src/database/drivers/base-table.js +1 -1
  520. package/src/database/drivers/base.js +10 -10
  521. package/src/database/drivers/mssql/index.js +1 -1
  522. package/src/database/drivers/mssql/table.js +1 -1
  523. package/src/database/drivers/mysql/index.js +3 -3
  524. package/src/database/drivers/mysql/query.js +1 -1
  525. package/src/database/drivers/mysql/table.js +1 -1
  526. package/src/database/drivers/pgsql/index.js +2 -2
  527. package/src/database/drivers/sqlite/base.js +16 -16
  528. package/src/database/drivers/sqlite/index.js +11 -11
  529. package/src/database/drivers/sqlite/index.native.js +1 -1
  530. package/src/database/drivers/sqlite/index.web.js +3 -3
  531. package/src/database/drivers/sqlite/query.js +1 -1
  532. package/src/database/drivers/sqlite/query.web.js +1 -1
  533. package/src/database/drivers/sqlite/sql/alter-table.js +2 -2
  534. package/src/database/drivers/sqlite/table.js +1 -1
  535. package/src/database/migrator/files-finder.js +1 -1
  536. package/src/database/migrator.js +6 -6
  537. package/src/database/pool/async-tracked-multi-connection.js +27 -27
  538. package/src/database/pool/base-methods-forward.js +4 -4
  539. package/src/database/pool/base.js +8 -8
  540. package/src/database/query/from-base.js +1 -1
  541. package/src/database/query/index.js +10 -10
  542. package/src/database/query/join-object.js +3 -3
  543. package/src/database/query/model-class-query.js +39 -39
  544. package/src/database/query/preloader/belongs-to.js +22 -22
  545. package/src/database/query/preloader/has-many.js +20 -20
  546. package/src/database/query/preloader/has-one.js +8 -8
  547. package/src/database/query/preloader/selection.js +3 -3
  548. package/src/database/query/preloader.js +8 -8
  549. package/src/database/query/query-data.js +9 -9
  550. package/src/database/query/where-model-class-hash.js +7 -7
  551. package/src/database/query/with-count.js +9 -9
  552. package/src/database/record/acts-as-list.js +15 -15
  553. package/src/database/record/attachments/handle.js +2 -2
  554. package/src/database/record/attachments/normalize-input.js +12 -12
  555. package/src/database/record/attachments/storage-drivers/s3.js +6 -6
  556. package/src/database/record/attachments/store.js +13 -13
  557. package/src/database/record/index.js +127 -128
  558. package/src/database/record/instance-relationships/base.js +11 -11
  559. package/src/database/record/instance-relationships/belongs-to.js +3 -3
  560. package/src/database/record/instance-relationships/has-many.js +8 -8
  561. package/src/database/record/instance-relationships/has-one.js +7 -7
  562. package/src/database/record/relationships/base.js +2 -2
  563. package/src/database/record/state-machine.js +7 -7
  564. package/src/database/record/validators/presence.js +1 -1
  565. package/src/database/record/validators/uniqueness.js +5 -5
  566. package/src/database/table-data/index.js +4 -4
  567. package/src/environment-handlers/base.js +2 -2
  568. package/src/environment-handlers/browser.js +7 -7
  569. package/src/environment-handlers/node/cli/commands/cli-command-context.js +1 -1
  570. package/src/environment-handlers/node/cli/commands/console.js +4 -4
  571. package/src/environment-handlers/node/cli/commands/db/schema/dump.js +1 -1
  572. package/src/environment-handlers/node/cli/commands/db/schema/load.js +3 -3
  573. package/src/environment-handlers/node/cli/commands/generate/base-models.js +1 -16
  574. package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +179 -89
  575. package/src/environment-handlers/node.js +6 -6
  576. package/src/frontend-model-controller.js +90 -82
  577. package/src/frontend-model-resource/base-resource.js +343 -167
  578. package/src/frontend-models/base.js +362 -205
  579. package/src/frontend-models/clear-pending-debounced-callback.js +1 -1
  580. package/src/frontend-models/event-hook-models.js +2 -2
  581. package/src/frontend-models/outgoing-event-buffer.js +2 -2
  582. package/src/frontend-models/preloader.js +8 -7
  583. package/src/frontend-models/query.js +43 -38
  584. package/src/frontend-models/resource-definition.js +6 -6
  585. package/src/frontend-models/transport-serialization.js +11 -11
  586. package/src/frontend-models/use-destroyed-event.js +10 -10
  587. package/src/frontend-models/use-model-class-event.js +11 -11
  588. package/src/frontend-models/use-updated-event.js +11 -11
  589. package/src/frontend-models/websocket-channel.js +26 -17
  590. package/src/frontend-models/websocket-publishers.js +9 -9
  591. package/src/http-client/response.js +2 -2
  592. package/src/http-server/client/index.js +8 -8
  593. package/src/http-server/client/params-to-object.js +10 -10
  594. package/src/http-server/client/request-buffer/form-data-part.js +2 -2
  595. package/src/http-server/client/request-buffer/index.js +8 -8
  596. package/src/http-server/client/request-parser.js +3 -3
  597. package/src/http-server/client/request-runner.js +6 -6
  598. package/src/http-server/client/request-timing.js +5 -5
  599. package/src/http-server/client/request.js +1 -1
  600. package/src/http-server/client/response.js +5 -5
  601. package/src/http-server/client/websocket-request.js +4 -4
  602. package/src/http-server/client/websocket-session.js +18 -18
  603. package/src/http-server/development-reloader.js +5 -5
  604. package/src/http-server/index.js +12 -12
  605. package/src/http-server/remote-address.js +4 -4
  606. package/src/http-server/server-client.js +3 -3
  607. package/src/http-server/server-lock.js +4 -4
  608. package/src/http-server/websocket-channel-subscribers.js +3 -3
  609. package/src/http-server/websocket-channel.js +1 -1
  610. package/src/http-server/websocket-connection.js +1 -1
  611. package/src/http-server/websocket-event-log-store.js +6 -6
  612. package/src/http-server/websocket-events-host.js +2 -2
  613. package/src/http-server/worker-handler/in-process.js +7 -7
  614. package/src/http-server/worker-handler/index.js +4 -4
  615. package/src/http-server/worker-handler/worker-thread.js +3 -3
  616. package/src/logger/outputs/array-output.js +3 -3
  617. package/src/logger/outputs/console-output.js +1 -1
  618. package/src/logger/outputs/file-output.js +4 -4
  619. package/src/logger/outputs/stdout-output.js +1 -1
  620. package/src/logger.js +10 -10
  621. package/src/mailer/backends/smtp.js +3 -3
  622. package/src/mailer/base.js +8 -8
  623. package/src/mailer/delivery.js +4 -4
  624. package/src/plugins/sqljs-wasm-route-controller.js +1 -1
  625. package/src/routes/base-route.js +6 -6
  626. package/src/routes/basic-route.js +1 -1
  627. package/src/routes/hooks/frontend-model-command-route-hook.js +1 -1
  628. package/src/routes/index.js +1 -1
  629. package/src/routes/plugin-routes.js +1 -1
  630. package/src/routes/resolver.js +11 -11
  631. package/src/routes/resource-route.js +1 -1
  632. package/src/testing/browser-frontend-model-event-hook-scenarios.js +8 -8
  633. package/src/testing/expect-utils.js +11 -11
  634. package/src/testing/expect.js +16 -7
  635. package/src/testing/test-files-finder.js +8 -8
  636. package/src/testing/test-filter-parser.js +2 -2
  637. package/src/testing/test-runner.js +16 -16
  638. package/src/testing/test-suite-splitter.js +1 -1
  639. package/src/testing/test.js +15 -15
  640. package/src/utils/backtrace-cleaner.js +1 -1
  641. package/src/utils/format-value.js +1 -1
  642. package/src/utils/model-scope.js +1 -1
  643. package/src/utils/ransack.js +26 -26
  644. package/src/utils/with-tracked-stack-async-hooks.js +7 -7
  645. package/src/utils/with-tracked-stack.js +3 -3
@@ -4,10 +4,29 @@ import AuthorizationBaseResource from "../authorization/base-resource.js"
4
4
  import * as inflection from "inflection"
5
5
  import isPlainObject from "../utils/plain-object.js"
6
6
 
7
+ /**
8
+ * Built-in frontend-model resource action.
9
+ * @typedef {"index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url"} FrontendModelResourceAction
10
+ */
11
+
12
+ /**
13
+ * Frontend-model controller methods used by resources.
14
+ * @typedef {import("../controller.js").default & {
15
+ * currentAbility: () => import("../authorization/ability.js").default | undefined,
16
+ * frontendModelAbilityAction: (action: FrontendModelResourceAction) => string,
17
+ * frontendModelAuthorizedQuery: (action: FrontendModelResourceAction) => import("../database/query/model-class-query.js").default<typeof import("../database/record/index.js").default>,
18
+ * frontendModelIndexQuery: () => import("../database/query/model-class-query.js").default<typeof import("../database/record/index.js").default>,
19
+ * frontendModelParams: () => import("../configuration-types.js").VelociousParams,
20
+ * frontendModelPreload: () => import("../database/query/index.js").NestedPreloadRecord | null,
21
+ * frontendModelResourceConfigurationForModelClass: (modelClass: typeof import("../database/record/index.js").default) => FrontendModelResolvedResourceConfiguration | null,
22
+ * serializeFrontendModel: (model: import("../database/record/index.js").default) => Promise<Record<string, object | string | number | boolean | null>>
23
+ * }} FrontendModelResourceController
24
+ */
25
+
7
26
  /**
8
27
  * FrontendModelResourceControllerArgs type.
9
28
  * @typedef {object} FrontendModelResourceControllerArgs
10
- * @property {import("../controller.js").default} controller - Frontend-model controller instance.
29
+ * @property {FrontendModelResourceController} controller - Frontend-model controller instance.
11
30
  * @property {typeof import("../database/record/index.js").default} modelClass - Backing model class.
12
31
  * @property {string} modelName - Model name.
13
32
  * @property {import("../configuration-types.js").VelociousParams} params - Request params.
@@ -35,51 +54,68 @@ import isPlainObject from "../utils/plain-object.js"
35
54
  * @property {import("../configuration-types.js").NormalizedFrontendModelResourceConfiguration} resourceConfiguration - Normalized resource configuration.
36
55
  */
37
56
 
57
+ /**
58
+ * Transport-safe value accepted in frontend-model resource mutation payloads.
59
+ * Nested object/array values are intentionally opaque because TypeScript rejects
60
+ * recursive JSDoc typedefs for this transport payload contract.
61
+ * @typedef {import("../frontend-models/base.js").FrontendModelTransportValue | import("../database/record/index.js").default | Record<string, unknown> | Array<unknown>} FrontendModelResourcePayloadValue
62
+ */
63
+
64
+ /**
65
+ * Attribute payload accepted by frontend-model resource mutations.
66
+ * @typedef {Record<string, FrontendModelResourcePayloadValue>} FrontendModelResourceAttributePayload
67
+ */
68
+
69
+ /**
70
+ * Virtual setter method on a frontend-model resource.
71
+ * @typedef {function(import("../database/record/index.js").default, FrontendModelResourcePayloadValue): (void | Promise<void>)} FrontendModelResourceVirtualSetter
72
+ */
73
+
74
+ /**
75
+ * Static helpers used when checking whether a model-like receiver accepts an attribute.
76
+ * @typedef {object} WritableAttributeReceiverClass
77
+ * @property {function(string): string} [resolveAttributeName] - Resolves aliases to canonical attribute names.
78
+ * @property {function(Record<string, ?>, string): string} [findMemberNameInsensitive] - Locates a setter method on the receiver.
79
+ */
80
+
81
+ /**
82
+ * Options passed while saving frontend-model resource mutations.
83
+ * @typedef {object} FrontendModelResourceSaveOptions
84
+ * @property {FrontendModelResourceAttributePayload | null} [attachments] - Uploaded attachment attributes.
85
+ * @property {FrontendModelResourceController | null} [controller] - Controller handling the mutation.
86
+ * @property {FrontendModelResourceAttributePayload | null} [nestedAttributes] - Nested attributes payload.
87
+ */
88
+
89
+ /**
90
+ * Normalized nested attributes entry.
91
+ * @typedef {FrontendModelResourceAttributePayload & {id?: string | number, _destroy?: boolean, attributes?: FrontendModelResourceAttributePayload, attachments?: FrontendModelResourceAttributePayload, nestedAttributes?: FrontendModelResourceAttributePayload}} FrontendModelResourceNestedEntry
92
+ */
93
+
38
94
  /**
39
95
  * Base class for backend frontend-model resources.
40
96
  * @template {typeof import("../database/record/index.js").default} [TModelClass=typeof import("../database/record/index.js").default]
41
97
  */
42
98
  export default class FrontendModelBaseResource extends AuthorizationBaseResource {
43
- /**
44
- * Backing model class.
45
- @type {typeof import("../database/record/index.js").default | undefined} */
99
+ /** @type {typeof import("../database/record/index.js").default | undefined} */
46
100
  static ModelClass = undefined
47
101
 
48
- /**
49
- * Attributes.
50
- @type {Record<string, ?> | string[] | undefined} */
102
+ /** @type {Record<string, ?> | string[] | undefined} */
51
103
  static attributes = undefined
52
- /**
53
- * Abilities.
54
- @type {string[] | undefined} */
104
+ /** @type {string[] | undefined} */
55
105
  static abilities = undefined
56
- /**
57
- * Attachments.
58
- @type {Record<string, ?> | undefined} */
106
+ /** @type {Record<string, ?> | undefined} */
59
107
  static attachments = undefined
60
- /**
61
- * Collection commands.
62
- @type {string[] | undefined} */
108
+ /** @type {string[] | undefined} */
63
109
  static collectionCommands = undefined
64
- /**
65
- * Built in collection commands.
66
- @type {string[] | undefined} */
110
+ /** @type {string[] | undefined} */
67
111
  static builtInCollectionCommands = undefined
68
- /**
69
- * Member commands.
70
- @type {string[] | undefined} */
112
+ /** @type {string[] | undefined} */
71
113
  static memberCommands = undefined
72
- /**
73
- * Built in member commands.
74
- @type {string[] | undefined} */
114
+ /** @type {string[] | undefined} */
75
115
  static builtInMemberCommands = undefined
76
- /**
77
- * Relationships.
78
- @type {string[] | undefined} */
116
+ /** @type {string[] | undefined} */
79
117
  static relationships = undefined
80
- /**
81
- * Translated attributes.
82
- @type {string[] | undefined} */
118
+ /** @type {string[] | undefined} */
83
119
  static translatedAttributes = undefined
84
120
 
85
121
  /**
@@ -93,32 +129,22 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
93
129
  locals: "locals" in args ? args.locals || {} : {}
94
130
  })
95
131
 
132
+ const ResourceClass = /** @type {typeof FrontendModelBaseResource} */ (this.constructor)
133
+ const defaultResourceConfiguration = /** @type {import("../configuration-types.js").FrontendModelResourceConfiguration} */ ({attributes: []})
134
+
96
135
  this.controller = "controller" in args ? args.controller : undefined
97
- this.modelClassValue = "modelClass" in args ? args.modelClass : /**
98
- * Narrows the runtime value to the documented type.
99
- @type {typeof import("../database/record/index.js").default | undefined} */ (/**
100
- * Narrows the runtime value to the documented type.
101
- @type {typeof FrontendModelBaseResource} */ (this.constructor).modelClass())
136
+ this.modelClassValue = "modelClass" in args ? args.modelClass : ResourceClass.modelClass()
102
137
  this.modelNameValue = "modelName" in args ? args.modelName : (this.modelClassValue?.getModelName ? this.modelClassValue.getModelName() : this.modelClassValue?.name || "")
103
138
  this.paramsValue = "params" in args ? args.params : undefined
104
- this.resourceConfigurationValue = "resourceConfiguration" in args ? args.resourceConfiguration : /**
105
- * Narrows the runtime value to the documented type.
106
- @type {import("../configuration-types.js").FrontendModelResourceConfiguration} */ ({attributes: []})
139
+ this.resourceConfigurationValue = "resourceConfiguration" in args ? args.resourceConfiguration : defaultResourceConfiguration
107
140
  }
108
141
 
109
142
  /**
110
143
  * Runs typed controller instance.
111
- * @returns {import("../controller.js").default & {
112
- * frontendModelAuthorizedQuery: (action: "index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url") => import("../database/query/model-class-query.js").default<TModelClass>,
113
- * frontendModelAbilityAction: (action: string) => string,
114
- * currentAbility: () => import("../authorization/ability.js").default | undefined,
115
- * frontendModelIndexQuery: () => import("../database/query/model-class-query.js").default<TModelClass>,
116
- * frontendModelPreload: () => import("../database/query/index.js").NestedPreloadRecord | null,
117
- * serializeFrontendModel: (model: import("../database/record/index.js").default) => Promise<Record<string, unknown>>
118
- * }} - Controller instance with frontend-model helpers.
144
+ * @returns {FrontendModelResourceController} - Controller instance with frontend-model helpers.
119
145
  */
120
146
  typedControllerInstance() {
121
- return /** Narrows the runtime value to the documented type. @type {?} */ (this.controller)
147
+ return /** @type {FrontendModelResourceController} */ (this.controller)
122
148
  }
123
149
 
124
150
  /**
@@ -126,9 +152,7 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
126
152
  * @returns {import("../configuration-types.js").FrontendModelResourceConfiguration} - Static resource config (raw user input shape; consumers normalize).
127
153
  */
128
154
  static resourceConfig() {
129
- /**
130
- * Config.
131
- @type {import("../configuration-types.js").FrontendModelResourceConfiguration} */
155
+ /** @type {import("../configuration-types.js").FrontendModelResourceConfiguration} */
132
156
  const config = {
133
157
  attributes: this.attributes || []
134
158
  }
@@ -244,6 +268,118 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
244
268
  return []
245
269
  }
246
270
 
271
+ /**
272
+ * Normalizes create attributes before permission filtering and saving.
273
+ * @param {FrontendModelResourceAttributePayload} attributes - Incoming create attributes.
274
+ * @param {FrontendModelResourceSaveOptions} options - Save options.
275
+ * @returns {FrontendModelResourceAttributePayload | Promise<FrontendModelResourceAttributePayload>} - Normalized attributes.
276
+ */
277
+ normalizeCreateAttributes(attributes, options) {
278
+ void options
279
+
280
+ return attributes
281
+ }
282
+
283
+ /**
284
+ * Normalizes update attributes before permission filtering and saving.
285
+ * @param {import("../database/record/index.js").default} model - Existing model.
286
+ * @param {FrontendModelResourceAttributePayload} attributes - Incoming update attributes.
287
+ * @param {FrontendModelResourceSaveOptions} options - Save options.
288
+ * @returns {FrontendModelResourceAttributePayload | Promise<FrontendModelResourceAttributePayload>} - Normalized attributes.
289
+ */
290
+ normalizeUpdateAttributes(model, attributes, options) {
291
+ void model
292
+ void options
293
+
294
+ return attributes
295
+ }
296
+
297
+ /**
298
+ * Runs before create.
299
+ * @param {import("../database/record/index.js").default} model - New model before assignment/save.
300
+ * @param {FrontendModelResourceAttributePayload} attributes - Normalized create attributes.
301
+ * @param {FrontendModelResourceSaveOptions} options - Save options.
302
+ * @returns {void | Promise<void>} - Resolves when the hook finishes.
303
+ */
304
+ beforeCreate(model, attributes, options) {
305
+ void model
306
+ void attributes
307
+ void options
308
+ }
309
+
310
+ /**
311
+ * Runs after create.
312
+ * @param {import("../database/record/index.js").default} model - Created model.
313
+ * @param {FrontendModelResourceAttributePayload} attributes - Normalized create attributes.
314
+ * @param {FrontendModelResourceSaveOptions} options - Save options.
315
+ * @returns {void | Promise<void>} - Resolves when the hook finishes.
316
+ */
317
+ afterCreate(model, attributes, options) {
318
+ void model
319
+ void attributes
320
+ void options
321
+ }
322
+
323
+ /**
324
+ * Runs before update.
325
+ * @param {import("../database/record/index.js").default} model - Existing model before assignment/save.
326
+ * @param {FrontendModelResourceAttributePayload} attributes - Normalized update attributes.
327
+ * @param {FrontendModelResourceSaveOptions} options - Save options.
328
+ * @returns {void | Promise<void>} - Resolves when the hook finishes.
329
+ */
330
+ beforeUpdate(model, attributes, options) {
331
+ void model
332
+ void attributes
333
+ void options
334
+ }
335
+
336
+ /**
337
+ * Runs after update.
338
+ * @param {import("../database/record/index.js").default} model - Updated model.
339
+ * @param {FrontendModelResourceAttributePayload} attributes - Normalized update attributes.
340
+ * @param {FrontendModelResourceSaveOptions} options - Save options.
341
+ * @returns {void | Promise<void>} - Resolves when the hook finishes.
342
+ */
343
+ afterUpdate(model, attributes, options) {
344
+ void model
345
+ void attributes
346
+ void options
347
+ }
348
+
349
+ /**
350
+ * Runs before destroy.
351
+ * @param {import("../database/record/index.js").default} model - Model before destroy.
352
+ * @returns {void | Promise<void>} - Resolves when the hook finishes.
353
+ */
354
+ beforeDestroy(model) {
355
+ void model
356
+ }
357
+
358
+ /**
359
+ * Runs after destroy.
360
+ * @param {import("../database/record/index.js").default} model - Destroyed model.
361
+ * @returns {void | Promise<void>} - Resolves when the hook finishes.
362
+ */
363
+ afterDestroy(model) {
364
+ void model
365
+ }
366
+
367
+ /**
368
+ * Wraps create/update/destroy resource mutations.
369
+ * @template Result
370
+ * @param {object} args - Transaction args.
371
+ * @param {"create" | "update" | "destroy"} args.action - Mutation action.
372
+ * @param {import("../database/record/index.js").default} args.model - Mutated model.
373
+ * @param {() => Promise<Result>} args.callback - Mutation callback.
374
+ * @returns {Promise<Result>} - Callback result.
375
+ */
376
+ async runMutationTransaction({action, model, callback}) {
377
+ void action
378
+ void model
379
+
380
+ return await callback()
381
+ }
382
+
247
383
  /**
248
384
  * Runs primary key.
249
385
  * @returns {string} - Primary key.
@@ -252,11 +388,12 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
252
388
 
253
389
  /**
254
390
  * Runs authorized query.
255
- * @param {"index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url"} action - Ability action.
391
+ * @param {FrontendModelResourceAction} action - Ability action.
256
392
  * @returns {import("../database/query/model-class-query.js").default<TModelClass>} - Authorized query.
257
393
  */
258
394
  authorizedQuery(action) {
259
- return this.typedControllerInstance().frontendModelAuthorizedQuery(action)
395
+ // Narrows the controller query to this resource's model class.
396
+ return /** @type {import("../database/query/model-class-query.js").default<TModelClass>} */ (this.typedControllerInstance().frontendModelAuthorizedQuery(action))
260
397
  }
261
398
 
262
399
 
@@ -329,18 +466,30 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
329
466
 
330
467
  /**
331
468
  * Runs create.
332
- * @param {Record<string, ?>} attributes - Create attributes.
333
- * @param {{attachments?: Record<string, ?> | null, controller?: ?, nestedAttributes?: Record<string, ?> | null}} [options] - Save options.
469
+ * @param {FrontendModelResourceAttributePayload} attributes - Create attributes.
470
+ * @param {FrontendModelResourceSaveOptions} [options] - Save options.
334
471
  * @returns {Promise<import("../database/record/index.js").default>} - Created model.
335
472
  */
336
473
  async create(attributes, options = {}) {
337
- const attachmentSplit = this._extractAttachmentAttributes(attributes, options.attachments ?? null)
338
- const permit = parsePermittedParams(this.permittedParams({action: "create", ability: this.ability, locals: this.locals, params: attributes}))
474
+ const normalizedAttributes = await this.normalizeCreateAttributes(attributes, options)
475
+ const attachmentSplit = this._extractAttachmentAttributes(normalizedAttributes, options.attachments ?? null)
476
+ const permit = parsePermittedParams(this.permittedParams({action: "create", ability: this.ability, locals: this.locals, params: normalizedAttributes}))
339
477
  const filtered = filterWritableFrontendModelAttributes(this.modelClass().prototype, attachmentSplit.attributes, this, permit.attributes)
340
478
  const ModelClass = this.modelClass()
341
479
  const model = new ModelClass()
342
480
 
343
- return await this._saveWithNestedAttributes({filtered, model, options: {...options, attachments: attachmentSplit.attachments}, permit})
481
+ return await this.runMutationTransaction({
482
+ action: "create",
483
+ model,
484
+ callback: async () => {
485
+ await this.beforeCreate(model, normalizedAttributes, options)
486
+ const savedModel = await this._saveWithNestedAttributes({filtered, model, options: {...options, attachments: attachmentSplit.attachments}, permit})
487
+
488
+ await this.afterCreate(savedModel, normalizedAttributes, options)
489
+
490
+ return savedModel
491
+ }
492
+ })
344
493
  }
345
494
 
346
495
  /**
@@ -355,21 +504,33 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
355
504
  /**
356
505
  * Runs update.
357
506
  * @param {import("../database/record/index.js").default} model - Existing model.
358
- * @param {Record<string, ?>} attributes - Update attributes.
359
- * @param {{attachments?: Record<string, ?> | null, controller?: ?, nestedAttributes?: Record<string, ?> | null}} [options] - Save options.
507
+ * @param {FrontendModelResourceAttributePayload} attributes - Update attributes.
508
+ * @param {FrontendModelResourceSaveOptions} [options] - Save options.
360
509
  * @returns {Promise<import("../database/record/index.js").default>} - Updated model.
361
510
  */
362
511
  async update(model, attributes, options = {}) {
363
- const attachmentSplit = this._extractAttachmentAttributes(attributes, options.attachments ?? null)
364
- const permit = parsePermittedParams(this.permittedParams({action: "update", ability: this.ability, locals: this.locals, params: attributes}))
512
+ const normalizedAttributes = await this.normalizeUpdateAttributes(model, attributes, options)
513
+ const attachmentSplit = this._extractAttachmentAttributes(normalizedAttributes, options.attachments ?? null)
514
+ const permit = parsePermittedParams(this.permittedParams({action: "update", ability: this.ability, locals: this.locals, params: normalizedAttributes}))
365
515
  const filtered = filterWritableFrontendModelAttributes(model, attachmentSplit.attributes, this, permit.attributes)
366
516
 
367
- return await this._saveWithNestedAttributes({filtered, model, options: {...options, attachments: attachmentSplit.attachments}, permit})
517
+ return await this.runMutationTransaction({
518
+ action: "update",
519
+ model,
520
+ callback: async () => {
521
+ await this.beforeUpdate(model, normalizedAttributes, options)
522
+ const savedModel = await this._saveWithNestedAttributes({filtered, model, options: {...options, attachments: attachmentSplit.attachments}, permit})
523
+
524
+ await this.afterUpdate(savedModel, normalizedAttributes, options)
525
+
526
+ return savedModel
527
+ }
528
+ })
368
529
  }
369
530
 
370
531
  /**
371
532
  * Saves a model and applies nested attributes in one transaction.
372
- * @param {{filtered: Record<string, ?>, model: import("../database/record/index.js").default, options: {attachments?: Record<string, ?> | null, controller?: ?, nestedAttributes?: Record<string, ?> | null}, permit: {attributes: string[], nested: Record<string, ?>}}} args - Save arguments.
533
+ * @param {{filtered: Record<string, ?>, model: import("../database/record/index.js").default, options: FrontendModelResourceSaveOptions, permit: {attributes: string[], nested: Record<string, ?>}}} args - Save arguments.
373
534
  * @returns {Promise<import("../database/record/index.js").default>} - Saved model.
374
535
  */
375
536
  async _saveWithNestedAttributes({filtered, model, options, permit}) {
@@ -400,27 +561,17 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
400
561
  * @returns {Promise<void>}
401
562
  */
402
563
  async _assignWithVirtualSetters(model, attributes) {
403
- /**
404
- * Direct attributes.
405
- @type {Record<string, ?>} */
564
+ /** @type {Record<string, ?>} */
406
565
  const directAttributes = {}
407
- const translatedSet = new Set(/**
408
- * Narrows the runtime value to the documented type.
409
- @type {typeof FrontendModelBaseResource} */ (this.constructor).translatedAttributes || [])
566
+ const ResourceClass = /** @type {typeof FrontendModelBaseResource} */ (this.constructor)
567
+ const translatedSet = new Set(ResourceClass.translatedAttributes || [])
410
568
 
411
569
  for (const [name, value] of Object.entries(attributes)) {
412
570
  const resourceSetterName = `set${inflection.camelize(name)}Attribute`
571
+ const resourceSetter = virtualSetterLookup(this)[resourceSetterName]
413
572
 
414
- if (typeof /**
415
- * Narrows the runtime value to the documented type.
416
- @type {Record<string, ?>} */ (/**
417
- * Narrows the runtime value to the documented type.
418
- @type {?} */ (this))[resourceSetterName] === "function") {
419
- await /**
420
- * Narrows the runtime value to the documented type.
421
- @type {Record<string, ?>} */ (/**
422
- * Narrows the runtime value to the documented type.
423
- @type {?} */ (this))[resourceSetterName](model, value)
573
+ if (typeof resourceSetter === "function") {
574
+ await resourceSetter.call(this, model, value)
424
575
  } else if (translatedSet.has(name)) {
425
576
  await this._setTranslatedAttributeOnModel(model, name, value)
426
577
  } else {
@@ -516,25 +667,21 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
516
667
  * Sets a translated attribute on a model via the translations relationship.
517
668
  * @param {import("../database/record/index.js").default} model - Model instance.
518
669
  * @param {string} name - Attribute name.
519
- * @param {?} value - Attribute value.
670
+ * @param {FrontendModelResourcePayloadValue} value - Attribute value.
520
671
  * @returns {Promise<void>}
521
672
  */
522
673
  async _setTranslatedAttributeOnModel(model, name, value) {
523
674
  const locale = this.context?.configuration?.getLocale?.() || "en"
524
675
  const instanceRelationship = model.getRelationshipByName("translations")
525
676
 
526
- /**
527
- * Defines translation.
528
- @type {import("../database/record/index.js").default | undefined} */
677
+ /** @type {import("../database/record/index.js").default | undefined} */
529
678
  let translation
530
679
 
531
680
  if (model.isNewRecord()) {
532
681
  const loaded = instanceRelationship.loaded()
533
682
 
534
683
  if (Array.isArray(loaded)) {
535
- translation = loaded.find((/**
536
- * Narrows the runtime value to the documented type.
537
- @type {Record<string, ?>} */ t) => t.locale() === locale)
684
+ translation = loaded.find((t) => /** @type {Record<string, ?>} */ (t).locale() === locale)
538
685
  }
539
686
  } else {
540
687
  if (!instanceRelationship.getPreloaded()) {
@@ -544,9 +691,7 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
544
691
  const loaded = instanceRelationship.loaded()
545
692
 
546
693
  if (Array.isArray(loaded)) {
547
- translation = loaded.find((/**
548
- * Narrows the runtime value to the documented type.
549
- @type {Record<string, ?>} */ t) => t.locale() === locale)
694
+ translation = loaded.find((t) => /** @type {Record<string, ?>} */ (t).locale() === locale)
550
695
  }
551
696
  }
552
697
 
@@ -554,9 +699,7 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
554
699
  translation = instanceRelationship.build({locale})
555
700
  }
556
701
 
557
- /**
558
- * Assignments.
559
- @type {Record<string, ?>} */
702
+ /** @type {Record<string, ?>} */
560
703
  const assignments = {}
561
704
 
562
705
  assignments[name] = value
@@ -569,7 +712,15 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
569
712
  * @returns {Promise<void>} - No return value.
570
713
  */
571
714
  async destroy(model) {
572
- await model.destroy()
715
+ await this.runMutationTransaction({
716
+ action: "destroy",
717
+ model,
718
+ callback: async () => {
719
+ await this.beforeDestroy(model)
720
+ await model.destroy()
721
+ await this.afterDestroy(model)
722
+ }
723
+ })
573
724
  }
574
725
 
575
726
  /**
@@ -589,10 +740,10 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
589
740
  * @param {object} args - Nested relationship inputs.
590
741
  * @param {import("../database/record/index.js").default} args.parent - Parent model instance.
591
742
  * @param {string} args.relationshipName - Relationship receiving nested attributes.
592
- * @param {?} args.rawEntries - Raw nested entries from the request payload.
743
+ * @param {FrontendModelResourcePayloadValue} args.rawEntries - Raw nested entries from the request payload.
593
744
  * @param {{attributes: string[], nested: Record<string, ?>}} args.childPermit - Parsed child permit.
594
- * @param {?} args.controller - Controller instance for child resource lookup.
595
- * @returns {{ability: import("../authorization/ability.js").default | undefined, childResource: FrontendModelBaseResource, childResourceConfig: FrontendModelResolvedResourceConfiguration, childWritableAttributes: string[], destroyPermitted: boolean, entries: Array<Record<string, ?>>, relationship: import("../database/record/relationships/base.js").default, targetModelClass: typeof import("../database/record/index.js").default}} Nested relationship context.
745
+ * @param {FrontendModelResourceController | null | undefined} args.controller - Controller instance for child resource lookup.
746
+ * @returns {{ability: import("../authorization/ability.js").default | undefined, childResource: FrontendModelBaseResource, childResourceConfig: FrontendModelResolvedResourceConfiguration, childWritableAttributes: string[], destroyPermitted: boolean, entries: Array<FrontendModelResourceNestedEntry>, relationship: import("../database/record/relationships/base.js").default, targetModelClass: typeof import("../database/record/index.js").default}} Nested relationship context.
596
747
  */
597
748
  _nestedRelationshipContext({parent, relationshipName, rawEntries, childPermit, controller}) {
598
749
  const parentModelClass = parent.getModelClass()
@@ -631,7 +782,7 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
631
782
 
632
783
  const childResource = new childResourceConfig.resourceClass({
633
784
  ability: this.ability,
634
- controller,
785
+ controller: controller || undefined,
635
786
  context: this.context || {},
636
787
  locals: this.locals || {},
637
788
  modelClass: targetModelClass,
@@ -663,10 +814,10 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
663
814
  /**
664
815
  * Normalizes nested entries for collection and singular relationships.
665
816
  * @param {object} args - Nested entries inputs.
666
- * @param {?} args.rawEntries - Raw nested entries value.
817
+ * @param {FrontendModelResourcePayloadValue} args.rawEntries - Raw nested entries value.
667
818
  * @param {string} args.relationshipName - Relationship name.
668
819
  * @param {string} args.relationshipType - Relationship type.
669
- * @returns {Array<Record<string, ?>>} Normalized nested entry objects.
820
+ * @returns {Array<FrontendModelResourceNestedEntry>} Normalized nested entry objects.
670
821
  */
671
822
  _nestedRelationshipEntries({rawEntries, relationshipName, relationshipType}) {
672
823
  if (relationshipType === "hasMany") {
@@ -677,7 +828,8 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
677
828
  return rawEntries.map((entry) => {
678
829
  if (!isPlainObject(entry)) throw new Error(`nestedAttributes['${relationshipName}'] entries must be objects.`)
679
830
 
680
- return entry
831
+ // Narrows the plain-object payload to a normalized nested-entry object.
832
+ return /** @type {FrontendModelResourceNestedEntry} */ (entry)
681
833
  })
682
834
  }
683
835
 
@@ -686,14 +838,16 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
686
838
  return rawEntries.map((entry) => {
687
839
  if (!isPlainObject(entry)) throw new Error(`nestedAttributes['${relationshipName}'] entries must be objects.`)
688
840
 
689
- return entry
841
+ // Narrows the plain-object payload to a normalized nested-entry object.
842
+ return /** @type {FrontendModelResourceNestedEntry} */ (entry)
690
843
  })
691
844
  }
692
845
  if (!isPlainObject(rawEntries)) {
693
846
  throw new Error(`Expected object for nestedAttributes['${relationshipName}'] but got: ${typeof rawEntries}`)
694
847
  }
695
848
 
696
- return [rawEntries]
849
+ // Narrows the plain-object payload to a normalized nested-entry object.
850
+ return [/** @type {FrontendModelResourceNestedEntry} */ (rawEntries)]
697
851
  }
698
852
 
699
853
  /**
@@ -702,25 +856,38 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
702
856
  * fields (`{name, file, commentsAttributes}`).
703
857
  * @param {object} args - Normalization inputs.
704
858
  * @param {{attributes: string[], nested: Record<string, ?>}} args.childPermit - Parsed child permit spec.
705
- * @param {Record<string, ?>} args.entry - Raw nested entry.
859
+ * @param {FrontendModelResourceNestedEntry} args.entry - Raw nested entry.
706
860
  * @param {string} args.relationshipName - Relationship name for error messages.
707
861
  * @param {typeof import("../database/record/index.js").default} args.targetModelClass - Child model class.
708
- * @returns {Record<string, ?>} Normalized nested entry.
862
+ * @returns {FrontendModelResourceNestedEntry} Normalized nested entry.
709
863
  */
710
864
  _normalizeNestedRelationshipEntry({childPermit, entry, relationshipName, targetModelClass}) {
711
- /** @type {Record<string, ?>} */
865
+ /** @type {FrontendModelResourceAttributePayload} */
712
866
  const attributes = {}
713
- /** @type {Record<string, ?>} */
867
+ /** @type {FrontendModelResourceAttributePayload} */
714
868
  const attachments = {}
715
- /** @type {Record<string, ?>} */
869
+ /** @type {FrontendModelResourceAttributePayload} */
716
870
  const nestedAttributes = {}
717
- /** @type {Record<string, ?>} */
871
+ /** @type {FrontendModelResourceNestedEntry} */
718
872
  const normalized = {}
719
873
  const attachmentDefinitions = targetModelClass.getAttachmentsMap?.() || {}
720
874
 
721
875
  for (const [attributeName, value] of Object.entries(entry)) {
722
- if (attributeName === "id" || attributeName === "_destroy") {
723
- normalized[attributeName] = value
876
+ if (attributeName === "id") {
877
+ if (typeof value !== "string" && typeof value !== "number") {
878
+ throw new Error(`nestedAttributes['${relationshipName}'] entry id must be a string or number.`)
879
+ }
880
+
881
+ normalized.id = value
882
+ continue
883
+ }
884
+
885
+ if (attributeName === "_destroy") {
886
+ if (typeof value !== "boolean") {
887
+ throw new Error(`nestedAttributes['${relationshipName}'] entry _destroy must be a boolean.`)
888
+ }
889
+
890
+ normalized._destroy = value
724
891
  continue
725
892
  }
726
893
 
@@ -771,8 +938,8 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
771
938
  /**
772
939
  * Applies belongs-to nested attributes before the parent save so the parent FK can be set.
773
940
  * @param {import("../database/record/index.js").default} parent - Parent model instance.
774
- * @param {Record<string, ?>} nestedAttributes - Nested-attribute payload keyed by relationship name.
775
- * @param {?} controller - Controller instance for resource resolution and authorization.
941
+ * @param {FrontendModelResourceAttributePayload} nestedAttributes - Nested-attribute payload keyed by relationship name.
942
+ * @param {FrontendModelResourceController | null | undefined} controller - Controller instance for resource resolution and authorization.
776
943
  * @param {{attributes: string[], nested: Record<string, ?>} | null} [parentPermit] - Parsed parent permit spec.
777
944
  * @returns {Promise<void>}
778
945
  */
@@ -802,13 +969,15 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
802
969
  if (!context.destroyPermitted) {
803
970
  throw new Error(`nestedAttributes['${relationshipName}'] entry requested _destroy but "_destroy" is not in the permit for this relationship.`)
804
971
  }
805
- if (!entry.id) throw new Error(`nestedAttributes['${relationshipName}'] _destroy entry is missing an id.`)
972
+ const id = entry.id
973
+
974
+ if (id == undefined) throw new Error(`nestedAttributes['${relationshipName}'] _destroy entry is missing an id.`)
806
975
 
807
976
  const existing = await this._findNestedRecord({
808
977
  ability: context.ability,
809
978
  action: "destroy",
810
979
  childResourceConfiguration: context.childResourceConfig.resourceConfiguration,
811
- id: entry.id,
980
+ id,
812
981
  relationshipName,
813
982
  targetModelClass: context.targetModelClass
814
983
  })
@@ -818,12 +987,13 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
818
987
  continue
819
988
  }
820
989
 
821
- const child = entry.id
990
+ const id = entry.id
991
+ const child = id != undefined
822
992
  ? await this._findNestedRecord({
823
993
  ability: context.ability,
824
994
  action: "update",
825
995
  childResourceConfiguration: context.childResourceConfig.resourceConfiguration,
826
- id: entry.id,
996
+ id,
827
997
  relationshipName,
828
998
  targetModelClass: context.targetModelClass
829
999
  })
@@ -837,7 +1007,7 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
837
1007
  await context.childResource._applyBelongsToNestedAttributes(child, entry.nestedAttributes || {}, controller, childPermit)
838
1008
  await child.save()
839
1009
 
840
- if (!entry.id) {
1010
+ if (id == undefined) {
841
1011
  await this._authorizeCreatedChild({
842
1012
  ability: context.ability,
843
1013
  child,
@@ -869,8 +1039,8 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
869
1039
  * (allowDestroy, limit, rejectIf) come from the MODEL's
870
1040
  * `acceptedNestedAttributesFor(name)` declaration.
871
1041
  * @param {import("../database/record/index.js").default} parent - Parent model instance.
872
- * @param {Record<string, ?>} nestedAttributes - Nested-attribute payload keyed by relationship name.
873
- * @param {?} controller - Controller instance for resource resolution and authorization.
1042
+ * @param {FrontendModelResourceAttributePayload} nestedAttributes - Nested-attribute payload keyed by relationship name.
1043
+ * @param {FrontendModelResourceController | null | undefined} controller - Controller instance for resource resolution and authorization.
874
1044
  * @param {{attributes: string[], nested: Record<string, ?>} | null} [parentPermit] - Parsed parent permit spec.
875
1045
  * @returns {Promise<void>}
876
1046
  */
@@ -922,11 +1092,17 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
922
1092
  }
923
1093
 
924
1094
  for (const entry of destroyEntries) {
1095
+ const id = entry.id
1096
+
1097
+ if (id == undefined) {
1098
+ throw new Error(`nestedAttributes['${relationshipName}'] _destroy entry is missing an id.`)
1099
+ }
1100
+
925
1101
  const existing = await this._findScopedChild({
926
1102
  ability: context.ability,
927
1103
  action: "destroy",
928
1104
  childResourceConfiguration: context.childResourceConfig.resourceConfiguration,
929
- id: entry.id,
1105
+ id,
930
1106
  parent,
931
1107
  parentLinkAttributes,
932
1108
  relationshipName,
@@ -937,11 +1113,17 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
937
1113
  }
938
1114
 
939
1115
  for (const entry of updateEntries) {
1116
+ const id = entry.id
1117
+
1118
+ if (id == undefined) {
1119
+ throw new Error(`nestedAttributes['${relationshipName}'] update entry is missing an id.`)
1120
+ }
1121
+
940
1122
  const existing = await this._findScopedChild({
941
1123
  ability: context.ability,
942
1124
  action: "update",
943
1125
  childResourceConfiguration: context.childResourceConfig.resourceConfiguration,
944
- id: entry.id,
1126
+ id,
945
1127
  parent,
946
1128
  parentLinkAttributes,
947
1129
  relationshipName,
@@ -1098,9 +1280,7 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
1098
1280
  throw new Error(`Nested child resource must define an 'abilities' object to authorize nested ${action}.`)
1099
1281
  }
1100
1282
 
1101
- const abilityAction = /**
1102
- * Narrows the runtime value to the documented type.
1103
- @type {Record<string, string>} */ (abilities)[action]
1283
+ const abilityAction = /** @type {Record<string, string>} */ (abilities)[action]
1104
1284
 
1105
1285
  if (typeof abilityAction !== "string" || abilityAction.length < 1) {
1106
1286
  throw new Error(`Nested child resource must define abilities.${action}.`)
@@ -1130,12 +1310,8 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
1130
1310
  const primaryKey = targetModelClass.primaryKey()
1131
1311
  const lookup = {[primaryKey]: id, ...parentLinkAttributes}
1132
1312
  const query = ability
1133
- ? /**
1134
- * Narrows the runtime value to the documented type.
1135
- @type {?} */ (targetModelClass).accessibleFor(this._resolveChildAbilityAction(childResourceConfiguration, action), ability)
1136
- : /**
1137
- * Narrows the runtime value to the documented type.
1138
- @type {?} */ (targetModelClass).where({})
1313
+ ? targetModelClass.accessibleFor(this._resolveChildAbilityAction(childResourceConfiguration, action), ability)
1314
+ : targetModelClass.where({})
1139
1315
 
1140
1316
  const existing = await query.findBy(lookup)
1141
1317
 
@@ -1163,9 +1339,7 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
1163
1339
 
1164
1340
  const abilityAction = this._resolveChildAbilityAction(childResourceConfiguration, "create")
1165
1341
  const primaryKey = targetModelClass.primaryKey()
1166
- const authorizedIds = await /**
1167
- * Narrows the runtime value to the documented type.
1168
- @type {?} */ (targetModelClass)
1342
+ const authorizedIds = await targetModelClass
1169
1343
  .accessibleFor(abilityAction, ability)
1170
1344
  .where({[primaryKey]: child.readAttribute(primaryKey)})
1171
1345
  .pluck(primaryKey)
@@ -1189,12 +1363,8 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
1189
1363
  if (relationshipNames.length === 0) return
1190
1364
 
1191
1365
  for (const relationshipName of relationshipNames) {
1192
- if (typeof /**
1193
- * Narrows the runtime value to the documented type.
1194
- @type {?} */ (model).loadRelationship === "function") {
1195
- await /**
1196
- * Narrows the runtime value to the documented type.
1197
- @type {?} */ (model).loadRelationship(relationshipName)
1366
+ if (typeof model.loadRelationship === "function") {
1367
+ await model.loadRelationship(relationshipName)
1198
1368
  }
1199
1369
  }
1200
1370
  }
@@ -1220,13 +1390,9 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
1220
1390
  * @returns {{attributes: string[], nested: Record<string, {attributes: string[], nested: Record<string, ?>}>}} - Parsed structure.
1221
1391
  */
1222
1392
  function parsePermittedParams(permitSpec) {
1223
- /**
1224
- * Attributes.
1225
- @type {string[]} */
1393
+ /** @type {string[]} */
1226
1394
  const attributes = []
1227
- /**
1228
- * Nested.
1229
- @type {Record<string, {attributes: string[], nested: Record<string, ?>}>} */
1395
+ /** @type {Record<string, {attributes: string[], nested: Record<string, ?>}>} */
1230
1396
  const nested = {}
1231
1397
 
1232
1398
  if (!Array.isArray(permitSpec)) return {attributes, nested}
@@ -1258,6 +1424,15 @@ function parsePermittedParams(permitSpec) {
1258
1424
  return {attributes, nested}
1259
1425
  }
1260
1426
 
1427
+ /**
1428
+ * Narrows a resource to its dynamic virtual setter lookup surface.
1429
+ * @param {FrontendModelBaseResource} resource - Resource instance.
1430
+ * @returns {Record<string, FrontendModelResourceVirtualSetter>} Dynamic virtual setter lookup.
1431
+ */
1432
+ function virtualSetterLookup(resource) {
1433
+ return /** @type {Record<string, FrontendModelResourceVirtualSetter>} */ (/** @type {unknown} */ (resource))
1434
+ }
1435
+
1261
1436
  /**
1262
1437
  * Runs filter writable frontend model attributes.
1263
1438
  * @param {Record<string, ?>} receiver - Model instance or prototype.
@@ -1266,28 +1441,32 @@ function parsePermittedParams(permitSpec) {
1266
1441
  * @param {string[] | null} [permittedAttributeNames] - Optional explicit permit list. `null` falls back to setter-existence checks only.
1267
1442
  * @returns {Record<string, ?>} - Writable attributes only.
1268
1443
  */
1269
- function filterWritableFrontendModelAttributes(receiver, attributes, resource = /**
1270
- * Narrows the runtime value to the documented type.
1271
- @type {FrontendModelBaseResource | null} */ (null), permittedAttributeNames = null) {
1444
+ function filterWritableFrontendModelAttributes(
1445
+ receiver,
1446
+ attributes,
1447
+ resource = /** @type {FrontendModelBaseResource | null} */ (null),
1448
+ permittedAttributeNames = null
1449
+ ) {
1272
1450
  // Frontend-model writes should fail fast when callers submit read-only or unknown attrs.
1273
1451
  // Silent drops hide contract mistakes in generated models and app-side wrapper code.
1274
- /**
1275
- * Writable attributes.
1276
- @type {Record<string, ?>} */
1452
+ /** @type {Record<string, ?>} */
1277
1453
  const writableAttributes = {}
1278
- /**
1279
- * Invalid attributes.
1280
- @type {string[]} */
1454
+ /** @type {string[]} */
1281
1455
  const invalidAttributes = []
1282
- /**
1283
- * Not permitted attributes.
1284
- @type {string[]} */
1456
+ /** @type {string[]} */
1285
1457
  const notPermittedAttributes = []
1286
1458
 
1287
1459
  const permitSet = Array.isArray(permittedAttributeNames) ? new Set(permittedAttributeNames) : null
1288
- const translatedSet = resource ? new Set(/**
1289
- * Narrows the runtime value to the documented type.
1290
- @type {typeof FrontendModelBaseResource} */ (resource.constructor).translatedAttributes || []) : new Set()
1460
+ /** @type {string[]} */
1461
+ let translatedAttributes = []
1462
+
1463
+ if (resource) {
1464
+ const ResourceClass = /** @type {typeof FrontendModelBaseResource} */ (resource.constructor)
1465
+
1466
+ translatedAttributes = ResourceClass.translatedAttributes || []
1467
+ }
1468
+
1469
+ const translatedSet = new Set(translatedAttributes)
1291
1470
 
1292
1471
  for (const [attributeName, value] of Object.entries(attributes)) {
1293
1472
  if (permitSet && !permitSet.has(attributeName)) {
@@ -1295,19 +1474,16 @@ function filterWritableFrontendModelAttributes(receiver, attributes, resource =
1295
1474
  continue
1296
1475
  }
1297
1476
 
1298
- const modelClass = /** Narrows the runtime value to the documented type. @type {?} */ (receiver).constructor
1477
+ const modelClass = /** @type {WritableAttributeReceiverClass} */ (receiver.constructor)
1299
1478
  const resolvedAttributeName = (modelClass && typeof modelClass.resolveAttributeName === "function" && modelClass.resolveAttributeName(attributeName)) || attributeName
1300
1479
  const requestedSetterName = `set${inflection.camelize(resolvedAttributeName)}`
1301
1480
  const setterName = (modelClass && typeof modelClass.findMemberNameInsensitive === "function" && modelClass.findMemberNameInsensitive(receiver, requestedSetterName)) || requestedSetterName
1302
1481
  const resourceSetterName = `set${inflection.camelize(attributeName)}Attribute`
1482
+ const resourceSetter = resource ? virtualSetterLookup(resource)[resourceSetterName] : undefined
1303
1483
 
1304
1484
  if (setterName in receiver) {
1305
1485
  writableAttributes[attributeName] = value
1306
- } else if (resource && typeof /**
1307
- * Narrows the runtime value to the documented type.
1308
- @type {Record<string, ?>} */ (/**
1309
- * Narrows the runtime value to the documented type.
1310
- @type {?} */ (resource))[resourceSetterName] === "function") {
1486
+ } else if (typeof resourceSetter === "function") {
1311
1487
  writableAttributes[attributeName] = value
1312
1488
  } else if (translatedSet.has(attributeName)) {
1313
1489
  writableAttributes[attributeName] = value