@classytic/arc 2.30.0 → 2.31.0

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 (239) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +7 -0
  3. package/TRADEMARK.md +22 -0
  4. package/dist/{BaseController-B5DwufbB.mjs → BaseController-De7GqHck.mjs} +16 -17
  5. package/dist/{EventTransport-QmY7A258.d.mts → EventTransport-2uVV34WU.d.mts} +1 -0
  6. package/dist/{EventTransport-C6YA8xY9.mjs → EventTransport-9mlTgqzE.mjs} +1 -0
  7. package/dist/{HookSystem-WkIougo6.mjs → HookSystem-BcmPyq5I.mjs} +2 -1
  8. package/dist/{QueryCache-DOMT_iq7.d.mts → QueryCache-DFA-QrU9.d.mts} +2 -1
  9. package/dist/{ResourceRegistry-y9Hnypus.mjs → ResourceRegistry-qq0mn2s9.mjs} +3 -2
  10. package/dist/{actionPermissions-CAphcmUG.mjs → actionPermissions-BE5NwaQk.mjs} +1 -0
  11. package/dist/audit/index.d.mts +3 -2
  12. package/dist/audit/index.mjs +2 -1
  13. package/dist/auth/audit.d.mts +1 -0
  14. package/dist/auth/audit.mjs +1 -0
  15. package/dist/auth/index.d.mts +5 -4
  16. package/dist/auth/index.mjs +6 -5
  17. package/dist/auth/redis-session.d.mts +2 -1
  18. package/dist/auth/redis-session.mjs +1 -0
  19. package/dist/{applyPermissionResult-Cf6zUCvb.mjs → authorizationDecision-ChvUQGG1.mjs} +19 -10
  20. package/dist/{buildHandler-BYS3Vkfz.mjs → buildHandler-CDrCJzmB.mjs} +4 -3
  21. package/dist/cache/index.d.mts +4 -3
  22. package/dist/cache/index.mjs +4 -3
  23. package/dist/{caching-_NJeDd1_.mjs → caching-BFHA6Scf.mjs} +2 -1
  24. package/dist/cleanup/index.d.mts +4 -3
  25. package/dist/cleanup/index.mjs +9 -13
  26. package/dist/cli/commands/describe.d.mts +2 -1
  27. package/dist/cli/commands/describe.mjs +3 -2
  28. package/dist/cli/commands/docs.d.mts +1 -0
  29. package/dist/cli/commands/docs.mjs +3 -2
  30. package/dist/cli/commands/doctor.d.mts +1 -0
  31. package/dist/cli/commands/doctor.mjs +1 -0
  32. package/dist/cli/commands/generate.d.mts +2 -1
  33. package/dist/cli/commands/generate.mjs +2 -1
  34. package/dist/cli/commands/init.d.mts +2 -1
  35. package/dist/cli/commands/init.mjs +2 -1
  36. package/dist/cli/commands/introspect.d.mts +1 -0
  37. package/dist/cli/commands/introspect.mjs +2 -1
  38. package/dist/cli/index.d.mts +3 -2
  39. package/dist/cli/index.mjs +3 -2
  40. package/dist/{constants-DNre0wRg.mjs → constants-D3Dv4LAV.mjs} +1 -0
  41. package/dist/context/index.d.mts +1 -0
  42. package/dist/context/index.mjs +3 -2
  43. package/dist/core/index.d.mts +3 -2
  44. package/dist/core/index.mjs +7 -6
  45. package/dist/{core-poalJLvp.mjs → core-BGpIxINN.mjs} +3 -2
  46. package/dist/{createActionRouter-BxuywVRB.mjs → createActionRouter-CDXEdasl.mjs} +8 -7
  47. package/dist/{createAggregationRouter-BiY-lGdY.mjs → createAggregationRouter-KrQDzx-k.mjs} +7 -6
  48. package/dist/{createApp-CXrKFciH.mjs → createApp-Df89BEni.mjs} +484 -209
  49. package/dist/{defineAggregation-ufWjUwml.mjs → defineAggregation-DbPPS8RI.mjs} +1 -0
  50. package/dist/{defineResource-CxWqDCAy.mjs → defineResource-Bv6EtN1x.mjs} +63 -38
  51. package/dist/discovery/index.d.mts +1 -0
  52. package/dist/discovery/index.mjs +1 -0
  53. package/dist/docs/index.d.mts +3 -2
  54. package/dist/docs/index.mjs +3 -2
  55. package/dist/{elevation-C9s5hFP_.d.mts → elevation-BXCfVcNG.d.mts} +1 -0
  56. package/dist/{elevation-CIz1GjGm.mjs → elevation-D2l1mh4g.mjs} +4 -3
  57. package/dist/encryption/index.d.mts +2 -1
  58. package/dist/encryption/index.mjs +3 -2
  59. package/dist/{errorHandler-Bq2OeEQL.mjs → errorHandler-C5NeD6bT.mjs} +3 -2
  60. package/dist/{errorHandler-C6yv4ZTW.d.mts → errorHandler-CpYUJmPA.d.mts} +1 -0
  61. package/dist/{errors-DHcHVAgk.d.mts → errors-BGdVwP25.d.mts} +1 -0
  62. package/dist/{errors-DkO8Nk6-.mjs → errors-Cj42AntO.mjs} +1 -0
  63. package/dist/{eventPlugin-D2NNR4py.mjs → eventPlugin-CkffEihm.mjs} +5 -4
  64. package/dist/{eventPlugin-Bg3cP2_u.d.mts → eventPlugin-GF5nnBIq.d.mts} +2 -1
  65. package/dist/events/index.d.mts +4 -3
  66. package/dist/events/index.mjs +16 -4
  67. package/dist/events/transports/redis-stream-entry.d.mts +2 -1
  68. package/dist/events/transports/redis-stream-entry.mjs +1 -0
  69. package/dist/events/transports/redis.d.mts +2 -1
  70. package/dist/events/transports/redis.mjs +1 -0
  71. package/dist/{externalPaths-BE6wFpYv.d.mts → externalPaths-CmGOxOtD.d.mts} +1 -0
  72. package/dist/factory/index.d.mts +9 -5
  73. package/dist/factory/index.mjs +7 -6
  74. package/dist/{fastify-augmentation-0WhPnUH7.d.mts → fastify-augmentation-zh0_AYHa.d.mts} +13 -4
  75. package/dist/{fields-CQ_7KQWw.mjs → fields-CB5Cuzk9.mjs} +1 -0
  76. package/dist/{fields-D3lhgzhD.d.mts → fields-I_EeRWND.d.mts} +1 -0
  77. package/dist/{generate-BnsL9GBm.d.mts → generate-DCGuEbqI.d.mts} +1 -0
  78. package/dist/{generate-D0wZupBg.mjs → generate-EcA6ui4i.mjs} +2 -1
  79. package/dist/{headers-BvlQSDMB.mjs → headers-3zvE0rM3.mjs} +2 -1
  80. package/dist/{health-CB_m4S32.mjs → health-DjZYjM7H.mjs} +1 -0
  81. package/dist/hooks/index.d.mts +2 -1
  82. package/dist/hooks/index.mjs +2 -1
  83. package/dist/idempotency/index.d.mts +3 -2
  84. package/dist/idempotency/index.mjs +4 -3
  85. package/dist/idempotency/redis.d.mts +2 -1
  86. package/dist/idempotency/redis.mjs +1 -0
  87. package/dist/{index-Cx7bro4R.d.mts → index-Bc2ox8_U.d.mts} +5 -4
  88. package/dist/{index-CG4yqMDB.d.mts → index-C-481Zf2.d.mts} +74 -18
  89. package/dist/{index-Ch16aAah.d.mts → index-CEJNZFtX.d.mts} +53 -15
  90. package/dist/index-CFThn1K9.d.mts +224 -0
  91. package/dist/{index-Q5gzEIWY.d.mts → index-CpzxqePp.d.mts} +107 -32
  92. package/dist/index.d.mts +9 -8
  93. package/dist/index.mjs +11 -10
  94. package/dist/{init-tlRSt667.mjs → init-BEiNZBsJ.mjs} +19 -23
  95. package/dist/{init-BPEu8cs-.d.mts → init-Cscs4lab.d.mts} +1 -0
  96. package/dist/integrations/event-gateway.d.mts +3 -2
  97. package/dist/integrations/event-gateway.mjs +2 -1
  98. package/dist/integrations/index.d.mts +4 -3
  99. package/dist/integrations/index.mjs +1 -0
  100. package/dist/integrations/jobs/index.d.mts +2 -1
  101. package/dist/integrations/jobs/index.mjs +108 -32
  102. package/dist/integrations/mcp/index.d.mts +6 -6
  103. package/dist/integrations/mcp/index.mjs +2 -1
  104. package/dist/integrations/mcp/testing.d.mts +3 -2
  105. package/dist/integrations/mcp/testing.mjs +2 -2
  106. package/dist/integrations/streamline.d.mts +1 -0
  107. package/dist/integrations/streamline.mjs +3 -2
  108. package/dist/integrations/webhooks.d.mts +1 -0
  109. package/dist/integrations/webhooks.mjs +1 -0
  110. package/dist/integrations/websocket-pushref-redis.d.mts +2 -1
  111. package/dist/integrations/websocket-pushref-redis.mjs +1 -0
  112. package/dist/integrations/websocket-redis.d.mts +2 -1
  113. package/dist/integrations/websocket-redis.mjs +1 -0
  114. package/dist/integrations/websocket.d.mts +3 -2
  115. package/dist/integrations/websocket.mjs +1 -0
  116. package/dist/{interface-BGZCzOGL.d.mts → interface-BOk3YnTz.d.mts} +1 -0
  117. package/dist/{interface-Uk8ONJv7.d.mts → interface-DEWNOMza.d.mts} +1 -0
  118. package/dist/{interface-n5hpKg1d.mjs → interface-DYzM0q-u.mjs} +1 -0
  119. package/dist/{jsonBody-BlzAoGCe.mjs → jsonBody-BYbLDi5D.mjs} +2 -1
  120. package/dist/{keys-Cd316u_y.mjs → keys-C0_pa1tY.mjs} +1 -0
  121. package/dist/{loadResources-DpU8pfjV.mjs → loadResources-yawi9tAE.mjs} +2 -1
  122. package/dist/{loadResourcesFromEntry-kvGT6EcO.mjs → loadResourcesFromEntry-BAjYZonx.mjs} +2 -1
  123. package/dist/logger/index.d.mts +1 -0
  124. package/dist/logger/index.mjs +1 -0
  125. package/dist/{mcpPlugin-nRzm5g2s.mjs → mcpPlugin-B_es0hiF.mjs} +22 -23
  126. package/dist/{memory-CmnzXE5Z.mjs → memory-DR6wpa9F.mjs} +2 -1
  127. package/dist/{metrics-fdxI5mn_.mjs → metrics-CUdw_wLB.mjs} +2 -1
  128. package/dist/middleware/index.d.mts +2 -1
  129. package/dist/middleware/index.mjs +3 -2
  130. package/dist/migrations/index.d.mts +1 -0
  131. package/dist/migrations/index.mjs +2 -1
  132. package/dist/module-0pFJA6YW.mjs +78 -0
  133. package/dist/{multipartBody-D0eBvkeq.mjs → multipartBody-wOgj_fnj.mjs} +1 -0
  134. package/dist/{openapi-B8Vkbxbn.mjs → openapi-B2L4js1v.mjs} +6 -5
  135. package/dist/permissions/index.d.mts +5 -4
  136. package/dist/permissions/index.mjs +6 -5
  137. package/dist/{permissions-Bfu_8y1U.mjs → permissions-L--QDAir.mjs} +214 -41
  138. package/dist/{pipe-BOLmGIxa.mjs → pipe-DRDNsI2T.mjs} +2 -1
  139. package/dist/pipeline/index.d.mts +2 -1
  140. package/dist/pipeline/index.mjs +2 -1
  141. package/dist/{pipeline-BJnpkY9i.mjs → pipeline-UPQFatit.mjs} +6 -5
  142. package/dist/plugins/index.d.mts +8 -7
  143. package/dist/plugins/index.mjs +16 -15
  144. package/dist/plugins/response-cache.d.mts +1 -0
  145. package/dist/plugins/response-cache.mjs +2 -1
  146. package/dist/plugins/tracing-entry.d.mts +2 -1
  147. package/dist/plugins/tracing-entry.mjs +2 -1
  148. package/dist/{pluralize-BhrgwZb9.mjs → pluralize-O0FZgESl.mjs} +1 -0
  149. package/dist/{preloadResources-DCfa2egz.d.mts → preloadResources-CyoJMSiO.d.mts} +117 -12
  150. package/dist/{preloadResources-DSY5HGpS.mjs → preloadResources-Dz6BjzM6.mjs} +1 -0
  151. package/dist/presets/filesUpload.d.mts +6 -5
  152. package/dist/presets/filesUpload.mjs +9 -11
  153. package/dist/presets/index.d.mts +3 -2
  154. package/dist/presets/index.mjs +2 -1
  155. package/dist/presets/multiTenant.d.mts +2 -1
  156. package/dist/presets/multiTenant.mjs +3 -2
  157. package/dist/presets/search.d.mts +3 -2
  158. package/dist/presets/search.mjs +2 -1
  159. package/dist/{presets-CJOEXLuk.mjs → presets-C4-g4_4P.mjs} +4 -3
  160. package/dist/{projection-UBDgfMwu.mjs → projection-BHw6xlOQ.mjs} +2 -1
  161. package/dist/{pushref-registry-BbcnpNTm.d.mts → pushref-registry-Dov9l4qE.d.mts} +1 -0
  162. package/dist/{queryCachePlugin-DSGuSVLW.mjs → queryCachePlugin-BZr8v46E.mjs} +5 -4
  163. package/dist/{queryCachePlugin-br4otiqF.d.mts → queryCachePlugin-BzqxconG.d.mts} +3 -2
  164. package/dist/{rateLimit-DrED-C4v.mjs → rateLimit-CMPY5Qms.mjs} +7 -3
  165. package/dist/{rateLimitKey-BoWRH3e7.mjs → rateLimitKey-Dn5JNmkj.mjs} +1 -0
  166. package/dist/{realtime-B9nmIY5V.mjs → realtime-C5NoxWFY.mjs} +20 -9
  167. package/dist/{redis-UDtLaNj6.d.mts → redis-CduhKsJi.d.mts} +2 -1
  168. package/dist/{redis-stream-uIaauNpZ.d.mts → redis-stream-qN6A-RFv.d.mts} +2 -1
  169. package/dist/{registerRoute-JkxIEBJq.mjs → registerRoute-CBN84fc7.mjs} +4 -3
  170. package/dist/registry/index.d.mts +3 -2
  171. package/dist/registry/index.mjs +3 -2
  172. package/dist/{registry-BYXUUg21.mjs → registry-4213U4zb.mjs} +2 -1
  173. package/dist/{renewingLease-3cuQH7h8.mjs → renewingLease-Dll8mGgc.mjs} +1 -0
  174. package/dist/{replyHelpers-67tRawQZ.mjs → replyHelpers-DIiZKGUt.mjs} +2 -1
  175. package/dist/{repositoryFilter-Dz8k3S4P.mjs → repositoryFilter-f9eAfq_l.mjs} +1 -0
  176. package/dist/{requestContext-MrlJwECf.mjs → requestContext-2TxXOJ7T.mjs} +1 -0
  177. package/dist/{rolldown-runtime-8H4AJuhK.mjs → rolldown-runtime-CUsjNX9R.mjs} +1 -0
  178. package/dist/{schedules-CjOd2MG9.mjs → schedules-DdtlICTf.mjs} +3 -2
  179. package/dist/{schemaConverter-DJvodiHj.mjs → schemaConverter-D--wdJM-.mjs} +70 -17
  180. package/dist/{schemaIR-Ca7ySNPx.mjs → schemaIR-Bm2enbnh.mjs} +2 -1
  181. package/dist/schemas/index.d.mts +1 -0
  182. package/dist/schemas/index.mjs +1 -0
  183. package/dist/scim/index.d.mts +1 -0
  184. package/dist/scim/index.mjs +2 -1
  185. package/dist/scope/index.d.mts +3 -2
  186. package/dist/scope/index.mjs +5 -4
  187. package/dist/{scope-DfaPntpo.mjs → scope-DHx5Fous.mjs} +4 -3
  188. package/dist/{sessionManager-BHOM3N_0.d.mts → sessionManager-qimlKV0P.d.mts} +1 -0
  189. package/dist/{simpleEqualityMatcher-Cg05HPsn.mjs → simpleEqualityMatcher-DjQJ7FKA.mjs} +1 -0
  190. package/dist/{sse-BQ1w0dfl.mjs → sse-CUTo9y0X.mjs} +5 -4
  191. package/dist/{sseStream-BQ7Qaynp.mjs → sseStream-B_5XcVVd.mjs} +2 -1
  192. package/dist/{storage-DLUZ04kK.d.mts → storage-Cx9Sz3Ou.d.mts} +1 -0
  193. package/dist/{store-helpers-DY6xq4sM.mjs → store-helpers-D1MP_gRG.mjs} +1 -0
  194. package/dist/{streaming-BQcW6_Kq.mjs → streaming-PSZwi8JV.mjs} +1 -0
  195. package/dist/{subscribe-helpers-DfzsivM6.mjs → subscribe-helpers-C0Tb2aUp.mjs} +2 -1
  196. package/dist/sync/changelog.d.mts +1 -0
  197. package/dist/sync/changelog.mjs +1 -0
  198. package/dist/testing/cleanupStoreContract.d.mts +2 -1
  199. package/dist/testing/cleanupStoreContract.mjs +1 -0
  200. package/dist/testing/index.d.mts +139 -12
  201. package/dist/testing/index.mjs +230 -16
  202. package/dist/testing/outboxStoreContract.d.mts +1 -0
  203. package/dist/testing/outboxStoreContract.mjs +1 -0
  204. package/dist/testing/storageContract.d.mts +2 -1
  205. package/dist/testing/storageContract.mjs +1 -0
  206. package/dist/{tracing-CsGKQJGg.d.mts → tracing-Dro5eH6j.d.mts} +1 -0
  207. package/dist/{transactionContext-PNdoyLLm.mjs → transactionContext-dAfBVUrk.mjs} +1 -0
  208. package/dist/{typeGuards-C9KXUly6.mjs → typeGuards-B0ryPo3I.mjs} +1 -0
  209. package/dist/types/index.d.mts +6 -5
  210. package/dist/types/index.mjs +1 -0
  211. package/dist/types/storage.d.mts +2 -1
  212. package/dist/types/storage.mjs +1 -0
  213. package/dist/{types-ChvvVNP8.d.mts → types-B7XeLEgK.d.mts} +34 -2
  214. package/dist/{types-Cc91nXuA.d.mts → types-ByHn9rEO.d.mts} +1 -0
  215. package/dist/{types-C_ZZRcD6.d.mts → types-C4QE8qnm.d.mts} +1 -0
  216. package/dist/{types-D_n8L6VY.d.mts → types-CBgvkp8O.d.mts} +51 -12
  217. package/dist/{types-DjC-KUCz.d.mts → types-ChuShpCP.d.mts} +1 -0
  218. package/dist/{types-Bmmn-fO0.d.mts → types-CwcUQ610.d.mts} +2 -6
  219. package/dist/{types-CSyCxl2V.mjs → types-Cys0ko9Z.mjs} +4 -3
  220. package/dist/usage/index.d.mts +3 -2
  221. package/dist/usage/index.mjs +0 -0
  222. package/dist/{userHelpers-Cg_y3fBz.mjs → userHelpers-ChYJ6CdK.mjs} +1 -0
  223. package/dist/utils/index.d.mts +16 -7
  224. package/dist/utils/index.mjs +10 -9
  225. package/dist/{utils-taUVD4p3.mjs → utils-vL8BrDE2.mjs} +3 -2
  226. package/dist/{validate-Bk44F2yT.mjs → validate-_KbifpHF.mjs} +2 -1
  227. package/dist/{versioning-gKWyryT3.mjs → versioning-CHDTor3P.mjs} +3 -2
  228. package/dist/{versioning-DXgY5BUm.d.mts → versioning-D_rhHfZm.d.mts} +5 -2
  229. package/dist/{websocket-_Pd0CgR0.d.mts → websocket-BOXVggMf.d.mts} +2 -1
  230. package/package.json +547 -539
  231. package/skills/arc/SKILL.md +22 -8
  232. package/skills/arc/references/api-reference.md +4 -0
  233. package/skills/arc/references/auth.md +3 -0
  234. package/skills/arc/references/integrations.md +33 -0
  235. package/skills/arc/references/mcp.md +6 -7
  236. package/skills/arc/references/production.md +24 -25
  237. package/skills/arc-code-review/references/anti-patterns.md +3 -3
  238. package/dist/index-CCzVR-N3.d.mts +0 -107
  239. package/dist/module-_-M7C5ES.mjs +0 -29
