velocious 1.0.430 → 1.0.431

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 (811) hide show
  1. package/bin/velocious.js +48 -0
  2. package/build/bin/velocious.js +39 -34
  3. package/build/index.js +1 -2
  4. package/build/src/application.js +214 -187
  5. package/build/src/authorization/ability.d.ts +24 -23
  6. package/build/src/authorization/ability.d.ts.map +1 -1
  7. package/build/src/authorization/ability.js +300 -252
  8. package/build/src/authorization/base-resource.d.ts +20 -26
  9. package/build/src/authorization/base-resource.d.ts.map +1 -1
  10. package/build/src/authorization/base-resource.js +136 -118
  11. package/build/src/background-jobs/client.js +47 -43
  12. package/build/src/background-jobs/cron-expression.js +166 -127
  13. package/build/src/background-jobs/forked-runner-child.js +47 -37
  14. package/build/src/background-jobs/job-record.js +10 -8
  15. package/build/src/background-jobs/job-registry.js +84 -72
  16. package/build/src/background-jobs/job-runner.js +81 -74
  17. package/build/src/background-jobs/job.js +72 -62
  18. package/build/src/background-jobs/json-socket.js +70 -65
  19. package/build/src/background-jobs/main.js +900 -841
  20. package/build/src/background-jobs/normalize-error.js +11 -12
  21. package/build/src/background-jobs/scheduler.js +247 -205
  22. package/build/src/background-jobs/socket-request.js +65 -60
  23. package/build/src/background-jobs/status-reporter.js +96 -86
  24. package/build/src/background-jobs/store.js +980 -862
  25. package/build/src/background-jobs/types.js +3 -2
  26. package/build/src/background-jobs/web/authorization.js +50 -38
  27. package/build/src/background-jobs/web/controller.js +268 -232
  28. package/build/src/background-jobs/web/index.js +40 -36
  29. package/build/src/background-jobs/web/path-matcher.js +48 -45
  30. package/build/src/background-jobs/web/registry.js +14 -9
  31. package/build/src/background-jobs/worker.js +639 -585
  32. package/build/src/beacon/client.js +293 -264
  33. package/build/src/beacon/in-process-broker.js +25 -20
  34. package/build/src/beacon/in-process-client.js +116 -104
  35. package/build/src/beacon/server.js +126 -110
  36. package/build/src/beacon/types.js +8 -2
  37. package/build/src/cli/base-command.js +57 -49
  38. package/build/src/cli/browser-cli.js +42 -37
  39. package/build/src/cli/commands/background-jobs-main.js +5 -5
  40. package/build/src/cli/commands/background-jobs-runner.js +5 -5
  41. package/build/src/cli/commands/background-jobs-worker.js +5 -5
  42. package/build/src/cli/commands/beacon.js +5 -5
  43. package/build/src/cli/commands/console.js +10 -10
  44. package/build/src/cli/commands/db/base-command.js +76 -71
  45. package/build/src/cli/commands/db/create.js +61 -53
  46. package/build/src/cli/commands/db/drop.js +71 -62
  47. package/build/src/cli/commands/db/migrate.js +15 -13
  48. package/build/src/cli/commands/db/reset.js +19 -16
  49. package/build/src/cli/commands/db/rollback.js +13 -12
  50. package/build/src/cli/commands/db/schema/dump.js +9 -9
  51. package/build/src/cli/commands/db/schema/load.js +9 -9
  52. package/build/src/cli/commands/db/seed.js +9 -9
  53. package/build/src/cli/commands/db/tenants/check.js +35 -32
  54. package/build/src/cli/commands/db/tenants/create.js +29 -26
  55. package/build/src/cli/commands/db/tenants/migrate.js +44 -40
  56. package/build/src/cli/commands/destroy/migration.js +5 -5
  57. package/build/src/cli/commands/generate/base-models.js +5 -5
  58. package/build/src/cli/commands/generate/frontend-models.js +9 -9
  59. package/build/src/cli/commands/generate/migration.js +5 -5
  60. package/build/src/cli/commands/generate/model.js +5 -5
  61. package/build/src/cli/commands/init.js +9 -7
  62. package/build/src/cli/commands/routes.js +6 -6
  63. package/build/src/cli/commands/run-script.js +9 -9
  64. package/build/src/cli/commands/runner.js +9 -9
  65. package/build/src/cli/commands/server.js +6 -6
  66. package/build/src/cli/commands/test.js +7 -6
  67. package/build/src/cli/index.js +141 -127
  68. package/build/src/cli/tenant-database-command-helper.js +185 -154
  69. package/build/src/cli/use-browser-cli.js +20 -15
  70. package/build/src/configuration-resolver.js +54 -47
  71. package/build/src/configuration-types.d.ts +21 -2
  72. package/build/src/configuration-types.d.ts.map +1 -1
  73. package/build/src/configuration-types.js +60 -3
  74. package/build/src/configuration.js +2547 -2240
  75. package/build/src/controller.js +407 -363
  76. package/build/src/current-configuration.js +12 -9
  77. package/build/src/current.js +75 -70
  78. package/build/src/database/annotations-async-hooks.js +22 -16
  79. package/build/src/database/annotations.js +18 -12
  80. package/build/src/database/drivers/base-column.js +179 -155
  81. package/build/src/database/drivers/base-columns-index.js +78 -69
  82. package/build/src/database/drivers/base-foreign-key.js +101 -89
  83. package/build/src/database/drivers/base-table.js +149 -124
  84. package/build/src/database/drivers/base.js +1489 -1306
  85. package/build/src/database/drivers/mssql/column.js +50 -39
  86. package/build/src/database/drivers/mssql/columns-index.js +3 -2
  87. package/build/src/database/drivers/mssql/connect-connection.js +9 -11
  88. package/build/src/database/drivers/mssql/foreign-key.js +9 -8
  89. package/build/src/database/drivers/mssql/index.js +587 -507
  90. package/build/src/database/drivers/mssql/options.js +75 -68
  91. package/build/src/database/drivers/mssql/query-parser.js +3 -2
  92. package/build/src/database/drivers/mssql/sql/alter-table.js +2 -2
  93. package/build/src/database/drivers/mssql/sql/create-database.js +31 -24
  94. package/build/src/database/drivers/mssql/sql/create-index.js +2 -2
  95. package/build/src/database/drivers/mssql/sql/create-table.js +2 -2
  96. package/build/src/database/drivers/mssql/sql/delete.js +16 -14
  97. package/build/src/database/drivers/mssql/sql/drop-database.js +31 -24
  98. package/build/src/database/drivers/mssql/sql/drop-table.js +2 -2
  99. package/build/src/database/drivers/mssql/sql/insert.js +2 -2
  100. package/build/src/database/drivers/mssql/sql/update.js +28 -24
  101. package/build/src/database/drivers/mssql/sql/upsert.js +20 -18
  102. package/build/src/database/drivers/mssql/structure-sql.js +114 -102
  103. package/build/src/database/drivers/mssql/table.js +96 -81
  104. package/build/src/database/drivers/mysql/column.js +92 -75
  105. package/build/src/database/drivers/mysql/columns-index.js +19 -16
  106. package/build/src/database/drivers/mysql/foreign-key.js +9 -8
  107. package/build/src/database/drivers/mysql/index.js +457 -396
  108. package/build/src/database/drivers/mysql/options.js +30 -26
  109. package/build/src/database/drivers/mysql/query-parser.js +3 -2
  110. package/build/src/database/drivers/mysql/query.js +29 -26
  111. package/build/src/database/drivers/mysql/sql/alter-table.js +3 -2
  112. package/build/src/database/drivers/mysql/sql/create-database.js +28 -23
  113. package/build/src/database/drivers/mysql/sql/create-index.js +3 -2
  114. package/build/src/database/drivers/mysql/sql/create-table.js +3 -2
  115. package/build/src/database/drivers/mysql/sql/delete.js +17 -14
  116. package/build/src/database/drivers/mysql/sql/drop-database.js +3 -2
  117. package/build/src/database/drivers/mysql/sql/drop-table.js +3 -2
  118. package/build/src/database/drivers/mysql/sql/insert.js +3 -2
  119. package/build/src/database/drivers/mysql/sql/update.js +29 -24
  120. package/build/src/database/drivers/mysql/sql/upsert.js +10 -8
  121. package/build/src/database/drivers/mysql/structure-sql.js +88 -79
  122. package/build/src/database/drivers/mysql/table.js +98 -83
  123. package/build/src/database/drivers/pgsql/column.js +72 -56
  124. package/build/src/database/drivers/pgsql/columns-index.js +3 -2
  125. package/build/src/database/drivers/pgsql/foreign-key.js +9 -8
  126. package/build/src/database/drivers/pgsql/index.js +438 -377
  127. package/build/src/database/drivers/pgsql/options.js +28 -25
  128. package/build/src/database/drivers/pgsql/query-parser.js +3 -2
  129. package/build/src/database/drivers/pgsql/sql/alter-table.js +3 -2
  130. package/build/src/database/drivers/pgsql/sql/create-database.js +23 -19
  131. package/build/src/database/drivers/pgsql/sql/create-index.js +3 -2
  132. package/build/src/database/drivers/pgsql/sql/create-table.js +3 -2
  133. package/build/src/database/drivers/pgsql/sql/delete.js +17 -14
  134. package/build/src/database/drivers/pgsql/sql/drop-database.js +3 -2
  135. package/build/src/database/drivers/pgsql/sql/drop-table.js +3 -2
  136. package/build/src/database/drivers/pgsql/sql/insert.js +3 -2
  137. package/build/src/database/drivers/pgsql/sql/update.js +29 -24
  138. package/build/src/database/drivers/pgsql/sql/upsert.js +11 -9
  139. package/build/src/database/drivers/pgsql/structure-sql.js +120 -108
  140. package/build/src/database/drivers/pgsql/table.js +77 -60
  141. package/build/src/database/drivers/sqlite/base.js +478 -405
  142. package/build/src/database/drivers/sqlite/column.js +69 -54
  143. package/build/src/database/drivers/sqlite/columns-index.js +27 -22
  144. package/build/src/database/drivers/sqlite/connection-sql-js.js +42 -35
  145. package/build/src/database/drivers/sqlite/foreign-key.js +21 -18
  146. package/build/src/database/drivers/sqlite/index.js +373 -330
  147. package/build/src/database/drivers/sqlite/index.native.js +64 -55
  148. package/build/src/database/drivers/sqlite/index.web.js +87 -69
  149. package/build/src/database/drivers/sqlite/options.js +28 -25
  150. package/build/src/database/drivers/sqlite/query-parser.js +3 -2
  151. package/build/src/database/drivers/sqlite/query.js +24 -21
  152. package/build/src/database/drivers/sqlite/query.native.js +25 -20
  153. package/build/src/database/drivers/sqlite/query.web.js +37 -30
  154. package/build/src/database/drivers/sqlite/sql/alter-table.js +179 -159
  155. package/build/src/database/drivers/sqlite/sql/create-index.js +3 -2
  156. package/build/src/database/drivers/sqlite/sql/create-table.js +3 -2
  157. package/build/src/database/drivers/sqlite/sql/delete.js +22 -17
  158. package/build/src/database/drivers/sqlite/sql/drop-table.js +3 -2
  159. package/build/src/database/drivers/sqlite/sql/insert.js +3 -2
  160. package/build/src/database/drivers/sqlite/sql/update.js +29 -24
  161. package/build/src/database/drivers/sqlite/sql/upsert.js +11 -9
  162. package/build/src/database/drivers/sqlite/structure-sql.js +52 -49
  163. package/build/src/database/drivers/sqlite/table-rebuilder.js +75 -62
  164. package/build/src/database/drivers/sqlite/table.js +125 -102
  165. package/build/src/database/drivers/structure-sql/utils.js +17 -14
  166. package/build/src/database/handler.js +10 -9
  167. package/build/src/database/initializer-from-require-context.js +87 -76
  168. package/build/src/database/migration/index.js +395 -332
  169. package/build/src/database/migrator/files-finder.js +50 -40
  170. package/build/src/database/migrator/types.js +30 -2
  171. package/build/src/database/migrator.js +526 -454
  172. package/build/src/database/pool/async-tracked-multi-connection.js +1147 -997
  173. package/build/src/database/pool/base-methods-forward.js +43 -40
  174. package/build/src/database/pool/base.js +343 -298
  175. package/build/src/database/pool/single-multi-use.js +110 -93
  176. package/build/src/database/query/alter-table-base.js +99 -84
  177. package/build/src/database/query/base.js +46 -39
  178. package/build/src/database/query/create-database-base.js +30 -25
  179. package/build/src/database/query/create-index-base.js +94 -75
  180. package/build/src/database/query/create-table-base.js +193 -151
  181. package/build/src/database/query/delete-base.js +16 -14
  182. package/build/src/database/query/drop-database-base.js +28 -23
  183. package/build/src/database/query/drop-table-base.js +53 -42
  184. package/build/src/database/query/from-base.js +33 -30
  185. package/build/src/database/query/from-plain.js +13 -11
  186. package/build/src/database/query/from-table.js +15 -13
  187. package/build/src/database/query/index.js +472 -410
  188. package/build/src/database/query/insert-base.js +164 -143
  189. package/build/src/database/query/join-base.js +40 -35
  190. package/build/src/database/query/join-object.js +153 -128
  191. package/build/src/database/query/join-plain.js +15 -13
  192. package/build/src/database/query/join-tracker.js +90 -76
  193. package/build/src/database/query/model-class-query.js +1370 -1134
  194. package/build/src/database/query/order-base.js +30 -27
  195. package/build/src/database/query/order-column.js +53 -44
  196. package/build/src/database/query/order-plain.js +24 -20
  197. package/build/src/database/query/preloader/belongs-to.js +258 -210
  198. package/build/src/database/query/preloader/ensure-model-class-initialized.js +9 -8
  199. package/build/src/database/query/preloader/has-many.js +301 -240
  200. package/build/src/database/query/preloader/has-one.js +117 -91
  201. package/build/src/database/query/preloader/selection.js +129 -117
  202. package/build/src/database/query/preloader.js +185 -160
  203. package/build/src/database/query/query-data.js +201 -157
  204. package/build/src/database/query/select-base.js +27 -25
  205. package/build/src/database/query/select-plain.js +15 -13
  206. package/build/src/database/query/select-table-and-column.js +25 -21
  207. package/build/src/database/query/update-base.js +38 -35
  208. package/build/src/database/query/upsert-base.js +100 -93
  209. package/build/src/database/query/where-base.js +35 -32
  210. package/build/src/database/query/where-combinator.d.ts.map +1 -1
  211. package/build/src/database/query/where-combinator.js +28 -26
  212. package/build/src/database/query/where-hash.js +68 -61
  213. package/build/src/database/query/where-model-class-hash.js +469 -414
  214. package/build/src/database/query/where-not.js +20 -18
  215. package/build/src/database/query/where-plain.js +17 -15
  216. package/build/src/database/query/with-count.js +159 -125
  217. package/build/src/database/query-parser/base-query-parser.js +37 -32
  218. package/build/src/database/query-parser/from-parser.js +45 -36
  219. package/build/src/database/query-parser/group-parser.js +50 -42
  220. package/build/src/database/query-parser/joins-parser.js +33 -28
  221. package/build/src/database/query-parser/limit-parser.js +70 -67
  222. package/build/src/database/query-parser/options.js +82 -75
  223. package/build/src/database/query-parser/order-parser.js +40 -36
  224. package/build/src/database/query-parser/select-parser.js +60 -49
  225. package/build/src/database/query-parser/where-parser.js +41 -36
  226. package/build/src/database/record/acts-as-list.js +273 -235
  227. package/build/src/database/record/attachments/download.js +45 -44
  228. package/build/src/database/record/attachments/handle.js +161 -141
  229. package/build/src/database/record/attachments/normalize-input.js +138 -128
  230. package/build/src/database/record/attachments/storage-drivers/filesystem.js +91 -77
  231. package/build/src/database/record/attachments/storage-drivers/native.js +121 -112
  232. package/build/src/database/record/attachments/storage-drivers/s3.js +208 -177
  233. package/build/src/database/record/attachments/store.d.ts +1 -1
  234. package/build/src/database/record/attachments/store.d.ts.map +1 -1
  235. package/build/src/database/record/attachments/store.js +540 -468
  236. package/build/src/database/record/index.d.ts +17 -15
  237. package/build/src/database/record/index.d.ts.map +1 -1
  238. package/build/src/database/record/index.js +3894 -3361
  239. package/build/src/database/record/instance-relationships/base.js +268 -234
  240. package/build/src/database/record/instance-relationships/belongs-to.js +73 -58
  241. package/build/src/database/record/instance-relationships/has-many.js +264 -225
  242. package/build/src/database/record/instance-relationships/has-one.js +105 -85
  243. package/build/src/database/record/record-not-found-error.js +2 -3
  244. package/build/src/database/record/relationships/base.d.ts +2 -2
  245. package/build/src/database/record/relationships/base.d.ts.map +1 -1
  246. package/build/src/database/record/relationships/base.js +167 -145
  247. package/build/src/database/record/relationships/belongs-to.js +51 -44
  248. package/build/src/database/record/relationships/has-many.js +40 -32
  249. package/build/src/database/record/relationships/has-one.js +40 -32
  250. package/build/src/database/record/state-machine.js +208 -156
  251. package/build/src/database/record/user-module.js +38 -32
  252. package/build/src/database/record/validators/base.js +24 -22
  253. package/build/src/database/record/validators/format.js +46 -36
  254. package/build/src/database/record/validators/presence.js +20 -18
  255. package/build/src/database/record/validators/uniqueness.js +117 -99
  256. package/build/src/database/table-data/index.js +231 -199
  257. package/build/src/database/table-data/table-column.js +382 -338
  258. package/build/src/database/table-data/table-foreign-key.js +66 -57
  259. package/build/src/database/table-data/table-index.js +36 -29
  260. package/build/src/database/table-data/table-reference.js +10 -10
  261. package/build/src/database/use-database.js +40 -32
  262. package/build/src/environment-handlers/base.js +544 -484
  263. package/build/src/environment-handlers/browser.js +294 -241
  264. package/build/src/environment-handlers/node/cli/commands/background-jobs-main.js +19 -16
  265. package/build/src/environment-handlers/node/cli/commands/background-jobs-runner.js +21 -18
  266. package/build/src/environment-handlers/node/cli/commands/background-jobs-worker.js +29 -22
  267. package/build/src/environment-handlers/node/cli/commands/beacon.js +19 -16
  268. package/build/src/environment-handlers/node/cli/commands/cli-command-context.js +15 -14
  269. package/build/src/environment-handlers/node/cli/commands/console.js +120 -99
  270. package/build/src/environment-handlers/node/cli/commands/db/schema/dump.js +39 -34
  271. package/build/src/environment-handlers/node/cli/commands/db/schema/load.js +63 -57
  272. package/build/src/environment-handlers/node/cli/commands/db/seed.js +63 -51
  273. package/build/src/environment-handlers/node/cli/commands/destroy/migration.js +40 -32
  274. package/build/src/environment-handlers/node/cli/commands/generate/base-models.d.ts.map +1 -1
  275. package/build/src/environment-handlers/node/cli/commands/generate/base-models.js +353 -298
  276. package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +844 -729
  277. package/build/src/environment-handlers/node/cli/commands/generate/migration.js +38 -34
  278. package/build/src/environment-handlers/node/cli/commands/generate/model.js +38 -34
  279. package/build/src/environment-handlers/node/cli/commands/init.js +61 -56
  280. package/build/src/environment-handlers/node/cli/commands/routes.js +59 -51
  281. package/build/src/environment-handlers/node/cli/commands/run-script.js +68 -54
  282. package/build/src/environment-handlers/node/cli/commands/runner.js +74 -56
  283. package/build/src/environment-handlers/node/cli/commands/server.js +106 -93
  284. package/build/src/environment-handlers/node/cli/commands/test.js +113 -97
  285. package/build/src/environment-handlers/node.js +874 -753
  286. package/build/src/error-logger.js +21 -22
  287. package/build/src/frontend-model-controller.d.ts +6 -6
  288. package/build/src/frontend-model-controller.d.ts.map +1 -1
  289. package/build/src/frontend-model-controller.js +3288 -2788
  290. package/build/src/frontend-model-resource/base-resource.d.ts +18 -17
  291. package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
  292. package/build/src/frontend-model-resource/base-resource.js +869 -759
  293. package/build/src/frontend-models/base.d.ts +19 -12
  294. package/build/src/frontend-models/base.d.ts.map +1 -1
  295. package/build/src/frontend-models/base.js +3602 -3114
  296. package/build/src/frontend-models/clear-pending-debounced-callback.js +8 -7
  297. package/build/src/frontend-models/event-hook-models.js +21 -16
  298. package/build/src/frontend-models/model-registry.js +11 -9
  299. package/build/src/frontend-models/outgoing-event-buffer.js +17 -10
  300. package/build/src/frontend-models/preloader.d.ts +6 -6
  301. package/build/src/frontend-models/preloader.d.ts.map +1 -1
  302. package/build/src/frontend-models/preloader.js +149 -131
  303. package/build/src/frontend-models/query.d.ts.map +1 -1
  304. package/build/src/frontend-models/query.js +1855 -1560
  305. package/build/src/frontend-models/resource-config-validation.js +37 -27
  306. package/build/src/frontend-models/resource-definition.js +288 -234
  307. package/build/src/frontend-models/transport-serialization.js +266 -203
  308. package/build/src/frontend-models/use-created-event.js +7 -5
  309. package/build/src/frontend-models/use-destroyed-event.js +93 -80
  310. package/build/src/frontend-models/use-model-class-event.js +91 -79
  311. package/build/src/frontend-models/use-updated-event.js +97 -84
  312. package/build/src/frontend-models/websocket-channel.js +441 -381
  313. package/build/src/frontend-models/websocket-publishers.js +173 -140
  314. package/build/src/http-client/header.js +14 -13
  315. package/build/src/http-client/index.js +132 -116
  316. package/build/src/http-client/request.js +87 -71
  317. package/build/src/http-client/response.js +140 -122
  318. package/build/src/http-client/websocket-client.js +17 -15
  319. package/build/src/http-server/client/index.js +465 -409
  320. package/build/src/http-server/client/params-to-object.js +135 -124
  321. package/build/src/http-server/client/request-buffer/form-data-part.js +132 -111
  322. package/build/src/http-server/client/request-buffer/header.js +16 -15
  323. package/build/src/http-server/client/request-buffer/index.js +506 -446
  324. package/build/src/http-server/client/request-parser.js +186 -163
  325. package/build/src/http-server/client/request-runner.js +259 -226
  326. package/build/src/http-server/client/request-timing.js +151 -132
  327. package/build/src/http-server/client/request.js +108 -96
  328. package/build/src/http-server/client/response.js +235 -213
  329. package/build/src/http-server/client/uploaded-file/memory-uploaded-file.js +29 -25
  330. package/build/src/http-server/client/uploaded-file/temporary-uploaded-file.js +29 -25
  331. package/build/src/http-server/client/uploaded-file/uploaded-file.js +33 -33
  332. package/build/src/http-server/client/websocket-request.js +137 -114
  333. package/build/src/http-server/client/websocket-session.js +1657 -1452
  334. package/build/src/http-server/cookie.js +236 -216
  335. package/build/src/http-server/development-reloader.js +221 -190
  336. package/build/src/http-server/index.js +525 -451
  337. package/build/src/http-server/remote-address.js +50 -38
  338. package/build/src/http-server/server-client.js +208 -181
  339. package/build/src/http-server/server-lock.js +167 -153
  340. package/build/src/http-server/websocket-channel-subscribers.js +93 -81
  341. package/build/src/http-server/websocket-channel.js +117 -104
  342. package/build/src/http-server/websocket-connection.js +104 -96
  343. package/build/src/http-server/websocket-event-log-store.js +404 -350
  344. package/build/src/http-server/websocket-events-host.js +164 -145
  345. package/build/src/http-server/websocket-events.js +47 -47
  346. package/build/src/http-server/worker-handler/channel-subscriber-dispatch.js +14 -13
  347. package/build/src/http-server/worker-handler/in-process.js +141 -123
  348. package/build/src/http-server/worker-handler/index.js +349 -313
  349. package/build/src/http-server/worker-handler/worker-script.js +5 -4
  350. package/build/src/http-server/worker-handler/worker-thread.js +269 -240
  351. package/build/src/initializer.js +36 -31
  352. package/build/src/jobs/mail-delivery.js +15 -13
  353. package/build/src/logger/base-logger.js +26 -24
  354. package/build/src/logger/console-logger.js +23 -21
  355. package/build/src/logger/file-logger.js +31 -29
  356. package/build/src/logger/outputs/array-output.js +42 -37
  357. package/build/src/logger/outputs/console-output.js +24 -20
  358. package/build/src/logger/outputs/file-output.js +48 -43
  359. package/build/src/logger/outputs/stdout-output.js +48 -39
  360. package/build/src/logger.js +394 -338
  361. package/build/src/mailer/backends/smtp.js +163 -134
  362. package/build/src/mailer/base.js +251 -211
  363. package/build/src/mailer/delivery.js +64 -56
  364. package/build/src/mailer/index.js +22 -4
  365. package/build/src/mailer.js +13 -4
  366. package/build/src/plugins/sqljs-wasm-route-controller.js +52 -42
  367. package/build/src/plugins/sqljs-wasm-route.js +38 -28
  368. package/build/src/record-payload-values.js +28 -25
  369. package/build/src/routes/app-routes.js +14 -12
  370. package/build/src/routes/base-route.js +130 -112
  371. package/build/src/routes/basic-route.js +102 -83
  372. package/build/src/routes/built-in/debug/controller.js +10 -10
  373. package/build/src/routes/built-in/errors/controller.js +5 -5
  374. package/build/src/routes/get-route.js +63 -50
  375. package/build/src/routes/hooks/frontend-model-command-route-hook.js +80 -66
  376. package/build/src/routes/index.js +43 -36
  377. package/build/src/routes/namespace-route.js +47 -38
  378. package/build/src/routes/plugin-routes.js +124 -107
  379. package/build/src/routes/post-route.js +62 -51
  380. package/build/src/routes/resolver.js +494 -422
  381. package/build/src/routes/resource-route.js +143 -124
  382. package/build/src/routes/root-route.js +8 -7
  383. package/build/src/testing/base-expect.js +14 -13
  384. package/build/src/testing/browser-frontend-model-event-hook-scenarios.js +405 -329
  385. package/build/src/testing/browser-test-app.js +29 -23
  386. package/build/src/testing/expect-to-change.js +50 -41
  387. package/build/src/testing/expect-utils.js +184 -139
  388. package/build/src/testing/expect.js +731 -638
  389. package/build/src/testing/request-client.js +85 -70
  390. package/build/src/testing/test-files-finder.js +339 -285
  391. package/build/src/testing/test-filter-parser.js +155 -124
  392. package/build/src/testing/test-runner.js +1020 -883
  393. package/build/src/testing/test-suite-splitter.js +142 -114
  394. package/build/src/testing/test.js +256 -216
  395. package/build/src/utils/backtrace-cleaner-node.js +69 -62
  396. package/build/src/utils/backtrace-cleaner.js +216 -188
  397. package/build/src/utils/ensure-error.js +7 -7
  398. package/build/src/utils/event-emitter.js +6 -4
  399. package/build/src/utils/file-exists.js +10 -9
  400. package/build/src/utils/format-value.js +76 -67
  401. package/build/src/utils/model-scope.js +31 -27
  402. package/build/src/utils/nest-callbacks.js +13 -10
  403. package/build/src/utils/plain-object.js +6 -5
  404. package/build/src/utils/ransack.d.ts.map +1 -1
  405. package/build/src/utils/ransack.js +563 -449
  406. package/build/src/utils/rest-args-error.js +6 -5
  407. package/build/src/utils/singularize-model-name.js +11 -9
  408. package/build/src/utils/split-sql-statements.js +79 -68
  409. package/build/src/utils/to-import-specifier.js +30 -24
  410. package/build/src/utils/with-tracked-stack-async-hooks.js +74 -60
  411. package/build/src/utils/with-tracked-stack.js +18 -14
  412. package/build/src/velocious-error.js +30 -27
  413. package/index.js +1 -0
  414. package/package.json +10 -4
  415. package/scripts/clean-build.js +8 -0
  416. package/scripts/ensure-bin-executable.js +13 -0
  417. package/scripts/run-tests.js +37 -0
  418. package/scripts/test-browser.js +486 -0
  419. package/src/application.js +229 -0
  420. package/src/authorization/ability.js +329 -0
  421. package/src/authorization/base-resource.js +143 -0
  422. package/src/background-jobs/client.js +50 -0
  423. package/src/background-jobs/cron-expression.js +277 -0
  424. package/src/background-jobs/forked-runner-child.js +86 -0
  425. package/src/background-jobs/job-record.js +13 -0
  426. package/src/background-jobs/job-registry.js +92 -0
  427. package/src/background-jobs/job-runner.js +107 -0
  428. package/src/background-jobs/job.js +77 -0
  429. package/src/background-jobs/json-socket.js +78 -0
  430. package/src/background-jobs/main.js +926 -0
  431. package/src/background-jobs/normalize-error.js +26 -0
  432. package/src/background-jobs/scheduler.js +274 -0
  433. package/src/background-jobs/socket-request.js +68 -0
  434. package/src/background-jobs/status-reporter.js +101 -0
  435. package/src/background-jobs/store.js +994 -0
  436. package/src/background-jobs/types.js +70 -0
  437. package/src/background-jobs/web/authorization.js +89 -0
  438. package/src/background-jobs/web/controller.js +280 -0
  439. package/src/background-jobs/web/index.js +57 -0
  440. package/src/background-jobs/web/path-matcher.js +74 -0
  441. package/src/background-jobs/web/registry.js +49 -0
  442. package/src/background-jobs/worker.js +683 -0
  443. package/src/beacon/client.js +330 -0
  444. package/src/beacon/in-process-broker.js +71 -0
  445. package/src/beacon/in-process-client.js +139 -0
  446. package/src/beacon/server.js +148 -0
  447. package/src/beacon/types.js +55 -0
  448. package/src/cli/base-command.js +67 -0
  449. package/src/cli/browser-cli.js +45 -0
  450. package/src/cli/commands/background-jobs-main.js +7 -0
  451. package/src/cli/commands/background-jobs-runner.js +7 -0
  452. package/src/cli/commands/background-jobs-worker.js +7 -0
  453. package/src/cli/commands/beacon.js +7 -0
  454. package/src/cli/commands/console.js +12 -0
  455. package/src/cli/commands/db/base-command.js +82 -0
  456. package/src/cli/commands/db/create.js +64 -0
  457. package/src/cli/commands/db/drop.js +75 -0
  458. package/src/cli/commands/db/migrate.js +17 -0
  459. package/src/cli/commands/db/reset.js +22 -0
  460. package/src/cli/commands/db/rollback.js +15 -0
  461. package/src/cli/commands/db/schema/dump.js +12 -0
  462. package/src/cli/commands/db/schema/load.js +12 -0
  463. package/src/cli/commands/db/seed.js +12 -0
  464. package/src/cli/commands/db/tenants/check.js +38 -0
  465. package/src/cli/commands/db/tenants/create.js +33 -0
  466. package/src/cli/commands/db/tenants/migrate.js +49 -0
  467. package/src/cli/commands/destroy/migration.js +7 -0
  468. package/src/cli/commands/generate/base-models.js +7 -0
  469. package/src/cli/commands/generate/frontend-models.js +12 -0
  470. package/src/cli/commands/generate/migration.js +7 -0
  471. package/src/cli/commands/generate/model.js +7 -0
  472. package/src/cli/commands/init.js +11 -0
  473. package/src/cli/commands/routes.js +7 -0
  474. package/src/cli/commands/run-script.js +12 -0
  475. package/src/cli/commands/runner.js +12 -0
  476. package/src/cli/commands/server.js +7 -0
  477. package/src/cli/commands/test.js +9 -0
  478. package/src/cli/index.js +152 -0
  479. package/src/cli/tenant-database-command-helper.js +198 -0
  480. package/src/cli/use-browser-cli.js +30 -0
  481. package/src/configuration-resolver.js +65 -0
  482. package/src/configuration-types.js +429 -0
  483. package/src/configuration.js +2590 -0
  484. package/src/controller.js +421 -0
  485. package/src/current-configuration.js +31 -0
  486. package/src/current.js +80 -0
  487. package/src/database/annotations-async-hooks.js +47 -0
  488. package/src/database/annotations.js +40 -0
  489. package/src/database/drivers/base-column.js +182 -0
  490. package/src/database/drivers/base-columns-index.js +81 -0
  491. package/src/database/drivers/base-foreign-key.js +104 -0
  492. package/src/database/drivers/base-table.js +156 -0
  493. package/src/database/drivers/base.js +1609 -0
  494. package/src/database/drivers/mssql/column.js +74 -0
  495. package/src/database/drivers/mssql/columns-index.js +6 -0
  496. package/src/database/drivers/mssql/connect-connection.js +16 -0
  497. package/src/database/drivers/mssql/foreign-key.js +12 -0
  498. package/src/database/drivers/mssql/index.js +590 -0
  499. package/src/database/drivers/mssql/options.js +79 -0
  500. package/src/database/drivers/mssql/query-parser.js +6 -0
  501. package/src/database/drivers/mssql/sql/alter-table.js +4 -0
  502. package/src/database/drivers/mssql/sql/create-database.js +36 -0
  503. package/src/database/drivers/mssql/sql/create-index.js +4 -0
  504. package/src/database/drivers/mssql/sql/create-table.js +4 -0
  505. package/src/database/drivers/mssql/sql/delete.js +19 -0
  506. package/src/database/drivers/mssql/sql/drop-database.js +36 -0
  507. package/src/database/drivers/mssql/sql/drop-table.js +4 -0
  508. package/src/database/drivers/mssql/sql/insert.js +4 -0
  509. package/src/database/drivers/mssql/sql/update.js +31 -0
  510. package/src/database/drivers/mssql/sql/upsert.js +23 -0
  511. package/src/database/drivers/mssql/structure-sql.js +120 -0
  512. package/src/database/drivers/mssql/table.js +145 -0
  513. package/src/database/drivers/mysql/column.js +112 -0
  514. package/src/database/drivers/mysql/columns-index.js +22 -0
  515. package/src/database/drivers/mysql/foreign-key.js +12 -0
  516. package/src/database/drivers/mysql/index.js +473 -0
  517. package/src/database/drivers/mysql/options.js +34 -0
  518. package/src/database/drivers/mysql/query-parser.js +6 -0
  519. package/src/database/drivers/mysql/query.js +37 -0
  520. package/src/database/drivers/mysql/sql/alter-table.js +6 -0
  521. package/src/database/drivers/mysql/sql/create-database.js +39 -0
  522. package/src/database/drivers/mysql/sql/create-index.js +6 -0
  523. package/src/database/drivers/mysql/sql/create-table.js +6 -0
  524. package/src/database/drivers/mysql/sql/delete.js +21 -0
  525. package/src/database/drivers/mysql/sql/drop-database.js +6 -0
  526. package/src/database/drivers/mysql/sql/drop-table.js +6 -0
  527. package/src/database/drivers/mysql/sql/insert.js +6 -0
  528. package/src/database/drivers/mysql/sql/update.js +33 -0
  529. package/src/database/drivers/mysql/sql/upsert.js +13 -0
  530. package/src/database/drivers/mysql/structure-sql.js +93 -0
  531. package/src/database/drivers/mysql/table.js +121 -0
  532. package/src/database/drivers/pgsql/column.js +90 -0
  533. package/src/database/drivers/pgsql/columns-index.js +6 -0
  534. package/src/database/drivers/pgsql/foreign-key.js +12 -0
  535. package/src/database/drivers/pgsql/index.js +441 -0
  536. package/src/database/drivers/pgsql/options.js +32 -0
  537. package/src/database/drivers/pgsql/query-parser.js +6 -0
  538. package/src/database/drivers/pgsql/sql/alter-table.js +6 -0
  539. package/src/database/drivers/pgsql/sql/create-database.js +38 -0
  540. package/src/database/drivers/pgsql/sql/create-index.js +6 -0
  541. package/src/database/drivers/pgsql/sql/create-table.js +6 -0
  542. package/src/database/drivers/pgsql/sql/delete.js +21 -0
  543. package/src/database/drivers/pgsql/sql/drop-database.js +6 -0
  544. package/src/database/drivers/pgsql/sql/drop-table.js +6 -0
  545. package/src/database/drivers/pgsql/sql/insert.js +6 -0
  546. package/src/database/drivers/pgsql/sql/update.js +33 -0
  547. package/src/database/drivers/pgsql/sql/upsert.js +14 -0
  548. package/src/database/drivers/pgsql/structure-sql.js +126 -0
  549. package/src/database/drivers/pgsql/table.js +135 -0
  550. package/src/database/drivers/sqlite/base.js +509 -0
  551. package/src/database/drivers/sqlite/column.js +75 -0
  552. package/src/database/drivers/sqlite/columns-index.js +30 -0
  553. package/src/database/drivers/sqlite/connection-sql-js.js +46 -0
  554. package/src/database/drivers/sqlite/foreign-key.js +24 -0
  555. package/src/database/drivers/sqlite/index.js +394 -0
  556. package/src/database/drivers/sqlite/index.native.js +72 -0
  557. package/src/database/drivers/sqlite/index.web.js +99 -0
  558. package/src/database/drivers/sqlite/options.js +32 -0
  559. package/src/database/drivers/sqlite/query-parser.js +6 -0
  560. package/src/database/drivers/sqlite/query.js +35 -0
  561. package/src/database/drivers/sqlite/query.native.js +35 -0
  562. package/src/database/drivers/sqlite/query.web.js +49 -0
  563. package/src/database/drivers/sqlite/sql/alter-table.js +187 -0
  564. package/src/database/drivers/sqlite/sql/create-index.js +6 -0
  565. package/src/database/drivers/sqlite/sql/create-table.js +6 -0
  566. package/src/database/drivers/sqlite/sql/delete.js +26 -0
  567. package/src/database/drivers/sqlite/sql/drop-table.js +6 -0
  568. package/src/database/drivers/sqlite/sql/insert.js +6 -0
  569. package/src/database/drivers/sqlite/sql/update.js +33 -0
  570. package/src/database/drivers/sqlite/sql/upsert.js +14 -0
  571. package/src/database/drivers/sqlite/structure-sql.js +56 -0
  572. package/src/database/drivers/sqlite/table-rebuilder.js +96 -0
  573. package/src/database/drivers/sqlite/table.js +131 -0
  574. package/src/database/drivers/structure-sql/utils.js +35 -0
  575. package/src/database/handler.js +13 -0
  576. package/src/database/initializer-from-require-context.js +101 -0
  577. package/src/database/migration/index.js +438 -0
  578. package/src/database/migrator/files-finder.js +55 -0
  579. package/src/database/migrator/types.js +31 -0
  580. package/src/database/migrator.js +557 -0
  581. package/src/database/pool/async-tracked-multi-connection.js +1164 -0
  582. package/src/database/pool/base-methods-forward.js +52 -0
  583. package/src/database/pool/base.js +380 -0
  584. package/src/database/pool/single-multi-use.js +118 -0
  585. package/src/database/query/alter-table-base.js +104 -0
  586. package/src/database/query/base.js +49 -0
  587. package/src/database/query/create-database-base.js +42 -0
  588. package/src/database/query/create-index-base.js +117 -0
  589. package/src/database/query/create-table-base.js +205 -0
  590. package/src/database/query/delete-base.js +19 -0
  591. package/src/database/query/drop-database-base.js +38 -0
  592. package/src/database/query/drop-table-base.js +58 -0
  593. package/src/database/query/from-base.js +36 -0
  594. package/src/database/query/from-plain.js +16 -0
  595. package/src/database/query/from-table.js +18 -0
  596. package/src/database/query/index.js +533 -0
  597. package/src/database/query/insert-base.js +172 -0
  598. package/src/database/query/join-base.js +43 -0
  599. package/src/database/query/join-object.js +167 -0
  600. package/src/database/query/join-plain.js +18 -0
  601. package/src/database/query/join-tracker.js +93 -0
  602. package/src/database/query/model-class-query.js +1577 -0
  603. package/src/database/query/order-base.js +33 -0
  604. package/src/database/query/order-column.js +77 -0
  605. package/src/database/query/order-plain.js +28 -0
  606. package/src/database/query/preloader/belongs-to.js +267 -0
  607. package/src/database/query/preloader/ensure-model-class-initialized.js +18 -0
  608. package/src/database/query/preloader/has-many.js +316 -0
  609. package/src/database/query/preloader/has-one.js +123 -0
  610. package/src/database/query/preloader/selection.js +152 -0
  611. package/src/database/query/preloader.js +201 -0
  612. package/src/database/query/query-data.js +305 -0
  613. package/src/database/query/select-base.js +30 -0
  614. package/src/database/query/select-plain.js +18 -0
  615. package/src/database/query/select-table-and-column.js +28 -0
  616. package/src/database/query/update-base.js +41 -0
  617. package/src/database/query/upsert-base.js +103 -0
  618. package/src/database/query/where-base.js +38 -0
  619. package/src/database/query/where-combinator.js +31 -0
  620. package/src/database/query/where-hash.js +77 -0
  621. package/src/database/query/where-model-class-hash.js +505 -0
  622. package/src/database/query/where-not.js +23 -0
  623. package/src/database/query/where-plain.js +20 -0
  624. package/src/database/query/with-count.js +219 -0
  625. package/src/database/query-parser/base-query-parser.js +40 -0
  626. package/src/database/query-parser/from-parser.js +49 -0
  627. package/src/database/query-parser/group-parser.js +55 -0
  628. package/src/database/query-parser/joins-parser.js +37 -0
  629. package/src/database/query-parser/limit-parser.js +77 -0
  630. package/src/database/query-parser/options.js +94 -0
  631. package/src/database/query-parser/order-parser.js +45 -0
  632. package/src/database/query-parser/select-parser.js +67 -0
  633. package/src/database/query-parser/where-parser.js +46 -0
  634. package/src/database/record/acts-as-list.js +374 -0
  635. package/src/database/record/attachments/download.js +49 -0
  636. package/src/database/record/attachments/handle.js +188 -0
  637. package/src/database/record/attachments/normalize-input.js +213 -0
  638. package/src/database/record/attachments/storage-drivers/filesystem.js +114 -0
  639. package/src/database/record/attachments/storage-drivers/native.js +146 -0
  640. package/src/database/record/attachments/storage-drivers/s3.js +245 -0
  641. package/src/database/record/attachments/store.js +591 -0
  642. package/src/database/record/index.js +3970 -0
  643. package/src/database/record/instance-relationships/base.js +289 -0
  644. package/src/database/record/instance-relationships/belongs-to.js +84 -0
  645. package/src/database/record/instance-relationships/has-many.js +284 -0
  646. package/src/database/record/instance-relationships/has-one.js +117 -0
  647. package/src/database/record/record-not-found-error.js +3 -0
  648. package/src/database/record/relationships/base.js +195 -0
  649. package/src/database/record/relationships/belongs-to.js +57 -0
  650. package/src/database/record/relationships/has-many.js +46 -0
  651. package/src/database/record/relationships/has-one.js +46 -0
  652. package/src/database/record/state-machine.js +278 -0
  653. package/src/database/record/user-module.js +43 -0
  654. package/src/database/record/validators/base.js +27 -0
  655. package/src/database/record/validators/format.js +50 -0
  656. package/src/database/record/validators/presence.js +24 -0
  657. package/src/database/record/validators/uniqueness.js +124 -0
  658. package/src/database/table-data/index.js +241 -0
  659. package/src/database/table-data/table-column.js +416 -0
  660. package/src/database/table-data/table-foreign-key.js +69 -0
  661. package/src/database/table-data/table-index.js +46 -0
  662. package/src/database/table-data/table-reference.js +13 -0
  663. package/src/database/use-database.js +48 -0
  664. package/src/environment-handlers/base.js +561 -0
  665. package/src/environment-handlers/browser.js +338 -0
  666. package/src/environment-handlers/node/cli/commands/background-jobs-main.js +21 -0
  667. package/src/environment-handlers/node/cli/commands/background-jobs-runner.js +24 -0
  668. package/src/environment-handlers/node/cli/commands/background-jobs-worker.js +47 -0
  669. package/src/environment-handlers/node/cli/commands/beacon.js +21 -0
  670. package/src/environment-handlers/node/cli/commands/cli-command-context.js +31 -0
  671. package/src/environment-handlers/node/cli/commands/console.js +149 -0
  672. package/src/environment-handlers/node/cli/commands/db/schema/dump.js +43 -0
  673. package/src/environment-handlers/node/cli/commands/db/schema/load.js +69 -0
  674. package/src/environment-handlers/node/cli/commands/db/seed.js +79 -0
  675. package/src/environment-handlers/node/cli/commands/destroy/migration.js +47 -0
  676. package/src/environment-handlers/node/cli/commands/generate/base-models.js +367 -0
  677. package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +872 -0
  678. package/src/environment-handlers/node/cli/commands/generate/migration.js +45 -0
  679. package/src/environment-handlers/node/cli/commands/generate/model.js +45 -0
  680. package/src/environment-handlers/node/cli/commands/init.js +68 -0
  681. package/src/environment-handlers/node/cli/commands/routes.js +63 -0
  682. package/src/environment-handlers/node/cli/commands/run-script.js +85 -0
  683. package/src/environment-handlers/node/cli/commands/runner.js +84 -0
  684. package/src/environment-handlers/node/cli/commands/server.js +151 -0
  685. package/src/environment-handlers/node/cli/commands/test.js +118 -0
  686. package/src/environment-handlers/node.js +887 -0
  687. package/src/error-logger.js +30 -0
  688. package/src/frontend-model-controller.js +3491 -0
  689. package/src/frontend-model-resource/base-resource.js +935 -0
  690. package/src/frontend-models/base.js +4004 -0
  691. package/src/frontend-models/clear-pending-debounced-callback.js +16 -0
  692. package/src/frontend-models/event-hook-models.js +49 -0
  693. package/src/frontend-models/model-registry.js +28 -0
  694. package/src/frontend-models/outgoing-event-buffer.js +51 -0
  695. package/src/frontend-models/preloader.js +169 -0
  696. package/src/frontend-models/query.js +2245 -0
  697. package/src/frontend-models/resource-config-validation.js +56 -0
  698. package/src/frontend-models/resource-definition.js +399 -0
  699. package/src/frontend-models/transport-serialization.js +369 -0
  700. package/src/frontend-models/use-created-event.js +21 -0
  701. package/src/frontend-models/use-destroyed-event.js +148 -0
  702. package/src/frontend-models/use-model-class-event.js +164 -0
  703. package/src/frontend-models/use-updated-event.js +152 -0
  704. package/src/frontend-models/websocket-channel.js +494 -0
  705. package/src/frontend-models/websocket-publishers.js +224 -0
  706. package/src/http-client/header.js +17 -0
  707. package/src/http-client/index.js +139 -0
  708. package/src/http-client/request.js +94 -0
  709. package/src/http-client/response.js +151 -0
  710. package/src/http-client/websocket-client.js +27 -0
  711. package/src/http-server/client/index.js +507 -0
  712. package/src/http-server/client/params-to-object.js +152 -0
  713. package/src/http-server/client/request-buffer/form-data-part.js +139 -0
  714. package/src/http-server/client/request-buffer/header.js +19 -0
  715. package/src/http-server/client/request-buffer/index.js +535 -0
  716. package/src/http-server/client/request-parser.js +195 -0
  717. package/src/http-server/client/request-runner.js +321 -0
  718. package/src/http-server/client/request-timing.js +171 -0
  719. package/src/http-server/client/request.js +114 -0
  720. package/src/http-server/client/response.js +251 -0
  721. package/src/http-server/client/uploaded-file/memory-uploaded-file.js +32 -0
  722. package/src/http-server/client/uploaded-file/temporary-uploaded-file.js +32 -0
  723. package/src/http-server/client/uploaded-file/uploaded-file.js +36 -0
  724. package/src/http-server/client/websocket-request.js +147 -0
  725. package/src/http-server/client/websocket-session.js +1755 -0
  726. package/src/http-server/cookie.js +245 -0
  727. package/src/http-server/development-reloader.js +240 -0
  728. package/src/http-server/index.js +561 -0
  729. package/src/http-server/remote-address.js +77 -0
  730. package/src/http-server/server-client.js +222 -0
  731. package/src/http-server/server-lock.js +178 -0
  732. package/src/http-server/websocket-channel-subscribers.js +110 -0
  733. package/src/http-server/websocket-channel.js +137 -0
  734. package/src/http-server/websocket-connection.js +118 -0
  735. package/src/http-server/websocket-event-log-store.js +433 -0
  736. package/src/http-server/websocket-events-host.js +170 -0
  737. package/src/http-server/websocket-events.js +50 -0
  738. package/src/http-server/worker-handler/channel-subscriber-dispatch.js +28 -0
  739. package/src/http-server/worker-handler/in-process.js +155 -0
  740. package/src/http-server/worker-handler/index.js +370 -0
  741. package/src/http-server/worker-handler/worker-script.js +6 -0
  742. package/src/http-server/worker-handler/worker-thread.js +286 -0
  743. package/src/initializer.js +39 -0
  744. package/src/jobs/.gitkeep +1 -0
  745. package/src/jobs/mail-delivery.js +22 -0
  746. package/src/logger/base-logger.js +34 -0
  747. package/src/logger/console-logger.js +28 -0
  748. package/src/logger/file-logger.js +36 -0
  749. package/src/logger/outputs/array-output.js +50 -0
  750. package/src/logger/outputs/console-output.js +32 -0
  751. package/src/logger/outputs/file-output.js +55 -0
  752. package/src/logger/outputs/stdout-output.js +64 -0
  753. package/src/logger.js +507 -0
  754. package/src/mailer/backends/smtp.js +197 -0
  755. package/src/mailer/base.js +337 -0
  756. package/src/mailer/delivery.js +70 -0
  757. package/src/mailer/index.js +24 -0
  758. package/src/mailer.js +15 -0
  759. package/src/plugins/sqljs-wasm-route-controller.js +70 -0
  760. package/src/plugins/sqljs-wasm-route.js +71 -0
  761. package/src/record-payload-values.js +83 -0
  762. package/src/routes/app-routes.js +17 -0
  763. package/src/routes/base-route.js +133 -0
  764. package/src/routes/basic-route.js +109 -0
  765. package/src/routes/built-in/debug/controller.js +12 -0
  766. package/src/routes/built-in/errors/controller.js +7 -0
  767. package/src/routes/built-in/errors/not-found.ejs +1 -0
  768. package/src/routes/get-route.js +75 -0
  769. package/src/routes/hooks/frontend-model-command-route-hook.js +100 -0
  770. package/src/routes/index.js +50 -0
  771. package/src/routes/namespace-route.js +51 -0
  772. package/src/routes/plugin-routes.js +141 -0
  773. package/src/routes/post-route.js +74 -0
  774. package/src/routes/resolver.js +535 -0
  775. package/src/routes/resource-route.js +154 -0
  776. package/src/routes/root-route.js +11 -0
  777. package/src/templates/configuration.js +61 -0
  778. package/src/templates/generate-migration.js +11 -0
  779. package/src/templates/generate-model.js +6 -0
  780. package/src/templates/routes.js +11 -0
  781. package/src/testing/base-expect.js +17 -0
  782. package/src/testing/browser-frontend-model-event-hook-scenarios.js +520 -0
  783. package/src/testing/browser-test-app.js +32 -0
  784. package/src/testing/expect-to-change.js +55 -0
  785. package/src/testing/expect-utils.js +269 -0
  786. package/src/testing/expect.js +763 -0
  787. package/src/testing/request-client.js +90 -0
  788. package/src/testing/test-files-finder.js +364 -0
  789. package/src/testing/test-filter-parser.js +198 -0
  790. package/src/testing/test-runner.js +1168 -0
  791. package/src/testing/test-suite-splitter.js +177 -0
  792. package/src/testing/test.js +370 -0
  793. package/src/types/external-modules.d.ts +57 -0
  794. package/src/utils/backtrace-cleaner-node.js +87 -0
  795. package/src/utils/backtrace-cleaner.js +266 -0
  796. package/src/utils/ensure-error.js +15 -0
  797. package/src/utils/event-emitter.js +8 -0
  798. package/src/utils/file-exists.js +18 -0
  799. package/src/utils/format-value.js +101 -0
  800. package/src/utils/model-scope.js +56 -0
  801. package/src/utils/nest-callbacks.js +22 -0
  802. package/src/utils/plain-object.js +14 -0
  803. package/src/utils/ransack.js +859 -0
  804. package/src/utils/rest-args-error.js +14 -0
  805. package/src/utils/singularize-model-name.js +18 -0
  806. package/src/utils/split-sql-statements.js +88 -0
  807. package/src/utils/to-import-specifier.js +53 -0
  808. package/src/utils/with-tracked-stack-async-hooks.js +103 -0
  809. package/src/utils/with-tracked-stack.js +38 -0
  810. package/src/velocious-error.js +34 -0
  811. package/tsconfig.json +16 -0
