@venizia/ignis 0.2.0-4 → 0.2.0-40

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 (361) hide show
  1. package/dist/base/applications/base.d.ts +19 -13
  2. package/dist/base/applications/base.d.ts.map +1 -1
  3. package/dist/base/applications/base.js +85 -65
  4. package/dist/base/applications/base.js.map +1 -1
  5. package/dist/base/applications/boot-steps.d.ts +13 -0
  6. package/dist/base/applications/boot-steps.d.ts.map +1 -0
  7. package/dist/base/applications/boot-steps.js +23 -0
  8. package/dist/base/applications/boot-steps.js.map +1 -0
  9. package/dist/base/applications/common/index.d.ts +2 -0
  10. package/dist/base/applications/common/index.d.ts.map +1 -0
  11. package/dist/base/applications/common/index.js +18 -0
  12. package/dist/base/applications/common/index.js.map +1 -0
  13. package/dist/base/applications/{types.d.ts → common/types.d.ts} +11 -0
  14. package/dist/base/applications/common/types.d.ts.map +1 -0
  15. package/dist/base/applications/common/types.js.map +1 -0
  16. package/dist/base/applications/index.d.ts +2 -1
  17. package/dist/base/applications/index.d.ts.map +1 -1
  18. package/dist/base/applications/index.js +2 -1
  19. package/dist/base/applications/index.js.map +1 -1
  20. package/dist/base/applications/server.d.ts +4 -2
  21. package/dist/base/applications/server.d.ts.map +1 -1
  22. package/dist/base/applications/server.js +25 -2
  23. package/dist/base/applications/server.js.map +1 -1
  24. package/dist/base/controllers/grpc/adapter.d.ts +2 -1
  25. package/dist/base/controllers/grpc/adapter.d.ts.map +1 -1
  26. package/dist/base/controllers/grpc/adapter.js +4 -3
  27. package/dist/base/controllers/grpc/adapter.js.map +1 -1
  28. package/dist/base/middlewares/app-error/app-error.middleware.d.ts +3 -2
  29. package/dist/base/middlewares/app-error/app-error.middleware.d.ts.map +1 -1
  30. package/dist/base/middlewares/app-error/app-error.middleware.js +4 -3
  31. package/dist/base/middlewares/app-error/app-error.middleware.js.map +1 -1
  32. package/dist/base/middlewares/request-spy/request-spy.middleware.d.ts.map +1 -1
  33. package/dist/base/middlewares/request-spy/request-spy.middleware.js +2 -1
  34. package/dist/base/middlewares/request-spy/request-spy.middleware.js.map +1 -1
  35. package/dist/common/constants.d.ts.map +1 -1
  36. package/dist/common/constants.js +2 -1
  37. package/dist/common/constants.js.map +1 -1
  38. package/dist/common/environments.d.ts +31 -10
  39. package/dist/common/environments.d.ts.map +1 -1
  40. package/dist/common/environments.js +33 -10
  41. package/dist/common/environments.js.map +1 -1
  42. package/dist/components/api-reference/component.js +2 -2
  43. package/dist/components/api-reference/component.js.map +1 -1
  44. package/dist/components/api-reference/index.d.ts +1 -1
  45. package/dist/components/api-reference/index.d.ts.map +1 -1
  46. package/dist/components/api-reference/index.js +1 -1
  47. package/dist/components/api-reference/index.js.map +1 -1
  48. package/dist/components/api-reference/ui/factory.d.ts +14 -0
  49. package/dist/components/api-reference/ui/factory.d.ts.map +1 -0
  50. package/dist/components/api-reference/{ui-factory.js → ui/factory.js} +12 -25
  51. package/dist/components/api-reference/ui/factory.js.map +1 -0
  52. package/dist/components/api-reference/ui/index.d.ts +4 -0
  53. package/dist/components/api-reference/ui/index.d.ts.map +1 -0
  54. package/dist/components/api-reference/ui/index.js +20 -0
  55. package/dist/components/api-reference/ui/index.js.map +1 -0
  56. package/dist/components/api-reference/ui/scalar.d.ts +8 -0
  57. package/dist/components/api-reference/ui/scalar.d.ts.map +1 -0
  58. package/dist/components/api-reference/ui/scalar.js +28 -0
  59. package/dist/components/api-reference/ui/scalar.js.map +1 -0
  60. package/dist/components/api-reference/ui/swagger.d.ts +8 -0
  61. package/dist/components/api-reference/ui/swagger.d.ts.map +1 -0
  62. package/dist/components/api-reference/ui/swagger.js +28 -0
  63. package/dist/components/api-reference/ui/swagger.js.map +1 -0
  64. package/dist/components/auth/authenticate/controllers/factory.d.ts +33 -2
  65. package/dist/components/auth/authenticate/controllers/factory.d.ts.map +1 -1
  66. package/dist/components/auth/authenticate/services/service/verifier.service.d.ts.map +1 -1
  67. package/dist/components/auth/authenticate/services/service/verifier.service.js +10 -6
  68. package/dist/components/auth/authenticate/services/service/verifier.service.js.map +1 -1
  69. package/dist/components/auth/authenticate/strategies/index.d.ts +1 -1
  70. package/dist/components/auth/authenticate/strategies/index.d.ts.map +1 -1
  71. package/dist/components/auth/authenticate/strategies/index.js +1 -1
  72. package/dist/components/auth/authenticate/strategies/index.js.map +1 -1
  73. package/dist/components/auth/authenticate/strategies/jwks/index.d.ts +3 -0
  74. package/dist/components/auth/authenticate/strategies/jwks/index.d.ts.map +1 -0
  75. package/dist/components/auth/authenticate/strategies/jwks/index.js +19 -0
  76. package/dist/components/auth/authenticate/strategies/jwks/index.js.map +1 -0
  77. package/dist/components/auth/authenticate/strategies/jwks/issuer.d.ts +13 -0
  78. package/dist/components/auth/authenticate/strategies/jwks/issuer.d.ts.map +1 -0
  79. package/dist/components/auth/authenticate/strategies/{jwks.strategy.js → jwks/issuer.js} +4 -27
  80. package/dist/components/auth/authenticate/strategies/jwks/issuer.js.map +1 -0
  81. package/dist/components/auth/authenticate/strategies/{jwks.strategy.d.ts → jwks/verifier.d.ts} +2 -10
  82. package/dist/components/auth/authenticate/strategies/jwks/verifier.d.ts.map +1 -0
  83. package/dist/components/auth/authenticate/strategies/jwks/verifier.js +45 -0
  84. package/dist/components/auth/authenticate/strategies/jwks/verifier.js.map +1 -0
  85. package/dist/components/auth/authorize/adapters/base-filtered.d.ts +1 -1
  86. package/dist/components/auth/authorize/adapters/base-filtered.d.ts.map +1 -1
  87. package/dist/components/auth/authorize/adapters/base-filtered.js +5 -8
  88. package/dist/components/auth/authorize/adapters/base-filtered.js.map +1 -1
  89. package/dist/components/auth/authorize/adapters/common/index.d.ts +2 -0
  90. package/dist/components/auth/authorize/adapters/common/index.d.ts.map +1 -0
  91. package/dist/components/auth/authorize/adapters/common/index.js +18 -0
  92. package/dist/components/auth/authorize/adapters/common/index.js.map +1 -0
  93. package/dist/components/auth/authorize/adapters/{types.d.ts → common/types.d.ts} +1 -1
  94. package/dist/components/auth/authorize/adapters/common/types.d.ts.map +1 -0
  95. package/dist/components/auth/authorize/adapters/common/types.js.map +1 -0
  96. package/dist/components/auth/authorize/adapters/connector.d.ts +10 -0
  97. package/dist/components/auth/authorize/adapters/connector.d.ts.map +1 -0
  98. package/dist/components/auth/authorize/adapters/connector.js +20 -0
  99. package/dist/components/auth/authorize/adapters/connector.js.map +1 -0
  100. package/dist/components/auth/authorize/adapters/custom-grant-expander.d.ts +52 -0
  101. package/dist/components/auth/authorize/adapters/custom-grant-expander.d.ts.map +1 -0
  102. package/dist/components/auth/authorize/adapters/custom-grant-expander.js +111 -0
  103. package/dist/components/auth/authorize/adapters/custom-grant-expander.js.map +1 -0
  104. package/dist/components/auth/authorize/adapters/index.d.ts +4 -2
  105. package/dist/components/auth/authorize/adapters/index.d.ts.map +1 -1
  106. package/dist/components/auth/authorize/adapters/index.js +4 -2
  107. package/dist/components/auth/authorize/adapters/index.js.map +1 -1
  108. package/dist/components/auth/authorize/adapters/scoped-casbin/adapter.d.ts +93 -0
  109. package/dist/components/auth/authorize/adapters/scoped-casbin/adapter.d.ts.map +1 -0
  110. package/dist/components/auth/authorize/adapters/{scoped-casbin.adapter.js → scoped-casbin/adapter.js} +95 -97
  111. package/dist/components/auth/authorize/adapters/scoped-casbin/adapter.js.map +1 -0
  112. package/dist/components/auth/authorize/adapters/scoped-casbin/common/constants.d.ts +2 -0
  113. package/dist/components/auth/authorize/adapters/scoped-casbin/common/constants.d.ts.map +1 -0
  114. package/dist/components/auth/authorize/adapters/scoped-casbin/common/constants.js +5 -0
  115. package/dist/components/auth/authorize/adapters/scoped-casbin/common/constants.js.map +1 -0
  116. package/dist/components/auth/authorize/adapters/scoped-casbin/common/index.d.ts +3 -0
  117. package/dist/components/auth/authorize/adapters/scoped-casbin/common/index.d.ts.map +1 -0
  118. package/dist/components/auth/authorize/adapters/scoped-casbin/common/index.js +19 -0
  119. package/dist/components/auth/authorize/adapters/scoped-casbin/common/index.js.map +1 -0
  120. package/dist/components/auth/authorize/adapters/scoped-casbin/common/types.d.ts +50 -0
  121. package/dist/components/auth/authorize/adapters/scoped-casbin/common/types.d.ts.map +1 -0
  122. package/dist/components/auth/authorize/adapters/scoped-casbin/common/types.js +3 -0
  123. package/dist/components/auth/authorize/adapters/scoped-casbin/common/types.js.map +1 -0
  124. package/dist/components/auth/authorize/adapters/scoped-casbin/index.d.ts +4 -0
  125. package/dist/components/auth/authorize/adapters/scoped-casbin/index.d.ts.map +1 -0
  126. package/dist/components/auth/authorize/adapters/scoped-casbin/index.js +19 -0
  127. package/dist/components/auth/authorize/adapters/scoped-casbin/index.js.map +1 -0
  128. package/dist/components/auth/authorize/adapters/scoped-casbin/policy-edges.d.ts +17 -0
  129. package/dist/components/auth/authorize/adapters/scoped-casbin/policy-edges.d.ts.map +1 -0
  130. package/dist/components/auth/authorize/adapters/scoped-casbin/policy-edges.js +11 -0
  131. package/dist/components/auth/authorize/adapters/scoped-casbin/policy-edges.js.map +1 -0
  132. package/dist/components/auth/authorize/component.d.ts.map +1 -1
  133. package/dist/components/auth/authorize/component.js +3 -0
  134. package/dist/components/auth/authorize/component.js.map +1 -1
  135. package/dist/components/auth/authorize/enforcers/casbin.enforcer.d.ts +7 -36
  136. package/dist/components/auth/authorize/enforcers/casbin.enforcer.d.ts.map +1 -1
  137. package/dist/components/auth/authorize/enforcers/casbin.enforcer.js +48 -117
  138. package/dist/components/auth/authorize/enforcers/casbin.enforcer.js.map +1 -1
  139. package/dist/components/auth/authorize/enforcers/index.d.ts +2 -1
  140. package/dist/components/auth/authorize/enforcers/index.d.ts.map +1 -1
  141. package/dist/components/auth/authorize/enforcers/index.js +2 -1
  142. package/dist/components/auth/authorize/enforcers/index.js.map +1 -1
  143. package/dist/components/auth/authorize/enforcers/policy-line-codec.d.ts +26 -0
  144. package/dist/components/auth/authorize/enforcers/policy-line-codec.d.ts.map +1 -0
  145. package/dist/components/auth/authorize/enforcers/policy-line-codec.js +57 -0
  146. package/dist/components/auth/authorize/enforcers/policy-line-codec.js.map +1 -0
  147. package/dist/components/auth/authorize/enforcers/user-policy-line-cache.d.ts +46 -0
  148. package/dist/components/auth/authorize/enforcers/user-policy-line-cache.d.ts.map +1 -0
  149. package/dist/components/auth/authorize/enforcers/user-policy-line-cache.js +98 -0
  150. package/dist/components/auth/authorize/enforcers/user-policy-line-cache.js.map +1 -0
  151. package/dist/components/auth/authorize/index.d.ts +1 -0
  152. package/dist/components/auth/authorize/index.d.ts.map +1 -1
  153. package/dist/components/auth/authorize/index.js +1 -0
  154. package/dist/components/auth/authorize/index.js.map +1 -1
  155. package/dist/components/auth/authorize/role-managers/base.d.ts +34 -0
  156. package/dist/components/auth/authorize/role-managers/base.d.ts.map +1 -0
  157. package/dist/components/auth/authorize/role-managers/base.js +76 -0
  158. package/dist/components/auth/authorize/role-managers/base.js.map +1 -0
  159. package/dist/components/auth/authorize/role-managers/domain-hierarchy.d.ts +28 -0
  160. package/dist/components/auth/authorize/role-managers/domain-hierarchy.d.ts.map +1 -0
  161. package/dist/components/auth/authorize/role-managers/domain-hierarchy.js +77 -0
  162. package/dist/components/auth/authorize/role-managers/domain-hierarchy.js.map +1 -0
  163. package/dist/components/auth/authorize/role-managers/index.d.ts +5 -0
  164. package/dist/components/auth/authorize/role-managers/index.d.ts.map +1 -0
  165. package/dist/components/auth/authorize/role-managers/index.js +21 -0
  166. package/dist/components/auth/authorize/role-managers/index.js.map +1 -0
  167. package/dist/components/auth/authorize/role-managers/membership.d.ts +21 -0
  168. package/dist/components/auth/authorize/role-managers/membership.d.ts.map +1 -0
  169. package/dist/components/auth/authorize/role-managers/membership.js +69 -0
  170. package/dist/components/auth/authorize/role-managers/membership.js.map +1 -0
  171. package/dist/components/auth/authorize/{enforcers/resource-role-manager.d.ts → role-managers/resource.d.ts} +3 -9
  172. package/dist/components/auth/authorize/role-managers/resource.d.ts.map +1 -0
  173. package/dist/components/auth/authorize/{enforcers/resource-role-manager.js → role-managers/resource.js} +9 -21
  174. package/dist/components/auth/authorize/role-managers/resource.js.map +1 -0
  175. package/dist/components/auth/models/entities/policy-definition.model.d.ts +49 -14
  176. package/dist/components/auth/models/entities/policy-definition.model.d.ts.map +1 -1
  177. package/dist/components/auth/models/entities/policy-definition.model.js +45 -11
  178. package/dist/components/auth/models/entities/policy-definition.model.js.map +1 -1
  179. package/dist/components/auth/models/entities/user.model.d.ts +2 -2
  180. package/dist/components/health-check/common/constants.d.ts +5 -0
  181. package/dist/components/health-check/common/constants.d.ts.map +1 -0
  182. package/dist/components/health-check/common/constants.js +9 -0
  183. package/dist/components/health-check/common/constants.js.map +1 -0
  184. package/dist/components/health-check/common/index.d.ts +1 -0
  185. package/dist/components/health-check/common/index.d.ts.map +1 -1
  186. package/dist/components/health-check/common/index.js +1 -0
  187. package/dist/components/health-check/common/index.js.map +1 -1
  188. package/dist/components/health-check/common/keys.d.ts +2 -0
  189. package/dist/components/health-check/common/keys.d.ts.map +1 -1
  190. package/dist/components/health-check/common/keys.js +2 -0
  191. package/dist/components/health-check/common/keys.js.map +1 -1
  192. package/dist/components/health-check/common/rest-paths.d.ts +1 -0
  193. package/dist/components/health-check/common/rest-paths.d.ts.map +1 -1
  194. package/dist/components/health-check/common/rest-paths.js +1 -0
  195. package/dist/components/health-check/common/rest-paths.js.map +1 -1
  196. package/dist/components/health-check/common/types.d.ts +22 -0
  197. package/dist/components/health-check/common/types.d.ts.map +1 -1
  198. package/dist/components/health-check/component.d.ts +9 -3
  199. package/dist/components/health-check/component.d.ts.map +1 -1
  200. package/dist/components/health-check/component.js +38 -1
  201. package/dist/components/health-check/component.js.map +1 -1
  202. package/dist/components/health-check/controller.d.ts +10 -2
  203. package/dist/components/health-check/controller.d.ts.map +1 -1
  204. package/dist/components/health-check/controller.js +79 -13
  205. package/dist/components/health-check/controller.js.map +1 -1
  206. package/dist/components/health-check/index.d.ts +1 -0
  207. package/dist/components/health-check/index.d.ts.map +1 -1
  208. package/dist/components/health-check/index.js +1 -0
  209. package/dist/components/health-check/index.js.map +1 -1
  210. package/dist/components/health-check/reporter.d.ts +98 -0
  211. package/dist/components/health-check/reporter.d.ts.map +1 -0
  212. package/dist/components/health-check/reporter.js +138 -0
  213. package/dist/components/health-check/reporter.js.map +1 -0
  214. package/dist/components/mail/common/constants.d.ts +8 -0
  215. package/dist/components/mail/common/constants.d.ts.map +1 -1
  216. package/dist/components/mail/common/constants.js +9 -1
  217. package/dist/components/mail/common/constants.js.map +1 -1
  218. package/dist/components/mail/common/types/index.d.ts +6 -0
  219. package/dist/components/mail/common/types/index.d.ts.map +1 -0
  220. package/dist/components/mail/common/types/index.js +22 -0
  221. package/dist/components/mail/common/types/index.js.map +1 -0
  222. package/dist/components/mail/common/types/message.d.ts +49 -0
  223. package/dist/components/mail/common/types/message.d.ts.map +1 -0
  224. package/dist/components/mail/common/types/message.js +3 -0
  225. package/dist/components/mail/common/types/message.js.map +1 -0
  226. package/dist/components/mail/common/types/options.d.ts +65 -0
  227. package/dist/components/mail/common/types/options.d.ts.map +1 -0
  228. package/dist/components/mail/common/types/options.js +3 -0
  229. package/dist/components/mail/common/types/options.js.map +1 -0
  230. package/dist/components/mail/common/types/queue.d.ts +53 -0
  231. package/dist/components/mail/common/types/queue.d.ts.map +1 -0
  232. package/dist/components/mail/common/types/queue.js +3 -0
  233. package/dist/components/mail/common/types/queue.js.map +1 -0
  234. package/dist/components/mail/common/types/template.d.ts +33 -0
  235. package/dist/components/mail/common/types/template.d.ts.map +1 -0
  236. package/dist/components/mail/common/types/template.js +3 -0
  237. package/dist/components/mail/common/types/template.js.map +1 -0
  238. package/dist/components/mail/common/types/verification.d.ts +26 -0
  239. package/dist/components/mail/common/types/verification.d.ts.map +1 -0
  240. package/dist/components/mail/common/types/verification.js +3 -0
  241. package/dist/components/mail/common/types/verification.js.map +1 -0
  242. package/dist/components/mail/component.d.ts +4 -1
  243. package/dist/components/mail/component.d.ts.map +1 -1
  244. package/dist/components/mail/component.js +12 -0
  245. package/dist/components/mail/component.js.map +1 -1
  246. package/dist/components/mail/helpers/transporters/amazon-ses-transporter.helper.d.ts +19 -0
  247. package/dist/components/mail/helpers/transporters/amazon-ses-transporter.helper.d.ts.map +1 -0
  248. package/dist/components/mail/helpers/transporters/amazon-ses-transporter.helper.js +111 -0
  249. package/dist/components/mail/helpers/transporters/amazon-ses-transporter.helper.js.map +1 -0
  250. package/dist/components/mail/helpers/transporters/index.d.ts +2 -1
  251. package/dist/components/mail/helpers/transporters/index.d.ts.map +1 -1
  252. package/dist/components/mail/helpers/transporters/index.js +2 -1
  253. package/dist/components/mail/helpers/transporters/index.js.map +1 -1
  254. package/dist/components/mail/providers/mail-transporter.provider.d.ts +2 -0
  255. package/dist/components/mail/providers/mail-transporter.provider.d.ts.map +1 -1
  256. package/dist/components/mail/providers/mail-transporter.provider.js +17 -0
  257. package/dist/components/mail/providers/mail-transporter.provider.js.map +1 -1
  258. package/dist/components/mail/services/generators/index.d.ts +4 -0
  259. package/dist/components/mail/services/generators/index.d.ts.map +1 -0
  260. package/dist/components/mail/services/generators/index.js +20 -0
  261. package/dist/components/mail/services/generators/index.js.map +1 -0
  262. package/dist/components/mail/services/generators/numeric-code.d.ts +7 -0
  263. package/dist/components/mail/services/generators/numeric-code.d.ts.map +1 -0
  264. package/dist/components/mail/services/generators/numeric-code.js +20 -0
  265. package/dist/components/mail/services/generators/numeric-code.js.map +1 -0
  266. package/dist/components/mail/services/generators/random-token.d.ts +7 -0
  267. package/dist/components/mail/services/generators/random-token.d.ts.map +1 -0
  268. package/dist/components/mail/services/generators/random-token.js +18 -0
  269. package/dist/components/mail/services/generators/random-token.js.map +1 -0
  270. package/dist/components/mail/services/generators/verification-data.d.ts +9 -0
  271. package/dist/components/mail/services/generators/verification-data.d.ts.map +1 -0
  272. package/dist/components/mail/services/{generator.service.js → generators/verification-data.js} +10 -25
  273. package/dist/components/mail/services/generators/verification-data.js.map +1 -0
  274. package/dist/components/mail/services/index.d.ts +1 -1
  275. package/dist/components/mail/services/index.d.ts.map +1 -1
  276. package/dist/components/mail/services/index.js +1 -1
  277. package/dist/components/mail/services/index.js.map +1 -1
  278. package/dist/components/mail/utilities/address.utility.d.ts +9 -0
  279. package/dist/components/mail/utilities/address.utility.d.ts.map +1 -0
  280. package/dist/components/mail/utilities/address.utility.js +36 -0
  281. package/dist/components/mail/utilities/address.utility.js.map +1 -0
  282. package/dist/components/mail/utilities/index.d.ts +2 -0
  283. package/dist/components/mail/utilities/index.d.ts.map +1 -1
  284. package/dist/components/mail/utilities/index.js +2 -0
  285. package/dist/components/mail/utilities/index.js.map +1 -1
  286. package/dist/components/mail/utilities/mime.utility.d.ts +19 -0
  287. package/dist/components/mail/utilities/mime.utility.d.ts.map +1 -0
  288. package/dist/components/mail/utilities/mime.utility.js +412 -0
  289. package/dist/components/mail/utilities/mime.utility.js.map +1 -0
  290. package/dist/components/static-asset/common/commit-token.d.ts +23 -0
  291. package/dist/components/static-asset/common/commit-token.d.ts.map +1 -0
  292. package/dist/components/static-asset/common/commit-token.js +40 -0
  293. package/dist/components/static-asset/common/commit-token.js.map +1 -0
  294. package/dist/components/static-asset/common/constants.d.ts +17 -1
  295. package/dist/components/static-asset/common/constants.d.ts.map +1 -1
  296. package/dist/components/static-asset/common/constants.js +33 -2
  297. package/dist/components/static-asset/common/constants.js.map +1 -1
  298. package/dist/components/static-asset/common/errors.d.ts +34 -0
  299. package/dist/components/static-asset/common/errors.d.ts.map +1 -1
  300. package/dist/components/static-asset/common/errors.js +28 -0
  301. package/dist/components/static-asset/common/errors.js.map +1 -1
  302. package/dist/components/static-asset/common/index.d.ts +1 -0
  303. package/dist/components/static-asset/common/index.d.ts.map +1 -1
  304. package/dist/components/static-asset/common/index.js +1 -0
  305. package/dist/components/static-asset/common/index.js.map +1 -1
  306. package/dist/components/static-asset/common/keys.d.ts +1 -0
  307. package/dist/components/static-asset/common/keys.d.ts.map +1 -1
  308. package/dist/components/static-asset/common/keys.js +1 -0
  309. package/dist/components/static-asset/common/keys.js.map +1 -1
  310. package/dist/components/static-asset/common/types.d.ts +87 -30
  311. package/dist/components/static-asset/common/types.d.ts.map +1 -1
  312. package/dist/components/static-asset/component.d.ts +4 -1
  313. package/dist/components/static-asset/component.d.ts.map +1 -1
  314. package/dist/components/static-asset/component.js +19 -9
  315. package/dist/components/static-asset/component.js.map +1 -1
  316. package/dist/components/static-asset/controller/base.definition.d.ts +27 -554
  317. package/dist/components/static-asset/controller/base.definition.d.ts.map +1 -1
  318. package/dist/components/static-asset/controller/base.definition.js +250 -285
  319. package/dist/components/static-asset/controller/base.definition.js.map +1 -1
  320. package/dist/components/static-asset/controller/factory.d.ts +71 -6
  321. package/dist/components/static-asset/controller/factory.d.ts.map +1 -1
  322. package/dist/components/static-asset/controller/factory.js +578 -138
  323. package/dist/components/static-asset/controller/factory.js.map +1 -1
  324. package/dist/components/static-asset/index.d.ts +1 -0
  325. package/dist/components/static-asset/index.d.ts.map +1 -1
  326. package/dist/components/static-asset/index.js +1 -0
  327. package/dist/components/static-asset/index.js.map +1 -1
  328. package/dist/components/static-asset/ingest.d.ts +58 -0
  329. package/dist/components/static-asset/ingest.d.ts.map +1 -0
  330. package/dist/components/static-asset/ingest.js +82 -0
  331. package/dist/components/static-asset/ingest.js.map +1 -0
  332. package/dist/components/static-asset/models/base.model.d.ts +48 -6
  333. package/dist/components/static-asset/models/base.model.d.ts.map +1 -1
  334. package/dist/components/static-asset/models/base.model.js +12 -1
  335. package/dist/components/static-asset/models/base.model.js.map +1 -1
  336. package/dist/index.d.ts +1 -1
  337. package/dist/index.d.ts.map +1 -1
  338. package/package.json +27 -23
  339. package/dist/base/applications/types.d.ts.map +0 -1
  340. package/dist/base/applications/types.js.map +0 -1
  341. package/dist/components/api-reference/ui-factory.d.ts +0 -21
  342. package/dist/components/api-reference/ui-factory.d.ts.map +0 -1
  343. package/dist/components/api-reference/ui-factory.js.map +0 -1
  344. package/dist/components/auth/authenticate/strategies/jwks.strategy.d.ts.map +0 -1
  345. package/dist/components/auth/authenticate/strategies/jwks.strategy.js.map +0 -1
  346. package/dist/components/auth/authorize/adapters/scoped-casbin.adapter.d.ts +0 -121
  347. package/dist/components/auth/authorize/adapters/scoped-casbin.adapter.d.ts.map +0 -1
  348. package/dist/components/auth/authorize/adapters/scoped-casbin.adapter.js.map +0 -1
  349. package/dist/components/auth/authorize/adapters/types.d.ts.map +0 -1
  350. package/dist/components/auth/authorize/adapters/types.js.map +0 -1
  351. package/dist/components/auth/authorize/enforcers/resource-role-manager.d.ts.map +0 -1
  352. package/dist/components/auth/authorize/enforcers/resource-role-manager.js.map +0 -1
  353. package/dist/components/mail/common/types.d.ts +0 -201
  354. package/dist/components/mail/common/types.d.ts.map +0 -1
  355. package/dist/components/mail/common/types.js +0 -10
  356. package/dist/components/mail/common/types.js.map +0 -1
  357. package/dist/components/mail/services/generator.service.d.ts +0 -14
  358. package/dist/components/mail/services/generator.service.d.ts.map +0 -1
  359. package/dist/components/mail/services/generator.service.js.map +0 -1
  360. /package/dist/base/applications/{types.js → common/types.js} +0 -0
  361. /package/dist/components/auth/authorize/adapters/{types.js → common/types.js} +0 -0
