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
@@ -1,711 +1,815 @@
1
1
  // @ts-check
2
- import AuthorizationBaseResource from "../authorization/base-resource.js";
3
- import * as inflection from "inflection";
2
+
3
+ import AuthorizationBaseResource from "../authorization/base-resource.js"
4
+ import * as inflection from "inflection"
5
+
4
6
  /**
5
7
  * FrontendModelResourceControllerArgs type.
6
8
  * @typedef {object} FrontendModelResourceControllerArgs
7
9
  * @property {import("../controller.js").default} controller - Frontend-model controller instance.
8
10
  * @property {typeof import("../database/record/index.js").default} modelClass - Backing model class.
9
11
  * @property {string} modelName - Model name.
10
- * @property {Record<string, ?>} params - Request params.
12
+ * @property {import("../configuration-types.js").VelociousLooseObject} params - Request params.
11
13
  * @property {import("../configuration-types.js").NormalizedFrontendModelResourceConfiguration | import("../configuration-types.js").FrontendModelResourceConfiguration} resourceConfiguration - Normalized resource configuration (or raw input shape during early bootstrap).
12
14
  */
15
+
13
16
  /**
14
17
  * FrontendModelResourceAbilityArgs type.
15
18
  * @typedef {object} FrontendModelResourceAbilityArgs
16
19
  * @property {import("../authorization/ability.js").default} [ability] - Ability instance when the resource is used directly for authorization.
17
- * @property {Record<string, ?>} [context] - Ability context.
18
- * @property {Record<string, ?>} [locals] - Ability locals.
20
+ * @property {import("../configuration-types.js").VelociousLooseObject} [context] - Ability context.
21
+ * @property {import("../configuration-types.js").VelociousLooseObject} [locals] - Ability locals.
19
22
  * @property {typeof import("../database/record/index.js").default} [modelClass] - Optional backing model class override.
20
23
  * @property {string} [modelName] - Optional model name override.
21
- * @property {Record<string, ?>} [params] - Optional params override.
24
+ * @property {import("../configuration-types.js").VelociousLooseObject} [params] - Optional params override.
22
25
  * @property {import("../configuration-types.js").NormalizedFrontendModelResourceConfiguration | import("../configuration-types.js").FrontendModelResourceConfiguration} [resourceConfiguration] - Optional normalized resource configuration.
23
26
  */
27
+
24
28
  /**
25
29
  * Base class for backend frontend-model resources.
26
30
  */