@@ -1,12 +1,158 @@
1
- import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
1
+ /*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
2
+ import { t as __exportAll } from "./rolldown-runtime-CUsjNX9R.mjs";
2
3
  import { arcLog, configureArcLogger, createPinoWriter } from "./logger/index.mjs";
3
- import { n as PUBLIC_SCOPE } from "./types-CSyCxl2V.mjs";
4
- import { t as createTenantKeyGenerator } from "./rateLimitKey-BoWRH3e7.mjs";
5
- import { i as createRequestIdGenerator, r as mergeHealthChecks } from "./health-CB_m4S32.mjs";
6
- import { t as parseJsonBody } from "./jsonBody-BlzAoGCe.mjs";
7
- import { r as wrapWithBoundary } from "./subscribe-helpers-DfzsivM6.mjs";
4
+ import { n as PUBLIC_SCOPE } from "./types-Cys0ko9Z.mjs";
5
+ import { t as createTenantKeyGenerator } from "./rateLimitKey-Dn5JNmkj.mjs";
6
+ import { i as createRequestIdGenerator, r as mergeHealthChecks } from "./health-DjZYjM7H.mjs";
7
+ import { t as parseJsonBody } from "./jsonBody-BYbLDi5D.mjs";
8
+ import { r as wrapWithBoundary } from "./subscribe-helpers-C0Tb2aUp.mjs";
8
9
  import Fastify from "fastify";
9
10
  import qs from "qs";
11
+ //#region src/factory/security/pluginLoader.ts
12
+ const PLUGIN_REGISTRY = {
13
+ cors: {
14
+ package: "@fastify/cors",
15
+ loader: () => import("@fastify/cors").then((m) => m.default)
16
+ },
17
+ helmet: {
18
+ package: "@fastify/helmet",
19
+ loader: () => import("@fastify/helmet").then((m) => m.default)
20
+ },
21
+ rateLimit: {
22
+ package: "@fastify/rate-limit",
23
+ loader: () => import("@fastify/rate-limit").then((m) => m.default)
24
+ },
25
+ underPressure: {
26
+ package: "@fastify/under-pressure",
27
+ loader: () => import("@fastify/under-pressure").then((m) => m.default)
28
+ },
29
+ sensible: {
30
+ package: "@fastify/sensible",
31
+ loader: () => import("@fastify/sensible").then((m) => m.default)
32
+ },
33
+ multipart: {
34
+ package: "@fastify/multipart",
35
+ loader: () => import("@fastify/multipart").then((m) => m.default),
36
+ optional: true
37
+ },
38
+ rawBody: {
39
+ package: "fastify-raw-body",
40
+ loader: () => import("fastify-raw-body").then((m) => m.default),
41
+ optional: true
42
+ },
43
+ static: {
44
+ package: "@fastify/static",
45
+ loader: () => import("@fastify/static").then((m) => m.default),
46
+ optional: true
47
+ }
48
+ };
49
+ async function loadPlugin(name, logger) {
50
+ const entry = PLUGIN_REGISTRY[name];
51
+ if (!entry) throw new Error(`Unknown plugin: ${name}`);
52
+ try {
53
+ return await entry.loader();
54
+ } catch (error) {
55
+ const err = error;
56
+ const isModuleNotFound = err.message.includes("Cannot find module") || err.message.includes("Cannot find package") || err.message.includes("MODULE_NOT_FOUND") || err.message.includes("Could not resolve");
57
+ if (isModuleNotFound && entry.optional) {
58
+ logger?.warn(`Optional plugin '${name}' skipped (${entry.package} not installed)`);
59
+ return null;
60
+ }
61
+ if (isModuleNotFound) throw new Error(`Plugin '${name}' requires package '${entry.package}' which is not installed.\nInstall it with: npm install ${entry.package}\nOr disable this plugin by setting ${name}: false in createApp options.`);
62
+ throw new Error(`Failed to load plugin '${name}': ${err.message}`);
63
+ }
64
+ }
65
+ //#endregion
66
+ //#region src/factory/assets.ts
67
+ /**
68
+ * Cache-Control per preset.
69
+ *
70
+ * - `immutable` — content-addressed output ONLY (a hashed filename that changes
71
+ * when the bytes change). One year + `immutable` so a revalidation is never
72
+ * even attempted.
73
+ * - `revalidate` — the default. The client may cache but MUST check freshness;
74
+ * `@fastify/static`'s `ETag`/`Last-Modified` then makes the common case a 304
75
+ * with no body, which is nearly as cheap as a cache hit and always correct.
76
+ * - `none` — never store. For a path that is authorization-dependent, where a
77
+ * shared cache holding the bytes would leak them to the next caller.
78
+ */
79
+ const CACHE_CONTROL = {
80
+ immutable: "public, max-age=31536000, immutable",
81
+ revalidate: "public, max-age=0, must-revalidate",
82
+ none: "no-store"
83
+ };
84
+ /**
85
+ * Resolve one asset root's declaration into the header policy arc applies per
86
+ * file. Pure — no Fastify, no filesystem — so the policy is unit-testable
87
+ * without booting an app.
88
+ */
89
+ function resolveAssetPolicy(root) {
90
+ const cache = root.cache ?? "revalidate";
91
+ const crossOrigin = root.crossOrigin ?? "same-site";
92
+ const disposition = root.disposition ?? "attachment";
93
+ return {
94
+ cacheControl: CACHE_CONTROL[cache],
95
+ crossOriginResourcePolicy: crossOrigin,
96
+ disposition,
97
+ varyOrigin: crossOrigin !== "same-origin"
98
+ };
99
+ }
100
+ /** Quote a filename for `Content-Disposition` per RFC 6266 / RFC 5987. */
101
+ function contentDisposition(type, filePath) {
102
+ const name = filePath.split(/[\\/]/).pop() ?? "download";
103
+ return `${type}; filename="${name.replace(/[^\x20-\x7e]/g, "_").replace(/["\\]/g, "\\$&")}"; filename*=UTF-8''${encodeURIComponent(name)}`;
104
+ }
105
+ /**
106
+ * Register every declared asset root.
107
+ *
108
+ * Each root becomes its OWN `@fastify/static` registration. That is deliberate:
109
+ * `@fastify/static` scopes `prefix`, `root`, and the send options per
110
+ * registration, so two roots with different cache or cross-origin policy stay
111
+ * genuinely independent instead of sharing one mutable options bag.
112
+ *
113
+ * Registered AFTER the security plugins so the per-prefix CORP override lands on
114
+ * top of helmet's app-wide default — helmet sets its headers in an `onRequest`
115
+ * hook, while `setHeaders` runs at send time, so the asset policy wins.
116
+ */
117
+ async function registerAssetRoots(fastify, roots) {
118
+ if (roots.length === 0) return;
119
+ const staticPlugin = await loadPlugin("static", fastify.log);
120
+ if (!staticPlugin) throw new Error("[arc] `assets` was configured but '@fastify/static' is not installed. Install it with: npm install @fastify/static — arc delegates ranges, ETags, immutable caching and pre-compressed variants to it rather than reimplementing them.");
121
+ const seen = /* @__PURE__ */ new Map();
122
+ for (const root of roots) {
123
+ const prior = seen.get(root.prefix);
124
+ if (prior !== void 0) throw new Error(`[arc] duplicate asset prefix "${root.prefix}" — declared for roots "${prior}" and "${root.root}". Asset prefixes must be unique; nest them (e.g. "/files/public" and "/files/private") instead of sharing one.`);
125
+ seen.set(root.prefix, String(root.root));
126
+ }
127
+ for (const root of roots) {
128
+ const policy = resolveAssetPolicy(root);
129
+ await fastify.register(staticPlugin, {
130
+ root: root.root,
131
+ prefix: root.prefix,
132
+ decorateReply: root.decorateReply ?? false,
133
+ list: false,
134
+ index: root.index ?? false,
135
+ dotfiles: "deny",
136
+ serveDotFiles: false,
137
+ cacheControl: false,
138
+ etag: root.etag ?? true,
139
+ lastModified: root.lastModified ?? true,
140
+ acceptRanges: root.acceptRanges ?? true,
141
+ ...root.preCompressed !== void 0 ? { preCompressed: root.preCompressed } : {},
142
+ ...root.allowedPath ? { allowedPath: root.allowedPath } : {},
143
+ setHeaders: (reply, filePath) => {
144
+ reply.header("Cache-Control", policy.cacheControl);
145
+ reply.header("Cross-Origin-Resource-Policy", policy.crossOriginResourcePolicy);
146
+ reply.header("Content-Disposition", contentDisposition(policy.disposition, filePath));
147
+ reply.header("X-Content-Type-Options", "nosniff");
148
+ if (policy.varyOrigin) reply.header("Vary", "Origin, Accept-Encoding");
149
+ root.setHeaders?.(reply, filePath);
150
+ }
151
+ });
152
+ fastify.log.debug(`[arc] asset root "${root.prefix}" → ${String(root.root)} (cache: ${root.cache ?? "revalidate"}, cross-origin: ${policy.crossOriginResourcePolicy})`);
153
+ }
154
+ }
155
+ //#endregion
10
156
  //#region src/factory/module/resolve.ts