@@ -9,22 +9,131 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.AssetControllerFactory = void 0;
12
+ exports.AssetControllerFactory = exports.resolveServedContentType = exports.buildObjectLink = exports.findLabelsInQuery = void 0;
13
13
  const controllers_1 = require("../../../base/controllers");
14
14
  const metadata_1 = require("../../../base/metadata");
15
- const core_1 = require("@venizia/ignis-helpers/core");
16
- const common_1 = require("@venizia/ignis-helpers/common");
17
15
  const ignis_helpers_1 = require("@venizia/ignis-helpers");
16
+ const common_1 = require("@venizia/ignis-helpers/common");
17
+ const core_1 = require("@venizia/ignis-helpers/core");
18
18
  const node_fs_1 = require("node:fs");
19
19
  const common_2 = require("../common");
20
+ const common_3 = require("../common");
21
+ const node_crypto_1 = require("node:crypto");
20
22
  const base_definition_1 = require("./base.definition");
21
23
  /** Hono ALREADY percent-decodes path params - a second decodeURIComponent throws on `report_100%.pdf` and turns `a%2Fb.png` into a DIFFERENT object; `isValidName`/`isValidPath` still run on this value, so traversal is still rejected. */
24
+ /** The upload labels, read off the multipart form. Every value arrives as text, so `sequence` is the one that needs converting. */
25
+ /** The label names, in one place: the reader below and the stale-caller warning have to agree. */
26
+ const UPLOAD_LABEL_NAMES = [
27
+ 'principalType',
28
+ 'principalId',
29
+ 'variant',
30
+ 'sequence',
31
+ 'folderPath',
32
+ ];
33
+ /**
34
+ * Names any label still arriving in the query string. The value is still READ - see
35
+ * {@link readUploadLabels} - so this is a deprecation notice, not a report of something dropped.
36
+ */
37
+ const findLabelsInQuery = (opts) => UPLOAD_LABEL_NAMES.filter(name => opts.query[name] !== undefined);
38
+ exports.findLabelsInQuery = findLabelsInQuery;
39
+ /**
40
+ * The labels, body first and query as the way back.
41
+ *
42
+ * The query is where they used to travel. Refusing to read it would have been a clean break on
43
+ * paper and a broken upload in practice: a client whose form library only emits `append(key, value,
44
+ * filename)` - the three-argument form, which requires a Blob - cannot put a text field in a
45
+ * multipart body at all, so it THROWS rather than quietly losing a label. A read-with-warning lets
46
+ * each consumer move on its own schedule; the body always wins, so a migrated caller is never
47
+ * affected by a stale parameter left in a URL.
48
+ */
49
+ const readUploadLabels = (opts) => {
50
+ const { fields, fallback = {} } = opts;
51
+ const read = (name) => fields[name] || fallback[name] || undefined;
52
+ const sequence = read('sequence');
53
+ return {
54
+ principalType: read('principalType'),
55
+ principalId: read('principalId'),
56
+ variant: read('variant'),
57
+ sequence: sequence === undefined ? undefined : Number(sequence),
58
+ folderPath: read('folderPath'),
59
+ };
60
+ };
22
61
  const readObjectName = rawObjectName => rawObjectName;
