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
@@ -16,10 +16,32 @@ import {readPayloadAssociationCount, readPayloadComputedAbility, readPayloadQuer
16
16
 
17
17
  /**
18
18
  * FrontendModelCommandType type.
19
- @typedef {"create" | "find" | "index" | "update" | "destroy" | "attach" | "download" | "url"} FrontendModelCommandType */
19
+ * @typedef {"create" | "find" | "index" | "update" | "destroy" | "attach" | "download" | "url"} FrontendModelCommandType */
20
20
  /**
21
21
  * FrontendModelRequestCommandType type.
22
- @typedef {FrontendModelCommandType | string} FrontendModelRequestCommandType */
22
+ * @typedef {FrontendModelCommandType | string} FrontendModelRequestCommandType */
23
+ /**
24
+ * Model-like instance value supported by frontend-model transport.
25
+ * @typedef {{attributes: () => Record<string, unknown>}} FrontendModelTransportModelValue
26
+ */
27
+ /**
28
+ * Special scalar values restored by frontend-model transport.
29
+ * @typedef {undefined | null | boolean | number | string | bigint | Date | FrontendModelTransportModelValue} FrontendModelTransportScalarValue
30
+ */
31
+ /**
32
+ * Plain object supported by frontend-model transport values.
33
+ * Nested values are intentionally opaque because TypeScript rejects recursive
34
+ * JSDoc typedefs for this transport value contract.
35
+ * @typedef {Record<string, unknown>} FrontendModelTransportObject
36
+ */
37
+ /**
38
+ * Value supported by frontend-model transport serialization and deserialization.
39
+ * @typedef {FrontendModelTransportScalarValue | FrontendModelTransportObject | Array<unknown>} FrontendModelTransportValue
40
+ */
41
+ /**
42
+ * Frontend model attribute value used when generated metadata cannot infer a narrower type.
43
+ * @typedef {FrontendModelTransportValue} FrontendModelAttributeValue
44
+ */
23
45
  /**
24
46
  * Defines this typedef.
25
47
  * @typedef {{type: "hasOne" | "hasMany"}} FrontendModelAttachmentDefinition
@@ -34,11 +56,27 @@ import {readPayloadAssociationCount, readPayloadComputedAbility, readPayloadQuer
34
56
  */
35
57
  /**
36
58
  * Frontend model constructor type.
37
- * @typedef {{new (attributes?: Record<string, ?>): FrontendModelBase}} FrontendModelConstructor
59
+ * @template {FrontendModelBase} [T=FrontendModelBase]
60
+ * @typedef {{new (attributes?: Record<string, FrontendModelAttributeValue>): T}} FrontendModelConstructor
38
61
  */
39
62
  /**
40
- * Frontend model static side without generated per-model create overloads.
41
- * @typedef {FrontendModelConstructor & Omit<typeof FrontendModelBase, "create">} FrontendModelClass
63
+ * Frontend model static side.
64
+ * @template {FrontendModelBase} [T=FrontendModelBase]
65
+ * @template {Record<string, FrontendModelAttributeValue>} [Attributes=Record<string, FrontendModelAttributeValue>]
66
+ * @template {Record<string, FrontendModelAttributeValue>} [CreateAttributes=Record<string, FrontendModelAttributeValue>]
67
+ * @typedef {{new (): T, create(attributes?: CreateAttributes): Promise<T>} & Omit<typeof FrontendModelBase, "create">} FrontendModelClass
68
+ */
69
+ /**
70
+ * Create attributes accepted by a frontend model instance.
71
+ * @template {FrontendModelBase} T
72
+ * @typedef {T extends FrontendModelBase<Record<string, FrontendModelAttributeValue>, infer CreateAttributes, infer _UpdateAttributes> ? CreateAttributes : Record<string, FrontendModelAttributeValue>} FrontendModelCreateAttributesFor
73
+ */
74
+ /**
75
+ * Loaded instance type for relationship helper generics. Older generated
76
+ * frontend models passed model classes into relationship helpers, while newer
77
+ * generated models pass instance types.
78
+ * @template {FrontendModelBase | typeof FrontendModelBase} T
79
+ * @typedef {T extends new (...args: any[]) => infer Instance ? Instance : T} FrontendModelRelationshipModel
42
80
  */
43
81
  /**
44
82
  * FrontendModelTransportConfig type.
@@ -59,7 +97,7 @@ import {readPayloadAssociationCount, readPayloadComputedAbility, readPayloadQuer
59
97
 
60
98
  /**
61
99
  * Frontend model transport config.
62
- @type {FrontendModelTransportConfig} */
100
+ * @type {FrontendModelTransportConfig} */
63
101
  const frontendModelTransportConfig = {}
64
102
  const SHARED_FRONTEND_MODEL_API_PATH = "/frontend-models"
65
103
  const PRELOADED_RELATIONSHIPS_KEY = "__preloadedRelationships"
@@ -69,19 +107,19 @@ const QUERY_DATA_KEY = "__queryData"
69
107
  const ABILITIES_KEY = "__abilities"
70
108
  /**
71
109
  * Pending shared frontend model requests.
72
- @type {Array<{commandName?: string, commandType: FrontendModelRequestCommandType, customPath?: string, modelClass: FrontendModelClass, payload: Record<string, ?>, requestId: string, resolve: (response: Record<string, ?>) => void, reject: (error: ?) => void, resourcePath?: string | null}>} */
110
+ * @type {Array<{commandName?: string, commandType: FrontendModelRequestCommandType, customPath?: string, modelClass: FrontendModelClass, payload: Record<string, ?>, requestId: string, resolve: (response: Record<string, ?>) => void, reject: (error: ?) => void, resourcePath?: string | null}>} */
73
111
  let pendingSharedFrontendModelRequests = []
74
112
  let sharedFrontendModelRequestId = 0
75
113
  let sharedFrontendModelFlushScheduled = false
76
114
  let activeFrontendModelTransportRequestCount = 0
77
115
  /**
78
116
  * Frontend model idle resolvers.
79
- @type {Array<() => void>} */
117
+ * @type {Array<() => void>} */
80
118
  let frontendModelIdleResolvers = []
81
119
 
82
120
  /**
83
121
  * Internal websocket client.
84
- @type {VelociousWebsocketClient | null} */
122
+ * @type {VelociousWebsocketClient | null} */
85
123
  let internalWebsocketClient = null
86
124
 
87
125
  /**
@@ -96,7 +134,7 @@ function frontendModelTransportIsIdle() {
96
134
 
97
135
  /**
98
136
  * Runs resolve frontend model idle waiters.
99
- @returns {void} */
137
+ * @returns {void} */
100
138
  function resolveFrontendModelIdleWaiters() {
101
139
  if (!frontendModelTransportIsIdle()) return
102
140
 
@@ -189,7 +227,7 @@ function resolveInternalWebsocketClient() {
189
227
 
190
228
  /**
191
229
  * Runs flush buffered outgoing events after reconnect.
192
- @returns {Promise<void>} */
230
+ * @returns {Promise<void>} */
193
231
  async function flushBufferedOutgoingEventsAfterReconnect() {
194
232
  if (!internalWebsocketClient) return
195
233
 
@@ -238,27 +276,29 @@ export class AttributeNotSelectedError extends Error {
238
276
 
239
277
  /**
240
278
  * Lightweight singular relationship state holder for frontend model instances.
241
- * @template {FrontendModelClass} S
242
- * @template {FrontendModelClass} T
279
+ * @template {FrontendModelBase | typeof FrontendModelBase} S
280
+ * @template {FrontendModelBase | typeof FrontendModelBase} T
281
+ * @template {Record<string, FrontendModelAttributeValue>} [TargetCreateAttributes=Record<string, FrontendModelAttributeValue>]
243
282
  */
244
283
  export class FrontendModelSingularRelationship {
245
284
  /**
246
285
  * Runs constructor.
247
- * @param {InstanceType<S>} model - Parent model.
286
+ * @param {FrontendModelBase} model - Parent model.
248
287
  * @param {string} relationshipName - Relationship name.
249
- * @param {T | null} targetModelClass - Target model class.
288
+ * @param {FrontendModelClass<FrontendModelRelationshipModel<T>, Record<string, FrontendModelAttributeValue>, TargetCreateAttributes> | null} targetModelClass - Target model class.
250
289
  */
251
290
  constructor(model, relationshipName, targetModelClass) {
252
291
  this.model = model
253
292
  this.relationshipName = relationshipName
254
293
  this.targetModelClass = targetModelClass
255
294
  this._preloaded = false
295
+ /** @type {FrontendModelRelationshipModel<T> | null} */
256
296
  this._loadedValue = null
257
297
  }
258
298
 
259
299
  /**
260
300
  * Runs set loaded.
261
- * @param {?} loadedValue - Loaded relationship value.
301
+ * @param {FrontendModelRelationshipModel<T> | null | undefined} loadedValue - Loaded relationship value.
262
302
  * @returns {void}
263
303
  */
264
304
  setLoaded(loadedValue) {
@@ -276,7 +316,7 @@ export class FrontendModelSingularRelationship {
276
316
 
277
317
  /**
278
318
  * Runs loaded.
279
- * @returns {?} - Loaded relationship value.
319
+ * @returns {FrontendModelRelationshipModel<T> | null} - Loaded relationship value.
280
320
  */
281
321
  loaded() {
282
322
  if (!this._preloaded) {
@@ -286,42 +326,91 @@ export class FrontendModelSingularRelationship {
286
326
  return this._loadedValue
287
327
  }
288
328
 
329
+ /**
330
+ * Copies loaded value from another singular relationship helper.
331
+ * @param {FrontendModelRelationship} sourceRelationship - Source relationship helper.
332
+ * @returns {void}
333
+ */
334
+ copyLoadedFrom(sourceRelationship) {
335
+ if (sourceRelationship instanceof FrontendModelHasManyRelationship) {
336
+ throw new Error(`Expected ${this.model.constructor.name}#${this.relationshipName} source relationship to be singular`)
337
+ }
338
+
339
+ // Narrows the runtime value to the target relationship's documented model type.
340
+ const loadedValue = /** @type {FrontendModelRelationshipModel<T> | null} */ (sourceRelationship.loaded())
341
+
342
+ this.setLoaded(loadedValue)
343
+ }
344
+
289
345
  /**
290
346
  * Runs build.
291
- * @param {Record<string, ?>} [attributes] - New model attributes.
292
- * @returns {InstanceType<T>} - Built model.
347
+ * @param {TargetCreateAttributes} [attributes] - New model attributes.
348
+ * @returns {FrontendModelRelationshipModel<T>} - Built model.
293
349
  */
294
- build(attributes = {}) {
350
+ build(attributes = /** @type {TargetCreateAttributes} */ ({})) {
295
351
  if (!this.targetModelClass) {
296
352
  throw new Error(`No target model class configured for ${this.model.constructor.name}#${this.relationshipName}`)
297
353
  }
298
354
 
299
- const model = /**
300
- * Narrows the runtime value to the documented type.
301
- @type {InstanceType<T>} */ (new this.targetModelClass(attributes))
355
+ const ModelClass = /** @type {new (attributes?: TargetCreateAttributes) => FrontendModelRelationshipModel<T>} */ (this.targetModelClass)
356
+ const model = new ModelClass(attributes)
302
357
 
303
358
  this.setLoaded(model)
304
359
 
305
360
  return model
306
361
  }
362
+
363
+ /**
364
+ * Force-reload the relationship.
365
+ * @returns {Promise<FrontendModelRelationshipModel<T> | null>} - Loaded relationship model.
366
+ */
367
+ async load() {
368
+ this._preloaded = false
369
+ this._loadedValue = null
370
+
371
+ const batched = await this.model._tryCohortPreload(this.relationshipName)
372
+
373
+ if (batched) return this.loaded()
374
+
375
+ await this.model.loadRelationship(this.relationshipName)
376
+
377
+ return this.loaded()
378
+ }
379
+
380
+ /**
381
+ * Returns the loaded relationship or loads it.
382
+ * @returns {Promise<FrontendModelRelationshipModel<T> | null>} - Loaded relationship model.
383
+ */
384
+ async orLoad() {
385
+ if (this.getPreloaded()) return this.loaded()
386
+
387
+ const batched = await this.model._tryCohortPreload(this.relationshipName)
388
+
389
+ if (batched) return this.loaded()
390
+
391
+ await this.model.loadRelationship(this.relationshipName)
392
+
393
+ return this.loaded()
394
+ }
307
395
  }
308
396
 
309
397
  /**
310
398
  * Lightweight has-many relationship state holder for frontend model instances.
311
- * @template {FrontendModelClass} S
312
- * @template {FrontendModelClass} T
399
+ * @template {FrontendModelBase | typeof FrontendModelBase} S
400
+ * @template {FrontendModelBase | typeof FrontendModelBase} T
401
+ * @template {Record<string, FrontendModelAttributeValue>} [TargetCreateAttributes=Record<string, FrontendModelAttributeValue>]
313
402
  */
314
403
  export class FrontendModelHasManyRelationship {
315
404
  /**
316
405
  * Narrows the runtime value to the documented type.
317
- @type {Array<InstanceType<T>>} */
406
+ * @type {Array<FrontendModelRelationshipModel<T>>} */
318
407
  _loadedValue
319
408
 
320
409
  /**
321
410
  * Runs constructor.
322
- * @param {InstanceType<S>} model - Parent model.
411
+ * @param {FrontendModelBase} model - Parent model.
323
412
  * @param {string} relationshipName - Relationship name.
324
- * @param {T | null} targetModelClass - Target model class.
413
+ * @param {FrontendModelClass<FrontendModelRelationshipModel<T>, Record<string, FrontendModelAttributeValue>, TargetCreateAttributes> | null} targetModelClass - Target model class.
325
414
  */
326
415
  constructor(model, relationshipName, targetModelClass) {
327
416
  this.model = model
@@ -333,11 +422,15 @@ export class FrontendModelHasManyRelationship {
333
422
 
334
423
  /**
335
424
  * Runs set loaded.
336
- * @param {Array<InstanceType<T>>} loadedValue - Loaded relationship value.
425
+ * @param {Array<FrontendModelRelationshipModel<T>>} loadedValue - Loaded relationship value.
337
426
  * @returns {void}
338
427
  */
339
428
  setLoaded(loadedValue) {
340
- this._loadedValue = Array.isArray(loadedValue) ? loadedValue : []
429
+ if (!Array.isArray(loadedValue)) {
430
+ throw new Error(`Expected ${this.model.constructor.name}#${this.relationshipName} to be loaded with an array`)
431
+ }
432
+
433
+ this._loadedValue = loadedValue
341
434
  this._preloaded = true
342
435
  }
343
436
 
@@ -351,7 +444,7 @@ export class FrontendModelHasManyRelationship {
351
444
 
352
445
  /**
353
446
  * Runs loaded.
354
- * @returns {Array<InstanceType<T>>} - Loaded relationship values.
447
+ * @returns {Array<FrontendModelRelationshipModel<T>>} - Loaded relationship values.
355
448
  */
356
449
  loaded() {
357
450
  if (!this._preloaded) {
@@ -361,9 +454,25 @@ export class FrontendModelHasManyRelationship {
361
454
  return this._loadedValue
362
455
  }
363
456
 
457
+ /**
458
+ * Copies loaded value from another has-many relationship helper.
459
+ * @param {FrontendModelRelationship} sourceRelationship - Source relationship helper.
460
+ * @returns {void}
461
+ */
462
+ copyLoadedFrom(sourceRelationship) {
463
+ if (!(sourceRelationship instanceof FrontendModelHasManyRelationship)) {
464
+ throw new Error(`Expected ${this.model.constructor.name}#${this.relationshipName} source relationship to be has-many`)
465
+ }
466
+
467
+ // Narrows the runtime value to the target relationship's documented model type.
468
+ const loadedValue = /** @type {Array<FrontendModelRelationshipModel<T>>} */ (sourceRelationship.loaded())
469
+
470
+ this.setLoaded(loadedValue)
471
+ }
472
+
364
473
  /**
365
474
  * Runs add to loaded.
366
- * @param {Array<InstanceType<T>>} models - Models to append.
475
+ * @param {Array<FrontendModelRelationshipModel<T>>} models - Models to append.
367
476
  * @returns {void}
368
477
  */
369
478
  addToLoaded(models) {
@@ -374,17 +483,16 @@ export class FrontendModelHasManyRelationship {
374
483
 
375
484
  /**
376
485
  * Runs build.
377
- * @param {Record<string, ?>} [attributes] - New model attributes.
378
- * @returns {InstanceType<T>} - Built model.
486
+ * @param {TargetCreateAttributes} [attributes] - New model attributes.
487
+ * @returns {FrontendModelRelationshipModel<T>} - Built model.
379
488
  */
380
- build(attributes = {}) {
489
+ build(attributes = /** @type {TargetCreateAttributes} */ ({})) {
381
490
  if (!this.targetModelClass) {
382
491
  throw new Error(`No target model class configured for ${this.model.constructor.name}#${this.relationshipName}`)
383
492
  }
384
493
 
385
- const model = /**
386
- * Narrows the runtime value to the documented type.
387
- @type {InstanceType<T>} */ (new this.targetModelClass(attributes))
494
+ const ModelClass = /** @type {new (attributes?: TargetCreateAttributes) => FrontendModelRelationshipModel<T>} */ (this.targetModelClass)
495
+ const model = new ModelClass(attributes)
388
496
 
389
497
  this.addToLoaded([model])
390
498
 
@@ -397,25 +505,25 @@ export class FrontendModelHasManyRelationship {
397
505
  * batched into one request via the cohort preloader. The scoped query path
398
506
  * (`Model.where(...).preload([name]).toArray()` directly from user code)
399
507
  * bypasses cohort batching by design.
400
- * @returns {Promise<Array<InstanceType<T>>>} - Loaded relationship models.
508
+ * @returns {Promise<Array<FrontendModelRelationshipModel<T>>>} - Loaded relationship models.
401
509
  */
402
510
  async load() {
403
511
  // Reset so the cohort preloader (or single-record fallback) repopulates.
404
512
  this._preloaded = false
405
513
  this._loadedValue = []
406
514
 
407
- const batched = await /**
408
- * Narrows the runtime value to the documented type.
409
- @type {?} */ (this.model)._tryCohortPreload(this.relationshipName)
515
+ const batched = await this.model._tryCohortPreload(this.relationshipName)
410
516
 
411
517
  if (batched) return this._loadedValue
412
518
 
413
- return /** Narrows the runtime value to the documented type. @type {Promise<Array<InstanceType<T>>>} */ (this.model.loadRelationship(this.relationshipName))
519
+ await this.model.loadRelationship(this.relationshipName)
520
+
521
+ return this.loaded()
414
522
  }
415
523
 
416
524
  /**
417
525
  * Runs to array.
418
- * @returns {Promise<Array<InstanceType<T>>>} - Loaded relationship models.
526
+ * @returns {Promise<Array<FrontendModelRelationshipModel<T>>>} - Loaded relationship models.
419
527
  */
420
528
  async toArray() {
421
529
  if (this.getPreloaded() || this._loadedValue.length > 0) {
@@ -426,6 +534,22 @@ export class FrontendModelHasManyRelationship {
426
534
  }
427
535
  }
428
536
 
537
+ /**
538
+ * Frontend model relationship helper type.
539
+ * @typedef {FrontendModelHasManyRelationship<FrontendModelBase, FrontendModelBase, Record<string, FrontendModelAttributeValue>> | FrontendModelSingularRelationship<FrontendModelBase, FrontendModelBase, Record<string, FrontendModelAttributeValue>>} FrontendModelRelationship
540
+ */
541
+
542
+ /**
543
+ * Copies loaded relationship state between helpers of the same relationship shape.
544
+ * @param {object} args - Arguments.
545
+ * @param {FrontendModelRelationship} args.sourceRelationship - Source relationship helper.
546
+ * @param {FrontendModelRelationship} args.targetRelationship - Target relationship helper.
547
+ * @returns {void}
548
+ */
549
+ function copyLoadedRelationshipValue({sourceRelationship, targetRelationship}) {
550
+ targetRelationship.copyLoadedFrom(sourceRelationship)
551
+ }
552
+
429
553
  /**
430
554
  * Runs relationship type is collection.
431
555
  * @param {string} relationshipType - Relationship type.
@@ -499,7 +623,7 @@ export class FrontendModelAttachmentDownload {
499
623
  function frontendModelAttachmentCommandPayload(attachment, attachmentId) {
500
624
  /**
501
625
  * Payload.
502
- @type {Record<string, ?>} */
626
+ * @type {Record<string, ?>} */
503
627
  const payload = {
504
628
  attachmentName: attachment.attachmentName,
505
629
  id: attachment.model.primaryKeyValue()
@@ -527,7 +651,7 @@ function frontendAttachmentValueIsBytes(value) {
527
651
  function frontendAttachmentValueSupportsArrayBuffer(value) {
528
652
  return Boolean(value && typeof value === "object" && typeof /**
529
653
  * Narrows the runtime value to the documented type.
530
- @type {?} */ (value).arrayBuffer === "function")
654
+ * @type {?} */ (value).arrayBuffer === "function")
531
655
  }
532
656
 
533
657
  /**
@@ -540,10 +664,10 @@ function frontendAttachmentNormalizeBytes(value) {
540
664
  if (value instanceof ArrayBuffer) return new Uint8Array(value)
541
665
  if (typeof Buffer !== "undefined" && Buffer.isBuffer(/**
542
666
  * Narrows the runtime value to the documented type.
543
- @type {?} */ (value))) {
667
+ * @type {?} */ (value))) {
544
668
  return new Uint8Array(/**
545
669
  * Narrows the runtime value to the documented type.
546
- @type {Buffer} */ (value))
670
+ * @type {Buffer} */ (value))
547
671
  }
548
672
 
549
673
  throw new Error("Unsupported attachment bytes value")
@@ -676,21 +800,21 @@ async function normalizeFrontendAttachmentInput(input) {
676
800
  contentBase64: frontendAttachmentBytesToBase64(bytes),
677
801
  contentType: typeof /**
678
802
  * Narrows the runtime value to the documented type.
679
- @type {?} */ (input).type === "string" && /**
803
+ * @type {?} */ (input).type === "string" && /**
680
804
  * Narrows the runtime value to the documented type.
681
- @type {?} */ (input).type.length > 0
805
+ * @type {?} */ (input).type.length > 0
682
806
  ? /**
683
807
  * Narrows the runtime value to the documented type.
684
- @type {?} */ (input).type
808
+ * @type {?} */ (input).type
685
809
  : null,
686
810
  filename: typeof /**
687
811
  * Narrows the runtime value to the documented type.
688
- @type {?} */ (input).name === "string" && /**
812
+ * @type {?} */ (input).name === "string" && /**
689
813
  * Narrows the runtime value to the documented type.
690
- @type {?} */ (input).name.length > 0
814
+ * @type {?} */ (input).name.length > 0
691
815
  ? /**
692
816
  * Narrows the runtime value to the documented type.
693
- @type {?} */ (input).name
817
+ * @type {?} */ (input).name
694
818
  : "attachment.bin"
695
819
  }
696
820
  }
@@ -698,7 +822,7 @@ async function normalizeFrontendAttachmentInput(input) {
698
822
  if (frontendAttachmentValueIsBytes(input)) {
699
823
  const bytes = frontendAttachmentNormalizeBytes(/**
700
824
  * Narrows the runtime value to the documented type.
701
- @type {Uint8Array | Buffer | ArrayBuffer} */ (input))
825
+ * @type {Uint8Array | Buffer | ArrayBuffer} */ (input))
702
826
 
703
827
  return {
704
828
  contentBase64: frontendAttachmentBytesToBase64(bytes),
@@ -898,7 +1022,7 @@ function frontendModelTransportUrl() {
898
1022
  * @returns {Record<string, ?>} - Cloned attributes hash.
899
1023
  */
900
1024
  function cloneFrontendModelAttributes(value) {
901
- return /** Narrows the runtime value to the documented type. @type {Record<string, ?>} */ (deserializeFrontendModelTransportValue(serializeFrontendModelTransportValue(value)))
1025
+ return /** @type {Record<string, ?>} */ (deserializeFrontendModelTransportValue(serializeFrontendModelTransportValue(value)))
902
1026
  }
903
1027
 
904
1028
  /**
@@ -943,10 +1067,10 @@ function mergeFrontendModelEventPreload(target, source) {
943
1067
  mergeFrontendModelEventPreload(
944
1068
  /**
945
1069
  * Narrows the runtime value to the documented type.
946
- @type {Record<string, import("./query.js").FrontendModelTransportValue>} */ (target[relationshipName]),
1070
+ * @type {Record<string, import("./query.js").FrontendModelTransportValue>} */ (target[relationshipName]),
947
1071
  /**
948
1072
  * Narrows the runtime value to the documented type.
949
- @type {Record<string, import("./query.js").FrontendModelTransportValue>} */ (value)
1073
+ * @type {Record<string, import("./query.js").FrontendModelTransportValue>} */ (value)
950
1074
  )
951
1075
  }
952
1076
  }
@@ -1038,7 +1162,7 @@ function frontendModelMatchedEventFilterKeys(body) {
1038
1162
 
1039
1163
  const keys = /**
1040
1164
  * Narrows the runtime value to the documented type.
1041
- @type {{matchedEventFilterKeys?: ?}} */ (body).matchedEventFilterKeys
1165
+ * @type {{matchedEventFilterKeys?: ?}} */ (body).matchedEventFilterKeys
1042
1166
 
1043
1167
  if (!Array.isArray(keys)) return new Set()
1044
1168
 
@@ -1091,56 +1215,57 @@ class FrontendModelEventSubscription {
1091
1215
  this.ModelClass = ModelClass
1092
1216
  /**
1093
1217
  * Narrows the runtime value to the documented type.
1094
- @type {Set<FrontendModelModelEventCallbackEntry>} */
1218
+ * @type {Set<FrontendModelModelEventCallbackEntry>} */
1095
1219
  this.classCreateCallbacks = new Set()
1096
1220
  /**
1097
1221
  * Narrows the runtime value to the documented type.
1098
- @type {Set<FrontendModelModelEventCallbackEntry>} */
1222
+ * @type {Set<FrontendModelModelEventCallbackEntry>} */
1099
1223
  this.classUpdateCallbacks = new Set()
1100
1224
  /**
1101
1225
  * Narrows the runtime value to the documented type.
1102
- @type {Set<FrontendModelDestroyEventCallbackEntry>} */
1226
+ * @type {Set<FrontendModelDestroyEventCallbackEntry>} */
1103
1227
  this.classDestroyCallbacks = new Set()
1104
1228
  /**
1105
1229
  * Narrows the runtime value to the documented type.
1106
- @type {Map<string, {instance: FrontendModelBase, updateCallbacks: Set<FrontendModelModelEventCallbackEntry>, destroyCallbacks: Set<FrontendModelDestroyEventCallbackEntry>}>} */
1230
+ * @type {Map<string, {instance: FrontendModelBase, updateCallbacks: Set<FrontendModelModelEventCallbackEntry>, destroyCallbacks: Set<FrontendModelDestroyEventCallbackEntry>}>} */
1107
1231
  this.instanceListeners = new Map()
1108
1232
  /**
1109
1233
  * Narrows the runtime value to the documented type.
1110
- @type {?} */
1234
+ * @type {?} */
1111
1235
  this.channelHandle = null
1112
1236
  /**
1113
1237
  * Narrows the runtime value to the documented type.
1114
- @type {Promise<void> | null} */
1238
+ * @type {Promise<void> | null} */
1115
1239
  this.readyPromise = null
1116
1240
  /**
1117
1241
  * Narrows the runtime value to the documented type.
1118
- @type {string | null} */
1242
+ * @type {string | null} */
1119
1243
  this.subscriptionParamsKey = null
1120
1244
  }
1121
1245
 
1122
1246
  /**
1123
1247
  * Runs subscription params.
1124
- * @returns {{model: string, eventFilters?: import("./query.js").FrontendModelEventFilterPayloadEntry[], unfilteredEventDelivery?: boolean} & import("./query.js").FrontendModelProjectionPayload} - Current websocket subscription params.
1248
+ * @returns {{model: string, destroyEventDelivery?: boolean, eventFilters?: import("./query.js").FrontendModelEventFilterPayloadEntry[], unfilteredEventDelivery?: boolean} & import("./query.js").FrontendModelProjectionPayload} - Current websocket subscription params.
1125
1249
  */
1126
1250
  subscriptionParams() {
1127
1251
  /**
1128
1252
  * Projection payload.
1129
- @type {import("./query.js").FrontendModelProjectionPayload} */
1253
+ * @type {import("./query.js").FrontendModelProjectionPayload} */
1130
1254
  const projectionPayload = {}
1131
1255
  /**
1132
1256
  * Event filters by key.
1133
- @type {Record<string, import("./query.js").FrontendModelEventFilterPayloadEntry>} */
1257
+ * @type {Record<string, import("./query.js").FrontendModelEventFilterPayloadEntry>} */
1134
1258
  const eventFiltersByKey = {}
1135
1259
  const projectionEntries = []
1136
- let hasUnfilteredEventDelivery = this.classDestroyCallbacks.size > 0
1260
+ let hasDestroyEventDelivery = this.classDestroyCallbacks.size > 0
1261
+ let hasUnfilteredEventDelivery = false
1137
1262
 
1138
1263
  for (const entry of this.classCreateCallbacks) projectionEntries.push(entry)
1139
1264
  for (const entry of this.classUpdateCallbacks) projectionEntries.push(entry)
1140
1265
 
1141
1266
  for (const listener of this.instanceListeners.values()) {
1142
1267
  for (const entry of listener.updateCallbacks) projectionEntries.push(entry)
1143
- if (listener.destroyCallbacks.size > 0) hasUnfilteredEventDelivery = true
1268
+ if (listener.destroyCallbacks.size > 0) hasDestroyEventDelivery = true
1144
1269
  }
1145
1270
 
1146
1271
  for (const entry of projectionEntries) {
@@ -1160,6 +1285,7 @@ class FrontendModelEventSubscription {
1160
1285
  const eventFilterParams = eventFilters.length > 0
1161
1286
  ? {
1162
1287
  eventFilters,
1288
+ ...(hasDestroyEventDelivery ? {destroyEventDelivery: true} : {}),
1163
1289
  ...(hasUnfilteredEventDelivery ? {unfilteredEventDelivery: true} : {})
1164
1290
  }
1165
1291
  : {}
@@ -1181,7 +1307,7 @@ class FrontendModelEventSubscription {
1181
1307
 
1182
1308
  /**
1183
1309
  * Runs ensure subscribed.
1184
- @returns {Promise<void>} */
1310
+ * @returns {Promise<void>} */
1185
1311
  async ensureSubscribed() {
1186
1312
  const paramsJson = this.subscriptionParamsJson()
1187
1313
 
@@ -1206,7 +1332,7 @@ class FrontendModelEventSubscription {
1206
1332
 
1207
1333
  const client = /**
1208
1334
  * Narrows the runtime value to the documented type.
1209
- @type {?} */ (frontendModelTransportConfig.websocketClient || resolveInternalWebsocketClient())
1335
+ * @type {?} */ (frontendModelTransportConfig.websocketClient || resolveInternalWebsocketClient())
1210
1336
 
1211
1337
  if (!client || typeof client.subscribeChannel !== "function") {
1212
1338
  throw new Error("Frontend model event subscriptions require configureTransport({websocketUrl}) or configureTransport({websocketClient})")
@@ -1222,7 +1348,7 @@ class FrontendModelEventSubscription {
1222
1348
  params,
1223
1349
  onMessage: (/**
1224
1350
  * Narrows the runtime value to the documented type.
1225
- @type {?} */ body) => this._dispatchEvent(body),
1351
+ * @type {?} */ body) => this._dispatchEvent(body),
1226
1352
  onClose: () => {
1227
1353
  this.channelHandle = null
1228
1354
  this.readyPromise = null
@@ -1279,10 +1405,10 @@ class FrontendModelEventSubscription {
1279
1405
 
1280
1406
  const deserializedRecord = /**
1281
1407
  * Narrows the runtime value to the documented type.
1282
- @type {Record<string, ?>} */ (deserializeFrontendModelTransportValue(body.record))
1408
+ * @type {Record<string, ?>} */ (deserializeFrontendModelTransportValue(body.record))
1283
1409
  const freshModel = /**
1284
1410
  * Narrows the runtime value to the documented type.
1285
- @type {?} */ (this.ModelClass).instantiateFromResponse(deserializedRecord)
1411
+ * @type {?} */ (this.ModelClass).instantiateFromResponse(deserializedRecord)
1286
1412
  const listener = this.instanceListeners.get(id)
1287
1413
 
1288
1414
  if (action === "update" && listener) {
@@ -1295,7 +1421,7 @@ class FrontendModelEventSubscription {
1295
1421
  // through the same handle see fresh attributes.
1296
1422
  const instanceAny = /**
1297
1423
  * Narrows the runtime value to the documented type.
1298
- @type {?} */ (listener.instance)
1424
+ * @type {?} */ (listener.instance)
1299
1425
 
1300
1426
  instanceAny.assignAttributes(freshModel.attributes())
1301
1427
  instanceAny._persistedAttributes = cloneFrontendModelAttributes(listener.instance.attributes())
@@ -1317,7 +1443,7 @@ class FrontendModelEventSubscription {
1317
1443
 
1318
1444
  /**
1319
1445
  * Runs maybe teardown.
1320
- @returns {void} */
1446
+ * @returns {void} */
1321
1447
  maybeTeardown() {
1322
1448
  const hasAnyListener = this.classCreateCallbacks.size > 0
1323
1449
  || this.classUpdateCallbacks.size > 0
@@ -1341,7 +1467,7 @@ class FrontendModelEventSubscription {
1341
1467
 
1342
1468
  /**
1343
1469
  * Frontend model event subscriptions.
1344
- @type {WeakMap<FrontendModelClass, FrontendModelEventSubscription>} */
1470
+ * @type {WeakMap<FrontendModelClass, FrontendModelEventSubscription>} */
1345
1471
  const frontendModelEventSubscriptions = new WeakMap()
1346
1472
 
1347
1473
  /**
@@ -1453,7 +1579,7 @@ async function performSharedFrontendModelApiRequest(requestPayload) {
1453
1579
  })
1454
1580
  const responseJson = response.json()
1455
1581
 
1456
- return /** Narrows the runtime value to the documented type. @type {Record<string, ?>} */ (deserializeFrontendModelTransportValue(responseJson))
1582
+ return /** @type {Record<string, ?>} */ (deserializeFrontendModelTransportValue(responseJson))
1457
1583
  }
1458
1584
 
1459
1585
  const response = await fetch(url, {
@@ -1475,7 +1601,7 @@ async function performSharedFrontendModelApiRequest(requestPayload) {
1475
1601
 
1476
1602
  const json = responseText.length > 0 ? JSON.parse(responseText) : {}
1477
1603
 
1478
- return /** Narrows the runtime value to the documented type. @type {Record<string, ?>} */ (deserializeFrontendModelTransportValue(json))
1604
+ return /** @type {Record<string, ?>} */ (deserializeFrontendModelTransportValue(json))
1479
1605
  }
1480
1606
 
1481
1607
  /**
@@ -1494,7 +1620,7 @@ function throwFrontendModelHttpError({commandLabel, response, responseText}) {
1494
1620
  if (responseContentType && responseContentType.includes("application/json") && responseText.length > 0) {
1495
1621
  /**
1496
1622
  * Defines errorBody.
1497
- @type {Record<string, ?> | null} */
1623
+ * @type {Record<string, ?> | null} */
1498
1624
  let errorBody
1499
1625
 
1500
1626
  try {
@@ -1565,7 +1691,7 @@ async function flushPendingSharedFrontendModelRequests() {
1565
1691
 
1566
1692
  request.resolve(/**
1567
1693
  * Narrows the runtime value to the documented type.
1568
- @type {Record<string, ?>} */ (responsePayload))
1694
+ * @type {Record<string, ?>} */ (responsePayload))
1569
1695
  }
1570
1696
  } catch (error) {
1571
1697
  for (const request of batchedRequests) {
@@ -1577,7 +1703,7 @@ async function flushPendingSharedFrontendModelRequests() {
1577
1703
 
1578
1704
  /**
1579
1705
  * Runs schedule shared frontend model request flush.
1580
- @returns {void} */
1706
+ * @returns {void} */
1581
1707
  function scheduleSharedFrontendModelRequestFlush() {
1582
1708
  if (sharedFrontendModelFlushScheduled) return
1583
1709
 
@@ -1671,7 +1797,7 @@ function assertDefinedFindByConditionValue(value, keyPath) {
1671
1797
 
1672
1798
  const objectValue = /**
1673
1799
  * Narrows the runtime value to the documented type.
1674
- @type {Record<string, ?>} */ (value)
1800
+ * @type {Record<string, ?>} */ (value)
1675
1801
  const prototype = Object.getPrototypeOf(objectValue)
1676
1802
 
1677
1803
  if (prototype !== Object.prototype && prototype !== null) {
@@ -1686,7 +1812,7 @@ function assertDefinedFindByConditionValue(value, keyPath) {
1686
1812
 
1687
1813
  const valueObject = /**
1688
1814
  * Narrows the runtime value to the documented type.
1689
- @type {Record<string, ?>} */ (value)
1815
+ * @type {Record<string, ?>} */ (value)
1690
1816
 
1691
1817
  Object.keys(valueObject).forEach((nestedKey) => {
1692
1818
  assertDefinedFindByConditionValue(valueObject[nestedKey], `${keyPath}.${nestedKey}`)
@@ -1694,11 +1820,16 @@ function assertDefinedFindByConditionValue(value, keyPath) {
1694
1820
  }
1695
1821
  }
1696
1822
 
1697
- /** Base class for generated frontend model classes. */
1823
+ /**
1824
+ * Base frontend model.
1825
+ * @template {Record<string, FrontendModelAttributeValue>} [Attributes=Record<string, FrontendModelAttributeValue>]
1826
+ * @template {Record<string, FrontendModelAttributeValue>} [CreateAttributes=Record<string, FrontendModelAttributeValue>]
1827
+ * @template {Record<string, FrontendModelAttributeValue>} [UpdateAttributes=Record<string, FrontendModelAttributeValue>]
1828
+ */
1698
1829
  export default class FrontendModelBase {
1699
1830
  /**
1700
1831
  * Narrows the runtime value to the documented type.
1701
- @type {string | undefined} */
1832
+ * @type {string | undefined} */
1702
1833
  static modelName
1703
1834
 
1704
1835
  /**
@@ -1722,15 +1853,15 @@ export default class FrontendModelBase {
1722
1853
 
1723
1854
  /**
1724
1855
  * Narrows the runtime value to the documented type.
1725
- @type {Record<string, ?>} */
1856
+ * @type {Record<string, FrontendModelAttributeValue>} */
1726
1857
  _attributes
1727
1858
  /**
1728
1859
  * Narrows the runtime value to the documented type.
1729
- @type {Record<string, FrontendModelHasManyRelationship<FrontendModelClass, FrontendModelClass> | FrontendModelSingularRelationship<FrontendModelClass, FrontendModelClass>>} */
1860
+ * @type {Record<string, FrontendModelHasManyRelationship<FrontendModelBase, FrontendModelBase, Record<string, FrontendModelAttributeValue>> | FrontendModelSingularRelationship<FrontendModelBase, FrontendModelBase, Record<string, FrontendModelAttributeValue>>>} */
1730
1861
  _relationships
1731
1862
  /**
1732
1863
  * Narrows the runtime value to the documented type.
1733
- @type {Record<string, FrontendModelAttachmentHandle>} */
1864
+ * @type {Record<string, FrontendModelAttachmentHandle>} */
1734
1865
  _attachments
1735
1866
  /**
1736
1867
  * Rails-style nested attribute payloads queued for the next save.
@@ -1739,19 +1870,19 @@ export default class FrontendModelBase {
1739
1870
  _pendingNestedAttributes
1740
1871
  /**
1741
1872
  * Narrows the runtime value to the documented type.
1742
- @type {Set<string> | null} */
1873
+ * @type {Set<string> | null} */
1743
1874
  _selectedAttributes
1744
1875
  /**
1745
1876
  * Narrows the runtime value to the documented type.
1746
- @type {boolean} */
1877
+ * @type {boolean} */
1747
1878
  _isNewRecord
1748
1879
  /**
1749
1880
  * Narrows the runtime value to the documented type.
1750
- @type {boolean} */
1881
+ * @type {boolean} */
1751
1882
  _markedForDestruction
1752
1883
  /**
1753
1884
  * Narrows the runtime value to the documented type.
1754
- @type {Record<string, ?>} */
1885
+ * @type {Record<string, FrontendModelAttributeValue>} */
1755
1886
  _persistedAttributes
1756
1887
  /**
1757
1888
  * Narrows the runtime value to the documented type.
@@ -1761,9 +1892,9 @@ export default class FrontendModelBase {
1761
1892
 
1762
1893
  /**
1763
1894
  * Runs constructor.
1764
- * @param {Record<string, ?>} [attributes] - Initial attributes.
1895
+ * @param {Attributes | CreateAttributes} [attributes] - Initial attributes.
1765
1896
  */
1766
- constructor(attributes = {}) {
1897
+ constructor(attributes) {
1767
1898
  const ModelClass = frontendModelClassFor(this)
1768
1899
 
1769
1900
  ModelClass.ensureGeneratedAttachmentMethods()
@@ -1775,7 +1906,7 @@ export default class FrontendModelBase {
1775
1906
  this._isNewRecord = true
1776
1907
  this._markedForDestruction = false
1777
1908
  this._persistedAttributes = {}
1778
- this.assignAttributes(attributes)
1909
+ if (attributes) this.assignAttributes(attributes)
1779
1910
  }
1780
1911
 
1781
1912
  /**
@@ -1789,7 +1920,7 @@ export default class FrontendModelBase {
1789
1920
  const attachments = this.attachmentDefinitions()
1790
1921
  const prototype = /**
1791
1922
  * Narrows the runtime value to the documented type.
1792
- @type {Record<string, ?>} */ (this.prototype)
1923
+ * @type {Record<string, ?>} */ (this.prototype)
1793
1924
 
1794
1925
  for (const attachmentName of Object.keys(attachments)) {
1795
1926
  if (!(attachmentName in prototype)) {
@@ -1924,10 +2055,10 @@ export default class FrontendModelBase {
1924
2055
 
1925
2056
  /**
1926
2057
  * Runs attributes.
1927
- * @returns {Record<string, ?>} - Attributes hash.
2058
+ * @returns {Attributes} - Attributes hash.
1928
2059
  */
1929
2060
  attributes() {
1930
- return this._attributes
2061
+ return /** @type {Attributes} */ (this._attributes)
1931
2062
  }
1932
2063
 
1933
2064
  /**
@@ -1980,7 +2111,7 @@ export default class FrontendModelBase {
1980
2111
  changes() {
1981
2112
  /**
1982
2113
  * Changed attributes.
1983
- @type {Record<string, Array<?>>} */
2114
+ * @type {Record<string, Array<?>>} */
1984
2115
  const changedAttributes = {}
1985
2116
  const attributeNames = new Set([
1986
2117
  ...Object.keys(this._persistedAttributes),
@@ -2010,7 +2141,7 @@ export default class FrontendModelBase {
2010
2141
  /**
2011
2142
  * Runs get relationship by name.
2012
2143
  * @param {string} relationshipName - Relationship name.
2013
- * @returns {FrontendModelHasManyRelationship<FrontendModelClass, FrontendModelClass> | FrontendModelSingularRelationship<FrontendModelClass, FrontendModelClass>} - Relationship state object.
2144
+ * @returns {FrontendModelRelationship} - Relationship state object.
2014
2145
  */
2015
2146
  getRelationshipByName(relationshipName) {
2016
2147
  if (!this._relationships[relationshipName]) {
@@ -2054,7 +2185,7 @@ export default class FrontendModelBase {
2054
2185
  /**
2055
2186
  * Runs load relationship.
2056
2187
  * @param {string} relationshipName - Relationship name.
2057
- * @returns {Promise<?>} - Loaded relationship value.
2188
+ * @returns {Promise<FrontendModelBase | Array<FrontendModelBase> | null>} - Loaded relationship value.
2058
2189
  */
2059
2190
  async loadRelationship(relationshipName) {
2060
2191
  const ModelClass = frontendModelClassFor(this)
@@ -2062,11 +2193,12 @@ export default class FrontendModelBase {
2062
2193
  const reloadedModel = await ModelClass
2063
2194
  .preload([relationshipName])
2064
2195
  .find(id)
2065
- const loadedValue = reloadedModel.getRelationshipByName(relationshipName).loaded()
2196
+ const sourceRelationship = reloadedModel.getRelationshipByName(relationshipName)
2197
+ const targetRelationship = this.getRelationshipByName(relationshipName)
2066
2198
 
2067
- this.getRelationshipByName(relationshipName).setLoaded(loadedValue)
2199
+ copyLoadedRelationshipValue({sourceRelationship, targetRelationship})
2068
2200
 
2069
- return loadedValue
2201
+ return targetRelationship.loaded()
2070
2202
  }
2071
2203
 
2072
2204
  /**
@@ -2087,7 +2219,7 @@ export default class FrontendModelBase {
2087
2219
  /**
2088
2220
  * Runs relationship or load.
2089
2221
  * @param {string} relationshipName - Relationship name.
2090
- * @returns {Promise<?>} - Loaded relationship value.
2222
+ * @returns {Promise<FrontendModelBase | Array<FrontendModelBase> | null>} - Loaded relationship value.
2091
2223
  */
2092
2224
  async relationshipOrLoad(relationshipName) {
2093
2225
  const relationship = this.getRelationshipByName(relationshipName)
@@ -2129,7 +2261,7 @@ export default class FrontendModelBase {
2129
2261
 
2130
2262
  /**
2131
2263
  * Batch.
2132
- @type {Array<FrontendModelBase>} */
2264
+ * @type {Array<FrontendModelBase>} */
2133
2265
  const batch = []
2134
2266
 
2135
2267
  // Exact same class, persisted, no existing in-memory relationship state.
@@ -2158,7 +2290,7 @@ export default class FrontendModelBase {
2158
2290
 
2159
2291
  /**
2160
2292
  * Reloaded by id.
2161
- @type {Map<string, FrontendModelBase>} */
2293
+ * @type {Map<string, FrontendModelBase>} */
2162
2294
  const reloadedById = new Map()
2163
2295
 
2164
2296
  for (const reloaded of reloadedBatch) {
@@ -2171,9 +2303,10 @@ export default class FrontendModelBase {
2171
2303
 
2172
2304
  if (!reloaded) continue
2173
2305
 
2174
- const reloadedValue = reloaded.getRelationshipByName(relationshipName).loaded()
2175
-
2176
- sibling.getRelationshipByName(relationshipName).setLoaded(reloadedValue)
2306
+ copyLoadedRelationshipValue({
2307
+ sourceRelationship: reloaded.getRelationshipByName(relationshipName),
2308
+ targetRelationship: sibling.getRelationshipByName(relationshipName)
2309
+ })
2177
2310
  }
2178
2311
 
2179
2312
  // If the caller itself was not populated (record deleted/filtered between
@@ -2188,8 +2321,8 @@ export default class FrontendModelBase {
2188
2321
  /**
2189
2322
  * Runs set relationship.
2190
2323
  * @param {string} relationshipName - Relationship name.
2191
- * @param {?} relationshipValue - Relationship value.
2192
- * @returns {?} - Assigned relationship value.
2324
+ * @param {FrontendModelBase | null | undefined} relationshipValue - Relationship value.
2325
+ * @returns {FrontendModelBase | null | undefined} - Assigned relationship value.
2193
2326
  */
2194
2327
  setRelationship(relationshipName, relationshipValue) {
2195
2328
  const ModelClass = frontendModelClassFor(this)
@@ -2199,23 +2332,27 @@ export default class FrontendModelBase {
2199
2332
  throw new Error(`Unknown relationship: ${ModelClass.name}#${relationshipName}`)
2200
2333
  }
2201
2334
 
2202
- if (relationshipTypeIsCollection(relationshipDefinition.type)) {
2335
+ const relationship = this.getRelationshipByName(relationshipName)
2336
+
2337
+ if (relationship instanceof FrontendModelHasManyRelationship) {
2203
2338
  throw new Error(`Cannot set has-many relationship with setRelationship(): ${ModelClass.name}#${relationshipName}`)
2204
2339
  }
2205
2340
 
2206
- this.getRelationshipByName(relationshipName).setLoaded(relationshipValue)
2341
+ relationship.setLoaded(relationshipValue)
2207
2342
 
2208
2343
  return relationshipValue
2209
2344
  }
2210
2345
 
2211
2346
  /**
2212
2347
  * Runs assign attributes.
2213
- * @param {Record<string, ?>} attributes - Attributes to assign.
2348
+ * @param {Attributes | CreateAttributes | UpdateAttributes | Record<string, FrontendModelAttributeValue>} attributes - Attributes to assign.
2214
2349
  * @returns {void} - No return value.
2215
2350
  */
2216
2351
  assignAttributes(attributes) {
2217
- for (const key in attributes) {
2218
- this.setAttribute(key, attributes[key])
2352
+ const attributeValues = /** @type {Record<string, FrontendModelAttributeValue>} */ (attributes)
2353
+
2354
+ for (const key in attributeValues) {
2355
+ this.setAttribute(key, attributeValues[key])
2219
2356
  }
2220
2357
  }
2221
2358
 
@@ -2289,9 +2426,9 @@ export default class FrontendModelBase {
2289
2426
  readCount(attributeName) {
2290
2427
  return readPayloadAssociationCount(/**
2291
2428
  * Narrows the runtime value to the documented type.
2292
- @type {import("../record-payload-values.js").RecordPayloadValuesTarget} */ (/**
2429
+ * @type {import("../record-payload-values.js").RecordPayloadValuesTarget} */ (/**
2293
2430
  * Narrows the runtime value to the documented type.
2294
- @type {?} */ (this)), attributeName)
2431
+ * @type {?} */ (this)), attributeName)
2295
2432
  }
2296
2433
 
2297
2434
  /**
@@ -2304,9 +2441,9 @@ export default class FrontendModelBase {
2304
2441
  _setAssociationCount(attributeName, value) {
2305
2442
  setPayloadAssociationCount(/**
2306
2443
  * Narrows the runtime value to the documented type.
2307
- @type {import("../record-payload-values.js").RecordPayloadValuesTarget} */ (/**
2444
+ * @type {import("../record-payload-values.js").RecordPayloadValuesTarget} */ (/**
2308
2445
  * Narrows the runtime value to the documented type.
2309
- @type {?} */ (this)), attributeName, value)
2446
+ * @type {?} */ (this)), attributeName, value)
2310
2447
  }
2311
2448
 
2312
2449
  /**
@@ -2323,9 +2460,9 @@ export default class FrontendModelBase {
2323
2460
  can(action) {
2324
2461
  return readPayloadComputedAbility(/**
2325
2462
  * Narrows the runtime value to the documented type.
2326
- @type {import("../record-payload-values.js").RecordPayloadValuesTarget} */ (/**
2463
+ * @type {import("../record-payload-values.js").RecordPayloadValuesTarget} */ (/**
2327
2464
  * Narrows the runtime value to the documented type.
2328
- @type {?} */ (this)), action)
2465
+ * @type {?} */ (this)), action)
2329
2466
  }
2330
2467
 
2331
2468
  /**
@@ -2339,9 +2476,9 @@ export default class FrontendModelBase {
2339
2476
  _setComputedAbility(action, value) {
2340
2477
  setPayloadComputedAbility(/**
2341
2478
  * Narrows the runtime value to the documented type.
2342
- @type {import("../record-payload-values.js").RecordPayloadValuesTarget} */ (/**
2479
+ * @type {import("../record-payload-values.js").RecordPayloadValuesTarget} */ (/**
2343
2480
  * Narrows the runtime value to the documented type.
2344
- @type {?} */ (this)), action, value)
2481
+ * @type {?} */ (this)), action, value)
2345
2482
  }
2346
2483
 
2347
2484
  /**
@@ -2356,9 +2493,9 @@ export default class FrontendModelBase {
2356
2493
  queryData(name) {
2357
2494
  return readPayloadQueryData(/**
2358
2495
  * Narrows the runtime value to the documented type.
2359
- @type {import("../record-payload-values.js").RecordPayloadValuesTarget} */ (/**
2496
+ * @type {import("../record-payload-values.js").RecordPayloadValuesTarget} */ (/**
2360
2497
  * Narrows the runtime value to the documented type.
2361
- @type {?} */ (this)), name)
2498
+ * @type {?} */ (this)), name)
2362
2499
  }
2363
2500
 
2364
2501
  /**
@@ -2371,9 +2508,9 @@ export default class FrontendModelBase {
2371
2508
  _setQueryData(name, value) {
2372
2509
  setPayloadQueryData(/**
2373
2510
  * Narrows the runtime value to the documented type.
2374
- @type {import("../record-payload-values.js").RecordPayloadValuesTarget} */ (/**
2511
+ * @type {import("../record-payload-values.js").RecordPayloadValuesTarget} */ (/**
2375
2512
  * Narrows the runtime value to the documented type.
2376
- @type {?} */ (this)), name, value)
2513
+ * @type {?} */ (this)), name, value)
2377
2514
  }
2378
2515
 
2379
2516
  /**
@@ -2436,7 +2573,7 @@ export default class FrontendModelBase {
2436
2573
  for (const relationshipName of Object.keys(this._relationships)) {
2437
2574
  const definition = /**
2438
2575
  * Narrows the runtime value to the documented type.
2439
- @type {?} */ (definitions[relationshipName])
2576
+ * @type {?} */ (definitions[relationshipName])
2440
2577
 
2441
2578
  if (!definition || definition.type !== "belongsTo") continue
2442
2579
 
@@ -2499,12 +2636,12 @@ export default class FrontendModelBase {
2499
2636
  return {arg1: payload}
2500
2637
  }
2501
2638
 
2502
- return /** Narrows the runtime value to the documented type. @type {Record<string, ?>} */ (payload)
2639
+ return /** @type {Record<string, ?>} */ (payload)
2503
2640
  }
2504
2641
 
2505
2642
  /**
2506
2643
  * Payload.
2507
- @type {Record<string, number | string | Array<?>>} */
2644
+ * @type {Record<string, number | string | Array<?>>} */
2508
2645
  const payload = {}
2509
2646
 
2510
2647
  for (let index = 0; index < args.length; index += 1) {
@@ -2650,7 +2787,7 @@ export default class FrontendModelBase {
2650
2787
  static setWebsocketMetadata(key, value) {
2651
2788
  const client = /**
2652
2789
  * Narrows the runtime value to the documented type.
2653
- @type {?} */ (frontendModelTransportConfig.websocketClient || resolveInternalWebsocketClient())
2790
+ * @type {?} */ (frontendModelTransportConfig.websocketClient || resolveInternalWebsocketClient())
2654
2791
 
2655
2792
  if (!client || typeof client.setMetadata !== "function") return
2656
2793
 
@@ -2670,12 +2807,12 @@ export default class FrontendModelBase {
2670
2807
  static openManagedConnection(connectionType, options) {
2671
2808
  /**
2672
2809
  * Connection.
2673
- @type {?} */
2810
+ * @type {?} */
2674
2811
  let connection = null
2675
2812
  let closed = false
2676
2813
  /**
2677
2814
  * Retry timer.
2678
- @type {ReturnType<typeof setTimeout> | null} */
2815
+ * @type {ReturnType<typeof setTimeout> | null} */
2679
2816
  let retryTimer = null
2680
2817
  let lastParamsJson = ""
2681
2818
 
@@ -2714,7 +2851,7 @@ export default class FrontendModelBase {
2714
2851
  // which only reflects the internal client.
2715
2852
  const client = /**
2716
2853
  * Narrows the runtime value to the documented type.
2717
- @type {?} */ (frontendModelTransportConfig.websocketClient || resolveInternalWebsocketClient())
2854
+ * @type {?} */ (frontendModelTransportConfig.websocketClient || resolveInternalWebsocketClient())
2718
2855
 
2719
2856
  if (!client || !client.isOpen()) {
2720
2857
  if (retryTimer === null) {
@@ -2764,7 +2901,7 @@ export default class FrontendModelBase {
2764
2901
  static openWebsocketConnection(connectionType, options) {
2765
2902
  const client = /**
2766
2903
  * Narrows the runtime value to the documented type.
2767
- @type {?} */ (frontendModelTransportConfig.websocketClient || resolveInternalWebsocketClient())
2904
+ * @type {?} */ (frontendModelTransportConfig.websocketClient || resolveInternalWebsocketClient())
2768
2905
 
2769
2906
  if (!client || typeof client.openConnection !== "function") {
2770
2907
  throw new Error("openWebsocketConnection requires configureTransport({websocketUrl})")
@@ -2783,7 +2920,7 @@ export default class FrontendModelBase {
2783
2920
  static subscribeWebsocketChannel(channelType, options) {
2784
2921
  const client = /**
2785
2922
  * Narrows the runtime value to the documented type.
2786
- @type {?} */ (frontendModelTransportConfig.websocketClient || resolveInternalWebsocketClient())
2923
+ * @type {?} */ (frontendModelTransportConfig.websocketClient || resolveInternalWebsocketClient())
2787
2924
 
2788
2925
  if (!client || typeof client.subscribeChannel !== "function") {
2789
2926
  throw new Error("subscribeWebsocketChannel requires configureTransport({websocketUrl})")
@@ -2800,7 +2937,7 @@ export default class FrontendModelBase {
2800
2937
  static installWebsocketTestHooks() {
2801
2938
  if (typeof globalThis === "undefined") return
2802
2939
 
2803
- /** Narrows the runtime value to the documented type. @type {?} */ (globalThis).__velocious_websocket_hooks = {
2940
+ /** @type {?} */ (globalThis).__velocious_websocket_hooks = {
2804
2941
  connect: () => this.connectWebsocket(),
2805
2942
  disconnect: () => this.disconnectWebsocket(),
2806
2943
  drop: () => this.dropWebsocket(),
@@ -2812,7 +2949,7 @@ export default class FrontendModelBase {
2812
2949
  * Runs attributes from response.
2813
2950
  * @this {FrontendModelClass}
2814
2951
  * @param {object} response - Response payload.
2815
- * @returns {Record<string, ?>} - Attributes from payload.
2952
+ * @returns {Record<string, FrontendModelAttributeValue>} - Attributes from payload.
2816
2953
  */
2817
2954
  static attributesFromResponse(response) {
2818
2955
  const modelData = this.modelDataFromResponse(response)
@@ -2824,59 +2961,52 @@ export default class FrontendModelBase {
2824
2961
  * Runs model data from response.
2825
2962
  * @this {FrontendModelClass}
2826
2963
  * @param {object} response - Response payload.
2827
- * @returns {{abilities: Record<string, boolean>, attributes: Record<string, ?>, associationCounts: Record<string, number>, queryData: Record<string, ?>, preloadedRelationships: Record<string, ?>, selectedAttributes: Set<string>}} - Attributes, preloaded relationships, association counts, queryData, abilities, and the selected-attributes set.
2964
+ * @returns {{abilities: Record<string, boolean>, attributes: Record<string, FrontendModelAttributeValue>, associationCounts: Record<string, number>, queryData: Record<string, FrontendModelAttributeValue>, preloadedRelationships: Record<string, FrontendModelAttributeValue>, selectedAttributes: Set<string>}} - Attributes, preloaded relationships, association counts, queryData, abilities, and the selected-attributes set.
2828
2965
  */
2829
2966
  static modelDataFromResponse(response) {
2830
2967
  if (!response || typeof response !== "object") {
2831
2968
  throw new Error(`Expected object response but got: ${response}`)
2832
2969
  }
2833
2970
 
2834
- const responseObject = /**
2835
- * Narrows the runtime value to the documented type.
2836
- @type {Record<string, ?>} */ (response)
2971
+ // Narrows the response object to the frontend-model transport value map.
2972
+ const responseObject = /** @type {Record<string, FrontendModelAttributeValue>} */ (response)
2837
2973
 
2838
2974
  /**
2839
2975
  * Defines modelData.
2840
- @type {Record<string, ?>} */
2976
+ * @type {Record<string, FrontendModelAttributeValue>} */
2841
2977
  let modelData
2842
2978
 
2843
2979
  if (responseObject.model && typeof responseObject.model === "object") {
2844
- modelData = /**
2845
- * Narrows the runtime value to the documented type.
2846
- @type {Record<string, ?>} */ (responseObject.model)
2980
+ // Narrows the nested model payload to the frontend-model value map.
2981
+ modelData = /** @type {Record<string, FrontendModelAttributeValue>} */ (responseObject.model)
2847
2982
  } else if (responseObject.attributes && typeof responseObject.attributes === "object") {
2848
- modelData = /**
2849
- * Narrows the runtime value to the documented type.
2850
- @type {Record<string, ?>} */ (responseObject.attributes)
2983
+ // Narrows the nested attributes payload to the frontend-model value map.
2984
+ modelData = /** @type {Record<string, FrontendModelAttributeValue>} */ (responseObject.attributes)
2851
2985
  } else {
2852
2986
  modelData = responseObject
2853
2987
  }
2854
2988
 
2855
- const attributes = {...modelData}
2989
+ const attributes = /** @type {Record<string, FrontendModelAttributeValue>} */ ({...modelData})
2856
2990
  const preloadedRelationships = isPlainObject(attributes[PRELOADED_RELATIONSHIPS_KEY])
2857
- ? /**
2858
- * Narrows the runtime value to the documented type.
2859
- @type {Record<string, ?>} */ (attributes[PRELOADED_RELATIONSHIPS_KEY])
2991
+ ? /** @type {Record<string, FrontendModelAttributeValue>} */ (attributes[PRELOADED_RELATIONSHIPS_KEY])
2860
2992
  : {}
2861
2993
  const associationCounts = isPlainObject(attributes[ASSOCIATION_COUNTS_KEY])
2862
2994
  ? /**
2863
2995
  * Narrows the runtime value to the documented type.
2864
- @type {Record<string, number>} */ (attributes[ASSOCIATION_COUNTS_KEY])
2996
+ * @type {Record<string, number>} */ (attributes[ASSOCIATION_COUNTS_KEY])
2865
2997
  : {}
2866
2998
  const queryData = isPlainObject(attributes[QUERY_DATA_KEY])
2867
- ? /**
2868
- * Narrows the runtime value to the documented type.
2869
- @type {Record<string, ?>} */ (attributes[QUERY_DATA_KEY])
2999
+ ? /** @type {Record<string, FrontendModelAttributeValue>} */ (attributes[QUERY_DATA_KEY])
2870
3000
  : {}
2871
3001
  const abilities = isPlainObject(attributes[ABILITIES_KEY])
2872
3002
  ? /**
2873
3003
  * Narrows the runtime value to the documented type.
2874
- @type {Record<string, boolean>} */ (attributes[ABILITIES_KEY])
3004
+ * @type {Record<string, boolean>} */ (attributes[ABILITIES_KEY])
2875
3005
  : {}
2876
3006
  const selectedAttributesFromPayload = Array.isArray(attributes[SELECTED_ATTRIBUTES_KEY])
2877
3007
  ? new Set(/**
2878
3008
  * Narrows the runtime value to the documented type.
2879
- @type {string[]} */ (attributes[SELECTED_ATTRIBUTES_KEY]).filter((attributeName) => typeof attributeName === "string"))
3009
+ * @type {string[]} */ (attributes[SELECTED_ATTRIBUTES_KEY]).filter((attributeName) => typeof attributeName === "string"))
2880
3010
  : null
2881
3011
 
2882
3012
  delete attributes[PRELOADED_RELATIONSHIPS_KEY]
@@ -2902,12 +3032,39 @@ export default class FrontendModelBase {
2902
3032
  const relationship = model.getRelationshipByName(relationshipName)
2903
3033
  const targetModelClass = this.relationshipModelClass(relationshipName)
2904
3034
 
2905
- if (Array.isArray(relationshipPayload)) {
2906
- relationship.setLoaded(relationshipPayload.map((entry) => this.instantiateRelationshipValue(entry, targetModelClass)))
3035
+ if (relationship instanceof FrontendModelHasManyRelationship) {
3036
+ if (!Array.isArray(relationshipPayload)) {
3037
+ throw new Error(`Expected ${this.name}#${relationshipName} payload to be an array`)
3038
+ }
3039
+
3040
+ /** @type {Array<FrontendModelBase>} */
3041
+ const relatedModels = []
3042
+
3043
+ for (const entry of relationshipPayload) {
3044
+ const relatedModel = this.instantiateRelationshipValue(entry, targetModelClass)
3045
+
3046
+ if (!(relatedModel instanceof FrontendModelBase)) {
3047
+ throw new Error(`Expected ${this.name}#${relationshipName} payload entry to instantiate a frontend model`)
3048
+ }
3049
+
3050
+ relatedModels.push(relatedModel)
3051
+ }
3052
+
3053
+ relationship.setLoaded(relatedModels)
2907
3054
  continue
2908
3055
  }
2909
3056
 
2910
- relationship.setLoaded(this.instantiateRelationshipValue(relationshipPayload, targetModelClass))
3057
+ if (Array.isArray(relationshipPayload)) {
3058
+ throw new Error(`Expected ${this.name}#${relationshipName} payload to be singular`)
3059
+ }
3060
+
3061
+ const relatedModel = this.instantiateRelationshipValue(relationshipPayload, targetModelClass)
3062
+
3063
+ if (relatedModel != undefined && !(relatedModel instanceof FrontendModelBase)) {
3064
+ throw new Error(`Expected ${this.name}#${relationshipName} payload to instantiate a frontend model`)
3065
+ }
3066
+
3067
+ relationship.setLoaded(relatedModel)
2911
3068
  }
2912
3069
  }
2913
3070
 
@@ -2944,7 +3101,7 @@ export default class FrontendModelBase {
2944
3101
  // model instance into a new constructor call and produce a broken model
2945
3102
  // with internal state keys promoted to attributes.
2946
3103
  if (response instanceof this) {
2947
- return /** Narrows the runtime value to the documented type. @type {InstanceType<T>} */ (response)
3104
+ return /** @type {InstanceType<T>} */ (response)
2948
3105
  }
2949
3106
 
2950
3107
  const modelData = this.modelDataFromResponse(response)
@@ -2954,9 +3111,9 @@ export default class FrontendModelBase {
2954
3111
  const queryData = modelData.queryData
2955
3112
  const abilities = modelData.abilities
2956
3113
  const selectedAttributes = modelData.selectedAttributes
2957
- const model = /**
2958
- * Narrows the runtime value to the documented type.
2959
- @type {InstanceType<T>} */ (new this(attributes))
3114
+ const receiver = /** @type {unknown} */ (this)
3115
+ const ModelClass = /** @type {new (attributes?: Record<string, FrontendModelAttributeValue>) => InstanceType<T>} */ (receiver)
3116
+ const model = new ModelClass(attributes)
2960
3117
  model._selectedAttributes = selectedAttributes ? new Set(selectedAttributes) : null
2961
3118
 
2962
3119
  this.applyPreloadedRelationships(model, preloadedRelationships)
@@ -3196,7 +3353,7 @@ export default class FrontendModelBase {
3196
3353
  async onUpdate(callback, options = {}) {
3197
3354
  const self = /**
3198
3355
  * Narrows the runtime value to the documented type.
3199
- @type {?} */ (this)
3356
+ * @type {?} */ (this)
3200
3357
  const ModelClass = frontendModelClassFor(this)
3201
3358
  const sub = ensureFrontendModelEventSubscription(ModelClass)
3202
3359
  const id = String(self.id())
@@ -3228,7 +3385,7 @@ export default class FrontendModelBase {
3228
3385
  async onDestroy(callback, options = {}) {
3229
3386
  const self = /**
3230
3387
  * Narrows the runtime value to the documented type.
3231
- @type {?} */ (this)
3388
+ * @type {?} */ (this)
3232
3389
  const ModelClass = frontendModelClassFor(this)
3233
3390
 
3234
3391
  assertNoDestroyEventFilter(ModelClass, options)
@@ -3341,7 +3498,7 @@ export default class FrontendModelBase {
3341
3498
  * @returns {FrontendModelQuery<T>} - Query builder.
3342
3499
  */
3343
3500
  static query() {
3344
- return /** Narrows the runtime value to the documented type. @type {FrontendModelQuery<T>} */ (new FrontendModelQuery({modelClass: this}))
3501
+ return /** @type {FrontendModelQuery<T>} */ (new FrontendModelQuery({modelClass: this}))
3345
3502
  }
3346
3503
 
3347
3504
  /**
@@ -3352,7 +3509,7 @@ export default class FrontendModelBase {
3352
3509
  * @returns {FrontendModelQuery<T>} - Query with preload.
3353
3510
  */
3354
3511
  static preload(preload) {
3355
- return /** Narrows the runtime value to the documented type. @type {FrontendModelQuery<T>} */ (this.query().preload(preload))
3512
+ return /** @type {FrontendModelQuery<T>} */ (this.query().preload(preload))
3356
3513
  }
3357
3514
 
3358
3515
  /**
@@ -3363,7 +3520,7 @@ export default class FrontendModelBase {
3363
3520
  * @returns {FrontendModelQuery<T>} - Query with selected attributes.
3364
3521
  */
3365
3522
  static select(select) {
3366
- return /** Narrows the runtime value to the documented type. @type {FrontendModelQuery<T>} */ (this.query().select(select))
3523
+ return /** @type {FrontendModelQuery<T>} */ (this.query().select(select))
3367
3524
  }
3368
3525
 
3369
3526
  /**
@@ -3374,7 +3531,7 @@ export default class FrontendModelBase {
3374
3531
  * @returns {FrontendModelQuery<T>} - Query with extra selected attributes.
3375
3532
  */
3376
3533
  static selectsExtra(select) {
3377
- return /** Narrows the runtime value to the documented type. @type {FrontendModelQuery<T>} */ (this.query().selectsExtra(select))
3534
+ return /** @type {FrontendModelQuery<T>} */ (this.query().selectsExtra(select))
3378
3535
  }
3379
3536
 
3380
3537
  /**
@@ -3422,15 +3579,15 @@ export default class FrontendModelBase {
3422
3579
 
3423
3580
  /**
3424
3581
  * Runs create.
3425
- * @template {FrontendModelClass} T
3426
- * @this {T}
3427
- * @param {Record<string, ?>} [attributes] - Initial attributes.
3428
- * @returns {Promise<InstanceType<T>>} - Persisted model.
3582
+ * @template {FrontendModelClass} ModelClass
3583
+ * @this {ModelClass}
3584
+ * @param {FrontendModelCreateAttributesFor<InstanceType<ModelClass>>} [attributes] - Initial attributes.
3585
+ * @returns {Promise<InstanceType<ModelClass>>} - Persisted model.
3429
3586
  */
3430
- static async create(attributes = {}) {
3431
- const model = /**
3432
- * Narrows the runtime value to the documented type.
3433
- @type {InstanceType<T>} */ (new this(attributes))
3587
+ static async create(attributes) {
3588
+ const receiver = /** @type {unknown} */ (this)
3589
+ const ModelClass = /** @type {new (attributes?: FrontendModelCreateAttributesFor<InstanceType<ModelClass>>) => InstanceType<ModelClass>} */ (receiver)
3590
+ const model = new ModelClass(attributes)
3434
3591
 
3435
3592
  await model.save()
3436
3593
 
@@ -3518,10 +3675,10 @@ export default class FrontendModelBase {
3518
3675
 
3519
3676
  const actualObject = /**
3520
3677
  * Narrows the runtime value to the documented type.
3521
- @type {Record<string, ?>} */ (actualValue)
3678
+ * @type {Record<string, ?>} */ (actualValue)
3522
3679
  const expectedObject = /**
3523
3680
  * Narrows the runtime value to the documented type.
3524
- @type {Record<string, ?>} */ (expectedValue)
3681
+ * @type {Record<string, ?>} */ (expectedValue)
3525
3682
  const actualKeys = Object.keys(actualObject)
3526
3683
  const expectedKeys = Object.keys(expectedObject)
3527
3684
 
@@ -3601,13 +3758,13 @@ export default class FrontendModelBase {
3601
3758
 
3602
3759
  /**
3603
3760
  * Runs update.
3604
- * @param {Record<string, ?>} [newAttributes] - New values to assign before update.
3761
+ * @param {UpdateAttributes} [newAttributes] - New values to assign before update.
3605
3762
  * @returns {Promise<this>} - Updated model.
3606
3763
  */
3607
- async update(newAttributes = {}) {
3608
- this.assignAttributes(newAttributes)
3764
+ async update(newAttributes) {
3765
+ if (newAttributes) this.assignAttributes(newAttributes)
3609
3766
 
3610
- return await this.save()
3767
+ return /** @type {this} */ (await this.save())
3611
3768
  }
3612
3769
 
3613
3770
  /**
@@ -3653,7 +3810,7 @@ export default class FrontendModelBase {
3653
3810
  const commandType = isNew ? "create" : "update"
3654
3811
  /**
3655
3812
  * Payload.
3656
- @type {Record<string, ?>} */
3813
+ * @type {Record<string, ?>} */
3657
3814
  const payload = {
3658
3815
  attributes: this._changedAttributesForSave()
3659
3816
  }
@@ -3693,12 +3850,12 @@ export default class FrontendModelBase {
3693
3850
  * fields the caller actually changed — avoiding strict permit rejections on
3694
3851
  * framework-managed fields like `id`, `createdAt`, `updatedAt`, or owner
3695
3852
  * foreign keys that the resource never lists in `permittedParams`.
3696
- * @returns {Record<string, ?>} - Changed attributes hash.
3853
+ * @returns {Record<string, FrontendModelAttributeValue>} - Changed attributes hash.
3697
3854
  */
3698
3855
  _changedAttributesForSave() {
3699
3856
  /**
3700
3857
  * Changed attributes.
3701
- @type {Record<string, ?>} */
3858
+ * @type {Record<string, FrontendModelAttributeValue>} */
3702
3859
  const changedAttributes = {}
3703
3860
 
3704
3861
  for (const [attributeName, [previousValue, currentValue]] of Object.entries(this.changes())) {
@@ -3781,7 +3938,7 @@ export default class FrontendModelBase {
3781
3938
 
3782
3939
  /**
3783
3940
  * Payload.
3784
- @type {Record<string, Array<Record<string, ?>>>} */
3941
+ * @type {Record<string, Array<Record<string, ?>>>} */
3785
3942
  const payload = {}
3786
3943
 
3787
3944
  for (const relationshipName of Object.keys(nestedAttributesConfig)) {
@@ -3843,7 +4000,7 @@ export default class FrontendModelBase {
3843
4000
  if (this.isNewRecord()) {
3844
4001
  /**
3845
4002
  * Entry.
3846
- @type {Record<string, ?>} */
4003
+ * @type {Record<string, ?>} */
3847
4004
  const entry = {}
3848
4005
  const attributes = this._changedAttributesForSave()
3849
4006
 
@@ -3858,7 +4015,7 @@ export default class FrontendModelBase {
3858
4015
 
3859
4016
  /**
3860
4017
  * Entry.
3861
- @type {Record<string, ?>} */
4018
+ * @type {Record<string, ?>} */
3862
4019
  const entry = {id: this.primaryKeyValue()}
3863
4020
 
3864
4021
  if (this.isChanged()) entry.attributes = this._changedAttributesForSave()
@@ -4006,7 +4163,7 @@ export default class FrontendModelBase {
4006
4163
 
4007
4164
  /**
4008
4165
  * Relevant preloads.
4009
- @type {Record<string, ?>} */
4166
+ * @type {Record<string, ?>} */
4010
4167
  const relevantPreloads = {}
4011
4168
 
4012
4169
  for (const relationshipName of Object.keys(nestedAttributesConfig)) {
@@ -4031,7 +4188,7 @@ export default class FrontendModelBase {
4031
4188
  const commandName = this.commandName(commandType)
4032
4189
  const serializedPayload = /**
4033
4190
  * Narrows the runtime value to the documented type.
4034
- @type {Record<string, ?>} */ (serializeFrontendModelTransportValue(payload))
4191
+ * @type {Record<string, ?>} */ (serializeFrontendModelTransportValue(payload))
4035
4192
  const resourcePath = this.resourcePath()
4036
4193
  const containsAttachmentUpload = frontendModelPayloadContainsAttachmentUpload(serializedPayload)
4037
4194
  const useSharedTransport = !containsAttachmentUpload
@@ -4055,7 +4212,7 @@ export default class FrontendModelBase {
4055
4212
 
4056
4213
  const decodedBatchResponse = /**
4057
4214
  * Narrows the runtime value to the documented type.
4058
- @type {Record<string, ?>} */ (batchResponse)
4215
+ * @type {Record<string, ?>} */ (batchResponse)
4059
4216
 
4060
4217
  this.throwOnErrorFrontendModelResponse({
4061
4218
  commandType,
@@ -4086,7 +4243,7 @@ export default class FrontendModelBase {
4086
4243
  const directJson = directResponseText.length > 0 ? JSON.parse(directResponseText) : {}
4087
4244
  const decodedDirectResponse = /**
4088
4245
  * Narrows the runtime value to the documented type.
4089
- @type {Record<string, ?>} */ (deserializeFrontendModelTransportValue(directJson))
4246
+ * @type {Record<string, ?>} */ (deserializeFrontendModelTransportValue(directJson))
4090
4247
 
4091
4248
  this.throwOnErrorFrontendModelResponse({
4092
4249
  commandType,
@@ -4111,7 +4268,7 @@ export default class FrontendModelBase {
4111
4268
  static async executeCustomCommand({commandName, commandType, memberId = null, payload, resourcePath}) {
4112
4269
  const serializedPayload = /**
4113
4270
  * Narrows the runtime value to the documented type.
4114
- @type {Record<string, ?>} */ (serializeFrontendModelTransportValue(payload))
4271
+ * @type {Record<string, ?>} */ (serializeFrontendModelTransportValue(payload))
4115
4272
  const customPath = frontendModelCustomCommandPath({
4116
4273
  commandName,
4117
4274
  memberId,
@@ -4135,7 +4292,7 @@ export default class FrontendModelBase {
4135
4292
 
4136
4293
  const decodedBatchResponse = /**
4137
4294
  * Narrows the runtime value to the documented type.
4138
- @type {Record<string, ?>} */ (batchResponse)
4295
+ * @type {Record<string, ?>} */ (batchResponse)
4139
4296
 
4140
4297
  this.throwOnErrorFrontendModelResponse({
4141
4298
  commandType,
@@ -4181,7 +4338,7 @@ export default class FrontendModelBase {
4181
4338
 
4182
4339
  const error = /**
4183
4340
  * Narrows the runtime value to the documented type.
4184
- @type {Error & {velocious?: Record<string, ?>, errorType?: string, validationErrors?: Record<string, ?>, debugErrorClass?: string, debugBacktrace?: string[]}} */ (new Error(errorMessage))
4341
+ * @type {Error & {velocious?: Record<string, ?>, errorType?: string, validationErrors?: Record<string, ?>, debugErrorClass?: string, debugBacktrace?: string[]}} */ (new Error(errorMessage))
4185
4342
  if (response.velocious && typeof response.velocious === "object") {
4186
4343
  error.velocious = response.velocious
4187
4344
  }
@@ -4212,7 +4369,7 @@ export default class FrontendModelBase {
4212
4369
  static configuredFrontendModelAttributeNames() {
4213
4370
  const resourceConfig = /**
4214
4371
  * Narrows the runtime value to the documented type.
4215
- @type {Record<string, ?>} */ (this.resourceConfig())
4372
+ * @type {Record<string, ?>} */ (this.resourceConfig())
4216
4373
  const attributes = resourceConfig.attributes
4217
4374
 
4218
4375
  if (Array.isArray(attributes)) {