11
157
  /**
12
158
  * Resolve any module-input form to a concrete `ArcModule`. A thunk is invoked
@@ -16,6 +162,7 @@ import qs from "qs";
16
162
  async function resolveModule(input) {
17
163
  const resolved = typeof input === "function" ? await input() : await input;
18
164
  if (!resolved || typeof resolved !== "object" || typeof resolved.name !== "string") throw new Error("[arc] a `modules` entry resolved to something that is not an ArcModule (expected an object with a string `name`). Check dynamic-import thunks return `createXModule(deps)` (or the module object), not the namespace.");
165
+ if (resolved.name.trim().length === 0) throw new Error("[arc] a `modules` entry has an empty (or whitespace-only) `name`. A module's name is its graph key, its `arc.modules` export key, its `dependsOn` target, and its label in every boot diagnostic — give it a stable, non-empty identifier.");
19
166
  return resolved;
20
167
  }
21
168
  /** Resolve a `ModuleContribution` to a concrete array (array passthrough or factory call). */
@@ -23,6 +170,26 @@ async function resolveContribution(contribution, fastify) {
23
170
  if (!contribution) return [];
24
171
  return typeof contribution === "function" ? await contribution(fastify) : contribution;
25
172
  }
173
+ /**
174
+ * `resolveContribution` with module attribution — the form every arm collector
175
+ * should use.
176
+ *
177
+ * A contribution factory closes over booted engines, so it throws for exactly
178
+ * the reasons a bootstrap does ("connection unavailable", "engine not ready").
179
+ * Bare, that message names neither the module nor the arm, which in a graph of
180
+ * twenty modules turns a one-line fix into a bisect. Every other module
181
+ * boundary (`plugins`, `bootstrap`, `resources`, `afterResources`) already
182
+ * wraps with the owning module's name; this keeps the arms consistent with
183
+ * them. The original error is preserved via `{ cause }`.
184
+ */
185
+ async function resolveModuleArm(module, arm, contribution, fastify) {
186
+ try {
187
+ return await resolveContribution(contribution, fastify);
188
+ } catch (err) {
189
+ const msg = err instanceof Error ? err.message : String(err);
190
+ throw new Error(`[arc] module "${module.name}" ${arm} factory threw: ${msg}`, { cause: err });
191
+ }
192
+ }
26
193
  //#endregion