23
62
  /** Encodes an object path into a SINGLE url segment: `{objectName}` matches one segment only, so `/` must be percent-encoded too (Hono decodes it back before the handler reads the param). */
24
63
  const encodeObjectPath = objectPath => {
25
64
  return encodeURIComponent(objectPath);
26
65
  };
27
- /** Sets whitelisted metadata headers on the response context. */
66
+ /** The bucket one request works on: a configured value wins - called on every request, so an application may read an environment variable lazily - else the path param, which is only registered when there is no configured bucket. */
67
+ const resolveBucket = async (opts) => {
68
+ const { configured, param } = opts;
69
+ if (configured === undefined) {
70
+ return param ?? '';
71
+ }
72
+ return (0, common_1.resolveValueAsync)(configured);
73
+ };
74
+ /** The URL of one object, in the shape its controller serves: no `/buckets/<name>` with a configured bucket, and a raw path with `rawObjectPath`. */
75
+ const buildObjectLink = (opts) => {
76
+ const { basePath, bucket, object } = opts;
77
+ const { hasConfiguredBucket = false, rawObjectPath = false } = opts;
78
+ const bucketSegment = hasConfiguredBucket ? '' : `/buckets/${bucket.name}`;
79
+ const objectSegment = rawObjectPath ? object.key : encodeObjectPath(object.key);
80
+ return `${basePath}${bucketSegment}/objects/${objectSegment}`;
81
+ };
82
+ exports.buildObjectLink = buildObjectLink;
83
+ /** Mirrors `BaseStorageHelper.normalizeObjectName`, which is protected: `resolveObjectName` is offered the very name the helper would otherwise have written. */
84
+ const defaultObjectName = ({ file }) => {
85
+ const { originalName, folderPath } = file;
86
+ const normalizedFileName = originalName.toLowerCase().replace(/ /g, '_');
87
+ if (!folderPath) {
88
+ return normalizedFileName;
89
+ }
90
+ return `${folderPath.toLowerCase().replace(/ /g, '_')}/${normalizedFileName}`;
91
+ };
92
+ /**
93
+ * The served type comes from the object NAME, never the backend or the uploader: a renderable type on
94
+ * the API origin is stored XSS. Exported so a hand-written route makes the same decision - the
95
+ * backend's own content-type is dropped by `WHITELIST_HEADERS`.
96
+ */
97
+ const resolveServedContentType = (opts) => {
98
+ const { helper, object } = opts;
99
+ const candidate = helper.getMimeType({ filename: object.key }).toLowerCase().split(';')[0].trim();
100
+ if (!common_2.RENDERABLE_CONTENT_TYPES.has(candidate)) {
101
+ return { contentType: common_1.HTTP.HeaderValues.APPLICATION_OCTET_STREAM, isRenderable: false };
102
+ }
103
+ return { contentType: candidate, isRenderable: true };
104
+ };
105
+ exports.resolveServedContentType = resolveServedContentType;
106
+ /**
107
+ * Reads one `Range` header. Only a single byte range is honoured - multipart ranges need a multipart
108
+ * body no browser asks for here. An unsatisfiable or malformed range returns `null`, and the caller
109
+ * serves the whole object, which is what RFC 9110 allows.
110
+ */
111
+ const readByteRange = (opts) => {
112
+ const { header, size } = opts;
113
+ if (!header || size <= 0) {
114
+ return null;
115
+ }
116
+ const match = /^bytes=(\d*)-(\d*)$/.exec(header.trim());
117
+ if (!match) {
118
+ return null;
119
+ }
120
+ const [, rawStart, rawEnd] = match;
121
+ // `bytes=-500` means the LAST 500 bytes, not "from 0 to 500".
122
+ if (!rawStart) {
123
+ const suffixLength = Number(rawEnd);
124
+ if (!Number.isInteger(suffixLength) || suffixLength <= 0) {
125
+ return null;
126
+ }
127
+ return { start: Math.max(size - suffixLength, 0), end: size - 1 };
128
+ }
129
+ const start = Number(rawStart);
130
+ const end = rawEnd ? Number(rawEnd) : size - 1;
131
+ if (!Number.isInteger(start) || !Number.isInteger(end) || start > end || start >= size) {
132
+ return null;
133
+ }
134
+ return { start, end: Math.min(end, size - 1) };
135
+ };
136
+ /** Sets whitelisted metadata headers on the response context. `content-type` is not among them. */
28
137
  const applyMetadataHeaders = ({ ctx, metadata }) => {
29
138
  Object.entries(metadata).forEach(([key, value]) => {
30
139
  if (!common_2.WHITELIST_HEADERS.includes(key.toLowerCase())) {
@@ -33,16 +142,67 @@ const applyMetadataHeaders = ({ ctx, metadata }) => {
33
142
  ctx.header(key.toLowerCase(), String(value).replace(/[\r\n]/g, ''));
34
143
  });
35
144
  };
145
+ /**
146
+ * The MetaLink row for one uploaded object. Both upload paths go through here, so a direct upload
147
+ * lands exactly the row an ordinary one does - the application's `createMetaLink` when it gives
148
+ * one, the component's default otherwise.
149
+ */
150
+ const createMetaLinkRow = async (opts) => {
151
+ const { metaLink, helper, storage, uploadResult, fileStat, query } = opts;
152
+ if (metaLink.createMetaLink) {
153
+ const created = await metaLink.createMetaLink({ uploadResult, fileStat, query });
154
+ return created.data;
155
+ }
156
+ // Resolved per call so an application that swaps the repository is not pinned to the first one.
157
+ const repository = await (0, common_1.resolveValueAsync)(metaLink.repository);
158
+ const created = await repository.create({
159
+ data: {
160
+ bucketName: uploadResult.bucket.name,
161
+ objectName: uploadResult.object.key,
162
+ link: uploadResult.link,
163
+ // The column is NOT NULL and a backend may report nothing.
164
+ mimetype: fileStat.metadata?.mimetype ?? helper.getMimeType({ filename: uploadResult.object.key }),
165
+ size: fileStat.size,
166
+ etag: fileStat.etag,
167
+ metadata: fileStat.metadata,
168
+ storageType: storage,
169
+ isSynced: true,
170
+ principalId: query.principalId ? String(query.principalId) : undefined,
171
+ principalType: query.principalType ? String(query.principalType) : undefined,
172
+ variant: query.variant ? String(query.variant) : undefined,
173
+ // Absent leaves the column default rather than writing 0, so a row the caller never
174
+ // ordered is indistinguishable from a legacy one.
175
+ sequence: query.sequence === undefined ? undefined : Number(query.sequence),
176
+ },
177
+ });
178
+ return created.data;
179
+ };
36
180
  class AssetControllerFactory extends core_1.BaseHelper {
37
181
  constructor() {
38
182
  super({ scope: AssetControllerFactory.name });
39
183
  }
40
184
  static defineAssetController(opts) {
41
185
  const { controller, helper, options, useMetaLink, metaLink, storage } = opts;
42
- const { name, basePath, routes, isStrict = true } = controller;
186
+ const { resolveObjectName, defineRoutesBefore, defineExtraRoutes } = opts;
187
+ const { directUpload } = controller;
188
+ const { name, basePath, routes, isStrict = true, bucket, rawObjectPath = false } = controller;
43
189
  const maxFolderDepth = options?.maxFolderDepth ?? ignis_helpers_1.BaseStorageHelper.DEFAULT_MAX_FOLDER_DEPTH;
190
+ const maxBytes = options?.maxBytes;
191
+ const hasConfiguredBucket = bucket !== undefined;
192
+ const definitions = (0, base_definition_1.buildAssetDefinitions)({ hasConfiguredBucket, rawObjectPath });
44
193
  // The mount path may be declared with or without a leading slash; a link must carry exactly one.
45
194
  const normalizedBasePath = basePath.startsWith('/') ? basePath : `/${basePath}`;
195
+ /** Only when the URL shape deviates: with neither option the storage helper's own link is left untouched. */
196
+ const shapedLinkFn = hasConfiguredBucket || rawObjectPath
197
+ ? (linkOptions) => (0, exports.buildObjectLink)({
198
+ basePath: normalizedBasePath,
199
+ bucket: linkOptions.bucket,
200
+ object: linkOptions.object,
201
+ hasConfiguredBucket,
202
+ rawObjectPath,
203
+ })
204
+ : undefined;
205
+ const normalizeLinkFn = options?.normalizeLinkFn ?? shapedLinkFn;
46
206
  let GeneratedStaticAssetController = class GeneratedStaticAssetController extends controllers_1.BaseRestController {
47
207
  constructor() {
48
208
  super({
@@ -65,79 +225,161 @@ class AssetControllerFactory extends core_1.BaseHelper {
65
225
  }
66
226
  }
67
227
  binding() {
228
+ // First, so a literal path can win over the built-in catch-all `rawObjectPath` registers.
229
+ defineRoutesBefore?.({ controller: this, helper, basePath: normalizedBasePath });
230
+ // A single-bucket application has no reason to expose bucket creation or deletion, so the
231
+ // four bucket-management routes are not registered once `bucket` is configured.
232
+ if (!hasConfiguredBucket) {
233
+ this.bindRoute({
234
+ configs: { ...definitions.GET_BUCKETS, ...routes?.getBuckets },
235
+ }).to({
236
+ handler: async (ctx) => {
237
+ const buckets = await helper.getBuckets();
238
+ return ctx.json(buckets, common_1.HTTP.ResultCodes.RS_2.Ok);
239
+ },
240
+ });
241
+ this.bindRoute({
242
+ configs: { ...definitions.GET_BUCKET_BY_NAME, ...routes?.getBucketByName },
243
+ }).to({
244
+ handler: async (ctx) => {
245
+ const params = ctx.req.valid('param');
246
+ const bucketName = await resolveBucket({
247
+ configured: bucket,
248
+ param: params.bucketName,
249
+ });
250
+ if (!helper.isValidBucketName({ bucket: { name: bucketName } })) {
251
+ throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.BUCKET_NAME_INVALID });
252
+ }
253
+ const found = await helper.getBucket({ bucket: { name: bucketName } });
254
+ return ctx.json(found, common_1.HTTP.ResultCodes.RS_2.Ok);
255
+ },
256
+ });
257
+ this.bindRoute({
258
+ configs: { ...definitions.CREATE_BUCKET, ...routes?.createBucket },
259
+ }).to({
260
+ handler: async (ctx) => {
261
+ const params = ctx.req.valid('param');
262
+ const bucketName = await resolveBucket({
263
+ configured: bucket,
264
+ param: params.bucketName,
265
+ });
266
+ if (!helper.isValidBucketName({ bucket: { name: bucketName } })) {
267
+ throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.BUCKET_NAME_INVALID });
268
+ }
269
+ const createdBucket = await helper.createBucket({ bucket: { name: bucketName } });
270
+ return ctx.json(createdBucket, common_1.HTTP.ResultCodes.RS_2.Ok);
271
+ },
272
+ });
273
+ this.bindRoute({
274
+ configs: { ...definitions.DELETE_BUCKET, ...routes?.deleteBucket },
275
+ }).to({
276
+ handler: async (ctx) => {
277
+ const params = ctx.req.valid('param');
278
+ const bucketName = await resolveBucket({
279
+ configured: bucket,
280
+ param: params.bucketName,
281
+ });
282
+ if (!helper.isValidBucketName({ bucket: { name: bucketName } })) {
283
+ throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.BUCKET_NAME_INVALID });
284
+ }
285
+ const isRemovedBucket = await helper.removeBucket({
286
+ bucket: { name: bucketName },
287
+ });
288
+ return ctx.json({ isDeleted: isRemovedBucket }, common_1.HTTP.ResultCodes.RS_2.Ok);
289
+ },
290
+ });
291
+ }
68
292
  this.bindRoute({
69
- configs: { ...base_definition_1.StaticAssetDefinitions.GET_BUCKETS, ...routes?.getBuckets },
70
- }).to({
71
- handler: async (ctx) => {
72
- const bucket = await helper.getBuckets();
73
- return ctx.json(bucket, common_1.HTTP.ResultCodes.RS_2.Ok);
74
- },
75
- });
76
- this.bindRoute({
77
- configs: { ...base_definition_1.StaticAssetDefinitions.GET_BUCKET_BY_NAME, ...routes?.getBucketByName },
78
- }).to({
79
- handler: async (ctx) => {
80
- const { bucketName } = ctx.req.valid('param');
81
- if (!helper.isValidName(bucketName)) {
82
- throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.BUCKET_NAME_INVALID });
83
- }
84
- const bucket = await helper.getBucket({ name: bucketName });
85
- return ctx.json(bucket, common_1.HTTP.ResultCodes.RS_2.Ok);
86
- },
87
- });
88
- this.bindRoute({
89
- configs: { ...base_definition_1.StaticAssetDefinitions.GET_OBJECT_BY_NAME, ...routes?.getObjectByName },
293
+ configs: { ...definitions.GET_OBJECT_BY_NAME, ...routes?.getObjectByName },
90
294
  }).to({
91
295
  handler: async (ctx) => {
92
- const { bucketName, objectName: rawObjectName } = ctx.req.valid('param');
93
- const objectName = readObjectName(rawObjectName);
94
- if (!helper.isValidName(bucketName)) {
296
+ const params = ctx.req.valid('param');
297
+ const bucketName = await resolveBucket({
298
+ configured: bucket,
299
+ param: params.bucketName,
300
+ });
301
+ const objectName = readObjectName(params.objectName);
302
+ if (!helper.isValidBucketName({ bucket: { name: bucketName } })) {
95
303
  throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.BUCKET_NAME_INVALID });
96
304
  }
97
- if (!helper.isValidPath(objectName, { maxDepth: maxFolderDepth })) {
305
+ if (!helper.isValidObjectKey({ object: { key: objectName }, maxDepth: maxFolderDepth })) {
98
306
  throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.OBJECT_NAME_INVALID });
99
307
  }
100
- const fileStat = await helper.getStat({ bucket: bucketName, name: objectName });
308
+ const fileStat = await helper.getStat({
309
+ bucket: { name: bucketName },
310
+ object: { key: objectName },
311
+ });
101
312
  const { size, metadata } = fileStat;
102
313
  applyMetadataHeaders({ ctx, metadata });
103
- if (!ctx.res.headers.has(common_1.HTTP.Headers.CONTENT_TYPE)) {
104
- ctx.header(common_1.HTTP.Headers.CONTENT_TYPE, common_1.HTTP.HeaderValues.APPLICATION_OCTET_STREAM);
105
- }
106
- ctx.header(common_1.HTTP.Headers.CONTENT_LENGTH, size.toString());
314
+ const served = (0, exports.resolveServedContentType)({ helper, object: { key: objectName } });
315
+ ctx.header(common_1.HTTP.Headers.CONTENT_TYPE, served.contentType);
107
316
  ctx.header('x-content-type-options', 'nosniff');
108
- const stream = await helper.getFile({ bucket: bucketName, name: objectName });
317
+ // Sandboxed even when renderable, so an unforeseen renderable type cannot reach this origin.
318
+ ctx.header(common_1.HTTP.Headers.CONTENT_SECURITY_POLICY, 'sandbox');
319
+ // Advertised unconditionally: a player only offers seeking once it sees this.
320
+ ctx.header('accept-ranges', 'bytes');
321
+ // A type outside the allow-list downloads instead of rendering; that is the XSS boundary.
322
+ if (!served.isRenderable) {
323
+ const inlineName = objectName.split('/').pop() ?? objectName;
324
+ ctx.header(common_1.HTTP.Headers.CONTENT_DISPOSITION, (0, ignis_helpers_1.createContentDispositionHeader)({ filename: inlineName, type: 'attachment' }));
325
+ }
326
+ const range = readByteRange({ header: ctx.req.header('range'), size });
327
+ if (range) {
328
+ ctx.header(common_1.HTTP.Headers.CONTENT_LENGTH, String(range.end - range.start + 1));
329
+ ctx.header(common_1.HTTP.Headers.CONTENT_RANGE, `bytes ${range.start}-${range.end}/${size}`);
330
+ }
331
+ else {
332
+ ctx.header(common_1.HTTP.Headers.CONTENT_LENGTH, size.toString());
333
+ }
334
+ // The web stream is what a Response body wants, so no Node Readable sits in between.
335
+ const stream = await helper.getObjectStream({
336
+ bucket: { name: bucketName },
337
+ object: { key: objectName },
338
+ ...(range ? { range } : {}),
339
+ });
109
340
  return new Response(stream, {
110
341
  headers: ctx.res.headers,
111
- status: common_1.HTTP.ResultCodes.RS_2.Ok,
342
+ status: range ? common_1.HTTP.ResultCodes.RS_2.PartialContent : common_1.HTTP.ResultCodes.RS_2.Ok,
112
343
  });
113
344
  },
