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,9 +1,11 @@
1
1
  // @ts-check
2
- import net from "net";
3
- import JsonSocket from "./json-socket.js";
4
- import BackgroundJobsScheduler from "./scheduler.js";
5
- import BackgroundJobsStore from "./store.js";
6
- import Logger from "../logger.js";
2
+
3
+ import net from "net"
4
+ import JsonSocket from "./json-socket.js"
5
+ import BackgroundJobsScheduler from "./scheduler.js"
6
+ import BackgroundJobsStore from "./store.js"
7
+ import Logger from "../logger.js"
8
+
7
9
  /**
8
10
  * Channel used by `background-jobs-main` to coordinate dispatch wake-ups
9
11
  * across processes via Beacon. Workers do NOT subscribe to this channel
@@ -11,13 +13,14 @@ import Logger from "../logger.js";
11
13
  * main; this channel exists so cross-process enqueues (or future
12
14
  * multi-main deployments) can poke an idle main to drain.
13
15
  */
14
- const DISPATCH_CHANNEL = "velocious-background-jobs-dispatch";
16
+ const DISPATCH_CHANNEL = "velocious-background-jobs-dispatch"
17
+
15
18
  /**
16
19
  * `setTimeout` is implemented with 32-bit signed delays on Node; passing
17
20
  * anything larger silently clamps to 1ms and fires immediately. Cap the
18
21
  * scheduled-job timer here and re-arm when it expires.
19
22
  */
20
- const MAX_TIMER_MS = 2_147_483_647; // ~24.8 days
23
+ const MAX_TIMER_MS = 2_147_483_647 // ~24.8 days
21
24
  /**
22
25
  * WorkerExecutionModeCapability type.
23
26
  * @typedef {object} WorkerExecutionModeCapability
@@ -28,840 +31,896 @@ const MAX_TIMER_MS = 2_147_483_647; // ~24.8 days
28
31
  * Worker execution mode capabilities.
29
32
  @type {WorkerExecutionModeCapability[]} */