27
31
  export default class FrontendModelBaseResource extends AuthorizationBaseResource {
28
- /**
29
- * Attributes.
30
- @type {Record<string, ?> | string[] | undefined} */
31
- static attributes = undefined;
32
- /**
33
- * Abilities.
34
- @type {string[] | undefined} */
35
- static abilities = undefined;
36
- /**
37
- * Attachments.
38
- @type {Record<string, ?> | undefined} */
39
- static attachments = undefined;
40
- /**
41
- * Collection commands.
42
- @type {string[] | undefined} */
43
- static collectionCommands = undefined;
44
- /**
45
- * Built in collection commands.
46
- @type {string[] | undefined} */
47
- static builtInCollectionCommands = undefined;
48
- /**
49
- * Member commands.
50
- @type {string[] | undefined} */
51
- static memberCommands = undefined;
52
- /**
53
- * Built in member commands.
54
- @type {string[] | undefined} */
55
- static builtInMemberCommands = undefined;
56
- /**
57
- * Relationships.
58
- @type {string[] | undefined} */
59
- static relationships = undefined;
60
- /**
61
- * Translated attributes.
62
- @type {string[] | undefined} */
63
- static translatedAttributes = undefined;
64
- /**
65
- * Runs constructor.
66
- * @param {FrontendModelResourceAbilityArgs | FrontendModelResourceControllerArgs} args - Resource args.
67
- */
68
- constructor(args) {
69
- super({
70
- ability: "ability" in args ? args.ability : undefined,
71
- context: "context" in args ? args.context || {} : {},
72
- locals: "locals" in args ? args.locals || {} : {}
73
- });
74
- this.controller = "controller" in args ? args.controller : undefined;
75
- this.modelClassValue = "modelClass" in args ? args.modelClass : /**
76
- * Narrows the runtime value to the documented type.
77
- @type {typeof import("../database/record/index.js").default | undefined} */
78
- ( /**
79
- * Narrows the runtime value to the documented type.
80
- @type {typeof FrontendModelBaseResource} */(this.constructor).modelClass());
81
- this.modelNameValue = "modelName" in args ? args.modelName : (this.modelClassValue?.getModelName ? this.modelClassValue.getModelName() : this.modelClassValue?.name || "");
82
- this.paramsValue = "params" in args ? args.params : undefined;
83
- this.resourceConfigurationValue = "resourceConfiguration" in args ? args.resourceConfiguration : /**
84
- * Narrows the runtime value to the documented type.
85
- @type {import("../configuration-types.js").FrontendModelResourceConfiguration} */
86
- ({ attributes: [] });
87
- }
88
- /**
89
- * Runs typed controller instance.
90
- * @returns {import("../controller.js").default & {
91
- * frontendModelAuthorizedQuery: (action: "index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url") => import("../database/query/model-class-query.js").default<unknown>,
92
- * frontendModelIndexQuery: () => import("../database/query/model-class-query.js").default<unknown>,
93
- * frontendModelPreload: () => import("../database/query/index.js").NestedPreloadRecord | null,
94
- * serializeFrontendModel: (model: import("../database/record/index.js").default) => Promise<Record<string, unknown>>
95
- * }} - Controller instance with frontend-model helpers.
96
- */
97
- typedControllerInstance() {
98
- return /** Narrows the runtime value to the documented type. @type {?} */ (this.controller);
99
- }
100
- /**
101
- * Runs resource config.
102
- * @returns {import("../configuration-types.js").FrontendModelResourceConfiguration} - Static resource config (raw user input shape; consumers normalize).
103
- */
104
- static resourceConfig() {
105
- /**
106
- * Config.
107
- @type {import("../configuration-types.js").FrontendModelResourceConfiguration} */
108
- const config = {
109
- attributes: this.attributes || []
110
- };
111
- if (this.abilities)
112
- config.abilities = this.abilities;
113
- if (this.attachments)
114
- config.attachments = this.attachments;
115
- if (this.builtInCollectionCommands)
116
- config.builtInCollectionCommands = this.builtInCollectionCommands;
117
- if (this.builtInMemberCommands)
118
- config.builtInMemberCommands = this.builtInMemberCommands;
119
- if (this.collectionCommands)
120
- config.collectionCommands = this.collectionCommands;
121
- if (this.memberCommands)
122
- config.memberCommands = this.memberCommands;
123
- if (this.relationships)
124
- config.relationships = this.relationships;
125
- return config;
126
- }
127
- /**
128
- * Runs controller instance.
129
- * @returns {import("../controller.js").default} - Controller instance.
130
- */
131
- controllerInstance() {
132
- if (!this.controller)
133
- throw new Error(`${this.constructor.name} requires a controller instance.`);
134
- return this.controller;
135
- }
136
- /**
137
- * Runs model class.
138
- * @returns {typeof import("../database/record/index.js").default} - Model class.
139
- */
140
- modelClass() {
141
- if (!this.modelClassValue) {
142
- throw new Error(`${this.constructor.name} requires a model class.`);
143
- }
144
- return this.modelClassValue;
145
- }
146
- /**
147
- * Runs model name.
148
- * @returns {string} - Model name.
149
- */
150
- modelName() {
151
- if (!this.modelNameValue)
152
- throw new Error(`${this.constructor.name} requires a model name.`);
153
- return this.modelNameValue;
154
- }
155
- /**
156
- * Runs params.
157
- * @returns {Record<string, ?>} - Params.
158
- */
159
- params() { return this.paramsValue || super.params() || {}; }
160
- /**
161
- * Runs resource configuration.
162
- * @returns {import("../configuration-types.js").NormalizedFrontendModelResourceConfiguration | import("../configuration-types.js").FrontendModelResourceConfiguration} - Resource config (normalized at runtime; raw during early bootstrap).
163
- */
164
- resourceConfiguration() {
165
- if (!this.resourceConfigurationValue)
166
- throw new Error(`${this.constructor.name} requires a resource configuration.`);
167
- return this.resourceConfigurationValue;
168
- }
169
- /**
170
- * Returns a Rails-strong-params / api_maker-style permit spec declaring
171
- * which attributes and nested attributes are writable for the current
172
- * request. Submitting an attribute or nested-relationship key that is
173
- * not permitted raises an error and fails the write.
174
- *
175
- * The returned value is a flat array that mixes:
176
- * - `"attributeName"` strings for plain attribute writes
177
- * - `{<relationshipName>Attributes: [...]}` objects where the value
178
- * is itself a permit spec for the nested relationship
179
- *
180
- * This matches Rails strong_params (`permit(:first_name, :last_name,
181
- * contact_attributes: [:email, details_attributes: [:detail]])`) and
182
- * the api_maker sister project. Include `"_destroy"` inside a nested
183
- * permit to allow `_destroy: true` entries for that relationship —
184
- * the model must also declare `acceptsNestedAttributesFor(name,
185
- * {allowDestroy: true})` for the destroy to be applied.
186
- *
187
- * Example:
188
- *
189
- * class ProjectResource extends FrontendModelBaseResource {
190
- * permittedParams(arg) {
191
- * return [
192
- * "name",
193
- * "description",
194
- * {tasksAttributes: ["id", "_destroy", "name",
195
- * {subtasksAttributes: ["id", "_destroy", "name"]}
196
- * ]}
197
- * ]
198
- * }
199
- * }
200
- *
201
- * Default implementation returns `[]` nothing permitted. Subclasses
202
- * must override to enable writes. A resource that does not declare
203
- * `permittedParams` cannot accept any write.
204
- * @param {{action?: "create" | "update", params?: Record<string, ?>, ability?: import("../authorization/ability.js").default, locals?: Record<string, ?>}} [arg] - Request context.
205
- * @returns {Array<string | Record<string, ?>>} - Permit spec.
206
- */
207
- permittedParams(arg) {
208
- void arg;
209
- return [];
210
- }
211
- /**
212
- * Runs primary key.
213
- * @returns {string} - Primary key.
214
- */
215
- primaryKey() { return this.modelClass().primaryKey(); }
216
- /**
217
- * Runs authorized query.
218
- * @param {"index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url"} action - Ability action.
219
- * @returns {import("../database/query/model-class-query.js").default<?>} - Authorized query.
220
- */
221
- authorizedQuery(action) {
222
- return this.typedControllerInstance().frontendModelAuthorizedQuery(action);
223
- }
224
- /**
225
- * Runs supports pluck.
226
- * @param {"index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url"} action - Action.
227
- * @returns {boolean | Promise<boolean>} - Whether pluck is supported.
228
- */
229
- supportsPluck(action) {
230
- void action;
231
- return Object.getPrototypeOf(this).records === FrontendModelBaseResource.prototype.records;
232
- }
233
- /**
234
- * Runs supports count.
235
- * @param {"index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url"} action - Action.
236
- * @returns {boolean | Promise<boolean>} - Whether count is supported.
237
- */
238
- supportsCount(action) {
239
- void action;
240
- return Object.getPrototypeOf(this).records === FrontendModelBaseResource.prototype.records ||
241
- Object.getPrototypeOf(this).count !== FrontendModelBaseResource.prototype.count;
242
- }
243
- /**
244
- * Runs before action.
245
- * @param {"index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url"} action - Action.
246
- * @returns {boolean | void | Promise<boolean | void>} - Continue processing unless false.
247
- */
248
- beforeAction(action) {
249
- void action;
250
- // No-op by default.
251
- }
252
- /**
253
- * Runs records.
254
- * @returns {Promise<import("../database/record/index.js").default[]>} - Records for index action.
255
- */
256
- async records() {
257
- return await this.typedControllerInstance().frontendModelIndexQuery().toArray();
258
- }
259
- /**
260
- * Runs count.
261
- * @returns {Promise<number>} - Records count for index action.
262
- */
263
- async count() {
264
- return await this.typedControllerInstance().frontendModelIndexQuery().count();
265
- }
266
- /**
267
- * Runs find.
268
- * @param {"find" | "update" | "destroy" | "attach" | "download" | "url"} action - Action.
269
- * @param {string | number} id - Record id.
270
- * @returns {Promise<import("../database/record/index.js").default | null>} - Located model.
271
- */
272
- async find(action, id) {
273
- let query = this.authorizedQuery(action);
274
- const preload = action === "find" ? this.typedControllerInstance().frontendModelPreload() : null;
275
- if (preload) {
276
- query = query.preload(preload);
277
- }
278
- return await query.findBy({ [this.primaryKey()]: id });
279
- }
280
- /**
281
- * Runs create.
282
- * @param {Record<string, ?>} attributes - Create attributes.
283
- * @param {{controller?: ?, nestedAttributes?: Record<string, ?> | null}} [options] - Save options.
284
- * @returns {Promise<import("../database/record/index.js").default>} - Created model.
285
- */
286
- async create(attributes, options = {}) {
287
- const permit = parsePermittedParams(this.permittedParams({ action: "create", ability: this.ability, locals: this.locals, params: attributes }));
288
- const filtered = filterWritableFrontendModelAttributes(this.modelClass().prototype, attributes, this, permit.attributes);
289
- const ModelClass = this.modelClass();
290
- const model = new ModelClass();
291
- await ModelClass.transaction(async () => {
292
- await this._assignWithVirtualSetters(model, filtered);
293
- await model.save();
294
- if (options.nestedAttributes) {
295
- await this._applyNestedAttributes(model, options.nestedAttributes, options.controller || null, permit);
296
- }
297
- });
298
- await this._preloadNestedWritableRelationships(model, permit);
299
- return model;
300
- }
301
- /**
302
- * Runs handle unauthorized created model.
303
- * @param {import("../database/record/index.js").default} model - Created model.
304
- * @returns {Promise<void>} - Cleanup after failed authorization.
305
- */
306
- async handleUnauthorizedCreatedModel(model) {
307
- await model.destroy();
308
- }
309
- /**
310
- * Runs update.
311
- * @param {import("../database/record/index.js").default} model - Existing model.
312
- * @param {Record<string, ?>} attributes - Update attributes.
313
- * @param {{controller?: ?, nestedAttributes?: Record<string, ?> | null}} [options] - Save options.
314
- * @returns {Promise<import("../database/record/index.js").default>} - Updated model.
315
- */
316
- async update(model, attributes, options = {}) {
317
- const permit = parsePermittedParams(this.permittedParams({ action: "update", ability: this.ability, locals: this.locals, params: attributes }));
318
- const filtered = filterWritableFrontendModelAttributes(model, attributes, this, permit.attributes);
319
- const ModelClass = this.modelClass();
320
- await ModelClass.transaction(async () => {
321
- await this._assignWithVirtualSetters(model, filtered);
322
- await model.save();
323
- if (options.nestedAttributes) {
324
- await this._applyNestedAttributes(model, options.nestedAttributes, options.controller || null, permit);
325
- }
326
- });
327
- await this._preloadNestedWritableRelationships(model, permit);
328
- return model;
329
- }
330
- /**
331
- * Assigns attributes to a model, using virtual setters on the resource when available.
332
- * @param {import("../database/record/index.js").default} model - Model instance.
333
- * @param {Record<string, ?>} attributes - Attributes to assign.
334
- * @returns {Promise<void>}
335
- */
336
- async _assignWithVirtualSetters(model, attributes) {
337
- /**
338
- * Direct attributes.
339
- @type {Record<string, ?>} */
340
- const directAttributes = {};
341
- const translatedSet = new Set(/**
342
- * Narrows the runtime value to the documented type.
343
- @type {typeof FrontendModelBaseResource} */ (this.constructor).translatedAttributes || []);
344
- for (const [name, value] of Object.entries(attributes)) {
345
- const resourceSetterName = `set${inflection.camelize(name)}Attribute`;
346
- if (typeof /**
347
- * Narrows the runtime value to the documented type.
348
- @type {Record<string, ?>} */ ( /**
349
- * Narrows the runtime value to the documented type.
350
- @type {?} */(this))[resourceSetterName] === "function") {
351
- await /**
352
- * Narrows the runtime value to the documented type.
353
- @type {Record<string, ?>} */ ( /**
32
+ /**
33
+ * Backing model class.
34
+ @type {typeof import("../database/record/index.js").default | undefined} */
35
+ static ModelClass = undefined
36
+
37
+ /**
38
+ * Attributes.
39
+ @type {Record<string, ?> | string[] | undefined} */
40
+ static attributes = undefined
41
+ /**
42
+ * Abilities.
43
+ @type {string[] | undefined} */
44
+ static abilities = undefined
45
+ /**
46
+ * Attachments.
47
+ @type {Record<string, ?> | undefined} */
48
+ static attachments = undefined
49
+ /**
50
+ * Collection commands.
51
+ @type {string[] | undefined} */
52
+ static collectionCommands = undefined
53
+ /**
54
+ * Built in collection commands.
55
+ @type {string[] | undefined} */
56
+ static builtInCollectionCommands = undefined
57
+ /**
58
+ * Member commands.
59
+ @type {string[] | undefined} */
60
+ static memberCommands = undefined
61
+ /**
62
+ * Built in member commands.
63
+ @type {string[] | undefined} */
64
+ static builtInMemberCommands = undefined
65
+ /**
66
+ * Relationships.
67
+ @type {string[] | undefined} */
68
+ static relationships = undefined
69
+ /**
70
+ * Translated attributes.
71
+ @type {string[] | undefined} */
72
+ static translatedAttributes = undefined
73
+
74
+ /**
75
+ * Runs constructor.
76
+ * @param {FrontendModelResourceAbilityArgs | FrontendModelResourceControllerArgs} args - Resource args.
77
+ */
78
+ constructor(args) {
79
+ super({
80
+ ability: "ability" in args ? args.ability : undefined,
81
+ context: "context" in args ? args.context || {} : {},
82
+ locals: "locals" in args ? args.locals || {} : {}
83
+ })
84
+
85
+ this.controller = "controller" in args ? args.controller : undefined
86
+ this.modelClassValue = "modelClass" in args ? args.modelClass : /**
87
+ * Narrows the runtime value to the documented type.
88
+ @type {typeof import("../database/record/index.js").default | undefined} */ (/**
89
+ * Narrows the runtime value to the documented type.
90
+ @type {typeof FrontendModelBaseResource} */ (this.constructor).modelClass())
91
+ this.modelNameValue = "modelName" in args ? args.modelName : (this.modelClassValue?.getModelName ? this.modelClassValue.getModelName() : this.modelClassValue?.name || "")
92
+ this.paramsValue = "params" in args ? args.params : undefined
93
+ this.resourceConfigurationValue = "resourceConfiguration" in args ? args.resourceConfiguration : /**
94
+ * Narrows the runtime value to the documented type.
95
+ @type {import("../configuration-types.js").FrontendModelResourceConfiguration} */ ({attributes: []})
96
+ }
97
+
98
+ /**
99
+ * Runs typed controller instance.
100
+ * @returns {import("../controller.js").default & {
101
+ * frontendModelAuthorizedQuery: (action: "index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url") => import("../database/query/model-class-query.js").default<typeof import("../database/record/index.js").default>,
102
+ * frontendModelIndexQuery: () => import("../database/query/model-class-query.js").default<typeof import("../database/record/index.js").default>,
103
+ * frontendModelPreload: () => import("../database/query/index.js").NestedPreloadRecord | null,
104
+ * serializeFrontendModel: (model: import("../database/record/index.js").default) => Promise<Record<string, unknown>>
105
+ * }} - Controller instance with frontend-model helpers.
106
+ */
107
+ typedControllerInstance() {
108
+ return /** Narrows the runtime value to the documented type. @type {?} */ (this.controller)
109
+ }
110
+
111
+ /**
112
+ * Runs resource config.
113
+ * @returns {import("../configuration-types.js").FrontendModelResourceConfiguration} - Static resource config (raw user input shape; consumers normalize).
114
+ */
115
+ static resourceConfig() {
116
+ /**
117
+ * Config.
118
+ @type {import("../configuration-types.js").FrontendModelResourceConfiguration} */
119
+ const config = {
120
+ attributes: this.attributes || []
121
+ }
122
+
123
+ if (this.abilities) config.abilities = this.abilities
124
+ if (this.attachments) config.attachments = this.attachments
125
+ if (this.builtInCollectionCommands) config.builtInCollectionCommands = this.builtInCollectionCommands
126
+ if (this.builtInMemberCommands) config.builtInMemberCommands = this.builtInMemberCommands
127
+ if (this.collectionCommands) config.collectionCommands = this.collectionCommands
128
+ if (this.memberCommands) config.memberCommands = this.memberCommands
129
+ if (this.relationships) config.relationships = this.relationships
130
+
131
+ return config
132
+ }
133
+
134
+ /**
135
+ * Runs static model class.
136
+ * @returns {typeof import("../database/record/index.js").default | undefined} - Backing model class.
137
+ */
138
+ static modelClass() {
139
+ return this.ModelClass
140
+ }
141
+
142
+ /**
143
+ * Runs controller instance.
144
+ * @returns {import("../controller.js").default} - Controller instance.
145
+ */
146
+ controllerInstance() {
147
+ if (!this.controller) throw new Error(`${this.constructor.name} requires a controller instance.`)
148
+
149
+ return this.controller
150
+ }
151
+
152
+ /**
153
+ * Runs model class.
154
+ * @returns {typeof import("../database/record/index.js").default} - Model class.
155
+ */
156
+ modelClass() {
157
+ if (!this.modelClassValue) {
158
+ throw new Error(`${this.constructor.name} requires a model class.`)
159
+ }
160
+
161
+ return this.modelClassValue
162
+ }
163
+
164
+ /**
165
+ * Runs model name.
166
+ * @returns {string} - Model name.
167
+ */
168
+ modelName() {
169
+ if (!this.modelNameValue) throw new Error(`${this.constructor.name} requires a model name.`)
170
+
171
+ return this.modelNameValue
172
+ }
173
+
174
+ /**
175
+ * Runs params.
176
+ * @returns {import("../configuration-types.js").VelociousLooseObject} - Params.
177
+ */
178
+ params() { return this.paramsValue || super.params() || {} }
179
+
180
+ /**
181
+ * Runs resource configuration.
182
+ * @returns {import("../configuration-types.js").NormalizedFrontendModelResourceConfiguration | import("../configuration-types.js").FrontendModelResourceConfiguration} - Resource config (normalized at runtime; raw during early bootstrap).
183
+ */
184
+ resourceConfiguration() {
185
+ if (!this.resourceConfigurationValue) throw new Error(`${this.constructor.name} requires a resource configuration.`)
186
+
187
+ return this.resourceConfigurationValue
188
+ }
189
+
190
+ /**
191
+ * Returns a Rails-strong-params / api_maker-style permit spec declaring
192
+ * which attributes and nested attributes are writable for the current
193
+ * request. Submitting an attribute or nested-relationship key that is
194
+ * not permitted raises an error and fails the write.
195
+ *
196
+ * The returned value is a flat array that mixes:
197
+ * - `"attributeName"` strings for plain attribute writes
198
+ * - `{<relationshipName>Attributes: [...]}` objects where the value
199
+ * is itself a permit spec for the nested relationship
200
+ *
201
+ * This matches Rails strong_params (`permit(:first_name, :last_name,
202
+ * contact_attributes: [:email, details_attributes: [:detail]])`) and
203
+ * the api_maker sister project. Include `"_destroy"` inside a nested
204
+ * permit to allow `_destroy: true` entries for that relationship —
205
+ * the model must also declare `acceptsNestedAttributesFor(name,
206
+ * {allowDestroy: true})` for the destroy to be applied.
207
+ *
208
+ * Example:
209
+ *
210
+ * class ProjectResource extends FrontendModelBaseResource {
211
+ * permittedParams(arg) {
212
+ * return [
213
+ * "name",
214
+ * "description",
215
+ * {tasksAttributes: ["id", "_destroy", "name",
216
+ * {subtasksAttributes: ["id", "_destroy", "name"]}
217
+ * ]}
218
+ * ]
219
+ * }
220
+ * }
221
+ *
222
+ * Default implementation returns `[]` nothing permitted. Subclasses
223
+ * must override to enable writes. A resource that does not declare
224
+ * `permittedParams` cannot accept any write.
225
+ * @param {{action?: "create" | "update", params?: Record<string, ?>, ability?: import("../authorization/ability.js").default, locals?: Record<string, ?>}} [arg] - Request context.
226
+ * @returns {Array<string | Record<string, ?>>} - Permit spec.
227
+ */
228
+ permittedParams(arg) {
229
+ void arg
230
+
231
+ return []
232
+ }
233
+
234
+ /**
235
+ * Runs primary key.
236
+ * @returns {string} - Primary key.
237
+ */
238
+ primaryKey() { return this.modelClass().primaryKey() }
239
+
240
+ /**
241
+ * Runs authorized query.
242
+ * @param {"index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url"} action - Ability action.
243
+ * @template {typeof import("../database/record/index.js").default} [MC=typeof import("../database/record/index.js").default]
244
+ * @returns {import("../database/query/model-class-query.js").default<MC>} - Authorized query.
245
+ */
246
+ authorizedQuery(action) {
247
+ return /** Narrows the authorized query to the resource's model class. @type {import("../database/query/model-class-query.js").default<MC>} */ (this.typedControllerInstance().frontendModelAuthorizedQuery(action))
248
+ }
249
+
250
+ /**
251
+ * Runs supports pluck.
252
+ * @param {"index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url"} action - Action.
253
+ * @returns {boolean | Promise<boolean>} - Whether pluck is supported.
254
+ */
255
+ supportsPluck(action) {
256
+ void action
257
+
258
+ return Object.getPrototypeOf(this).records === FrontendModelBaseResource.prototype.records
259
+ }
260
+
261
+ /**
262
+ * Runs supports count.
263
+ * @param {"index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url"} action - Action.
264
+ * @returns {boolean | Promise<boolean>} - Whether count is supported.
265
+ */
266
+ supportsCount(action) {
267
+ void action
268
+
269
+ return Object.getPrototypeOf(this).records === FrontendModelBaseResource.prototype.records ||
270
+ Object.getPrototypeOf(this).count !== FrontendModelBaseResource.prototype.count
271
+ }
272
+
273
+ /**
274
+ * Runs before action.
275
+ * @param {"index" | "find" | "create" | "update" | "destroy" | "attach" | "download" | "url"} action - Action.
276
+ * @returns {boolean | void | Promise<boolean | void>} - Continue processing unless false.
277
+ */
278
+ beforeAction(action) {
279
+ void action
280
+
281
+ // No-op by default.
282
+ }
283
+
284
+ /**
285
+ * Runs records.
286
+ * @returns {Promise<import("../database/record/index.js").default[]>} - Records for index action.
287
+ */
288
+ async records() {
289
+ return await this.typedControllerInstance().frontendModelIndexQuery().toArray()
290
+ }
291
+
292
+ /**
293
+ * Runs count.
294
+ * @returns {Promise<number>} - Records count for index action.
295
+ */
296
+ async count() {
297
+ return await this.typedControllerInstance().frontendModelIndexQuery().count()
298
+ }
299
+
300
+ /**
301
+ * Runs find.
302
+ * @param {"find" | "update" | "destroy" | "attach" | "download" | "url"} action - Action.
303
+ * @param {string | number} id - Record id.
304
+ * @returns {Promise<import("../database/record/index.js").default | null>} - Located model.
305
+ */
306
+ async find(action, id) {
307
+ let query = this.authorizedQuery(action)
308
+ const preload = action === "find" ? this.typedControllerInstance().frontendModelPreload() : null
309
+
310
+ if (preload) {
311
+ query = query.preload(preload)
312
+ }
313
+
314
+ return await query.findBy({[this.primaryKey()]: id})
315
+ }
316
+
317
+ /**
318
+ * Runs create.
319
+ * @param {Record<string, ?>} attributes - Create attributes.
320
+ * @param {{controller?: ?, nestedAttributes?: Record<string, ?> | null}} [options] - Save options.
321
+ * @returns {Promise<import("../database/record/index.js").default>} - Created model.
322
+ */
323
+ async create(attributes, options = {}) {
324
+ const permit = parsePermittedParams(this.permittedParams({action: "create", ability: this.ability, locals: this.locals, params: attributes}))
325
+ const filtered = filterWritableFrontendModelAttributes(this.modelClass().prototype, attributes, this, permit.attributes)
326
+ const ModelClass = this.modelClass()
327
+ const model = new ModelClass()
328
+
329
+ await ModelClass.transaction(async () => {
330
+ await this._assignWithVirtualSetters(model, filtered)
331
+ await model.save()
332
+
333
+ if (options.nestedAttributes) {
334
+ await this._applyNestedAttributes(model, options.nestedAttributes, options.controller || null, permit)
335
+ }
336
+ })
337
+
338
+ await this._preloadNestedWritableRelationships(model, permit)
339
+
340
+ return model
341
+ }
342
+
343
+ /**
344
+ * Runs handle unauthorized created model.
345
+ * @param {import("../database/record/index.js").default} model - Created model.
346
+ * @returns {Promise<void>} - Cleanup after failed authorization.
347
+ */
348
+ async handleUnauthorizedCreatedModel(model) {
349
+ await model.destroy()
350
+ }
351
+
352
+ /**
353
+ * Runs update.
354
+ * @param {import("../database/record/index.js").default} model - Existing model.
355
+ * @param {Record<string, ?>} attributes - Update attributes.
356
+ * @param {{controller?: ?, nestedAttributes?: Record<string, ?> | null}} [options] - Save options.
357
+ * @returns {Promise<import("../database/record/index.js").default>} - Updated model.
358
+ */
359
+ async update(model, attributes, options = {}) {
360
+ const permit = parsePermittedParams(this.permittedParams({action: "update", ability: this.ability, locals: this.locals, params: attributes}))
361
+ const filtered = filterWritableFrontendModelAttributes(model, attributes, this, permit.attributes)
362
+ const ModelClass = this.modelClass()
363
+
364
+ await ModelClass.transaction(async () => {
365
+ await this._assignWithVirtualSetters(model, filtered)
366
+ await model.save()
367
+
368
+ if (options.nestedAttributes) {
369
+ await this._applyNestedAttributes(model, options.nestedAttributes, options.controller || null, permit)
370
+ }
371
+ })
372
+
373
+ await this._preloadNestedWritableRelationships(model, permit)
374
+
375
+ return model
376
+ }
377
+
378
+ /**
379
+ * Assigns attributes to a model, using virtual setters on the resource when available.
380
+ * @param {import("../database/record/index.js").default} model - Model instance.
381
+ * @param {Record<string, ?>} attributes - Attributes to assign.
382
+ * @returns {Promise<void>}
383
+ */
384
+ async _assignWithVirtualSetters(model, attributes) {
385
+ /**
386
+ * Direct attributes.
387
+ @type {Record<string, ?>} */
388
+ const directAttributes = {}
389
+ const translatedSet = new Set(/**
390
+ * Narrows the runtime value to the documented type.
391
+ @type {typeof FrontendModelBaseResource} */ (this.constructor).translatedAttributes || [])
392
+
393
+ for (const [name, value] of Object.entries(attributes)) {
394
+ const resourceSetterName = `set${inflection.camelize(name)}Attribute`
395
+
396
+ if (typeof /**
397
+ * Narrows the runtime value to the documented type.
398
+ @type {Record<string, ?>} */ (/**
399
+ * Narrows the runtime value to the documented type.
400
+ @type {?} */ (this))[resourceSetterName] === "function") {
401
+ await /**
402
+ * Narrows the runtime value to the documented type.
403
+ @type {Record<string, ?>} */ (/**
354
404
  * Narrows the runtime value to the documented type.
355
- @type {?} */(this))[resourceSetterName](model, value);
356
- }
357
- else if (translatedSet.has(name)) {
358
- await this._setTranslatedAttributeOnModel(model, name, value);
359
- }
360
- else {
361
- directAttributes[name] = value;
362
- }
405
+ @type {?} */ (this))[resourceSetterName](model, value)
406
+ } else if (translatedSet.has(name)) {
407
+ await this._setTranslatedAttributeOnModel(model, name, value)
408
+ } else {
409
+ directAttributes[name] = value
410
+ }
411
+ }
412
+
413
+ if (Object.keys(directAttributes).length > 0) {
414
+ model.assign(directAttributes)
415
+ }
416
+ }
417
+
418
+ /**
419
+ * Sets a translated attribute on a model via the translations relationship.
420
+ * @param {import("../database/record/index.js").default} model - Model instance.
421
+ * @param {string} name - Attribute name.
422
+ * @param {?} value - Attribute value.
423
+ * @returns {Promise<void>}
424
+ */
425
+ async _setTranslatedAttributeOnModel(model, name, value) {
426
+ const locale = this.context?.configuration?.getLocale?.() || "en"
427
+ const instanceRelationship = model.getRelationshipByName("translations")
428
+
429
+ /**
430
+ * Defines translation.
431
+ @type {import("../database/record/index.js").default | undefined} */
432
+ let translation
433
+
434
+ if (model.isNewRecord()) {
435
+ const loaded = instanceRelationship.loaded()
436
+
437
+ if (Array.isArray(loaded)) {
438
+ translation = loaded.find((/**
439
+ * Narrows the runtime value to the documented type.
440
+ @type {Record<string, ?>} */ t) => t.locale() === locale)
441
+ }
442
+ } else {
443
+ if (!instanceRelationship.getPreloaded()) {
444
+ await model.loadRelationship("translations")
445
+ }
446
+
447
+ const loaded = instanceRelationship.loaded()
448
+
449
+ if (Array.isArray(loaded)) {
450
+ translation = loaded.find((/**
451
+ * Narrows the runtime value to the documented type.
452
+ @type {Record<string, ?>} */ t) => t.locale() === locale)
453
+ }
454
+ }
455
+
456
+ if (!translation) {
457
+ translation = instanceRelationship.build({locale})
458
+ }
459
+
460
+ /**
461
+ * Assignments.
462
+ @type {Record<string, ?>} */
463
+ const assignments = {}
464
+
465
+ assignments[name] = value
466
+ translation.assign(assignments)
467
+ }
468
+
469
+ /**
470
+ * Runs destroy.
471
+ * @param {import("../database/record/index.js").default} model - Existing model.
472
+ * @returns {Promise<void>} - No return value.
473
+ */
474
+ async destroy(model) {
475
+ await model.destroy()
476
+ }
477
+
478
+ /**
479
+ * Runs serialize.
480
+ * @param {import("../database/record/index.js").default} model - Model to serialize.
481
+ * @param {"index" | "find" | "create" | "update"} [action] - Action.
482
+ * @returns {Promise<Record<string, ?>>} - Serialized model payload.
483
+ */
484
+ async serialize(model, action) {
485
+ void action
486
+
487
+ return await this.typedControllerInstance().serializeFrontendModel(model)
488
+ }
489
+
490
+ /**
491
+ * Applies a `nestedAttributes` payload to a freshly-saved parent model,
492
+ * cascading create/update/destroy writes across the declared relationships.
493
+ *
494
+ * Each child is authorized against its own resource's abilities (never the
495
+ * parent's). Destroys run before updates, updates before creates, to avoid
496
+ * unique-constraint conflicts when replacing a child at the same natural key.
497
+ *
498
+ * Attribute filtering for nested children uses the parent resource's
499
+ * permit spec for that relationship — api_maker-style. Policy options
500
+ * (allowDestroy, limit, rejectIf) come from the MODEL's
501
+ * `acceptedNestedAttributesFor(name)` declaration.
502
+ * @param {import("../database/record/index.js").default} parent - Parent model instance.
503
+ * @param {Record<string, ?>} nestedAttributes - Nested-attribute payload keyed by relationship name.
504
+ * @param {?} controller - Controller instance for resource resolution and authorization.
505
+ * @param {{attributes: string[], nested: Record<string, ?>} | null} [parentPermit] - Parsed parent permit spec.
506
+ * @returns {Promise<void>}
507
+ */
508
+ async _applyNestedAttributes(parent, nestedAttributes, controller, parentPermit = null) {
509
+ const resolvedParent = parentPermit
510
+ || parsePermittedParams(this.permittedParams({action: "update", ability: this.ability, locals: this.locals, params: {}}))
511
+
512
+ for (const relationshipName of Object.keys(nestedAttributes)) {
513
+ const childPermit = resolvedParent.nested[relationshipName]
514
+
515
+ if (!childPermit) {
516
+ throw new Error(`Nested attributes for '${relationshipName}' are not permitted by ${this.constructor.name}.permittedParams(). Include {${relationshipName}Attributes: [...]} in the returned permit.`)
517
+ }
518
+
519
+ const entries = nestedAttributes[relationshipName]
520
+
521
+ if (!Array.isArray(entries)) {
522
+ throw new Error(`Expected array for nestedAttributes['${relationshipName}'] but got: ${typeof entries}`)
523
+ }
524
+
525
+ const parentModelClass = /**
526
+ * Narrows the runtime value to the documented type.
527
+ @type {?} */ (parent.getModelClass())
528
+ const modelAcceptance = parentModelClass.acceptedNestedAttributesFor?.(relationshipName)
529
+
530
+ if (!modelAcceptance) {
531
+ throw new Error(`Model ${parentModelClass.name} does not accept nested attributes for '${relationshipName}'. Declare it via ${parentModelClass.name}.acceptsNestedAttributesFor('${relationshipName}').`)
532
+ }
533
+
534
+ const destroyPermitted = childPermit.attributes.includes("_destroy")
535
+
536
+ if (destroyPermitted && !modelAcceptance.allowDestroy) {
537
+ throw new Error(`Resource permits _destroy on nestedAttributes['${relationshipName}'] but the model ${parentModelClass.name} does not allow destroy for that relationship. Set {allowDestroy: true} on ${parentModelClass.name}.acceptsNestedAttributesFor('${relationshipName}', ...).`)
538
+ }
539
+
540
+ if (typeof modelAcceptance.limit === "number" && entries.length > modelAcceptance.limit) {
541
+ throw new Error(`nestedAttributes['${relationshipName}'] exceeds model-declared limit of ${modelAcceptance.limit}.`)
542
+ }
543
+
544
+ const parentRelationship = parent.getRelationshipByName(relationshipName)
545
+ const relationshipDefinitions = parentModelClass.relationships?.() || {}
546
+ const definition = relationshipDefinitions[relationshipName]
547
+
548
+ if (!definition || definition.type !== "hasMany") {
549
+ throw new Error(`Nested attributes for '${relationshipName}' require a hasMany relationship. v1 does not support '${definition?.type}'.`)
550
+ }
551
+
552
+ const targetModelClass = /**
553
+ * Narrows the runtime value to the documented type.
554
+ @type {?} */ (parent.getModelClass()).relationshipModelClass?.(relationshipName)
555
+
556
+ if (!targetModelClass) {
557
+ throw new Error(`No target model class resolved for relationship '${relationshipName}' on ${parent.getModelClass().name}.`)
558
+ }
559
+
560
+ const childResourceConfig = controller?.frontendModelResourceConfigurationForModelClass?.(targetModelClass)
561
+
562
+ if (!childResourceConfig) {
563
+ throw new Error(`No frontend-model resource registered for child model '${targetModelClass.getModelName?.() || targetModelClass.name}' under relationship '${relationshipName}'.`)
564
+ }
565
+
566
+ const childResource = new childResourceConfig.resourceClass({
567
+ ability: this.ability,
568
+ controller,
569
+ context: this.context || {},
570
+ locals: this.locals || {},
571
+ modelClass: targetModelClass,
572
+ modelName: childResourceConfig.modelName,
573
+ params: controller?.frontendModelParams?.() || {},
574
+ resourceConfiguration: childResourceConfig.resourceConfiguration
575
+ })
576
+
577
+ const foreignKey = definition.foreignKey || this._inferForeignKey(parent, definition)
578
+ const ability = controller?.currentAbility?.()
579
+
580
+ const destroyEntries = []
581
+ const updateEntries = []
582
+ const createEntries = []
583
+
584
+ for (const entry of entries) {
585
+ if (typeof modelAcceptance.rejectIf === "function" && modelAcceptance.rejectIf(entry?.attributes || {})) continue
586
+
587
+ if (entry?._destroy) {
588
+ if (!destroyPermitted) {
589
+ throw new Error(`nestedAttributes['${relationshipName}'] entry requested _destroy but "_destroy" is not in the permit for this relationship.`)
590
+ }
591
+ if (!entry.id) {
592
+ throw new Error(`nestedAttributes['${relationshipName}'] _destroy entry is missing an id.`)
593
+ }
594
+ destroyEntries.push(entry)
595
+ } else if (entry?.id) {
596
+ updateEntries.push(entry)
597
+ } else {
598
+ createEntries.push(entry)
363
599
  }
364
- if (Object.keys(directAttributes).length > 0) {
365
- model.assign(directAttributes);
600
+ }
601
+
602
+ // The permit's attribute list governs what child fields can be written.
603
+ // Exclude `_destroy` from the writable set since it's a control flag,
604
+ // not an attribute on the record.
605
+ const childWritableAttributes = /**
606
+ * Narrows the runtime value to the documented type.
607
+ @type {string[]} */ (childPermit.attributes).filter((name) => name !== "_destroy")
608
+
609
+ for (const entry of destroyEntries) {
610
+ const existing = await this._findScopedChild({
611
+ ability,
612
+ action: "destroy",
613
+ childResourceConfiguration: childResourceConfig.resourceConfiguration,
614
+ foreignKey,
615
+ id: entry.id,
616
+ parent,
617
+ relationshipName,
618
+ targetModelClass
619
+ })
620
+
621
+ await childResource.destroy(existing)
622
+ }
623
+
624
+ for (const entry of updateEntries) {
625
+ const existing = await this._findScopedChild({
626
+ ability,
627
+ action: "update",
628
+ childResourceConfiguration: childResourceConfig.resourceConfiguration,
629
+ foreignKey,
630
+ id: entry.id,
631
+ parent,
632
+ relationshipName,
633
+ targetModelClass
634
+ })
635
+
636
+ if (entry.attributes && typeof entry.attributes === "object") {
637
+ const filtered = filterWritableFrontendModelAttributes(existing, entry.attributes, childResource, childWritableAttributes)
638
+ await /**
639
+ * Narrows the runtime value to the documented type.
640
+ @type {?} */ (childResource)._assignWithVirtualSetters(existing, filtered)
641
+ await existing.save()
366
642
  }
367
- }
368
- /**
369
- * Sets a translated attribute on a model via the translations relationship.
370
- * @param {import("../database/record/index.js").default} model - Model instance.
371
- * @param {string} name - Attribute name.
372
- * @param {?} value - Attribute value.
373
- * @returns {Promise<void>}
374
- */
375
- async _setTranslatedAttributeOnModel(model, name, value) {
376
- const locale = this.context?.configuration?.getLocale?.() || "en";
377
- const instanceRelationship = model.getRelationshipByName("translations");
378
- /**
379
- * Defines translation.
380
- @type {import("../database/record/index.js").default | undefined} */
381
- let translation;
382
- if (model.isNewRecord()) {
383
- const loaded = instanceRelationship.loaded();
384
- if (Array.isArray(loaded)) {
385
- translation = loaded.find((/**
386
- * Narrows the runtime value to the documented type.
387
- @type {Record<string, ?>} */ t) => t.locale() === locale);
388
- }
643
+
644
+ if (entry.nestedAttributes) {
645
+ await /**
646
+ * Narrows the runtime value to the documented type.
647
+ @type {?} */ (childResource)._applyNestedAttributes(existing, entry.nestedAttributes, controller, childPermit)
389
648
  }
390
- else {
391
- if (!instanceRelationship.getPreloaded()) {
392
- await model.loadRelationship("translations");
393
- }
394
- const loaded = instanceRelationship.loaded();
395
- if (Array.isArray(loaded)) {
396
- translation = loaded.find((/**
397
- * Narrows the runtime value to the documented type.
398
- @type {Record<string, ?>} */ t) => t.locale() === locale);
399
- }
400
- }
401
- if (!translation) {
402
- translation = instanceRelationship.build({ locale });
649
+ }
650
+
651
+ for (const entry of createEntries) {
652
+ const childAttributes = entry?.attributes && typeof entry.attributes === "object" ? entry.attributes : {}
653
+
654
+ const child = parentRelationship.build({...childAttributes, [foreignKey]: parent.id()})
655
+
656
+ const filtered = filterWritableFrontendModelAttributes(child, childAttributes, childResource, childWritableAttributes)
657
+
658
+ await /**
659
+ * Narrows the runtime value to the documented type.
660
+ @type {?} */ (childResource)._assignWithVirtualSetters(child, filtered)
661
+ await child.save()
662
+
663
+ await this._authorizeCreatedChild({
664
+ ability,
665
+ child,
666
+ childResourceConfiguration: childResourceConfig.resourceConfiguration,
667
+ relationshipName,
668
+ targetModelClass
669
+ })
670
+
671
+ if (entry.nestedAttributes) {
672
+ await /**
673
+ * Narrows the runtime value to the documented type.
674
+ @type {?} */ (childResource)._applyNestedAttributes(child, entry.nestedAttributes, controller, childPermit)
403
675
  }
404
- /**
405
- * Assignments.
406
- @type {Record<string, ?>} */
407
- const assignments = {};
408
- assignments[name] = value;
409
- translation.assign(assignments);
410
- }
411
- /**
412
- * Runs destroy.
413
- * @param {import("../database/record/index.js").default} model - Existing model.
414
- * @returns {Promise<void>} - No return value.
415
- */
416
- async destroy(model) {
417
- await model.destroy();
418
- }
419
- /**
420
- * Runs serialize.
421
- * @param {import("../database/record/index.js").default} model - Model to serialize.
422
- * @param {"index" | "find" | "create" | "update"} [action] - Action.
423
- * @returns {Promise<Record<string, ?>>} - Serialized model payload.
424
- */
425
- async serialize(model, action) {
426
- void action;
427
- return await this.typedControllerInstance().serializeFrontendModel(model);
428
- }
429
- /**
430
- * Applies a `nestedAttributes` payload to a freshly-saved parent model,
431
- * cascading create/update/destroy writes across the declared relationships.
432
- *
433
- * Each child is authorized against its own resource's abilities (never the
434
- * parent's). Destroys run before updates, updates before creates, to avoid
435
- * unique-constraint conflicts when replacing a child at the same natural key.
436
- *
437
- * Attribute filtering for nested children uses the parent resource's
438
- * permit spec for that relationship — api_maker-style. Policy options
439
- * (allowDestroy, limit, rejectIf) come from the MODEL's
440
- * `acceptedNestedAttributesFor(name)` declaration.
441
- * @param {import("../database/record/index.js").default} parent - Parent model instance.
442
- * @param {Record<string, ?>} nestedAttributes - Nested-attribute payload keyed by relationship name.
443
- * @param {?} controller - Controller instance for resource resolution and authorization.
444
- * @param {{attributes: string[], nested: Record<string, ?>} | null} [parentPermit] - Parsed parent permit spec.
445
- * @returns {Promise<void>}
446
- */
447
- async _applyNestedAttributes(parent, nestedAttributes, controller, parentPermit = null) {
448
- const resolvedParent = parentPermit
449
- || parsePermittedParams(this.permittedParams({ action: "update", ability: this.ability, locals: this.locals, params: {} }));
450
- for (const relationshipName of Object.keys(nestedAttributes)) {
451
- const childPermit = resolvedParent.nested[relationshipName];
452
- if (!childPermit) {
453
- throw new Error(`Nested attributes for '${relationshipName}' are not permitted by ${this.constructor.name}.permittedParams(). Include {${relationshipName}Attributes: [...]} in the returned permit.`);
454
- }
455
- const entries = nestedAttributes[relationshipName];
456
- if (!Array.isArray(entries)) {
457
- throw new Error(`Expected array for nestedAttributes['${relationshipName}'] but got: ${typeof entries}`);
458
- }
459
- const parentModelClass = /**
460
- * Narrows the runtime value to the documented type.
461
- @type {?} */ (parent.getModelClass());
462
- const modelAcceptance = parentModelClass.acceptedNestedAttributesFor?.(relationshipName);
463
- if (!modelAcceptance) {
464
- throw new Error(`Model ${parentModelClass.name} does not accept nested attributes for '${relationshipName}'. Declare it via ${parentModelClass.name}.acceptsNestedAttributesFor('${relationshipName}').`);
465
- }
466
- const destroyPermitted = childPermit.attributes.includes("_destroy");
467
- if (destroyPermitted && !modelAcceptance.allowDestroy) {
468
- throw new Error(`Resource permits _destroy on nestedAttributes['${relationshipName}'] but the model ${parentModelClass.name} does not allow destroy for that relationship. Set {allowDestroy: true} on ${parentModelClass.name}.acceptsNestedAttributesFor('${relationshipName}', ...).`);
469
- }
470
- if (typeof modelAcceptance.limit === "number" && entries.length > modelAcceptance.limit) {
471
- throw new Error(`nestedAttributes['${relationshipName}'] exceeds model-declared limit of ${modelAcceptance.limit}.`);
472
- }
473
- const parentRelationship = parent.getRelationshipByName(relationshipName);
474
- const relationshipDefinitions = parentModelClass.relationships?.() || {};
475
- const definition = relationshipDefinitions[relationshipName];
476
- if (!definition || definition.type !== "hasMany") {
477
- throw new Error(`Nested attributes for '${relationshipName}' require a hasMany relationship. v1 does not support '${definition?.type}'.`);
478
- }
479
- const targetModelClass = /**
480
- * Narrows the runtime value to the documented type.
481
- @type {?} */ (parent.getModelClass()).relationshipModelClass?.(relationshipName);
482
- if (!targetModelClass) {
483
- throw new Error(`No target model class resolved for relationship '${relationshipName}' on ${parent.getModelClass().name}.`);
484
- }
485
- const childResourceConfig = controller?.frontendModelResourceConfigurationForModelClass?.(targetModelClass);
486
- if (!childResourceConfig) {
487
- throw new Error(`No frontend-model resource registered for child model '${targetModelClass.getModelName?.() || targetModelClass.name}' under relationship '${relationshipName}'.`);
488
- }
489
- const childResource = new childResourceConfig.resourceClass({
490
- ability: this.ability,
491
- controller,
492
- context: this.context || {},
493
- locals: this.locals || {},
494
- modelClass: targetModelClass,
495
- modelName: childResourceConfig.modelName,
496
- params: controller?.frontendModelParams?.() || {},
497
- resourceConfiguration: childResourceConfig.resourceConfiguration
498
- });
499
- const foreignKey = definition.foreignKey || this._inferForeignKey(parent, definition);
500
- const ability = controller?.currentAbility?.();
501
- const destroyEntries = [];
502
- const updateEntries = [];
503
- const createEntries = [];
504
- for (const entry of entries) {
505
- if (typeof modelAcceptance.rejectIf === "function" && modelAcceptance.rejectIf(entry?.attributes || {}))
506
- continue;
507
- if (entry?._destroy) {
508
- if (!destroyPermitted) {
509
- throw new Error(`nestedAttributes['${relationshipName}'] entry requested _destroy but "_destroy" is not in the permit for this relationship.`);
510
- }
511
- if (!entry.id) {
512
- throw new Error(`nestedAttributes['${relationshipName}'] _destroy entry is missing an id.`);
513
- }
514
- destroyEntries.push(entry);
515
- }
516
- else if (entry?.id) {
517
- updateEntries.push(entry);
518
- }
519
- else {
520
- createEntries.push(entry);
521
- }
522
- }
523
- // The permit's attribute list governs what child fields can be written.
524
- // Exclude `_destroy` from the writable set since it's a control flag,
525
- // not an attribute on the record.
526
- const childWritableAttributes = /**
527
- * Narrows the runtime value to the documented type.
528
- @type {string[]} */ (childPermit.attributes).filter((name) => name !== "_destroy");
529
- for (const entry of destroyEntries) {
530
- const existing = await this._findScopedChild({
531
- ability,
532
- action: "destroy",
533
- childResourceConfiguration: childResourceConfig.resourceConfiguration,
534
- foreignKey,
535
- id: entry.id,
536
- parent,
537
- relationshipName,
538
- targetModelClass
539
- });
540
- await childResource.destroy(existing);
541
- }
542
- for (const entry of updateEntries) {
543
- const existing = await this._findScopedChild({
544
- ability,
545
- action: "update",
546
- childResourceConfiguration: childResourceConfig.resourceConfiguration,
547
- foreignKey,
548
- id: entry.id,
549
- parent,
550
- relationshipName,
551
- targetModelClass
552
- });
553
- if (entry.attributes && typeof entry.attributes === "object") {
554
- const filtered = filterWritableFrontendModelAttributes(existing, entry.attributes, childResource, childWritableAttributes);
555
- await /**
556
- * Narrows the runtime value to the documented type.
557
- @type {?} */ (childResource)._assignWithVirtualSetters(existing, filtered);
558
- await existing.save();
559
- }
560
- if (entry.nestedAttributes) {
561
- await /**
562
- * Narrows the runtime value to the documented type.
563
- @type {?} */ (childResource)._applyNestedAttributes(existing, entry.nestedAttributes, controller, childPermit);
564
- }
565
- }
566
- for (const entry of createEntries) {
567
- const childAttributes = entry?.attributes && typeof entry.attributes === "object" ? entry.attributes : {};
568
- const child = parentRelationship.build({ ...childAttributes, [foreignKey]: parent.id() });
569
- const filtered = filterWritableFrontendModelAttributes(child, childAttributes, childResource, childWritableAttributes);
570
- await /**
571
- * Narrows the runtime value to the documented type.
572
- @type {?} */ (childResource)._assignWithVirtualSetters(child, filtered);
573
- await child.save();
574
- await this._authorizeCreatedChild({
575
- ability,
576
- child,
577
- childResourceConfiguration: childResourceConfig.resourceConfiguration,
578
- relationshipName,
579
- targetModelClass
580
- });
581
- if (entry.nestedAttributes) {
582
- await /**
676
+ }
677
+ }
678
+ }
679
+
680
+ /**
681
+ * Resolves the ability action for a child resource using the child's own
682
+ * `abilities` mapping — never the parent controller's. This preserves
683
+ * custom mappings like `{update: "manage"}` and catches unmapped actions
684
+ * instead of silently defaulting to the raw action name.
685
+ * @param {import("../configuration-types.js").FrontendModelResourceConfiguration} childResourceConfiguration - Child resource configuration.
686
+ * @param {"create" | "update" | "destroy"} action - Frontend action.
687
+ * @returns {string} - Ability action for the child resource.
688
+ */
689
+ _resolveChildAbilityAction(childResourceConfiguration, action) {
690
+ const abilities = childResourceConfiguration?.abilities
691
+
692
+ if (!abilities || typeof abilities !== "object" || Array.isArray(abilities)) {
693
+ throw new Error(`Nested child resource must define an 'abilities' object to authorize nested ${action}.`)
694
+ }
695
+
696
+ const abilityAction = /**
583
697
  * Narrows the runtime value to the documented type.
584
- @type {?} */ (childResource)._applyNestedAttributes(child, entry.nestedAttributes, controller, childPermit);
585
- }
586
- }
587
- }
588
- }
589
- /**
590
- * Resolves the ability action for a child resource using the child's own
591
- * `abilities` mapping — never the parent controller's. This preserves
592
- * custom mappings like `{update: "manage"}` and catches unmapped actions
593
- * instead of silently defaulting to the raw action name.
594
- * @param {import("../configuration-types.js").FrontendModelResourceConfiguration} childResourceConfiguration - Child resource configuration.
595
- * @param {"create" | "update" | "destroy"} action - Frontend action.
596
- * @returns {string} - Ability action for the child resource.
597
- */
598
- _resolveChildAbilityAction(childResourceConfiguration, action) {
599
- const abilities = childResourceConfiguration?.abilities;
600
- if (!abilities || typeof abilities !== "object" || Array.isArray(abilities)) {
601
- throw new Error(`Nested child resource must define an 'abilities' object to authorize nested ${action}.`);
602
- }
603
- const abilityAction = /**
604
- * Narrows the runtime value to the documented type.
605
- @type {Record<string, string>} */ (abilities)[action];
606
- if (typeof abilityAction !== "string" || abilityAction.length < 1) {
607
- throw new Error(`Nested child resource must define abilities.${action}.`);
608
- }
609
- return abilityAction;
610
- }
611
- /**
612
- * Finds an existing child for a nested update/destroy, scoped to the
613
- * child's own model class, the parent's foreign key, AND the child
614
- * resource's ability mapping for the requested action. Throws when the
615
- * child does not exist, does not belong to the current parent, or is
616
- * not authorized all of which must roll the transaction back.
617
- * @param {object} args - Arguments.
618
- * @param {import("../authorization/ability.js").default | undefined} args.ability - Current ability.
619
- * @param {"update" | "destroy"} args.action - Frontend action.
620
- * @param {import("../configuration-types.js").FrontendModelResourceConfiguration} args.childResourceConfiguration - Child resource configuration.
621
- * @param {string} args.foreignKey - Foreign-key attribute on the child pointing to the parent.
622
- * @param {string | number} args.id - Child id from the payload.
623
- * @param {import("../database/record/index.js").default} args.parent - Parent model instance.
624
- * @param {string} args.relationshipName - Parent's relationship name (for error messages).
625
- * @param {typeof import("../database/record/index.js").default} args.targetModelClass - Child model class.
626
- * @returns {Promise<import("../database/record/index.js").default>} - Authorized, parent-linked child model.
627
- */
628
- async _findScopedChild({ ability, action, childResourceConfiguration, foreignKey, id, parent, relationshipName, targetModelClass }) {
629
- const primaryKey = targetModelClass.primaryKey();
630
- const lookup = { [primaryKey]: id, [foreignKey]: parent.id() };
631
- const query = ability
632
- ? /**
633
- * Narrows the runtime value to the documented type.
634
- @type {?} */
635
- (targetModelClass).accessibleFor(this._resolveChildAbilityAction(childResourceConfiguration, action), ability)
636
- : /**
698
+ @type {Record<string, string>} */ (abilities)[action]
699
+
700
+ if (typeof abilityAction !== "string" || abilityAction.length < 1) {
701
+ throw new Error(`Nested child resource must define abilities.${action}.`)
702
+ }
703
+
704
+ return abilityAction
705
+ }
706
+
707
+ /**
708
+ * Finds an existing child for a nested update/destroy, scoped to the
709
+ * child's own model class, the parent's foreign key, AND the child
710
+ * resource's ability mapping for the requested action. Throws when the
711
+ * child does not exist, does not belong to the current parent, or is
712
+ * not authorized — all of which must roll the transaction back.
713
+ * @param {object} args - Arguments.
714
+ * @param {import("../authorization/ability.js").default | undefined} args.ability - Current ability.
715
+ * @param {"update" | "destroy"} args.action - Frontend action.
716
+ * @param {import("../configuration-types.js").FrontendModelResourceConfiguration} args.childResourceConfiguration - Child resource configuration.
717
+ * @param {string} args.foreignKey - Foreign-key attribute on the child pointing to the parent.
718
+ * @param {string | number} args.id - Child id from the payload.
719
+ * @param {import("../database/record/index.js").default} args.parent - Parent model instance.
720
+ * @param {string} args.relationshipName - Parent's relationship name (for error messages).
721
+ * @param {typeof import("../database/record/index.js").default} args.targetModelClass - Child model class.
722
+ * @returns {Promise<import("../database/record/index.js").default>} - Authorized, parent-linked child model.
723
+ */
724
+ async _findScopedChild({ability, action, childResourceConfiguration, foreignKey, id, parent, relationshipName, targetModelClass}) {
725
+ const primaryKey = targetModelClass.primaryKey()
726
+ const lookup = {[primaryKey]: id, [foreignKey]: parent.id()}
727
+ const query = ability
728
+ ? /**
729
+ * Narrows the runtime value to the documented type.
730
+ @type {?} */ (targetModelClass).accessibleFor(this._resolveChildAbilityAction(childResourceConfiguration, action), ability)
731
+ : /**
732
+ * Narrows the runtime value to the documented type.
733
+ @type {?} */ (targetModelClass).where({})
734
+
735
+ const existing = await query.findBy(lookup)
736
+
737
+ if (!existing) {
738
+ throw new Error(`Cannot ${action} nested ${relationshipName}[id=${id}]: record not found, does not belong to parent ${parent.getModelClass().name}[id=${parent.id()}], or is not authorized.`)
739
+ }
740
+
741
+ return existing
742
+ }
743
+
744
+ /**
745
+ * Verifies an already-saved nested child is authorized under the child
746
+ * resource's own `create` ability. Rolls back via thrown error when not
747
+ * authorized so the outer transaction destroys the insert.
748
+ * @param {object} args - Arguments.
749
+ * @param {import("../authorization/ability.js").default | undefined} args.ability - Current ability.
750
+ * @param {import("../database/record/index.js").default} args.child - Child model instance just created.
751
+ * @param {import("../configuration-types.js").FrontendModelResourceConfiguration} args.childResourceConfiguration - Child resource configuration.
752
+ * @param {string} args.relationshipName - Parent's relationship name (for error messages).
753
+ * @param {typeof import("../database/record/index.js").default} args.targetModelClass - Child model class.
754
+ * @returns {Promise<void>}
755
+ */
756
+ async _authorizeCreatedChild({ability, child, childResourceConfiguration, relationshipName, targetModelClass}) {
757
+ if (!ability) return
758
+
759
+ const abilityAction = this._resolveChildAbilityAction(childResourceConfiguration, "create")
760
+ const primaryKey = targetModelClass.primaryKey()
761
+ const authorizedIds = await /**
762
+ * Narrows the runtime value to the documented type.
763
+ @type {?} */ (targetModelClass)
764
+ .accessibleFor(abilityAction, ability)
765
+ .where({[primaryKey]: child.readAttribute(primaryKey)})
766
+ .pluck(primaryKey)
767
+
768
+ if (authorizedIds.length === 0) {
769
+ throw new Error(`Nested create on ${relationshipName}[${targetModelClass.name}] not authorized.`)
770
+ }
771
+ }
772
+
773
+ /**
774
+ * Best-effort foreign-key inference for relationships that don't declare it.
775
+ * @param {import("../database/record/index.js").default} parent - Parent model.
776
+ * @param {{foreignKey?: string}} definition - Relationship definition.
777
+ * @returns {string} - Foreign-key attribute name.
778
+ */
779
+ _inferForeignKey(parent, definition) {
780
+ if (definition.foreignKey) return definition.foreignKey
781
+
782
+ const parentModelName = parent.getModelClass().name || ""
783
+ const underscored = parentModelName.replace(/([A-Z])/g, (match, letter, index) => (index === 0 ? letter.toLowerCase() : `_${letter.toLowerCase()}`))
784
+
785
+ return `${inflection.camelize(underscored, true)}Id`
786
+ }
787
+
788
+ /**
789
+ * After nested writes, preload every relationship declared in the
790
+ * parent's permit so the post-save serialize step emits them and the
791
+ * client can reconcile ids.
792
+ * @param {import("../database/record/index.js").default} model - Saved parent model.
793
+ * @param {{attributes: string[], nested: Record<string, ?>}} permit - Parsed parent permit.
794
+ * @returns {Promise<void>}
795
+ */
796
+ async _preloadNestedWritableRelationships(model, permit) {
797
+ const relationshipNames = Object.keys(permit.nested)
798
+
799
+ if (relationshipNames.length === 0) return
800
+
801
+ for (const relationshipName of relationshipNames) {
802
+ if (typeof /**
803
+ * Narrows the runtime value to the documented type.
804
+ @type {?} */ (model).loadRelationship === "function") {
805
+ await /**
637
806
  * Narrows the runtime value to the documented type.
638
- @type {?} */
639
- (targetModelClass).where({});
640
- const existing = await query.findBy(lookup);
641
- if (!existing) {
642
- throw new Error(`Cannot ${action} nested ${relationshipName}[id=${id}]: record not found, does not belong to parent ${parent.getModelClass().name}[id=${parent.id()}], or is not authorized.`);
643
- }
644
- return existing;
645
- }
646
- /**
647
- * Verifies an already-saved nested child is authorized under the child
648
- * resource's own `create` ability. Rolls back via thrown error when not
649
- * authorized so the outer transaction destroys the insert.
650
- * @param {object} args - Arguments.
651
- * @param {import("../authorization/ability.js").default | undefined} args.ability - Current ability.
652
- * @param {import("../database/record/index.js").default} args.child - Child model instance just created.
653
- * @param {import("../configuration-types.js").FrontendModelResourceConfiguration} args.childResourceConfiguration - Child resource configuration.
654
- * @param {string} args.relationshipName - Parent's relationship name (for error messages).
655
- * @param {typeof import("../database/record/index.js").default} args.targetModelClass - Child model class.
656
- * @returns {Promise<void>}
657
- */
658
- async _authorizeCreatedChild({ ability, child, childResourceConfiguration, relationshipName, targetModelClass }) {
659
- if (!ability)
660
- return;
661
- const abilityAction = this._resolveChildAbilityAction(childResourceConfiguration, "create");
662
- const primaryKey = targetModelClass.primaryKey();
663
- const authorizedIds = await /**
664
- * Narrows the runtime value to the documented type.
665
- @type {?} */ (targetModelClass)
666
- .accessibleFor(abilityAction, ability)
667
- .where({ [primaryKey]: child.readAttribute(primaryKey) })
668
- .pluck(primaryKey);
669
- if (authorizedIds.length === 0) {
670
- throw new Error(`Nested create on ${relationshipName}[${targetModelClass.name}] not authorized.`);
671
- }
672
- }
673
- /**
674
- * Best-effort foreign-key inference for relationships that don't declare it.
675
- * @param {import("../database/record/index.js").default} parent - Parent model.
676
- * @param {{foreignKey?: string}} definition - Relationship definition.
677
- * @returns {string} - Foreign-key attribute name.
678
- */
679
- _inferForeignKey(parent, definition) {
680
- if (definition.foreignKey)
681
- return definition.foreignKey;
682
- const parentModelName = parent.getModelClass().name || "";
683
- const underscored = parentModelName.replace(/([A-Z])/g, (match, letter, index) => (index === 0 ? letter.toLowerCase() : `_${letter.toLowerCase()}`));
684
- return `${inflection.camelize(underscored, true)}Id`;
685
- }
686
- /**
687
- * After nested writes, preload every relationship declared in the
688
- * parent's permit so the post-save serialize step emits them and the
689
- * client can reconcile ids.
690
- * @param {import("../database/record/index.js").default} model - Saved parent model.
691
- * @param {{attributes: string[], nested: Record<string, ?>}} permit - Parsed parent permit.
692
- * @returns {Promise<void>}
693
- */
694
- async _preloadNestedWritableRelationships(model, permit) {
695
- const relationshipNames = Object.keys(permit.nested);
696
- if (relationshipNames.length === 0)
697
- return;
698
- for (const relationshipName of relationshipNames) {
699
- if (typeof /**
700
- * Narrows the runtime value to the documented type.
701
- @type {?} */ (model).loadRelationship === "function") {
702
- await /**
703
- * Narrows the runtime value to the documented type.
704
- @type {?} */ (model).loadRelationship(relationshipName);
705
- }
706
- }
807
+ @type {?} */ (model).loadRelationship(relationshipName)
808
+ }
707
809
  }
810
+ }
708
811
  }
812
+
709
813
  /**
710
814
  * Parses the Rails/api_maker-style flat permit spec returned from
711
815
  * `permittedParams(arg)` into a structured shape used internally by the
@@ -726,41 +830,44 @@ export default class FrontendModelBaseResource extends AuthorizationBaseResource
726
830
  * @returns {{attributes: string[], nested: Record<string, {attributes: string[], nested: Record<string, ?>}>}} - Parsed structure.
727
831
  */
728
832
  function parsePermittedParams(permitSpec) {
729
- /**
730
- * Attributes.
731
- @type {string[]} */
732
- const attributes = [];
733
- /**
734
- * Nested.
735
- @type {Record<string, {attributes: string[], nested: Record<string, ?>}>} */
736
- const nested = {};
737
- if (!Array.isArray(permitSpec))
738
- return { attributes, nested };
739
- for (const entry of permitSpec) {
740
- if (typeof entry === "string") {
741
- attributes.push(entry);
833
+ /**
834
+ * Attributes.
835
+ @type {string[]} */
836
+ const attributes = []
837
+ /**
838
+ * Nested.
839
+ @type {Record<string, {attributes: string[], nested: Record<string, ?>}>} */
840
+ const nested = {}
841
+
842
+ if (!Array.isArray(permitSpec)) return {attributes, nested}
843
+
844
+ for (const entry of permitSpec) {
845
+ if (typeof entry === "string") {
846
+ attributes.push(entry)
847
+ } else if (entry && typeof entry === "object" && !Array.isArray(entry)) {
848
+ for (const [key, value] of Object.entries(entry)) {
849
+ if (!key.endsWith("Attributes")) {
850
+ throw new Error(`Invalid permittedParams entry: nested relationship keys must end in "Attributes" (got "${key}"). Use "${key}Attributes" instead.`)
742
851
  }
743
- else if (entry && typeof entry === "object" && !Array.isArray(entry)) {
744
- for (const [key, value] of Object.entries(entry)) {
745
- if (!key.endsWith("Attributes")) {
746
- throw new Error(`Invalid permittedParams entry: nested relationship keys must end in "Attributes" (got "${key}"). Use "${key}Attributes" instead.`);
747
- }
748
- const relationshipName = key.slice(0, -"Attributes".length);
749
- if (!relationshipName) {
750
- throw new Error(`Invalid permittedParams entry: empty relationship name in key "${key}".`);
751
- }
752
- if (!Array.isArray(value)) {
753
- throw new Error(`Invalid permittedParams entry for "${key}": expected array permit spec, got ${typeof value}.`);
754
- }
755
- nested[relationshipName] = parsePermittedParams(value);
756
- }
852
+ const relationshipName = key.slice(0, -"Attributes".length)
853
+
854
+ if (!relationshipName) {
855
+ throw new Error(`Invalid permittedParams entry: empty relationship name in key "${key}".`)
757
856
  }
758
- else {
759
- throw new Error(`Invalid permittedParams entry: expected string or nested-attributes object, got ${typeof entry}.`);
857
+ if (!Array.isArray(value)) {
858
+ throw new Error(`Invalid permittedParams entry for "${key}": expected array permit spec, got ${typeof value}.`)
760
859
  }
761
- }
762
- return { attributes, nested };
860
+
861
+ nested[relationshipName] = parsePermittedParams(value)
862
+ }
863
+ } else {
864
+ throw new Error(`Invalid permittedParams entry: expected string or nested-attributes object, got ${typeof entry}.`)
865
+ }
866
+ }
867
+
868
+ return {attributes, nested}
763
869
  }
870
+
764
871
  /**
765
872
  * Runs filter writable frontend model attributes.
766
873
  * @param {Record<string, ?>} receiver - Model instance or prototype.
@@ -772,54 +879,57 @@ function parsePermittedParams(permitSpec) {
772
879
  function filterWritableFrontendModelAttributes(receiver, attributes, resource = /**
773
880
  * Narrows the runtime value to the documented type.
774
881
  @type {FrontendModelBaseResource | null} */ (null), permittedAttributeNames = null) {
775
- // Frontend-model writes should fail fast when callers submit read-only or unknown attrs.
776
- // Silent drops hide contract mistakes in generated models and app-side wrapper code.
777
- /**
778
- * Writable attributes.
779
- @type {Record<string, ?>} */
780
- const writableAttributes = {};
781
- /**
782
- * Invalid attributes.
783
- @type {string[]} */
784
- const invalidAttributes = [];
785
- /**
786
- * Not permitted attributes.
787
- @type {string[]} */
788
- const notPermittedAttributes = [];
789
- const permitSet = Array.isArray(permittedAttributeNames) ? new Set(permittedAttributeNames) : null;
790
- const translatedSet = resource ? new Set(/**
791
- * Narrows the runtime value to the documented type.
792
- @type {typeof FrontendModelBaseResource} */ (resource.constructor).translatedAttributes || []) : new Set();
793
- for (const [attributeName, value] of Object.entries(attributes)) {
794
- if (permitSet && !permitSet.has(attributeName)) {
795
- notPermittedAttributes.push(attributeName);
796
- continue;
797
- }
798
- const setterName = `set${inflection.camelize(attributeName)}`;
799
- const resourceSetterName = `${setterName}Attribute`;
800
- if (setterName in receiver) {
801
- writableAttributes[attributeName] = value;
802
- }
803
- else if (resource && typeof /**
804
- * Narrows the runtime value to the documented type.
805
- @type {Record<string, ?>} */ ( /**
806
- * Narrows the runtime value to the documented type.
807
- @type {?} */(resource))[resourceSetterName] === "function") {
808
- writableAttributes[attributeName] = value;
809
- }
810
- else if (translatedSet.has(attributeName)) {
811
- writableAttributes[attributeName] = value;
812
- }
813
- else {
814
- invalidAttributes.push(attributeName);
815
- }
816
- }
817
- if (notPermittedAttributes.length > 0) {
818
- throw new Error(`Frontend model write attributes not permitted by permittedParams(): ${notPermittedAttributes.join(", ")}`);
819
- }
820
- if (invalidAttributes.length > 0) {
821
- throw new Error(`Invalid frontend model write attributes: ${invalidAttributes.join(", ")}`);
822
- }
823
- return writableAttributes;
882
+ // Frontend-model writes should fail fast when callers submit read-only or unknown attrs.
883
+ // Silent drops hide contract mistakes in generated models and app-side wrapper code.
884
+ /**
885
+ * Writable attributes.
886
+ @type {Record<string, ?>} */
887
+ const writableAttributes = {}
888
+ /**
889
+ * Invalid attributes.
890
+ @type {string[]} */
891
+ const invalidAttributes = []
892
+ /**
893
+ * Not permitted attributes.
894
+ @type {string[]} */
895
+ const notPermittedAttributes = []
896
+
897
+ const permitSet = Array.isArray(permittedAttributeNames) ? new Set(permittedAttributeNames) : null
898
+ const translatedSet = resource ? new Set(/**
899
+ * Narrows the runtime value to the documented type.
900
+ @type {typeof FrontendModelBaseResource} */ (resource.constructor).translatedAttributes || []) : new Set()
901
+
902
+ for (const [attributeName, value] of Object.entries(attributes)) {
903
+ if (permitSet && !permitSet.has(attributeName)) {
904
+ notPermittedAttributes.push(attributeName)
905
+ continue
906
+ }
907
+
908
+ const setterName = `set${inflection.camelize(attributeName)}`
909
+ const resourceSetterName = `${setterName}Attribute`
910
+
911
+ if (setterName in receiver) {
912
+ writableAttributes[attributeName] = value
913
+ } else if (resource && typeof /**
914
+ * Narrows the runtime value to the documented type.
915
+ @type {Record<string, ?>} */ (/**
916
+ * Narrows the runtime value to the documented type.
917
+ @type {?} */ (resource))[resourceSetterName] === "function") {
918
+ writableAttributes[attributeName] = value
919
+ } else if (translatedSet.has(attributeName)) {
920
+ writableAttributes[attributeName] = value
921
+ } else {
922
+ invalidAttributes.push(attributeName)
923
+ }
924
+ }
925
+
926
+ if (notPermittedAttributes.length > 0) {
927
+ throw new Error(`Frontend model write attributes not permitted by permittedParams(): ${notPermittedAttributes.join(", ")}`)
928
+ }
929
+
930
+ if (invalidAttributes.length > 0) {
931
+ throw new Error(`Invalid frontend model write attributes: ${invalidAttributes.join(", ")}`)
932
+ }
933
+
934
+ return writableAttributes
824
935
  }
825
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFzZS1yZXNvdXJjZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9mcm9udGVuZC1tb2RlbC1yZXNvdXJjZS9iYXNlLXJlc291cmNlLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWixPQUFPLHlCQUF5QixNQUFNLG1DQUFtQyxDQUFBO0FBQ3pFLE9BQU8sS0FBSyxVQUFVLE1BQU0sWUFBWSxDQUFBO0FBRXhDOzs7Ozs7OztHQVFHO0FBRUg7Ozs7Ozs7Ozs7R0FVRztBQUVIOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE9BQU8sT0FBTyx5QkFBMEIsU0FBUSx5QkFBeUI7SUFDOUU7O3lEQUVxRDtJQUNyRCxNQUFNLENBQUMsVUFBVSxHQUFHLFNBQVMsQ0FBQTtJQUM3Qjs7cUNBRWlDO0lBQ2pDLE1BQU0sQ0FBQyxTQUFTLEdBQUcsU0FBUyxDQUFBO0lBQzVCOzs4Q0FFMEM7SUFDMUMsTUFBTSxDQUFDLFdBQVcsR0FBRyxTQUFTLENBQUE7SUFDOUI7O3FDQUVpQztJQUNqQyxNQUFNLENBQUMsa0JBQWtCLEdBQUcsU0FBUyxDQUFBO0lBQ3JDOztxQ0FFaUM7SUFDakMsTUFBTSxDQUFDLHlCQUF5QixHQUFHLFNBQVMsQ0FBQTtJQUM1Qzs7cUNBRWlDO0lBQ2pDLE1BQU0sQ0FBQyxjQUFjLEdBQUcsU0FBUyxDQUFBO0lBQ2pDOztxQ0FFaUM7SUFDakMsTUFBTSxDQUFDLHFCQUFxQixHQUFHLFNBQVMsQ0FBQTtJQUN4Qzs7cUNBRWlDO0lBQ2pDLE1BQU0sQ0FBQyxhQUFhLEdBQUcsU0FBUyxDQUFBO0lBQ2hDOztxQ0FFaUM7SUFDakMsTUFBTSxDQUFDLG9CQUFvQixHQUFHLFNBQVMsQ0FBQTtJQUV2Qzs7O09BR0c7SUFDSCxZQUFZLElBQUk7UUFDZCxLQUFLLENBQUM7WUFDSixPQUFPLEVBQUUsU0FBUyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsU0FBUztZ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SUFFRCxPQUFPLGtCQUFrQixDQUFBO0FBQzNCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IEF1dGhvcml6YXRpb25CYXNlUmVzb3VyY2UgZnJvbSBcIi4uL2F1dGhvcml6YXRpb24vYmFzZS1yZXNvdXJjZS5qc1wiXG5pbXBvcnQgKiBhcyBpbmZsZWN0aW9uIGZyb20gXCJpbmZsZWN0aW9uXCJcblxuLyoqXG4gKiBGcm9udGVuZE1vZGVsUmVzb3VyY2VDb250cm9sbGVyQXJncyB0eXBlLlxuICogQHR5cGVkZWYge29iamVjdH0gRnJvbnRlbmRNb2RlbFJlc291cmNlQ29udHJvbGxlckFyZ3NcbiAqIEBwcm9wZXJ0eSB7aW1wb3J0KFwiLi4vY29udHJvbGxlci5qc1wiKS5kZWZhdWx0fSBjb250cm9sbGVyIC0gRnJvbnRlbmQtbW9kZWwgY29udHJvbGxlciBpbnN0YW5jZS5cbiAqIEBwcm9wZXJ0eSB7dHlwZW9mIGltcG9ydChcIi4uL2RhdGFiYXNlL3JlY29yZC9pbmRleC5qc1wiKS5kZWZhdWx0fSBtb2RlbENsYXNzIC0gQmFja2luZyBtb2RlbCBjbGFzcy5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBtb2RlbE5hbWUgLSBNb2RlbCBuYW1lLlxuICogQHByb3BlcnR5IHtSZWNvcmQ8c3RyaW5nLCA/Pn0gcGFyYW1zIC0gUmVxdWVzdCBwYXJhbXMuXG4gKiBAcHJvcGVydHkge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuTm9ybWFsaXplZEZyb250ZW5kTW9kZWxSZXNvdXJjZUNvbmZpZ3VyYXRpb24gfCBpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLkZyb250ZW5kTW9kZWxSZXNvdXJjZUNvbmZpZ3VyYXRpb259IHJlc291cmNlQ29uZmlndXJhdGlvbiAtIE5vcm1hbGl6ZWQgcmVzb3VyY2UgY29uZmlndXJhdGlvbiAob3IgcmF3IGlucHV0IHNoYXBlIGR1cmluZyBlYXJseSBib290c3RyYXApLlxuICovXG5cbi8qKlxuICogRnJvbnRlbmRNb2RlbFJlc291cmNlQWJpbGl0eUFyZ3MgdHlwZS5cbiAqIEB0eXBlZGVmIHtvYmplY3R9IEZyb250ZW5kTW9kZWxSZXNvdXJjZUFiaWxpdHlBcmdzXG4gKiBAcHJvcGVydHkge2ltcG9ydChcIi4uL2F1dGhvcml6YXRpb24vYWJpbGl0eS5qc1wiKS5kZWZhdWx0fSBbYWJpbGl0eV0gLSBBYmlsaXR5IGluc3RhbmNlIHdoZW4gdGhlIHJlc291cmNlIGlzIHVzZWQgZGlyZWN0bHkgZm9yIGF1dGhvcml6YXRpb24uXG4gKiBAcHJvcGVydHkge1JlY29yZDxzdHJpbmcsID8+fSBbY29udGV4dF0gLSBBYmlsaXR5IGNvbnRleHQuXG4gKiBAcHJvcGVydHkge1JlY29yZDxzdHJpbmcsID8+fSBbbG9jYWxzXSAtIEFiaWxpdHkgbG9jYWxzLlxuICogQHByb3BlcnR5IHt0eXBlb2YgaW1wb3J0KFwiLi4vZGF0YWJhc2UvcmVjb3JkL2luZGV4LmpzXCIpLmRlZmF1bHR9IFttb2RlbENsYXNzXSAtIE9wdGlvbmFsIGJhY2tpbmcgbW9kZWwgY2xhc3Mgb3ZlcnJpZGUuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gW21vZGVsTmFtZV0gLSBPcHRpb25hbCBtb2RlbCBuYW1lIG92ZXJyaWRlLlxuICogQHByb3BlcnR5IHtSZWNvcmQ8c3RyaW5nLCA/Pn0gW3BhcmFtc10gLSBPcHRpb25hbCBwYXJhbXMgb3ZlcnJpZGUuXG4gKiBAcHJvcGVydHkge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuTm9ybWFsaXplZEZyb250ZW5kTW9kZWxSZXNvdXJjZUNvbmZpZ3VyYXRpb24gfCBpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLkZyb250ZW5kTW9kZWxSZXNvdXJjZUNvbmZpZ3VyYXRpb259IFtyZXNvdXJjZUNvbmZpZ3VyYXRpb25dIC0gT3B0aW9uYWwgbm9ybWFsaXplZCByZXNvdXJjZSBjb25maWd1cmF0aW9uLlxuICovXG5cbi8qKlxuICogQmFzZSBjbGFzcyBmb3IgYmFja2VuZCBmcm9udGVuZC1tb2RlbCByZXNvdXJjZXMuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEZyb250ZW5kTW9kZWxCYXNlUmVzb3VyY2UgZXh0ZW5kcyBBdXRob3JpemF0aW9uQmFzZVJlc291cmNlIHtcbiAgLyoqXG4gICAqIEF0dHJpYnV0ZXMuXG4gICAgQHR5cGUge1JlY29yZDxzdHJpbmcsID8+IHwgc3RyaW5nW10gfCB1bmRlZmluZWR9ICovXG4gIHN0YXRpYyBhdHRyaWJ1dGVzID0gdW5kZWZpbmVkXG4gIC8qKlxuICAgKiBBYmlsaXRpZXMuXG4gICAgQHR5cGUge3N0cmluZ1tdIHwgdW5kZWZpbmVkfSAqL1xuICBzdGF0aWMgYWJpbGl0aWVzID0gdW5kZWZpbmVkXG4gIC8qKlxuICAgKiBBdHRhY2htZW50cy5cbiAgICBAdHlwZSB7UmVjb3JkPHN0cmluZywgPz4gfCB1bmRlZmluZWR9ICovXG4gIHN0YXRpYyBhdHRhY2htZW50cyA9IHVuZGVmaW5lZFxuICAvKipcbiAgICogQ29sbGVjdGlvbiBjb21tYW5kcy5cbiAgICBAdHlwZSB7c3RyaW5nW10gfCB1bmRlZmluZWR9ICovXG4gIHN0YXRpYyBjb2xsZWN0aW9uQ29tbWFuZHMgPSB1bmRlZmluZWRcbiAgLyoqXG4gICAqIEJ1aWx0IGluIGNvbGxlY3Rpb24gY29tbWFuZHMuXG4gICAgQHR5cGUge3N0cmluZ1tdIHwgdW5kZWZpbmVkfSAqL1xuICBzdGF0aWMgYnVpbHRJbkNvbGxlY3Rpb25Db21tYW5kcyA9IHVuZGVmaW5lZFxuICAvKipcbiAgICogTWVtYmVyIGNvbW1hbmRzLlxuICAgIEB0eXBlIHtzdHJpbmdbXSB8IHVuZGVmaW5lZH0gKi9cbiAgc3RhdGljIG1lbWJlckNvbW1hbmRzID0gdW5kZWZpbmVkXG4gIC8qKlxuICAgKiBCdWlsdCBpbiBtZW1iZXIgY29tbWFuZHMuXG4gICAgQHR5cGUge3N0cmluZ1tdIHwgdW5kZWZpbmVkfSAqL1xuICBzdGF0aWMgYnVpbHRJbk1lbWJlckNvbW1hbmRzID0gdW5kZWZpbmVkXG4gIC8qKlxuICAgKiBSZWxhdGlvbnNoaXBzLlxuICAgIEB0eXBlIHtzdHJpbmdbXSB8IHVuZGVmaW5lZH0gKi9cbiAgc3RhdGljIHJlbGF0aW9uc2hpcHMgPSB1bmRlZmluZWRcbiAgLyoqXG4gICAqIFRyYW5zbGF0ZWQgYXR0cmlidXRlcy5cbiAgICBAdHlwZSB7c3RyaW5nW10gfCB1bmRlZmluZWR9ICovXG4gIHN0YXRpYyB0cmFuc2xhdGVkQXR0cmlidXRlcyA9IHVuZGVmaW5lZFxuXG4gIC8qKlxuICAgKiBSdW5zIGNvbnN0cnVjdG9yLlxuICAgKiBAcGFyYW0ge0Zyb250ZW5kTW9kZWxSZXNvdXJjZUFiaWxpdHlBcmdzIHwgRnJvbnRlbmRNb2RlbFJlc291cmNlQ29udHJvbGxlckFyZ3N9IGFyZ3MgLSBSZXNvdXJjZSBhcmdzLlxuICAgKi9cbiAgY29uc3RydWN0b3IoYXJncykge1xuICAgIHN1cGVyKHtcbiAgICAgIGFiaWxpdHk6IFwiYWJpbGl0eVwiIGluIGFyZ3MgPyBhcmdzLmFiaWxpdHkgOiB1bmRlZmluZWQsXG4gICAgICBjb250ZXh0OiBcImNvbnRleHRcIiBpbiBhcmdzID8gYXJncy5jb250ZXh0IHx8IHt9IDoge30sXG4gICAgICBsb2NhbHM6IFwibG9jYWxzXCIgaW4gYXJncyA/IGFyZ3MubG9jYWxzIHx8IHt9IDoge31cbiAgICB9KVxuXG4gICAgdGhpcy5jb250cm9sbGVyID0gXCJjb250cm9sbGVyXCIgaW4gYXJncyA/IGFyZ3MuY29udHJvbGxlciA6IHVuZGVmaW5lZFxuICAgIHRoaXMubW9kZWxDbGFzc1ZhbHVlID0gXCJtb2RlbENsYXNzXCIgaW4gYXJncyA/IGFyZ3MubW9kZWxDbGFzcyA6IC8qKlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQHR5cGUge3R5cGVvZiBpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdCB8IHVuZGVmaW5lZH0gKi8gKC8qKlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICogTmFycm93cyB0aGUgcnVudGltZSB2YWx1ZSB0byB0aGUgZG9jdW1lbnRlZCB0eXBlLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBAdHlwZSB7dHlwZW9mIEZyb250ZW5kTW9kZWxCYXNlUmVzb3VyY2V9ICovICh0aGlzLmNvbnN0cnVjdG9yKS5tb2RlbENsYXNzKCkpXG4gICAgdGhpcy5tb2RlbE5hbWVWYWx1ZSA9IFwibW9kZWxOYW1lXCIgaW4gYXJncyA/IGFyZ3MubW9kZWxOYW1lIDogKHRoaXMubW9kZWxDbGFzc1ZhbHVlPy5nZXRNb2RlbE5hbWUgPyB0aGlzLm1vZGVsQ2xhc3NWYWx1ZS5nZXRNb2RlbE5hbWUoKSA6IHRoaXMubW9kZWxDbGFzc1ZhbHVlPy5uYW1lIHx8IFwiXCIpXG4gICAgdGhpcy5wYXJhbXNWYWx1ZSA9IFwicGFyYW1zXCIgaW4gYXJncyA/IGFyZ3MucGFyYW1zIDogdW5kZWZpbmVkXG4gICAgdGhpcy5yZXNvdXJjZUNvbmZpZ3VyYXRpb25WYWx1ZSA9IFwicmVzb3VyY2VDb25maWd1cmF0aW9uXCIgaW4gYXJncyA/IGFyZ3MucmVzb3VyY2VDb25maWd1cmF0aW9uIDogLyoqXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBAdHlwZSB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5Gcm9udGVuZE1vZGVsUmVzb3VyY2VDb25maWd1cmF0aW9ufSAqLyAoe2F0dHJpYnV0ZXM6IFtdfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIHR5cGVkIGNvbnRyb2xsZXIgaW5zdGFuY2UuXG4gICAqIEByZXR1cm5zIHtpbXBvcnQoXCIuLi9jb250cm9sbGVyLmpzXCIpLmRlZmF1bHQgJiB7XG4gICAqICAgZnJvbnRlbmRNb2RlbEF1dGhvcml6ZWRRdWVyeTogKGFjdGlvbjogXCJpbmRleFwiIHwgXCJmaW5kXCIgfCBcImNyZWF0ZVwiIHwgXCJ1cGRhdGVcIiB8IFwiZGVzdHJveVwiIHwgXCJhdHRhY2hcIiB8IFwiZG93bmxvYWRcIiB8IFwidXJsXCIpID0+IGltcG9ydChcIi4uL2RhdGFiYXNlL3F1ZXJ5L21vZGVsLWNsYXNzLXF1ZXJ5LmpzXCIpLmRlZmF1bHQ8dW5rbm93bj4sXG4gICAqICAgZnJvbnRlbmRNb2RlbEluZGV4UXVlcnk6ICgpID0+IGltcG9ydChcIi4uL2RhdGFiYXNlL3F1ZXJ5L21vZGVsLWNsYXNzLXF1ZXJ5LmpzXCIpLmRlZmF1bHQ8dW5rbm93bj4sXG4gICAqICAgZnJvbnRlbmRNb2RlbFByZWxvYWQ6ICgpID0+IGltcG9ydChcIi4uL2RhdGFiYXNlL3F1ZXJ5L2luZGV4LmpzXCIpLk5lc3RlZFByZWxvYWRSZWNvcmQgfCBudWxsLFxuICAgKiAgIHNlcmlhbGl6ZUZyb250ZW5kTW9kZWw6IChtb2RlbDogaW1wb3J0KFwiLi4vZGF0YWJhc2UvcmVjb3JkL2luZGV4LmpzXCIpLmRlZmF1bHQpID0+IFByb21pc2U8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+XG4gICAqIH19IC0gQ29udHJvbGxlciBpbnN0YW5jZSB3aXRoIGZyb250ZW5kLW1vZGVsIGhlbHBlcnMuXG4gICAqL1xuICB0eXBlZENvbnRyb2xsZXJJbnN0YW5jZSgpIHtcbiAgICByZXR1cm4gLyoqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS4gQHR5cGUgez99ICovICh0aGlzLmNvbnRyb2xsZXIpXG4gIH1cblxuICAvKipcbiAgICogUnVucyByZXNvdXJjZSBjb25maWcuXG4gICAqIEByZXR1cm5zIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLkZyb250ZW5kTW9kZWxSZXNvdXJjZUNvbmZpZ3VyYXRpb259IC0gU3RhdGljIHJlc291cmNlIGNvbmZpZyAocmF3IHVzZXIgaW5wdXQgc2hhcGU7IGNvbnN1bWVycyBub3JtYWxpemUpLlxuICAgKi9cbiAgc3RhdGljIHJlc291cmNlQ29uZmlnKCkge1xuICAgIC8qKlxuICAgICAqIENvbmZpZy5cbiAgICAgIEB0eXBlIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLkZyb250ZW5kTW9kZWxSZXNvdXJjZUNvbmZpZ3VyYXRpb259ICovXG4gICAgY29uc3QgY29uZmlnID0ge1xuICAgICAgYXR0cmlidXRlczogdGhpcy5hdHRyaWJ1dGVzIHx8IFtdXG4gICAgfVxuXG4gICAgaWYgKHRoaXMuYWJpbGl0aWVzKSBjb25maWcuYWJpbGl0aWVzID0gdGhpcy5hYmlsaXRpZXNcbiAgICBpZiAodGhpcy5hdHRhY2htZW50cykgY29uZmlnLmF0dGFjaG1lbnRzID0gdGhpcy5hdHRhY2htZW50c1xuICAgIGlmICh0aGlzLmJ1aWx0SW5Db2xsZWN0aW9uQ29tbWFuZHMpIGNvbmZpZy5idWlsdEluQ29sbGVjdGlvbkNvbW1hbmRzID0gdGhpcy5idWlsdEluQ29sbGVjdGlvbkNvbW1hbmRzXG4gICAgaWYgKHRoaXMuYnVpbHRJbk1lbWJlckNvbW1hbmRzKSBjb25maWcuYnVpbHRJbk1lbWJlckNvbW1hbmRzID0gdGhpcy5idWlsdEluTWVtYmVyQ29tbWFuZHNcbiAgICBpZiAodGhpcy5jb2xsZWN0aW9uQ29tbWFuZHMpIGNvbmZpZy5jb2xsZWN0aW9uQ29tbWFuZHMgPSB0aGlzLmNvbGxlY3Rpb25Db21tYW5kc1xuICAgIGlmICh0aGlzLm1lbWJlckNvbW1hbmRzKSBjb25maWcubWVtYmVyQ29tbWFuZHMgPSB0aGlzLm1lbWJlckNvbW1hbmRzXG4gICAgaWYgKHRoaXMucmVsYXRpb25zaGlwcykgY29uZmlnLnJlbGF0aW9uc2hpcHMgPSB0aGlzLnJlbGF0aW9uc2hpcHNcblxuICAgIHJldHVybiBjb25maWdcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGNvbnRyb2xsZXIgaW5zdGFuY2UuXG4gICAqIEByZXR1cm5zIHtpbXBvcnQoXCIuLi9jb250cm9sbGVyLmpzXCIpLmRlZmF1bHR9IC0gQ29udHJvbGxlciBpbnN0YW5jZS5cbiAgICovXG4gIGNvbnRyb2xsZXJJbnN0YW5jZSgpIHtcbiAgICBpZiAoIXRoaXMuY29udHJvbGxlcikgdGhyb3cgbmV3IEVycm9yKGAke3RoaXMuY29uc3RydWN0b3IubmFtZX0gcmVxdWlyZXMgYSBjb250cm9sbGVyIGluc3RhbmNlLmApXG5cbiAgICByZXR1cm4gdGhpcy5jb250cm9sbGVyXG4gIH1cblxuICAvKipcbiAgICogUnVucyBtb2RlbCBjbGFzcy5cbiAgICogQHJldHVybnMge3R5cGVvZiBpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdH0gLSBNb2RlbCBjbGFzcy5cbiAgICovXG4gIG1vZGVsQ2xhc3MoKSB7XG4gICAgaWYgKCF0aGlzLm1vZGVsQ2xhc3NWYWx1ZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGAke3RoaXMuY29uc3RydWN0b3IubmFtZX0gcmVxdWlyZXMgYSBtb2RlbCBjbGFzcy5gKVxuICAgIH1cblxuICAgIHJldHVybiB0aGlzLm1vZGVsQ2xhc3NWYWx1ZVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgbW9kZWwgbmFtZS5cbiAgICogQHJldHVybnMge3N0cmluZ30gLSBNb2RlbCBuYW1lLlxuICAgKi9cbiAgbW9kZWxOYW1lKCkge1xuICAgIGlmICghdGhpcy5tb2RlbE5hbWVWYWx1ZSkgdGhyb3cgbmV3IEVycm9yKGAke3RoaXMuY29uc3RydWN0b3IubmFtZX0gcmVxdWlyZXMgYSBtb2RlbCBuYW1lLmApXG5cbiAgICByZXR1cm4gdGhpcy5tb2RlbE5hbWVWYWx1ZVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgcGFyYW1zLlxuICAgKiBAcmV0dXJucyB7UmVjb3JkPHN0cmluZywgPz59IC0gUGFyYW1zLlxuICAgKi9cbiAgcGFyYW1zKCkgeyByZXR1cm4gdGhpcy5wYXJhbXNWYWx1ZSB8fCBzdXBlci5wYXJhbXMoKSB8fCB7fSB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgcmVzb3VyY2UgY29uZmlndXJhdGlvbi5cbiAgICogQHJldHVybnMge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuTm9ybWFsaXplZEZyb250ZW5kTW9kZWxSZXNvdXJjZUNvbmZpZ3VyYXRpb24gfCBpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLkZyb250ZW5kTW9kZWxSZXNvdXJjZUNvbmZpZ3VyYXRpb259IC0gUmVzb3VyY2UgY29uZmlnIChub3JtYWxpemVkIGF0IHJ1bnRpbWU7IHJhdyBkdXJpbmcgZWFybHkgYm9vdHN0cmFwKS5cbiAgICovXG4gIHJlc291cmNlQ29uZmlndXJhdGlvbigpIHtcbiAgICBpZiAoIXRoaXMucmVzb3VyY2VDb25maWd1cmF0aW9uVmFsdWUpIHRocm93IG5ldyBFcnJvcihgJHt0aGlzLmNvbnN0cnVjdG9yLm5hbWV9IHJlcXVpcmVzIGEgcmVzb3VyY2UgY29uZmlndXJhdGlvbi5gKVxuXG4gICAgcmV0dXJuIHRoaXMucmVzb3VyY2VDb25maWd1cmF0aW9uVmFsdWVcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGEgUmFpbHMtc3Ryb25nLXBhcmFtcyAvIGFwaV9tYWtlci1zdHlsZSBwZXJtaXQgc3BlYyBkZWNsYXJpbmdcbiAgICogd2hpY2ggYXR0cmlidXRlcyBhbmQgbmVzdGVkIGF0dHJpYnV0ZXMgYXJlIHdyaXRhYmxlIGZvciB0aGUgY3VycmVudFxuICAgKiByZXF1ZXN0LiBTdWJtaXR0aW5nIGFuIGF0dHJpYnV0ZSBvciBuZXN0ZWQtcmVsYXRpb25zaGlwIGtleSB0aGF0IGlzXG4gICAqIG5vdCBwZXJtaXR0ZWQgcmFpc2VzIGFuIGVycm9yIGFuZCBmYWlscyB0aGUgd3JpdGUuXG4gICAqXG4gICAqIFRoZSByZXR1cm5lZCB2YWx1ZSBpcyBhIGZsYXQgYXJyYXkgdGhhdCBtaXhlczpcbiAgICogICAtIGBcImF0dHJpYnV0ZU5hbWVcImAgc3RyaW5ncyBmb3IgcGxhaW4gYXR0cmlidXRlIHdyaXRlc1xuICAgKiAgIC0gYHs8cmVsYXRpb25zaGlwTmFtZT5BdHRyaWJ1dGVzOiBbLi4uXX1gIG9iamVjdHMgd2hlcmUgdGhlIHZhbHVlXG4gICAqICAgICBpcyBpdHNlbGYgYSBwZXJtaXQgc3BlYyBmb3IgdGhlIG5lc3RlZCByZWxhdGlvbnNoaXBcbiAgICpcbiAgICogVGhpcyBtYXRjaGVzIFJhaWxzIHN0cm9uZ19wYXJhbXMgKGBwZXJtaXQoOmZpcnN0X25hbWUsIDpsYXN0X25hbWUsXG4gICAqIGNvbnRhY3RfYXR0cmlidXRlczogWzplbWFpbCwgZGV0YWlsc19hdHRyaWJ1dGVzOiBbOmRldGFpbF1dKWApIGFuZFxuICAgKiB0aGUgYXBpX21ha2VyIHNpc3RlciBwcm9qZWN0LiBJbmNsdWRlIGBcIl9kZXN0cm95XCJgIGluc2lkZSBhIG5lc3RlZFxuICAgKiBwZXJtaXQgdG8gYWxsb3cgYF9kZXN0cm95OiB0cnVlYCBlbnRyaWVzIGZvciB0aGF0IHJlbGF0aW9uc2hpcCDigJRcbiAgICogdGhlIG1vZGVsIG11c3QgYWxzbyBkZWNsYXJlIGBhY2NlcHRzTmVzdGVkQXR0cmlidXRlc0ZvcihuYW1lLFxuICAgKiB7YWxsb3dEZXN0cm95OiB0cnVlfSlgIGZvciB0aGUgZGVzdHJveSB0byBiZSBhcHBsaWVkLlxuICAgKlxuICAgKiBFeGFtcGxlOlxuICAgKlxuICAgKiAgIGNsYXNzIFByb2plY3RSZXNvdXJjZSBleHRlbmRzIEZyb250ZW5kTW9kZWxCYXNlUmVzb3VyY2Uge1xuICAgKiAgICAgcGVybWl0dGVkUGFyYW1zKGFyZykge1xuICAgKiAgICAgICByZXR1cm4gW1xuICAgKiAgICAgICAgIFwibmFtZVwiLFxuICAgKiAgICAgICAgIFwiZGVzY3JpcHRpb25cIixcbiAgICogICAgICAgICB7dGFza3NBdHRyaWJ1dGVzOiBbXCJpZFwiLCBcIl9kZXN0cm95XCIsIFwibmFtZVwiLFxuICAgKiAgICAgICAgICAge3N1YnRhc2tzQXR0cmlidXRlczogW1wiaWRcIiwgXCJfZGVzdHJveVwiLCBcIm5hbWVcIl19XG4gICAqICAgICAgICAgXX1cbiAgICogICAgICAgXVxuICAgKiAgICAgfVxuICAgKiAgIH1cbiAgICpcbiAgICogRGVmYXVsdCBpbXBsZW1lbnRhdGlvbiByZXR1cm5zIGBbXWAg4oCUIG5vdGhpbmcgcGVybWl0dGVkLiBTdWJjbGFzc2VzXG4gICAqIG11c3Qgb3ZlcnJpZGUgdG8gZW5hYmxlIHdyaXRlcy4gQSByZXNvdXJjZSB0aGF0IGRvZXMgbm90IGRlY2xhcmVcbiAgICogYHBlcm1pdHRlZFBhcmFtc2AgY2Fubm90IGFjY2VwdCBhbnkgd3JpdGUuXG4gICAqIEBwYXJhbSB7e2FjdGlvbj86IFwiY3JlYXRlXCIgfCBcInVwZGF0ZVwiLCBwYXJhbXM/OiBSZWNvcmQ8c3RyaW5nLCA/PiwgYWJpbGl0eT86IGltcG9ydChcIi4uL2F1dGhvcml6YXRpb24vYWJpbGl0eS5qc1wiKS5kZWZhdWx0LCBsb2NhbHM/OiBSZWNvcmQ8c3RyaW5nLCA/Pn19IFthcmddIC0gUmVxdWVzdCBjb250ZXh0LlxuICAgKiBAcmV0dXJucyB7QXJyYXk8c3RyaW5nIHwgUmVjb3JkPHN0cmluZywgPz4+fSAtIFBlcm1pdCBzcGVjLlxuICAgKi9cbiAgcGVybWl0dGVkUGFyYW1zKGFyZykge1xuICAgIHZvaWQgYXJnXG5cbiAgICByZXR1cm4gW11cbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIHByaW1hcnkga2V5LlxuICAgKiBAcmV0dXJucyB7c3RyaW5nfSAtIFByaW1hcnkga2V5LlxuICAgKi9cbiAgcHJpbWFyeUtleSgpIHsgcmV0dXJuIHRoaXMubW9kZWxDbGFzcygpLnByaW1hcnlLZXkoKSB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgYXV0aG9yaXplZCBxdWVyeS5cbiAgICogQHBhcmFtIHtcImluZGV4XCIgfCBcImZpbmRcIiB8IFwiY3JlYXRlXCIgfCBcInVwZGF0ZVwiIHwgXCJkZXN0cm95XCIgfCBcImF0dGFjaFwiIHwgXCJkb3dubG9hZFwiIHwgXCJ1cmxcIn0gYWN0aW9uIC0gQWJpbGl0eSBhY3Rpb24uXG4gICAqIEByZXR1cm5zIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9xdWVyeS9tb2RlbC1jbGFzcy1xdWVyeS5qc1wiKS5kZWZhdWx0PD8+fSAtIEF1dGhvcml6ZWQgcXVlcnkuXG4gICAqL1xuICBhdXRob3JpemVkUXVlcnkoYWN0aW9uKSB7XG4gICAgcmV0dXJuIHRoaXMudHlwZWRDb250cm9sbGVySW5zdGFuY2UoKS5mcm9udGVuZE1vZGVsQXV0aG9yaXplZFF1ZXJ5KGFjdGlvbilcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIHN1cHBvcnRzIHBsdWNrLlxuICAgKiBAcGFyYW0ge1wiaW5kZXhcIiB8IFwiZmluZFwiIHwgXCJjcmVhdGVcIiB8IFwidXBkYXRlXCIgfCBcImRlc3Ryb3lcIiB8IFwiYXR0YWNoXCIgfCBcImRvd25sb2FkXCIgfCBcInVybFwifSBhY3Rpb24gLSBBY3Rpb24uXG4gICAqIEByZXR1cm5zIHtib29sZWFuIHwgUHJvbWlzZTxib29sZWFuPn0gLSBXaGV0aGVyIHBsdWNrIGlzIHN1cHBvcnRlZC5cbiAgICovXG4gIHN1cHBvcnRzUGx1Y2soYWN0aW9uKSB7XG4gICAgdm9pZCBhY3Rpb25cblxuICAgIHJldHVybiBPYmplY3QuZ2V0UHJvdG90eXBlT2YodGhpcykucmVjb3JkcyA9PT0gRnJvbnRlbmRNb2RlbEJhc2VSZXNvdXJjZS5wcm90b3R5cGUucmVjb3Jkc1xuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgc3VwcG9ydHMgY291bnQuXG4gICAqIEBwYXJhbSB7XCJpbmRleFwiIHwgXCJmaW5kXCIgfCBcImNyZWF0ZVwiIHwgXCJ1cGRhdGVcIiB8IFwiZGVzdHJveVwiIHwgXCJhdHRhY2hcIiB8IFwiZG93bmxvYWRcIiB8IFwidXJsXCJ9IGFjdGlvbiAtIEFjdGlvbi5cbiAgICogQHJldHVybnMge2Jvb2xlYW4gfCBQcm9taXNlPGJvb2xlYW4+fSAtIFdoZXRoZXIgY291bnQgaXMgc3VwcG9ydGVkLlxuICAgKi9cbiAgc3VwcG9ydHNDb3VudChhY3Rpb24pIHtcbiAgICB2b2lkIGFjdGlvblxuXG4gICAgcmV0dXJuIE9iamVjdC5nZXRQcm90b3R5cGVPZih0aGlzKS5yZWNvcmRzID09PSBGcm9udGVuZE1vZGVsQmFzZVJlc291cmNlLnByb3RvdHlwZS5yZWNvcmRzIHx8XG4gICAgICBPYmplY3QuZ2V0UHJvdG90eXBlT2YodGhpcykuY291bnQgIT09IEZyb250ZW5kTW9kZWxCYXNlUmVzb3VyY2UucHJvdG90eXBlLmNvdW50XG4gIH1cblxuICAvKipcbiAgICogUnVucyBiZWZvcmUgYWN0aW9uLlxuICAgKiBAcGFyYW0ge1wiaW5kZXhcIiB8IFwiZmluZFwiIHwgXCJjcmVhdGVcIiB8IFwidXBkYXRlXCIgfCBcImRlc3Ryb3lcIiB8IFwiYXR0YWNoXCIgfCBcImRvd25sb2FkXCIgfCBcInVybFwifSBhY3Rpb24gLSBBY3Rpb24uXG4gICAqIEByZXR1cm5zIHtib29sZWFuIHwgdm9pZCB8IFByb21pc2U8Ym9vbGVhbiB8IHZvaWQ+fSAtIENvbnRpbnVlIHByb2Nlc3NpbmcgdW5sZXNzIGZhbHNlLlxuICAgKi9cbiAgYmVmb3JlQWN0aW9uKGFjdGlvbikge1xuICAgIHZvaWQgYWN0aW9uXG5cbiAgICAvLyBOby1vcCBieSBkZWZhdWx0LlxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgcmVjb3Jkcy5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi4vZGF0YWJhc2UvcmVjb3JkL2luZGV4LmpzXCIpLmRlZmF1bHRbXT59IC0gUmVjb3JkcyBmb3IgaW5kZXggYWN0aW9uLlxuICAgKi9cbiAgYXN5bmMgcmVjb3JkcygpIHtcbiAgICByZXR1cm4gYXdhaXQgdGhpcy50eXBlZENvbnRyb2xsZXJJbnN0YW5jZSgpLmZyb250ZW5kTW9kZWxJbmRleFF1ZXJ5KCkudG9BcnJheSgpXG4gIH1cblxuICAvKipcbiAgICogUnVucyBjb3VudC5cbiAgICogQHJldHVybnMge1Byb21pc2U8bnVtYmVyPn0gLSBSZWNvcmRzIGNvdW50IGZvciBpbmRleCBhY3Rpb24uXG4gICAqL1xuICBhc3luYyBjb3VudCgpIHtcbiAgICByZXR1cm4gYXdhaXQgdGhpcy50eXBlZENvbnRyb2xsZXJJbnN0YW5jZSgpLmZyb250ZW5kTW9kZWxJbmRleFF1ZXJ5KCkuY291bnQoKVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgZmluZC5cbiAgICogQHBhcmFtIHtcImZpbmRcIiB8IFwidXBkYXRlXCIgfCBcImRlc3Ryb3lcIiB8IFwiYXR0YWNoXCIgfCBcImRvd25sb2FkXCIgfCBcInVybFwifSBhY3Rpb24gLSBBY3Rpb24uXG4gICAqIEBwYXJhbSB7c3RyaW5nIHwgbnVtYmVyfSBpZCAtIFJlY29yZCBpZC5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi4vZGF0YWJhc2UvcmVjb3JkL2luZGV4LmpzXCIpLmRlZmF1bHQgfCBudWxsPn0gLSBMb2NhdGVkIG1vZGVsLlxuICAgKi9cbiAgYXN5bmMgZmluZChhY3Rpb24sIGlkKSB7XG4gICAgbGV0IHF1ZXJ5ID0gdGhpcy5hdXRob3JpemVkUXVlcnkoYWN0aW9uKVxuICAgIGNvbnN0IHByZWxvYWQgPSBhY3Rpb24gPT09IFwiZmluZFwiID8gdGhpcy50eXBlZENvbnRyb2xsZXJJbnN0YW5jZSgpLmZyb250ZW5kTW9kZWxQcmVsb2FkKCkgOiBudWxsXG5cbiAgICBpZiAocHJlbG9hZCkge1xuICAgICAgcXVlcnkgPSBxdWVyeS5wcmVsb2FkKHByZWxvYWQpXG4gICAgfVxuXG4gICAgcmV0dXJuIGF3YWl0IHF1ZXJ5LmZpbmRCeSh7W3RoaXMucHJpbWFyeUtleSgpXTogaWR9KVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgY3JlYXRlLlxuICAgKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsID8+fSBhdHRyaWJ1dGVzIC0gQ3JlYXRlIGF0dHJpYnV0ZXMuXG4gICAqIEBwYXJhbSB7e2NvbnRyb2xsZXI/OiA/LCBuZXN0ZWRBdHRyaWJ1dGVzPzogUmVjb3JkPHN0cmluZywgPz4gfCBudWxsfX0gW29wdGlvbnNdIC0gU2F2ZSBvcHRpb25zLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdD59IC0gQ3JlYXRlZCBtb2RlbC5cbiAgICovXG4gIGFzeW5jIGNyZWF0ZShhdHRyaWJ1dGVzLCBvcHRpb25zID0ge30pIHtcbiAgICBjb25zdCBwZXJtaXQgPSBwYXJzZVBlcm1pdHRlZFBhcmFtcyh0aGlzLnBlcm1pdHRlZFBhcmFtcyh7YWN0aW9uOiBcImNyZWF0ZVwiLCBhYmlsaXR5OiB0aGlzLmFiaWxpdHksIGxvY2FsczogdGhpcy5sb2NhbHMsIHBhcmFtczogYXR0cmlidXRlc30pKVxuICAgIGNvbnN0IGZpbHRlcmVkID0gZmlsdGVyV3JpdGFibGVGcm9udGVuZE1vZGVsQXR0cmlidXRlcyh0aGlzLm1vZGVsQ2xhc3MoKS5wcm90b3R5cGUsIGF0dHJpYnV0ZXMsIHRoaXMsIHBlcm1pdC5hdHRyaWJ1dGVzKVxuICAgIGNvbnN0IE1vZGVsQ2xhc3MgPSB0aGlzLm1vZGVsQ2xhc3MoKVxuICAgIGNvbnN0IG1vZGVsID0gbmV3IE1vZGVsQ2xhc3MoKVxuXG4gICAgYXdhaXQgTW9kZWxDbGFzcy50cmFuc2FjdGlvbihhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCB0aGlzLl9hc3NpZ25XaXRoVmlydHVhbFNldHRlcnMobW9kZWwsIGZpbHRlcmVkKVxuICAgICAgYXdhaXQgbW9kZWwuc2F2ZSgpXG5cbiAgICAgIGlmIChvcHRpb25zLm5lc3RlZEF0dHJpYnV0ZXMpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5fYXBwbHlOZXN0ZWRBdHRyaWJ1dGVzKG1vZGVsLCBvcHRpb25zLm5lc3RlZEF0dHJpYnV0ZXMsIG9wdGlvbnMuY29udHJvbGxlciB8fCBudWxsLCBwZXJtaXQpXG4gICAgICB9XG4gICAgfSlcblxuICAgIGF3YWl0IHRoaXMuX3ByZWxvYWROZXN0ZWRXcml0YWJsZVJlbGF0aW9uc2hpcHMobW9kZWwsIHBlcm1pdClcblxuICAgIHJldHVybiBtb2RlbFxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgaGFuZGxlIHVuYXV0aG9yaXplZCBjcmVhdGVkIG1vZGVsLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL3JlY29yZC9pbmRleC5qc1wiKS5kZWZhdWx0fSBtb2RlbCAtIENyZWF0ZWQgbW9kZWwuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIENsZWFudXAgYWZ0ZXIgZmFpbGVkIGF1dGhvcml6YXRpb24uXG4gICAqL1xuICBhc3luYyBoYW5kbGVVbmF1dGhvcml6ZWRDcmVhdGVkTW9kZWwobW9kZWwpIHtcbiAgICBhd2FpdCBtb2RlbC5kZXN0cm95KClcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIHVwZGF0ZS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdH0gbW9kZWwgLSBFeGlzdGluZyBtb2RlbC5cbiAgICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCA/Pn0gYXR0cmlidXRlcyAtIFVwZGF0ZSBhdHRyaWJ1dGVzLlxuICAgKiBAcGFyYW0ge3tjb250cm9sbGVyPzogPywgbmVzdGVkQXR0cmlidXRlcz86IFJlY29yZDxzdHJpbmcsID8+IHwgbnVsbH19IFtvcHRpb25zXSAtIFNhdmUgb3B0aW9ucy5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi4vZGF0YWJhc2UvcmVjb3JkL2luZGV4LmpzXCIpLmRlZmF1bHQ+fSAtIFVwZGF0ZWQgbW9kZWwuXG4gICAqL1xuICBhc3luYyB1cGRhdGUobW9kZWwsIGF0dHJpYnV0ZXMsIG9wdGlvbnMgPSB7fSkge1xuICAgIGNvbnN0IHBlcm1pdCA9IHBhcnNlUGVybWl0dGVkUGFyYW1zKHRoaXMucGVybWl0dGVkUGFyYW1zKHthY3Rpb246IFwidXBkYXRlXCIsIGFiaWxpdHk6IHRoaXMuYWJpbGl0eSwgbG9jYWxzOiB0aGlzLmxvY2FscywgcGFyYW1zOiBhdHRyaWJ1dGVzfSkpXG4gICAgY29uc3QgZmlsdGVyZWQgPSBmaWx0ZXJXcml0YWJsZUZyb250ZW5kTW9kZWxBdHRyaWJ1dGVzKG1vZGVsLCBhdHRyaWJ1dGVzLCB0aGlzLCBwZXJtaXQuYXR0cmlidXRlcylcbiAgICBjb25zdCBNb2RlbENsYXNzID0gdGhpcy5tb2RlbENsYXNzKClcblxuICAgIGF3YWl0IE1vZGVsQ2xhc3MudHJhbnNhY3Rpb24oYXN5bmMgKCkgPT4ge1xuICAgICAgYXdhaXQgdGhpcy5fYXNzaWduV2l0aFZpcnR1YWxTZXR0ZXJzKG1vZGVsLCBmaWx0ZXJlZClcbiAgICAgIGF3YWl0IG1vZGVsLnNhdmUoKVxuXG4gICAgICBpZiAob3B0aW9ucy5uZXN0ZWRBdHRyaWJ1dGVzKSB7XG4gICAgICAgIGF3YWl0IHRoaXMuX2FwcGx5TmVzdGVkQXR0cmlidXRlcyhtb2RlbCwgb3B0aW9ucy5uZXN0ZWRBdHRyaWJ1dGVzLCBvcHRpb25zLmNvbnRyb2xsZXIgfHwgbnVsbCwgcGVybWl0KVxuICAgICAgfVxuICAgIH0pXG5cbiAgICBhd2FpdCB0aGlzLl9wcmVsb2FkTmVzdGVkV3JpdGFibGVSZWxhdGlvbnNoaXBzKG1vZGVsLCBwZXJtaXQpXG5cbiAgICByZXR1cm4gbW9kZWxcbiAgfVxuXG4gIC8qKlxuICAgKiBBc3NpZ25zIGF0dHJpYnV0ZXMgdG8gYSBtb2RlbCwgdXNpbmcgdmlydHVhbCBzZXR0ZXJzIG9uIHRoZSByZXNvdXJjZSB3aGVuIGF2YWlsYWJsZS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdH0gbW9kZWwgLSBNb2RlbCBpbnN0YW5jZS5cbiAgICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCA/Pn0gYXR0cmlidXRlcyAtIEF0dHJpYnV0ZXMgdG8gYXNzaWduLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn1cbiAgICovXG4gIGFzeW5jIF9hc3NpZ25XaXRoVmlydHVhbFNldHRlcnMobW9kZWwsIGF0dHJpYnV0ZXMpIHtcbiAgICAvKipcbiAgICAgKiBEaXJlY3QgYXR0cmlidXRlcy5cbiAgICAgIEB0eXBlIHtSZWNvcmQ8c3RyaW5nLCA/Pn0gKi9cbiAgICBjb25zdCBkaXJlY3RBdHRyaWJ1dGVzID0ge31cbiAgICBjb25zdCB0cmFuc2xhdGVkU2V0ID0gbmV3IFNldCgvKipcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBAdHlwZSB7dHlwZW9mIEZyb250ZW5kTW9kZWxCYXNlUmVzb3VyY2V9ICovICh0aGlzLmNvbnN0cnVjdG9yKS50cmFuc2xhdGVkQXR0cmlidXRlcyB8fCBbXSlcblxuICAgIGZvciAoY29uc3QgW25hbWUsIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhhdHRyaWJ1dGVzKSkge1xuICAgICAgY29uc3QgcmVzb3VyY2VTZXR0ZXJOYW1lID0gYHNldCR7aW5mbGVjdGlvbi5jYW1lbGl6ZShuYW1lKX1BdHRyaWJ1dGVgXG5cbiAgICAgIGlmICh0eXBlb2YgLyoqXG4gICAgICAgICAgICAgICAgICAqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS5cbiAgICAgICAgICAgICAgICAgICBAdHlwZSB7UmVjb3JkPHN0cmluZywgPz59ICovICgvKipcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBAdHlwZSB7P30gKi8gKHRoaXMpKVtyZXNvdXJjZVNldHRlck5hbWVdID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgYXdhaXQgLyoqXG4gICAgICAgICAgICAgICAqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS5cbiAgICAgICAgICAgICAgICBAdHlwZSB7UmVjb3JkPHN0cmluZywgPz59ICovICgvKipcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBAdHlwZSB7P30gKi8gKHRoaXMpKVtyZXNvdXJjZVNldHRlck5hbWVdKG1vZGVsLCB2YWx1ZSlcbiAgICAgIH0gZWxzZSBpZiAodHJhbnNsYXRlZFNldC5oYXMobmFtZSkpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5fc2V0VHJhbnNsYXRlZEF0dHJpYnV0ZU9uTW9kZWwobW9kZWwsIG5hbWUsIHZhbHVlKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZGlyZWN0QXR0cmlidXRlc1tuYW1lXSA9IHZhbHVlXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKE9iamVjdC5rZXlzKGRpcmVjdEF0dHJpYnV0ZXMpLmxlbmd0aCA+IDApIHtcbiAgICAgIG1vZGVsLmFzc2lnbihkaXJlY3RBdHRyaWJ1dGVzKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTZXRzIGEgdHJhbnNsYXRlZCBhdHRyaWJ1dGUgb24gYSBtb2RlbCB2aWEgdGhlIHRyYW5zbGF0aW9ucyByZWxhdGlvbnNoaXAuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vZGF0YWJhc2UvcmVjb3JkL2luZGV4LmpzXCIpLmRlZmF1bHR9IG1vZGVsIC0gTW9kZWwgaW5zdGFuY2UuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBuYW1lIC0gQXR0cmlidXRlIG5hbWUuXG4gICAqIEBwYXJhbSB7P30gdmFsdWUgLSBBdHRyaWJ1dGUgdmFsdWUuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKi9cbiAgYXN5bmMgX3NldFRyYW5zbGF0ZWRBdHRyaWJ1dGVPbk1vZGVsKG1vZGVsLCBuYW1lLCB2YWx1ZSkge1xuICAgIGNvbnN0IGxvY2FsZSA9IHRoaXMuY29udGV4dD8uY29uZmlndXJhdGlvbj8uZ2V0TG9jYWxlPy4oKSB8fCBcImVuXCJcbiAgICBjb25zdCBpbnN0YW5jZVJlbGF0aW9uc2hpcCA9IG1vZGVsLmdldFJlbGF0aW9uc2hpcEJ5TmFtZShcInRyYW5zbGF0aW9uc1wiKVxuXG4gICAgLyoqXG4gICAgICogRGVmaW5lcyB0cmFuc2xhdGlvbi5cbiAgICAgIEB0eXBlIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdCB8IHVuZGVmaW5lZH0gKi9cbiAgICBsZXQgdHJhbnNsYXRpb25cblxuICAgIGlmIChtb2RlbC5pc05ld1JlY29yZCgpKSB7XG4gICAgICBjb25zdCBsb2FkZWQgPSBpbnN0YW5jZVJlbGF0aW9uc2hpcC5sb2FkZWQoKVxuXG4gICAgICBpZiAoQXJyYXkuaXNBcnJheShsb2FkZWQpKSB7XG4gICAgICAgIHRyYW5zbGF0aW9uID0gbG9hZGVkLmZpbmQoKC8qKlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQHR5cGUge1JlY29yZDxzdHJpbmcsID8+fSAqLyB0KSA9PiB0LmxvY2FsZSgpID09PSBsb2NhbGUpXG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGlmICghaW5zdGFuY2VSZWxhdGlvbnNoaXAuZ2V0UHJlbG9hZGVkKCkpIHtcbiAgICAgICAgYXdhaXQgbW9kZWwubG9hZFJlbGF0aW9uc2hpcChcInRyYW5zbGF0aW9uc1wiKVxuICAgICAgfVxuXG4gICAgICBjb25zdCBsb2FkZWQgPSBpbnN0YW5jZVJlbGF0aW9uc2hpcC5sb2FkZWQoKVxuXG4gICAgICBpZiAoQXJyYXkuaXNBcnJheShsb2FkZWQpKSB7XG4gICAgICAgIHRyYW5zbGF0aW9uID0gbG9hZGVkLmZpbmQoKC8qKlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQHR5cGUge1JlY29yZDxzdHJpbmcsID8+fSAqLyB0KSA9PiB0LmxvY2FsZSgpID09PSBsb2NhbGUpXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCF0cmFuc2xhdGlvbikge1xuICAgICAgdHJhbnNsYXRpb24gPSBpbnN0YW5jZVJlbGF0aW9uc2hpcC5idWlsZCh7bG9jYWxlfSlcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBc3NpZ25tZW50cy5cbiAgICAgIEB0eXBlIHtSZWNvcmQ8c3RyaW5nLCA/Pn0gKi9cbiAgICBjb25zdCBhc3NpZ25tZW50cyA9IHt9XG5cbiAgICBhc3NpZ25tZW50c1tuYW1lXSA9IHZhbHVlXG4gICAgdHJhbnNsYXRpb24uYXNzaWduKGFzc2lnbm1lbnRzKVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgZGVzdHJveS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdH0gbW9kZWwgLSBFeGlzdGluZyBtb2RlbC5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gTm8gcmV0dXJuIHZhbHVlLlxuICAgKi9cbiAgYXN5bmMgZGVzdHJveShtb2RlbCkge1xuICAgIGF3YWl0IG1vZGVsLmRlc3Ryb3koKVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgc2VyaWFsaXplLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL3JlY29yZC9pbmRleC5qc1wiKS5kZWZhdWx0fSBtb2RlbCAtIE1vZGVsIHRvIHNlcmlhbGl6ZS5cbiAgICogQHBhcmFtIHtcImluZGV4XCIgfCBcImZpbmRcIiB8IFwiY3JlYXRlXCIgfCBcInVwZGF0ZVwifSBbYWN0aW9uXSAtIEFjdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8UmVjb3JkPHN0cmluZywgPz4+fSAtIFNlcmlhbGl6ZWQgbW9kZWwgcGF5bG9hZC5cbiAgICovXG4gIGFzeW5jIHNlcmlhbGl6ZShtb2RlbCwgYWN0aW9uKSB7XG4gICAgdm9pZCBhY3Rpb25cblxuICAgIHJldHVybiBhd2FpdCB0aGlzLnR5cGVkQ29udHJvbGxlckluc3RhbmNlKCkuc2VyaWFsaXplRnJvbnRlbmRNb2RlbChtb2RlbClcbiAgfVxuXG4gIC8qKlxuICAgKiBBcHBsaWVzIGEgYG5lc3RlZEF0dHJpYnV0ZXNgIHBheWxvYWQgdG8gYSBmcmVzaGx5LXNhdmVkIHBhcmVudCBtb2RlbCxcbiAgICogY2FzY2FkaW5nIGNyZWF0ZS91cGRhdGUvZGVzdHJveSB3cml0ZXMgYWNyb3NzIHRoZSBkZWNsYXJlZCByZWxhdGlvbnNoaXBzLlxuICAgKlxuICAgKiBFYWNoIGNoaWxkIGlzIGF1dGhvcml6ZWQgYWdhaW5zdCBpdHMgb3duIHJlc291cmNlJ3MgYWJpbGl0aWVzIChuZXZlciB0aGVcbiAgICogcGFyZW50J3MpLiBEZXN0cm95cyBydW4gYmVmb3JlIHVwZGF0ZXMsIHVwZGF0ZXMgYmVmb3JlIGNyZWF0ZXMsIHRvIGF2b2lkXG4gICAqIHVuaXF1ZS1jb25zdHJhaW50IGNvbmZsaWN0cyB3aGVuIHJlcGxhY2luZyBhIGNoaWxkIGF0IHRoZSBzYW1lIG5hdHVyYWwga2V5LlxuICAgKlxuICAgKiBBdHRyaWJ1dGUgZmlsdGVyaW5nIGZvciBuZXN0ZWQgY2hpbGRyZW4gdXNlcyB0aGUgcGFyZW50IHJlc291cmNlJ3NcbiAgICogcGVybWl0IHNwZWMgZm9yIHRoYXQgcmVsYXRpb25zaGlwIOKAlCBhcGlfbWFrZXItc3R5bGUuIFBvbGljeSBvcHRpb25zXG4gICAqIChhbGxvd0Rlc3Ryb3ksIGxpbWl0LCByZWplY3RJZikgY29tZSBmcm9tIHRoZSBNT0RFTCdzXG4gICAqIGBhY2NlcHRlZE5lc3RlZEF0dHJpYnV0ZXNGb3IobmFtZSlgIGRlY2xhcmF0aW9uLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL3JlY29yZC9pbmRleC5qc1wiKS5kZWZhdWx0fSBwYXJlbnQgLSBQYXJlbnQgbW9kZWwgaW5zdGFuY2UuXG4gICAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgPz59IG5lc3RlZEF0dHJpYnV0ZXMgLSBOZXN0ZWQtYXR0cmlidXRlIHBheWxvYWQga2V5ZWQgYnkgcmVsYXRpb25zaGlwIG5hbWUuXG4gICAqIEBwYXJhbSB7P30gY29udHJvbGxlciAtIENvbnRyb2xsZXIgaW5zdGFuY2UgZm9yIHJlc291cmNlIHJlc29sdXRpb24gYW5kIGF1dGhvcml6YXRpb24uXG4gICAqIEBwYXJhbSB7e2F0dHJpYnV0ZXM6IHN0cmluZ1tdLCBuZXN0ZWQ6IFJlY29yZDxzdHJpbmcsID8+fSB8IG51bGx9IFtwYXJlbnRQZXJtaXRdIC0gUGFyc2VkIHBhcmVudCBwZXJtaXQgc3BlYy5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqL1xuICBhc3luYyBfYXBwbHlOZXN0ZWRBdHRyaWJ1dGVzKHBhcmVudCwgbmVzdGVkQXR0cmlidXRlcywgY29udHJvbGxlciwgcGFyZW50UGVybWl0ID0gbnVsbCkge1xuICAgIGNvbnN0IHJlc29sdmVkUGFyZW50ID0gcGFyZW50UGVybWl0XG4gICAgICB8fCBwYXJzZVBlcm1pdHRlZFBhcmFtcyh0aGlzLnBlcm1pdHRlZFBhcmFtcyh7YWN0aW9uOiBcInVwZGF0ZVwiLCBhYmlsaXR5OiB0aGlzLmFiaWxpdHksIGxvY2FsczogdGhpcy5sb2NhbHMsIHBhcmFtczoge319KSlcblxuICAgIGZvciAoY29uc3QgcmVsYXRpb25zaGlwTmFtZSBvZiBPYmplY3Qua2V5cyhuZXN0ZWRBdHRyaWJ1dGVzKSkge1xuICAgICAgY29uc3QgY2hpbGRQZXJtaXQgPSByZXNvbHZlZFBhcmVudC5uZXN0ZWRbcmVsYXRpb25zaGlwTmFtZV1cblxuICAgICAgaWYgKCFjaGlsZFBlcm1pdCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE5lc3RlZCBhdHRyaWJ1dGVzIGZvciAnJHtyZWxhdGlvbnNoaXBOYW1lfScgYXJlIG5vdCBwZXJtaXR0ZWQgYnkgJHt0aGlzLmNvbnN0cnVjdG9yLm5hbWV9LnBlcm1pdHRlZFBhcmFtcygpLiBJbmNsdWRlIHske3JlbGF0aW9uc2hpcE5hbWV9QXR0cmlidXRlczogWy4uLl19IGluIHRoZSByZXR1cm5lZCBwZXJtaXQuYClcbiAgICAgIH1cblxuICAgICAgY29uc3QgZW50cmllcyA9IG5lc3RlZEF0dHJpYnV0ZXNbcmVsYXRpb25zaGlwTmFtZV1cblxuICAgICAgaWYgKCFBcnJheS5pc0FycmF5KGVudHJpZXMpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYXJyYXkgZm9yIG5lc3RlZEF0dHJpYnV0ZXNbJyR7cmVsYXRpb25zaGlwTmFtZX0nXSBidXQgZ290OiAke3R5cGVvZiBlbnRyaWVzfWApXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHBhcmVudE1vZGVsQ2xhc3MgPSAvKipcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBAdHlwZSB7P30gKi8gKHBhcmVudC5nZXRNb2RlbENsYXNzKCkpXG4gICAgICBjb25zdCBtb2RlbEFjY2VwdGFuY2UgPSBwYXJlbnRNb2RlbENsYXNzLmFjY2VwdGVkTmVzdGVkQXR0cmlidXRlc0Zvcj8uKHJlbGF0aW9uc2hpcE5hbWUpXG5cbiAgICAgIGlmICghbW9kZWxBY2NlcHRhbmNlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgTW9kZWwgJHtwYXJlbnRNb2RlbENsYXNzLm5hbWV9IGRvZXMgbm90IGFjY2VwdCBuZXN0ZWQgYXR0cmlidXRlcyBmb3IgJyR7cmVsYXRpb25zaGlwTmFtZX0nLiBEZWNsYXJlIGl0IHZpYSAke3BhcmVudE1vZGVsQ2xhc3MubmFtZX0uYWNjZXB0c05lc3RlZEF0dHJpYnV0ZXNGb3IoJyR7cmVsYXRpb25zaGlwTmFtZX0nKS5gKVxuICAgICAgfVxuXG4gICAgICBjb25zdCBkZXN0cm95UGVybWl0dGVkID0gY2hpbGRQZXJtaXQuYXR0cmlidXRlcy5pbmNsdWRlcyhcIl9kZXN0cm95XCIpXG5cbiAgICAgIGlmIChkZXN0cm95UGVybWl0dGVkICYmICFtb2RlbEFjY2VwdGFuY2UuYWxsb3dEZXN0cm95KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgUmVzb3VyY2UgcGVybWl0cyBfZGVzdHJveSBvbiBuZXN0ZWRBdHRyaWJ1dGVzWycke3JlbGF0aW9uc2hpcE5hbWV9J10gYnV0IHRoZSBtb2RlbCAke3BhcmVudE1vZGVsQ2xhc3MubmFtZX0gZG9lcyBub3QgYWxsb3cgZGVzdHJveSBmb3IgdGhhdCByZWxhdGlvbnNoaXAuIFNldCB7YWxsb3dEZXN0cm95OiB0cnVlfSBvbiAke3BhcmVudE1vZGVsQ2xhc3MubmFtZX0uYWNjZXB0c05lc3RlZEF0dHJpYnV0ZXNGb3IoJyR7cmVsYXRpb25zaGlwTmFtZX0nLCAuLi4pLmApXG4gICAgICB9XG5cbiAgICAgIGlmICh0eXBlb2YgbW9kZWxBY2NlcHRhbmNlLmxpbWl0ID09PSBcIm51bWJlclwiICYmIGVudHJpZXMubGVuZ3RoID4gbW9kZWxBY2NlcHRhbmNlLmxpbWl0KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgbmVzdGVkQXR0cmlidXRlc1snJHtyZWxhdGlvbnNoaXBOYW1lfSddIGV4Y2VlZHMgbW9kZWwtZGVjbGFyZWQgbGltaXQgb2YgJHttb2RlbEFjY2VwdGFuY2UubGltaXR9LmApXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHBhcmVudFJlbGF0aW9uc2hpcCA9IHBhcmVudC5nZXRSZWxhdGlvbnNoaXBCeU5hbWUocmVsYXRpb25zaGlwTmFtZSlcbiAgICAgIGNvbnN0IHJlbGF0aW9uc2hpcERlZmluaXRpb25zID0gcGFyZW50TW9kZWxDbGFzcy5yZWxhdGlvbnNoaXBzPy4oKSB8fCB7fVxuICAgICAgY29uc3QgZGVmaW5pdGlvbiA9IHJlbGF0aW9uc2hpcERlZmluaXRpb25zW3JlbGF0aW9uc2hpcE5hbWVdXG5cbiAgICAgIGlmICghZGVmaW5pdGlvbiB8fCBkZWZpbml0aW9uLnR5cGUgIT09IFwiaGFzTWFueVwiKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgTmVzdGVkIGF0dHJpYnV0ZXMgZm9yICcke3JlbGF0aW9uc2hpcE5hbWV9JyByZXF1aXJlIGEgaGFzTWFueSByZWxhdGlvbnNoaXAuIHYxIGRvZXMgbm90IHN1cHBvcnQgJyR7ZGVmaW5pdGlvbj8udHlwZX0nLmApXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHRhcmdldE1vZGVsQ2xhc3MgPSAvKipcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBAdHlwZSB7P30gKi8gKHBhcmVudC5nZXRNb2RlbENsYXNzKCkpLnJlbGF0aW9uc2hpcE1vZGVsQ2xhc3M/LihyZWxhdGlvbnNoaXBOYW1lKVxuXG4gICAgICBpZiAoIXRhcmdldE1vZGVsQ2xhc3MpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBObyB0YXJnZXQgbW9kZWwgY2xhc3MgcmVzb2x2ZWQgZm9yIHJlbGF0aW9uc2hpcCAnJHtyZWxhdGlvbnNoaXBOYW1lfScgb24gJHtwYXJlbnQuZ2V0TW9kZWxDbGFzcygpLm5hbWV9LmApXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGNoaWxkUmVzb3VyY2VDb25maWcgPSBjb250cm9sbGVyPy5mcm9udGVuZE1vZGVsUmVzb3VyY2VDb25maWd1cmF0aW9uRm9yTW9kZWxDbGFzcz8uKHRhcmdldE1vZGVsQ2xhc3MpXG5cbiAgICAgIGlmICghY2hpbGRSZXNvdXJjZUNvbmZpZykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE5vIGZyb250ZW5kLW1vZGVsIHJlc291cmNlIHJlZ2lzdGVyZWQgZm9yIGNoaWxkIG1vZGVsICcke3RhcmdldE1vZGVsQ2xhc3MuZ2V0TW9kZWxOYW1lPy4oKSB8fCB0YXJnZXRNb2RlbENsYXNzLm5hbWV9JyB1bmRlciByZWxhdGlvbnNoaXAgJyR7cmVsYXRpb25zaGlwTmFtZX0nLmApXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGNoaWxkUmVzb3VyY2UgPSBuZXcgY2hpbGRSZXNvdXJjZUNvbmZpZy5yZXNvdXJjZUNsYXNzKHtcbiAgICAgICAgYWJpbGl0eTogdGhpcy5hYmlsaXR5LFxuICAgICAgICBjb250cm9sbGVyLFxuICAgICAgICBjb250ZXh0OiB0aGlzLmNvbnRleHQgfHwge30sXG4gICAgICAgIGxvY2FsczogdGhpcy5sb2NhbHMgfHwge30sXG4gICAgICAgIG1vZGVsQ2xhc3M6IHRhcmdldE1vZGVsQ2xhc3MsXG4gICAgICAgIG1vZGVsTmFtZTogY2hpbGRSZXNvdXJjZUNvbmZpZy5tb2RlbE5hbWUsXG4gICAgICAgIHBhcmFtczogY29udHJvbGxlcj8uZnJvbnRlbmRNb2RlbFBhcmFtcz8uKCkgfHwge30sXG4gICAgICAgIHJlc291cmNlQ29uZmlndXJhdGlvbjogY2hpbGRSZXNvdXJjZUNvbmZpZy5yZXNvdXJjZUNvbmZpZ3VyYXRpb25cbiAgICAgIH0pXG5cbiAgICAgIGNvbnN0IGZvcmVpZ25LZXkgPSBkZWZpbml0aW9uLmZvcmVpZ25LZXkgfHwgdGhpcy5faW5mZXJGb3JlaWduS2V5KHBhcmVudCwgZGVmaW5pdGlvbilcbiAgICAgIGNvbnN0IGFiaWxpdHkgPSBjb250cm9sbGVyPy5jdXJyZW50QWJpbGl0eT8uKClcblxuICAgICAgY29uc3QgZGVzdHJveUVudHJpZXMgPSBbXVxuICAgICAgY29uc3QgdXBkYXRlRW50cmllcyA9IFtdXG4gICAgICBjb25zdCBjcmVhdGVFbnRyaWVzID0gW11cblxuICAgICAgZm9yIChjb25zdCBlbnRyeSBvZiBlbnRyaWVzKSB7XG4gICAgICAgIGlmICh0eXBlb2YgbW9kZWxBY2NlcHRhbmNlLnJlamVjdElmID09PSBcImZ1bmN0aW9uXCIgJiYgbW9kZWxBY2NlcHRhbmNlLnJlamVjdElmKGVudHJ5Py5hdHRyaWJ1dGVzIHx8IHt9KSkgY29udGludWVcblxuICAgICAgICBpZiAoZW50cnk/Ll9kZXN0cm95KSB7XG4gICAgICAgICAgaWYgKCFkZXN0cm95UGVybWl0dGVkKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYG5lc3RlZEF0dHJpYnV0ZXNbJyR7cmVsYXRpb25zaGlwTmFtZX0nXSBlbnRyeSByZXF1ZXN0ZWQgX2Rlc3Ryb3kgYnV0IFwiX2Rlc3Ryb3lcIiBpcyBub3QgaW4gdGhlIHBlcm1pdCBmb3IgdGhpcyByZWxhdGlvbnNoaXAuYClcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKCFlbnRyeS5pZCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBuZXN0ZWRBdHRyaWJ1dGVzWycke3JlbGF0aW9uc2hpcE5hbWV9J10gX2Rlc3Ryb3kgZW50cnkgaXMgbWlzc2luZyBhbiBpZC5gKVxuICAgICAgICAgIH1cbiAgICAgICAgICBkZXN0cm95RW50cmllcy5wdXNoKGVudHJ5KVxuICAgICAgICB9IGVsc2UgaWYgKGVudHJ5Py5pZCkge1xuICAgICAgICAgIHVwZGF0ZUVudHJpZXMucHVzaChlbnRyeSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjcmVhdGVFbnRyaWVzLnB1c2goZW50cnkpXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gVGhlIHBlcm1pdCdzIGF0dHJpYnV0ZSBsaXN0IGdvdmVybnMgd2hhdCBjaGlsZCBmaWVsZHMgY2FuIGJlIHdyaXR0ZW4uXG4gICAgICAvLyBFeGNsdWRlIGBfZGVzdHJveWAgZnJvbSB0aGUgd3JpdGFibGUgc2V0IHNpbmNlIGl0J3MgYSBjb250cm9sIGZsYWcsXG4gICAgICAvLyBub3QgYW4gYXR0cmlidXRlIG9uIHRoZSByZWNvcmQuXG4gICAgICBjb25zdCBjaGlsZFdyaXRhYmxlQXR0cmlidXRlcyA9IC8qKlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQHR5cGUge3N0cmluZ1tdfSAqLyAoY2hpbGRQZXJtaXQuYXR0cmlidXRlcykuZmlsdGVyKChuYW1lKSA9PiBuYW1lICE9PSBcIl9kZXN0cm95XCIpXG5cbiAgICAgIGZvciAoY29uc3QgZW50cnkgb2YgZGVzdHJveUVudHJpZXMpIHtcbiAgICAgICAgY29uc3QgZXhpc3RpbmcgPSBhd2FpdCB0aGlzLl9maW5kU2NvcGVkQ2hpbGQoe1xuICAgICAgICAgIGFiaWxpdHksXG4gICAgICAgICAgYWN0aW9uOiBcImRlc3Ryb3lcIixcbiAgICAgICAgICBjaGlsZFJlc291cmNlQ29uZmlndXJhdGlvbjogY2hpbGRSZXNvdXJjZUNvbmZpZy5yZXNvdXJjZUNvbmZpZ3VyYXRpb24sXG4gICAgICAgICAgZm9yZWlnbktleSxcbiAgICAgICAgICBpZDogZW50cnkuaWQsXG4gICAgICAgICAgcGFyZW50LFxuICAgICAgICAgIHJlbGF0aW9uc2hpcE5hbWUsXG4gICAgICAgICAgdGFyZ2V0TW9kZWxDbGFzc1xuICAgICAgICB9KVxuXG4gICAgICAgIGF3YWl0IGNoaWxkUmVzb3VyY2UuZGVzdHJveShleGlzdGluZylcbiAgICAgIH1cblxuICAgICAgZm9yIChjb25zdCBlbnRyeSBvZiB1cGRhdGVFbnRyaWVzKSB7XG4gICAgICAgIGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgdGhpcy5fZmluZFNjb3BlZENoaWxkKHtcbiAgICAgICAgICBhYmlsaXR5LFxuICAgICAgICAgIGFjdGlvbjogXCJ1cGRhdGVcIixcbiAgICAgICAgICBjaGlsZFJlc291cmNlQ29uZmlndXJhdGlvbjogY2hpbGRSZXNvdXJjZUNvbmZpZy5yZXNvdXJjZUNvbmZpZ3VyYXRpb24sXG4gICAgICAgICAgZm9yZWlnbktleSxcbiAgICAgICAgICBpZDogZW50cnkuaWQsXG4gICAgICAgICAgcGFyZW50LFxuICAgICAgICAgIHJlbGF0aW9uc2hpcE5hbWUsXG4gICAgICAgICAgdGFyZ2V0TW9kZWxDbGFzc1xuICAgICAgICB9KVxuXG4gICAgICAgIGlmIChlbnRyeS5hdHRyaWJ1dGVzICYmIHR5cGVvZiBlbnRyeS5hdHRyaWJ1dGVzID09PSBcIm9iamVjdFwiKSB7XG4gICAgICAgICAgY29uc3QgZmlsdGVyZWQgPSBmaWx0ZXJXcml0YWJsZUZyb250ZW5kTW9kZWxBdHRyaWJ1dGVzKGV4aXN0aW5nLCBlbnRyeS5hdHRyaWJ1dGVzLCBjaGlsZFJlc291cmNlLCBjaGlsZFdyaXRhYmxlQXR0cmlidXRlcylcbiAgICAgICAgICBhd2FpdCAvKipcbiAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICBAdHlwZSB7P30gKi8gKGNoaWxkUmVzb3VyY2UpLl9hc3NpZ25XaXRoVmlydHVhbFNldHRlcnMoZXhpc3RpbmcsIGZpbHRlcmVkKVxuICAgICAgICAgIGF3YWl0IGV4aXN0aW5nLnNhdmUoKVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGVudHJ5Lm5lc3RlZEF0dHJpYnV0ZXMpIHtcbiAgICAgICAgICBhd2FpdCAvKipcbiAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICBAdHlwZSB7P30gKi8gKGNoaWxkUmVzb3VyY2UpLl9hcHBseU5lc3RlZEF0dHJpYnV0ZXMoZXhpc3RpbmcsIGVudHJ5Lm5lc3RlZEF0dHJpYnV0ZXMsIGNvbnRyb2xsZXIsIGNoaWxkUGVybWl0KVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGZvciAoY29uc3QgZW50cnkgb2YgY3JlYXRlRW50cmllcykge1xuICAgICAgICBjb25zdCBjaGlsZEF0dHJpYnV0ZXMgPSBlbnRyeT8uYXR0cmlidXRlcyAmJiB0eXBlb2YgZW50cnkuYXR0cmlidXRlcyA9PT0gXCJvYmplY3RcIiA/IGVudHJ5LmF0dHJpYnV0ZXMgOiB7fVxuXG4gICAgICAgIGNvbnN0IGNoaWxkID0gcGFyZW50UmVsYXRpb25zaGlwLmJ1aWxkKHsuLi5jaGlsZEF0dHJpYnV0ZXMsIFtmb3JlaWduS2V5XTogcGFyZW50LmlkKCl9KVxuXG4gICAgICAgIGNvbnN0IGZpbHRlcmVkID0gZmlsdGVyV3JpdGFibGVGcm9udGVuZE1vZGVsQXR0cmlidXRlcyhjaGlsZCwgY2hpbGRBdHRyaWJ1dGVzLCBjaGlsZFJlc291cmNlLCBjaGlsZFdyaXRhYmxlQXR0cmlidXRlcylcblxuICAgICAgICBhd2FpdCAvKipcbiAgICAgICAgICAgICAgICogTmFycm93cyB0aGUgcnVudGltZSB2YWx1ZSB0byB0aGUgZG9jdW1lbnRlZCB0eXBlLlxuICAgICAgICAgICAgICAgIEB0eXBlIHs/fSAqLyAoY2hpbGRSZXNvdXJjZSkuX2Fzc2lnbldpdGhWaXJ0dWFsU2V0dGVycyhjaGlsZCwgZmlsdGVyZWQpXG4gICAgICAgIGF3YWl0IGNoaWxkLnNhdmUoKVxuXG4gICAgICAgIGF3YWl0IHRoaXMuX2F1dGhvcml6ZUNyZWF0ZWRDaGlsZCh7XG4gICAgICAgICAgYWJpbGl0eSxcbiAgICAgICAgICBjaGlsZCxcbiAgICAgICAgICBjaGlsZFJlc291cmNlQ29uZmlndXJhdGlvbjogY2hpbGRSZXNvdXJjZUNvbmZpZy5yZXNvdXJjZUNvbmZpZ3VyYXRpb24sXG4gICAgICAgICAgcmVsYXRpb25zaGlwTmFtZSxcbiAgICAgICAgICB0YXJnZXRNb2RlbENsYXNzXG4gICAgICAgIH0pXG5cbiAgICAgICAgaWYgKGVudHJ5Lm5lc3RlZEF0dHJpYnV0ZXMpIHtcbiAgICAgICAgICBhd2FpdCAvKipcbiAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICBAdHlwZSB7P30gKi8gKGNoaWxkUmVzb3VyY2UpLl9hcHBseU5lc3RlZEF0dHJpYnV0ZXMoY2hpbGQsIGVudHJ5Lm5lc3RlZEF0dHJpYnV0ZXMsIGNvbnRyb2xsZXIsIGNoaWxkUGVybWl0KVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJlc29sdmVzIHRoZSBhYmlsaXR5IGFjdGlvbiBmb3IgYSBjaGlsZCByZXNvdXJjZSB1c2luZyB0aGUgY2hpbGQncyBvd25cbiAgICogYGFiaWxpdGllc2AgbWFwcGluZyDigJQgbmV2ZXIgdGhlIHBhcmVudCBjb250cm9sbGVyJ3MuIFRoaXMgcHJlc2VydmVzXG4gICAqIGN1c3RvbSBtYXBwaW5ncyBsaWtlIGB7dXBkYXRlOiBcIm1hbmFnZVwifWAgYW5kIGNhdGNoZXMgdW5tYXBwZWQgYWN0aW9uc1xuICAgKiBpbnN0ZWFkIG9mIHNpbGVudGx5IGRlZmF1bHRpbmcgdG8gdGhlIHJhdyBhY3Rpb24gbmFtZS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLkZyb250ZW5kTW9kZWxSZXNvdXJjZUNvbmZpZ3VyYXRpb259IGNoaWxkUmVzb3VyY2VDb25maWd1cmF0aW9uIC0gQ2hpbGQgcmVzb3VyY2UgY29uZmlndXJhdGlvbi5cbiAgICogQHBhcmFtIHtcImNyZWF0ZVwiIHwgXCJ1cGRhdGVcIiB8IFwiZGVzdHJveVwifSBhY3Rpb24gLSBGcm9udGVuZCBhY3Rpb24uXG4gICAqIEByZXR1cm5zIHtzdHJpbmd9IC0gQWJpbGl0eSBhY3Rpb24gZm9yIHRoZSBjaGlsZCByZXNvdXJjZS5cbiAgICovXG4gIF9yZXNvbHZlQ2hpbGRBYmlsaXR5QWN0aW9uKGNoaWxkUmVzb3VyY2VDb25maWd1cmF0aW9uLCBhY3Rpb24pIHtcbiAgICBjb25zdCBhYmlsaXRpZXMgPSBjaGlsZFJlc291cmNlQ29uZmlndXJhdGlvbj8uYWJpbGl0aWVzXG5cbiAgICBpZiAoIWFiaWxpdGllcyB8fCB0eXBlb2YgYWJpbGl0aWVzICE9PSBcIm9iamVjdFwiIHx8IEFycmF5LmlzQXJyYXkoYWJpbGl0aWVzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBOZXN0ZWQgY2hpbGQgcmVzb3VyY2UgbXVzdCBkZWZpbmUgYW4gJ2FiaWxpdGllcycgb2JqZWN0IHRvIGF1dGhvcml6ZSBuZXN0ZWQgJHthY3Rpb259LmApXG4gICAgfVxuXG4gICAgY29uc3QgYWJpbGl0eUFjdGlvbiA9IC8qKlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgQHR5cGUge1JlY29yZDxzdHJpbmcsIHN0cmluZz59ICovIChhYmlsaXRpZXMpW2FjdGlvbl1cblxuICAgIGlmICh0eXBlb2YgYWJpbGl0eUFjdGlvbiAhPT0gXCJzdHJpbmdcIiB8fCBhYmlsaXR5QWN0aW9uLmxlbmd0aCA8IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgTmVzdGVkIGNoaWxkIHJlc291cmNlIG11c3QgZGVmaW5lIGFiaWxpdGllcy4ke2FjdGlvbn0uYClcbiAgICB9XG5cbiAgICByZXR1cm4gYWJpbGl0eUFjdGlvblxuICB9XG5cbiAgLyoqXG4gICAqIEZpbmRzIGFuIGV4aXN0aW5nIGNoaWxkIGZvciBhIG5lc3RlZCB1cGRhdGUvZGVzdHJveSwgc2NvcGVkIHRvIHRoZVxuICAgKiBjaGlsZCdzIG93biBtb2RlbCBjbGFzcywgdGhlIHBhcmVudCdzIGZvcmVpZ24ga2V5LCBBTkQgdGhlIGNoaWxkXG4gICAqIHJlc291cmNlJ3MgYWJpbGl0eSBtYXBwaW5nIGZvciB0aGUgcmVxdWVzdGVkIGFjdGlvbi4gVGhyb3dzIHdoZW4gdGhlXG4gICAqIGNoaWxkIGRvZXMgbm90IGV4aXN0LCBkb2VzIG5vdCBiZWxvbmcgdG8gdGhlIGN1cnJlbnQgcGFyZW50LCBvciBpc1xuICAgKiBub3QgYXV0aG9yaXplZCDigJQgYWxsIG9mIHdoaWNoIG11c3Qgcm9sbCB0aGUgdHJhbnNhY3Rpb24gYmFjay5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBBcmd1bWVudHMuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vYXV0aG9yaXphdGlvbi9hYmlsaXR5LmpzXCIpLmRlZmF1bHQgfCB1bmRlZmluZWR9IGFyZ3MuYWJpbGl0eSAtIEN1cnJlbnQgYWJpbGl0eS5cbiAgICogQHBhcmFtIHtcInVwZGF0ZVwiIHwgXCJkZXN0cm95XCJ9IGFyZ3MuYWN0aW9uIC0gRnJvbnRlbmQgYWN0aW9uLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuRnJvbnRlbmRNb2RlbFJlc291cmNlQ29uZmlndXJhdGlvbn0gYXJncy5jaGlsZFJlc291cmNlQ29uZmlndXJhdGlvbiAtIENoaWxkIHJlc291cmNlIGNvbmZpZ3VyYXRpb24uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmZvcmVpZ25LZXkgLSBGb3JlaWduLWtleSBhdHRyaWJ1dGUgb24gdGhlIGNoaWxkIHBvaW50aW5nIHRvIHRoZSBwYXJlbnQuXG4gICAqIEBwYXJhbSB7c3RyaW5nIHwgbnVtYmVyfSBhcmdzLmlkIC0gQ2hpbGQgaWQgZnJvbSB0aGUgcGF5bG9hZC5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdH0gYXJncy5wYXJlbnQgLSBQYXJlbnQgbW9kZWwgaW5zdGFuY2UuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLnJlbGF0aW9uc2hpcE5hbWUgLSBQYXJlbnQncyByZWxhdGlvbnNoaXAgbmFtZSAoZm9yIGVycm9yIG1lc3NhZ2VzKS5cbiAgICogQHBhcmFtIHt0eXBlb2YgaW1wb3J0KFwiLi4vZGF0YWJhc2UvcmVjb3JkL2luZGV4LmpzXCIpLmRlZmF1bHR9IGFyZ3MudGFyZ2V0TW9kZWxDbGFzcyAtIENoaWxkIG1vZGVsIGNsYXNzLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdD59IC0gQXV0aG9yaXplZCwgcGFyZW50LWxpbmtlZCBjaGlsZCBtb2RlbC5cbiAgICovXG4gIGFzeW5jIF9maW5kU2NvcGVkQ2hpbGQoe2FiaWxpdHksIGFjdGlvbiwgY2hpbGRSZXNvdXJjZUNvbmZpZ3VyYXRpb24sIGZvcmVpZ25LZXksIGlkLCBwYXJlbnQsIHJlbGF0aW9uc2hpcE5hbWUsIHRhcmdldE1vZGVsQ2xhc3N9KSB7XG4gICAgY29uc3QgcHJpbWFyeUtleSA9IHRhcmdldE1vZGVsQ2xhc3MucHJpbWFyeUtleSgpXG4gICAgY29uc3QgbG9va3VwID0ge1twcmltYXJ5S2V5XTogaWQsIFtmb3JlaWduS2V5XTogcGFyZW50LmlkKCl9XG4gICAgY29uc3QgcXVlcnkgPSBhYmlsaXR5XG4gICAgICA/IC8qKlxuICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgQHR5cGUgez99ICovICh0YXJnZXRNb2RlbENsYXNzKS5hY2Nlc3NpYmxlRm9yKHRoaXMuX3Jlc29sdmVDaGlsZEFiaWxpdHlBY3Rpb24oY2hpbGRSZXNvdXJjZUNvbmZpZ3VyYXRpb24sIGFjdGlvbiksIGFiaWxpdHkpXG4gICAgICA6IC8qKlxuICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgQHR5cGUgez99ICovICh0YXJnZXRNb2RlbENsYXNzKS53aGVyZSh7fSlcblxuICAgIGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgcXVlcnkuZmluZEJ5KGxvb2t1cClcblxuICAgIGlmICghZXhpc3RpbmcpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgQ2Fubm90ICR7YWN0aW9ufSBuZXN0ZWQgJHtyZWxhdGlvbnNoaXBOYW1lfVtpZD0ke2lkfV06IHJlY29yZCBub3QgZm91bmQsIGRvZXMgbm90IGJlbG9uZyB0byBwYXJlbnQgJHtwYXJlbnQuZ2V0TW9kZWxDbGFzcygpLm5hbWV9W2lkPSR7cGFyZW50LmlkKCl9XSwgb3IgaXMgbm90IGF1dGhvcml6ZWQuYClcbiAgICB9XG5cbiAgICByZXR1cm4gZXhpc3RpbmdcbiAgfVxuXG4gIC8qKlxuICAgKiBWZXJpZmllcyBhbiBhbHJlYWR5LXNhdmVkIG5lc3RlZCBjaGlsZCBpcyBhdXRob3JpemVkIHVuZGVyIHRoZSBjaGlsZFxuICAgKiByZXNvdXJjZSdzIG93biBgY3JlYXRlYCBhYmlsaXR5LiBSb2xscyBiYWNrIHZpYSB0aHJvd24gZXJyb3Igd2hlbiBub3RcbiAgICogYXV0aG9yaXplZCBzbyB0aGUgb3V0ZXIgdHJhbnNhY3Rpb24gZGVzdHJveXMgdGhlIGluc2VydC5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBBcmd1bWVudHMuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vYXV0aG9yaXphdGlvbi9hYmlsaXR5LmpzXCIpLmRlZmF1bHQgfCB1bmRlZmluZWR9IGFyZ3MuYWJpbGl0eSAtIEN1cnJlbnQgYWJpbGl0eS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9yZWNvcmQvaW5kZXguanNcIikuZGVmYXVsdH0gYXJncy5jaGlsZCAtIENoaWxkIG1vZGVsIGluc3RhbmNlIGp1c3QgY3JlYXRlZC5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLkZyb250ZW5kTW9kZWxSZXNvdXJjZUNvbmZpZ3VyYXRpb259IGFyZ3MuY2hpbGRSZXNvdXJjZUNvbmZpZ3VyYXRpb24gLSBDaGlsZCByZXNvdXJjZSBjb25maWd1cmF0aW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5yZWxhdGlvbnNoaXBOYW1lIC0gUGFyZW50J3MgcmVsYXRpb25zaGlwIG5hbWUgKGZvciBlcnJvciBtZXNzYWdlcykuXG4gICAqIEBwYXJhbSB7dHlwZW9mIGltcG9ydChcIi4uL2RhdGFiYXNlL3JlY29yZC9pbmRleC5qc1wiKS5kZWZhdWx0fSBhcmdzLnRhcmdldE1vZGVsQ2xhc3MgLSBDaGlsZCBtb2RlbCBjbGFzcy5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqL1xuICBhc3luYyBfYXV0aG9yaXplQ3JlYXRlZENoaWxkKHthYmlsaXR5LCBjaGlsZCwgY2hpbGRSZXNvdXJjZUNvbmZpZ3VyYXRpb24sIHJlbGF0aW9uc2hpcE5hbWUsIHRhcmdldE1vZGVsQ2xhc3N9KSB7XG4gICAgaWYgKCFhYmlsaXR5KSByZXR1cm5cblxuICAgIGNvbnN0IGFiaWxpdHlBY3Rpb24gPSB0aGlzLl9yZXNvbHZlQ2hpbGRBYmlsaXR5QWN0aW9uKGNoaWxkUmVzb3VyY2VDb25maWd1cmF0aW9uLCBcImNyZWF0ZVwiKVxuICAgIGNvbnN0IHByaW1hcnlLZXkgPSB0YXJnZXRNb2RlbENsYXNzLnByaW1hcnlLZXkoKVxuICAgIGNvbnN0IGF1dGhvcml6ZWRJZHMgPSBhd2FpdCAvKipcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICogTmFycm93cyB0aGUgcnVudGltZSB2YWx1ZSB0byB0aGUgZG9jdW1lbnRlZCB0eXBlLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEB0eXBlIHs/fSAqLyAodGFyZ2V0TW9kZWxDbGFzcylcbiAgICAgIC5hY2Nlc3NpYmxlRm9yKGFiaWxpdHlBY3Rpb24sIGFiaWxpdHkpXG4gICAgICAud2hlcmUoe1twcmltYXJ5S2V5XTogY2hpbGQucmVhZEF0dHJpYnV0ZShwcmltYXJ5S2V5KX0pXG4gICAgICAucGx1Y2socHJpbWFyeUtleSlcblxuICAgIGlmIChhdXRob3JpemVkSWRzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBOZXN0ZWQgY3JlYXRlIG9uICR7cmVsYXRpb25zaGlwTmFtZX1bJHt0YXJnZXRNb2RlbENsYXNzLm5hbWV9XSBub3QgYXV0aG9yaXplZC5gKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBCZXN0LWVmZm9ydCBmb3JlaWduLWtleSBpbmZlcmVuY2UgZm9yIHJlbGF0aW9uc2hpcHMgdGhhdCBkb24ndCBkZWNsYXJlIGl0LlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL3JlY29yZC9pbmRleC5qc1wiKS5kZWZhdWx0fSBwYXJlbnQgLSBQYXJlbnQgbW9kZWwuXG4gICAqIEBwYXJhbSB7e2ZvcmVpZ25LZXk/OiBzdHJpbmd9fSBkZWZpbml0aW9uIC0gUmVsYXRpb25zaGlwIGRlZmluaXRpb24uXG4gICAqIEByZXR1cm5zIHtzdHJpbmd9IC0gRm9yZWlnbi1rZXkgYXR0cmlidXRlIG5hbWUuXG4gICAqL1xuICBfaW5mZXJGb3JlaWduS2V5KHBhcmVudCwgZGVmaW5pdGlvbikge1xuICAgIGlmIChkZWZpbml0aW9uLmZvcmVpZ25LZXkpIHJldHVybiBkZWZpbml0aW9uLmZvcmVpZ25LZXlcblxuICAgIGNvbnN0IHBhcmVudE1vZGVsTmFtZSA9IHBhcmVudC5nZXRNb2RlbENsYXNzKCkubmFtZSB8fCBcIlwiXG4gICAgY29uc3QgdW5kZXJzY29yZWQgPSBwYXJlbnRNb2RlbE5hbWUucmVwbGFjZSgvKFtBLVpdKS9nLCAobWF0Y2gsIGxldHRlciwgaW5kZXgpID0+IChpbmRleCA9PT0gMCA/IGxldHRlci50b0xvd2VyQ2FzZSgpIDogYF8ke2xldHRlci50b0xvd2VyQ2FzZSgpfWApKVxuXG4gICAgcmV0dXJuIGAke2luZmxlY3Rpb24uY2FtZWxpemUodW5kZXJzY29yZWQsIHRydWUpfUlkYFxuICB9XG5cbiAgLyoqXG4gICAqIEFmdGVyIG5lc3RlZCB3cml0ZXMsIHByZWxvYWQgZXZlcnkgcmVsYXRpb25zaGlwIGRlY2xhcmVkIGluIHRoZVxuICAgKiBwYXJlbnQncyBwZXJtaXQgc28gdGhlIHBvc3Qtc2F2ZSBzZXJpYWxpemUgc3RlcCBlbWl0cyB0aGVtIGFuZCB0aGVcbiAgICogY2xpZW50IGNhbiByZWNvbmNpbGUgaWRzLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2RhdGFiYXNlL3JlY29yZC9pbmRleC5qc1wiKS5kZWZhdWx0fSBtb2RlbCAtIFNhdmVkIHBhcmVudCBtb2RlbC5cbiAgICogQHBhcmFtIHt7YXR0cmlidXRlczogc3RyaW5nW10sIG5lc3RlZDogUmVjb3JkPHN0cmluZywgPz59fSBwZXJtaXQgLSBQYXJzZWQgcGFyZW50IHBlcm1pdC5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqL1xuICBhc3luYyBfcHJlbG9hZE5lc3RlZFdyaXRhYmxlUmVsYXRpb25zaGlwcyhtb2RlbCwgcGVybWl0KSB7XG4gICAgY29uc3QgcmVsYXRpb25zaGlwTmFtZXMgPSBPYmplY3Qua2V5cyhwZXJtaXQubmVzdGVkKVxuXG4gICAgaWYgKHJlbGF0aW9uc2hpcE5hbWVzLmxlbmd0aCA9PT0gMCkgcmV0dXJuXG5cbiAgICBmb3IgKGNvbnN0IHJlbGF0aW9uc2hpcE5hbWUgb2YgcmVsYXRpb25zaGlwTmFtZXMpIHtcbiAgICAgIGlmICh0eXBlb2YgLyoqXG4gICAgICAgICAgICAgICAgICAqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS5cbiAgICAgICAgICAgICAgICAgICBAdHlwZSB7P30gKi8gKG1vZGVsKS5sb2FkUmVsYXRpb25zaGlwID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgYXdhaXQgLyoqXG4gICAgICAgICAgICAgICAqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS5cbiAgICAgICAgICAgICAgICBAdHlwZSB7P30gKi8gKG1vZGVsKS5sb2FkUmVsYXRpb25zaGlwKHJlbGF0aW9uc2hpcE5hbWUpXG4gICAgICB9XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogUGFyc2VzIHRoZSBSYWlscy9hcGlfbWFrZXItc3R5bGUgZmxhdCBwZXJtaXQgc3BlYyByZXR1cm5lZCBmcm9tXG4gKiBgcGVybWl0dGVkUGFyYW1zKGFyZylgIGludG8gYSBzdHJ1Y3R1cmVkIHNoYXBlIHVzZWQgaW50ZXJuYWxseSBieSB0aGVcbiAqIHdyaXRlIHBpcGVsaW5lLiBTdHJpbmdzIGJlY29tZSBhdHRyaWJ1dGUgcGVybWl0czsgb2JqZWN0cyB3aG9zZSBrZXlzXG4gKiBlbmQgaW4gYEF0dHJpYnV0ZXNgIGJlY29tZSBuZXN0ZWQgcGVybWl0cyAodGhlIGtleSBwcmVmaXggbmFtZXMgdGhlXG4gKiByZWxhdGlvbnNoaXApLlxuICpcbiAqICAgcGFyc2VQZXJtaXR0ZWRQYXJhbXMoW1wiZmlyc3ROYW1lXCIsIFwibGFzdE5hbWVcIixcbiAqICAgICB7dGFza3NBdHRyaWJ1dGVzOiBbXCJpZFwiLCBcIl9kZXN0cm95XCIsIFwibmFtZVwiXX1cbiAqICAgXSlcbiAqICAgLy8g4oaSIHtcbiAqICAgLy8gICBhdHRyaWJ1dGVzOiBbXCJmaXJzdE5hbWVcIiwgXCJsYXN0TmFtZVwiXSxcbiAqICAgLy8gICBuZXN0ZWQ6IHtcbiAqICAgLy8gICAgIHRhc2tzOiB7YXR0cmlidXRlczogW1wiaWRcIiwgXCJfZGVzdHJveVwiLCBcIm5hbWVcIl0sIG5lc3RlZDoge319XG4gKiAgIC8vICAgfVxuICogICAvLyB9XG4gKiBAcGFyYW0ge0FycmF5PHN0cmluZyB8IFJlY29yZDxzdHJpbmcsID8+PiB8IHVuZGVmaW5lZH0gcGVybWl0U3BlYyAtIEZsYXQgcGVybWl0IHNwZWMuXG4gKiBAcmV0dXJucyB7e2F0dHJpYnV0ZXM6IHN0cmluZ1tdLCBuZXN0ZWQ6IFJlY29yZDxzdHJpbmcsIHthdHRyaWJ1dGVzOiBzdHJpbmdbXSwgbmVzdGVkOiBSZWNvcmQ8c3RyaW5nLCA/Pn0+fX0gLSBQYXJzZWQgc3RydWN0dXJlLlxuICovXG5mdW5jdGlvbiBwYXJzZVBlcm1pdHRlZFBhcmFtcyhwZXJtaXRTcGVjKSB7XG4gIC8qKlxuICAgKiBBdHRyaWJ1dGVzLlxuICAgIEB0eXBlIHtzdHJpbmdbXX0gKi9cbiAgY29uc3QgYXR0cmlidXRlcyA9IFtdXG4gIC8qKlxuICAgKiBOZXN0ZWQuXG4gICAgQHR5cGUge1JlY29yZDxzdHJpbmcsIHthdHRyaWJ1dGVzOiBzdHJpbmdbXSwgbmVzdGVkOiBSZWNvcmQ8c3RyaW5nLCA/Pn0+fSAqL1xuICBjb25zdCBuZXN0ZWQgPSB7fVxuXG4gIGlmICghQXJyYXkuaXNBcnJheShwZXJtaXRTcGVjKSkgcmV0dXJuIHthdHRyaWJ1dGVzLCBuZXN0ZWR9XG5cbiAgZm9yIChjb25zdCBlbnRyeSBvZiBwZXJtaXRTcGVjKSB7XG4gICAgaWYgKHR5cGVvZiBlbnRyeSA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgYXR0cmlidXRlcy5wdXNoKGVudHJ5KVxuICAgIH0gZWxzZSBpZiAoZW50cnkgJiYgdHlwZW9mIGVudHJ5ID09PSBcIm9iamVjdFwiICYmICFBcnJheS5pc0FycmF5KGVudHJ5KSkge1xuICAgICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoZW50cnkpKSB7XG4gICAgICAgIGlmICgha2V5LmVuZHNXaXRoKFwiQXR0cmlidXRlc1wiKSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBwZXJtaXR0ZWRQYXJhbXMgZW50cnk6IG5lc3RlZCByZWxhdGlvbnNoaXAga2V5cyBtdXN0IGVuZCBpbiBcIkF0dHJpYnV0ZXNcIiAoZ290IFwiJHtrZXl9XCIpLiBVc2UgXCIke2tleX1BdHRyaWJ1dGVzXCIgaW5zdGVhZC5gKVxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHJlbGF0aW9uc2hpcE5hbWUgPSBrZXkuc2xpY2UoMCwgLVwiQXR0cmlidXRlc1wiLmxlbmd0aClcblxuICAgICAgICBpZiAoIXJlbGF0aW9uc2hpcE5hbWUpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgcGVybWl0dGVkUGFyYW1zIGVudHJ5OiBlbXB0eSByZWxhdGlvbnNoaXAgbmFtZSBpbiBrZXkgXCIke2tleX1cIi5gKVxuICAgICAgICB9XG4gICAgICAgIGlmICghQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgcGVybWl0dGVkUGFyYW1zIGVudHJ5IGZvciBcIiR7a2V5fVwiOiBleHBlY3RlZCBhcnJheSBwZXJtaXQgc3BlYywgZ290ICR7dHlwZW9mIHZhbHVlfS5gKVxuICAgICAgICB9XG5cbiAgICAgICAgbmVzdGVkW3JlbGF0aW9uc2hpcE5hbWVdID0gcGFyc2VQZXJtaXR0ZWRQYXJhbXModmFsdWUpXG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBwZXJtaXR0ZWRQYXJhbXMgZW50cnk6IGV4cGVjdGVkIHN0cmluZyBvciBuZXN0ZWQtYXR0cmlidXRlcyBvYmplY3QsIGdvdCAke3R5cGVvZiBlbnRyeX0uYClcbiAgICB9XG4gIH1cblxuICByZXR1cm4ge2F0dHJpYnV0ZXMsIG5lc3RlZH1cbn1cblxuLyoqXG4gKiBSdW5zIGZpbHRlciB3cml0YWJsZSBmcm9udGVuZCBtb2RlbCBhdHRyaWJ1dGVzLlxuICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCA/Pn0gcmVjZWl2ZXIgLSBNb2RlbCBpbnN0YW5jZSBvciBwcm90b3R5cGUuXG4gKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsID8+fSBhdHRyaWJ1dGVzIC0gSW5jb21pbmcgZnJvbnRlbmQtbW9kZWwgYXR0cmlidXRlcy5cbiAqIEBwYXJhbSB7RnJvbnRlbmRNb2RlbEJhc2VSZXNvdXJjZSB8IG51bGx9IFtyZXNvdXJjZV0gLSBSZXNvdXJjZSBpbnN0YW5jZSBmb3IgdmlydHVhbC1zZXR0ZXIgZGV0ZWN0aW9uLlxuICogQHBhcmFtIHtzdHJpbmdbXSB8IG51bGx9IFtwZXJtaXR0ZWRBdHRyaWJ1dGVOYW1lc10gLSBPcHRpb25hbCBleHBsaWNpdCBwZXJtaXQgbGlzdC4gYG51bGxgIGZhbGxzIGJhY2sgdG8gc2V0dGVyLWV4aXN0ZW5jZSBjaGVja3Mgb25seS5cbiAqIEByZXR1cm5zIHtSZWNvcmQ8c3RyaW5nLCA/Pn0gLSBXcml0YWJsZSBhdHRyaWJ1dGVzIG9ubHkuXG4gKi9cbmZ1bmN0aW9uIGZpbHRlcldyaXRhYmxlRnJvbnRlbmRNb2RlbEF0dHJpYnV0ZXMocmVjZWl2ZXIsIGF0dHJpYnV0ZXMsIHJlc291cmNlID0gLyoqXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBAdHlwZSB7RnJvbnRlbmRNb2RlbEJhc2VSZXNvdXJjZSB8IG51bGx9ICovIChudWxsKSwgcGVybWl0dGVkQXR0cmlidXRlTmFtZXMgPSBudWxsKSB7XG4gIC8vIEZyb250ZW5kLW1vZGVsIHdyaXRlcyBzaG91bGQgZmFpbCBmYXN0IHdoZW4gY2FsbGVycyBzdWJtaXQgcmVhZC1vbmx5IG9yIHVua25vd24gYXR0cnMuXG4gIC8vIFNpbGVudCBkcm9wcyBoaWRlIGNvbnRyYWN0IG1pc3Rha2VzIGluIGdlbmVyYXRlZCBtb2RlbHMgYW5kIGFwcC1zaWRlIHdyYXBwZXIgY29kZS5cbiAgLyoqXG4gICAqIFdyaXRhYmxlIGF0dHJpYnV0ZXMuXG4gICAgQHR5cGUge1JlY29yZDxzdHJpbmcsID8+fSAqL1xuICBjb25zdCB3cml0YWJsZUF0dHJpYnV0ZXMgPSB7fVxuICAvKipcbiAgICogSW52YWxpZCBhdHRyaWJ1dGVzLlxuICAgIEB0eXBlIHtzdHJpbmdbXX0gKi9cbiAgY29uc3QgaW52YWxpZEF0dHJpYnV0ZXMgPSBbXVxuICAvKipcbiAgICogTm90IHBlcm1pdHRlZCBhdHRyaWJ1dGVzLlxuICAgIEB0eXBlIHtzdHJpbmdbXX0gKi9cbiAgY29uc3Qgbm90UGVybWl0dGVkQXR0cmlidXRlcyA9IFtdXG5cbiAgY29uc3QgcGVybWl0U2V0ID0gQXJyYXkuaXNBcnJheShwZXJtaXR0ZWRBdHRyaWJ1dGVOYW1lcykgPyBuZXcgU2V0KHBlcm1pdHRlZEF0dHJpYnV0ZU5hbWVzKSA6IG51bGxcbiAgY29uc3QgdHJhbnNsYXRlZFNldCA9IHJlc291cmNlID8gbmV3IFNldCgvKipcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBAdHlwZSB7dHlwZW9mIEZyb250ZW5kTW9kZWxCYXNlUmVzb3VyY2V9ICovIChyZXNvdXJjZS5jb25zdHJ1Y3RvcikudHJhbnNsYXRlZEF0dHJpYnV0ZXMgfHwgW10pIDogbmV3IFNldCgpXG5cbiAgZm9yIChjb25zdCBbYXR0cmlidXRlTmFtZSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGF0dHJpYnV0ZXMpKSB7XG4gICAgaWYgKHBlcm1pdFNldCAmJiAhcGVybWl0U2V0LmhhcyhhdHRyaWJ1dGVOYW1lKSkge1xuICAgICAgbm90UGVybWl0dGVkQXR0cmlidXRlcy5wdXNoKGF0dHJpYnV0ZU5hbWUpXG4gICAgICBjb250aW51ZVxuICAgIH1cblxuICAgIGNvbnN0IHNldHRlck5hbWUgPSBgc2V0JHtpbmZsZWN0aW9uLmNhbWVsaXplKGF0dHJpYnV0ZU5hbWUpfWBcbiAgICBjb25zdCByZXNvdXJjZVNldHRlck5hbWUgPSBgJHtzZXR0ZXJOYW1lfUF0dHJpYnV0ZWBcblxuICAgIGlmIChzZXR0ZXJOYW1lIGluIHJlY2VpdmVyKSB7XG4gICAgICB3cml0YWJsZUF0dHJpYnV0ZXNbYXR0cmlidXRlTmFtZV0gPSB2YWx1ZVxuICAgIH0gZWxzZSBpZiAocmVzb3VyY2UgJiYgdHlwZW9mIC8qKlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEB0eXBlIHtSZWNvcmQ8c3RyaW5nLCA/Pn0gKi8gKC8qKlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICogTmFycm93cyB0aGUgcnVudGltZSB2YWx1ZSB0byB0aGUgZG9jdW1lbnRlZCB0eXBlLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBAdHlwZSB7P30gKi8gKHJlc291cmNlKSlbcmVzb3VyY2VTZXR0ZXJOYW1lXSA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICB3cml0YWJsZUF0dHJpYnV0ZXNbYXR0cmlidXRlTmFtZV0gPSB2YWx1ZVxuICAgIH0gZWxzZSBpZiAodHJhbnNsYXRlZFNldC5oYXMoYXR0cmlidXRlTmFtZSkpIHtcbiAgICAgIHdyaXRhYmxlQXR0cmlidXRlc1thdHRyaWJ1dGVOYW1lXSA9IHZhbHVlXG4gICAgfSBlbHNlIHtcbiAgICAgIGludmFsaWRBdHRyaWJ1dGVzLnB1c2goYXR0cmlidXRlTmFtZSlcbiAgICB9XG4gIH1cblxuICBpZiAobm90UGVybWl0dGVkQXR0cmlidXRlcy5sZW5ndGggPiAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBGcm9udGVuZCBtb2RlbCB3cml0ZSBhdHRyaWJ1dGVzIG5vdCBwZXJtaXR0ZWQgYnkgcGVybWl0dGVkUGFyYW1zKCk6ICR7bm90UGVybWl0dGVkQXR0cmlidXRlcy5qb2luKFwiLCBcIil9YClcbiAgfVxuXG4gIGlmIChpbnZhbGlkQXR0cmlidXRlcy5sZW5ndGggPiAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGZyb250ZW5kIG1vZGVsIHdyaXRlIGF0dHJpYnV0ZXM6ICR7aW52YWxpZEF0dHJpYnV0ZXMuam9pbihcIiwgXCIpfWApXG4gIH1cblxuICByZXR1cm4gd3JpdGFibGVBdHRyaWJ1dGVzXG59XG4iXX0=