114
345
  });
115
346
  this.bindRoute({
116
347
  configs: {
117
- ...base_definition_1.StaticAssetDefinitions.DOWNLOAD_OBJECT_BY_NAME,
348
+ ...definitions.DOWNLOAD_OBJECT_BY_NAME,
118
349
  ...routes?.downloadObjectByName,
119
350
  },
120
351
  }).to({
121
352
  handler: async (ctx) => {
122
- const { bucketName, objectName: rawObjectName } = ctx.req.valid('param');
123
- const objectName = readObjectName(rawObjectName);
124
- if (!helper.isValidName(bucketName)) {
353
+ const params = ctx.req.valid('param');
354
+ const bucketName = await resolveBucket({
355
+ configured: bucket,
356
+ param: params.bucketName,
357
+ });
358
+ const objectName = readObjectName(params.objectName);
359
+ if (!helper.isValidBucketName({ bucket: { name: bucketName } })) {
125
360
  throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.BUCKET_NAME_INVALID });
126
361
  }
127
- if (!helper.isValidPath(objectName, { maxDepth: maxFolderDepth })) {
362
+ if (!helper.isValidObjectKey({ object: { key: objectName }, maxDepth: maxFolderDepth })) {
128
363
  throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.OBJECT_NAME_INVALID });
129
364
  }
130
- const fileStat = await helper.getStat({ bucket: bucketName, name: objectName });
365
+ const fileStat = await helper.getStat({
366
+ bucket: { name: bucketName },
367
+ object: { key: objectName },
368
+ });
131
369
  const { size, metadata } = fileStat;
132
370
  applyMetadataHeaders({ ctx, metadata });
133
- if (!ctx.res.headers.has(common_1.HTTP.Headers.CONTENT_TYPE)) {
134
- ctx.header(common_1.HTTP.Headers.CONTENT_TYPE, common_1.HTTP.HeaderValues.APPLICATION_OCTET_STREAM);
135
- }
371
+ // Download always attaches, so the served type only has to be honest, never renderable.
372
+ const served = (0, exports.resolveServedContentType)({ helper, object: { key: objectName } });
373
+ ctx.header(common_1.HTTP.Headers.CONTENT_TYPE, served.contentType);
136
374
  ctx.header(common_1.HTTP.Headers.CONTENT_LENGTH, size.toString());
137
375
  const fileName = objectName.split('/').pop() ?? objectName;
138
376
  ctx.header(common_1.HTTP.Headers.CONTENT_DISPOSITION, (0, ignis_helpers_1.createContentDispositionHeader)({ filename: fileName, type: 'attachment' }));
139
377
  ctx.header('x-content-type-options', 'nosniff');
140
- const stream = await helper.getFile({ bucket: bucketName, name: objectName });
378
+ ctx.header(common_1.HTTP.Headers.CONTENT_SECURITY_POLICY, 'sandbox');
379
+ const stream = await helper.getObjectStream({
380
+ bucket: { name: bucketName },
381
+ object: { key: objectName },
382
+ });
141
383
  return new Response(stream, {
142
384
  headers: ctx.res.headers,
143
385
  status: common_1.HTTP.ResultCodes.RS_2.Ok,
@@ -145,28 +387,18 @@ class AssetControllerFactory extends core_1.BaseHelper {
145
387
  },
146
388
  });
147
389
  this.bindRoute({
148
- configs: { ...base_definition_1.StaticAssetDefinitions.CREATE_BUCKET, ...routes?.createBucket },
390
+ configs: { ...definitions.UPLOAD, ...routes?.upload },
149
391
  }).to({
150
392
  handler: async (ctx) => {
151
- const { bucketName } = ctx.req.valid('param');
152
- if (!helper.isValidName(bucketName)) {
393
+ // A configured bucket drops `params` from this route entirely, so it is read only when it exists.
394
+ const bucketParam = hasConfiguredBucket
395
+ ? undefined
396
+ : ctx.req.valid('param').bucketName;
397
+ const bucketName = await resolveBucket({ configured: bucket, param: bucketParam });
398
+ if (!helper.isValidBucketName({ bucket: { name: bucketName } })) {
153
399
  throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.BUCKET_NAME_INVALID });
154
400
  }
155
- const createdBucket = await helper.createBucket({ name: bucketName });
156
- return ctx.json(createdBucket, common_1.HTTP.ResultCodes.RS_2.Ok);
157
- },
158
- });
159
- this.bindRoute({
160
- configs: { ...base_definition_1.StaticAssetDefinitions.UPLOAD, ...routes?.upload },
161
- }).to({
162
- handler: async (ctx) => {
163
- const { bucketName } = ctx.req.valid('param');
164
- const query = ctx.req.valid('query');
165
- if (!helper.isValidName(bucketName)) {
166
- throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.BUCKET_NAME_INVALID });
167
- }
168
- const folderPath = query.folderPath;
169
- if (folderPath) {
401
+ const validateFolderPath = (folderPath) => {
170
402
  const normalizedFolder = folderPath.replace(/^\/+|\/+$/g, '');
171
403
  if (!normalizedFolder) {
172
404
  throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.FOLDER_PATH_INVALID });
@@ -179,19 +411,46 @@ class AssetControllerFactory extends core_1.BaseHelper {
179
411
  message: `Folder path exceeds max depth of ${maxFolderDepth}`,
180
412
  });
181
413
  }