30
33
  const WORKER_EXECUTION_MODE_CAPABILITIES = [
31
- { executionMode: "inline", accepts: (worker) => worker.acceptsInlineJobs !== false },
32
- { executionMode: "forked", accepts: (worker) => worker.acceptsForkedJobs !== false },
33
- { executionMode: "spawned", accepts: (worker) => worker.acceptsSpawnedJobs !== false }
34
- ];
35
- const WORKER_EXECUTION_MODE_CAPABILITIES_BY_MODE = new Map(WORKER_EXECUTION_MODE_CAPABILITIES.map((capability) => [capability.executionMode, capability]));
34
+ {executionMode: "inline", accepts: (worker) => worker.acceptsInlineJobs !== false},
35
+ {executionMode: "forked", accepts: (worker) => worker.acceptsForkedJobs !== false},
36
+ {executionMode: "spawned", accepts: (worker) => worker.acceptsSpawnedJobs !== false}
37
+ ]
38
+ const WORKER_EXECUTION_MODE_CAPABILITIES_BY_MODE = new Map(
39
+ WORKER_EXECUTION_MODE_CAPABILITIES.map((capability) => [capability.executionMode, capability])
40
+ )
41
+
36
42
  export default class BackgroundJobsMain {
37
- /**
38
- * Runs constructor.
39
- * @param {object} args - Options.
40
- * @param {import("../configuration.js").default} args.configuration - Configuration.
41
- * @param {string} [args.host] - Hostname.
42
- * @param {number} [args.port] - Port.
43
- */
44
- constructor({ configuration, host, port }) {
45
- this.configuration = configuration;
46
- const config = configuration.getBackgroundJobsConfig();
47
- this.host = host || config.host;
48
- this.port = typeof port === "number" ? port : config.port;
49
- this.dispatchStrategy = config.dispatchStrategy;
50
- this.pollIntervalMs = config.pollIntervalMs;
51
- this.store = new BackgroundJobsStore({ configuration, databaseIdentifier: config.databaseIdentifier });
52
- this.logger = new Logger(this);
53
- /**
54
- * Narrows the runtime value to the documented type.
55
- @type {Set<JsonSocket>} */
56
- this.workers = new Set();
57
- /**
58
- * Narrows the runtime value to the documented type.
59
- @type {Set<JsonSocket>} */
60
- this.readyWorkers = new Set();
61
- /**
62
- * Narrows the runtime value to the documented type.
63
- @type {net.Server | undefined} */
64
- this.server = undefined;
65
- /**
66
- * Narrows the runtime value to the documented type.
67
- @type {NodeJS.Timeout | undefined} */
68
- this._pollTimer = undefined;
69
- /**
70
- * Narrows the runtime value to the documented type.
71
- @type {NodeJS.Timeout | undefined} */
72
- this._scheduledTimer = undefined;
73
- /**
74
- * Narrows the runtime value to the documented type.
75
- @type {NodeJS.Timeout | undefined} */
76
- this._errorRetryTimer = undefined;
77
- /**
78
- * Narrows the runtime value to the documented type.
79
- @type {NodeJS.Timeout | undefined} */
80
- this._orphanTimer = undefined;
81
- /**
82
- * Narrows the runtime value to the documented type.
83
- @type {BackgroundJobsScheduler | undefined} */
84
- this.scheduler = undefined;
85
- this._draining = false;
86
- this._redrainQueued = false;
87
- this._stopped = false;
88
- /**
89
- * Narrows the runtime value to the documented type.
90
- @type {(() => void) | undefined} */
91
- this._unsubscribeBeacon = undefined;
92
- /**
93
- * Narrows the runtime value to the documented type.
94
- @type {((...args: Array<?>) => void) | undefined} */
95
- this._beaconConnectHandler = undefined;
96
- /**
97
- * Narrows the runtime value to the documented type.
98
- @type {import("../beacon/client.js").default | import("../beacon/in-process-client.js").default | undefined} */
99
- this._beaconClient = undefined;
100
- }
101
- /**
102
- * Runs start.
103
- * @returns {Promise<void>} - Resolves when listening.
104
- */
105
- async start() {
106
- this._stopped = false;
107
- this.configuration.setCurrent();
108
- await this.configuration.initialize({ type: "background-jobs-main" });
109
- await this.configuration.connectBeacon({ peerType: "background-jobs-main" });
110
- await this.store.ensureReady();
111
- const server = net.createServer((socket) => this._handleConnection(socket));
112
- this.server = server;
113
- try {
114
- await new Promise((resolve, reject) => {
115
- server.once("error", reject);
116
- server.listen(this.port, this.host, () => resolve(undefined));
117
- });
118
- const address = server.address();
119
- if (address && typeof address === "object") {
120
- this.port = address.port;
121
- }
122
- this._setupDispatchTriggers();
123
- this._orphanTimer = setInterval(() => {
124
- void this._sweepOrphans();
125
- }, 60000);
126
- this.scheduler = new BackgroundJobsScheduler({
127
- configuration: this.configuration,
128
- enqueueJob: async ({ args, jobClass, options }) => {
129
- await this.store.enqueue({
130
- jobName: jobClass.jobName(),
131
- args,
132
- options
133
- });
134
- this._notifyEnqueued();
135
- await this._drain();
136
- }
137
- });
138
- await this.scheduler.start();
139
- // Startup catch-up: drain anything that was waiting before this
140
- // process came up. In beacon mode this is also the safety net for
141
- // races between attaching the connect listener and the initial
142
- // connect firing (the listener could miss the very first connect,
143
- // but this drain covers it).
144
- await this._drain();
145
- }
146
- catch (error) {
147
- await this.stop();
148
- throw error;
149
- }
150
- }
151
- /**
152
- * Runs stop.
153
- * @returns {Promise<void>} - Resolves when closed.
154
- */
155
- async stop() {
156
- this._stopped = true;
157
- this._closeWorkers();
158
- this._clearTimers();
159
- this._disconnectBeaconHandlers();
160
- this.scheduler?.stop();
161
- await this._stopBeaconAndServer();
162
- }
163
- /**
164
- * Runs close workers.
165
- @returns {void} */
166
- _closeWorkers() {
167
- for (const worker of this.workers) {
168
- worker.close();
169
- }
170
- }
171
- /**
172
- * Runs clear timers.
173
- @returns {void} */
174
- _clearTimers() {
175
- if (this._pollTimer)
176
- clearInterval(this._pollTimer);
177
- if (this._scheduledTimer)
178
- clearTimeout(this._scheduledTimer);
179
- if (this._errorRetryTimer)
180
- clearTimeout(this._errorRetryTimer);
181
- if (this._orphanTimer)
182
- clearInterval(this._orphanTimer);
183
- this._pollTimer = undefined;
184
- this._scheduledTimer = undefined;
185
- this._errorRetryTimer = undefined;
186
- this._orphanTimer = undefined;
187
- }
188
- /**
189
- * Runs disconnect beacon handlers.
190
- @returns {void} */
191
- _disconnectBeaconHandlers() {
192
- if (this._unsubscribeBeacon) {
193
- this._unsubscribeBeacon();
194
- this._unsubscribeBeacon = undefined;
195
- }
196
- if (this._beaconClient && this._beaconConnectHandler) {
197
- this._beaconClient.off("connect", this._beaconConnectHandler);
198
- }
199
- this._beaconConnectHandler = undefined;
200
- this._beaconClient = undefined;
201
- }
202
- /**
203
- * Runs stop beacon and server.
204
- @returns {Promise<void>} */
205
- async _stopBeaconAndServer() {
206
- try {
207
- await this.configuration.disconnectBeacon();
208
- }
209
- finally {
210
- await this._closeServerAndDatabaseConnections();
211
- }
212
- }
213
- /**
214
- * Runs close server and database connections.
215
- @returns {Promise<void>} */
216
- async _closeServerAndDatabaseConnections() {
217
- try {
218
- await this._closeServer();
219
- }
220
- finally {
221
- await this.configuration.closeDatabaseConnections();
222
- }
223
- }
224
- /**
225
- * Runs close server.
226
- @returns {Promise<void>} */
227
- async _closeServer() {
228
- if (!this.server)
229
- return;
230
- const { server } = this;
231
- this.server = undefined;
232
- await new Promise((resolve) => server.close(() => resolve(undefined)));
233
- }
234
- /**
235
- * Runs get port.
236
- * @returns {number} - Bound port.
237
- */
238
- getPort() {
239
- return this.port;
240
- }
241
- /**
242
- * Wires up the dispatch-triggering signal sources for the configured
243
- * strategy. In `"beacon"` mode (default) this means subscribing to the
244
- * `velocious-background-jobs-dispatch` channel for cross-process
245
- * wake-ups, listening for Beacon (re)connects to catch up on missed
246
- * work, and relying on direct in-process calls from `_handleEnqueue`,
247
- * `_handleJobComplete`/`Failed`, worker hello/ready, and the
248
- * scheduled-job `setTimeout`. In `"polling"` mode we restore the
249
- * legacy fixed-interval poll for users who want the previous behavior.
250
- * @returns {void}
251
- */
252
- _setupDispatchTriggers() {
253
- if (this.dispatchStrategy === "polling") {
254
- this._pollTimer = setInterval(() => {
255
- void this._drain();
256
- }, this.pollIntervalMs);
257
- return;
258
- }
259
- const beaconClient = this.configuration.getBeaconClient();
260
- if (!beaconClient)
261
- return;
262
- this._beaconClient = beaconClient;
263
- this._unsubscribeBeacon = beaconClient.onBroadcast((message) => {
264
- if (message?.channel !== DISPATCH_CHANNEL)
265
- return;
266
- void this._drain();
267
- });
268
- // Drain on every (re)connect to catch up on jobs enqueued while the
269
- // bus was unreachable. The DB is the durable log; Beacon is just the
270
- // wake-up signal.
271
- this._beaconConnectHandler = () => {
272
- void this._drain();
273
- };
274
- beaconClient.on("connect", this._beaconConnectHandler);
275
- }
276
- /**
277
- * Publishes a dispatch wake-up on the Beacon channel. No-op in polling
278
- * mode or when Beacon is not connected; in those cases the direct
279
- * in-process `_drain()` call in the enqueue/handle paths is sufficient
280
- * (there are no other processes to notify).
281
- * @returns {void}
282
- */
283
- _notifyEnqueued() {
284
- if (this.dispatchStrategy === "polling")
285
- return;
286
- const beaconClient = this.configuration.getBeaconClient();
287
- if (!beaconClient || !beaconClient.isConnected())
288
- return;
289
- try {
290
- beaconClient.publish({
291
- channel: DISPATCH_CHANNEL,
292
- broadcastParams: {},
293
- body: { action: "wake" }
294
- });
295
- }
296
- catch (error) {
297
- this.logger.warn(() => ["Failed to publish background jobs wake broadcast:", error]);
298
- }
299
- }
300
- /**
301
- * Runs handle connection.
302
- * @param {import("net").Socket} socket - Socket.
303
- * @returns {void}
304
- */
305
- _handleConnection(socket) {
306
- const jsonSocket = new JsonSocket(socket);
307
- /**
308
- * Role.
309
- @type {import("./types.js").BackgroundJobSocketRole | null} */
310
- let role = null;
311
- const cleanup = () => {
312
- if (role === "worker") {
313
- this.workers.delete(jsonSocket);
314
- this.readyWorkers.delete(jsonSocket);
315
- }
316
- };
317
- jsonSocket.on("close", cleanup);
318
- jsonSocket.on("error", (error) => {
319
- this.logger.warn(() => ["Background jobs connection error:", error]);
320
- cleanup();
321
- });
322
- jsonSocket.on("message", (message) => {
323
- role = this._handleSocketMessage({ jsonSocket, message, role });
324
- });
325
- }
326
- /**
327
- * Runs handle socket message.
328
- * @param {object} args - Options.
329
- * @param {JsonSocket} args.jsonSocket - JSON socket.
330
- * @param {import("./types.js").BackgroundJobSocketMessage} args.message - Socket message.
331
- * @param {import("./types.js").BackgroundJobSocketRole | null} args.role - Current socket role.
332
- * @returns {import("./types.js").BackgroundJobSocketRole | null} - Updated socket role.
333
- */
334
- _handleSocketMessage({ jsonSocket, message, role }) {
335
- if (!role)
336
- return this._handleRolelessSocketMessage({ jsonSocket, message });
337
- if (role === "client")
338
- this._handleClientSocketMessage({ jsonSocket, message });
339
- if (role === "worker")
340
- this._handleWorkerSocketMessage({ jsonSocket, message });
341
- if (role === "reporter")
342
- this._handleReporterSocketMessage({ jsonSocket, message });
343
- return role;
344
- }
345
- /**
346
- * Runs handle roleless socket message.
347
- * @param {object} args - Options.
348
- * @param {JsonSocket} args.jsonSocket - JSON socket.
349
- * @param {import("./types.js").BackgroundJobSocketMessage} args.message - Socket message.
350
- * @returns {import("./types.js").BackgroundJobSocketRole | null} - New socket role.
351
- */
352
- _handleRolelessSocketMessage({ jsonSocket, message }) {
353
- if (message?.type !== "hello")
354
- return null;
355
- if (message.role === "worker") {
356
- jsonSocket.workerId = message.workerId;
357
- this.workers.add(jsonSocket);
358
- }
359
- return message.role;
360
- }
361
- /**
362
- * Runs handle client socket message.
363
- * @param {object} args - Options.
364
- * @param {JsonSocket} args.jsonSocket - JSON socket.
365
- * @param {import("./types.js").BackgroundJobSocketMessage} args.message - Socket message.
366
- * @returns {void}
367
- */
368
- _handleClientSocketMessage({ jsonSocket, message }) {
369
- if (message?.type === "enqueue") {
370
- this._handleEnqueue({ jsonSocket, message });
371
- }
372
- }
373
- /**
374
- * Runs handle worker socket message.
375
- * @param {object} args - Options.
376
- * @param {JsonSocket} args.jsonSocket - JSON socket.
377
- * @param {import("./types.js").BackgroundJobSocketMessage} args.message - Socket message.
378
- * @returns {void}
379
- */
380
- _handleWorkerSocketMessage({ jsonSocket, message }) {
381
- if (message?.type === "ready") {
382
- this._handleWorkerReady({ jsonSocket, message });
383
- return;
384
- }
385
- if (message?.type === "draining") {
386
- this._handleWorkerDraining({ jsonSocket });
387
- return;
388
- }
389
- this._handleReporterSocketMessage({ jsonSocket, message });
390
- }
391
- /**
392
- * Runs handle reporter socket message.
393
- * @param {object} args - Options.
394
- * @param {JsonSocket} args.jsonSocket - JSON socket.
395
- * @param {import("./types.js").BackgroundJobSocketMessage} args.message - Socket message.
396
- * @returns {void}
397
- */
398
- _handleReporterSocketMessage({ jsonSocket, message }) {
399
- if (message?.type === "job-complete") {
400
- this._handleJobComplete({ jsonSocket, message });
401
- return;
402
- }
403
- if (message?.type === "job-failed") {
404
- this._handleJobFailed({ jsonSocket, message });
405
- }
406
- }
407
- /**
408
- * Runs handle worker ready.
409
- * @param {object} args - Options.
410
- * @param {JsonSocket} args.jsonSocket - JSON socket.
411
- * @param {import("./types.js").BackgroundJobReadyMessage} args.message - Ready message.
412
- * @returns {void}
413
- */
414
- _handleWorkerReady({ jsonSocket, message }) {
415
- jsonSocket.acceptsSpawnedJobs = message.acceptsSpawned !== false && message.acceptsForked !== false;
416
- jsonSocket.acceptsForkedJobs = message.acceptsForked !== false;
417
- jsonSocket.acceptsInlineJobs = message.acceptsInline !== false;
418
- this.readyWorkers.add(jsonSocket);
419
- void this._drain();
420
- }
421
- /**
422
- * Runs handle worker draining.
423
- * @param {object} args - Options.
424
- * @param {JsonSocket} args.jsonSocket - JSON socket.
425
- * @returns {void}
426
- */
427
- _handleWorkerDraining({ jsonSocket }) {
428
- // The worker is shutting down gracefully. Stop dispatching new jobs
429
- // to it but keep the connection in `workers` so any in-flight job
430
- // it's still draining can report its result.
431
- this.readyWorkers.delete(jsonSocket);
432
- }
433
- /**
434
- * Runs handle enqueue.
435
- * @param {object} args - Options.
436
- * @param {JsonSocket} args.jsonSocket - JSON socket.
437
- * @param {import("./types.js").BackgroundJobEnqueueMessage} args.message - Message.
438
- * @returns {Promise<void>} - Resolves when handled.
439
- */
440
- async _handleEnqueue({ jsonSocket, message }) {
441
- try {
442
- const jobId = await this.store.enqueue({
443
- jobName: message.jobName,
444
- args: message.args || [],
445
- options: message.options || {}
446
- });
447
- jsonSocket.send({ type: "enqueued", jobId });
448
- this._notifyEnqueued();
449
- await this._drain();
450
- }
451
- catch (error) {
452
- this.logger.error(() => ["Failed to enqueue background job:", error]);
453
- jsonSocket.send({ type: "enqueue-error", error: "Failed to enqueue job" });
454
- }
455
- }
456
- /**
457
- * Runs handle job complete.
458
- * @param {object} args - Options.
459
- * @param {JsonSocket} args.jsonSocket - JSON socket.
460
- * @param {import("./types.js").BackgroundJobCompleteMessage} args.message - Message.
461
- * @returns {Promise<void>} - Resolves when handled.
462
- */
463
- async _handleJobComplete({ jsonSocket, message }) {
464
- try {
465
- await this.store.markCompleted({
466
- jobId: message.jobId,
467
- workerId: message.workerId,
468
- handedOffAtMs: message.handedOffAtMs
469
- });
470
- jsonSocket.send({ type: "job-updated", jobId: message.jobId });
471
- }
472
- catch (error) {
473
- this.logger.error(() => ["Failed to update job completion:", error]);
474
- jsonSocket.send({ type: "job-update-error", jobId: message.jobId, error: "Failed to update job" });
475
- }
476
- }
477
- /**
478
- * Runs handle job failed.
479
- * @param {object} args - Options.
480
- * @param {JsonSocket} args.jsonSocket - JSON socket.
481
- * @param {import("./types.js").BackgroundJobFailedMessage} args.message - Message.
482
- * @returns {Promise<void>} - Resolves when handled.
483
- */
484
- async _handleJobFailed({ jsonSocket, message }) {
485
- try {
486
- const failedJob = await this.store.markFailed({
487
- jobId: message.jobId,
488
- error: message.error,
489
- workerId: message.workerId,
490
- handedOffAtMs: message.handedOffAtMs
491
- });
492
- if (failedJob) {
493
- this._emitBackgroundJobFailed({
494
- error: message.error,
495
- handedOffAtMs: message.handedOffAtMs,
496
- job: failedJob,
497
- workerId: message.workerId
498
- });
499
- }
500
- jsonSocket.send({ type: "job-updated", jobId: message.jobId });
501
- // A failed job may have been re-queued (with backoff) for retry —
502
- // poke the dispatcher so the retry timer is armed.
503
- this._notifyEnqueued();
504
- await this._drain();
505
- }
506
- catch (error) {
507
- this.logger.error(() => ["Failed to update job failure:", error]);
508
- jsonSocket.send({ type: "job-update-error", jobId: message.jobId, error: "Failed to update job" });
509
- }
510
- }
511
- /**
512
- * Runs emit background job failed.
513
- * @param {{error: ?, handedOffAtMs?: number, job: import("./types.js").BackgroundJobRow, workerId?: string}} args - Failure event data.
514
- * @returns {void}
515
- */
516
- _emitBackgroundJobFailed({ error, handedOffAtMs, job, workerId }) {
517
- const normalizedError = this._normalizeFailureError(error);
518
- const payload = {
519
- context: {
520
- attempts: job.attempts,
521
- handedOffAtMs,
522
- jobArgs: job.args,
523
- jobId: job.id,
524
- jobName: job.jobName,
525
- maxRetries: job.maxRetries,
526
- stage: "background-job-failed",
527
- status: job.status,
528
- terminal: job.status === "failed" || job.status === "orphaned",
529
- willRetry: job.status === "queued",
530
- workerId
531
- },
532
- error: normalizedError
533
- };
534
- const errorEvents = this.configuration.getErrorEvents();
535
- errorEvents.emit("background-job-failed", payload);
536
- errorEvents.emit("all-error", { ...payload, errorType: "background-job-failed" });
537
- }
538
- /**
539
- * Runs normalize failure error.
540
- * @param {?} error - Reported failure value.
541
- * @returns {Error} Normalized error.
542
- */
543
- _normalizeFailureError(error) {
544
- if (error instanceof Error)
545
- return error;
546
- return this._errorFromUnknownFailure(error);
547
- }
548
- /**
549
- * Runs error from unknown failure.
550
- * @param {?} error - Reported failure value.
551
- * @returns {Error} Normalized error.
552
- */
553
- _errorFromUnknownFailure(error) {
554
- const message = this._messageFromUnknownFailure(error);
555
- const normalizedError = new Error(message);
556
- this._copyStringFailureStack({ error, normalizedError });
557
- return normalizedError;
558
- }
559
- /**
560
- * Runs message from unknown failure.
561
- * @param {?} error - Reported failure value.
562
- * @returns {string} Error message.
563
- */
564
- _messageFromUnknownFailure(error) {
565
- if (this._hasStringFailure(error))
566
- return error.trim().split("\n")[0];
567
- return String(error || "Background job failed");
568
- }
569
- /**
570
- * Runs has string failure.
571
- * @param {?} error - Reported failure value.
572
- * @returns {error is string} Whether the value is a non-empty string.
573
- */
574
- _hasStringFailure(error) {
575
- return typeof error === "string" && error.trim().length > 0;
576
- }
577
- /**
578
- * Runs copy string failure stack.
579
- * @param {object} args - Options.
580
- * @param {?} args.error - Reported failure value.
581
- * @param {Error} args.normalizedError - Normalized error.
582
- * @returns {void}
583
- */
584
- _copyStringFailureStack({ error, normalizedError }) {
585
- if (this._hasStringFailure(error))
586
- normalizedError.stack = error;
587
- }
588
- /**
589
- * Drains all dispatchable jobs to ready workers, then arms the
590
- * scheduled-job timer for the next future `scheduled_at_ms`. Coalesces
591
- * concurrent triggers: a wake-up that lands while a drain is in
592
- * flight just sets a re-drain flag and lets the in-flight drain
593
- * re-loop after it finishes, so no signal is dropped but no two
594
- * drains run in parallel.
595
- *
596
- * Resilience: in beacon mode this is the sole wake-up path for
597
- * already-queued work, so a transient DB error during the drain (e.g.
598
- * `nextAvailableJob()` rejecting) must not strand the queue until the
599
- * next external signal. On any error we log it and arm a one-shot
600
- * retry via `_scheduleErrorRetry` using `pollIntervalMs` as the
601
- * cadence; on success the retry timer is cleared. Polling-mode runs
602
- * `_drain` from its own interval, so the retry timer is a no-op there.
603
- * @returns {Promise<void>}
604
- */
605
- async _drain() {
606
- if (!this._startDrain())
607
- return;
608
- const errored = await this._drainUntilIdle();
609
- await this._finishDrain({ errored });
610
- }
611
- /**
612
- * Runs start drain.
613
- * @returns {boolean} - Whether the drain should continue.
614
- */
615
- _startDrain() {
616
- if (this._stopped)
617
- return false;
618
- if (this._queueDrainIfAlreadyRunning())
619
- return false;
620
- this._draining = true;
621
- return true;
622
- }
623
- /**
624
- * Runs finish drain.
625
- * @param {object} args - Options.
626
- * @param {boolean} args.errored - Whether the drain hit an error.
627
- * @returns {Promise<void>} - Resolves after follow-up timers are handled.
628
- */
629
- async _finishDrain({ errored }) {
630
- if (this._stopped)
631
- return;
632
- if (errored)
633
- return this._scheduleErrorRetry();
634
- await this._armScheduledTimerOrRetry();
635
- }
636
- /**
637
- * Runs arm scheduled timer or retry.
638
- * @returns {Promise<void>} - Resolves after scheduled timer handling.
639
- */
640
- async _armScheduledTimerOrRetry() {
641
- try {
642
- await this._armScheduledTimer();
643
- }
644
- catch (error) {
645
- this.logger.error(() => ["Background jobs scheduled-timer arming failed:", error]);
646
- this._scheduleErrorRetry();
647
- return;
648
- }
649
- this._clearErrorRetryTimer();
650
- }
651
- /**
652
- * Runs clear error retry timer.
653
- @returns {void} */
654
- _clearErrorRetryTimer() {
655
- if (this._errorRetryTimer) {
656
- clearTimeout(this._errorRetryTimer);
657
- this._errorRetryTimer = undefined;
658
- }
659
- }
660
- /**
661
- * Runs queue drain if already running.
662
- * @returns {boolean} - Whether another drain is already in progress.
663
- */
664
- _queueDrainIfAlreadyRunning() {
665
- if (!this._draining)
666
- return false;
667
- this._redrainQueued = true;
668
- return true;
669
- }
670
- /**
671
- * Runs drain until idle.
672
- * @returns {Promise<boolean>} - Whether the drain hit an error.
673
- */
674
- async _drainUntilIdle() {
675
- try {
676
- return await this._runDrainLoop();
677
- }
678
- finally {
679
- this._draining = false;
680
- }
681
- }
682
- /**
683
- * Runs run drain loop.
684
- * @returns {Promise<boolean>} - Whether the drain hit an error.
685
- */
686
- async _runDrainLoop() {
687
- do {
688
- this._redrainQueued = false;
689
- const errored = await this._drainOnceWithErrorReport();
690
- if (errored)
691
- return true;
692
- } while (this._redrainQueued && !this._stopped);
693
- return false;
694
- }
695
- /**
696
- * Runs drain once with error report.
697
- * @returns {Promise<boolean>} - Whether one drain pass failed.
698
- */
699
- async _drainOnceWithErrorReport() {
700
- try {
701
- await this._drainOnce();
702
- return false;
703
- }
704
- catch (error) {
705
- this.logger.error(() => ["Background jobs drain failed:", error]);
706
- return true;
707
- }
708
- }
709
- /**
710
- * Arms a one-shot `setTimeout` to retry `_drain` after a transient
711
- * failure. Idempotent — repeated calls while a retry is already
712
- * pending are no-ops. Polling mode already retries via its own
713
- * interval, so this is a no-op in that mode.
714
- * @returns {void}
715
- */
716
- _scheduleErrorRetry() {
717
- if (this._stopped)
718
- return;
719
- if (this._errorRetryTimer)
720
- return;
721
- if (this.dispatchStrategy === "polling")
722
- return;
723
- this._errorRetryTimer = setTimeout(() => {
724
- this._errorRetryTimer = undefined;
725
- void this._drain();
726
- }, this.pollIntervalMs);
727
- }
728
- /**
729
- * Inner drain loop: pulls eligible queued jobs and hands them off to
730
- * ready workers until one of them runs out.
731
- * @returns {Promise<void>}
732
- */
733
- async _drainOnce() {
734
- while (this.readyWorkers.size > 0 && !this._stopped) {
735
- const job = await this.nextAvailableJobForReadyWorkers();
736
- if (!job)
737
- return;
738
- const worker = this.readyWorkerForJob(job);
739
- if (!worker)
740
- return;
741
- this.readyWorkers.delete(worker);
742
- const handedOffAtMs = await this.store.markHandedOff({ jobId: job.id, workerId: worker.workerId });
743
- try {
744
- worker.send({
745
- type: "job",
746
- payload: {
747
- id: job.id,
748
- jobName: job.jobName,
749
- args: job.args,
750
- workerId: worker.workerId,
751
- handedOffAtMs,
752
- options: {
753
- executionMode: job.executionMode,
754
- forked: job.forked
755
- }
756
- }
757
- });
758
- }
759
- catch (error) {
760
- this.logger.warn(() => ["Failed to send job to worker, re-queueing:", error]);
761
- await this.store.markReturnedToQueue({ jobId: job.id });
762
- this.readyWorkers.add(worker);
763
- }
764
- }
765
- }
766
- /**
767
- * Runs next available job for ready workers.
768
- * @returns {Promise<import("./types.js").BackgroundJobRow | null>} - Next queued job matching ready worker capacity.
769
- */
770
- async nextAvailableJobForReadyWorkers() {
771
- const executionModes = this.readyWorkerExecutionModes();
772
- if (executionModes.length === 0)
773
- return null;
774
- if (executionModes.length === 3)
775
- return await this.store.nextAvailableJob();
776
- return await this.store.nextAvailableJob({ executionMode: executionModes });
777
- }
778
- /**
779
- * Runs ready worker execution modes.
780
- * @returns {import("./types.js").BackgroundJobExecutionMode[]} - Execution modes currently accepted by ready workers.
781
- */
782
- readyWorkerExecutionModes() {
783
- const executionModes = new Set();
784
- for (const worker of this.readyWorkers) {
785
- this._addAcceptedExecutionModes({ executionModes, worker });
786
- }
787
- return /** Narrows the runtime value to the documented type. @type {import("./types.js").BackgroundJobExecutionMode[]} */ ([...executionModes]);
788
- }
789
- /**
790
- * Runs add accepted execution modes.
791
- * @param {object} args - Options.
792
- * @param {Set<import("./types.js").BackgroundJobExecutionMode>} args.executionModes - Accepted modes.
793
- * @param {JsonSocket} args.worker - Worker socket.
794
- * @returns {void}
795
- */
796
- _addAcceptedExecutionModes({ executionModes, worker }) {
797
- for (const capability of WORKER_EXECUTION_MODE_CAPABILITIES) {
798
- if (capability.accepts(worker))
799
- executionModes.add(capability.executionMode);
800
- }
801
- }
802
- /**
803
- * Runs ready worker for job.
804
- * @param {import("./types.js").BackgroundJobRow} job - Job being handed off.
805
- * @returns {JsonSocket | undefined} - Ready worker for the job type.
806
- */
807
- readyWorkerForJob(job) {
808
- for (const worker of this.readyWorkers) {
809
- if (this._workerAcceptsJob({ job, worker }))
810
- return worker;
811
- }
812
- }
813
- /**
814
- * Runs worker accepts job.
815
- * @param {object} args - Options.
816
- * @param {import("./types.js").BackgroundJobRow} args.job - Job being handed off.
817
- * @param {JsonSocket} args.worker - Worker socket.
818
- * @returns {boolean} - Whether the worker accepts the job mode.
819
- */
820
- _workerAcceptsJob({ job, worker }) {
821
- const capability = WORKER_EXECUTION_MODE_CAPABILITIES_BY_MODE.get(job.executionMode);
822
- if (!capability)
823
- return false;
824
- return capability.accepts(worker);
825
- }
826
- /**
827
- * Arms a single `setTimeout` for the soonest future-scheduled job's
828
- * `scheduled_at_ms`. Replaces the second responsibility of the legacy
829
- * 1-second poll (becoming-eligible scheduled jobs). The timer is
830
- * idempotently re-armed at the end of every drain.
831
- * @returns {Promise<void>}
832
- */
833
- async _armScheduledTimer() {
834
- if (this._scheduledTimer) {
835
- clearTimeout(this._scheduledTimer);
836
- this._scheduledTimer = undefined;
837
- }
838
- if (this._stopped)
839
- return;
840
- if (this.dispatchStrategy === "polling")
841
- return;
842
- const next = await this.store.nextScheduledJob();
843
- if (!next || typeof next.scheduledAtMs !== "number")
844
- return;
845
- const delay = Math.max(0, Math.min(next.scheduledAtMs - Date.now(), MAX_TIMER_MS));
846
- this._scheduledTimer = setTimeout(() => {
847
- this._scheduledTimer = undefined;
848
- void this._drain();
849
- }, delay);
850
- }
851
- async _sweepOrphans() {
852
- try {
853
- const count = await this.store.markOrphanedJobs();
854
- if (count > 0) {
855
- this.logger.warn(() => ["Marked orphaned background jobs", count]);
856
- // Reclaimed orphans become `queued` again — wake the dispatcher
857
- // so they aren't stranded until the next external signal.
858
- this._notifyEnqueued();
859
- await this._drain();
43
+ /**
44
+ * Runs constructor.
45
+ * @param {object} args - Options.
46
+ * @param {import("../configuration.js").default} args.configuration - Configuration.
47
+ * @param {string} [args.host] - Hostname.
48
+ * @param {number} [args.port] - Port.
49
+ */
50
+ constructor({configuration, host, port}) {
51
+ this.configuration = configuration
52
+ const config = configuration.getBackgroundJobsConfig()
53
+ this.host = host || config.host
54
+ this.port = typeof port === "number" ? port : config.port
55
+ this.dispatchStrategy = config.dispatchStrategy
56
+ this.pollIntervalMs = config.pollIntervalMs
57
+ this.store = new BackgroundJobsStore({configuration, databaseIdentifier: config.databaseIdentifier})
58
+ this.logger = new Logger(this)
59
+ /**
60
+ * Narrows the runtime value to the documented type.
61
+ @type {Set<JsonSocket>} */
62
+ this.workers = new Set()
63
+ /**
64
+ * Narrows the runtime value to the documented type.
65
+ @type {Set<JsonSocket>} */
66
+ this.readyWorkers = new Set()
67
+ /**
68
+ * Narrows the runtime value to the documented type.
69
+ @type {net.Server | undefined} */
70
+ this.server = undefined
71
+ /**
72
+ * Narrows the runtime value to the documented type.
73
+ @type {NodeJS.Timeout | undefined} */
74
+ this._pollTimer = undefined
75
+ /**
76
+ * Narrows the runtime value to the documented type.
77
+ @type {NodeJS.Timeout | undefined} */
78
+ this._scheduledTimer = undefined
79
+ /**
80
+ * Narrows the runtime value to the documented type.
81
+ @type {NodeJS.Timeout | undefined} */
82
+ this._errorRetryTimer = undefined
83
+ /**
84
+ * Narrows the runtime value to the documented type.
85
+ @type {NodeJS.Timeout | undefined} */
86
+ this._orphanTimer = undefined
87
+ /**
88
+ * Narrows the runtime value to the documented type.
89
+ @type {BackgroundJobsScheduler | undefined} */
90
+ this.scheduler = undefined
91
+ this._draining = false
92
+ this._redrainQueued = false
93
+ this._stopped = false
94
+ /**
95
+ * Narrows the runtime value to the documented type.
96
+ @type {(() => void) | undefined} */
97
+ this._unsubscribeBeacon = undefined
98
+ /**
99
+ * Narrows the runtime value to the documented type.
100
+ @type {((...args: Array<?>) => void) | undefined} */
101
+ this._beaconConnectHandler = undefined
102
+ /**
103
+ * Narrows the runtime value to the documented type.
104
+ @type {import("../beacon/client.js").default | import("../beacon/in-process-client.js").default | undefined} */
105
+ this._beaconClient = undefined
106
+ }
107
+
108
+ /**
109
+ * Runs start.
110
+ * @returns {Promise<void>} - Resolves when listening.
111
+ */
112
+ async start() {
113
+ this._stopped = false
114
+ this.configuration.setCurrent()
115
+ await this.configuration.initialize({type: "background-jobs-main"})
116
+ await this.configuration.connectBeacon({peerType: "background-jobs-main"})
117
+ await this.store.ensureReady()
118
+ const server = net.createServer((socket) => this._handleConnection(socket))
119
+ this.server = server
120
+
121
+ try {
122
+ await new Promise((resolve, reject) => {
123
+ server.once("error", reject)
124
+ server.listen(this.port, this.host, () => resolve(undefined))
125
+ })
126
+
127
+ const address = server.address()
128
+ if (address && typeof address === "object") {
129
+ this.port = address.port
130
+ }
131
+
132
+ this._setupDispatchTriggers()
133
+
134
+ this._orphanTimer = setInterval(() => {
135
+ void this._sweepOrphans()
136
+ }, 60000)
137
+
138
+ this.scheduler = new BackgroundJobsScheduler({
139
+ configuration: this.configuration,
140
+ enqueueJob: async ({args, jobClass, options}) => {
141
+ await this.store.enqueue({
142
+ jobName: jobClass.jobName(),
143
+ args,
144
+ options
145
+ })
146
+ this._notifyEnqueued()
147
+ await this._drain()
148
+ }
149
+ })
150
+ await this.scheduler.start()
151
+
152
+ // Startup catch-up: drain anything that was waiting before this
153
+ // process came up. In beacon mode this is also the safety net for
154
+ // races between attaching the connect listener and the initial
155
+ // connect firing (the listener could miss the very first connect,
156
+ // but this drain covers it).
157
+ await this._drain()
158
+ } catch (error) {
159
+ await this.stop()
160
+ throw error
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Runs stop.
166
+ * @returns {Promise<void>} - Resolves when closed.
167
+ */
168
+ async stop() {
169
+ this._stopped = true
170
+
171
+ this._closeWorkers()
172
+ this._clearTimers()
173
+ this._disconnectBeaconHandlers()
174
+ this.scheduler?.stop()
175
+
176
+ await this._stopBeaconAndServer()
177
+ }
178
+
179
+ /**
180
+ * Runs close workers.
181
+ @returns {void} */
182
+ _closeWorkers() {
183
+ for (const worker of this.workers) {
184
+ worker.close()
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Runs clear timers.
190
+ @returns {void} */
191
+ _clearTimers() {
192
+ if (this._pollTimer) clearInterval(this._pollTimer)
193
+ if (this._scheduledTimer) clearTimeout(this._scheduledTimer)
194
+ if (this._errorRetryTimer) clearTimeout(this._errorRetryTimer)
195
+ if (this._orphanTimer) clearInterval(this._orphanTimer)
196
+ this._pollTimer = undefined
197
+ this._scheduledTimer = undefined
198
+ this._errorRetryTimer = undefined
199
+ this._orphanTimer = undefined
200
+ }
201
+
202
+ /**
203
+ * Runs disconnect beacon handlers.
204
+ @returns {void} */
205
+ _disconnectBeaconHandlers() {
206
+ if (this._unsubscribeBeacon) {
207
+ this._unsubscribeBeacon()
208
+ this._unsubscribeBeacon = undefined
209
+ }
210
+
211
+ if (this._beaconClient && this._beaconConnectHandler) {
212
+ this._beaconClient.off("connect", this._beaconConnectHandler)
213
+ }
214
+ this._beaconConnectHandler = undefined
215
+ this._beaconClient = undefined
216
+ }
217
+
218
+ /**
219
+ * Runs stop beacon and server.
220
+ @returns {Promise<void>} */
221
+ async _stopBeaconAndServer() {
222
+ try {
223
+ await this.configuration.disconnectBeacon()
224
+ } finally {
225
+ await this._closeServerAndDatabaseConnections()
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Runs close server and database connections.
231
+ @returns {Promise<void>} */
232
+ async _closeServerAndDatabaseConnections() {
233
+ try {
234
+ await this._closeServer()
235
+ } finally {
236
+ await this.configuration.closeDatabaseConnections()
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Runs close server.
242
+ @returns {Promise<void>} */
243
+ async _closeServer() {
244
+ if (!this.server) return
245
+
246
+ const {server} = this
247
+ this.server = undefined
248
+ await new Promise((resolve) => server.close(() => resolve(undefined)))
249
+ }
250
+
251
+ /**
252
+ * Runs get port.
253
+ * @returns {number} - Bound port.
254
+ */
255
+ getPort() {
256
+ return this.port
257
+ }
258
+
259
+ /**
260
+ * Wires up the dispatch-triggering signal sources for the configured
261
+ * strategy. In `"beacon"` mode (default) this means subscribing to the
262
+ * `velocious-background-jobs-dispatch` channel for cross-process
263
+ * wake-ups, listening for Beacon (re)connects to catch up on missed
264
+ * work, and relying on direct in-process calls from `_handleEnqueue`,
265
+ * `_handleJobComplete`/`Failed`, worker hello/ready, and the
266
+ * scheduled-job `setTimeout`. In `"polling"` mode we restore the
267
+ * legacy fixed-interval poll for users who want the previous behavior.
268
+ * @returns {void}
269
+ */
270
+ _setupDispatchTriggers() {
271
+ if (this.dispatchStrategy === "polling") {
272
+ this._pollTimer = setInterval(() => {
273
+ void this._drain()
274
+ }, this.pollIntervalMs)
275
+ return
276
+ }
277
+
278
+ const beaconClient = this.configuration.getBeaconClient()
279
+ if (!beaconClient) return
280
+
281
+ this._beaconClient = beaconClient
282
+
283
+ this._unsubscribeBeacon = beaconClient.onBroadcast((message) => {
284
+ if (message?.channel !== DISPATCH_CHANNEL) return
285
+ void this._drain()
286
+ })
287
+
288
+ // Drain on every (re)connect to catch up on jobs enqueued while the
289
+ // bus was unreachable. The DB is the durable log; Beacon is just the
290
+ // wake-up signal.
291
+ this._beaconConnectHandler = () => {
292
+ void this._drain()
293
+ }
294
+ beaconClient.on("connect", this._beaconConnectHandler)
295
+ }
296
+
297
+ /**
298
+ * Publishes a dispatch wake-up on the Beacon channel. No-op in polling
299
+ * mode or when Beacon is not connected; in those cases the direct
300
+ * in-process `_drain()` call in the enqueue/handle paths is sufficient
301
+ * (there are no other processes to notify).
302
+ * @returns {void}
303
+ */
304
+ _notifyEnqueued() {
305
+ if (this.dispatchStrategy === "polling") return
306
+
307
+ const beaconClient = this.configuration.getBeaconClient()
308
+ if (!beaconClient || !beaconClient.isConnected()) return
309
+
310
+ try {
311
+ beaconClient.publish({
312
+ channel: DISPATCH_CHANNEL,
313
+ broadcastParams: {},
314
+ body: {action: "wake"}
315
+ })
316
+ } catch (error) {
317
+ this.logger.warn(() => ["Failed to publish background jobs wake broadcast:", error])
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Runs handle connection.
323
+ * @param {import("net").Socket} socket - Socket.
324
+ * @returns {void}
325
+ */
326
+ _handleConnection(socket) {
327
+ const jsonSocket = new JsonSocket(socket)
328
+ /**
329
+ * Role.
330
+ @type {import("./types.js").BackgroundJobSocketRole | null} */
331
+ let role = null
332
+
333
+ const cleanup = () => {
334
+ if (role === "worker") {
335
+ this.workers.delete(jsonSocket)
336
+ this.readyWorkers.delete(jsonSocket)
337
+ }
338
+ }
339
+
340
+ jsonSocket.on("close", cleanup)
341
+ jsonSocket.on("error", (error) => {
342
+ this.logger.warn(() => ["Background jobs connection error:", error])
343
+ cleanup()
344
+ })
345
+
346
+ jsonSocket.on("message", (message) => {
347
+ role = this._handleSocketMessage({jsonSocket, message, role})
348
+ })
349
+ }
350
+
351
+ /**
352
+ * Runs handle socket message.
353
+ * @param {object} args - Options.
354
+ * @param {JsonSocket} args.jsonSocket - JSON socket.
355
+ * @param {import("./types.js").BackgroundJobSocketMessage} args.message - Socket message.
356
+ * @param {import("./types.js").BackgroundJobSocketRole | null} args.role - Current socket role.
357
+ * @returns {import("./types.js").BackgroundJobSocketRole | null} - Updated socket role.
358
+ */
359
+ _handleSocketMessage({jsonSocket, message, role}) {
360
+ if (!role) return this._handleRolelessSocketMessage({jsonSocket, message})
361
+ if (role === "client") this._handleClientSocketMessage({jsonSocket, message})
362
+ if (role === "worker") this._handleWorkerSocketMessage({jsonSocket, message})
363
+ if (role === "reporter") this._handleReporterSocketMessage({jsonSocket, message})
364
+
365
+ return role
366
+ }
367
+
368
+ /**
369
+ * Runs handle roleless socket message.
370
+ * @param {object} args - Options.
371
+ * @param {JsonSocket} args.jsonSocket - JSON socket.
372
+ * @param {import("./types.js").BackgroundJobSocketMessage} args.message - Socket message.
373
+ * @returns {import("./types.js").BackgroundJobSocketRole | null} - New socket role.
374
+ */
375
+ _handleRolelessSocketMessage({jsonSocket, message}) {
376
+ if (message?.type !== "hello") return null
377
+
378
+ if (message.role === "worker") {
379
+ jsonSocket.workerId = message.workerId
380
+ this.workers.add(jsonSocket)
381
+ }
382
+
383
+ return message.role
384
+ }
385
+
386
+ /**
387
+ * Runs handle client socket message.
388
+ * @param {object} args - Options.
389
+ * @param {JsonSocket} args.jsonSocket - JSON socket.
390
+ * @param {import("./types.js").BackgroundJobSocketMessage} args.message - Socket message.
391
+ * @returns {void}
392
+ */
393
+ _handleClientSocketMessage({jsonSocket, message}) {
394
+ if (message?.type === "enqueue") {
395
+ this._handleEnqueue({jsonSocket, message})
396
+ }
397
+ }
398
+
399
+ /**
400
+ * Runs handle worker socket message.
401
+ * @param {object} args - Options.
402
+ * @param {JsonSocket} args.jsonSocket - JSON socket.
403
+ * @param {import("./types.js").BackgroundJobSocketMessage} args.message - Socket message.
404
+ * @returns {void}
405
+ */
406
+ _handleWorkerSocketMessage({jsonSocket, message}) {
407
+ if (message?.type === "ready") {
408
+ this._handleWorkerReady({jsonSocket, message})
409
+ return
410
+ }
411
+
412
+ if (message?.type === "draining") {
413
+ this._handleWorkerDraining({jsonSocket})
414
+ return
415
+ }
416
+
417
+ this._handleReporterSocketMessage({jsonSocket, message})
418
+ }
419
+
420
+ /**
421
+ * Runs handle reporter socket message.
422
+ * @param {object} args - Options.
423
+ * @param {JsonSocket} args.jsonSocket - JSON socket.
424
+ * @param {import("./types.js").BackgroundJobSocketMessage} args.message - Socket message.
425
+ * @returns {void}
426
+ */
427
+ _handleReporterSocketMessage({jsonSocket, message}) {
428
+ if (message?.type === "job-complete") {
429
+ this._handleJobComplete({jsonSocket, message})
430
+ return
431
+ }
432
+
433
+ if (message?.type === "job-failed") {
434
+ this._handleJobFailed({jsonSocket, message})
435
+ }
436
+ }
437
+
438
+ /**
439
+ * Runs handle worker ready.
440
+ * @param {object} args - Options.
441
+ * @param {JsonSocket} args.jsonSocket - JSON socket.
442
+ * @param {import("./types.js").BackgroundJobReadyMessage} args.message - Ready message.
443
+ * @returns {void}
444
+ */
445
+ _handleWorkerReady({jsonSocket, message}) {
446
+ jsonSocket.acceptsSpawnedJobs = message.acceptsSpawned !== false && message.acceptsForked !== false
447
+ jsonSocket.acceptsForkedJobs = message.acceptsForked !== false
448
+ jsonSocket.acceptsInlineJobs = message.acceptsInline !== false
449
+ this.readyWorkers.add(jsonSocket)
450
+ void this._drain()
451
+ }
452
+
453
+ /**
454
+ * Runs handle worker draining.
455
+ * @param {object} args - Options.
456
+ * @param {JsonSocket} args.jsonSocket - JSON socket.
457
+ * @returns {void}
458
+ */
459
+ _handleWorkerDraining({jsonSocket}) {
460
+ // The worker is shutting down gracefully. Stop dispatching new jobs
461
+ // to it but keep the connection in `workers` so any in-flight job
462
+ // it's still draining can report its result.
463
+ this.readyWorkers.delete(jsonSocket)
464
+ }
465
+
466
+ /**
467
+ * Runs handle enqueue.
468
+ * @param {object} args - Options.
469
+ * @param {JsonSocket} args.jsonSocket - JSON socket.
470
+ * @param {import("./types.js").BackgroundJobEnqueueMessage} args.message - Message.
471
+ * @returns {Promise<void>} - Resolves when handled.
472
+ */
473
+ async _handleEnqueue({jsonSocket, message}) {
474
+ try {
475
+ const jobId = await this.store.enqueue({
476
+ jobName: message.jobName,
477
+ args: message.args || [],
478
+ options: message.options || {}
479
+ })
480
+
481
+ jsonSocket.send({type: "enqueued", jobId})
482
+ this._notifyEnqueued()
483
+ await this._drain()
484
+ } catch (error) {
485
+ this.logger.error(() => ["Failed to enqueue background job:", error])
486
+ jsonSocket.send({type: "enqueue-error", error: "Failed to enqueue job"})
487
+ }
488
+ }
489
+
490
+ /**
491
+ * Runs handle job complete.
492
+ * @param {object} args - Options.
493
+ * @param {JsonSocket} args.jsonSocket - JSON socket.
494
+ * @param {import("./types.js").BackgroundJobCompleteMessage} args.message - Message.
495
+ * @returns {Promise<void>} - Resolves when handled.
496
+ */
497
+ async _handleJobComplete({jsonSocket, message}) {
498
+ try {
499
+ await this.store.markCompleted({
500
+ jobId: message.jobId,
501
+ workerId: message.workerId,
502
+ handedOffAtMs: message.handedOffAtMs
503
+ })
504
+ jsonSocket.send({type: "job-updated", jobId: message.jobId})
505
+ } catch (error) {
506
+ this.logger.error(() => ["Failed to update job completion:", error])
507
+ jsonSocket.send({type: "job-update-error", jobId: message.jobId, error: "Failed to update job"})
508
+ }
509
+ }
510
+
511
+ /**
512
+ * Runs handle job failed.
513
+ * @param {object} args - Options.
514
+ * @param {JsonSocket} args.jsonSocket - JSON socket.
515
+ * @param {import("./types.js").BackgroundJobFailedMessage} args.message - Message.
516
+ * @returns {Promise<void>} - Resolves when handled.
517
+ */
518
+ async _handleJobFailed({jsonSocket, message}) {
519
+ try {
520
+ const failedJob = await this.store.markFailed({
521
+ jobId: message.jobId,
522
+ error: message.error,
523
+ workerId: message.workerId,
524
+ handedOffAtMs: message.handedOffAtMs
525
+ })
526
+
527
+ if (failedJob) {
528
+ this._emitBackgroundJobFailed({
529
+ error: message.error,
530
+ handedOffAtMs: message.handedOffAtMs,
531
+ job: failedJob,
532
+ workerId: message.workerId
533
+ })
534
+ }
535
+
536
+ jsonSocket.send({type: "job-updated", jobId: message.jobId})
537
+ // A failed job may have been re-queued (with backoff) for retry —
538
+ // poke the dispatcher so the retry timer is armed.
539
+ this._notifyEnqueued()
540
+ await this._drain()
541
+ } catch (error) {
542
+ this.logger.error(() => ["Failed to update job failure:", error])
543
+ jsonSocket.send({type: "job-update-error", jobId: message.jobId, error: "Failed to update job"})
544
+ }
545
+ }
546
+
547
+ /**
548
+ * Runs emit background job failed.
549
+ * @param {{error: ?, handedOffAtMs?: number, job: import("./types.js").BackgroundJobRow, workerId?: string}} args - Failure event data.
550
+ * @returns {void}
551
+ */
552
+ _emitBackgroundJobFailed({error, handedOffAtMs, job, workerId}) {
553
+ const normalizedError = this._normalizeFailureError(error)
554
+ const payload = {
555
+ context: {
556
+ attempts: job.attempts,
557
+ handedOffAtMs,
558
+ jobArgs: job.args,
559
+ jobId: job.id,
560
+ jobName: job.jobName,
561
+ maxRetries: job.maxRetries,
562
+ stage: "background-job-failed",
563
+ status: job.status,
564
+ terminal: job.status === "failed" || job.status === "orphaned",
565
+ willRetry: job.status === "queued",
566
+ workerId
567
+ },
568
+ error: normalizedError
569
+ }
570
+ const errorEvents = this.configuration.getErrorEvents()
571
+
572
+ errorEvents.emit("background-job-failed", payload)
573
+ errorEvents.emit("all-error", {...payload, errorType: "background-job-failed"})
574
+ }
575
+
576
+ /**
577
+ * Runs normalize failure error.
578
+ * @param {?} error - Reported failure value.
579
+ * @returns {Error} Normalized error.
580
+ */
581
+ _normalizeFailureError(error) {
582
+ if (error instanceof Error) return error
583
+
584
+ return this._errorFromUnknownFailure(error)
585
+ }
586
+
587
+ /**
588
+ * Runs error from unknown failure.
589
+ * @param {?} error - Reported failure value.
590
+ * @returns {Error} Normalized error.
591
+ */
592
+ _errorFromUnknownFailure(error) {
593
+ const message = this._messageFromUnknownFailure(error)
594
+ const normalizedError = new Error(message)
595
+
596
+ this._copyStringFailureStack({error, normalizedError})
597
+
598
+ return normalizedError
599
+ }
600
+
601
+ /**
602
+ * Runs message from unknown failure.
603
+ * @param {?} error - Reported failure value.
604
+ * @returns {string} Error message.
605
+ */
606
+ _messageFromUnknownFailure(error) {
607
+ if (this._hasStringFailure(error)) return error.trim().split("\n")[0]
608
+
609
+ return String(error || "Background job failed")
610
+ }
611
+
612
+ /**
613
+ * Runs has string failure.
614
+ * @param {?} error - Reported failure value.
615
+ * @returns {error is string} Whether the value is a non-empty string.
616
+ */
617
+ _hasStringFailure(error) {
618
+ return typeof error === "string" && error.trim().length > 0
619
+ }
620
+
621
+ /**
622
+ * Runs copy string failure stack.
623
+ * @param {object} args - Options.
624
+ * @param {?} args.error - Reported failure value.
625
+ * @param {Error} args.normalizedError - Normalized error.
626
+ * @returns {void}
627
+ */
628
+ _copyStringFailureStack({error, normalizedError}) {
629
+ if (this._hasStringFailure(error)) normalizedError.stack = error
630
+ }
631
+
632
+ /**
633
+ * Drains all dispatchable jobs to ready workers, then arms the
634
+ * scheduled-job timer for the next future `scheduled_at_ms`. Coalesces
635
+ * concurrent triggers: a wake-up that lands while a drain is in
636
+ * flight just sets a re-drain flag and lets the in-flight drain
637
+ * re-loop after it finishes, so no signal is dropped but no two
638
+ * drains run in parallel.
639
+ *
640
+ * Resilience: in beacon mode this is the sole wake-up path for
641
+ * already-queued work, so a transient DB error during the drain (e.g.
642
+ * `nextAvailableJob()` rejecting) must not strand the queue until the
643
+ * next external signal. On any error we log it and arm a one-shot
644
+ * retry via `_scheduleErrorRetry` using `pollIntervalMs` as the
645
+ * cadence; on success the retry timer is cleared. Polling-mode runs
646
+ * `_drain` from its own interval, so the retry timer is a no-op there.
647
+ * @returns {Promise<void>}
648
+ */
649
+ async _drain() {
650
+ if (!this._startDrain()) return
651
+
652
+ const errored = await this._drainUntilIdle()
653
+
654
+ await this._finishDrain({errored})
655
+ }
656
+
657
+ /**
658
+ * Runs start drain.
659
+ * @returns {boolean} - Whether the drain should continue.
660
+ */
661
+ _startDrain() {
662
+ if (this._stopped) return false
663
+ if (this._queueDrainIfAlreadyRunning()) return false
664
+
665
+ this._draining = true
666
+ return true
667
+ }
668
+
669
+ /**
670
+ * Runs finish drain.
671
+ * @param {object} args - Options.
672
+ * @param {boolean} args.errored - Whether the drain hit an error.
673
+ * @returns {Promise<void>} - Resolves after follow-up timers are handled.
674
+ */
675
+ async _finishDrain({errored}) {
676
+ if (this._stopped) return
677
+ if (errored) return this._scheduleErrorRetry()
678
+
679
+ await this._armScheduledTimerOrRetry()
680
+ }
681
+
682
+ /**
683
+ * Runs arm scheduled timer or retry.
684
+ * @returns {Promise<void>} - Resolves after scheduled timer handling.
685
+ */
686
+ async _armScheduledTimerOrRetry() {
687
+ try {
688
+ await this._armScheduledTimer()
689
+ } catch (error) {
690
+ this.logger.error(() => ["Background jobs scheduled-timer arming failed:", error])
691
+ this._scheduleErrorRetry()
692
+ return
693
+ }
694
+
695
+ this._clearErrorRetryTimer()
696
+ }
697
+
698
+ /**
699
+ * Runs clear error retry timer.
700
+ @returns {void} */
701
+ _clearErrorRetryTimer() {
702
+ if (this._errorRetryTimer) {
703
+ clearTimeout(this._errorRetryTimer)
704
+ this._errorRetryTimer = undefined
705
+ }
706
+ }
707
+
708
+ /**
709
+ * Runs queue drain if already running.
710
+ * @returns {boolean} - Whether another drain is already in progress.
711
+ */
712
+ _queueDrainIfAlreadyRunning() {
713
+ if (!this._draining) return false
714
+
715
+ this._redrainQueued = true
716
+ return true
717
+ }
718
+
719
+ /**
720
+ * Runs drain until idle.
721
+ * @returns {Promise<boolean>} - Whether the drain hit an error.
722
+ */
723
+ async _drainUntilIdle() {
724
+ try {
725
+ return await this._runDrainLoop()
726
+ } finally {
727
+ this._draining = false
728
+ }
729
+ }
730
+
731
+ /**
732
+ * Runs run drain loop.
733
+ * @returns {Promise<boolean>} - Whether the drain hit an error.
734
+ */
735
+ async _runDrainLoop() {
736
+ do {
737
+ this._redrainQueued = false
738
+ const errored = await this._drainOnceWithErrorReport()
739
+
740
+ if (errored) return true
741
+ } while (this._redrainQueued && !this._stopped)
742
+
743
+ return false
744
+ }
745
+
746
+ /**
747
+ * Runs drain once with error report.
748
+ * @returns {Promise<boolean>} - Whether one drain pass failed.
749
+ */
750
+ async _drainOnceWithErrorReport() {
751
+ try {
752
+ await this._drainOnce()
753
+ return false
754
+ } catch (error) {
755
+ this.logger.error(() => ["Background jobs drain failed:", error])
756
+ return true
757
+ }
758
+ }
759
+
760
+ /**
761
+ * Arms a one-shot `setTimeout` to retry `_drain` after a transient
762
+ * failure. Idempotent — repeated calls while a retry is already
763
+ * pending are no-ops. Polling mode already retries via its own
764
+ * interval, so this is a no-op in that mode.
765
+ * @returns {void}
766
+ */
767
+ _scheduleErrorRetry() {
768
+ if (this._stopped) return
769
+ if (this._errorRetryTimer) return
770
+ if (this.dispatchStrategy === "polling") return
771
+
772
+ this._errorRetryTimer = setTimeout(() => {
773
+ this._errorRetryTimer = undefined
774
+ void this._drain()
775
+ }, this.pollIntervalMs)
776
+ }
777
+
778
+ /**
779
+ * Inner drain loop: pulls eligible queued jobs and hands them off to
780
+ * ready workers until one of them runs out.
781
+ * @returns {Promise<void>}
782
+ */
783
+ async _drainOnce() {
784
+ while (this.readyWorkers.size > 0 && !this._stopped) {
785
+ const job = await this.nextAvailableJobForReadyWorkers()
786
+ if (!job) return
787
+
788
+ const worker = this.readyWorkerForJob(job)
789
+ if (!worker) return
790
+
791
+ this.readyWorkers.delete(worker)
792
+
793
+ const handedOffAtMs = await this.store.markHandedOff({jobId: job.id, workerId: worker.workerId})
794
+
795
+ try {
796
+ worker.send({
797
+ type: "job",
798
+ payload: {
799
+ id: job.id,
800
+ jobName: job.jobName,
801
+ args: job.args,
802
+ workerId: worker.workerId,
803
+ handedOffAtMs,
804
+ options: {
805
+ executionMode: job.executionMode,
806
+ forked: job.forked
860
807
  }
861
- }
862
- catch (error) {
863
- this.logger.error(() => ["Failed to mark orphaned jobs:", error]);
864
- }
865
- }
808
+ }
809
+ })
810
+ } catch (error) {
811
+ this.logger.warn(() => ["Failed to send job to worker, re-queueing:", error])
812
+ await this.store.markReturnedToQueue({jobId: job.id})
813
+ this.readyWorkers.add(worker)
814
+ }
815
+ }
816
+ }
817
+
818
+ /**
819
+ * Runs next available job for ready workers.
820
+ * @returns {Promise<import("./types.js").BackgroundJobRow | null>} - Next queued job matching ready worker capacity.
821
+ */
822
+ async nextAvailableJobForReadyWorkers() {
823
+ const executionModes = this.readyWorkerExecutionModes()
824
+
825
+ if (executionModes.length === 0) return null
826
+ if (executionModes.length === 3) return await this.store.nextAvailableJob()
827
+
828
+ return await this.store.nextAvailableJob({executionMode: executionModes})
829
+ }
830
+
831
+ /**
832
+ * Runs ready worker execution modes.
833
+ * @returns {import("./types.js").BackgroundJobExecutionMode[]} - Execution modes currently accepted by ready workers.
834
+ */
835
+ readyWorkerExecutionModes() {
836
+ const executionModes = new Set()
837
+
838
+ for (const worker of this.readyWorkers) {
839
+ this._addAcceptedExecutionModes({executionModes, worker})
840
+ }
841
+
842
+ return /** Narrows the runtime value to the documented type. @type {import("./types.js").BackgroundJobExecutionMode[]} */ ([...executionModes])
843
+ }
844
+
845
+ /**
846
+ * Runs add accepted execution modes.
847
+ * @param {object} args - Options.
848
+ * @param {Set<import("./types.js").BackgroundJobExecutionMode>} args.executionModes - Accepted modes.
849
+ * @param {JsonSocket} args.worker - Worker socket.
850
+ * @returns {void}
851
+ */
852
+ _addAcceptedExecutionModes({executionModes, worker}) {
853
+ for (const capability of WORKER_EXECUTION_MODE_CAPABILITIES) {
854
+ if (capability.accepts(worker)) executionModes.add(capability.executionMode)
855
+ }
856
+ }
857
+
858
+ /**
859
+ * Runs ready worker for job.
860
+ * @param {import("./types.js").BackgroundJobRow} job - Job being handed off.
861
+ * @returns {JsonSocket | undefined} - Ready worker for the job type.
862
+ */
863
+ readyWorkerForJob(job) {
864
+ for (const worker of this.readyWorkers) {
865
+ if (this._workerAcceptsJob({job, worker})) return worker
866
+ }
867
+ }
868
+
869
+ /**
870
+ * Runs worker accepts job.
871
+ * @param {object} args - Options.
872
+ * @param {import("./types.js").BackgroundJobRow} args.job - Job being handed off.
873
+ * @param {JsonSocket} args.worker - Worker socket.
874
+ * @returns {boolean} - Whether the worker accepts the job mode.
875
+ */
876
+ _workerAcceptsJob({job, worker}) {
877
+ const capability = WORKER_EXECUTION_MODE_CAPABILITIES_BY_MODE.get(job.executionMode)
878
+
879
+ if (!capability) return false
880
+
881
+ return capability.accepts(worker)
882
+ }
883
+
884
+ /**
885
+ * Arms a single `setTimeout` for the soonest future-scheduled job's
886
+ * `scheduled_at_ms`. Replaces the second responsibility of the legacy
887
+ * 1-second poll (becoming-eligible scheduled jobs). The timer is
888
+ * idempotently re-armed at the end of every drain.
889
+ * @returns {Promise<void>}
890
+ */
891
+ async _armScheduledTimer() {
892
+ if (this._scheduledTimer) {
893
+ clearTimeout(this._scheduledTimer)
894
+ this._scheduledTimer = undefined
895
+ }
896
+
897
+ if (this._stopped) return
898
+ if (this.dispatchStrategy === "polling") return
899
+
900
+ const next = await this.store.nextScheduledJob()
901
+ if (!next || typeof next.scheduledAtMs !== "number") return
902
+
903
+ const delay = Math.max(0, Math.min(next.scheduledAtMs - Date.now(), MAX_TIMER_MS))
904
+
905
+ this._scheduledTimer = setTimeout(() => {
906
+ this._scheduledTimer = undefined
907
+ void this._drain()
908
+ }, delay)
909
+ }
910
+
911
+ async _sweepOrphans() {
912
+ try {
913
+ const count = await this.store.markOrphanedJobs()
914
+
915
+ if (count > 0) {
916
+ this.logger.warn(() => ["Marked orphaned background jobs", count])
917
+ // Reclaimed orphans become `queued` again — wake the dispatcher
918
+ // so they aren't stranded until the next external signal.
919
+ this._notifyEnqueued()
920
+ await this._drain()
921
+ }
922
+ } catch (error) {
923
+ this.logger.error(() => ["Failed to mark orphaned jobs:", error])
924
+ }
925
+ }
866
926
  }
867
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9iYWNrZ3JvdW5kLWpvYnMvbWFpbi5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVosT0FBTyxHQUFHLE1BQU0sS0FBSyxDQUFBO0FBQ3JCLE9BQU8sVUFBVSxNQUFNLGtCQUFrQixDQUFBO0FBQ3pDLE9BQU8sdUJBQXVCLE1BQU0sZ0JBQWdCLENBQUE7QUFDcEQsT0FBTyxtQkFBbUIsTUFBTSxZQUFZLENBQUE7QUFDNUMsT0FBTyxNQUFNLE1BQU0sY0FBYyxDQUFBO0FBRWpDOzs7Ozs7R0FNRztBQUNILE1BQU0sZ0JBQWdCLEdBQUcsb0NBQW9DLENBQUE7QUFFN0Q7Ozs7R0FJRztBQUNILE1BQU0sWUFBWSxHQUFHLGFBQWEsQ0FBQSxDQUFDLGFBQWE7QUFDaEQ7Ozs7O0dBS0c7QUFDSDs7NENBRTRDO0FBQzVDLE1BQU0sa0NBQWtDLEdBQUc7SUFDekMsRUFBQyxhQUFhLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLGlCQUFpQixLQUFLLEtBQUssRUFBQztJQUNsRixFQUFDLGFBQWEsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsaUJBQWlCLEtBQUssS0FBSyxFQUFDO0lBQ2xGLEVBQUMsYUFBYSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxrQkFBa0IsS0FBSyxLQUFLLEVBQUM7Q0FDckYsQ0FBQTtBQUNELE1BQU0sMENBQTBDLEdBQUcsSUFBSSxHQUFHLENBQ3hELGtDQUFrQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQUMsQ0FBQyxVQUFVLENBQUMsYUFBYSxFQUFFLFVBQVUsQ0FBQyxDQUFDLENBQy9GLENBQUE7QUFFRCxNQUFNLENB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QUFFLENBQUMsQ0FBQyxpQ0FBaUMsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFBO2dCQUNsRSxnRUFBZ0U7Z0JBQ2hFLDBEQUEwRDtnQkFDMUQsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFBO2dCQUN0QixNQUFNLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQTtZQUNyQixDQUFDO1FBQ0gsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLCtCQUErQixFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUE7UUFDbkUsQ0FBQztJQUNILENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5pbXBvcnQgbmV0IGZyb20gXCJuZXRcIlxuaW1wb3J0IEpzb25Tb2NrZXQgZnJvbSBcIi4vanNvbi1zb2NrZXQuanNcIlxuaW1wb3J0IEJhY2tncm91bmRKb2JzU2NoZWR1bGVyIGZyb20gXCIuL3NjaGVkdWxlci5qc1wiXG5pbXBvcnQgQmFja2dyb3VuZEpvYnNTdG9yZSBmcm9tIFwiLi9zdG9yZS5qc1wiXG5pbXBvcnQgTG9nZ2VyIGZyb20gXCIuLi9sb2dnZXIuanNcIlxuXG4vKipcbiAqIENoYW5uZWwgdXNlZCBieSBgYmFja2dyb3VuZC1qb2JzLW1haW5gIHRvIGNvb3JkaW5hdGUgZGlzcGF0Y2ggd2FrZS11cHNcbiAqIGFjcm9zcyBwcm9jZXNzZXMgdmlhIEJlYWNvbi4gV29ya2VycyBkbyBOT1Qgc3Vic2NyaWJlIHRvIHRoaXMgY2hhbm5lbFxuICog4oCUIHRoZXkgYWxyZWFkeSByZWNlaXZlIGpvYi1oYW5kb2ZmIG1lc3NhZ2VzIG9uIHRoZWlyIEpzb25Tb2NrZXQgdG9cbiAqIG1haW47IHRoaXMgY2hhbm5lbCBleGlzdHMgc28gY3Jvc3MtcHJvY2VzcyBlbnF1ZXVlcyAob3IgZnV0dXJlXG4gKiBtdWx0aS1tYWluIGRlcGxveW1lbnRzKSBjYW4gcG9rZSBhbiBpZGxlIG1haW4gdG8gZHJhaW4uXG4gKi9cbmNvbnN0IERJU1BBVENIX0NIQU5ORUwgPSBcInZlbG9jaW91cy1iYWNrZ3JvdW5kLWpvYnMtZGlzcGF0Y2hcIlxuXG4vKipcbiAqIGBzZXRUaW1lb3V0YCBpcyBpbXBsZW1lbnRlZCB3aXRoIDMyLWJpdCBzaWduZWQgZGVsYXlzIG9uIE5vZGU7IHBhc3NpbmdcbiAqIGFueXRoaW5nIGxhcmdlciBzaWxlbnRseSBjbGFtcHMgdG8gMW1zIGFuZCBmaXJlcyBpbW1lZGlhdGVseS4gQ2FwIHRoZVxuICogc2NoZWR1bGVkLWpvYiB0aW1lciBoZXJlIGFuZCByZS1hcm0gd2hlbiBpdCBleHBpcmVzLlxuICovXG5jb25zdCBNQVhfVElNRVJfTVMgPSAyXzE0N180ODNfNjQ3IC8vIH4yNC44IGRheXNcbi8qKlxuICogV29ya2VyRXhlY3V0aW9uTW9kZUNhcGFiaWxpdHkgdHlwZS5cbiAqIEB0eXBlZGVmIHtvYmplY3R9IFdvcmtlckV4ZWN1dGlvbk1vZGVDYXBhYmlsaXR5XG4gKiBAcHJvcGVydHkge2ltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYkV4ZWN1dGlvbk1vZGV9IGV4ZWN1dGlvbk1vZGUgLSBFeGVjdXRpb24gbW9kZS5cbiAqIEBwcm9wZXJ0eSB7KHdvcmtlcjogSnNvblNvY2tldCkgPT4gYm9vbGVhbn0gYWNjZXB0cyAtIFdoZXRoZXIgdGhlIHdvcmtlciBhY2NlcHRzIHRoaXMgbW9kZS5cbiAqL1xuLyoqXG4gKiBXb3JrZXIgZXhlY3V0aW9uIG1vZGUgY2FwYWJpbGl0aWVzLlxuICBAdHlwZSB7V29ya2VyRXhlY3V0aW9uTW9kZUNhcGFiaWxpdHlbXX0gKi9cbmNvbnN0IFdPUktFUl9FWEVDVVRJT05fTU9ERV9DQVBBQklMSVRJRVMgPSBbXG4gIHtleGVjdXRpb25Nb2RlOiBcImlubGluZVwiLCBhY2NlcHRzOiAod29ya2VyKSA9PiB3b3JrZXIuYWNjZXB0c0lubGluZUpvYnMgIT09IGZhbHNlfSxcbiAge2V4ZWN1dGlvbk1vZGU6IFwiZm9ya2VkXCIsIGFjY2VwdHM6ICh3b3JrZXIpID0+IHdvcmtlci5hY2NlcHRzRm9ya2VkSm9icyAhPT0gZmFsc2V9LFxuICB7ZXhlY3V0aW9uTW9kZTogXCJzcGF3bmVkXCIsIGFjY2VwdHM6ICh3b3JrZXIpID0+IHdvcmtlci5hY2NlcHRzU3Bhd25lZEpvYnMgIT09IGZhbHNlfVxuXVxuY29uc3QgV09SS0VSX0VYRUNVVElPTl9NT0RFX0NBUEFCSUxJVElFU19CWV9NT0RFID0gbmV3IE1hcChcbiAgV09SS0VSX0VYRUNVVElPTl9NT0RFX0NBUEFCSUxJVElFUy5tYXAoKGNhcGFiaWxpdHkpID0+IFtjYXBhYmlsaXR5LmV4ZWN1dGlvbk1vZGUsIGNhcGFiaWxpdHldKVxuKVxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBCYWNrZ3JvdW5kSm9ic01haW4ge1xuICAvKipcbiAgICogUnVucyBjb25zdHJ1Y3Rvci5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdH0gYXJncy5jb25maWd1cmF0aW9uIC0gQ29uZmlndXJhdGlvbi5cbiAgICogQHBhcmFtIHtzdHJpbmd9IFthcmdzLmhvc3RdIC0gSG9zdG5hbWUuXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbYXJncy5wb3J0XSAtIFBvcnQuXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih7Y29uZmlndXJhdGlvbiwgaG9zdCwgcG9ydH0pIHtcbiAgICB0aGlzLmNvbmZpZ3VyYXRpb24gPSBjb25maWd1cmF0aW9uXG4gICAgY29uc3QgY29uZmlnID0gY29uZmlndXJhdGlvbi5nZXRCYWNrZ3JvdW5kSm9ic0NvbmZpZygpXG4gICAgdGhpcy5ob3N0ID0gaG9zdCB8fCBjb25maWcuaG9zdFxuICAgIHRoaXMucG9ydCA9IHR5cGVvZiBwb3J0ID09PSBcIm51bWJlclwiID8gcG9ydCA6IGNvbmZpZy5wb3J0XG4gICAgdGhpcy5kaXNwYXRjaFN0cmF0ZWd5ID0gY29uZmlnLmRpc3BhdGNoU3RyYXRlZ3lcbiAgICB0aGlzLnBvbGxJbnRlcnZhbE1zID0gY29uZmlnLnBvbGxJbnRlcnZhbE1zXG4gICAgdGhpcy5zdG9yZSA9IG5ldyBCYWNrZ3JvdW5kSm9ic1N0b3JlKHtjb25maWd1cmF0aW9uLCBkYXRhYmFzZUlkZW50aWZpZXI6IGNvbmZpZy5kYXRhYmFzZUlkZW50aWZpZXJ9KVxuICAgIHRoaXMubG9nZ2VyID0gbmV3IExvZ2dlcih0aGlzKVxuICAgIC8qKlxuICAgICAqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS5cbiAgICAgIEB0eXBlIHtTZXQ8SnNvblNvY2tldD59ICovXG4gICAgdGhpcy53b3JrZXJzID0gbmV3IFNldCgpXG4gICAgLyoqXG4gICAgICogTmFycm93cyB0aGUgcnVudGltZSB2YWx1ZSB0byB0aGUgZG9jdW1lbnRlZCB0eXBlLlxuICAgICAgQHR5cGUge1NldDxKc29uU29ja2V0Pn0gKi9cbiAgICB0aGlzLnJlYWR5V29ya2VycyA9IG5ldyBTZXQoKVxuICAgIC8qKlxuICAgICAqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS5cbiAgICAgIEB0eXBlIHtuZXQuU2VydmVyIHwgdW5kZWZpbmVkfSAqL1xuICAgIHRoaXMuc2VydmVyID0gdW5kZWZpbmVkXG4gICAgLyoqXG4gICAgICogTmFycm93cyB0aGUgcnVudGltZSB2YWx1ZSB0byB0aGUgZG9jdW1lbnRlZCB0eXBlLlxuICAgICAgQHR5cGUge05vZGVKUy5UaW1lb3V0IHwgdW5kZWZpbmVkfSAqL1xuICAgIHRoaXMuX3BvbGxUaW1lciA9IHVuZGVmaW5lZFxuICAgIC8qKlxuICAgICAqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS5cbiAgICAgIEB0eXBlIHtOb2RlSlMuVGltZW91dCB8IHVuZGVmaW5lZH0gKi9cbiAgICB0aGlzLl9zY2hlZHVsZWRUaW1lciA9IHVuZGVmaW5lZFxuICAgIC8qKlxuICAgICAqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS5cbiAgICAgIEB0eXBlIHtOb2RlSlMuVGltZW91dCB8IHVuZGVmaW5lZH0gKi9cbiAgICB0aGlzLl9lcnJvclJldHJ5VGltZXIgPSB1bmRlZmluZWRcbiAgICAvKipcbiAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICBAdHlwZSB7Tm9kZUpTLlRpbWVvdXQgfCB1bmRlZmluZWR9ICovXG4gICAgdGhpcy5fb3JwaGFuVGltZXIgPSB1bmRlZmluZWRcbiAgICAvKipcbiAgICAgKiBOYXJyb3dzIHRoZSBydW50aW1lIHZhbHVlIHRvIHRoZSBkb2N1bWVudGVkIHR5cGUuXG4gICAgICBAdHlwZSB7QmFja2dyb3VuZEpvYnNTY2hlZHVsZXIgfCB1bmRlZmluZWR9ICovXG4gICAgdGhpcy5zY2hlZHVsZXIgPSB1bmRlZmluZWRcbiAgICB0aGlzLl9kcmFpbmluZyA9IGZhbHNlXG4gICAgdGhpcy5fcmVkcmFpblF1ZXVlZCA9IGZhbHNlXG4gICAgdGhpcy5fc3RvcHBlZCA9IGZhbHNlXG4gICAgLyoqXG4gICAgICogTmFycm93cyB0aGUgcnVudGltZSB2YWx1ZSB0byB0aGUgZG9jdW1lbnRlZCB0eXBlLlxuICAgICAgQHR5cGUgeygoKSA9PiB2b2lkKSB8IHVuZGVmaW5lZH0gKi9cbiAgICB0aGlzLl91bnN1YnNjcmliZUJlYWNvbiA9IHVuZGVmaW5lZFxuICAgIC8qKlxuICAgICAqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS5cbiAgICAgIEB0eXBlIHsoKC4uLmFyZ3M6IEFycmF5PD8+KSA9PiB2b2lkKSB8IHVuZGVmaW5lZH0gKi9cbiAgICB0aGlzLl9iZWFjb25Db25uZWN0SGFuZGxlciA9IHVuZGVmaW5lZFxuICAgIC8qKlxuICAgICAqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS5cbiAgICAgIEB0eXBlIHtpbXBvcnQoXCIuLi9iZWFjb24vY2xpZW50LmpzXCIpLmRlZmF1bHQgfCBpbXBvcnQoXCIuLi9iZWFjb24vaW4tcHJvY2Vzcy1jbGllbnQuanNcIikuZGVmYXVsdCB8IHVuZGVmaW5lZH0gKi9cbiAgICB0aGlzLl9iZWFjb25DbGllbnQgPSB1bmRlZmluZWRcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIHN0YXJ0LlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIGxpc3RlbmluZy5cbiAgICovXG4gIGFzeW5jIHN0YXJ0KCkge1xuICAgIHRoaXMuX3N0b3BwZWQgPSBmYWxzZVxuICAgIHRoaXMuY29uZmlndXJhdGlvbi5zZXRDdXJyZW50KClcbiAgICBhd2FpdCB0aGlzLmNvbmZpZ3VyYXRpb24uaW5pdGlhbGl6ZSh7dHlwZTogXCJiYWNrZ3JvdW5kLWpvYnMtbWFpblwifSlcbiAgICBhd2FpdCB0aGlzLmNvbmZpZ3VyYXRpb24uY29ubmVjdEJlYWNvbih7cGVlclR5cGU6IFwiYmFja2dyb3VuZC1qb2JzLW1haW5cIn0pXG4gICAgYXdhaXQgdGhpcy5zdG9yZS5lbnN1cmVSZWFkeSgpXG4gICAgY29uc3Qgc2VydmVyID0gbmV0LmNyZWF0ZVNlcnZlcigoc29ja2V0KSA9PiB0aGlzLl9oYW5kbGVDb25uZWN0aW9uKHNvY2tldCkpXG4gICAgdGhpcy5zZXJ2ZXIgPSBzZXJ2ZXJcblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIHNlcnZlci5vbmNlKFwiZXJyb3JcIiwgcmVqZWN0KVxuICAgICAgICBzZXJ2ZXIubGlzdGVuKHRoaXMucG9ydCwgdGhpcy5ob3N0LCAoKSA9PiByZXNvbHZlKHVuZGVmaW5lZCkpXG4gICAgICB9KVxuXG4gICAgICBjb25zdCBhZGRyZXNzID0gc2VydmVyLmFkZHJlc3MoKVxuICAgICAgaWYgKGFkZHJlc3MgJiYgdHlwZW9mIGFkZHJlc3MgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgICAgdGhpcy5wb3J0ID0gYWRkcmVzcy5wb3J0XG4gICAgICB9XG5cbiAgICAgIHRoaXMuX3NldHVwRGlzcGF0Y2hUcmlnZ2VycygpXG5cbiAgICAgIHRoaXMuX29ycGhhblRpbWVyID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICAgICAgICB2b2lkIHRoaXMuX3N3ZWVwT3JwaGFucygpXG4gICAgICB9LCA2MDAwMClcblxuICAgICAgdGhpcy5zY2hlZHVsZXIgPSBuZXcgQmFja2dyb3VuZEpvYnNTY2hlZHVsZXIoe1xuICAgICAgICBjb25maWd1cmF0aW9uOiB0aGlzLmNvbmZpZ3VyYXRpb24sXG4gICAgICAgIGVucXVldWVKb2I6IGFzeW5jICh7YXJncywgam9iQ2xhc3MsIG9wdGlvbnN9KSA9PiB7XG4gICAgICAgICAgYXdhaXQgdGhpcy5zdG9yZS5lbnF1ZXVlKHtcbiAgICAgICAgICAgIGpvYk5hbWU6IGpvYkNsYXNzLmpvYk5hbWUoKSxcbiAgICAgICAgICAgIGFyZ3MsXG4gICAgICAgICAgICBvcHRpb25zXG4gICAgICAgICAgfSlcbiAgICAgICAgICB0aGlzLl9ub3RpZnlFbnF1ZXVlZCgpXG4gICAgICAgICAgYXdhaXQgdGhpcy5fZHJhaW4oKVxuICAgICAgICB9XG4gICAgICB9KVxuICAgICAgYXdhaXQgdGhpcy5zY2hlZHVsZXIuc3RhcnQoKVxuXG4gICAgICAvLyBTdGFydHVwIGNhdGNoLXVwOiBkcmFpbiBhbnl0aGluZyB0aGF0IHdhcyB3YWl0aW5nIGJlZm9yZSB0aGlzXG4gICAgICAvLyBwcm9jZXNzIGNhbWUgdXAuIEluIGJlYWNvbiBtb2RlIHRoaXMgaXMgYWxzbyB0aGUgc2FmZXR5IG5ldCBmb3JcbiAgICAgIC8vIHJhY2VzIGJldHdlZW4gYXR0YWNoaW5nIHRoZSBjb25uZWN0IGxpc3RlbmVyIGFuZCB0aGUgaW5pdGlhbFxuICAgICAgLy8gY29ubmVjdCBmaXJpbmcgKHRoZSBsaXN0ZW5lciBjb3VsZCBtaXNzIHRoZSB2ZXJ5IGZpcnN0IGNvbm5lY3QsXG4gICAgICAvLyBidXQgdGhpcyBkcmFpbiBjb3ZlcnMgaXQpLlxuICAgICAgYXdhaXQgdGhpcy5fZHJhaW4oKVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBhd2FpdCB0aGlzLnN0b3AoKVxuICAgICAgdGhyb3cgZXJyb3JcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUnVucyBzdG9wLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIGNsb3NlZC5cbiAgICovXG4gIGFzeW5jIHN0b3AoKSB7XG4gICAgdGhpcy5fc3RvcHBlZCA9IHRydWVcblxuICAgIHRoaXMuX2Nsb3NlV29ya2VycygpXG4gICAgdGhpcy5fY2xlYXJUaW1lcnMoKVxuICAgIHRoaXMuX2Rpc2Nvbm5lY3RCZWFjb25IYW5kbGVycygpXG4gICAgdGhpcy5zY2hlZHVsZXI/LnN0b3AoKVxuXG4gICAgYXdhaXQgdGhpcy5fc3RvcEJlYWNvbkFuZFNlcnZlcigpXG4gIH1cblxuICAvKipcbiAgICogUnVucyBjbG9zZSB3b3JrZXJzLlxuICAgIEByZXR1cm5zIHt2b2lkfSAqL1xuICBfY2xvc2VXb3JrZXJzKCkge1xuICAgIGZvciAoY29uc3Qgd29ya2VyIG9mIHRoaXMud29ya2Vycykge1xuICAgICAgd29ya2VyLmNsb3NlKClcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUnVucyBjbGVhciB0aW1lcnMuXG4gICAgQHJldHVybnMge3ZvaWR9ICovXG4gIF9jbGVhclRpbWVycygpIHtcbiAgICBpZiAodGhpcy5fcG9sbFRpbWVyKSBjbGVhckludGVydmFsKHRoaXMuX3BvbGxUaW1lcilcbiAgICBpZiAodGhpcy5fc2NoZWR1bGVkVGltZXIpIGNsZWFyVGltZW91dCh0aGlzLl9zY2hlZHVsZWRUaW1lcilcbiAgICBpZiAodGhpcy5fZXJyb3JSZXRyeVRpbWVyKSBjbGVhclRpbWVvdXQodGhpcy5fZXJyb3JSZXRyeVRpbWVyKVxuICAgIGlmICh0aGlzLl9vcnBoYW5UaW1lcikgY2xlYXJJbnRlcnZhbCh0aGlzLl9vcnBoYW5UaW1lcilcbiAgICB0aGlzLl9wb2xsVGltZXIgPSB1bmRlZmluZWRcbiAgICB0aGlzLl9zY2hlZHVsZWRUaW1lciA9IHVuZGVmaW5lZFxuICAgIHRoaXMuX2Vycm9yUmV0cnlUaW1lciA9IHVuZGVmaW5lZFxuICAgIHRoaXMuX29ycGhhblRpbWVyID0gdW5kZWZpbmVkXG4gIH1cblxuICAvKipcbiAgICogUnVucyBkaXNjb25uZWN0IGJlYWNvbiBoYW5kbGVycy5cbiAgICBAcmV0dXJucyB7dm9pZH0gKi9cbiAgX2Rpc2Nvbm5lY3RCZWFjb25IYW5kbGVycygpIHtcbiAgICBpZiAodGhpcy5fdW5zdWJzY3JpYmVCZWFjb24pIHtcbiAgICAgIHRoaXMuX3Vuc3Vic2NyaWJlQmVhY29uKClcbiAgICAgIHRoaXMuX3Vuc3Vic2NyaWJlQmVhY29uID0gdW5kZWZpbmVkXG4gICAgfVxuXG4gICAgaWYgKHRoaXMuX2JlYWNvbkNsaWVudCAmJiB0aGlzLl9iZWFjb25Db25uZWN0SGFuZGxlcikge1xuICAgICAgdGhpcy5fYmVhY29uQ2xpZW50Lm9mZihcImNvbm5lY3RcIiwgdGhpcy5fYmVhY29uQ29ubmVjdEhhbmRsZXIpXG4gICAgfVxuICAgIHRoaXMuX2JlYWNvbkNvbm5lY3RIYW5kbGVyID0gdW5kZWZpbmVkXG4gICAgdGhpcy5fYmVhY29uQ2xpZW50ID0gdW5kZWZpbmVkXG4gIH1cblxuICAvKipcbiAgICogUnVucyBzdG9wIGJlYWNvbiBhbmQgc2VydmVyLlxuICAgIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAqL1xuICBhc3luYyBfc3RvcEJlYWNvbkFuZFNlcnZlcigpIHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5jb25maWd1cmF0aW9uLmRpc2Nvbm5lY3RCZWFjb24oKVxuICAgIH0gZmluYWxseSB7XG4gICAgICBhd2FpdCB0aGlzLl9jbG9zZVNlcnZlckFuZERhdGFiYXNlQ29ubmVjdGlvbnMoKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGNsb3NlIHNlcnZlciBhbmQgZGF0YWJhc2UgY29ubmVjdGlvbnMuXG4gICAgQHJldHVybnMge1Byb21pc2U8dm9pZD59ICovXG4gIGFzeW5jIF9jbG9zZVNlcnZlckFuZERhdGFiYXNlQ29ubmVjdGlvbnMoKSB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHRoaXMuX2Nsb3NlU2VydmVyKClcbiAgICB9IGZpbmFsbHkge1xuICAgICAgYXdhaXQgdGhpcy5jb25maWd1cmF0aW9uLmNsb3NlRGF0YWJhc2VDb25uZWN0aW9ucygpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgY2xvc2Ugc2VydmVyLlxuICAgIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAqL1xuICBhc3luYyBfY2xvc2VTZXJ2ZXIoKSB7XG4gICAgaWYgKCF0aGlzLnNlcnZlcikgcmV0dXJuXG5cbiAgICBjb25zdCB7c2VydmVyfSA9IHRoaXNcbiAgICB0aGlzLnNlcnZlciA9IHVuZGVmaW5lZFxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXJ2ZXIuY2xvc2UoKCkgPT4gcmVzb2x2ZSh1bmRlZmluZWQpKSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGdldCBwb3J0LlxuICAgKiBAcmV0dXJucyB7bnVtYmVyfSAtIEJvdW5kIHBvcnQuXG4gICAqL1xuICBnZXRQb3J0KCkge1xuICAgIHJldHVybiB0aGlzLnBvcnRcbiAgfVxuXG4gIC8qKlxuICAgKiBXaXJlcyB1cCB0aGUgZGlzcGF0Y2gtdHJpZ2dlcmluZyBzaWduYWwgc291cmNlcyBmb3IgdGhlIGNvbmZpZ3VyZWRcbiAgICogc3RyYXRlZ3kuIEluIGBcImJlYWNvblwiYCBtb2RlIChkZWZhdWx0KSB0aGlzIG1lYW5zIHN1YnNjcmliaW5nIHRvIHRoZVxuICAgKiBgdmVsb2Npb3VzLWJhY2tncm91bmQtam9icy1kaXNwYXRjaGAgY2hhbm5lbCBmb3IgY3Jvc3MtcHJvY2Vzc1xuICAgKiB3YWtlLXVwcywgbGlzdGVuaW5nIGZvciBCZWFjb24gKHJlKWNvbm5lY3RzIHRvIGNhdGNoIHVwIG9uIG1pc3NlZFxuICAgKiB3b3JrLCBhbmQgcmVseWluZyBvbiBkaXJlY3QgaW4tcHJvY2VzcyBjYWxscyBmcm9tIGBfaGFuZGxlRW5xdWV1ZWAsXG4gICAqIGBfaGFuZGxlSm9iQ29tcGxldGVgL2BGYWlsZWRgLCB3b3JrZXIgaGVsbG8vcmVhZHksIGFuZCB0aGVcbiAgICogc2NoZWR1bGVkLWpvYiBgc2V0VGltZW91dGAuIEluIGBcInBvbGxpbmdcImAgbW9kZSB3ZSByZXN0b3JlIHRoZVxuICAgKiBsZWdhY3kgZml4ZWQtaW50ZXJ2YWwgcG9sbCBmb3IgdXNlcnMgd2hvIHdhbnQgdGhlIHByZXZpb3VzIGJlaGF2aW9yLlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIF9zZXR1cERpc3BhdGNoVHJpZ2dlcnMoKSB7XG4gICAgaWYgKHRoaXMuZGlzcGF0Y2hTdHJhdGVneSA9PT0gXCJwb2xsaW5nXCIpIHtcbiAgICAgIHRoaXMuX3BvbGxUaW1lciA9IHNldEludGVydmFsKCgpID0+IHtcbiAgICAgICAgdm9pZCB0aGlzLl9kcmFpbigpXG4gICAgICB9LCB0aGlzLnBvbGxJbnRlcnZhbE1zKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgY29uc3QgYmVhY29uQ2xpZW50ID0gdGhpcy5jb25maWd1cmF0aW9uLmdldEJlYWNvbkNsaWVudCgpXG4gICAgaWYgKCFiZWFjb25DbGllbnQpIHJldHVyblxuXG4gICAgdGhpcy5fYmVhY29uQ2xpZW50ID0gYmVhY29uQ2xpZW50XG5cbiAgICB0aGlzLl91bnN1YnNjcmliZUJlYWNvbiA9IGJlYWNvbkNsaWVudC5vbkJyb2FkY2FzdCgobWVzc2FnZSkgPT4ge1xuICAgICAgaWYgKG1lc3NhZ2U/LmNoYW5uZWwgIT09IERJU1BBVENIX0NIQU5ORUwpIHJldHVyblxuICAgICAgdm9pZCB0aGlzLl9kcmFpbigpXG4gICAgfSlcblxuICAgIC8vIERyYWluIG9uIGV2ZXJ5IChyZSljb25uZWN0IHRvIGNhdGNoIHVwIG9uIGpvYnMgZW5xdWV1ZWQgd2hpbGUgdGhlXG4gICAgLy8gYnVzIHdhcyB1bnJlYWNoYWJsZS4gVGhlIERCIGlzIHRoZSBkdXJhYmxlIGxvZzsgQmVhY29uIGlzIGp1c3QgdGhlXG4gICAgLy8gd2FrZS11cCBzaWduYWwuXG4gICAgdGhpcy5fYmVhY29uQ29ubmVjdEhhbmRsZXIgPSAoKSA9PiB7XG4gICAgICB2b2lkIHRoaXMuX2RyYWluKClcbiAgICB9XG4gICAgYmVhY29uQ2xpZW50Lm9uKFwiY29ubmVjdFwiLCB0aGlzLl9iZWFjb25Db25uZWN0SGFuZGxlcilcbiAgfVxuXG4gIC8qKlxuICAgKiBQdWJsaXNoZXMgYSBkaXNwYXRjaCB3YWtlLXVwIG9uIHRoZSBCZWFjb24gY2hhbm5lbC4gTm8tb3AgaW4gcG9sbGluZ1xuICAgKiBtb2RlIG9yIHdoZW4gQmVhY29uIGlzIG5vdCBjb25uZWN0ZWQ7IGluIHRob3NlIGNhc2VzIHRoZSBkaXJlY3RcbiAgICogaW4tcHJvY2VzcyBgX2RyYWluKClgIGNhbGwgaW4gdGhlIGVucXVldWUvaGFuZGxlIHBhdGhzIGlzIHN1ZmZpY2llbnRcbiAgICogKHRoZXJlIGFyZSBubyBvdGhlciBwcm9jZXNzZXMgdG8gbm90aWZ5KS5cbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICBfbm90aWZ5RW5xdWV1ZWQoKSB7XG4gICAgaWYgKHRoaXMuZGlzcGF0Y2hTdHJhdGVneSA9PT0gXCJwb2xsaW5nXCIpIHJldHVyblxuXG4gICAgY29uc3QgYmVhY29uQ2xpZW50ID0gdGhpcy5jb25maWd1cmF0aW9uLmdldEJlYWNvbkNsaWVudCgpXG4gICAgaWYgKCFiZWFjb25DbGllbnQgfHwgIWJlYWNvbkNsaWVudC5pc0Nvbm5lY3RlZCgpKSByZXR1cm5cblxuICAgIHRyeSB7XG4gICAgICBiZWFjb25DbGllbnQucHVibGlzaCh7XG4gICAgICAgIGNoYW5uZWw6IERJU1BBVENIX0NIQU5ORUwsXG4gICAgICAgIGJyb2FkY2FzdFBhcmFtczoge30sXG4gICAgICAgIGJvZHk6IHthY3Rpb246IFwid2FrZVwifVxuICAgICAgfSlcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhpcy5sb2dnZXIud2FybigoKSA9PiBbXCJGYWlsZWQgdG8gcHVibGlzaCBiYWNrZ3JvdW5kIGpvYnMgd2FrZSBicm9hZGNhc3Q6XCIsIGVycm9yXSlcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUnVucyBoYW5kbGUgY29ubmVjdGlvbi5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCJuZXRcIikuU29ja2V0fSBzb2NrZXQgLSBTb2NrZXQuXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgX2hhbmRsZUNvbm5lY3Rpb24oc29ja2V0KSB7XG4gICAgY29uc3QganNvblNvY2tldCA9IG5ldyBKc29uU29ja2V0KHNvY2tldClcbiAgICAvKipcbiAgICAgKiBSb2xlLlxuICAgICAgQHR5cGUge2ltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlNvY2tldFJvbGUgfCBudWxsfSAqL1xuICAgIGxldCByb2xlID0gbnVsbFxuXG4gICAgY29uc3QgY2xlYW51cCA9ICgpID0+IHtcbiAgICAgIGlmIChyb2xlID09PSBcIndvcmtlclwiKSB7XG4gICAgICAgIHRoaXMud29ya2Vycy5kZWxldGUoanNvblNvY2tldClcbiAgICAgICAgdGhpcy5yZWFkeVdvcmtlcnMuZGVsZXRlKGpzb25Tb2NrZXQpXG4gICAgICB9XG4gICAgfVxuXG4gICAganNvblNvY2tldC5vbihcImNsb3NlXCIsIGNsZWFudXApXG4gICAganNvblNvY2tldC5vbihcImVycm9yXCIsIChlcnJvcikgPT4ge1xuICAgICAgdGhpcy5sb2dnZXIud2FybigoKSA9PiBbXCJCYWNrZ3JvdW5kIGpvYnMgY29ubmVjdGlvbiBlcnJvcjpcIiwgZXJyb3JdKVxuICAgICAgY2xlYW51cCgpXG4gICAgfSlcblxuICAgIGpzb25Tb2NrZXQub24oXCJtZXNzYWdlXCIsIChtZXNzYWdlKSA9PiB7XG4gICAgICByb2xlID0gdGhpcy5faGFuZGxlU29ja2V0TWVzc2FnZSh7anNvblNvY2tldCwgbWVzc2FnZSwgcm9sZX0pXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGhhbmRsZSBzb2NrZXQgbWVzc2FnZS5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zLlxuICAgKiBAcGFyYW0ge0pzb25Tb2NrZXR9IGFyZ3MuanNvblNvY2tldCAtIEpTT04gc29ja2V0LlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlNvY2tldE1lc3NhZ2V9IGFyZ3MubWVzc2FnZSAtIFNvY2tldCBtZXNzYWdlLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlNvY2tldFJvbGUgfCBudWxsfSBhcmdzLnJvbGUgLSBDdXJyZW50IHNvY2tldCByb2xlLlxuICAgKiBAcmV0dXJucyB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iU29ja2V0Um9sZSB8IG51bGx9IC0gVXBkYXRlZCBzb2NrZXQgcm9sZS5cbiAgICovXG4gIF9oYW5kbGVTb2NrZXRNZXNzYWdlKHtqc29uU29ja2V0LCBtZXNzYWdlLCByb2xlfSkge1xuICAgIGlmICghcm9sZSkgcmV0dXJuIHRoaXMuX2hhbmRsZVJvbGVsZXNzU29ja2V0TWVzc2FnZSh7anNvblNvY2tldCwgbWVzc2FnZX0pXG4gICAgaWYgKHJvbGUgPT09IFwiY2xpZW50XCIpIHRoaXMuX2hhbmRsZUNsaWVudFNvY2tldE1lc3NhZ2Uoe2pzb25Tb2NrZXQsIG1lc3NhZ2V9KVxuICAgIGlmIChyb2xlID09PSBcIndvcmtlclwiKSB0aGlzLl9oYW5kbGVXb3JrZXJTb2NrZXRNZXNzYWdlKHtqc29uU29ja2V0LCBtZXNzYWdlfSlcbiAgICBpZiAocm9sZSA9PT0gXCJyZXBvcnRlclwiKSB0aGlzLl9oYW5kbGVSZXBvcnRlclNvY2tldE1lc3NhZ2Uoe2pzb25Tb2NrZXQsIG1lc3NhZ2V9KVxuXG4gICAgcmV0dXJuIHJvbGVcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGhhbmRsZSByb2xlbGVzcyBzb2NrZXQgbWVzc2FnZS5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zLlxuICAgKiBAcGFyYW0ge0pzb25Tb2NrZXR9IGFyZ3MuanNvblNvY2tldCAtIEpTT04gc29ja2V0LlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlNvY2tldE1lc3NhZ2V9IGFyZ3MubWVzc2FnZSAtIFNvY2tldCBtZXNzYWdlLlxuICAgKiBAcmV0dXJucyB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iU29ja2V0Um9sZSB8IG51bGx9IC0gTmV3IHNvY2tldCByb2xlLlxuICAgKi9cbiAgX2hhbmRsZVJvbGVsZXNzU29ja2V0TWVzc2FnZSh7anNvblNvY2tldCwgbWVzc2FnZX0pIHtcbiAgICBpZiAobWVzc2FnZT8udHlwZSAhPT0gXCJoZWxsb1wiKSByZXR1cm4gbnVsbFxuXG4gICAgaWYgKG1lc3NhZ2Uucm9sZSA9PT0gXCJ3b3JrZXJcIikge1xuICAgICAganNvblNvY2tldC53b3JrZXJJZCA9IG1lc3NhZ2Uud29ya2VySWRcbiAgICAgIHRoaXMud29ya2Vycy5hZGQoanNvblNvY2tldClcbiAgICB9XG5cbiAgICByZXR1cm4gbWVzc2FnZS5yb2xlXG4gIH1cblxuICAvKipcbiAgICogUnVucyBoYW5kbGUgY2xpZW50IHNvY2tldCBtZXNzYWdlLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7SnNvblNvY2tldH0gYXJncy5qc29uU29ja2V0IC0gSlNPTiBzb2NrZXQuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iU29ja2V0TWVzc2FnZX0gYXJncy5tZXNzYWdlIC0gU29ja2V0IG1lc3NhZ2UuXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgX2hhbmRsZUNsaWVudFNvY2tldE1lc3NhZ2Uoe2pzb25Tb2NrZXQsIG1lc3NhZ2V9KSB7XG4gICAgaWYgKG1lc3NhZ2U/LnR5cGUgPT09IFwiZW5xdWV1ZVwiKSB7XG4gICAgICB0aGlzLl9oYW5kbGVFbnF1ZXVlKHtqc29uU29ja2V0LCBtZXNzYWdlfSlcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUnVucyBoYW5kbGUgd29ya2VyIHNvY2tldCBtZXNzYWdlLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7SnNvblNvY2tldH0gYXJncy5qc29uU29ja2V0IC0gSlNPTiBzb2NrZXQuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iU29ja2V0TWVzc2FnZX0gYXJncy5tZXNzYWdlIC0gU29ja2V0IG1lc3NhZ2UuXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgX2hhbmRsZVdvcmtlclNvY2tldE1lc3NhZ2Uoe2pzb25Tb2NrZXQsIG1lc3NhZ2V9KSB7XG4gICAgaWYgKG1lc3NhZ2U/LnR5cGUgPT09IFwicmVhZHlcIikge1xuICAgICAgdGhpcy5faGFuZGxlV29ya2VyUmVhZHkoe2pzb25Tb2NrZXQsIG1lc3NhZ2V9KVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgaWYgKG1lc3NhZ2U/LnR5cGUgPT09IFwiZHJhaW5pbmdcIikge1xuICAgICAgdGhpcy5faGFuZGxlV29ya2VyRHJhaW5pbmcoe2pzb25Tb2NrZXR9KVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgdGhpcy5faGFuZGxlUmVwb3J0ZXJTb2NrZXRNZXNzYWdlKHtqc29uU29ja2V0LCBtZXNzYWdlfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGhhbmRsZSByZXBvcnRlciBzb2NrZXQgbWVzc2FnZS5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zLlxuICAgKiBAcGFyYW0ge0pzb25Tb2NrZXR9IGFyZ3MuanNvblNvY2tldCAtIEpTT04gc29ja2V0LlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlNvY2tldE1lc3NhZ2V9IGFyZ3MubWVzc2FnZSAtIFNvY2tldCBtZXNzYWdlLlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIF9oYW5kbGVSZXBvcnRlclNvY2tldE1lc3NhZ2Uoe2pzb25Tb2NrZXQsIG1lc3NhZ2V9KSB7XG4gICAgaWYgKG1lc3NhZ2U/LnR5cGUgPT09IFwiam9iLWNvbXBsZXRlXCIpIHtcbiAgICAgIHRoaXMuX2hhbmRsZUpvYkNvbXBsZXRlKHtqc29uU29ja2V0LCBtZXNzYWdlfSlcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChtZXNzYWdlPy50eXBlID09PSBcImpvYi1mYWlsZWRcIikge1xuICAgICAgdGhpcy5faGFuZGxlSm9iRmFpbGVkKHtqc29uU29ja2V0LCBtZXNzYWdlfSlcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUnVucyBoYW5kbGUgd29ya2VyIHJlYWR5LlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7SnNvblNvY2tldH0gYXJncy5qc29uU29ja2V0IC0gSlNPTiBzb2NrZXQuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iUmVhZHlNZXNzYWdlfSBhcmdzLm1lc3NhZ2UgLSBSZWFkeSBtZXNzYWdlLlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIF9oYW5kbGVXb3JrZXJSZWFkeSh7anNvblNvY2tldCwgbWVzc2FnZX0pIHtcbiAgICBqc29uU29ja2V0LmFjY2VwdHNTcGF3bmVkSm9icyA9IG1lc3NhZ2UuYWNjZXB0c1NwYXduZWQgIT09IGZhbHNlICYmIG1lc3NhZ2UuYWNjZXB0c0ZvcmtlZCAhPT0gZmFsc2VcbiAgICBqc29uU29ja2V0LmFjY2VwdHNGb3JrZWRKb2JzID0gbWVzc2FnZS5hY2NlcHRzRm9ya2VkICE9PSBmYWxzZVxuICAgIGpzb25Tb2NrZXQuYWNjZXB0c0lubGluZUpvYnMgPSBtZXNzYWdlLmFjY2VwdHNJbmxpbmUgIT09IGZhbHNlXG4gICAgdGhpcy5yZWFkeVdvcmtlcnMuYWRkKGpzb25Tb2NrZXQpXG4gICAgdm9pZCB0aGlzLl9kcmFpbigpXG4gIH1cblxuICAvKipcbiAgICogUnVucyBoYW5kbGUgd29ya2VyIGRyYWluaW5nLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7SnNvblNvY2tldH0gYXJncy5qc29uU29ja2V0IC0gSlNPTiBzb2NrZXQuXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgX2hhbmRsZVdvcmtlckRyYWluaW5nKHtqc29uU29ja2V0fSkge1xuICAgIC8vIFRoZSB3b3JrZXIgaXMgc2h1dHRpbmcgZG93biBncmFjZWZ1bGx5LiBTdG9wIGRpc3BhdGNoaW5nIG5ldyBqb2JzXG4gICAgLy8gdG8gaXQgYnV0IGtlZXAgdGhlIGNvbm5lY3Rpb24gaW4gYHdvcmtlcnNgIHNvIGFueSBpbi1mbGlnaHQgam9iXG4gICAgLy8gaXQncyBzdGlsbCBkcmFpbmluZyBjYW4gcmVwb3J0IGl0cyByZXN1bHQuXG4gICAgdGhpcy5yZWFkeVdvcmtlcnMuZGVsZXRlKGpzb25Tb2NrZXQpXG4gIH1cblxuICAvKipcbiAgICogUnVucyBoYW5kbGUgZW5xdWV1ZS5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zLlxuICAgKiBAcGFyYW0ge0pzb25Tb2NrZXR9IGFyZ3MuanNvblNvY2tldCAtIEpTT04gc29ja2V0LlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYkVucXVldWVNZXNzYWdlfSBhcmdzLm1lc3NhZ2UgLSBNZXNzYWdlLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIGhhbmRsZWQuXG4gICAqL1xuICBhc3luYyBfaGFuZGxlRW5xdWV1ZSh7anNvblNvY2tldCwgbWVzc2FnZX0pIHtcbiAgICB0cnkge1xuICAgICAgY29uc3Qgam9iSWQgPSBhd2FpdCB0aGlzLnN0b3JlLmVucXVldWUoe1xuICAgICAgICBqb2JOYW1lOiBtZXNzYWdlLmpvYk5hbWUsXG4gICAgICAgIGFyZ3M6IG1lc3NhZ2UuYXJncyB8fCBbXSxcbiAgICAgICAgb3B0aW9uczogbWVzc2FnZS5vcHRpb25zIHx8IHt9XG4gICAgICB9KVxuXG4gICAgICBqc29uU29ja2V0LnNlbmQoe3R5cGU6IFwiZW5xdWV1ZWRcIiwgam9iSWR9KVxuICAgICAgdGhpcy5fbm90aWZ5RW5xdWV1ZWQoKVxuICAgICAgYXdhaXQgdGhpcy5fZHJhaW4oKVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICB0aGlzLmxvZ2dlci5lcnJvcigoKSA9PiBbXCJGYWlsZWQgdG8gZW5xdWV1ZSBiYWNrZ3JvdW5kIGpvYjpcIiwgZXJyb3JdKVxuICAgICAganNvblNvY2tldC5zZW5kKHt0eXBlOiBcImVucXVldWUtZXJyb3JcIiwgZXJyb3I6IFwiRmFpbGVkIHRvIGVucXVldWUgam9iXCJ9KVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGhhbmRsZSBqb2IgY29tcGxldGUuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtKc29uU29ja2V0fSBhcmdzLmpzb25Tb2NrZXQgLSBKU09OIHNvY2tldC5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJhY2tncm91bmRKb2JDb21wbGV0ZU1lc3NhZ2V9IGFyZ3MubWVzc2FnZSAtIE1lc3NhZ2UuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIHdoZW4gaGFuZGxlZC5cbiAgICovXG4gIGFzeW5jIF9oYW5kbGVKb2JDb21wbGV0ZSh7anNvblNvY2tldCwgbWVzc2FnZX0pIHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5zdG9yZS5tYXJrQ29tcGxldGVkKHtcbiAgICAgICAgam9iSWQ6IG1lc3NhZ2Uuam9iSWQsXG4gICAgICAgIHdvcmtlcklkOiBtZXNzYWdlLndvcmtlcklkLFxuICAgICAgICBoYW5kZWRPZmZBdE1zOiBtZXNzYWdlLmhhbmRlZE9mZkF0TXNcbiAgICAgIH0pXG4gICAgICBqc29uU29ja2V0LnNlbmQoe3R5cGU6IFwiam9iLXVwZGF0ZWRcIiwgam9iSWQ6IG1lc3NhZ2Uuam9iSWR9KVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICB0aGlzLmxvZ2dlci5lcnJvcigoKSA9PiBbXCJGYWlsZWQgdG8gdXBkYXRlIGpvYiBjb21wbGV0aW9uOlwiLCBlcnJvcl0pXG4gICAgICBqc29uU29ja2V0LnNlbmQoe3R5cGU6IFwiam9iLXVwZGF0ZS1lcnJvclwiLCBqb2JJZDogbWVzc2FnZS5qb2JJZCwgZXJyb3I6IFwiRmFpbGVkIHRvIHVwZGF0ZSBqb2JcIn0pXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgaGFuZGxlIGpvYiBmYWlsZWQuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtKc29uU29ja2V0fSBhcmdzLmpzb25Tb2NrZXQgLSBKU09OIHNvY2tldC5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJhY2tncm91bmRKb2JGYWlsZWRNZXNzYWdlfSBhcmdzLm1lc3NhZ2UgLSBNZXNzYWdlLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIGhhbmRsZWQuXG4gICAqL1xuICBhc3luYyBfaGFuZGxlSm9iRmFpbGVkKHtqc29uU29ja2V0LCBtZXNzYWdlfSkge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBmYWlsZWRKb2IgPSBhd2FpdCB0aGlzLnN0b3JlLm1hcmtGYWlsZWQoe1xuICAgICAgICBqb2JJZDogbWVzc2FnZS5qb2JJZCxcbiAgICAgICAgZXJyb3I6IG1lc3NhZ2UuZXJyb3IsXG4gICAgICAgIHdvcmtlcklkOiBtZXNzYWdlLndvcmtlcklkLFxuICAgICAgICBoYW5kZWRPZmZBdE1zOiBtZXNzYWdlLmhhbmRlZE9mZkF0TXNcbiAgICAgIH0pXG5cbiAgICAgIGlmIChmYWlsZWRKb2IpIHtcbiAgICAgICAgdGhpcy5fZW1pdEJhY2tncm91bmRKb2JGYWlsZWQoe1xuICAgICAgICAgIGVycm9yOiBtZXNzYWdlLmVycm9yLFxuICAgICAgICAgIGhhbmRlZE9mZkF0TXM6IG1lc3NhZ2UuaGFuZGVkT2ZmQXRNcyxcbiAgICAgICAgICBqb2I6IGZhaWxlZEpvYixcbiAgICAgICAgICB3b3JrZXJJZDogbWVzc2FnZS53b3JrZXJJZFxuICAgICAgICB9KVxuICAgICAgfVxuXG4gICAgICBqc29uU29ja2V0LnNlbmQoe3R5cGU6IFwiam9iLXVwZGF0ZWRcIiwgam9iSWQ6IG1lc3NhZ2Uuam9iSWR9KVxuICAgICAgLy8gQSBmYWlsZWQgam9iIG1heSBoYXZlIGJlZW4gcmUtcXVldWVkICh3aXRoIGJhY2tvZmYpIGZvciByZXRyeSDigJRcbiAgICAgIC8vIHBva2UgdGhlIGRpc3BhdGNoZXIgc28gdGhlIHJldHJ5IHRpbWVyIGlzIGFybWVkLlxuICAgICAgdGhpcy5fbm90aWZ5RW5xdWV1ZWQoKVxuICAgICAgYXdhaXQgdGhpcy5fZHJhaW4oKVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICB0aGlzLmxvZ2dlci5lcnJvcigoKSA9PiBbXCJGYWlsZWQgdG8gdXBkYXRlIGpvYiBmYWlsdXJlOlwiLCBlcnJvcl0pXG4gICAgICBqc29uU29ja2V0LnNlbmQoe3R5cGU6IFwiam9iLXVwZGF0ZS1lcnJvclwiLCBqb2JJZDogbWVzc2FnZS5qb2JJZCwgZXJyb3I6IFwiRmFpbGVkIHRvIHVwZGF0ZSBqb2JcIn0pXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgZW1pdCBiYWNrZ3JvdW5kIGpvYiBmYWlsZWQuXG4gICAqIEBwYXJhbSB7e2Vycm9yOiA/LCBoYW5kZWRPZmZBdE1zPzogbnVtYmVyLCBqb2I6IGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlJvdywgd29ya2VySWQ/OiBzdHJpbmd9fSBhcmdzIC0gRmFpbHVyZSBldmVudCBkYXRhLlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIF9lbWl0QmFja2dyb3VuZEpvYkZhaWxlZCh7ZXJyb3IsIGhhbmRlZE9mZkF0TXMsIGpvYiwgd29ya2VySWR9KSB7XG4gICAgY29uc3Qgbm9ybWFsaXplZEVycm9yID0gdGhpcy5fbm9ybWFsaXplRmFpbHVyZUVycm9yKGVycm9yKVxuICAgIGNvbnN0IHBheWxvYWQgPSB7XG4gICAgICBjb250ZXh0OiB7XG4gICAgICAgIGF0dGVtcHRzOiBqb2IuYXR0ZW1wdHMsXG4gICAgICAgIGhhbmRlZE9mZkF0TXMsXG4gICAgICAgIGpvYkFyZ3M6IGpvYi5hcmdzLFxuICAgICAgICBqb2JJZDogam9iLmlkLFxuICAgICAgICBqb2JOYW1lOiBqb2Iuam9iTmFtZSxcbiAgICAgICAgbWF4UmV0cmllczogam9iLm1heFJldHJpZXMsXG4gICAgICAgIHN0YWdlOiBcImJhY2tncm91bmQtam9iLWZhaWxlZFwiLFxuICAgICAgICBzdGF0dXM6IGpvYi5zdGF0dXMsXG4gICAgICAgIHRlcm1pbmFsOiBqb2Iuc3RhdHVzID09PSBcImZhaWxlZFwiIHx8IGpvYi5zdGF0dXMgPT09IFwib3JwaGFuZWRcIixcbiAgICAgICAgd2lsbFJldHJ5OiBqb2Iuc3RhdHVzID09PSBcInF1ZXVlZFwiLFxuICAgICAgICB3b3JrZXJJZFxuICAgICAgfSxcbiAgICAgIGVycm9yOiBub3JtYWxpemVkRXJyb3JcbiAgICB9XG4gICAgY29uc3QgZXJyb3JFdmVudHMgPSB0aGlzLmNvbmZpZ3VyYXRpb24uZ2V0RXJyb3JFdmVudHMoKVxuXG4gICAgZXJyb3JFdmVudHMuZW1pdChcImJhY2tncm91bmQtam9iLWZhaWxlZFwiLCBwYXlsb2FkKVxuICAgIGVycm9yRXZlbnRzLmVtaXQoXCJhbGwtZXJyb3JcIiwgey4uLnBheWxvYWQsIGVycm9yVHlwZTogXCJiYWNrZ3JvdW5kLWpvYi1mYWlsZWRcIn0pXG4gIH1cblxuICAvKipcbiAgICogUnVucyBub3JtYWxpemUgZmFpbHVyZSBlcnJvci5cbiAgICogQHBhcmFtIHs/fSBlcnJvciAtIFJlcG9ydGVkIGZhaWx1cmUgdmFsdWUuXG4gICAqIEByZXR1cm5zIHtFcnJvcn0gTm9ybWFsaXplZCBlcnJvci5cbiAgICovXG4gIF9ub3JtYWxpemVGYWlsdXJlRXJyb3IoZXJyb3IpIHtcbiAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFcnJvcikgcmV0dXJuIGVycm9yXG5cbiAgICByZXR1cm4gdGhpcy5fZXJyb3JGcm9tVW5rbm93bkZhaWx1cmUoZXJyb3IpXG4gIH1cblxuICAvKipcbiAgICogUnVucyBlcnJvciBmcm9tIHVua25vd24gZmFpbHVyZS5cbiAgICogQHBhcmFtIHs/fSBlcnJvciAtIFJlcG9ydGVkIGZhaWx1cmUgdmFsdWUuXG4gICAqIEByZXR1cm5zIHtFcnJvcn0gTm9ybWFsaXplZCBlcnJvci5cbiAgICovXG4gIF9lcnJvckZyb21Vbmtub3duRmFpbHVyZShlcnJvcikge1xuICAgIGNvbnN0IG1lc3NhZ2UgPSB0aGlzLl9tZXNzYWdlRnJvbVVua25vd25GYWlsdXJlKGVycm9yKVxuICAgIGNvbnN0IG5vcm1hbGl6ZWRFcnJvciA9IG5ldyBFcnJvcihtZXNzYWdlKVxuXG4gICAgdGhpcy5fY29weVN0cmluZ0ZhaWx1cmVTdGFjayh7ZXJyb3IsIG5vcm1hbGl6ZWRFcnJvcn0pXG5cbiAgICByZXR1cm4gbm9ybWFsaXplZEVycm9yXG4gIH1cblxuICAvKipcbiAgICogUnVucyBtZXNzYWdlIGZyb20gdW5rbm93biBmYWlsdXJlLlxuICAgKiBAcGFyYW0gez99IGVycm9yIC0gUmVwb3J0ZWQgZmFpbHVyZSB2YWx1ZS5cbiAgICogQHJldHVybnMge3N0cmluZ30gRXJyb3IgbWVzc2FnZS5cbiAgICovXG4gIF9tZXNzYWdlRnJvbVVua25vd25GYWlsdXJlKGVycm9yKSB7XG4gICAgaWYgKHRoaXMuX2hhc1N0cmluZ0ZhaWx1cmUoZXJyb3IpKSByZXR1cm4gZXJyb3IudHJpbSgpLnNwbGl0KFwiXFxuXCIpWzBdXG5cbiAgICByZXR1cm4gU3RyaW5nKGVycm9yIHx8IFwiQmFja2dyb3VuZCBqb2IgZmFpbGVkXCIpXG4gIH1cblxuICAvKipcbiAgICogUnVucyBoYXMgc3RyaW5nIGZhaWx1cmUuXG4gICAqIEBwYXJhbSB7P30gZXJyb3IgLSBSZXBvcnRlZCBmYWlsdXJlIHZhbHVlLlxuICAgKiBAcmV0dXJucyB7ZXJyb3IgaXMgc3RyaW5nfSBXaGV0aGVyIHRoZSB2YWx1ZSBpcyBhIG5vbi1lbXB0eSBzdHJpbmcuXG4gICAqL1xuICBfaGFzU3RyaW5nRmFpbHVyZShlcnJvcikge1xuICAgIHJldHVybiB0eXBlb2YgZXJyb3IgPT09IFwic3RyaW5nXCIgJiYgZXJyb3IudHJpbSgpLmxlbmd0aCA+IDBcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGNvcHkgc3RyaW5nIGZhaWx1cmUgc3RhY2suXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHs/fSBhcmdzLmVycm9yIC0gUmVwb3J0ZWQgZmFpbHVyZSB2YWx1ZS5cbiAgICogQHBhcmFtIHtFcnJvcn0gYXJncy5ub3JtYWxpemVkRXJyb3IgLSBOb3JtYWxpemVkIGVycm9yLlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIF9jb3B5U3RyaW5nRmFpbHVyZVN0YWNrKHtlcnJvciwgbm9ybWFsaXplZEVycm9yfSkge1xuICAgIGlmICh0aGlzLl9oYXNTdHJpbmdGYWlsdXJlKGVycm9yKSkgbm9ybWFsaXplZEVycm9yLnN0YWNrID0gZXJyb3JcbiAgfVxuXG4gIC8qKlxuICAgKiBEcmFpbnMgYWxsIGRpc3BhdGNoYWJsZSBqb2JzIHRvIHJlYWR5IHdvcmtlcnMsIHRoZW4gYXJtcyB0aGVcbiAgICogc2NoZWR1bGVkLWpvYiB0aW1lciBmb3IgdGhlIG5leHQgZnV0dXJlIGBzY2hlZHVsZWRfYXRfbXNgLiBDb2FsZXNjZXNcbiAgICogY29uY3VycmVudCB0cmlnZ2VyczogYSB3YWtlLXVwIHRoYXQgbGFuZHMgd2hpbGUgYSBkcmFpbiBpcyBpblxuICAgKiBmbGlnaHQganVzdCBzZXRzIGEgcmUtZHJhaW4gZmxhZyBhbmQgbGV0cyB0aGUgaW4tZmxpZ2h0IGRyYWluXG4gICAqIHJlLWxvb3AgYWZ0ZXIgaXQgZmluaXNoZXMsIHNvIG5vIHNpZ25hbCBpcyBkcm9wcGVkIGJ1dCBubyB0d29cbiAgICogZHJhaW5zIHJ1biBpbiBwYXJhbGxlbC5cbiAgICpcbiAgICogUmVzaWxpZW5jZTogaW4gYmVhY29uIG1vZGUgdGhpcyBpcyB0aGUgc29sZSB3YWtlLXVwIHBhdGggZm9yXG4gICAqIGFscmVhZHktcXVldWVkIHdvcmssIHNvIGEgdHJhbnNpZW50IERCIGVycm9yIGR1cmluZyB0aGUgZHJhaW4gKGUuZy5cbiAgICogYG5leHRBdmFpbGFibGVKb2IoKWAgcmVqZWN0aW5nKSBtdXN0IG5vdCBzdHJhbmQgdGhlIHF1ZXVlIHVudGlsIHRoZVxuICAgKiBuZXh0IGV4dGVybmFsIHNpZ25hbC4gT24gYW55IGVycm9yIHdlIGxvZyBpdCBhbmQgYXJtIGEgb25lLXNob3RcbiAgICogcmV0cnkgdmlhIGBfc2NoZWR1bGVFcnJvclJldHJ5YCB1c2luZyBgcG9sbEludGVydmFsTXNgIGFzIHRoZVxuICAgKiBjYWRlbmNlOyBvbiBzdWNjZXNzIHRoZSByZXRyeSB0aW1lciBpcyBjbGVhcmVkLiBQb2xsaW5nLW1vZGUgcnVuc1xuICAgKiBgX2RyYWluYCBmcm9tIGl0cyBvd24gaW50ZXJ2YWwsIHNvIHRoZSByZXRyeSB0aW1lciBpcyBhIG5vLW9wIHRoZXJlLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn1cbiAgICovXG4gIGFzeW5jIF9kcmFpbigpIHtcbiAgICBpZiAoIXRoaXMuX3N0YXJ0RHJhaW4oKSkgcmV0dXJuXG5cbiAgICBjb25zdCBlcnJvcmVkID0gYXdhaXQgdGhpcy5fZHJhaW5VbnRpbElkbGUoKVxuXG4gICAgYXdhaXQgdGhpcy5fZmluaXNoRHJhaW4oe2Vycm9yZWR9KVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgc3RhcnQgZHJhaW4uXG4gICAqIEByZXR1cm5zIHtib29sZWFufSAtIFdoZXRoZXIgdGhlIGRyYWluIHNob3VsZCBjb250aW51ZS5cbiAgICovXG4gIF9zdGFydERyYWluKCkge1xuICAgIGlmICh0aGlzLl9zdG9wcGVkKSByZXR1cm4gZmFsc2VcbiAgICBpZiAodGhpcy5fcXVldWVEcmFpbklmQWxyZWFkeVJ1bm5pbmcoKSkgcmV0dXJuIGZhbHNlXG5cbiAgICB0aGlzLl9kcmFpbmluZyA9IHRydWVcbiAgICByZXR1cm4gdHJ1ZVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgZmluaXNoIGRyYWluLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gYXJncy5lcnJvcmVkIC0gV2hldGhlciB0aGUgZHJhaW4gaGl0IGFuIGVycm9yLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyBhZnRlciBmb2xsb3ctdXAgdGltZXJzIGFyZSBoYW5kbGVkLlxuICAgKi9cbiAgYXN5bmMgX2ZpbmlzaERyYWluKHtlcnJvcmVkfSkge1xuICAgIGlmICh0aGlzLl9zdG9wcGVkKSByZXR1cm5cbiAgICBpZiAoZXJyb3JlZCkgcmV0dXJuIHRoaXMuX3NjaGVkdWxlRXJyb3JSZXRyeSgpXG5cbiAgICBhd2FpdCB0aGlzLl9hcm1TY2hlZHVsZWRUaW1lck9yUmV0cnkoKVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgYXJtIHNjaGVkdWxlZCB0aW1lciBvciByZXRyeS5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgYWZ0ZXIgc2NoZWR1bGVkIHRpbWVyIGhhbmRsaW5nLlxuICAgKi9cbiAgYXN5bmMgX2FybVNjaGVkdWxlZFRpbWVyT3JSZXRyeSgpIHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5fYXJtU2NoZWR1bGVkVGltZXIoKVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICB0aGlzLmxvZ2dlci5lcnJvcigoKSA9PiBbXCJCYWNrZ3JvdW5kIGpvYnMgc2NoZWR1bGVkLXRpbWVyIGFybWluZyBmYWlsZWQ6XCIsIGVycm9yXSlcbiAgICAgIHRoaXMuX3NjaGVkdWxlRXJyb3JSZXRyeSgpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICB0aGlzLl9jbGVhckVycm9yUmV0cnlUaW1lcigpXG4gIH1cblxuICAvKipcbiAgICogUnVucyBjbGVhciBlcnJvciByZXRyeSB0aW1lci5cbiAgICBAcmV0dXJucyB7dm9pZH0gKi9cbiAgX2NsZWFyRXJyb3JSZXRyeVRpbWVyKCkge1xuICAgIGlmICh0aGlzLl9lcnJvclJldHJ5VGltZXIpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aGlzLl9lcnJvclJldHJ5VGltZXIpXG4gICAgICB0aGlzLl9lcnJvclJldHJ5VGltZXIgPSB1bmRlZmluZWRcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUnVucyBxdWV1ZSBkcmFpbiBpZiBhbHJlYWR5IHJ1bm5pbmcuXG4gICAqIEByZXR1cm5zIHtib29sZWFufSAtIFdoZXRoZXIgYW5vdGhlciBkcmFpbiBpcyBhbHJlYWR5IGluIHByb2dyZXNzLlxuICAgKi9cbiAgX3F1ZXVlRHJhaW5JZkFscmVhZHlSdW5uaW5nKCkge1xuICAgIGlmICghdGhpcy5fZHJhaW5pbmcpIHJldHVybiBmYWxzZVxuXG4gICAgdGhpcy5fcmVkcmFpblF1ZXVlZCA9IHRydWVcbiAgICByZXR1cm4gdHJ1ZVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgZHJhaW4gdW50aWwgaWRsZS5cbiAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IC0gV2hldGhlciB0aGUgZHJhaW4gaGl0IGFuIGVycm9yLlxuICAgKi9cbiAgYXN5bmMgX2RyYWluVW50aWxJZGxlKCkge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gYXdhaXQgdGhpcy5fcnVuRHJhaW5Mb29wKClcbiAgICB9IGZpbmFsbHkge1xuICAgICAgdGhpcy5fZHJhaW5pbmcgPSBmYWxzZVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIHJ1biBkcmFpbiBsb29wLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gLSBXaGV0aGVyIHRoZSBkcmFpbiBoaXQgYW4gZXJyb3IuXG4gICAqL1xuICBhc3luYyBfcnVuRHJhaW5Mb29wKCkge1xuICAgIGRvIHtcbiAgICAgIHRoaXMuX3JlZHJhaW5RdWV1ZWQgPSBmYWxzZVxuICAgICAgY29uc3QgZXJyb3JlZCA9IGF3YWl0IHRoaXMuX2RyYWluT25jZVdpdGhFcnJvclJlcG9ydCgpXG5cbiAgICAgIGlmIChlcnJvcmVkKSByZXR1cm4gdHJ1ZVxuICAgIH0gd2hpbGUgKHRoaXMuX3JlZHJhaW5RdWV1ZWQgJiYgIXRoaXMuX3N0b3BwZWQpXG5cbiAgICByZXR1cm4gZmFsc2VcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGRyYWluIG9uY2Ugd2l0aCBlcnJvciByZXBvcnQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSAtIFdoZXRoZXIgb25lIGRyYWluIHBhc3MgZmFpbGVkLlxuICAgKi9cbiAgYXN5bmMgX2RyYWluT25jZVdpdGhFcnJvclJlcG9ydCgpIHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5fZHJhaW5PbmNlKClcbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICB0aGlzLmxvZ2dlci5lcnJvcigoKSA9PiBbXCJCYWNrZ3JvdW5kIGpvYnMgZHJhaW4gZmFpbGVkOlwiLCBlcnJvcl0pXG4gICAgICByZXR1cm4gdHJ1ZVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBBcm1zIGEgb25lLXNob3QgYHNldFRpbWVvdXRgIHRvIHJldHJ5IGBfZHJhaW5gIGFmdGVyIGEgdHJhbnNpZW50XG4gICAqIGZhaWx1cmUuIElkZW1wb3RlbnQg4oCUIHJlcGVhdGVkIGNhbGxzIHdoaWxlIGEgcmV0cnkgaXMgYWxyZWFkeVxuICAgKiBwZW5kaW5nIGFyZSBuby1vcHMuIFBvbGxpbmcgbW9kZSBhbHJlYWR5IHJldHJpZXMgdmlhIGl0cyBvd25cbiAgICogaW50ZXJ2YWwsIHNvIHRoaXMgaXMgYSBuby1vcCBpbiB0aGF0IG1vZGUuXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgX3NjaGVkdWxlRXJyb3JSZXRyeSgpIHtcbiAgICBpZiAodGhpcy5fc3RvcHBlZCkgcmV0dXJuXG4gICAgaWYgKHRoaXMuX2Vycm9yUmV0cnlUaW1lcikgcmV0dXJuXG4gICAgaWYgKHRoaXMuZGlzcGF0Y2hTdHJhdGVneSA9PT0gXCJwb2xsaW5nXCIpIHJldHVyblxuXG4gICAgdGhpcy5fZXJyb3JSZXRyeVRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICB0aGlzLl9lcnJvclJldHJ5VGltZXIgPSB1bmRlZmluZWRcbiAgICAgIHZvaWQgdGhpcy5fZHJhaW4oKVxuICAgIH0sIHRoaXMucG9sbEludGVydmFsTXMpXG4gIH1cblxuICAvKipcbiAgICogSW5uZXIgZHJhaW4gbG9vcDogcHVsbHMgZWxpZ2libGUgcXVldWVkIGpvYnMgYW5kIGhhbmRzIHRoZW0gb2ZmIHRvXG4gICAqIHJlYWR5IHdvcmtlcnMgdW50aWwgb25lIG9mIHRoZW0gcnVucyBvdXQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKi9cbiAgYXN5bmMgX2RyYWluT25jZSgpIHtcbiAgICB3aGlsZSAodGhpcy5yZWFkeVdvcmtlcnMuc2l6ZSA+IDAgJiYgIXRoaXMuX3N0b3BwZWQpIHtcbiAgICAgIGNvbnN0IGpvYiA9IGF3YWl0IHRoaXMubmV4dEF2YWlsYWJsZUpvYkZvclJlYWR5V29ya2VycygpXG4gICAgICBpZiAoIWpvYikgcmV0dXJuXG5cbiAgICAgIGNvbnN0IHdvcmtlciA9IHRoaXMucmVhZHlXb3JrZXJGb3JKb2Ioam9iKVxuICAgICAgaWYgKCF3b3JrZXIpIHJldHVyblxuXG4gICAgICB0aGlzLnJlYWR5V29ya2Vycy5kZWxldGUod29ya2VyKVxuXG4gICAgICBjb25zdCBoYW5kZWRPZmZBdE1zID0gYXdhaXQgdGhpcy5zdG9yZS5tYXJrSGFuZGVkT2ZmKHtqb2JJZDogam9iLmlkLCB3b3JrZXJJZDogd29ya2VyLndvcmtlcklkfSlcblxuICAgICAgdHJ5IHtcbiAgICAgICAgd29ya2VyLnNlbmQoe1xuICAgICAgICAgIHR5cGU6IFwiam9iXCIsXG4gICAgICAgICAgcGF5bG9hZDoge1xuICAgICAgICAgICAgaWQ6IGpvYi5pZCxcbiAgICAgICAgICAgIGpvYk5hbWU6IGpvYi5qb2JOYW1lLFxuICAgICAgICAgICAgYXJnczogam9iLmFyZ3MsXG4gICAgICAgICAgICB3b3JrZXJJZDogd29ya2VyLndvcmtlcklkLFxuICAgICAgICAgICAgaGFuZGVkT2ZmQXRNcyxcbiAgICAgICAgICAgIG9wdGlvbnM6IHtcbiAgICAgICAgICAgICAgZXhlY3V0aW9uTW9kZTogam9iLmV4ZWN1dGlvbk1vZGUsXG4gICAgICAgICAgICAgIGZvcmtlZDogam9iLmZvcmtlZFxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIHRoaXMubG9nZ2VyLndhcm4oKCkgPT4gW1wiRmFpbGVkIHRvIHNlbmQgam9iIHRvIHdvcmtlciwgcmUtcXVldWVpbmc6XCIsIGVycm9yXSlcbiAgICAgICAgYXdhaXQgdGhpcy5zdG9yZS5tYXJrUmV0dXJuZWRUb1F1ZXVlKHtqb2JJZDogam9iLmlkfSlcbiAgICAgICAgdGhpcy5yZWFkeVdvcmtlcnMuYWRkKHdvcmtlcilcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUnVucyBuZXh0IGF2YWlsYWJsZSBqb2IgZm9yIHJlYWR5IHdvcmtlcnMuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlJvdyB8IG51bGw+fSAtIE5leHQgcXVldWVkIGpvYiBtYXRjaGluZyByZWFkeSB3b3JrZXIgY2FwYWNpdHkuXG4gICAqL1xuICBhc3luYyBuZXh0QXZhaWxhYmxlSm9iRm9yUmVhZHlXb3JrZXJzKCkge1xuICAgIGNvbnN0IGV4ZWN1dGlvbk1vZGVzID0gdGhpcy5yZWFkeVdvcmtlckV4ZWN1dGlvbk1vZGVzKClcblxuICAgIGlmIChleGVjdXRpb25Nb2Rlcy5sZW5ndGggPT09IDApIHJldHVybiBudWxsXG4gICAgaWYgKGV4ZWN1dGlvbk1vZGVzLmxlbmd0aCA9PT0gMykgcmV0dXJuIGF3YWl0IHRoaXMuc3RvcmUubmV4dEF2YWlsYWJsZUpvYigpXG5cbiAgICByZXR1cm4gYXdhaXQgdGhpcy5zdG9yZS5uZXh0QXZhaWxhYmxlSm9iKHtleGVjdXRpb25Nb2RlOiBleGVjdXRpb25Nb2Rlc30pXG4gIH1cblxuICAvKipcbiAgICogUnVucyByZWFkeSB3b3JrZXIgZXhlY3V0aW9uIG1vZGVzLlxuICAgKiBAcmV0dXJucyB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iRXhlY3V0aW9uTW9kZVtdfSAtIEV4ZWN1dGlvbiBtb2RlcyBjdXJyZW50bHkgYWNjZXB0ZWQgYnkgcmVhZHkgd29ya2Vycy5cbiAgICovXG4gIHJlYWR5V29ya2VyRXhlY3V0aW9uTW9kZXMoKSB7XG4gICAgY29uc3QgZXhlY3V0aW9uTW9kZXMgPSBuZXcgU2V0KClcblxuICAgIGZvciAoY29uc3Qgd29ya2VyIG9mIHRoaXMucmVhZHlXb3JrZXJzKSB7XG4gICAgICB0aGlzLl9hZGRBY2NlcHRlZEV4ZWN1dGlvbk1vZGVzKHtleGVjdXRpb25Nb2Rlcywgd29ya2VyfSlcbiAgICB9XG5cbiAgICByZXR1cm4gLyoqIE5hcnJvd3MgdGhlIHJ1bnRpbWUgdmFsdWUgdG8gdGhlIGRvY3VtZW50ZWQgdHlwZS4gQHR5cGUge2ltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYkV4ZWN1dGlvbk1vZGVbXX0gKi8gKFsuLi5leGVjdXRpb25Nb2Rlc10pXG4gIH1cblxuICAvKipcbiAgICogUnVucyBhZGQgYWNjZXB0ZWQgZXhlY3V0aW9uIG1vZGVzLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7U2V0PGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYkV4ZWN1dGlvbk1vZGU+fSBhcmdzLmV4ZWN1dGlvbk1vZGVzIC0gQWNjZXB0ZWQgbW9kZXMuXG4gICAqIEBwYXJhbSB7SnNvblNvY2tldH0gYXJncy53b3JrZXIgLSBXb3JrZXIgc29ja2V0LlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIF9hZGRBY2NlcHRlZEV4ZWN1dGlvbk1vZGVzKHtleGVjdXRpb25Nb2Rlcywgd29ya2VyfSkge1xuICAgIGZvciAoY29uc3QgY2FwYWJpbGl0eSBvZiBXT1JLRVJfRVhFQ1VUSU9OX01PREVfQ0FQQUJJTElUSUVTKSB7XG4gICAgICBpZiAoY2FwYWJpbGl0eS5hY2NlcHRzKHdvcmtlcikpIGV4ZWN1dGlvbk1vZGVzLmFkZChjYXBhYmlsaXR5LmV4ZWN1dGlvbk1vZGUpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgcmVhZHkgd29ya2VyIGZvciBqb2IuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iUm93fSBqb2IgLSBKb2IgYmVpbmcgaGFuZGVkIG9mZi5cbiAgICogQHJldHVybnMge0pzb25Tb2NrZXQgfCB1bmRlZmluZWR9IC0gUmVhZHkgd29ya2VyIGZvciB0aGUgam9iIHR5cGUuXG4gICAqL1xuICByZWFkeVdvcmtlckZvckpvYihqb2IpIHtcbiAgICBmb3IgKGNvbnN0IHdvcmtlciBvZiB0aGlzLnJlYWR5V29ya2Vycykge1xuICAgICAgaWYgKHRoaXMuX3dvcmtlckFjY2VwdHNKb2Ioe2pvYiwgd29ya2VyfSkpIHJldHVybiB3b3JrZXJcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUnVucyB3b3JrZXIgYWNjZXB0cyBqb2IuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJhY2tncm91bmRKb2JSb3d9IGFyZ3Muam9iIC0gSm9iIGJlaW5nIGhhbmRlZCBvZmYuXG4gICAqIEBwYXJhbSB7SnNvblNvY2tldH0gYXJncy53b3JrZXIgLSBXb3JrZXIgc29ja2V0LlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBXaGV0aGVyIHRoZSB3b3JrZXIgYWNjZXB0cyB0aGUgam9iIG1vZGUuXG4gICAqL1xuICBfd29ya2VyQWNjZXB0c0pvYih7am9iLCB3b3JrZXJ9KSB7XG4gICAgY29uc3QgY2FwYWJpbGl0eSA9IFdPUktFUl9FWEVDVVRJT05fTU9ERV9DQVBBQklMSVRJRVNfQllfTU9ERS5nZXQoam9iLmV4ZWN1dGlvbk1vZGUpXG5cbiAgICBpZiAoIWNhcGFiaWxpdHkpIHJldHVybiBmYWxzZVxuXG4gICAgcmV0dXJuIGNhcGFiaWxpdHkuYWNjZXB0cyh3b3JrZXIpXG4gIH1cblxuICAvKipcbiAgICogQXJtcyBhIHNpbmdsZSBgc2V0VGltZW91dGAgZm9yIHRoZSBzb29uZXN0IGZ1dHVyZS1zY2hlZHVsZWQgam9iJ3NcbiAgICogYHNjaGVkdWxlZF9hdF9tc2AuIFJlcGxhY2VzIHRoZSBzZWNvbmQgcmVzcG9uc2liaWxpdHkgb2YgdGhlIGxlZ2FjeVxuICAgKiAxLXNlY29uZCBwb2xsIChiZWNvbWluZy1lbGlnaWJsZSBzY2hlZHVsZWQgam9icykuIFRoZSB0aW1lciBpc1xuICAgKiBpZGVtcG90ZW50bHkgcmUtYXJtZWQgYXQgdGhlIGVuZCBvZiBldmVyeSBkcmFpbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqL1xuICBhc3luYyBfYXJtU2NoZWR1bGVkVGltZXIoKSB7XG4gICAgaWYgKHRoaXMuX3NjaGVkdWxlZFRpbWVyKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5fc2NoZWR1bGVkVGltZXIpXG4gICAgICB0aGlzLl9zY2hlZHVsZWRUaW1lciA9IHVuZGVmaW5lZFxuICAgIH1cblxuICAgIGlmICh0aGlzLl9zdG9wcGVkKSByZXR1cm5cbiAgICBpZiAodGhpcy5kaXNwYXRjaFN0cmF0ZWd5ID09PSBcInBvbGxpbmdcIikgcmV0dXJuXG5cbiAgICBjb25zdCBuZXh0ID0gYXdhaXQgdGhpcy5zdG9yZS5uZXh0U2NoZWR1bGVkSm9iKClcbiAgICBpZiAoIW5leHQgfHwgdHlwZW9mIG5leHQuc2NoZWR1bGVkQXRNcyAhPT0gXCJudW1iZXJcIikgcmV0dXJuXG5cbiAgICBjb25zdCBkZWxheSA9IE1hdGgubWF4KDAsIE1hdGgubWluKG5leHQuc2NoZWR1bGVkQXRNcyAtIERhdGUubm93KCksIE1BWF9USU1FUl9NUykpXG5cbiAgICB0aGlzLl9zY2hlZHVsZWRUaW1lciA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgdGhpcy5fc2NoZWR1bGVkVGltZXIgPSB1bmRlZmluZWRcbiAgICAgIHZvaWQgdGhpcy5fZHJhaW4oKVxuICAgIH0sIGRlbGF5KVxuICB9XG5cbiAgYXN5bmMgX3N3ZWVwT3JwaGFucygpIHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgY291bnQgPSBhd2FpdCB0aGlzLnN0b3JlLm1hcmtPcnBoYW5lZEpvYnMoKVxuXG4gICAgICBpZiAoY291bnQgPiAwKSB7XG4gICAgICAgIHRoaXMubG9nZ2VyLndhcm4oKCkgPT4gW1wiTWFya2VkIG9ycGhhbmVkIGJhY2tncm91bmQgam9ic1wiLCBjb3VudF0pXG4gICAgICAgIC8vIFJlY2xhaW1lZCBvcnBoYW5zIGJlY29tZSBgcXVldWVkYCBhZ2FpbiDigJQgd2FrZSB0aGUgZGlzcGF0Y2hlclxuICAgICAgICAvLyBzbyB0aGV5IGFyZW4ndCBzdHJhbmRlZCB1bnRpbCB0aGUgbmV4dCBleHRlcm5hbCBzaWduYWwuXG4gICAgICAgIHRoaXMuX25vdGlmeUVucXVldWVkKClcbiAgICAgICAgYXdhaXQgdGhpcy5fZHJhaW4oKVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICB0aGlzLmxvZ2dlci5lcnJvcigoKSA9PiBbXCJGYWlsZWQgdG8gbWFyayBvcnBoYW5lZCBqb2JzOlwiLCBlcnJvcl0pXG4gICAgfVxuICB9XG59XG4iXX0=