@@ -0,0 +1,872 @@
1
+ import BaseCommand from "../../../../../cli/base-command.js"
2
+ import fs from "fs/promises"
3
+ import path from "node:path"
4
+ import * as inflection from "inflection"
5
+ import {frontendModelResourceClassFromDefinition, frontendModelResourceConfigurationFromDefinition, frontendModelResourcesForBackendProject} from "../../../../../frontend-models/resource-definition.js"
6
+
7
+
8
+ /** Node CLI command that generates frontend model classes from backend project resource config. */
9
+ export default class DbGenerateFrontendModels extends BaseCommand {
10
+ /**
11
+ * Runs execute.
12
+ * @returns {Promise<void>} - Resolves when files are generated.
13
+ */
14
+ async execute() {
15
+ const configuration = this.getConfiguration()
16
+ const backendProjects = configuration.getBackendProjects()
17
+
18
+ await configuration.initializeModels()
19
+
20
+ const environmentHandler = configuration.getEnvironmentHandler()
21
+
22
+ if (typeof environmentHandler.autoDiscoverResources === "function") {
23
+ await environmentHandler.autoDiscoverResources(configuration)
24
+ }
25
+
26
+ if (!Array.isArray(backendProjects) || backendProjects.length === 0) {
27
+ throw new Error("No backend projects configured. Configure 'backendProjects' in your configuration first")
28
+ }
29
+
30
+ /**
31
+ * Ensured directories.
32
+ @type {Set<string>} */
33
+ const ensuredDirectories = new Set()
34
+ /**
35
+ * Generated model names by directory.
36
+ @type {Map<string, Set<string>>} */
37
+ const generatedModelNamesByDirectory = new Map()
38
+ /**
39
+ * Generated files by directory.
40
+ @type {Map<string, Array<{className: string, fileName: string}>>} */
41
+ const generatedFilesByDirectory = new Map()
42
+
43
+ for (const backendProject of backendProjects) {
44
+ // Canonicalize the output directory so equivalent spellings (a trailing
45
+ // slash, `.`/`..` segments, duplicate separators, relative vs absolute)
46
+ // resolve to a single key. Otherwise the per-directory maps below treat
47
+ // them as different directories, duplicate class names slip past detection,
48
+ // and the split buckets write incomplete index.js/setup.js for files that
49
+ // actually land in the same directory on disk.
50
+ const frontendModelsDir = path.resolve(this.frontendModelsDirectoryForBackendProject(backendProject))
51
+ const importPath = this.importPathForFrontendModelsDirectory(frontendModelsDir)
52
+
53
+ if (!ensuredDirectories.has(frontendModelsDir)) {
54
+ await fs.mkdir(frontendModelsDir, {recursive: true})
55
+ ensuredDirectories.add(frontendModelsDir)
56
+ }
57
+
58
+ if (!generatedFilesByDirectory.has(frontendModelsDir)) {
59
+ generatedFilesByDirectory.set(frontendModelsDir, [])
60
+ }
61
+
62
+ if (!generatedModelNamesByDirectory.has(frontendModelsDir)) {
63
+ generatedModelNamesByDirectory.set(frontendModelsDir, new Set())
64
+ }
65
+
66
+ const generatedFiles = generatedFilesByDirectory.get(frontendModelsDir)
67
+ const generatedModelNames = generatedModelNamesByDirectory.get(frontendModelsDir)
68
+
69
+ if (!generatedFiles) throw new Error(`Generated files list missing for ${frontendModelsDir}`)
70
+ if (!generatedModelNames) throw new Error(`Generated model names set missing for ${frontendModelsDir}`)
71
+ const resources = this.resourcesForBackendProject(backendProject)
72
+ const availableFrontendModelClassNames = this.availableFrontendModelClassNames(resources)
73
+
74
+ for (const modelClassName in resources) {
75
+ const modelConfig = frontendModelResourceConfigurationFromDefinition(resources[modelClassName])
76
+ const className = inflection.camelize(modelClassName.replaceAll("-", "_"))
77
+ const fileName = `${inflection.dasherize(inflection.underscore(className))}.js`
78
+ const filePath = `${frontendModelsDir}/${fileName}`
79
+
80
+ if (!modelConfig) {
81
+ throw new Error(`Invalid frontend model resource definition for '${className}'`)
82
+ }
83
+
84
+ this.validateModelConfig({availableFrontendModelClassNames, className, modelConfig, resourceClass: frontendModelResourceClassFromDefinition(resources[modelClassName])})
85
+
86
+ if (generatedModelNames.has(className)) {
87
+ throw new Error(`Duplicate frontend model definition for '${className}'`)
88
+ }
89
+
90
+ generatedModelNames.add(className)
91
+
92
+ const fileContent = this.buildModelFileContent({
93
+ className,
94
+ importPath,
95
+ modelClass: configuration.getModelClasses()[className],
96
+ modelConfig,
97
+ resourceClass: frontendModelResourceClassFromDefinition(resources[modelClassName])
98
+ })
99
+
100
+ await fs.writeFile(filePath, fileContent)
101
+ generatedFiles.push({className, fileName})
102
+
103
+ console.log(`create src/frontend-models/${fileName}`)
104
+ }
105
+ }
106
+
107
+ for (const [frontendModelsDir, generatedFiles] of generatedFilesByDirectory) {
108
+ const indexContent = this.buildIndexFileContent(generatedFiles)
109
+
110
+ await fs.writeFile(`${frontendModelsDir}/index.js`, indexContent)
111
+
112
+ console.log("create src/frontend-models/index.js")
113
+
114
+ const setupContent = this.buildSetupFileContent(generatedFiles)
115
+
116
+ await fs.writeFile(`${frontendModelsDir}/setup.js`, setupContent)
117
+
118
+ console.log("create src/frontend-models/setup.js")
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Runs validate model config.
124
+ * @param {object} args - Arguments.
125
+ * @param {Set<string>} args.availableFrontendModelClassNames - Available frontend model class names in backend project.
126
+ * @param {string} args.className - Model class name.
127
+ * @param {Record<string, ?>} args.modelConfig - Model configuration.
128
+ * @param {typeof import("../../../../../frontend-model-resource/base-resource.js").default | null} [args.resourceClass]
129
+ * @returns {void} - No return value.
130
+ */
131
+ validateModelConfig({availableFrontendModelClassNames, className, modelConfig, resourceClass}) {
132
+ const abilities = modelConfig.abilities
133
+
134
+ if (!abilities || typeof abilities !== "object") {
135
+ throw new Error(`Model '${className}' is missing required 'abilities' config`)
136
+ }
137
+
138
+ const readActions = ["index", "find"]
139
+
140
+ for (const action of readActions) {
141
+ const abilityAction = abilities[action]
142
+
143
+ if (typeof abilityAction !== "string" || abilityAction.length < 1) {
144
+ throw new Error(`Model '${className}' is missing required abilities.${action} config`)
145
+ }
146
+ }
147
+
148
+ const relationships = modelConfig.relationships
149
+
150
+ if (relationships === undefined) return
151
+
152
+ const normalizedRelationships = this.relationshipsForModel({className, modelConfig, resourceClass})
153
+
154
+ for (const relationship of normalizedRelationships) {
155
+ if (!availableFrontendModelClassNames.has(relationship.targetClassName)) {
156
+ throw new Error(`Model '${className}' relationship '${relationship.relationshipName}' references '${relationship.targetClassName}', but no frontend model resource exists for that target in this backend project`)
157
+ }
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Runs resources for backend project.
163
+ * @param {import("../../../../../configuration-types.js").BackendProjectConfiguration} backendProject - Backend project config.
164
+ * @returns {Record<string, import("../../../../../configuration-types.js").FrontendModelResourceDefinition>} - Resource definitions keyed by model class name.
165
+ */
166
+ resourcesForBackendProject(backendProject) {
167
+ return frontendModelResourcesForBackendProject(backendProject)
168
+ }
169
+
170
+ /**
171
+ * Runs available frontend model class names.
172
+ * @param {Record<string, ?>} resources - Resource configuration keyed by model name.
173
+ * @returns {Set<string>} - Available frontend model class names.
174
+ */
175
+ availableFrontendModelClassNames(resources) {
176
+ /**
177
+ * Class names.
178
+ @type {Set<string>} */
179
+ const classNames = new Set()
180
+
181
+ for (const resourceModelName in resources) {
182
+ classNames.add(inflection.camelize(resourceModelName.replaceAll("-", "_")))
183
+ }
184
+
185
+ return classNames
186
+ }
187
+
188
+ /**
189
+ * Runs frontend models directory for backend project.
190
+ * @param {{frontendModelsOutputPath?: string}} backendProject - Backend project config.
191
+ * @returns {string} - Absolute frontend models output directory.
192
+ */
193
+ frontendModelsDirectoryForBackendProject(backendProject) {
194
+ const outputPath = backendProject.frontendModelsOutputPath || this.directory()
195
+
196
+ return `${outputPath}/src/frontend-models`
197
+ }
198
+
199
+ /**
200
+ * Runs import path for frontend models directory.
201
+ * @param {string} frontendModelsDir - Frontend models output directory.
202
+ * @returns {string} - Base class import path.
203
+ */
204
+ importPathForFrontendModelsDirectory(frontendModelsDir) {
205
+ const devMode = frontendModelsDir.includes("/spec/dummy/src/frontend-models")
206
+
207
+ if (devMode) {
208
+ return "../../../../src/frontend-models/base.js"
209
+ }
210
+
211
+ return "velocious/build/src/frontend-models/base.js"
212
+ }
213
+
214
+ /**
215
+ * Runs build model file content.
216
+ * @param {object} args - Method args.
217
+ * @param {string} args.className - Model class name.
218
+ * @param {string} args.importPath - Base class import path.
219
+ * @param {typeof import("../../../../../database/record/index.js").default | undefined} args.modelClass - Backend model class.
220
+ * @param {Record<string, ?>} args.modelConfig - Model configuration.
221
+ * @param {typeof import("../../../../../frontend-model-resource/base-resource.js").default | null} [args.resourceClass]
222
+ * @returns {string} - Generated file content.
223
+ */
224
+ buildModelFileContent({className, importPath, modelClass, modelConfig, resourceClass}) {
225
+ const attributes = this.attributeDefinitionsForModel({modelClass, modelConfig})
226
+ const relationships = this.relationshipsForModel({className, modelConfig, resourceClass})
227
+ const attachments = modelConfig.attachments && typeof modelConfig.attachments === "object"
228
+ ? modelConfig.attachments
229
+ : {}
230
+ const attributesTypeName = `${className}Attributes`
231
+ const attributeNames = attributes.map((attribute) => attribute.name)
232
+ const builtInCollectionCommands = {
233
+ create: modelConfig.builtInCollectionCommands.create || "create",
234
+ index: modelConfig.builtInCollectionCommands.index || "index"
235
+ }
236
+ const builtInMemberCommands = {
237
+ attach: modelConfig.builtInMemberCommands.attach || "attach",
238
+ destroy: modelConfig.builtInMemberCommands.destroy || "destroy",
239
+ download: modelConfig.builtInMemberCommands.download || "download",
240
+ find: modelConfig.builtInMemberCommands.find || "find",
241
+ update: modelConfig.builtInMemberCommands.update || "update",
242
+ url: modelConfig.builtInMemberCommands.url || "url"
243
+ }
244
+ const collectionCommands = modelConfig.collectionCommands
245
+ const memberCommands = modelConfig.memberCommands
246
+ const builtInCollectionCommandsAreDefault = builtInCollectionCommands.create === "create" && builtInCollectionCommands.index === "index"
247
+ const builtInMemberCommandsAreDefault = builtInMemberCommands.attach === "attach"
248
+ && builtInMemberCommands.destroy === "destroy"
249
+ && builtInMemberCommands.download === "download"
250
+ && builtInMemberCommands.find === "find"
251
+ && builtInMemberCommands.update === "update"
252
+ && builtInMemberCommands.url === "url"
253
+
254
+ let fileContent = ""
255
+
256
+ fileContent += `import FrontendModelBase from "${importPath}"\n`
257
+
258
+ fileContent += "\n"
259
+ fileContent += "/**\n"
260
+ fileContent += ` * Frontend model resource config.\n`
261
+ fileContent += ` * @typedef {import("${importPath}").FrontendModelResourceConfig} FrontendModelResourceConfig\n`
262
+ fileContent += " */\n"
263
+ fileContent += "\n"
264
+ fileContent += "/**\n"
265
+ fileContent += ` * ${attributesTypeName} type.\n`
266
+ fileContent += ` * @typedef {object} ${attributesTypeName}\n`
267
+ for (const attribute of attributes) {
268
+ fileContent += ` * @property {${attribute.jsDocType}} ${attribute.name} - Attribute value.\n`
269
+ }
270
+ fileContent += " */\n"
271
+ fileContent += `/** Frontend model for ${className}. */\n`
272
+ fileContent += `export default class ${className} extends FrontendModelBase {\n`
273
+ fileContent += " /** @returns {FrontendModelResourceConfig} - Resource config. */\n"
274
+ fileContent += " static resourceConfig() {\n"
275
+ fileContent += " return {\n"
276
+ fileContent += ` modelName: ${JSON.stringify(className)},\n`
277
+ if (Object.keys(attachments).length > 0) {
278
+ fileContent += " attachments: {\n"
279
+ for (const [attachmentName, attachmentConfig] of Object.entries(attachments)) {
280
+ const attachmentType = attachmentConfig && typeof attachmentConfig === "object" && attachmentConfig.type === "hasMany"
281
+ ? "hasMany"
282
+ : "hasOne"
283
+
284
+ fileContent += ` ${attachmentName}: {type: ${JSON.stringify(attachmentType)}},\n`
285
+ }
286
+ fileContent += " },\n"
287
+ }
288
+ fileContent += this.formattedArrayProperty({
289
+ indent: " ",
290
+ propertyName: "attributes",
291
+ values: attributeNames
292
+ })
293
+ if (!builtInCollectionCommandsAreDefault) {
294
+ fileContent += this.formattedObjectProperty({
295
+ filterDefaultValues: {create: "create", index: "index"},
296
+ indent: " ",
297
+ propertyName: "builtInCollectionCommands",
298
+ values: builtInCollectionCommands
299
+ })
300
+ }
301
+ if (!builtInMemberCommandsAreDefault) {
302
+ fileContent += this.formattedObjectProperty({
303
+ filterDefaultValues: {
304
+ attach: "attach",
305
+ destroy: "destroy",
306
+ download: "download",
307
+ find: "find",
308
+ update: "update",
309
+ url: "url"
310
+ },
311
+ indent: " ",
312
+ propertyName: "builtInMemberCommands",
313
+ values: builtInMemberCommands
314
+ })
315
+ }
316
+ if (Object.keys(collectionCommands).length > 0) {
317
+ fileContent += this.formattedCommandsProperty({
318
+ indent: " ",
319
+ propertyName: "collectionCommands",
320
+ values: collectionCommands
321
+ })
322
+ }
323
+ if (Object.keys(memberCommands).length > 0) {
324
+ fileContent += this.formattedCommandsProperty({
325
+ indent: " ",
326
+ propertyName: "memberCommands",
327
+ values: memberCommands
328
+ })
329
+ }
330
+ if (modelClass && modelClass.primaryKey() !== "id") {
331
+ fileContent += ` primaryKey: ${JSON.stringify(modelClass.primaryKey())},\n`
332
+ }
333
+ const nestedRelationshipNames = this.nestedRelationshipNamesForGenerator(resourceClass || null)
334
+ if (nestedRelationshipNames.length > 0) {
335
+ fileContent += " nestedAttributes: {\n"
336
+ for (const relationshipName of nestedRelationshipNames) {
337
+ fileContent += ` ${relationshipName}: {},\n`
338
+ }
339
+ fileContent += " },\n"
340
+ }
341
+ fileContent += " }\n"
342
+ fileContent += " }\n"
343
+
344
+ if (relationships.length > 0) {
345
+ fileContent += "\n"
346
+ fileContent += " /** @returns {Record<string, {type: \"belongsTo\" | \"hasOne\" | \"hasMany\", autoload?: boolean}>} - Relationship definitions. */\n"
347
+ fileContent += " static relationshipDefinitions() {\n"
348
+ fileContent += " return {\n"
349
+ for (const relationship of relationships) {
350
+ const parts = [`type: ${JSON.stringify(relationship.type)}`]
351
+
352
+ if (relationship.autoload === false) parts.push("autoload: false")
353
+
354
+ fileContent += ` ${relationship.relationshipName}: {${parts.join(", ")}},\n`
355
+ }
356
+ fileContent += " }\n"
357
+ fileContent += " }\n"
358
+
359
+ fileContent += "\n"
360
+ fileContent += " /** @returns {Record<string, string>} - Relationship model class names. */\n"
361
+ fileContent += " static relationshipModelClasses() {\n"
362
+ fileContent += " return {\n"
363
+ for (const relationship of relationships) {
364
+ fileContent += ` ${relationship.relationshipName}: ${JSON.stringify(relationship.targetClassName)},\n`
365
+ }
366
+ fileContent += " }\n"
367
+ fileContent += " }\n"
368
+ }
369
+
370
+ for (const attribute of attributes) {
371
+ const camelizedAttribute = inflection.camelize(attribute.name, true)
372
+ const camelizedAttributeUpper = inflection.camelize(attribute.name)
373
+
374
+ fileContent += "\n"
375
+ fileContent += ` /** @returns {${attributesTypeName}[${JSON.stringify(attribute.name)}]} - Attribute value. */\n`
376
+ fileContent += ` ${camelizedAttribute}() { return this.readAttribute(${JSON.stringify(attribute.name)}) }\n`
377
+
378
+ fileContent += "\n"
379
+ fileContent += " /**\n"
380
+ fileContent += ` * @param {${attributesTypeName}[${JSON.stringify(attribute.name)}]} newValue - New attribute value.\n`
381
+ fileContent += ` * @returns {${attributesTypeName}[${JSON.stringify(attribute.name)}]} - Assigned value.\n`
382
+ fileContent += " */\n"
383
+ fileContent += ` set${camelizedAttributeUpper}(newValue) { return this.setAttribute(${JSON.stringify(attribute.name)}, newValue) }\n`
384
+ }
385
+
386
+ for (const methodName of Object.keys(collectionCommands)) {
387
+ fileContent += "\n"
388
+ fileContent += " /**\n"
389
+ fileContent += ` * Runs ${methodName}.\n`
390
+ fileContent += " * @param {...?} commandArguments - Custom command arguments.\n"
391
+ fileContent += " * @returns {Promise<Record<string, ?>>} - Command response.\n"
392
+ fileContent += " */\n"
393
+ fileContent += ` static async ${methodName}(...commandArguments) {\n`
394
+ fileContent += " return await this.executeCustomCommand({\n"
395
+ fileContent += ` commandName: ${JSON.stringify(collectionCommands[methodName])},\n`
396
+ fileContent += ` commandType: ${JSON.stringify(collectionCommands[methodName])},\n`
397
+ fileContent += ` payload: ${className}.normalizeCustomCommandPayloadArguments(commandArguments),\n`
398
+ fileContent += " resourcePath: this.resourcePath()\n"
399
+ fileContent += " })\n"
400
+ fileContent += " }\n"
401
+ }
402
+
403
+ for (const methodName of Object.keys(memberCommands)) {
404
+ fileContent += "\n"
405
+ fileContent += " /**\n"
406
+ fileContent += ` * Runs ${methodName}.\n`
407
+ fileContent += " * @param {...?} commandArguments - Custom command arguments.\n"
408
+ fileContent += " * @returns {Promise<Record<string, ?>>} - Command response.\n"
409
+ fileContent += " */\n"
410
+ fileContent += ` async ${methodName}(...commandArguments) {\n`
411
+ fileContent += ` return await ${className}.executeCustomCommand({\n`
412
+ fileContent += ` commandName: ${JSON.stringify(memberCommands[methodName])},\n`
413
+ fileContent += ` commandType: ${JSON.stringify(memberCommands[methodName])},\n`
414
+ fileContent += " memberId: this.primaryKeyValue(),\n"
415
+ fileContent += ` payload: ${className}.normalizeCustomCommandPayloadArguments(commandArguments),\n`
416
+ fileContent += ` resourcePath: ${className}.resourcePath()\n`
417
+ fileContent += " })\n"
418
+ fileContent += " }\n"
419
+ }
420
+
421
+ for (const relationship of relationships) {
422
+ const relationshipNameCamelized = inflection.camelize(relationship.relationshipName)
423
+ const targetImportPath = `./${relationship.targetFileName}.js`
424
+
425
+ if (relationship.type == "hasMany") {
426
+ fileContent += "\n"
427
+ fileContent += " /**\n"
428
+ fileContent += ` * Returns ${relationship.relationshipName}.\n`
429
+ fileContent += ` * @returns {import(${JSON.stringify(importPath)}).FrontendModelHasManyRelationship<typeof import(${JSON.stringify(`./${inflection.dasherize(inflection.underscore(className))}.js`)}).default, typeof import(${JSON.stringify(targetImportPath)}).default>} - Relationship helper.\n`
430
+ fileContent += " */\n"
431
+ fileContent += ` ${relationship.relationshipName}() { return /** @type {import(${JSON.stringify(importPath)}).FrontendModelHasManyRelationship<typeof import(${JSON.stringify(`./${inflection.dasherize(inflection.underscore(className))}.js`)}).default, typeof import(${JSON.stringify(targetImportPath)}).default>} */ (this.getRelationshipByName(${JSON.stringify(relationship.relationshipName)})) }\n`
432
+
433
+ fileContent += "\n"
434
+ fileContent += " /**\n"
435
+ fileContent += ` * Returns loaded ${relationship.relationshipName}.\n`
436
+ fileContent += ` * @returns {Array<import(${JSON.stringify(targetImportPath)}).default>} - Loaded related models.\n`
437
+ fileContent += " */\n"
438
+ fileContent += ` ${relationship.relationshipName}Loaded() { return /** @type {Array<import(${JSON.stringify(targetImportPath)}).default>} */ (this.getRelationshipByName(${JSON.stringify(relationship.relationshipName)}).loaded()) }\n`
439
+
440
+ fileContent += "\n"
441
+ fileContent += " /**\n"
442
+ fileContent += ` * Loads ${relationship.relationshipName}.\n`
443
+ fileContent += ` * @returns {Promise<Array<import(${JSON.stringify(targetImportPath)}).default>>} - Loaded related models.\n`
444
+ fileContent += " */\n"
445
+ fileContent += ` async load${relationshipNameCamelized}() { return /** @type {Promise<Array<import(${JSON.stringify(targetImportPath)}).default>>} */ (this.loadRelationship(${JSON.stringify(relationship.relationshipName)})) }\n`
446
+ } else {
447
+ fileContent += "\n"
448
+ fileContent += " /**\n"
449
+ fileContent += ` * Returns ${relationship.relationshipName}.\n`
450
+ fileContent += ` * @returns {import(${JSON.stringify(targetImportPath)}).default | null} - Loaded related model.\n`
451
+ fileContent += " */\n"
452
+ fileContent += ` ${relationship.relationshipName}() { return /** @type {import(${JSON.stringify(targetImportPath)}).default | null} */ (this.getRelationshipByName(${JSON.stringify(relationship.relationshipName)}).loaded()) }\n`
453
+
454
+ fileContent += "\n"
455
+ fileContent += " /**\n"
456
+ fileContent += ` * Builds ${relationship.relationshipName}.\n`
457
+ fileContent += ` * @param {Record<string, ?>} [attributes] - Attributes for the new related model.\n`
458
+ fileContent += ` * @returns {import(${JSON.stringify(targetImportPath)}).default} - Built related model.\n`
459
+ fileContent += " */\n"
460
+ fileContent += ` build${relationshipNameCamelized}(attributes = {}) { return /** @type {import(${JSON.stringify(targetImportPath)}).default} */ (this.getRelationshipByName(${JSON.stringify(relationship.relationshipName)}).build(attributes)) }\n`
461
+
462
+ fileContent += "\n"
463
+ fileContent += " /**\n"
464
+ fileContent += ` * Loads ${relationship.relationshipName}.\n`
465
+ fileContent += ` * @returns {Promise<import(${JSON.stringify(targetImportPath)}).default | null>} - Loaded related model.\n`
466
+ fileContent += " */\n"
467
+ fileContent += ` async load${relationshipNameCamelized}() { return /** @type {Promise<import(${JSON.stringify(targetImportPath)}).default | null>} */ (this.loadRelationship(${JSON.stringify(relationship.relationshipName)})) }\n`
468
+
469
+ fileContent += "\n"
470
+ fileContent += " /**\n"
471
+ fileContent += ` * Returns or loads ${relationship.relationshipName}.\n`
472
+ fileContent += ` * @returns {Promise<import(${JSON.stringify(targetImportPath)}).default | null>} - Loaded related model.\n`
473
+ fileContent += " */\n"
474
+ fileContent += ` async ${relationship.relationshipName}OrLoad() { return /** @type {Promise<import(${JSON.stringify(targetImportPath)}).default | null>} */ (this.relationshipOrLoad(${JSON.stringify(relationship.relationshipName)})) }\n`
475
+
476
+ fileContent += "\n"
477
+ fileContent += " /**\n"
478
+ fileContent += ` * Sets ${relationship.relationshipName}.\n`
479
+ fileContent += ` * @param {import(${JSON.stringify(targetImportPath)}).default | null} model - Related model.\n`
480
+ fileContent += ` * @returns {import(${JSON.stringify(targetImportPath)}).default | null} - Assigned related model.\n`
481
+ fileContent += " */\n"
482
+ fileContent += ` set${relationshipNameCamelized}(model) { return /** @type {import(${JSON.stringify(targetImportPath)}).default | null} */ (this.setRelationship(${JSON.stringify(relationship.relationshipName)}, model)) }\n`
483
+ }
484
+ }
485
+
486
+ fileContent += "}\n"
487
+ fileContent += "\n"
488
+ fileContent += `FrontendModelBase.registerModel(${className})\n`
489
+
490
+ return fileContent
491
+ }
492
+
493
+ /**
494
+ * Runs build index file content.
495
+ * @param {Array<{className: string, fileName: string}>} generatedFiles - Generated model files.
496
+ * @returns {string} - Index file content that imports and re-exports all models.
497
+ */
498
+ buildIndexFileContent(generatedFiles) {
499
+ let content = ""
500
+
501
+ for (const {className, fileName} of generatedFiles) {
502
+ content += `export {default as ${className}} from "./${fileName}"\n`
503
+ }
504
+
505
+ return content
506
+ }
507
+
508
+ /**
509
+ * Runs build setup file content.
510
+ * @param {Array<{className: string, fileName: string}>} generatedFiles - Generated model files.
511
+ * @returns {string} - Setup file content with side-effect imports for model registration.
512
+ */
513
+ buildSetupFileContent(generatedFiles) {
514
+ let content = "// This file is auto-generated by Velocious. Do not edit manually.\n"
515
+
516
+ content += "// Run `velocious g:frontend-models` to regenerate.\n"
517
+
518
+ for (const {fileName} of generatedFiles) {
519
+ content += `import "./${fileName}"\n`
520
+ }
521
+
522
+ return content
523
+ }
524
+
525
+ /**
526
+ * Invokes a backend resource's `permittedParams()` instance method at
527
+ * generation time and extracts the relationship names that accept
528
+ * nested writes (`{fooAttributes: [...]}` entries). The generator
529
+ * emits those names into the frontend model's `resourceConfig()` so
530
+ * the client `save()` walker knows which relationships to ship.
531
+ *
532
+ * Constructed with no controller/ability so resource overrides must
533
+ * support being called without a request context.
534
+ * @param {typeof import("../../../../../frontend-model-resource/base-resource.js").default | null} resourceClass - Resource class.
535
+ * @returns {string[]} - Relationship names that accept nested writes (empty when none).
536
+ */
537
+ nestedRelationshipNamesForGenerator(resourceClass) {
538
+ if (!resourceClass || typeof resourceClass !== "function") return []
539
+
540
+ const prototypeWithMethod = /**
541
+ * Resource prototype.
542
+ * @type {{permittedParams?: (arg?: object) => Array<string | Record<string, ?>>}}
543
+ */ (resourceClass.prototype)
544
+
545
+ if (typeof prototypeWithMethod?.permittedParams !== "function") return []
546
+
547
+ let spec
548
+
549
+ try {
550
+ const instance = new resourceClass({
551
+ ability: undefined,
552
+ context: {},
553
+ locals: {},
554
+ modelClass: resourceClass.ModelClass,
555
+ modelName: resourceClass.ModelClass?.getModelName?.() || resourceClass.name,
556
+ params: {},
557
+ resourceConfiguration: /**
558
+ * Resource configuration.
559
+ * @type {import("../../../../../configuration-types.js").FrontendModelResourceConfiguration}
560
+ */ ({attributes: []})
561
+ })
562
+ spec = instance.permittedParams()
563
+ } catch (error) {
564
+ throw new Error(`Failed to invoke ${resourceClass.name}.permittedParams() while generating frontend models: ${error instanceof Error ? error.message : String(error)}`, {cause: error})
565
+ }
566
+
567
+ if (!Array.isArray(spec)) return []
568
+
569
+ /**
570
+ * Relationship names.
571
+ @type {string[]} */
572
+ const relationshipNames = []
573
+
574
+ for (const entry of spec) {
575
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue
576
+
577
+ for (const key of Object.keys(entry)) {
578
+ if (!key.endsWith("Attributes")) continue
579
+ const name = key.slice(0, -"Attributes".length)
580
+ if (name) relationshipNames.push(name)
581
+ }
582
+ }
583
+
584
+ return relationshipNames
585
+ }
586
+
587
+ /**
588
+ * Runs formatted array property.
589
+ * @param {object} args - Formatting args.
590
+ * @param {string} args.indent - Base indentation.
591
+ * @param {string} args.propertyName - Object property name.
592
+ * @param {string[]} args.values - String values.
593
+ * @returns {string} - Formatted multiline array property.
594
+ */
595
+ formattedArrayProperty({indent, propertyName, values}) {
596
+ let output = `${indent}${propertyName}: [\n`
597
+
598
+ for (const value of values) {
599
+ output += `${indent} ${JSON.stringify(value)},\n`
600
+ }
601
+
602
+ output += `${indent}],\n`
603
+
604
+ return output
605
+ }
606
+
607
+ /**
608
+ * Runs formatted commands property.
609
+ * @param {object} args - Formatting args.
610
+ * @param {string} args.indent - Base indentation.
611
+ * @param {string} args.propertyName - Object property name.
612
+ * @param {Record<string, string>} args.values - Command key-values.
613
+ * @returns {string} - Formatted multiline array property. Always emits
614
+ * the camelCase method-name array form (`memberCommands: ["updateAccess"]`)
615
+ * so the generated config matches the canonical
616
+ * `FrontendModelResourceConfig.{collection,member}Commands: string[]`
617
+ * shape. The runtime derives the command slug from the camelCase
618
+ * method name; consumers never need to write out
619
+ * `{updateAccess: "update-access"}` by hand.
620
+ */
621
+ formattedCommandsProperty({indent, propertyName, values}) {
622
+ return this.formattedArrayProperty({indent, propertyName, values: Object.keys(values)})
623
+ }
624
+
625
+ /**
626
+ * Runs formatted object property.
627
+ * @param {object} args - Formatting args.
628
+ * @param {string} args.indent - Base indentation.
629
+ * @param {string} args.propertyName - Object property name.
630
+ * @param {Record<string, string>} args.values - Object key-values.
631
+ * @param {Record<string, string>} [args.filterDefaultValues] - Default values to omit from output.
632
+ * @returns {string} - Formatted multiline object property.
633
+ */
634
+ formattedObjectProperty({filterDefaultValues, indent, propertyName, values}) {
635
+ let output = `${indent}${propertyName}: {\n`
636
+
637
+ for (const objectKey of Object.keys(values)) {
638
+ if (filterDefaultValues && filterDefaultValues[objectKey] === values[objectKey]) continue
639
+
640
+ output += `${indent} ${objectKey}: ${JSON.stringify(values[objectKey])},\n`
641
+ }
642
+
643
+ output += `${indent}},\n`
644
+
645
+ return output
646
+ }
647
+
648
+ /**
649
+ * Runs attribute definitions for model.
650
+ * @param {object} args - Arguments.
651
+ * @param {typeof import("../../../../../database/record/index.js").default | undefined} args.modelClass - Backend model class.
652
+ * @param {Record<string, ?>} args.modelConfig - Model configuration.
653
+ * @returns {Array<{jsDocType: string, name: string}>} - Attribute definitions.
654
+ */
655
+ attributeDefinitionsForModel({modelClass, modelConfig}) {
656
+ let attributes = modelConfig.attributes
657
+
658
+ // Auto-derive attributes from model columns when not explicitly defined
659
+ if ((!attributes || (Array.isArray(attributes) && attributes.length === 0)) && modelClass) {
660
+ try {
661
+ const columns = modelClass.getColumns()
662
+
663
+ if (Array.isArray(columns)) {
664
+ attributes = columns.map((column) => inflection.camelize(column.getName(), true))
665
+ }
666
+ } catch {
667
+ // Model may not be initialized yet
668
+ }
669
+ }
670
+
671
+ if (Array.isArray(attributes)) {
672
+ return attributes.map((entry) => {
673
+ const attributeName = typeof entry === "string" ? entry : entry.name
674
+
675
+ return {
676
+ jsDocType: this.jsDocTypeForFrontendAttribute({
677
+ attributeConfig: this.frontendAttributeConfigForModelAttribute({attributeName, modelClass})
678
+ }),
679
+ name: attributeName
680
+ }
681
+ })
682
+ }
683
+
684
+ if (!attributes || typeof attributes !== "object") {
685
+ throw new Error(`Expected 'attributes' as array or object but got: ${attributes}`)
686
+ }
687
+
688
+ return Object.keys(attributes).map((attributeName) => {
689
+ const attributeConfig = attributes[attributeName]
690
+
691
+ return {
692
+ jsDocType: this.jsDocTypeForFrontendAttribute({attributeConfig}),
693
+ name: attributeName
694
+ }
695
+ })
696
+ }
697
+
698
+ /**
699
+ * Runs js doc type for frontend attribute.
700
+ * @param {object} args - Arguments.
701
+ * @param {?} args.attributeConfig - Attribute configuration value.
702
+ * @returns {string} - JSDoc type.
703
+ */
704
+ jsDocTypeForFrontendAttribute({attributeConfig}) {
705
+ const jsDocType = this.jsDocTypeForFrontendAttributeBaseType(attributeConfig)
706
+
707
+ if (!this.frontendAttributeCanBeNull(attributeConfig)) {
708
+ return jsDocType
709
+ }
710
+
711
+ return `${jsDocType} | null`
712
+ }
713
+
714
+ /**
715
+ * Runs js doc type for frontend attribute base type.
716
+ * @param {?} attributeConfig - Attribute configuration value.
717
+ * @returns {string} - Non-nullable JSDoc type.
718
+ */
719
+ jsDocTypeForFrontendAttributeBaseType(attributeConfig) {
720
+ if (!attributeConfig || typeof attributeConfig !== "object") {
721
+ return "any"
722
+ }
723
+
724
+ const type = this.frontendAttributeTypeValue(attributeConfig)
725
+
726
+ if (type == "boolean") {
727
+ return "boolean"
728
+ } else if (type == "json" || type == "jsonb") {
729
+ return "Record<string, any>"
730
+ } else if (type && ["blob", "char", "nvarchar", "varchar", "text", "longtext", "uuid", "character varying"].includes(type)) {
731
+ return "string"
732
+ } else if (type && ["bit", "bigint", "decimal", "double", "double precision", "float", "int", "integer", "numeric", "real", "smallint", "tinyint"].includes(type)) {
733
+ return "number"
734
+ } else if (type && ["date", "datetime", "timestamp", "timestamp without time zone", "timestamptz"].includes(type)) {
735
+ return "Date"
736
+ } else {
737
+ return "any"
738
+ }
739
+ }
740
+
741
+ /**
742
+ * Runs frontend attribute can be null.
743
+ * @param {?} attributeConfig - Attribute configuration value.
744
+ * @returns {boolean} - Whether the attribute allows null values.
745
+ */
746
+ frontendAttributeCanBeNull(attributeConfig) {
747
+ if (!attributeConfig || typeof attributeConfig !== "object") {
748
+ return false
749
+ }
750
+
751
+ if (typeof attributeConfig.getNull == "function") {
752
+ return attributeConfig.getNull() === true
753
+ }
754
+
755
+ return attributeConfig.null === true
756
+ }
757
+
758
+ /**
759
+ * Runs frontend attribute type value.
760
+ * @param {?} attributeConfig - Attribute configuration value.
761
+ * @returns {string | null} - Normalized column type.
762
+ */
763
+ frontendAttributeTypeValue(attributeConfig) {
764
+ if (!attributeConfig || typeof attributeConfig !== "object") {
765
+ return null
766
+ }
767
+
768
+ if (typeof attributeConfig.getType == "function") {
769
+ return String(attributeConfig.getType())
770
+ }
771
+
772
+ const typeValue = attributeConfig.type || attributeConfig.columnType || attributeConfig.sqlType || attributeConfig.dataType
773
+
774
+ if (typeof typeValue !== "string") {
775
+ return null
776
+ }
777
+
778
+ return typeValue
779
+ }
780
+
781
+ /**
782
+ * Runs frontend attribute config for model attribute.
783
+ * @param {object} args - Arguments.
784
+ * @param {string} args.attributeName - Frontend model attribute name.
785
+ * @param {typeof import("../../../../../database/record/index.js").default | undefined} args.modelClass - Backend model class.
786
+ * @returns {?} - Attribute config inferred from the backend model when available.
787
+ */
788
+ frontendAttributeConfigForModelAttribute({attributeName, modelClass}) {
789
+ if (!modelClass) {
790
+ return null
791
+ }
792
+
793
+ const columnName = modelClass.getAttributeNameToColumnNameMap()[attributeName]
794
+
795
+ if (!columnName) {
796
+ return null
797
+ }
798
+
799
+ return modelClass.getColumnsHash()[columnName] || null
800
+ }
801
+
802
+ /**
803
+ * Runs relationships for model.
804
+ * @param {object} args - Arguments.
805
+ * @param {string} args.className - Model class name.
806
+ * @param {Record<string, ?>} args.modelConfig - Model configuration.
807
+ * @param {typeof import("../../../../../frontend-model-resource/base-resource.js").default | null} [args.resourceClass]
808
+ * @returns {Array<{autoload: boolean, relationshipName: string, targetClassName: string, targetFileName: string, type: "belongsTo" | "hasOne" | "hasMany"}>} - Relationships.
809
+ */
810
+ relationshipsForModel({className, modelConfig, resourceClass}) {
811
+ const relationships = modelConfig.relationships
812
+
813
+ if (relationships === undefined || relationships === null) {
814
+ return []
815
+ }
816
+
817
+ if (!Array.isArray(relationships)) {
818
+ throw new Error(`Model '${className}' has invalid relationships config — must be an array of relationship names, got ${typeof relationships}`)
819
+ }
820
+
821
+ return relationships.map((relationshipName) => this.inferredRelationshipDefinition({className, relationshipName, resourceClass}))
822
+ }
823
+
824
+ /**
825
+ * Runs inferred relationship definition.
826
+ * @param {object} args - Arguments.
827
+ * @param {string} args.className - Model class name.
828
+ * @param {string} args.relationshipName - Relationship name.
829
+ * @param {typeof import("../../../../../frontend-model-resource/base-resource.js").default | null} [args.resourceClass]
830
+ * @returns {{autoload: boolean, relationshipName: string, targetClassName: string, targetFileName: string, type: "belongsTo" | "hasOne" | "hasMany"}} Inferred relationship definition.
831
+ */
832
+ inferredRelationshipDefinition({className, relationshipName, resourceClass}) {
833
+ const modelClass = resourceClass?.ModelClass || this.getConfiguration().getModelClass(className)
834
+
835
+ if (!modelClass) {
836
+ throw new Error(`Could not find backend model class '${className}' for relationship '${relationshipName}'`)
837
+ }
838
+
839
+ const relationship = modelClass.getRelationshipByName(relationshipName)
840
+ const relationshipType = relationship.getType()
841
+
842
+ if (relationshipType !== "belongsTo" && relationshipType !== "hasOne" && relationshipType !== "hasMany") {
843
+ throw new Error(`Model '${className}' relationship '${relationshipName}' has unsupported type '${relationshipType}'`)
844
+ }
845
+
846
+ let targetClassName
847
+
848
+ try {
849
+ const targetModelClass = relationship.getTargetModelClass()
850
+
851
+ targetClassName = targetModelClass?.getModelName()
852
+ } catch {
853
+ // Model class not registered yet — fall back to className from relationship definition
854
+ }
855
+
856
+ if (!targetClassName) {
857
+ targetClassName = relationship.className
858
+
859
+ if (!targetClassName) {
860
+ throw new Error(`Model '${className}' relationship '${relationshipName}' has no target model class`)
861
+ }
862
+ }
863
+
864
+ return {
865
+ autoload: relationship.getAutoload(),
866
+ relationshipName,
867
+ targetClassName,
868
+ targetFileName: inflection.dasherize(inflection.underscore(targetClassName)),
869
+ type: relationshipType
870
+ }
871
+ }
872
+ }