182
- const invalidSegmentIndex = folderSegments.findIndex(segment => !helper.isValidName(segment));
414
+ const invalidSegmentIndex = folderSegments.findIndex(segment => !helper.isValidSegment({ segment }));
183
415
  if (invalidSegmentIndex !== -1) {
184
416
  throw (0, core_1.getError)({
185
417
  error: common_2.StaticAssetErrors.FOLDER_SEGMENT_INVALID,
186
418
  message: `Invalid folder path segment: ${folderSegments[invalidSegmentIndex]}`,
187
419
  });
188
420
  }
421
+ };
422
+ // Before the body is spooled. A multipart envelope is larger than the files inside it,
423
+ // so a request under the ceiling can still carry a file over it - hence the second check
424
+ // below. This one only buys the early exit, and a missing header buys nothing.
425
+ if (maxBytes !== undefined) {
426
+ const declaredLength = Number(ctx.req.header(common_1.HTTP.Headers.CONTENT_LENGTH));
427
+ if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
428
+ throw (0, core_1.getError)({
429
+ error: common_2.StaticAssetErrors.UPLOAD_TOO_LARGE,
430
+ message: `Request body of ${declaredLength} bytes exceeds the maximum of ${maxBytes}`,
431
+ });
432
+ }
189
433
  }