27
194
  //#region src/factory/module/contributions.ts
28
195
  /**
@@ -49,7 +216,7 @@ function collectModuleHealthChecks(modules) {
49
216
  async function collectModuleWorkflows(fastify, modules) {
50
217
  const out = [];
51
218
  for (const m of modules) {
52
- const wfs = await resolveContribution(m.workflows, fastify);
219
+ const wfs = await resolveModuleArm(m, "workflows", m.workflows, fastify);
53
220
  out.push(...wfs);
54
221
  }
55
222
  return out;
@@ -64,7 +231,7 @@ async function collectModuleScheduledJobs(fastify, modules) {
64
231
  const owner = /* @__PURE__ */ new Map();
65
232
  const out = [];
66
233
  for (const m of modules) {
67
- const jobs = await resolveContribution(m.scheduledJobs, fastify);
234
+ const jobs = await resolveModuleArm(m, "scheduledJobs", m.scheduledJobs, fastify);
68
235
  for (const job of jobs) {
69
236
  const prior = owner.get(job.name);
70
237
  if (prior !== void 0) throw new Error(`[arc] duplicate scheduled-job name "${job.name}" — declared by module "${prior}" and module "${m.name}". Scheduled-job names must be unique across the module graph.`);
@@ -91,7 +258,7 @@ async function subscribeModuleEventHandlers(fastify, modules) {
91
258
  const owner = /* @__PURE__ */ new Map();
92
259
  try {
93
260
  for (const m of modules) {
94
- const handlers = await resolveContribution(m.eventHandlers, fastify);
261
+ const handlers = await resolveModuleArm(m, "eventHandlers", m.eventHandlers, fastify);
95
262
  if (handlers.length === 0) continue;
96
263
  const bus = fastify.events;
97
264
  if (!bus) throw new Error(`[arc] module "${m.name}" declares eventHandlers but the event subsystem is unavailable (fastify.events). Enable arcPlugins.events.`);
@@ -235,6 +402,98 @@ function describeModuleCycle(remaining, byName) {
235
402
  return `[arc] module dependency cycle: ${cycle ? cycle.join(" → ") : [...remaining].join(", ")}. Modules cannot dependsOn each other circularly — break it with a shared module both point at, or wire the softer direction through an event/port instead of a hard dependsOn.`;
236
403
  }
237
404
  //#endregion
405
+ //#region src/factory/module/state.ts
406
+ /**
407
+ * The honest read of the state map. `fastify.arc` is optional (see
408
+ * fastify-augmentation.ts) and `moduleStates` is populated by
409
+ * registerResources — both absent means "no module graph composed here"
410
+ * (or a pre-2.31 embedding), so accessors degrade to undefined/false
411
+ * rather than throwing.
412
+ */
413
+ function readStates(fastify) {
414
+ return fastify.arc?.moduleStates;
415
+ }
416
+ /**
417
+ * Record every composed module as `resolved`. Runs at the top of
418
+ * registerResources, BEFORE any side-effecting phase — so from the first
419
+ * lifecycle callback onward, `hasModule()` answers for the whole graph.
420
+ * Null-proto map for the same reason as `arc.modules`: a module named
421
+ * "__proto__" must be an own key, not a prototype write.
422
+ */
423
+ function initModuleStates(fastify, names) {
424
+ const arc = fastify.arc;
425
+ if (!arc) return;
426
+ const states = Object.create(null);
427
+ for (const name of names) states[name] = "resolved";
428
+ arc.moduleStates = states;
429
+ }
430
+ /**
431
+ * Internal transition writer — no-op when no state map exists (see above),
432
+ * and no-op OUT of `failed`, which is terminal: rollback closes a module whose
433
+ * bootstrap threw, and that cleanup succeeding must not erase the fact that
434
+ * initialization failed (see the file header).
435
+ */
436
+ function setModuleState(fastify, name, state) {
437
+ const states = readStates(fastify);
438
+ if (!states) return;
439
+ if (states[name] === "failed") return;
440
+ states[name] = state;
441
+ }
442
+ /**
443
+ * Is `name` in the composed module graph? TRUE for resource-only modules and
444
+ * modules that exported nothing — the question `hasModuleExports()` cannot
445
+ * answer. FALSE before registerResources runs (no graph yet) and for
446
+ * uncomposed names.
447
+ */
448
+ function hasModule(fastify, name) {
449
+ const states = readStates(fastify);
450
+ return states !== void 0 && Object.hasOwn(states, name);
451
+ }
452
+ /**
453
+ * The module's current lifecycle state, or `undefined` when the module is not
454
+ * composed (or no graph has been composed on this instance). See the state
455
+ * machine in the file header.
456
+ */
457
+ function getModuleState(fastify, name) {
458
+ const states = readStates(fastify);
459
+ return states !== void 0 && Object.hasOwn(states, name) ? states[name] : void 0;
460
+ }
461
+ //#endregion
462
+ //#region src/factory/module/teardown.ts
463
+ function createModuleTeardown(fastify, modules) {
464
+ const initializedNames = /* @__PURE__ */ new Set();
465
+ const consumed = /* @__PURE__ */ new Set();
466
+ async function closeInitialized() {
467
+ const errors = [];
468
+ for (let index = modules.length - 1; index >= 0; index--) {
469
+ const m = modules[index];
470
+ if (!m || !initializedNames.has(m.name) || consumed.has(m.name)) continue;
471
+ consumed.add(m.name);
472
+ if (!m.onClose) {
473
+ setModuleState(fastify, m.name, "closed");
474
+ continue;
475
+ }
476
+ setModuleState(fastify, m.name, "closing");
477
+ try {
478
+ await m.onClose(fastify);
479
+ setModuleState(fastify, m.name, "closed");
480
+ } catch (err) {
481
+ setModuleState(fastify, m.name, "failed");
482
+ errors.push(new Error(`[arc] module "${m.name}" onClose() threw during teardown: ${err instanceof Error ? err.message : String(err)}`, { cause: err }));
483
+ }
484
+ }
485
+ return errors;
486
+ }
487
+ return {
488
+ markInitialized(module) {
489
+ initializedNames.add(module.name);
490
+ },
491
+ rollback: closeInitialized,
492
+ closeAll: closeInitialized,
493
+ hasComposedModules: () => modules.length > 0
494
+ };
495
+ }
496
+ //#endregion
238
497
  //#region src/factory/presets.ts
239
498
  /**
240
499
  * Production preset - strict security, performance optimized
@@ -460,7 +719,7 @@ async function registerArcCore(fastify, config, trackPlugin) {
460
719
  await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
461
720
  trackPlugin("arc-core");
462
721
  if (config.arcPlugins?.events !== false) {
463
- const { default: eventPlugin } = await import("./eventPlugin-D2NNR4py.mjs").then((n) => n.n);
722
+ const { default: eventPlugin } = await import("./eventPlugin-CkffEihm.mjs").then((n) => n.n);
464
723
  const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
465
724
  await fastify.register(eventPlugin, {
466
725
  ...eventOpts,
@@ -508,15 +767,15 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
508
767
  trackPlugin("arc-graceful-shutdown");
509
768
  }
510
769
  if (config.arcPlugins?.caching) {
511
- const { default: cachingPlugin } = await import("./caching-_NJeDd1_.mjs").then((n) => n.r);
770
+ const { default: cachingPlugin } = await import("./caching-BFHA6Scf.mjs").then((n) => n.r);
512
771
  const opts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
513
772
  await fastify.register(cachingPlugin, opts);
514
773
  trackPlugin("arc-caching", opts);
515
774
  }
516
775
  if (config.arcPlugins?.queryCache) {
517
- const { queryCachePlugin } = await import("./queryCachePlugin-DSGuSVLW.mjs").then((n) => n.n);
776
+ const { queryCachePlugin } = await import("./queryCachePlugin-BZr8v46E.mjs").then((n) => n.n);
518
777
  const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
519
- const store = config.stores?.queryCache ?? new (await (import("./memory-CmnzXE5Z.mjs").then((n) => n.n))).MemoryCacheStore();
778
+ const store = config.stores?.queryCache ?? new (await (import("./memory-DR6wpa9F.mjs").then((n) => n.n))).MemoryCacheStore();
520
779
  await fastify.register(queryCachePlugin, {
521
780
  store,
522
781
  ...opts
@@ -525,26 +784,26 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules, moduleH
525
784
  }
526
785
  if (config.arcPlugins?.sse) if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
527
786
  else {
528
- const { default: ssePlugin } = await import("./sse-BQ1w0dfl.mjs").then((n) => n.r);
787
+ const { default: ssePlugin } = await import("./sse-CUTo9y0X.mjs").then((n) => n.r);
529
788
  const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
530
789
  await fastify.register(ssePlugin, opts);
531
790
  trackPlugin("arc-sse", opts);
532
791
  }
533
792
  if (config.arcPlugins?.realtime) if (config.arcPlugins?.events === false) fastify.log.warn("Realtime plugin requires events plugin (arcPlugins.events). Realtime disabled.");
534
793
  else {
535
- const { default: realtimePlugin } = await import("./realtime-B9nmIY5V.mjs").then((n) => n.r);
794
+ const { default: realtimePlugin } = await import("./realtime-C5NoxWFY.mjs").then((n) => n.r);
536
795
  const opts = config.arcPlugins.realtime === true ? {} : config.arcPlugins.realtime;
537
796
  await fastify.register(realtimePlugin, opts);
538
797
  trackPlugin("arc-realtime", opts);
539
798
  }
540
799
  if (config.arcPlugins?.metrics) {
541
- const { default: metricsPlugin } = await import("./metrics-fdxI5mn_.mjs").then((n) => n.r);
800
+ const { default: metricsPlugin } = await import("./metrics-CUdw_wLB.mjs").then((n) => n.r);
542
801
  const opts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
543
802
  await fastify.register(metricsPlugin, opts);
544
803
  trackPlugin("arc-metrics", opts);
545
804
  }
546
805
  if (config.arcPlugins?.versioning) {
547
- const { default: versioningPlugin } = await import("./versioning-gKWyryT3.mjs").then((n) => n.r);
806
+ const { default: versioningPlugin } = await import("./versioning-CHDTor3P.mjs").then((n) => n.r);
548
807
  await fastify.register(versioningPlugin, config.arcPlugins.versioning);
549
808
  trackPlugin("arc-versioning", config.arcPlugins.versioning);
550
809
  }
@@ -613,7 +872,7 @@ async function registerAuth(fastify, config, trackPlugin) {
613
872
  */
614
873
  async function registerElevation(fastify, config, trackPlugin) {
615
874
  if (!config.elevation) return;
616
- const { elevationPlugin } = await import("./elevation-CIz1GjGm.mjs").then((n) => n.r);
875
+ const { elevationPlugin } = await import("./elevation-D2l1mh4g.mjs").then((n) => n.r);
617
876
  await fastify.register(elevationPlugin, config.elevation);
618
877
  trackPlugin("arc-elevation", config.elevation);
619
878
  fastify.log.debug("Elevation plugin enabled");
@@ -628,7 +887,7 @@ async function registerElevation(fastify, config, trackPlugin) {
628
887
  */
629
888
  async function registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers) {
630
889
  if (config.errorHandler === false) return;
631
- const { errorHandlerPlugin } = await import("./errorHandler-Bq2OeEQL.mjs").then((n) => n.r);
890
+ const { errorHandlerPlugin } = await import("./errorHandler-C5NeD6bT.mjs").then((n) => n.r);
632
891
  let errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
633
892
  if (moduleErrorMappers && moduleErrorMappers.length > 0) errorOpts = {
634
893
  ...errorOpts,
@@ -701,14 +960,29 @@ async function registerOne(parent, resource, mountRoutes) {
701
960
  * 5. resources factory (if any) — resolved AFTER bootstrap, so engine-backed
702
961
  * adapters can `await ensureEngine()` and pass
703
962
  * live models/repos into `defineResource(...)`
704
- * 5b. module resources — RESOLVED after the app factory, but PREPENDED
963
+ * 5a. module resources — RESOLVED after the app factory, but PREPENDED
705
964
  * so they REGISTER before app resources (two
706
- * orderings, both intentional — see §5b)
965
+ * orderings, both intentional — see §5a)
966
+ * 5b. owns validation + filter — verify each claim is supplied by its claimant,
967
+ * THEN drop the app forks it supersedes
707
968
  * 6. module.afterResources → afterResources() — post-registration wiring
708
969
  * 7. onReady — lifecycle hook
709
970
  * 7b. onClose — module.onClose (reverse composition order)
710
971
  * THEN app onClose, in ONE hook so module
711
972
  * teardown runs before shared infra closes.
973
+ *
974
+ * Boot is TRANSACTIONAL for module-held resources: phases 2–6 run inside one
975
+ * try/catch. On any failure, arc unsubscribes already-active module event
976
+ * handlers, then closes every module that ENTERED an init phase — reverse
977
+ * composition order, best-effort (see module/teardown.ts) — and rethrows the
978
+ * ORIGINAL boot error. Without this, a failure after module A's bootstrap but
979
+ * before the §7b hook registration leaked A's clients/timers/engines with no
980
+ * teardown path.
981
+ *
982
+ * "Entered", not "completed": a module is eligible for teardown from the
983
+ * moment it enters `plugins()`/`bootstrap()`, so the callback that allocates a
984
+ * client and THEN throws — the commonest partial-init shape — gets its closer
985
+ * called too.
712
986
  */
713
987
  async function registerResources(fastify, config, preResolvedModules) {
714
988
  if (config.preset === "production") {
@@ -722,160 +996,190 @@ async function registerResources(fastify, config, preResolvedModules) {
722
996
  };
723
997
  }
724
998
  const modules = preResolvedModules ?? orderModules(await Promise.all((config.modules ?? []).map((m) => resolveModule(m))));
725
- if (config.plugins) {
726
- await config.plugins(fastify);
727
- fastify.log.debug("Custom plugins registered");
728
- }
729
- for (const m of modules) {
730
- if (!m.plugins) continue;
731
- try {
732
- await m.plugins(fastify);
733
- } catch (err) {
734
- const msg = err instanceof Error ? err.message : String(err);
735
- throw new Error(`[arc] module "${m.name}" plugins() threw: ${msg}`, { cause: err });
736
- }
737
- }
738
- for (const m of modules) {
739
- if (!m.bootstrap) continue;
740
- let exported;
741
- try {
742
- exported = await m.bootstrap(fastify);
743
- } catch (err) {
744
- const msg = err instanceof Error ? err.message : String(err);
745
- throw new Error(`[arc] module "${m.name}" bootstrap threw: ${msg}`, { cause: err });
999
+ initModuleStates(fastify, modules.map((m) => m.name));
1000
+ const teardown = createModuleTeardown(fastify, modules);
1001
+ let eventUnsubscribes = [];
1002
+ try {
1003
+ if (config.plugins) {
1004
+ await config.plugins(fastify);
1005
+ fastify.log.debug("Custom plugins registered");
746
1006
  }
747
- if (exported !== void 0) {
748
- const arc = fastify.arc;
749
- if (!arc) throw new Error(`[arc] module "${m.name}" returned a public export but fastify.arc is not decorated — arcCorePlugin must register first.`);
750
- if (!arc.modules) arc.modules = Object.create(null);
751
- arc.modules[m.name] = exported;
1007
+ for (const m of modules) {
1008
+ if (!m.plugins) continue;
1009
+ teardown.markInitialized(m);
1010
+ try {
1011
+ await m.plugins(fastify);
1012
+ } catch (err) {
1013
+ setModuleState(fastify, m.name, "failed");
1014
+ const msg = err instanceof Error ? err.message : String(err);
1015
+ throw new Error(`[arc] module "${m.name}" plugins() threw: ${msg}`, { cause: err });
1016
+ }
752
1017
  }
753
- }
754
- if (modules.length) fastify.log.debug(`${modules.length} module(s) composed (in dependency order): ${modules.map((m) => m.name).join(" → ")}`);
755
- if (config.bootstrap?.length) {
756
- for (const init of config.bootstrap) await init(fastify);
757
- fastify.log.debug(`${config.bootstrap.length} bootstrap function(s) executed`);
758
- }
759
- let resolvedResources;
760
- if (isResourcesFactory(config.resources)) {
761
- try {
762
- resolvedResources = await config.resources(fastify);
763
- } catch (err) {
764
- const msg = err instanceof Error ? err.message : String(err);
765
- fastify.log.error(`Resources factory threw during boot: ${msg}`);
766
- throw new Error(`[arc] resources factory threw: ${msg}. Check engine bootstrap order (did you forget a bootstrap step?) and that \`defineResource(...)\` calls inside the factory receive fully-booted adapters / repositories.`, { cause: err });
1018
+ for (const m of modules) {
1019
+ if (!m.bootstrap) {
1020
+ setModuleState(fastify, m.name, "ready");
1021
+ teardown.markInitialized(m);
1022
+ continue;
1023
+ }
1024
+ let exported;
1025
+ teardown.markInitialized(m);
1026
+ setModuleState(fastify, m.name, "bootstrapping");
1027
+ try {
1028
+ exported = await m.bootstrap(fastify);
1029
+ } catch (err) {
1030
+ setModuleState(fastify, m.name, "failed");
1031
+ const msg = err instanceof Error ? err.message : String(err);
1032
+ throw new Error(`[arc] module "${m.name}" bootstrap threw: ${msg}`, { cause: err });
1033
+ }
1034
+ setModuleState(fastify, m.name, "ready");
1035
+ if (exported !== void 0) {
1036
+ const arc = fastify.arc;
1037
+ if (!arc) throw new Error(`[arc] module "${m.name}" returned a public export but fastify.arc is not decorated — arcCorePlugin must register first.`);
1038
+ if (!arc.modules) arc.modules = Object.create(null);
1039
+ arc.modules[m.name] = exported;
1040
+ }
767
1041
  }
768
- config = {
769
- ...config,
770
- resources: resolvedResources
771
- };
772
- } else resolvedResources = config.resources;
773
- let discoveryRawDir;
774
- let discoveryPath;
775
- let discoveryYieldedZero = false;
776
- if (resolvedResources === void 0 && config.resourceDir) {
777
- const { loadResources } = await import("./loadResources-DpU8pfjV.mjs").then((n) => n.n);
778
- const { resolve, dirname } = await import("node:path");
779
- const { fileURLToPath } = await import("node:url");
780
- const rawDir = config.resourceDir;
781
- const dir = rawDir.startsWith("file://") ? dirname(fileURLToPath(rawDir)) : resolve(rawDir);
782
- discoveryRawDir = rawDir;
783
- discoveryPath = dir;
784
- const discovered = await loadResources(dir, { logger: fastify.log });
785
- if (discovered.length === 0) {
786
- if (config.strictResourceDir) throw new Error(`[arc] loadResources: resourceDir "${rawDir}" resolved to "${dir}" but yielded 0 resources. Check the path, file naming (*.resource.{ts,js,mts,mjs}), and runtime layout (src/ vs dist/). Use \`strictResourceDir: true\` to fail boot.`);
787
- discoveryYieldedZero = true;
1042
+ if (modules.length) fastify.log.debug(`${modules.length} module(s) composed (in dependency order): ${modules.map((m) => m.name).join(" → ")}`);
1043
+ if (config.bootstrap?.length) {
1044
+ for (const init of config.bootstrap) await init(fastify);
1045
+ fastify.log.debug(`${config.bootstrap.length} bootstrap function(s) executed`);
788
1046
  }
789
- resolvedResources = discovered;
790
- }
791
- if (modules.length && resolvedResources && resolvedResources.length > 0) {
792
- const owned = /* @__PURE__ */ new Set();
793
- for (const m of modules) for (const name of m.owns ?? []) owned.add(name);
794
- if (owned.size > 0) {
795
- const before = resolvedResources.length;
796
- resolvedResources = resolvedResources.filter((r) => !(r.name != null && owned.has(r.name)));
797
- const dropped = before - resolvedResources.length;
798
- if (dropped > 0) fastify.log.debug(`[arc] ${dropped} app resource(s) superseded by module owns() — the module version registers instead`);
1047
+ let resolvedResources;
1048
+ if (isResourcesFactory(config.resources)) {
1049
+ try {
1050
+ resolvedResources = await config.resources(fastify);
1051
+ } catch (err) {
1052
+ const msg = err instanceof Error ? err.message : String(err);
1053
+ fastify.log.error(`Resources factory threw during boot: ${msg}`);
1054
+ throw new Error(`[arc] resources factory threw: ${msg}. Check engine bootstrap order (did you forget a bootstrap step?) and that \`defineResource(...)\` calls inside the factory receive fully-booted adapters / repositories.`, { cause: err });
1055
+ }
1056
+ config = {
1057
+ ...config,
1058
+ resources: resolvedResources
1059
+ };
1060
+ } else resolvedResources = config.resources;
1061
+ let discoveryRawDir;
1062
+ let discoveryPath;
1063
+ let discoveryYieldedZero = false;
1064
+ if (resolvedResources === void 0 && config.resourceDir) {
1065
+ const { loadResources } = await import("./loadResources-yawi9tAE.mjs").then((n) => n.n);
1066
+ const { resolve, dirname } = await import("node:path");
1067
+ const { fileURLToPath } = await import("node:url");
1068
+ const rawDir = config.resourceDir;
1069
+ const dir = rawDir.startsWith("file://") ? dirname(fileURLToPath(rawDir)) : resolve(rawDir);
1070
+ discoveryRawDir = rawDir;
1071
+ discoveryPath = dir;
1072
+ const discovered = await loadResources(dir, { logger: fastify.log });
1073
+ if (discovered.length === 0) {
1074
+ if (config.strictResourceDir) throw new Error(`[arc] loadResources: resourceDir "${rawDir}" resolved to "${dir}" but yielded 0 resources. Check the path, file naming (*.resource.{ts,js,mts,mjs}), and runtime layout (src/ vs dist/). Use \`strictResourceDir: true\` to fail boot.`);
1075
+ discoveryYieldedZero = true;
1076
+ }
1077
+ resolvedResources = discovered;
799
1078
  }
800
- }
801
- if (modules.length) {
1079
+ const moduleResourceNames = /* @__PURE__ */ new Map();
802
1080
  const moduleResources = [];
803
1081
  for (const m of modules) {
804
1082
  if (!m.resources) continue;
1083
+ let rs;
805
1084
  try {
806
- const rs = typeof m.resources === "function" ? await m.resources(fastify) : m.resources;
807
- moduleResources.push(...rs);
1085
+ rs = typeof m.resources === "function" ? await m.resources(fastify) : m.resources;
808
1086
  } catch (err) {
809
1087
  const msg = err instanceof Error ? err.message : String(err);
810
1088
  throw new Error(`[arc] module "${m.name}" resources factory threw: ${msg}. Check the module's bootstrap ran and its adapters received booted engines.`, { cause: err });
811
1089
  }
1090
+ const names = /* @__PURE__ */ new Set();
1091
+ for (const r of rs) if (r.name != null) names.add(r.name);
1092
+ moduleResourceNames.set(m.name, names);
1093
+ moduleResources.push(...rs);
1094
+ }
1095
+ for (const m of modules) {
1096
+ if (!m.owns?.length) continue;
1097
+ const supplied = moduleResourceNames.get(m.name);
1098
+ const missing = m.owns.filter((name) => !supplied?.has(name));
1099
+ if (missing.length > 0) {
1100
+ const provided = supplied?.size ? [...supplied].join(", ") : "(none)";
1101
+ throw new Error(`[arc] module "${m.name}" declares owns: [${missing.map((n) => `"${n}"`).join(", ")}] but its own \`resources\` do not supply ${missing.length === 1 ? "that name" : "those names"}. \`owns\` DROPS the app-level resource of that name, so an unmet claim removes the route entirely and nothing serves it. Provide it in this module's \`resources\`, or drop the \`owns\` entry.
1102
+ Resources supplied by "${m.name}": ${provided}.`);
1103
+ }
1104
+ }
1105
+ if (modules.length && resolvedResources && resolvedResources.length > 0) {
1106
+ const owned = /* @__PURE__ */ new Set();
1107
+ for (const m of modules) for (const name of m.owns ?? []) owned.add(name);
1108
+ if (owned.size > 0) {
1109
+ const before = resolvedResources.length;
1110
+ resolvedResources = resolvedResources.filter((r) => !(r.name != null && owned.has(r.name)));
1111
+ const dropped = before - resolvedResources.length;
1112
+ if (dropped > 0) fastify.log.debug(`[arc] ${dropped} app resource(s) superseded by module owns() — the module version registers instead`);
1113
+ }
812
1114
  }
813
1115
  if (moduleResources.length) resolvedResources = [...moduleResources, ...resolvedResources ?? []];
814
- }
815
- if (resolvedResources && resolvedResources.length > 0) {
816
- const seen = /* @__PURE__ */ new Set();
817
- for (const resource of resolvedResources) if (resource.name) {
818
- if (seen.has(resource.name)) {
819
- const msg = `Duplicate resource name "${resource.name}" detected. This will cause route conflicts. Check your resources array and loadResources() output. Common cause: stale compiled files in dist/ alongside src/. Use \`strictResources: true\` to fail boot.`;
820
- if (config.strictResources) throw new Error(msg);
821
- fastify.log.warn(msg);
1116
+ if (resolvedResources && resolvedResources.length > 0) {
1117
+ const seen = /* @__PURE__ */ new Set();
1118
+ for (const resource of resolvedResources) if (resource.name) {
1119
+ if (seen.has(resource.name)) {
1120
+ const msg = `Duplicate resource name "${resource.name}" detected. This will cause route conflicts. Check your resources array and loadResources() output. Common cause: stale compiled files in dist/ alongside src/. Use \`strictResources: true\` to fail boot.`;
1121
+ if (config.strictResources) throw new Error(msg);
1122
+ fastify.log.warn(msg);
1123
+ }
1124
+ seen.add(resource.name);
822
1125
  }
823
- seen.add(resource.name);
1126
+ const prefixed = [];
1127
+ const root = [];
1128
+ for (const resource of resolvedResources) if (resource.skipGlobalPrefix) root.push(resource);
1129
+ else prefixed.push(resource);
1130
+ for (const resource of root) await registerOne(fastify, resource, config.mountRoutes);
1131
+ if (prefixed.length) if (config.resourcePrefix) await fastify.register(async (scoped) => {
1132
+ for (const resource of prefixed) await registerOne(scoped, resource, config.mountRoutes);
1133
+ }, { prefix: config.resourcePrefix });
1134
+ else for (const resource of prefixed) await registerOne(fastify, resource, config.mountRoutes);
1135
+ const names = resolvedResources.map((r) => r.name ?? "?").join(", ");
1136
+ const prefix = config.resourcePrefix ? ` (prefix: ${config.resourcePrefix})` : "";
1137
+ fastify.log.info(`${resolvedResources.length} resource(s) registered${prefix}: ${names}`);
1138
+ } else {
1139
+ const prefix = config.resourcePrefix ? ` (prefix: ${config.resourcePrefix})` : "";
1140
+ const scanned = discoveryPath ? ` — resourceDir "${discoveryRawDir}" resolved to "${discoveryPath}"` : "";
1141
+ const hints = discoveryYieldedZero ? ` but yielded 0 resources. Check the path, file naming (*.resource.{ts,js,mts,mjs}), and runtime layout (src/ vs dist/). Use \`strictResourceDir: true\` to fail boot.` : "";
1142
+ fastify.log.warn(`0 resources registered${prefix}${scanned}${hints}`);
824
1143
  }
825
- const prefixed = [];
826
- const root = [];
827
- for (const resource of resolvedResources) if (resource.skipGlobalPrefix) root.push(resource);
828
- else prefixed.push(resource);
829
- for (const resource of root) await registerOne(fastify, resource, config.mountRoutes);
830
- if (prefixed.length) if (config.resourcePrefix) await fastify.register(async (scoped) => {
831
- for (const resource of prefixed) await registerOne(scoped, resource, config.mountRoutes);
832
- }, { prefix: config.resourcePrefix });
833
- else for (const resource of prefixed) await registerOne(fastify, resource, config.mountRoutes);
834
- const names = resolvedResources.map((r) => r.name ?? "?").join(", ");
835
- const prefix = config.resourcePrefix ? ` (prefix: ${config.resourcePrefix})` : "";
836
- fastify.log.info(`${resolvedResources.length} resource(s) registered${prefix}: ${names}`);
837
- } else {
838
- const prefix = config.resourcePrefix ? ` (prefix: ${config.resourcePrefix})` : "";
839
- const scanned = discoveryPath ? ` — resourceDir "${discoveryRawDir}" resolved to "${discoveryPath}"` : "";
840
- const hints = discoveryYieldedZero ? ` but yielded 0 resources. Check the path, file naming (*.resource.{ts,js,mts,mjs}), and runtime layout (src/ vs dist/). Use \`strictResourceDir: true\` to fail boot.` : "";
841
- fastify.log.warn(`0 resources registered${prefix}${scanned}${hints}`);
842
- }
843
- for (const m of modules) {
844
- if (!m.afterResources) continue;
845
- try {
846
- await m.afterResources(fastify);
847
- } catch (err) {
848
- const msg = err instanceof Error ? err.message : String(err);
849
- throw new Error(`[arc] module "${m.name}" afterResources() threw: ${msg}`, { cause: err });
1144
+ for (const m of modules) {
1145
+ if (!m.afterResources) continue;
1146
+ try {
1147
+ await m.afterResources(fastify);
1148
+ } catch (err) {
1149
+ const msg = err instanceof Error ? err.message : String(err);
1150
+ throw new Error(`[arc] module "${m.name}" afterResources() threw: ${msg}`, { cause: err });
1151
+ }
850
1152
  }
851
- }
852
- const scheduledJobs = await collectModuleScheduledJobs(fastify, modules);
853
- if (scheduledJobs.length > 0) {
854
- if (config.arcPlugins?.schedules === false) throw new Error("[arc] modules declare scheduledJobs but arcPlugins.schedules is false. Enable the scheduler or remove the declarations.");
855
- if (fastify.hasDecorator("getScheduleStats")) throw new Error("[arc] modules declare scheduledJobs but schedulesPlugin was already registered manually. Configure their runner through arcPlugins.schedules so Arc can compose one schedule table.");
856
- const frozenJobs = Object.freeze(scheduledJobs.map((job) => Object.freeze({ ...job })));
857
- const scheduleOptions = typeof config.arcPlugins?.schedules === "object" ? config.arcPlugins.schedules : {};
858
- const { default: schedulesPlugin } = await import("./schedules-CjOd2MG9.mjs").then((n) => n.r);
859
- await fastify.register(schedulesPlugin, {
860
- ...scheduleOptions,
861
- schedules: frozenJobs
862
- });
863
- if (fastify.arc) {
864
- fastify.arc.scheduledJobs = frozenJobs;
865
- fastify.arc.plugins.set("arc-schedules", {
866
- name: "arc-schedules",
867
- options: { scheduleCount: frozenJobs.length },
868
- registeredAt: (/* @__PURE__ */ new Date()).toISOString()
1153
+ const scheduledJobs = await collectModuleScheduledJobs(fastify, modules);
1154
+ if (scheduledJobs.length > 0) {
1155
+ if (config.arcPlugins?.schedules === false) throw new Error("[arc] modules declare scheduledJobs but arcPlugins.schedules is false. Enable the scheduler or remove the declarations.");
1156
+ if (fastify.hasDecorator("getScheduleStats")) throw new Error("[arc] modules declare scheduledJobs but schedulesPlugin was already registered manually. Configure their runner through arcPlugins.schedules so Arc can compose one schedule table.");
1157
+ const frozenJobs = Object.freeze(scheduledJobs.map((job) => Object.freeze({ ...job })));
1158
+ const scheduleOptions = typeof config.arcPlugins?.schedules === "object" ? config.arcPlugins.schedules : {};
1159
+ const { default: schedulesPlugin } = await import("./schedules-DdtlICTf.mjs").then((n) => n.r);
1160
+ await fastify.register(schedulesPlugin, {
1161
+ ...scheduleOptions,
1162
+ schedules: frozenJobs
869
1163
  });
1164
+ if (fastify.arc) {
1165
+ fastify.arc.scheduledJobs = frozenJobs;
1166
+ fastify.arc.plugins.set("arc-schedules", {
1167
+ name: "arc-schedules",
1168
+ options: { scheduleCount: frozenJobs.length },
1169
+ registeredAt: (/* @__PURE__ */ new Date()).toISOString()
1170
+ });
1171
+ }
1172
+ }
1173
+ eventUnsubscribes = await subscribeModuleEventHandlers(fastify, modules);
1174
+ if (config.afterResources) {
1175
+ await config.afterResources(fastify);
1176
+ fastify.log.debug("afterResources hook executed");
870
1177
  }
871
- }
872
- const eventUnsubscribes = await subscribeModuleEventHandlers(fastify, modules);
873
- if (config.afterResources) try {
874
- await config.afterResources(fastify);
875
- fastify.log.debug("afterResources hook executed");
876
1178
  } catch (err) {
877
- const rollbackErrors = await unsubscribeModuleEventHandlers(eventUnsubscribes);
878
- for (const rollbackError of rollbackErrors) fastify.log.error({ err: rollbackError }, "[arc] module event-handler rollback after afterResources failure failed");
1179
+ const unsubscribeErrors = await unsubscribeModuleEventHandlers(eventUnsubscribes);
1180
+ for (const unsubscribeError of unsubscribeErrors) fastify.log.error({ err: unsubscribeError }, "[arc] module event-handler rollback after boot failure failed");
1181
+ const closeErrors = await teardown.rollback();
1182
+ for (const closeError of closeErrors) fastify.log.error({ err: closeError }, "[arc] module onClose rollback after boot failure failed");
879
1183
  throw err;
880
1184
  }
881
1185
  if (config.onReady) {
@@ -884,13 +1188,14 @@ async function registerResources(fastify, config, preResolvedModules) {
884
1188
  await onReady(fastify);
885
1189
  });
886
1190
  }
887
- const closers = modules.filter((m) => m.onClose).reverse();
888
1191
  const appOnClose = config.onClose;
889
- if (closers.length || appOnClose || eventUnsubscribes.length) fastify.addHook("onClose", async () => {
1192
+ if (teardown.hasComposedModules() || appOnClose || eventUnsubscribes.length) fastify.addHook("onClose", async () => {
890
1193
  const unsubscribeErrors = await unsubscribeModuleEventHandlers(eventUnsubscribes);
891
1194
  for (const err of unsubscribeErrors) fastify.log.error({ err }, "[arc] module event-handler shutdown unsubscribe failed");
892
- for (const m of closers) await m.onClose?.(fastify);
1195
+ const closeErrors = await teardown.closeAll();
1196
+ for (const err of closeErrors) fastify.log.error({ err }, "[arc] module onClose failed during shutdown");
893
1197
  if (appOnClose) await appOnClose(fastify);
1198
+ if (closeErrors.length > 0) throw closeErrors[0];
894
1199
  });
895
1200
  }
896
1201
  //#endregion
@@ -956,56 +1261,6 @@ function resolveCorsOptions(config) {
956
1261
  };
957
1262
  }
958
1263
  //#endregion
959
- //#region src/factory/security/pluginLoader.ts
960
- const PLUGIN_REGISTRY = {
961
- cors: {
962
- package: "@fastify/cors",
963
- loader: () => import("@fastify/cors").then((m) => m.default)
964
- },
965
- helmet: {
966
- package: "@fastify/helmet",
967
- loader: () => import("@fastify/helmet").then((m) => m.default)
968
- },
969
- rateLimit: {
970
- package: "@fastify/rate-limit",
971
- loader: () => import("@fastify/rate-limit").then((m) => m.default)
972
- },
973
- underPressure: {
974
- package: "@fastify/under-pressure",
975
- loader: () => import("@fastify/under-pressure").then((m) => m.default)
976
- },
977
- sensible: {
978
- package: "@fastify/sensible",
979
- loader: () => import("@fastify/sensible").then((m) => m.default)
980
- },
981
- multipart: {
982
- package: "@fastify/multipart",
983
- loader: () => import("@fastify/multipart").then((m) => m.default),
984
- optional: true
985
- },
986
- rawBody: {
987
- package: "fastify-raw-body",
988
- loader: () => import("fastify-raw-body").then((m) => m.default),
989
- optional: true
990
- }
991
- };
992
- async function loadPlugin(name, logger) {
993
- const entry = PLUGIN_REGISTRY[name];
994
- if (!entry) throw new Error(`Unknown plugin: ${name}`);
995
- try {
996
- return await entry.loader();
997
- } catch (error) {
998
- const err = error;
999
- const isModuleNotFound = err.message.includes("Cannot find module") || err.message.includes("Cannot find package") || err.message.includes("MODULE_NOT_FOUND") || err.message.includes("Could not resolve");
1000
- if (isModuleNotFound && entry.optional) {
1001
- logger?.warn(`Optional plugin '${name}' skipped (${entry.package} not installed)`);
1002
- return null;
1003
- }
1004
- if (isModuleNotFound) throw new Error(`Plugin '${name}' requires package '${entry.package}' which is not installed.\nInstall it with: npm install ${entry.package}\nOr disable this plugin by setting ${name}: false in createApp options.`);
1005
- throw new Error(`Failed to load plugin '${name}': ${err.message}`);
1006
- }
1007
- }
1008
- //#endregion
1009
1264
  //#region src/factory/security/rateLimit.ts
1010
1265
  /** "Unlimited" as a number — @fastify/rate-limit's `max` fn must return one. */
1011
1266
  const UNLIMITED_MAX = Number.MAX_SAFE_INTEGER;
@@ -1329,8 +1584,26 @@ Missing:\n${lines}\n\nFix: pass durable adapters under \`stores\`, or use runtim
1329
1584
  * 10. afterResources() — post-registration wiring
1330
1585
  * 11. onReady/onClose — lifecycle hooks
1331
1586
  * ```
1587
+ *
1588
+ * **Boot is all-or-nothing.** If any phase throws, the partially-built
1589
+ * instance is closed before the error propagates — see the wrapper below.
1332
1590
  */
1333
1591
  async function createApp(options) {
1592
+ let partial;
1593
+ try {
1594
+ return await buildApp(options, (instance) => {
1595
+ partial = instance;
1596
+ });
1597
+ } catch (err) {
1598
+ if (partial) try {
1599
+ await partial.close();
1600
+ } catch (closeErr) {
1601
+ partial.log?.error?.({ err: closeErr }, "[arc] cleanup of a partially-booted app failed; original boot error follows");
1602
+ }
1603
+ throw err;
1604
+ }
1605
+ }
1606
+ async function buildApp(options, onInstance) {
1334
1607
  if (options.debug !== void 0 && options.debug !== false) configureArcLogger({ debug: options.debug });
1335
1608
  validateAuthOptions(options);
1336
1609
  const deferredWarnings = validateDistributedRuntime(options);
@@ -1371,6 +1644,7 @@ async function createApp(options) {
1371
1644
  keywords: ["example", ...config.ajv?.keywords ?? []]
1372
1645
  } }
1373
1646
  });
1647
+ onInstance(fastify);
1374
1648
  if (resolvedLogger !== false) configureArcLogger({
1375
1649
  ...options.debug !== void 0 && options.debug !== false ? { debug: options.debug } : {},
1376
1650
  writer: createPinoWriter(fastify.log)
@@ -1386,6 +1660,7 @@ async function createApp(options) {
1386
1660
  }
1387
1661
  });
1388
1662
  await registerSecurityPlugins(fastify, config);
1663
+ if (config.assets?.length) await registerAssetRoots(fastify, config.assets);
1389
1664
  await registerUtilityPlugins(fastify, config);
1390
1665
  const trackPlugin = (name, opts) => {
1391
1666
  const arc = fastify.arc;
@@ -1408,7 +1683,7 @@ async function createApp(options) {
1408
1683
  await registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers);
1409
1684
  await registerResources(fastify, config, resolvedModules);
1410
1685
  if (config.replyHelpers) {
1411
- const { replyHelpersPlugin } = await import("./replyHelpers-67tRawQZ.mjs").then((n) => n.n);
1686
+ const { replyHelpersPlugin } = await import("./replyHelpers-DIiZKGUt.mjs").then((n) => n.n);
1412
1687
  await fastify.register(replyHelpersPlugin);
1413
1688
  }
1414
1689
  if (config.serializeBigInt) {
@@ -1459,4 +1734,4 @@ const ArcFactory = {
1459
1734
  }
1460
1735
  };
1461
1736
  //#endregion
1462
- export { edgePreset as a, testingPreset as c, subscribeModuleEventHandlers as d, collectModuleHealthChecks as f, resolveModule as g, resolveContribution as h, developmentPreset as i, workerPreset as l, collectModuleWorkflows as m, createApp as n, getPreset as o, collectModuleScheduledJobs as p, createApp_exports as r, productionPreset as s, ArcFactory as t, orderModules as u };
1737
+ export { resolveContribution as _, edgePreset as a, testingPreset as c, hasModule as d, orderModules as f, collectModuleWorkflows as g, collectModuleScheduledJobs as h, developmentPreset as i, workerPreset as l, collectModuleHealthChecks as m, createApp as n, getPreset as o, subscribeModuleEventHandlers as p, createApp_exports as r, productionPreset as s, ArcFactory as t, getModuleState as u, resolveModule as v };