190
- const filesArray = await (0, ignis_helpers_1.parseMultipartBody)({
434
+ const { files: filesArray, fields } = await (0, ignis_helpers_1.parseMultipartBody)({
191
435
  context: ctx,
192
436
  storage: options?.parseMultipartBody?.storage,
193
437
  uploadDir: options?.parseMultipartBody?.uploadDir,
194
438
  });
439
+ // The labels travel WITH the payload rather than in the URL, so an identifier does not
440
+ // land in every access log along the way. The cost is that they are unreadable until
441
+ // the body is parsed, which is why the folder check runs here and not above.
442
+ const queryLabels = ctx.req.query();
443
+ const query = readUploadLabels({ fields, fallback: queryLabels });
444
+ const stale = (0, exports.findLabelsInQuery)({ query: queryLabels });
445
+ if (stale.length > 0) {
446
+ this.logger
447
+ .for('UPLOAD')
448
+ .warn('Label(s) read from the query string - they belong in the form body, and this fallback will go | names: %s', stale.join(', '));
449
+ }
450
+ const folderPath = query.folderPath;
451
+ if (folderPath) {
452
+ validateFolderPath(folderPath);
453
+ }
195
454
  const spoolPaths = filesArray
196
455
  .map(file => file.path)
197
456
  .filter((filePath) => Boolean(filePath));
@@ -206,6 +465,14 @@ class AssetControllerFactory extends core_1.BaseHelper {
206
465
  message: `Empty file content | name: ${file.originalname}`,
207
466
  });
208
467
  }
468
+ // The authoritative check: `buffer.length`, not the reported `size`, because the
469
+ // reported one is whatever the client declared.
470
+ if (maxBytes !== undefined && buffer.length > maxBytes) {
471
+ throw (0, core_1.getError)({
472
+ error: common_2.StaticAssetErrors.UPLOAD_TOO_LARGE,
473
+ message: `File ${file.originalname} of ${buffer.length} bytes exceeds the maximum of ${maxBytes}`,
474
+ });
475
+ }
209
476
  return {
210
477
  originalName: file.originalname,
211
478
  mimetype: file.mimetype,
@@ -215,11 +482,25 @@ class AssetControllerFactory extends core_1.BaseHelper {
215
482
  folderPath: folderPath ?? undefined,
216
483
  };
217
484
  });
485
+ // Built per request: the hook is offered the bucket, which only the route knows. No
486
+ // hook leaves `normalizeNameFn` exactly as configured, so the stored name is unchanged.
487
+ const normalizeNameFn = resolveObjectName
488
+ ? (naming) => {
489
+ const defaultKey = options?.normalizeNameFn
490
+ ? options.normalizeNameFn(naming)
491
+ : defaultObjectName(naming);
492
+ return resolveObjectName({
493
+ bucket: { name: bucketName },
494
+ file: naming.file,
495
+ defaultKey,
496
+ });
497
+ }
498
+ : options?.normalizeNameFn;
218
499
  uploaded = await helper.upload({
219
- bucket: bucketName,
500
+ bucket: { name: bucketName },
220
501
  files: modifiedFiles,
221
- normalizeNameFn: options?.normalizeNameFn,
222
- normalizeLinkFn: options?.normalizeLinkFn,
502
+ normalizeNameFn,
503
+ normalizeLinkFn,
223
504
  // Without this the helper re-validates against its own hard default of 2, so an app configured for a deeper tree spools the body and only then fails inside the helper.
224
505
  maxFolderDepth,
225
506
  });
@@ -234,47 +515,30 @@ class AssetControllerFactory extends core_1.BaseHelper {
234
515
  for (const uploadResult of uploaded) {
235
516
  try {
236
517
  const fileStat = await helper.getStat({
237
- bucket: uploadResult.bucketName,
238
- name: uploadResult.objectName,
518
+ bucket: uploadResult.bucket,
519
+ object: uploadResult.object,
239
520
  });
240
- const { data: createdMetaLink } = metaLink.createMetaLink
241
- ? await metaLink.createMetaLink({
242
- uploadResult,
243
- fileStat,
244
- query,
245
- })
246
- : await metaLink.repository.create({
247
- data: {
248
- bucketName: uploadResult.bucketName,
249
- objectName: uploadResult.objectName,
250
- link: uploadResult.link,
251
- mimetype: fileStat.metadata?.['mimetype'],
252
- size: fileStat.size,
253
- etag: fileStat.etag,
254
- metadata: fileStat.metadata,
255
- storageType: storage,
256
- isSynced: true,
257
- principalId: query.principalId ? String(query.principalId) : undefined,
258
- principalType: query.principalType
259
- ? String(query.principalType)
260
- : undefined,
261
- variant: query.variant ? String(query.variant) : undefined,
262
- },
263
- });
264
- results.push({ ...uploadResult, metaLink: createdMetaLink });
521
+ const createdMetaLink = await createMetaLinkRow({
522
+ metaLink,
523
+ helper,
524
+ storage,
525
+ uploadResult,
526
+ fileStat,
527
+ query,
528
+ });
529
+ results.push({ ...uploadResult, metaLink: { data: createdMetaLink } });
265
530
  }
266
531
  catch (error) {
267
532
  this.logger
268
533
  .for('UPLOAD')
269
- .error('Failed to create MetaLink | objectName: %s | Error: %s', uploadResult.objectName, error);
534
+ .error('Failed to create MetaLink | objectName: %s | Error: %s', uploadResult.object.key, error);
270
535
  results.push({
271
536
  ...uploadResult,
272
- metaLink: null,
273
537
  // A CODE, never the driver text. This handler returns 200, so it bypasses the
274
538
  // error middleware and `database.handler` - the two places that strip
275
539
  // `detail`/`table`/`constraint` - and shipped raw constraint names to the client.
276
540
  // The real error is already logged in full immediately above.
277
- metaLinkError: 'META_LINK_CREATE_FAILED',
541
+ metaLink: { error: common_2.META_LINK_CREATE_FAILED },
278
542
  });
279
543
  }
280
544
  }
@@ -282,36 +546,39 @@ class AssetControllerFactory extends core_1.BaseHelper {
282
546
  },
283
547
  });
284
548
  this.bindRoute({
285
- configs: { ...base_definition_1.StaticAssetDefinitions.DELETE_BUCKET, ...routes?.deleteBucket },
549
+ configs: { ...definitions.DELETE_OBJECT, ...routes?.deleteObject },
286
550
  }).to({
287
551
  handler: async (ctx) => {
288
- const { bucketName } = ctx.req.valid('param');
289
- if (!helper.isValidName(bucketName)) {
290
- throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.BUCKET_NAME_INVALID });
291
- }
292
- const isRemovedBucket = await helper.removeBucket({
293
- name: bucketName,
552
+ const params = ctx.req.valid('param');
553
+ const bucketName = await resolveBucket({
554
+ configured: bucket,
555
+ param: params.bucketName,
294
556
  });
295
- return ctx.json({ isDeleted: isRemovedBucket }, common_1.HTTP.ResultCodes.RS_2.Ok);
296
- },
297
- });
298
- this.bindRoute({
299
- configs: { ...base_definition_1.StaticAssetDefinitions.DELETE_OBJECT, ...routes?.deleteObject },
300
- }).to({
301
- handler: async (ctx) => {
302
- const { bucketName, objectName: rawObjectName } = ctx.req.valid('param');
303
- const objectName = readObjectName(rawObjectName);
304
- if (!helper.isValidName(bucketName)) {
557
+ const objectName = readObjectName(params.objectName);
558
+ if (!helper.isValidBucketName({ bucket: { name: bucketName } })) {
305
559
  throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.BUCKET_NAME_INVALID });
306
560
  }
307
- if (!helper.isValidPath(objectName, { maxDepth: maxFolderDepth })) {
561
+ if (!helper.isValidObjectKey({ object: { key: objectName }, maxDepth: maxFolderDepth })) {
308
562
  throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.OBJECT_NAME_INVALID });
309
563
  }
310
- await helper.removeObject({ bucket: bucketName, name: objectName });
564
+ // Deliberately idempotent: S3 answers 200 for a key that was never there, and consumers
565
+ // already depend on it. `disk` throws instead, so the two are reconciled here, not below.
566
+ try {
567
+ await helper.removeObject({
568
+ bucket: { name: bucketName },
569
+ object: { key: objectName },
570
+ });
571
+ }
572
+ catch (error) {
573
+ if (!(0, ignis_helpers_1.isNotFoundError)({ error })) {
574
+ throw error;
575
+ }
576
+ }
311
577
  if (!useMetaLink || !metaLink) {
312
578
  return ctx.json({ success: true }, common_1.HTTP.ResultCodes.RS_2.Ok);
313
579
  }
314
- metaLink.repository
580
+ const deleteRepository = await (0, common_1.resolveValueAsync)(metaLink.repository);
581
+ deleteRepository
315
582
  .deleteAll({
316
583
  where: {
317
584
  bucketName,
@@ -332,12 +599,16 @@ class AssetControllerFactory extends core_1.BaseHelper {
332
599
  },
333
600
  });
334
601
  this.bindRoute({
335
- configs: { ...base_definition_1.StaticAssetDefinitions.LIST_OBJECTS, ...routes?.listObjects },
602
+ configs: { ...definitions.LIST_OBJECTS, ...routes?.listObjects },
336
603
  }).to({
337
604
  handler: async (ctx) => {
338
- const { bucketName } = ctx.req.valid('param');
605
+ // A configured bucket drops `params` from this route entirely, so it is read only when it exists.
606
+ const bucketParam = hasConfiguredBucket
607
+ ? undefined
608
+ : ctx.req.valid('param').bucketName;
609
+ const bucketName = await resolveBucket({ configured: bucket, param: bucketParam });
339
610
  const { prefix, recursive, maxKeys } = ctx.req.valid('query');
340
- if (!helper.isValidName(bucketName)) {
611
+ if (!helper.isValidBucketName({ bucket: { name: bucketName } })) {
341
612
  throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.BUCKET_NAME_INVALID });
342
613
  }
343
614
  // A NaN or 0 maxKeys is silently treated as "unlimited" by the storage backends, so an unparsable value must be rejected instead of forwarded.
@@ -352,7 +623,7 @@ class AssetControllerFactory extends core_1.BaseHelper {
352
623
  }
353
624
  }
354
625
  const objects = await helper.listObjects({
355
- bucket: bucketName,
626
+ bucket: { name: bucketName },
356
627
  prefix,
357
628
  useRecursive: recursive === 'true',
358
629
  maxKeys: resolvedMaxKeys,
@@ -360,27 +631,191 @@ class AssetControllerFactory extends core_1.BaseHelper {
360
631
  return ctx.json(objects, common_1.HTTP.ResultCodes.RS_2.Ok);
361
632
  },
362
633
  });
634
+ if (directUpload) {
635
+ if (!hasConfiguredBucket) {
636
+ throw (0, core_1.getError)({
637
+ message: `[${name}] directUpload needs controller.bucket | a policy names one bucket, and a bucket in the URL is a bucket the caller chooses`,
638
+ });
639
+ }
640
+ const pendingPrefix = directUpload.pendingPrefix ?? common_2.DEFAULT_PENDING_PREFIX;
641
+ this.bindRoute({
642
+ configs: { ...definitions.UPLOAD_POLICY, ...routes?.uploadPolicy },
643
+ }).to({
644
+ handler: async (ctx) => {
645
+ if (!(await directUpload.authorize({ context: ctx }))) {
646
+ throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.UPLOAD_NOT_AUTHORIZED });
647
+ }
648
+ const { files } = ctx.req.valid('json');
649
+ const bucketName = await resolveBucket({ configured: bucket });
650
+ const policies = [];
651
+ for (const file of files) {
652
+ if (file.size !== undefined && file.size > directUpload.maxBytes) {
653
+ throw (0, core_1.getError)({
654
+ error: common_2.StaticAssetErrors.UPLOAD_TOO_LARGE,
655
+ message: `File '${file.fileName}' is ${file.size} bytes, over the ${directUpload.maxBytes} the policy allows`,
656
+ });
657
+ }
658
+ // Generated, never taken from the caller: a key the caller picks is a key the caller
659
+ // can collide with, and the pending object is the one thing a policy authorizes.
660
+ const objectName = `${pendingPrefix}${(0, node_crypto_1.randomUUID)()}/${file.fileName}`;
661
+ const policy = await helper.presignPost({
662
+ bucket: { name: bucketName },
663
+ keyPrefix: pendingPrefix,
664
+ maxBytes: directUpload.maxBytes,
665
+ contentType: file.contentType,
666
+ expiresIn: directUpload.expiresIn,
667
+ });
668
+ policies.push({
669
+ ...policy,
670
+ objectName,
671
+ commitToken: (0, common_3.buildCommitToken)({
672
+ payload: {
673
+ bucket: bucketName,
674
+ key: objectName,
675
+ expiresAt: new Date(policy.expiresAt).getTime(),
676
+ },
677
+ secretKey: directUpload.secretKey,
678
+ }),
679
+ });
680
+ }
681
+ return ctx.json(policies, common_1.HTTP.ResultCodes.RS_2.Ok);
682
+ },
683
+ });
684
+ this.bindRoute({
685
+ configs: { ...definitions.UPLOAD_COMMIT, ...routes?.uploadCommit },
686
+ }).to({
687
+ handler: async (ctx) => {
688
+ const { commitToken, ...labels } = ctx.req.valid('json');
689
+ // Verified BEFORE any storage call. That is what keeps this route from being a name
690
+ // prober: a forged token fails without a round trip, so timing says nothing.
691
+ const payload = (0, common_3.readCommitToken)({
692
+ token: commitToken,
693
+ secretKey: directUpload.secretKey,
694
+ });
695
+ // Labels, not authorization - the token deliberately carries no business fields, so
696
+ // they travel beside it rather than inside it.
697
+ const queryLabels = ctx.req.query();
698
+ const query = readUploadLabels({
699
+ fields: labels,
700
+ fallback: queryLabels,
701
+ });
702
+ const staleLabels = (0, exports.findLabelsInQuery)({ query: queryLabels });
703
+ if (staleLabels.length > 0) {
704
+ this.logger
705
+ .for('UPLOAD_COMMIT')
706
+ .warn('Label(s) read from the query string - they belong in the JSON body, and this fallback will go | names: %s', staleLabels.join(', '));
707
+ }
708
+ const bucketRef = { name: payload.bucket };
709
+ const pendingObject = { key: payload.key };
710
+ const object = { key: payload.key.slice(pendingPrefix.length) };
711
+ // Before the copy, so a hook that throws leaves the object under the pending prefix
712
+ // for a lifecycle rule, rather than stranding it at the final key.
713
+ await directUpload.onCommit?.({ bucket: bucketRef, pendingObject, object });
714
+ await helper.copyObject({
715
+ bucket: bucketRef,
716
+ source: pendingObject,
717
+ destination: object,
718
+ });
719
+ // The copy IS the commit; removing the temporary object is cleanup. A failure here
720
+ // leaves one object for the lifecycle rule, and failing the request would tell the
721
+ // caller their upload did not work when it did.
722
+ await helper
723
+ .removeObject({ bucket: bucketRef, object: pendingObject })
724
+ .catch(error => {
725
+ this.logger
726
+ .for('UPLOAD_COMMIT')
727
+ .warn('Committed, but the pending object remains | key: %s | error: %s', pendingObject.key, error);
728
+ });
729
+ const link = (0, exports.buildObjectLink)({
730
+ basePath: normalizedBasePath,
731
+ bucket: bucketRef,
732
+ object,
733
+ hasConfiguredBucket,
734
+ rawObjectPath,
735
+ });
736
+ // The two upload paths have to agree. Without this the ordinary upload writes a row
737
+ // and the direct one does not, so a client reading `metaLink` off the response renders
738
+ // an empty state with no error - the failure nobody reports.
739
+ let metaLinkResult;
740
+ if (useMetaLink && metaLink) {
741
+ try {
742
+ const fileStat = await helper.getStat({ bucket: bucketRef, object });
743
+ const data = await createMetaLinkRow({
744
+ metaLink,
745
+ helper,
746
+ storage,
747
+ uploadResult: {
748
+ bucket: bucketRef,
749
+ object: {
750
+ key: object.key,
751
+ size: fileStat.size,
752
+ contentType: fileStat.metadata?.mimetype ??
753
+ helper.getMimeType({ filename: object.key }),
754
+ },
755
+ link,
756
+ },
757
+ fileStat,
758
+ query,
759
+ });
760
+ metaLinkResult = { data };
761
+ }
762
+ catch (error) {
763
+ // The copy already happened and the pending object is gone, so the object IS
764
+ // committed. Answering 500 would send the caller back with a token whose source
765
+ // no longer exists - which is why the ordinary upload reports this in the body
766
+ // too, as a code rather than the driver's text.
767
+ this.logger
768
+ .for('UPLOAD_COMMIT')
769
+ .error('Failed to create MetaLink | objectName: %s | Error: %s', object.key, error);
770
+ metaLinkResult = { error: common_2.META_LINK_CREATE_FAILED };
771
+ }
772
+ }
773
+ return ctx.json({
774
+ bucket: bucketRef,
775
+ object,
776
+ link,
777
+ ...(metaLinkResult === undefined ? {} : { metaLink: metaLinkResult }),
778
+ }, common_1.HTTP.ResultCodes.RS_2.Ok);
779
+ },
780
+ });
781
+ }
363
782
  if (useMetaLink && metaLink) {
364
783
  this.bindRoute({
365
- configs: { ...base_definition_1.StaticAssetDefinitions.RECREATE_METALINK, ...routes?.recreateMetaLink },
784
+ configs: { ...definitions.RECREATE_METALINK, ...routes?.recreateMetaLink },
366
785
  }).to({
367
786
  handler: async (ctx) => {
368
- const { bucketName, objectName: rawObjectName } = ctx.req.valid('param');
369
- const objectName = readObjectName(rawObjectName);
370
- if (!helper.isValidName(bucketName)) {
787
+ const params = ctx.req.valid('param');
788
+ const bucketName = await resolveBucket({
789
+ configured: bucket,
790
+ param: params.bucketName,
791
+ });
792
+ const objectName = readObjectName(params.objectName);
793
+ if (!helper.isValidBucketName({ bucket: { name: bucketName } })) {
371
794
  throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.BUCKET_NAME_INVALID });
372
795
  }
373
- if (!helper.isValidPath(objectName, { maxDepth: maxFolderDepth })) {
796
+ if (!helper.isValidObjectKey({ object: { key: objectName }, maxDepth: maxFolderDepth })) {
374
797
  throw (0, core_1.getError)({ error: common_2.StaticAssetErrors.OBJECT_NAME_INVALID });
375
798
  }
376
799
  const fileStat = await helper.getStat({
377
- bucket: bucketName,
378
- name: objectName,
800
+ bucket: { name: bucketName },
801
+ object: { key: objectName },
379
802
  });
380
803
  const link = options?.normalizeLinkFn
381
- ? options.normalizeLinkFn({ bucketName, normalizeName: objectName })
382
- : `${normalizedBasePath}/buckets/${bucketName}/objects/${encodeObjectPath(objectName)}`;
383
- const existing = await metaLink.repository.findOne({
804
+ ? options.normalizeLinkFn({
805
+ bucket: { name: bucketName },
806
+ object: { key: objectName },
807
+ })
808
+ : (0, exports.buildObjectLink)({
809
+ basePath: normalizedBasePath,
810
+ bucket: { name: bucketName },
811
+ object: { key: objectName },
812
+ hasConfiguredBucket,
813
+ rawObjectPath,
814
+ });
815
+ // Read per call, never once per handler: an application may rebind the repository
816
+ // between two of these, and a copy held across them would answer from the old binding.
817
+ const findRepository = await (0, common_1.resolveValueAsync)(metaLink.repository);
818
+ const existing = await findRepository.findOne({
384
819
  filter: {
385
820
  where: {
386
821
  bucketName,
@@ -389,7 +824,8 @@ class AssetControllerFactory extends core_1.BaseHelper {
389
824
  },
390
825
  });
391
826
  if (existing) {
392
- await metaLink.repository.updateById({
827
+ const updateRepository = await (0, common_1.resolveValueAsync)(metaLink.repository);
828
+ await updateRepository.updateById({
393
829
  id: existing.id,
394
830
  data: {
395
831
  link,
@@ -401,15 +837,17 @@ class AssetControllerFactory extends core_1.BaseHelper {
401
837
  isSynced: true,
402
838
  },
403
839
  });
404
- const updatedMetaLink = await metaLink.repository.findById({ id: existing.id });
840
+ const readRepository = await (0, common_1.resolveValueAsync)(metaLink.repository);
841
+ const updatedMetaLink = await readRepository.findById({ id: existing.id });
405
842
  return ctx.json({ success: true, metaLink: updatedMetaLink }, common_1.HTTP.ResultCodes.RS_2.Ok);
406
843
  }
407
- const createdMetaLink = await metaLink.repository.create({
844
+ const createRepository = await (0, common_1.resolveValueAsync)(metaLink.repository);
845
+ const createdMetaLink = await createRepository.create({
408
846
  data: {
409
847
  bucketName,
410
848
  objectName,
411
849
  link,
412
- mimetype: fileStat.metadata?.['mimetype'],
850
+ mimetype: fileStat.metadata?.mimetype ?? helper.getMimeType({ filename: objectName }),
413
851
  size: fileStat.size,
414
852
  etag: fileStat.etag,
415
853
  metadata: fileStat.metadata,
@@ -421,6 +859,8 @@ class AssetControllerFactory extends core_1.BaseHelper {
421
859
  },
422
860
  });
423
861
  }
862
+ // Last, so a built-in route always wins a path collision with an application's own.
863
+ defineExtraRoutes?.({ controller: this, helper, basePath: normalizedBasePath });
424
864
  }
425
865
  };
426
866
  GeneratedStaticAssetController = __decorate([