every-plugin 2.10.0 → 3.0.0-rc.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 (462) hide show
  1. package/bin/every-plugin-serve.mjs +12 -0
  2. package/bin/every-plugin.mjs +59 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +1 -2
  4. package/dist/build/artifact-names.cjs +119 -0
  5. package/dist/build/artifact-names.cjs.map +1 -0
  6. package/dist/build/artifact-names.d.cts +68 -0
  7. package/dist/build/artifact-names.d.cts.map +1 -0
  8. package/dist/build/artifact-names.d.mts +68 -0
  9. package/dist/build/artifact-names.d.mts.map +1 -0
  10. package/dist/build/artifact-names.mjs +102 -0
  11. package/dist/build/artifact-names.mjs.map +1 -0
  12. package/dist/build/contract-types.cjs +89 -0
  13. package/dist/build/contract-types.cjs.map +1 -0
  14. package/dist/build/contract-types.mjs +81 -0
  15. package/dist/build/contract-types.mjs.map +1 -0
  16. package/dist/build/rspack/build-report-plugin.cjs +37 -0
  17. package/dist/build/rspack/build-report-plugin.cjs.map +1 -0
  18. package/dist/build/rspack/build-report-plugin.d.cts +18 -0
  19. package/dist/build/rspack/build-report-plugin.d.cts.map +1 -0
  20. package/dist/build/rspack/build-report-plugin.d.mts +18 -0
  21. package/dist/build/rspack/build-report-plugin.d.mts.map +1 -0
  22. package/dist/build/rspack/build-report-plugin.mjs +37 -0
  23. package/dist/build/rspack/build-report-plugin.mjs.map +1 -0
  24. package/dist/build/rspack/chunk-completeness-plugin.cjs +47 -0
  25. package/dist/build/rspack/chunk-completeness-plugin.cjs.map +1 -0
  26. package/dist/build/rspack/chunk-completeness-plugin.mjs +47 -0
  27. package/dist/build/rspack/chunk-completeness-plugin.mjs.map +1 -0
  28. package/dist/build/rspack/compose.cjs +69 -0
  29. package/dist/build/rspack/compose.cjs.map +1 -0
  30. package/dist/build/rspack/compose.d.cts +20 -0
  31. package/dist/build/rspack/compose.d.cts.map +1 -0
  32. package/dist/build/rspack/compose.d.mts +20 -0
  33. package/dist/build/rspack/compose.d.mts.map +1 -0
  34. package/dist/build/rspack/compose.mjs +65 -0
  35. package/dist/build/rspack/compose.mjs.map +1 -0
  36. package/dist/build/rspack/fix-mf-data-uri-plugin.cjs +11 -11
  37. package/dist/build/rspack/fix-mf-data-uri-plugin.cjs.map +1 -1
  38. package/dist/build/rspack/fix-mf-data-uri-plugin.d.cts +2 -0
  39. package/dist/build/rspack/fix-mf-data-uri-plugin.d.cts.map +1 -1
  40. package/dist/build/rspack/fix-mf-data-uri-plugin.d.mts +2 -0
  41. package/dist/build/rspack/fix-mf-data-uri-plugin.d.mts.map +1 -1
  42. package/dist/build/rspack/fix-mf-data-uri-plugin.mjs +11 -10
  43. package/dist/build/rspack/fix-mf-data-uri-plugin.mjs.map +1 -1
  44. package/dist/build/rspack/generated-config.cjs +35 -0
  45. package/dist/build/rspack/generated-config.cjs.map +1 -0
  46. package/dist/build/rspack/generated-config.mjs +32 -0
  47. package/dist/build/rspack/generated-config.mjs.map +1 -0
  48. package/dist/build/rspack/index.cjs +8 -5
  49. package/dist/build/rspack/index.d.cts +5 -3
  50. package/dist/build/rspack/index.d.mts +5 -3
  51. package/dist/build/rspack/index.mjs +5 -3
  52. package/dist/build/rspack/module-federation.cjs +2 -2
  53. package/dist/build/rspack/module-federation.cjs.map +1 -1
  54. package/dist/build/rspack/module-federation.mjs +1 -1
  55. package/dist/build/rspack/module-federation.mjs.map +1 -1
  56. package/dist/build/rspack/plugin.cjs +44 -69
  57. package/dist/build/rspack/plugin.cjs.map +1 -1
  58. package/dist/build/rspack/plugin.d.cts +4 -13
  59. package/dist/build/rspack/plugin.d.cts.map +1 -1
  60. package/dist/build/rspack/plugin.d.mts +4 -13
  61. package/dist/build/rspack/plugin.d.mts.map +1 -1
  62. package/dist/build/rspack/plugin.mjs +44 -69
  63. package/dist/build/rspack/plugin.mjs.map +1 -1
  64. package/dist/build/rspack/utils.cjs +7 -5
  65. package/dist/build/rspack/utils.cjs.map +1 -1
  66. package/dist/build/rspack/utils.mjs +7 -6
  67. package/dist/build/rspack/utils.mjs.map +1 -1
  68. package/dist/build/ui/factory.cjs +88 -0
  69. package/dist/build/ui/factory.cjs.map +1 -0
  70. package/dist/build/ui/factory.d.cts +11 -0
  71. package/dist/build/ui/factory.d.cts.map +1 -0
  72. package/dist/build/ui/factory.d.mts +11 -0
  73. package/dist/build/ui/factory.d.mts.map +1 -0
  74. package/dist/build/ui/factory.mjs +80 -0
  75. package/dist/build/ui/factory.mjs.map +1 -0
  76. package/dist/build/ui/generated-config.cjs +118 -0
  77. package/dist/build/ui/generated-config.cjs.map +1 -0
  78. package/dist/build/ui/generated-config.d.cts +33 -0
  79. package/dist/build/ui/generated-config.d.cts.map +1 -0
  80. package/dist/build/ui/generated-config.d.mts +33 -0
  81. package/dist/build/ui/generated-config.d.mts.map +1 -0
  82. package/dist/build/ui/generated-config.mjs +111 -0
  83. package/dist/build/ui/generated-config.mjs.map +1 -0
  84. package/dist/build/ui/hash-artifacts-plugin.cjs +61 -0
  85. package/dist/build/ui/hash-artifacts-plugin.cjs.map +1 -0
  86. package/dist/build/ui/hash-artifacts-plugin.d.cts +20 -0
  87. package/dist/build/ui/hash-artifacts-plugin.d.cts.map +1 -0
  88. package/dist/build/ui/hash-artifacts-plugin.d.mts +20 -0
  89. package/dist/build/ui/hash-artifacts-plugin.d.mts.map +1 -0
  90. package/dist/build/ui/hash-artifacts-plugin.mjs +58 -0
  91. package/dist/build/ui/hash-artifacts-plugin.mjs.map +1 -0
  92. package/dist/build/ui/index.cjs +90 -0
  93. package/dist/build/ui/index.cjs.map +1 -0
  94. package/dist/build/ui/index.d.cts +29 -0
  95. package/dist/build/ui/index.d.cts.map +1 -0
  96. package/dist/build/ui/index.d.mts +29 -0
  97. package/dist/build/ui/index.d.mts.map +1 -0
  98. package/dist/build/ui/index.mjs +68 -0
  99. package/dist/build/ui/index.mjs.map +1 -0
  100. package/dist/build/ui/manifest-plugin.cjs +37 -0
  101. package/dist/build/ui/manifest-plugin.cjs.map +1 -0
  102. package/dist/build/ui/manifest-plugin.d.cts +19 -0
  103. package/dist/build/ui/manifest-plugin.d.cts.map +1 -0
  104. package/dist/build/ui/manifest-plugin.d.mts +19 -0
  105. package/dist/build/ui/manifest-plugin.d.mts.map +1 -0
  106. package/dist/build/ui/manifest-plugin.mjs +37 -0
  107. package/dist/build/ui/manifest-plugin.mjs.map +1 -0
  108. package/dist/build/ui/rsbuild-config.cjs +203 -0
  109. package/dist/build/ui/rsbuild-config.cjs.map +1 -0
  110. package/dist/build/ui/rsbuild-config.d.cts +45 -0
  111. package/dist/build/ui/rsbuild-config.d.cts.map +1 -0
  112. package/dist/build/ui/rsbuild-config.d.mts +45 -0
  113. package/dist/build/ui/rsbuild-config.d.mts.map +1 -0
  114. package/dist/build/ui/rsbuild-config.mjs +200 -0
  115. package/dist/build/ui/rsbuild-config.mjs.map +1 -0
  116. package/dist/build/ui/stubs.cjs +107 -0
  117. package/dist/build/ui/stubs.cjs.map +1 -0
  118. package/dist/build/ui/stubs.d.cts +25 -0
  119. package/dist/build/ui/stubs.d.cts.map +1 -0
  120. package/dist/build/ui/stubs.d.mts +25 -0
  121. package/dist/build/ui/stubs.d.mts.map +1 -0
  122. package/dist/build/ui/stubs.mjs +106 -0
  123. package/dist/build/ui/stubs.mjs.map +1 -0
  124. package/dist/cli.cjs +83 -0
  125. package/dist/cli.cjs.map +1 -0
  126. package/dist/cli.d.cts +6 -0
  127. package/dist/cli.d.cts.map +1 -0
  128. package/dist/cli.d.mts +6 -0
  129. package/dist/cli.d.mts.map +1 -0
  130. package/dist/cli.mjs +82 -0
  131. package/dist/cli.mjs.map +1 -0
  132. package/dist/dev/serve.cjs +390 -0
  133. package/dist/dev/serve.cjs.map +1 -0
  134. package/dist/dev/serve.d.cts +23 -0
  135. package/dist/dev/serve.d.cts.map +1 -0
  136. package/dist/dev/serve.d.mts +23 -0
  137. package/dist/dev/serve.d.mts.map +1 -0
  138. package/dist/dev/serve.mjs +384 -0
  139. package/dist/dev/serve.mjs.map +1 -0
  140. package/dist/dev/watch-kill.cjs +39 -0
  141. package/dist/dev/watch-kill.cjs.map +1 -0
  142. package/dist/dev/watch-kill.mjs +38 -0
  143. package/dist/dev/watch-kill.mjs.map +1 -0
  144. package/dist/effect-helpers.cjs +23 -0
  145. package/dist/effect-helpers.cjs.map +1 -0
  146. package/dist/effect-helpers.d.cts +20 -0
  147. package/dist/effect-helpers.d.cts.map +1 -0
  148. package/dist/effect-helpers.d.mts +20 -0
  149. package/dist/effect-helpers.d.mts.map +1 -0
  150. package/dist/effect-helpers.mjs +22 -0
  151. package/dist/effect-helpers.mjs.map +1 -0
  152. package/dist/errors.cjs +49 -69
  153. package/dist/errors.cjs.map +1 -1
  154. package/dist/errors.d.cts +13 -96
  155. package/dist/errors.d.cts.map +1 -1
  156. package/dist/errors.d.mts +13 -96
  157. package/dist/errors.d.mts.map +1 -1
  158. package/dist/errors.mjs +16 -34
  159. package/dist/errors.mjs.map +1 -1
  160. package/dist/identity.cjs +19 -0
  161. package/dist/identity.cjs.map +1 -0
  162. package/dist/identity.d.cts +33 -0
  163. package/dist/identity.d.cts.map +1 -0
  164. package/dist/identity.d.mts +33 -0
  165. package/dist/identity.d.mts.map +1 -0
  166. package/dist/identity.mjs +16 -0
  167. package/dist/identity.mjs.map +1 -0
  168. package/dist/index.cjs +8 -5
  169. package/dist/index.d.cts +8 -6
  170. package/dist/index.d.mts +8 -6
  171. package/dist/index.mjs +6 -4
  172. package/dist/plugin.cjs +12 -26
  173. package/dist/plugin.cjs.map +1 -1
  174. package/dist/plugin.d.cts +44 -25
  175. package/dist/plugin.d.cts.map +1 -1
  176. package/dist/plugin.d.mts +45 -26
  177. package/dist/plugin.d.mts.map +1 -1
  178. package/dist/plugin.mjs +15 -29
  179. package/dist/plugin.mjs.map +1 -1
  180. package/dist/remote-entry.cjs +103 -0
  181. package/dist/remote-entry.cjs.map +1 -0
  182. package/dist/remote-entry.d.cts +53 -0
  183. package/dist/remote-entry.d.cts.map +1 -0
  184. package/dist/remote-entry.d.mts +53 -0
  185. package/dist/remote-entry.d.mts.map +1 -0
  186. package/dist/remote-entry.mjs +98 -0
  187. package/dist/remote-entry.mjs.map +1 -0
  188. package/dist/runtime/errors.cjs +140 -34
  189. package/dist/runtime/errors.cjs.map +1 -1
  190. package/dist/runtime/errors.d.cts +42 -7
  191. package/dist/runtime/errors.d.cts.map +1 -1
  192. package/dist/runtime/errors.d.mts +42 -7
  193. package/dist/runtime/errors.d.mts.map +1 -1
  194. package/dist/runtime/errors.mjs +136 -31
  195. package/dist/runtime/errors.mjs.map +1 -1
  196. package/dist/runtime/index.cjs +95 -23
  197. package/dist/runtime/index.cjs.map +1 -1
  198. package/dist/runtime/index.d.cts +16 -0
  199. package/dist/runtime/index.d.cts.map +1 -1
  200. package/dist/runtime/index.d.mts +16 -0
  201. package/dist/runtime/index.d.mts.map +1 -1
  202. package/dist/runtime/index.mjs +98 -26
  203. package/dist/runtime/index.mjs.map +1 -1
  204. package/dist/runtime/mf-config.cjs +3 -78
  205. package/dist/runtime/mf-config.cjs.map +1 -1
  206. package/dist/runtime/mf-config.d.cts +3 -49
  207. package/dist/runtime/mf-config.d.cts.map +1 -1
  208. package/dist/runtime/mf-config.d.mts +3 -49
  209. package/dist/runtime/mf-config.d.mts.map +1 -1
  210. package/dist/runtime/mf-config.mjs +5 -78
  211. package/dist/runtime/mf-config.mjs.map +1 -1
  212. package/dist/runtime/services/module-federation.service.cjs +63 -12
  213. package/dist/runtime/services/module-federation.service.cjs.map +1 -1
  214. package/dist/runtime/services/module-federation.service.d.cts +4 -13
  215. package/dist/runtime/services/module-federation.service.d.mts +4 -13
  216. package/dist/runtime/services/module-federation.service.mjs +63 -14
  217. package/dist/runtime/services/module-federation.service.mjs.map +1 -1
  218. package/dist/runtime/services/normalize.cjs +2 -22
  219. package/dist/runtime/services/normalize.d.cts +2 -21
  220. package/dist/runtime/services/normalize.d.mts +2 -21
  221. package/dist/runtime/services/normalize.mjs +2 -21
  222. package/dist/runtime/services/plugin-lifecycle.service.cjs +9 -11
  223. package/dist/runtime/services/plugin-lifecycle.service.cjs.map +1 -1
  224. package/dist/runtime/services/plugin-lifecycle.service.d.cts +3 -16
  225. package/dist/runtime/services/plugin-lifecycle.service.d.mts +3 -16
  226. package/dist/runtime/services/plugin-lifecycle.service.mjs +10 -13
  227. package/dist/runtime/services/plugin-lifecycle.service.mjs.map +1 -1
  228. package/dist/runtime/services/plugin-loader.service.cjs +30 -29
  229. package/dist/runtime/services/plugin-loader.service.cjs.map +1 -1
  230. package/dist/runtime/services/plugin-loader.service.d.cts +6 -53
  231. package/dist/runtime/services/plugin-loader.service.d.mts +6 -53
  232. package/dist/runtime/services/plugin-loader.service.mjs +29 -30
  233. package/dist/runtime/services/plugin-loader.service.mjs.map +1 -1
  234. package/dist/runtime/services/plugin.service.cjs +10 -12
  235. package/dist/runtime/services/plugin.service.cjs.map +1 -1
  236. package/dist/runtime/services/plugin.service.d.cts +14 -36
  237. package/dist/runtime/services/plugin.service.d.cts.map +1 -1
  238. package/dist/runtime/services/plugin.service.d.mts +14 -36
  239. package/dist/runtime/services/plugin.service.d.mts.map +1 -1
  240. package/dist/runtime/services/plugin.service.mjs +14 -18
  241. package/dist/runtime/services/plugin.service.mjs.map +1 -1
  242. package/dist/runtime/services/secrets.service.cjs +12 -12
  243. package/dist/runtime/services/secrets.service.cjs.map +1 -1
  244. package/dist/runtime/services/secrets.service.d.cts +3 -15
  245. package/dist/runtime/services/secrets.service.d.mts +3 -15
  246. package/dist/runtime/services/secrets.service.mjs +12 -13
  247. package/dist/runtime/services/secrets.service.mjs.map +1 -1
  248. package/dist/runtime/services/shared-identity.cjs +40 -0
  249. package/dist/runtime/services/shared-identity.cjs.map +1 -0
  250. package/dist/runtime/services/shared-identity.mjs +38 -0
  251. package/dist/runtime/services/shared-identity.mjs.map +1 -0
  252. package/dist/runtime/validation.cjs +2 -2
  253. package/dist/runtime/validation.cjs.map +1 -1
  254. package/dist/runtime/validation.mjs +2 -2
  255. package/dist/runtime/validation.mjs.map +1 -1
  256. package/dist/shared-deps-spec.cjs +384 -0
  257. package/dist/shared-deps-spec.cjs.map +1 -0
  258. package/dist/shared-deps-spec.d.cts +182 -0
  259. package/dist/shared-deps-spec.d.cts.map +1 -0
  260. package/dist/shared-deps-spec.d.mts +182 -0
  261. package/dist/shared-deps-spec.d.mts.map +1 -0
  262. package/dist/shared-deps-spec.mjs +368 -0
  263. package/dist/shared-deps-spec.mjs.map +1 -0
  264. package/dist/types.d.cts +22 -41
  265. package/dist/types.d.cts.map +1 -1
  266. package/dist/types.d.mts +22 -41
  267. package/dist/types.d.mts.map +1 -1
  268. package/dist/ui/manifest/construct.cjs +268 -0
  269. package/dist/ui/manifest/construct.cjs.map +1 -0
  270. package/dist/ui/manifest/construct.d.cts +48 -0
  271. package/dist/ui/manifest/construct.d.cts.map +1 -0
  272. package/dist/ui/manifest/construct.d.mts +48 -0
  273. package/dist/ui/manifest/construct.d.mts.map +1 -0
  274. package/dist/ui/manifest/construct.mjs +268 -0
  275. package/dist/ui/manifest/construct.mjs.map +1 -0
  276. package/dist/ui/manifest/contract.cjs +34 -0
  277. package/dist/ui/manifest/contract.cjs.map +1 -0
  278. package/dist/ui/manifest/contract.d.cts +26 -0
  279. package/dist/ui/manifest/contract.d.cts.map +1 -0
  280. package/dist/ui/manifest/contract.d.mts +26 -0
  281. package/dist/ui/manifest/contract.d.mts.map +1 -0
  282. package/dist/ui/manifest/contract.mjs +26 -0
  283. package/dist/ui/manifest/contract.mjs.map +1 -0
  284. package/dist/ui/manifest/descriptor.cjs +64 -0
  285. package/dist/ui/manifest/descriptor.cjs.map +1 -0
  286. package/dist/ui/manifest/descriptor.d.cts +60 -0
  287. package/dist/ui/manifest/descriptor.d.cts.map +1 -0
  288. package/dist/ui/manifest/descriptor.d.mts +60 -0
  289. package/dist/ui/manifest/descriptor.d.mts.map +1 -0
  290. package/dist/ui/manifest/descriptor.mjs +61 -0
  291. package/dist/ui/manifest/descriptor.mjs.map +1 -0
  292. package/dist/ui/manifest/generator.cjs +128 -0
  293. package/dist/ui/manifest/generator.cjs.map +1 -0
  294. package/dist/ui/manifest/generator.d.cts +41 -0
  295. package/dist/ui/manifest/generator.d.cts.map +1 -0
  296. package/dist/ui/manifest/generator.d.mts +41 -0
  297. package/dist/ui/manifest/generator.d.mts.map +1 -0
  298. package/dist/ui/manifest/generator.mjs +125 -0
  299. package/dist/ui/manifest/generator.mjs.map +1 -0
  300. package/dist/ui/manifest/index.cjs +35 -0
  301. package/dist/ui/manifest/index.d.cts +9 -0
  302. package/dist/ui/manifest/index.d.mts +9 -0
  303. package/dist/ui/manifest/index.mjs +9 -0
  304. package/dist/ui/manifest/manifest-schema.cjs +97 -0
  305. package/dist/ui/manifest/manifest-schema.cjs.map +1 -0
  306. package/dist/ui/manifest/manifest-schema.d.cts +132 -0
  307. package/dist/ui/manifest/manifest-schema.d.cts.map +1 -0
  308. package/dist/ui/manifest/manifest-schema.d.mts +132 -0
  309. package/dist/ui/manifest/manifest-schema.d.mts.map +1 -0
  310. package/dist/ui/manifest/manifest-schema.mjs +90 -0
  311. package/dist/ui/manifest/manifest-schema.mjs.map +1 -0
  312. package/dist/ui/manifest/mount-registry.cjs +52 -0
  313. package/dist/ui/manifest/mount-registry.cjs.map +1 -0
  314. package/dist/ui/manifest/mount-registry.d.cts +61 -0
  315. package/dist/ui/manifest/mount-registry.d.cts.map +1 -0
  316. package/dist/ui/manifest/mount-registry.d.mts +61 -0
  317. package/dist/ui/manifest/mount-registry.d.mts.map +1 -0
  318. package/dist/ui/manifest/mount-registry.mjs +48 -0
  319. package/dist/ui/manifest/mount-registry.mjs.map +1 -0
  320. package/dist/ui/manifest/nav.cjs +13 -0
  321. package/dist/ui/manifest/nav.cjs.map +1 -0
  322. package/dist/ui/manifest/nav.d.cts +34 -0
  323. package/dist/ui/manifest/nav.d.cts.map +1 -0
  324. package/dist/ui/manifest/nav.d.mts +34 -0
  325. package/dist/ui/manifest/nav.d.mts.map +1 -0
  326. package/dist/ui/manifest/nav.mjs +13 -0
  327. package/dist/ui/manifest/nav.mjs.map +1 -0
  328. package/dist/ui/manifest/route-config.cjs +33 -0
  329. package/dist/ui/manifest/route-config.cjs.map +1 -0
  330. package/dist/ui/manifest/route-config.d.cts +40 -0
  331. package/dist/ui/manifest/route-config.d.cts.map +1 -0
  332. package/dist/ui/manifest/route-config.d.mts +40 -0
  333. package/dist/ui/manifest/route-config.d.mts.map +1 -0
  334. package/dist/ui/manifest/route-config.mjs +33 -0
  335. package/dist/ui/manifest/route-config.mjs.map +1 -0
  336. package/dist/version-manifest.cjs +65 -0
  337. package/dist/version-manifest.cjs.map +1 -0
  338. package/dist/version-manifest.d.cts +52 -0
  339. package/dist/version-manifest.d.cts.map +1 -0
  340. package/dist/version-manifest.d.mts +52 -0
  341. package/dist/version-manifest.d.mts.map +1 -0
  342. package/dist/version-manifest.mjs +63 -0
  343. package/dist/version-manifest.mjs.map +1 -0
  344. package/package.json +165 -68
  345. package/skills/plugin-client/SKILL.md +24 -5
  346. package/skills/plugin-development/SKILL.md +79 -47
  347. package/skills/plugin-testing/SKILL.md +41 -12
  348. package/src/build/artifact-names.ts +117 -0
  349. package/src/build/contract-types.ts +101 -0
  350. package/src/build/rspack/build-report-plugin.ts +34 -0
  351. package/src/build/rspack/chunk-completeness-plugin.ts +57 -0
  352. package/src/build/rspack/compose.ts +87 -0
  353. package/src/build/rspack/fix-mf-data-uri-plugin.ts +23 -12
  354. package/src/build/rspack/generated-config.ts +35 -0
  355. package/src/build/rspack/index.ts +10 -5
  356. package/src/build/rspack/module-federation.ts +1 -1
  357. package/src/build/rspack/plugin.ts +103 -105
  358. package/src/build/rspack/utils.ts +8 -2
  359. package/src/build/ui/factory.ts +96 -0
  360. package/src/build/ui/generated-config.ts +133 -0
  361. package/src/build/ui/hash-artifacts-plugin.ts +79 -0
  362. package/src/build/ui/index.ts +98 -0
  363. package/src/build/ui/manifest-plugin.ts +54 -0
  364. package/src/build/ui/rsbuild-config.ts +256 -0
  365. package/src/build/ui/stubs.ts +103 -0
  366. package/src/cli.ts +100 -0
  367. package/src/dev/serve.ts +477 -0
  368. package/src/dev/watch-kill.ts +58 -0
  369. package/src/effect-helpers.ts +25 -0
  370. package/src/errors.ts +16 -18
  371. package/src/identity.ts +36 -0
  372. package/src/index.ts +4 -0
  373. package/src/plugin.ts +70 -92
  374. package/src/remote-entry.ts +171 -0
  375. package/src/runtime/errors.ts +186 -27
  376. package/src/runtime/index.ts +132 -43
  377. package/src/runtime/mf-config.ts +12 -107
  378. package/src/runtime/services/module-federation.service.ts +221 -117
  379. package/src/runtime/services/normalize.ts +1 -18
  380. package/src/runtime/services/plugin-lifecycle.service.ts +66 -75
  381. package/src/runtime/services/plugin-loader.service.ts +239 -212
  382. package/src/runtime/services/plugin.service.ts +61 -43
  383. package/src/runtime/services/secrets.service.ts +20 -15
  384. package/src/runtime/services/shared-identity.ts +68 -0
  385. package/src/runtime/validation.ts +5 -7
  386. package/src/shared-deps-spec.ts +478 -0
  387. package/src/types.ts +22 -48
  388. package/src/ui/manifest/construct.ts +403 -0
  389. package/src/ui/manifest/contract.ts +33 -0
  390. package/src/ui/manifest/descriptor.ts +96 -0
  391. package/src/ui/manifest/generator.ts +238 -0
  392. package/src/ui/manifest/index.ts +55 -0
  393. package/src/ui/manifest/manifest-schema.ts +107 -0
  394. package/src/ui/manifest/mount-registry.ts +50 -0
  395. package/src/ui/manifest/nav.ts +43 -0
  396. package/src/ui/manifest/route-config.ts +82 -0
  397. package/src/version-manifest.ts +88 -0
  398. package/dist/build/rspack/dev-server-middleware.cjs +0 -194
  399. package/dist/build/rspack/dev-server-middleware.cjs.map +0 -1
  400. package/dist/build/rspack/dev-server-middleware.mjs +0 -193
  401. package/dist/build/rspack/dev-server-middleware.mjs.map +0 -1
  402. package/dist/build/shared-deps.cjs +0 -83
  403. package/dist/build/shared-deps.cjs.map +0 -1
  404. package/dist/build/shared-deps.d.cts +0 -67
  405. package/dist/build/shared-deps.d.cts.map +0 -1
  406. package/dist/build/shared-deps.d.mts +0 -67
  407. package/dist/build/shared-deps.d.mts.map +0 -1
  408. package/dist/build/shared-deps.mjs +0 -79
  409. package/dist/build/shared-deps.mjs.map +0 -1
  410. package/dist/effect.cjs +0 -9
  411. package/dist/effect.d.cts +0 -1
  412. package/dist/effect.d.mts +0 -1
  413. package/dist/effect.mjs +0 -3
  414. package/dist/orpc-openapi.cjs +0 -17
  415. package/dist/orpc-openapi.d.cts +0 -2
  416. package/dist/orpc-openapi.d.mts +0 -2
  417. package/dist/orpc-openapi.mjs +0 -5
  418. package/dist/orpc.cjs +0 -46
  419. package/dist/orpc.d.cts +0 -7
  420. package/dist/orpc.d.mts +0 -7
  421. package/dist/orpc.mjs +0 -11
  422. package/dist/package.cjs +0 -23
  423. package/dist/package.cjs.map +0 -1
  424. package/dist/package.mjs +0 -17
  425. package/dist/package.mjs.map +0 -1
  426. package/dist/runtime/services/module-federation.service.d.cts.map +0 -1
  427. package/dist/runtime/services/module-federation.service.d.mts.map +0 -1
  428. package/dist/runtime/services/normalize.cjs.map +0 -1
  429. package/dist/runtime/services/normalize.d.cts.map +0 -1
  430. package/dist/runtime/services/normalize.d.mts.map +0 -1
  431. package/dist/runtime/services/normalize.mjs.map +0 -1
  432. package/dist/runtime/services/plugin-lifecycle.service.d.cts.map +0 -1
  433. package/dist/runtime/services/plugin-lifecycle.service.d.mts.map +0 -1
  434. package/dist/runtime/services/plugin-loader.service.d.cts.map +0 -1
  435. package/dist/runtime/services/plugin-loader.service.d.mts.map +0 -1
  436. package/dist/runtime/services/secrets.service.d.cts.map +0 -1
  437. package/dist/runtime/services/secrets.service.d.mts.map +0 -1
  438. package/dist/testing/index.cjs +0 -57
  439. package/dist/testing/index.cjs.map +0 -1
  440. package/dist/testing/index.d.cts +0 -54
  441. package/dist/testing/index.d.cts.map +0 -1
  442. package/dist/testing/index.d.mts +0 -54
  443. package/dist/testing/index.d.mts.map +0 -1
  444. package/dist/testing/index.mjs +0 -55
  445. package/dist/testing/index.mjs.map +0 -1
  446. package/dist/zod-core.cjs +0 -9
  447. package/dist/zod-core.d.cts +0 -1
  448. package/dist/zod-core.d.mts +0 -1
  449. package/dist/zod-core.mjs +0 -3
  450. package/dist/zod.cjs +0 -11
  451. package/dist/zod.d.cts +0 -2
  452. package/dist/zod.d.mts +0 -2
  453. package/dist/zod.mjs +0 -3
  454. package/src/build/rspack/dev-server-middleware.ts +0 -250
  455. package/src/build/shared-deps.ts +0 -102
  456. package/src/effect.ts +0 -1
  457. package/src/orpc-openapi.ts +0 -2
  458. package/src/orpc.ts +0 -9
  459. package/src/testing/index.ts +0 -87
  460. package/src/testing/mocks/module-federation.service.ts +0 -41
  461. package/src/zod-core.ts +0 -1
  462. package/src/zod.ts +0 -3
package/dist/plugin.d.mts CHANGED
@@ -1,6 +1,9 @@
1
- import { AnyContractRouter, AnySchema, InferSchemaOutput } from "@orpc/contract";
1
+ import { PluginRuntimeError } from "./runtime/errors.mjs";
2
2
  import { Context, Effect, Layer, Scope } from "effect";
3
- import { Implementer, Router } from "@orpc/server";
3
+ import { ContractedRouter, Implementer } from "@orpc/server";
4
+ import "@orpc/experimental-effect/extensions/effect";
5
+ import { AnyContractRouter, AnySchema, InferSchemaOutput } from "@orpc/contract";
6
+ import { WithEffectContext } from "@orpc/experimental-effect";
4
7
  //#region src/plugin.d.ts
5
8
  type ContextOutput<T> = T extends AnySchema ? InferSchemaOutput<T> : Record<string, never>;
6
9
  type PluginConfigFor<V extends AnySchema, S extends AnySchema, TRequestContext extends AnySchema | undefined> = {
@@ -12,65 +15,81 @@ type PluginInitializeInput<V extends AnySchema, S extends AnySchema> = {
12
15
  variables: InferSchemaOutput<V>;
13
16
  secrets: InferSchemaOutput<S>;
14
17
  };
18
+ declare const PluginIdTag_base: Context.ServiceClass<PluginIdTag, "PluginId", string>;
19
+ declare class PluginIdTag extends PluginIdTag_base {}
15
20
  /**
16
- * Tools provided to plugin initialize/services for building long-lived scoped resources.
21
+ * The `initialize` effect's environment: the plugin lifecycle scope plus the
22
+ * framework-assigned plugin id.
17
23
  */
18
- type ServiceOf<T> = T extends Context.Tag<any, infer S> ? S : never;
19
- type PluginServicesTools = {
20
- buildService: <T>(tag: T, layer: Layer.Layer<any, any, any>) => Effect.Effect<ServiceOf<T>>;
21
- };
22
- declare const PluginIdTag_base: Context.TagClass<PluginIdTag, "PluginId", string>;
23
- declare class PluginIdTag extends PluginIdTag_base {}
24
- type PluginDefinition<V extends AnySchema, S extends AnySchema, TContract extends AnyContractRouter, TRequestContext extends AnySchema | undefined, TDeps extends Record<string, any>, P extends Record<string, unknown>> = {
24
+ type PluginEnv = Scope.Scope | PluginIdTag;
25
+ type PluginDefinition<V extends AnySchema, S extends AnySchema, TContract extends AnyContractRouter, TRequestContext extends AnySchema | undefined, P extends Record<string, unknown>> = {
25
26
  variables: V;
26
27
  secrets: S;
27
28
  contract: TContract;
28
29
  context?: TRequestContext;
29
30
  /**
30
- * Initialize the plugin and build dependencies.
31
- * A third argument `tools` is provided for building long-lived scoped resources
32
- * (e.g. database pools) via `tools.buildService(tag, layer)`.
31
+ * Optional tag under which `initialize` exposes the plugin's services.
32
+ * The host uses it to read services (e.g. the auth plugin's Better Auth
33
+ * handler) from the built Effect context without importing the plugin
34
+ * module statically.
35
+ */
36
+ servicesTag?: Context.Key<any, any>;
37
+ /**
38
+ * Initialize the plugin by returning an Effect `Layer`.
39
+ *
40
+ * The runtime builds the Layer against the plugin's lifecycle scope —
41
+ * scoped resources (db pools, repositories, caches, publishers) release
42
+ * when the plugin shuts down. Services are provided to `.effect()`
43
+ * handlers via the oRPC context (`yield* Tag`).
44
+ */
45
+ initialize?: (config: PluginInitializeInput<V, S>, plugins: P) => Effect.Effect<Layer.Layer<any, Error, PluginEnv>, Error, PluginEnv>;
46
+ /**
47
+ * Creates the strongly-typed oRPC router for this plugin.
48
+ * Services come from the Effect context — access them with
49
+ * `yield* Tag` in `.effect()` handlers, or
50
+ * `Context.get(context["effect/context"], Tag)` in plain handlers.
51
+ * Sibling plugin entries in `plugins` carry `{ client, router }`.
33
52
  */
34
- initialize?: (config: PluginInitializeInput<V, S>, plugins: P, tools: PluginServicesTools) => Effect.Effect<TDeps, Error, Scope.Scope>;
35
- createRouter: (deps: TDeps, builder: Implementer<TContract, ContextOutput<TRequestContext>, ContextOutput<TRequestContext>>) => Router<TContract, any>;
36
- shutdown?: (deps: TDeps) => Effect.Effect<void, Error, never>;
53
+ createRouter: (builder: Implementer<TContract, ContextOutput<TRequestContext> & WithEffectContext<any>>, plugins: P) => ContractedRouter<TContract, any>;
37
54
  };
38
55
  /**
39
56
  * Loaded plugin with static binding property
40
57
  */
41
- interface LoadedPluginWithBinding<TContract extends AnyContractRouter, TVariables extends AnySchema, TSecrets extends AnySchema, TRequestContext extends AnySchema | undefined, TDeps extends Record<string, any> = Record<never, never>> {
42
- new (): Plugin<TContract, TVariables, TSecrets, TRequestContext, TDeps>;
58
+ interface LoadedPluginWithBinding<TContract extends AnyContractRouter, TVariables extends AnySchema, TSecrets extends AnySchema, TRequestContext extends AnySchema | undefined> {
59
+ new (): Plugin<TContract, TVariables, TSecrets, TRequestContext>;
43
60
  binding: {
44
61
  contract: TContract;
45
62
  variables: TVariables;
46
63
  secrets: TSecrets;
47
64
  context: TRequestContext;
65
+ servicesTag?: Context.Key<any, any>;
48
66
  };
49
67
  }
50
68
  /**
51
69
  * Plugin interface
52
70
  */
53
- interface Plugin<TContract extends AnyContractRouter, TVariables extends AnySchema, TSecrets extends AnySchema, TRequestContext extends AnySchema | undefined, TDeps extends Record<string, any> = Record<never, never>> {
71
+ interface Plugin<TContract extends AnyContractRouter, TVariables extends AnySchema, TSecrets extends AnySchema, TRequestContext extends AnySchema | undefined> {
54
72
  readonly id: string;
55
73
  readonly contract: TContract;
56
74
  readonly configSchema: PluginConfigFor<TVariables, TSecrets, TRequestContext>;
57
- initialize(config: PluginInitializeInput<TVariables, TSecrets>, plugins: Record<string, unknown>, tools: PluginServicesTools): Effect.Effect<TDeps, unknown, Scope.Scope>;
58
- shutdown(): Effect.Effect<void, never>;
75
+ readonly servicesTag?: Context.Key<any, any>;
76
+ initialize(config: PluginInitializeInput<TVariables, TSecrets>, plugins: Record<string, unknown>): Effect.Effect<Layer.Layer<any, Error, PluginEnv>, PluginRuntimeError, PluginEnv>;
59
77
  /**
60
78
  * Creates the strongly-typed oRPC router for this plugin.
61
79
  * The router's procedure types are inferred directly from the contract.
62
- * @param deps The initialized plugin dependencies
80
+ * @param plugins Sibling plugin entries (`{ client, router }`) for
81
+ * cross-plugin composition
63
82
  * @returns A router with procedures matching the plugin's contract
64
83
  */
65
- createRouter(deps: TDeps): Router<TContract, any>;
84
+ createRouter(plugins: Record<string, unknown>): ContractedRouter<TContract, any>;
66
85
  }
67
86
  interface CreatePluginFn {
68
- <V extends AnySchema, S extends AnySchema, TContract extends AnyContractRouter, TRequestContext extends AnySchema | undefined = undefined, TDeps extends Record<string, any> = Record<never, never>, P extends Record<string, unknown> = Record<string, never>>(config: PluginDefinition<V, S, TContract, TRequestContext, TDeps, P>): LoadedPluginWithBinding<TContract, V, S, TRequestContext, TDeps>;
87
+ <V extends AnySchema, S extends AnySchema, TContract extends AnyContractRouter, TRequestContext extends AnySchema | undefined = undefined, P extends Record<string, unknown> = Record<string, never>>(config: PluginDefinition<V, S, TContract, TRequestContext, P>): LoadedPluginWithBinding<TContract, V, S, TRequestContext>;
69
88
  withPlugins: <P extends Record<string, unknown>>() => CreatePluginWithPlugins<P>;
70
89
  }
71
90
  declare const createPlugin: CreatePluginFn;
72
- type CreatePluginWithPlugins<P extends Record<string, unknown>> = <V extends AnySchema, S extends AnySchema, TContract extends AnyContractRouter, TRequestContext extends AnySchema | undefined = undefined, TDeps extends Record<string, any> = Record<never, never>>(config: PluginDefinition<V, S, TContract, TRequestContext, TDeps, P>) => LoadedPluginWithBinding<TContract, V, S, TRequestContext, TDeps>;
91
+ type CreatePluginWithPlugins<P extends Record<string, unknown>> = <V extends AnySchema, S extends AnySchema, TContract extends AnyContractRouter, TRequestContext extends AnySchema | undefined = undefined>(config: PluginDefinition<V, S, TContract, TRequestContext, P>) => LoadedPluginWithBinding<TContract, V, S, TRequestContext>;
73
92
  declare function withPlugins<P extends Record<string, unknown>>(): CreatePluginWithPlugins<P>;
74
93
  //#endregion
75
- export { CreatePluginFn, CreatePluginWithPlugins, LoadedPluginWithBinding, Plugin, PluginConfigFor, PluginIdTag, PluginServicesTools, createPlugin, withPlugins };
94
+ export { CreatePluginFn, CreatePluginWithPlugins, LoadedPluginWithBinding, Plugin, PluginConfigFor, PluginEnv, PluginIdTag, createPlugin, withPlugins };
76
95
  //# sourceMappingURL=plugin.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;KAOK,cAAc,KAAK,UAAU,YAAY,kBAAkB,KAAK;KAEzD,gBACV,UAAU,WACV,UAAU,WACV,wBAAwB;EAExB,WAAW;EACX,SAAS;EACT,SAAS;;KAGN,sBAAsB,UAAU,WAAW,UAAU;EACxD,WAAW,kBAAkB;EAC7B,SAAS,kBAAkB;;;;;KAMxB,UAAU,KAAK,UAAU,QAAc,eAAe,KAAK;KAEpD;EACV,eAAe,GAAG,KAAK,GAAG,OAAO,MAAM,yBAAyB,OAAO,OAAO,UAAU;;;cAG7E,oBAAoB;KAE5B,iBACH,UAAU,WACV,UAAU,WACV,kBAAkB,mBAClB,wBAAwB,uBACxB,cAAc,qBACd,UAAU;EAEV,WAAW;EACX,SAAS;EACT,UAAU;EACV,UAAU;;;;;;EAMV,cACE,QAAQ,sBAAsB,GAAG,IACjC,SAAS,GACT,OAAO,wBACJ,OAAO,OAAO,OAAO,OAAO,MAAM;EACvC,eACE,MAAM,OACN,SAAS,YAAY,WAAW,cAAc,kBAAkB,cAAc,sBAC3E,OAAO;EACZ,YAAY,MAAM,UAAU,OAAO,aAAa;;;;;UAMjC,wBACf,kBAAkB,mBAClB,mBAAmB,WACnB,iBAAiB,WACjB,wBAAwB,uBACxB,cAAc,sBAAsB;UAE5B,OAAO,WAAW,YAAY,UAAU,iBAAiB;EACjE;IACE,UAAU;IACV,WAAW;IACX,SAAS;IACT,SAAS;;;;;;UAOI,OACf,kBAAkB,mBAClB,mBAAmB,WACnB,iBAAiB,WACjB,wBAAwB,uBACxB,cAAc,sBAAsB;WAE3B;WACA,UAAU;WACV,cAAc,gBAAgB,YAAY,UAAU;EAE7D,WACE,QAAQ,sBAAsB,YAAY,WAC1C,SAAS,yBACT,OAAO,sBACN,OAAO,OAAO,gBAAgB,MAAM;EAEvC,YAAY,OAAO;;;;;;;EAQnB,aAAa,MAAM,QAAQ,OAAO;;UAGnB;GAEb,UAAU,WACV,UAAU,WACV,kBAAkB,mBAClB,wBAAwB,mCACxB,cAAc,sBAAsB,sBACpC,UAAU,0BAA0B,uBAEpC,QAAQ,iBAAiB,GAAG,GAAG,WAAW,iBAAiB,OAAO,KACjE,wBAAwB,WAAW,GAAG,GAAG,iBAAiB;EAE7D,cAAc,UAAU,8BAA8B,wBAAwB;;cAGnE,cAAc;KAgGf,wBAAwB,UAAU,4BAC5C,UAAU,WACV,UAAU,WACV,kBAAkB,mBAClB,wBAAwB,mCACxB,cAAc,sBAAsB,sBAEpC,QAAQ,iBAAiB,GAAG,GAAG,WAAW,iBAAiB,OAAO,OAC/D,wBAAwB,WAAW,GAAG,GAAG,iBAAiB;iBAE/C,YAAY,UAAU,4BAA4B,wBAAwB"}
1
+ {"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;;;;KASK,cAAc,KAAK,UAAU,YAAY,kBAAkB,KAAK;KAEzD,gBACV,UAAU,WACV,UAAU,WACV,wBAAwB;EAExB,WAAW;EACX,SAAS;EACT,SAAS;;KAGN,sBAAsB,UAAU,WAAW,UAAU;EACxD,WAAW,kBAAkB;EAC7B,SAAS,kBAAkB;;;cAGhB,oBAAoB;;;;;KAMrB,YAAY,MAAM,QAAQ;KAEjC,iBACH,UAAU,WACV,UAAU,WACV,kBAAkB,mBAClB,wBAAwB,uBACxB,UAAU;EAEV,WAAW;EACX,SAAS;EACT,UAAU;EACV,UAAU;;;;;;;EAOV,cAAc,QAAQ;;;;;;;;;EAStB,cACE,QAAQ,sBAAsB,GAAG,IACjC,SAAS,MACN,OAAO,OAAO,MAAM,WAAW,OAAO,YAAY,OAAO;;;;;;;;EAQ9D,eACE,SAAS,YAAY,WAAW,cAAc,mBAAmB,yBACjE,SAAS,MACN,iBAAiB;;;;;UAMP,wBACf,kBAAkB,mBAClB,mBAAmB,WACnB,iBAAiB,WACjB,wBAAwB;UAEhB,OAAO,WAAW,YAAY,UAAU;EAChD;IACE,UAAU;IACV,WAAW;IACX,SAAS;IACT,SAAS;IACT,cAAc,QAAQ;;;;;;UAOT,OACf,kBAAkB,mBAClB,mBAAmB,WACnB,iBAAiB,WACjB,wBAAwB;WAEf;WACA,UAAU;WACV,cAAc,gBAAgB,YAAY,UAAU;WACpD,cAAc,QAAQ;EAE/B,WACE,QAAQ,sBAAsB,YAAY,WAC1C,SAAS,0BACR,OAAO,OAAO,MAAM,WAAW,OAAO,YAAY,oBAAoB;;;;;;;;EASzE,aAAa,SAAS,0BAA0B,iBAAiB;;UAGlD;GAEb,UAAU,WACV,UAAU,WACV,kBAAkB,mBAClB,wBAAwB,mCACxB,UAAU,0BAA0B,uBAEpC,QAAQ,iBAAiB,GAAG,GAAG,WAAW,iBAAiB,KAC1D,wBAAwB,WAAW,GAAG,GAAG;EAE5C,cAAc,UAAU,8BAA8B,wBAAwB;;cAGnE,cAAc;KAkEf,wBAAwB,UAAU,4BAC5C,UAAU,WACV,UAAU,WACV,kBAAkB,mBAClB,wBAAwB,mCAExB,QAAQ,iBAAiB,GAAG,GAAG,WAAW,iBAAiB,OACxD,wBAAwB,WAAW,GAAG,GAAG;iBAE9B,YAAY,UAAU,4BAA4B,wBAAwB"}
package/dist/plugin.mjs CHANGED
@@ -1,10 +1,10 @@
1
- import { extractFromFiberFailure, formatORPCError } from "./runtime/errors.mjs";
2
- import { ORPCError } from "@orpc/contract";
3
- import { Context, Effect } from "effect";
4
- import { implement, onError } from "@orpc/server";
1
+ import { toPluginRuntimeError } from "./runtime/errors.mjs";
2
+ import { Context, Effect, Layer } from "effect";
3
+ import { implement } from "@orpc/server";
4
+ import "@orpc/experimental-effect/extensions/effect";
5
5
 
6
6
  //#region src/plugin.ts
7
- var PluginIdTag = class extends Context.Tag("PluginId")() {};
7
+ var PluginIdTag = class extends Context.Service()("PluginId") {};
8
8
  const createPlugin = function createPlugin(config) {
9
9
  const configSchema = {
10
10
  variables: config.variables,
@@ -16,30 +16,15 @@ const createPlugin = function createPlugin(config) {
16
16
  id;
17
17
  contract = config.contract;
18
18
  configSchema = configSchema;
19
- _deps = null;
20
- initialize(pluginConfig, plugins = {}, tools) {
21
- return (config.initialize ?? (() => Effect.succeed({})))(pluginConfig, plugins, tools).pipe(Effect.tap((deps) => Effect.sync(() => {
22
- this._deps = deps;
23
- })), Effect.map(() => this._deps), Effect.mapError((error) => error));
19
+ servicesTag = config.servicesTag;
20
+ initialize(pluginConfig, plugins = {}) {
21
+ const init = config.initialize;
22
+ if (!init) return Effect.succeed(Layer.empty);
23
+ return init(pluginConfig, plugins).pipe(Effect.mapError((error) => toPluginRuntimeError(error, this.id, void 0, "initialize-plugin")));
24
24
  }
25
- shutdown() {
26
- const self = this;
27
- return Effect.gen(function* () {
28
- if (config.shutdown && self._deps) yield* config.shutdown(self._deps).pipe(Effect.catchAll((error) => Effect.logWarning(`Plugin shutdown hook failed for ${self.id}`, error)));
29
- self._deps = null;
30
- });
31
- }
32
- createRouter(deps) {
33
- const base = implement(config.contract).$context();
34
- const errorMiddleware = onError((error) => {
35
- const unwrapped = extractFromFiberFailure(error);
36
- if (unwrapped !== error && unwrapped instanceof ORPCError) throw unwrapped;
37
- const formatted = formatORPCError(error);
38
- if (formatted) console.error(formatted);
39
- throw error;
40
- });
41
- const builder = base.use(errorMiddleware);
42
- return config.createRouter(deps, builder);
25
+ createRouter(plugins) {
26
+ const builder = implement(config.contract).$context();
27
+ return config.createRouter(builder, plugins);
43
28
  }
44
29
  }
45
30
  const PluginConstructor = CreatedPlugin;
@@ -47,7 +32,8 @@ const createPlugin = function createPlugin(config) {
47
32
  contract: config.contract,
48
33
  variables: config.variables,
49
34
  secrets: config.secrets,
50
- context: config.context
35
+ context: config.context,
36
+ servicesTag: config.servicesTag
51
37
  };
52
38
  return PluginConstructor;
53
39
  };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.mjs","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["import type { AnyContractRouter, AnySchema, InferSchemaOutput } from \"@orpc/contract\";\nimport { ORPCError } from \"@orpc/contract\";\nimport type { Implementer, Router } from \"@orpc/server\";\nimport { implement, onError } from \"@orpc/server\";\nimport { Context, Effect, type Context as EffectContext, type Layer, type Scope } from \"effect\";\nimport { extractFromFiberFailure, formatORPCError } from \"./runtime/errors\";\n\ntype ContextOutput<T> = T extends AnySchema ? InferSchemaOutput<T> : Record<string, never>;\n\nexport type PluginConfigFor<\n V extends AnySchema,\n S extends AnySchema,\n TRequestContext extends AnySchema | undefined,\n> = {\n variables: V;\n secrets: S;\n context: TRequestContext;\n};\n\ntype PluginInitializeInput<V extends AnySchema, S extends AnySchema> = {\n variables: InferSchemaOutput<V>;\n secrets: InferSchemaOutput<S>;\n};\n\n/**\n * Tools provided to plugin initialize/services for building long-lived scoped resources.\n */\ntype ServiceOf<T> = T extends EffectContext.Tag<any, infer S> ? S : never;\n\nexport type PluginServicesTools = {\n buildService: <T>(tag: T, layer: Layer.Layer<any, any, any>) => Effect.Effect<ServiceOf<T>>;\n};\n\nexport class PluginIdTag extends Context.Tag(\"PluginId\")<PluginIdTag, string>() {}\n\ntype PluginDefinition<\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined,\n TDeps extends Record<string, any>,\n P extends Record<string, unknown>,\n> = {\n variables: V;\n secrets: S;\n contract: TContract;\n context?: TRequestContext;\n /**\n * Initialize the plugin and build dependencies.\n * A third argument `tools` is provided for building long-lived scoped resources\n * (e.g. database pools) via `tools.buildService(tag, layer)`.\n */\n initialize?: (\n config: PluginInitializeInput<V, S>,\n plugins: P,\n tools: PluginServicesTools,\n ) => Effect.Effect<TDeps, Error, Scope.Scope>;\n createRouter: (\n deps: TDeps,\n builder: Implementer<TContract, ContextOutput<TRequestContext>, ContextOutput<TRequestContext>>,\n ) => Router<TContract, any>;\n shutdown?: (deps: TDeps) => Effect.Effect<void, Error, never>;\n};\n\n/**\n * Loaded plugin with static binding property\n */\nexport interface LoadedPluginWithBinding<\n TContract extends AnyContractRouter,\n TVariables extends AnySchema,\n TSecrets extends AnySchema,\n TRequestContext extends AnySchema | undefined,\n TDeps extends Record<string, any> = Record<never, never>,\n> {\n new (): Plugin<TContract, TVariables, TSecrets, TRequestContext, TDeps>;\n binding: {\n contract: TContract;\n variables: TVariables;\n secrets: TSecrets;\n context: TRequestContext;\n };\n}\n\n/**\n * Plugin interface\n */\nexport interface Plugin<\n TContract extends AnyContractRouter,\n TVariables extends AnySchema,\n TSecrets extends AnySchema,\n TRequestContext extends AnySchema | undefined,\n TDeps extends Record<string, any> = Record<never, never>,\n> {\n readonly id: string;\n readonly contract: TContract;\n readonly configSchema: PluginConfigFor<TVariables, TSecrets, TRequestContext>;\n\n initialize(\n config: PluginInitializeInput<TVariables, TSecrets>,\n plugins: Record<string, unknown>,\n tools: PluginServicesTools,\n ): Effect.Effect<TDeps, unknown, Scope.Scope>;\n\n shutdown(): Effect.Effect<void, never>;\n\n /**\n * Creates the strongly-typed oRPC router for this plugin.\n * The router's procedure types are inferred directly from the contract.\n * @param deps The initialized plugin dependencies\n * @returns A router with procedures matching the plugin's contract\n */\n createRouter(deps: TDeps): Router<TContract, any>;\n}\n\nexport interface CreatePluginFn {\n <\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined = undefined,\n TDeps extends Record<string, any> = Record<never, never>,\n P extends Record<string, unknown> = Record<string, never>,\n >(\n config: PluginDefinition<V, S, TContract, TRequestContext, TDeps, P>,\n ): LoadedPluginWithBinding<TContract, V, S, TRequestContext, TDeps>;\n\n withPlugins: <P extends Record<string, unknown>>() => CreatePluginWithPlugins<P>;\n}\n\nexport const createPlugin: CreatePluginFn = function createPlugin<\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined = undefined,\n TDeps extends Record<string, any> = Record<never, never>,\n P extends Record<string, unknown> = Record<string, never>,\n>(config: PluginDefinition<V, S, TContract, TRequestContext, TDeps, P>) {\n const configSchema: PluginConfigFor<V, S, TRequestContext> = {\n variables: config.variables,\n secrets: config.secrets,\n context: config.context as TRequestContext,\n };\n\n class CreatedPlugin implements Plugin<TContract, V, S, TRequestContext, TDeps> {\n /** set during instantiation - registry key */\n id!: string;\n readonly contract = config.contract;\n readonly configSchema = configSchema;\n\n private _deps: TDeps | null = null;\n\n initialize(\n pluginConfig: PluginInitializeInput<V, S>,\n plugins: Record<string, unknown> = {},\n tools: PluginServicesTools,\n ): Effect.Effect<TDeps, unknown, Scope.Scope> {\n const init = config.initialize ?? (() => Effect.succeed({} as TDeps));\n\n return init(pluginConfig, plugins as P, tools).pipe(\n Effect.tap((deps) =>\n Effect.sync(() => {\n this._deps = deps;\n }),\n ),\n Effect.map(() => this._deps as TDeps),\n Effect.mapError((error) => error as unknown),\n );\n }\n\n shutdown(): Effect.Effect<void, never> {\n const self = this;\n return Effect.gen(function* () {\n if (config.shutdown && self._deps) {\n yield* config\n .shutdown(self._deps)\n .pipe(\n Effect.catchAll((error) =>\n Effect.logWarning(`Plugin shutdown hook failed for ${self.id}`, error),\n ),\n );\n }\n self._deps = null;\n });\n }\n\n createRouter(deps: TDeps): Router<TContract, any> {\n const base = implement(config.contract).$context<ContextOutput<TRequestContext>>();\n const errorMiddleware = onError((error: unknown) => {\n const unwrapped = extractFromFiberFailure(error);\n\n if (unwrapped !== error && unwrapped instanceof ORPCError) {\n throw unwrapped;\n }\n\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n throw error;\n }) as any;\n\n const builder = (base as any).use(errorMiddleware);\n const router = config.createRouter(deps, builder as any);\n return router as Router<TContract, any>;\n }\n }\n\n const PluginConstructor = CreatedPlugin as unknown as {\n new (): Plugin<TContract, V, S, TRequestContext, TDeps>;\n binding: {\n contract: TContract;\n variables: V;\n secrets: S;\n context: TRequestContext;\n };\n };\n\n PluginConstructor.binding = {\n contract: config.contract,\n variables: config.variables,\n secrets: config.secrets,\n context: config.context as TRequestContext,\n };\n\n return PluginConstructor as LoadedPluginWithBinding<TContract, V, S, TRequestContext, TDeps>;\n};\n\nexport type CreatePluginWithPlugins<P extends Record<string, unknown>> = <\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined = undefined,\n TDeps extends Record<string, any> = Record<never, never>,\n>(\n config: PluginDefinition<V, S, TContract, TRequestContext, TDeps, P>,\n) => LoadedPluginWithBinding<TContract, V, S, TRequestContext, TDeps>;\n\nexport function withPlugins<P extends Record<string, unknown>>(): CreatePluginWithPlugins<P> {\n return <\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined = undefined,\n TDeps extends Record<string, any> = Record<never, never>,\n >(\n config: PluginDefinition<V, S, TContract, TRequestContext, TDeps, P>,\n ) => createPlugin<V, S, TContract, TRequestContext, TDeps, P>(config as any);\n}\n\ncreatePlugin.withPlugins = withPlugins;\n"],"mappings":";;;;;;AAiCA,IAAa,cAAb,cAAiC,QAAQ,IAAI,UAAU,CAAC,CAAsB,CAAC,CAAC,CAAC;AAgGjF,MAAa,eAA+B,SAAS,aAOnD,QAAsE;CACtE,MAAM,eAAuD;EAC3D,WAAW,OAAO;EAClB,SAAS,OAAO;EAChB,SAAS,OAAO;CAClB;CAEA,MAAM,cAAyE;;EAE7E;EACA,AAAS,WAAW,OAAO;EAC3B,AAAS,eAAe;EAExB,AAAQ,QAAsB;EAE9B,WACE,cACA,UAAmC,CAAC,GACpC,OAC4C;GAG5C,QAFa,OAAO,qBAAqB,OAAO,QAAQ,CAAC,CAAU,GAExD,CAAC,cAAc,SAAc,KAAK,CAAC,CAAC,KAC7C,OAAO,KAAK,SACV,OAAO,WAAW;IAChB,KAAK,QAAQ;GACf,CAAC,CACH,GACA,OAAO,UAAU,KAAK,KAAc,GACpC,OAAO,UAAU,UAAU,KAAgB,CAC7C;EACF;EAEA,WAAuC;GACrC,MAAM,OAAO;GACb,OAAO,OAAO,IAAI,aAAa;IAC7B,IAAI,OAAO,YAAY,KAAK,OAC1B,OAAO,OACJ,SAAS,KAAK,KAAK,CAAC,CACpB,KACC,OAAO,UAAU,UACf,OAAO,WAAW,mCAAmC,KAAK,MAAM,KAAK,CACvE,CACF;IAEJ,KAAK,QAAQ;GACf,CAAC;EACH;EAEA,aAAa,MAAqC;GAChD,MAAM,OAAO,UAAU,OAAO,QAAQ,CAAC,CAAC,SAAyC;GACjF,MAAM,kBAAkB,SAAS,UAAmB;IAClD,MAAM,YAAY,wBAAwB,KAAK;IAE/C,IAAI,cAAc,SAAS,qBAAqB,WAC9C,MAAM;IAGR,MAAM,YAAY,gBAAgB,KAAK;IACvC,IAAI,WAAW,QAAQ,MAAM,SAAS;IACtC,MAAM;GACR,CAAC;GAED,MAAM,UAAW,KAAa,IAAI,eAAe;GAEjD,OADe,OAAO,aAAa,MAAM,OAC7B;EACd;CACF;CAEA,MAAM,oBAAoB;CAU1B,kBAAkB,UAAU;EAC1B,UAAU,OAAO;EACjB,WAAW,OAAO;EAClB,SAAS,OAAO;EAChB,SAAS,OAAO;CAClB;CAEA,OAAO;AACT;AAYA,SAAgB,cAA6E;CAC3F,QAOE,WACG,aAAyD,MAAa;AAC7E;AAEA,aAAa,cAAc"}
1
+ {"version":3,"file":"plugin.mjs","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["import type { AnyContractRouter, AnySchema, InferSchemaOutput } from \"@orpc/contract\";\nimport type { WithEffectContext } from \"@orpc/experimental-effect\";\nimport \"@orpc/experimental-effect/extensions/effect\";\nimport type { ContractedRouter, Implementer } from \"@orpc/server\";\nimport { implement } from \"@orpc/server\";\nimport type { Scope } from \"effect\";\nimport { Context, Effect, Layer } from \"effect\";\nimport { type PluginRuntimeError, toPluginRuntimeError } from \"./runtime/errors\";\n\ntype ContextOutput<T> = T extends AnySchema ? InferSchemaOutput<T> : Record<string, never>;\n\nexport type PluginConfigFor<\n V extends AnySchema,\n S extends AnySchema,\n TRequestContext extends AnySchema | undefined,\n> = {\n variables: V;\n secrets: S;\n context: TRequestContext;\n};\n\ntype PluginInitializeInput<V extends AnySchema, S extends AnySchema> = {\n variables: InferSchemaOutput<V>;\n secrets: InferSchemaOutput<S>;\n};\n\nexport class PluginIdTag extends Context.Service<PluginIdTag, string>()(\"PluginId\") {}\n\n/**\n * The `initialize` effect's environment: the plugin lifecycle scope plus the\n * framework-assigned plugin id.\n */\nexport type PluginEnv = Scope.Scope | PluginIdTag;\n\ntype PluginDefinition<\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined,\n P extends Record<string, unknown>,\n> = {\n variables: V;\n secrets: S;\n contract: TContract;\n context?: TRequestContext;\n /**\n * Optional tag under which `initialize` exposes the plugin's services.\n * The host uses it to read services (e.g. the auth plugin's Better Auth\n * handler) from the built Effect context without importing the plugin\n * module statically.\n */\n servicesTag?: Context.Key<any, any>;\n /**\n * Initialize the plugin by returning an Effect `Layer`.\n *\n * The runtime builds the Layer against the plugin's lifecycle scope —\n * scoped resources (db pools, repositories, caches, publishers) release\n * when the plugin shuts down. Services are provided to `.effect()`\n * handlers via the oRPC context (`yield* Tag`).\n */\n initialize?: (\n config: PluginInitializeInput<V, S>,\n plugins: P,\n ) => Effect.Effect<Layer.Layer<any, Error, PluginEnv>, Error, PluginEnv>;\n /**\n * Creates the strongly-typed oRPC router for this plugin.\n * Services come from the Effect context — access them with\n * `yield* Tag` in `.effect()` handlers, or\n * `Context.get(context[\"effect/context\"], Tag)` in plain handlers.\n * Sibling plugin entries in `plugins` carry `{ client, router }`.\n */\n createRouter: (\n builder: Implementer<TContract, ContextOutput<TRequestContext> & WithEffectContext<any>>,\n plugins: P,\n ) => ContractedRouter<TContract, any>;\n};\n\n/**\n * Loaded plugin with static binding property\n */\nexport interface LoadedPluginWithBinding<\n TContract extends AnyContractRouter,\n TVariables extends AnySchema,\n TSecrets extends AnySchema,\n TRequestContext extends AnySchema | undefined,\n> {\n new (): Plugin<TContract, TVariables, TSecrets, TRequestContext>;\n binding: {\n contract: TContract;\n variables: TVariables;\n secrets: TSecrets;\n context: TRequestContext;\n servicesTag?: Context.Key<any, any>;\n };\n}\n\n/**\n * Plugin interface\n */\nexport interface Plugin<\n TContract extends AnyContractRouter,\n TVariables extends AnySchema,\n TSecrets extends AnySchema,\n TRequestContext extends AnySchema | undefined,\n> {\n readonly id: string;\n readonly contract: TContract;\n readonly configSchema: PluginConfigFor<TVariables, TSecrets, TRequestContext>;\n readonly servicesTag?: Context.Key<any, any>;\n\n initialize(\n config: PluginInitializeInput<TVariables, TSecrets>,\n plugins: Record<string, unknown>,\n ): Effect.Effect<Layer.Layer<any, Error, PluginEnv>, PluginRuntimeError, PluginEnv>;\n\n /**\n * Creates the strongly-typed oRPC router for this plugin.\n * The router's procedure types are inferred directly from the contract.\n * @param plugins Sibling plugin entries (`{ client, router }`) for\n * cross-plugin composition\n * @returns A router with procedures matching the plugin's contract\n */\n createRouter(plugins: Record<string, unknown>): ContractedRouter<TContract, any>;\n}\n\nexport interface CreatePluginFn {\n <\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined = undefined,\n P extends Record<string, unknown> = Record<string, never>,\n >(\n config: PluginDefinition<V, S, TContract, TRequestContext, P>,\n ): LoadedPluginWithBinding<TContract, V, S, TRequestContext>;\n\n withPlugins: <P extends Record<string, unknown>>() => CreatePluginWithPlugins<P>;\n}\n\nexport const createPlugin: CreatePluginFn = function createPlugin<\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined = undefined,\n P extends Record<string, unknown> = Record<string, never>,\n>(config: PluginDefinition<V, S, TContract, TRequestContext, P>) {\n const configSchema: PluginConfigFor<V, S, TRequestContext> = {\n variables: config.variables,\n secrets: config.secrets,\n context: config.context as TRequestContext,\n };\n\n class CreatedPlugin implements Plugin<TContract, V, S, TRequestContext> {\n /** set during instantiation - registry key */\n id!: string;\n readonly contract = config.contract;\n readonly configSchema = configSchema;\n readonly servicesTag = config.servicesTag;\n\n initialize(\n pluginConfig: PluginInitializeInput<V, S>,\n plugins: Record<string, unknown> = {},\n ): Effect.Effect<Layer.Layer<any, Error, PluginEnv>, PluginRuntimeError, PluginEnv> {\n const init = config.initialize;\n if (!init) {\n return Effect.succeed(Layer.empty as unknown as Layer.Layer<any, Error, PluginEnv>);\n }\n\n return init(pluginConfig, plugins as P).pipe(\n Effect.mapError((error) =>\n toPluginRuntimeError(error, this.id, undefined, \"initialize-plugin\"),\n ),\n );\n }\n\n createRouter(plugins: Record<string, unknown>): ContractedRouter<TContract, any> {\n const builder = implement(config.contract).$context<\n ContextOutput<TRequestContext> & WithEffectContext<any>\n >();\n return config.createRouter(builder as any, plugins as P) as ContractedRouter<TContract, any>;\n }\n }\n\n const PluginConstructor = CreatedPlugin as unknown as {\n new (): Plugin<TContract, V, S, TRequestContext>;\n binding: {\n contract: TContract;\n variables: V;\n secrets: S;\n context: TRequestContext;\n servicesTag?: Context.Key<any, any>;\n };\n };\n\n PluginConstructor.binding = {\n contract: config.contract,\n variables: config.variables,\n secrets: config.secrets,\n context: config.context as TRequestContext,\n servicesTag: config.servicesTag,\n };\n\n return PluginConstructor as LoadedPluginWithBinding<TContract, V, S, TRequestContext>;\n};\n\nexport type CreatePluginWithPlugins<P extends Record<string, unknown>> = <\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined = undefined,\n>(\n config: PluginDefinition<V, S, TContract, TRequestContext, P>,\n) => LoadedPluginWithBinding<TContract, V, S, TRequestContext>;\n\nexport function withPlugins<P extends Record<string, unknown>>(): CreatePluginWithPlugins<P> {\n return <\n V extends AnySchema,\n S extends AnySchema,\n TContract extends AnyContractRouter,\n TRequestContext extends AnySchema | undefined = undefined,\n >(\n config: PluginDefinition<V, S, TContract, TRequestContext, P>,\n ) => createPlugin<V, S, TContract, TRequestContext, P>(config as any);\n}\n\ncreatePlugin.withPlugins = withPlugins;\n"],"mappings":";;;;;;AA0BA,IAAa,cAAb,cAAiC,QAAQ,QAA6B,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;AAiHrF,MAAa,eAA+B,SAAS,aAMnD,QAA+D;CAC/D,MAAM,eAAuD;EAC3D,WAAW,OAAO;EAClB,SAAS,OAAO;EAChB,SAAS,OAAO;CAClB;CAEA,MAAM,cAAkE;;EAEtE;EACA,AAAS,WAAW,OAAO;EAC3B,AAAS,eAAe;EACxB,AAAS,cAAc,OAAO;EAE9B,WACE,cACA,UAAmC,CAAC,GAC8C;GAClF,MAAM,OAAO,OAAO;GACpB,IAAI,CAAC,MACH,OAAO,OAAO,QAAQ,MAAM,KAAsD;GAGpF,OAAO,KAAK,cAAc,OAAY,CAAC,CAAC,KACtC,OAAO,UAAU,UACf,qBAAqB,OAAO,KAAK,IAAI,QAAW,mBAAmB,CACrE,CACF;EACF;EAEA,aAAa,SAAoE;GAC/E,MAAM,UAAU,UAAU,OAAO,QAAQ,CAAC,CAAC,SAEzC;GACF,OAAO,OAAO,aAAa,SAAgB,OAAY;EACzD;CACF;CAEA,MAAM,oBAAoB;CAW1B,kBAAkB,UAAU;EAC1B,UAAU,OAAO;EACjB,WAAW,OAAO;EAClB,SAAS,OAAO;EAChB,SAAS,OAAO;EAChB,aAAa,OAAO;CACtB;CAEA,OAAO;AACT;AAWA,SAAgB,cAA6E;CAC3F,QAME,WACG,aAAkD,MAAa;AACtE;AAEA,aAAa,cAAc"}
@@ -0,0 +1,103 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_errors = require('./runtime/errors.cjs');
3
+ let effect = require("effect");
4
+
5
+ //#region src/remote-entry.ts
6
+ /**
7
+ * Remote-entry loading resilience — the ONE contract every MF consumer in
8
+ * the dev stack shares (plugin dev serves, the host's plugin loader).
9
+ *
10
+ * Two failure classes it exists for:
11
+ * 1. Cold compile: remoteEntry.js 404s until the remote's first build —
12
+ * the 404 body gets eval'd as JS ("Unexpected identifier 'Found'").
13
+ * 2. Watch-rebuild poisoning: a failed entry load is memoized forever in
14
+ * `globalThis.__GLOBAL_LOADING_REMOTE_ENTRY__` (runtime-core never evicts
15
+ * rejections), so retries without a purge await the same failure.
16
+ */
17
+ const POLL_INTERVAL_MS = 300;
18
+ const DEFAULT_TIMEOUT_MS = 12e4;
19
+ const RETRY_BASE_DELAY_MS = 500;
20
+ const RETRY_MAX_DELAY_MS = 5e3;
21
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
22
+ /**
23
+ * Deletes the failed remote's keys from the process-global entry-load cache.
24
+ * Deleting a RESOLVED entry would force a wasteful re-eval, so only call
25
+ * this from failure paths.
26
+ */
27
+ const purgeRemoteEntryCache = (remoteUrl) => {
28
+ const globalLoading = globalThis.__GLOBAL_LOADING_REMOTE_ENTRY__;
29
+ if (!globalLoading) return;
30
+ for (const key of Object.keys(globalLoading)) if (key.endsWith(`:${remoteUrl}`)) delete globalLoading[key];
31
+ };
32
+ const isRemoteEntryBody = (body) => !/not found|<(!doctype|html)/i.test(body.slice(0, 64));
33
+ const waitForRemoteEntryReady = async (label, url, timeoutMs = DEFAULT_TIMEOUT_MS) => {
34
+ const deadline = Date.now() + timeoutMs;
35
+ let announced = false;
36
+ while (Date.now() < deadline) {
37
+ const res = await fetch(url).catch(() => null);
38
+ if (res?.ok && isRemoteEntryBody(await res.text().catch(() => ""))) return;
39
+ if (!announced) {
40
+ announced = true;
41
+ console.log(`⏳ waiting for ${label} to compile (remote entry not ready)…`);
42
+ }
43
+ await sleep(POLL_INTERVAL_MS);
44
+ }
45
+ throw new Error(`${label} remote entry never became ready at ${url}`);
46
+ };
47
+ /**
48
+ * The one plugin-load retry policy: poll the remote entry until it is actually
49
+ * served, then load with capped exponential backoff — purging the poisoned
50
+ * global entry cache between attempts so each retry genuinely re-fetches.
51
+ *
52
+ * Recovery policy is `classifyPluginFailure().retryable`: transient network
53
+ * conditions retry until the wall-clock budget runs out; permanent failures
54
+ * (MF identity skew, schema validation, dead remotes, TLS verification) fail
55
+ * fast on the first attempt instead of burning the budget silently.
56
+ *
57
+ * Fails with the ORIGINAL error from `load` — the host builds
58
+ * `PluginBootstrapError.cause` from it and classification walks it.
59
+ */
60
+ const loadRemoteWithRetry = (options) => {
61
+ const { label, remoteUrl, load, timeoutMs = DEFAULT_TIMEOUT_MS, readinessTimeoutMs, onAttempt } = options;
62
+ const readinessTimeout = readinessTimeoutMs ?? timeoutMs;
63
+ const schedule = effect.Schedule.exponential(effect.Duration.millis(RETRY_BASE_DELAY_MS)).pipe(effect.Schedule.modifyDelay(({ output }) => effect.Effect.succeed(effect.Duration.toMillis(output) > RETRY_MAX_DELAY_MS ? effect.Duration.millis(RETRY_MAX_DELAY_MS) : output)), effect.Schedule.upTo({ duration: effect.Duration.millis(timeoutMs) }));
64
+ let attempt = 0;
65
+ let lastSignature;
66
+ const reportFailure = (error) => {
67
+ attempt += 1;
68
+ purgeRemoteEntryCache(remoteUrl);
69
+ const classification = require_errors.classifyPluginFailure(error);
70
+ if (onAttempt) {
71
+ onAttempt(error, {
72
+ attempt,
73
+ classification
74
+ });
75
+ return;
76
+ }
77
+ const signature = `${classification.kind}::${classification.message}`;
78
+ if (signature === lastSignature) return;
79
+ lastSignature = signature;
80
+ const stack = require_errors.deepestErrorStack(error);
81
+ console.error(`[Plugins][${label}] ❌ ${classification.kind} failure (attempt ${attempt}): ${classification.message}` + (classification.suggestion ? `\n[Plugins][${label}] → ${classification.suggestion}` : "") + (stack ? `\n[Plugins][${label}] ${stack}` : ""));
82
+ };
83
+ const loadWithRetry = effect.Effect.tryPromise({
84
+ try: load,
85
+ catch: (error) => require_errors.toPluginRuntimeError(error, label, void 0, "remote-entry-load")
86
+ }).pipe(effect.Effect.tapError((error) => effect.Effect.sync(() => reportFailure(error))), effect.Effect.retry({
87
+ schedule,
88
+ while: (error) => require_errors.classifyPluginFailure(error).retryable
89
+ }));
90
+ if (readinessTimeout === 0) return loadWithRetry;
91
+ return effect.Effect.tryPromise({
92
+ try: () => waitForRemoteEntryReady(label, remoteUrl, readinessTimeout),
93
+ catch: (error) => require_errors.toPluginRuntimeError(error, label, void 0, "remote-entry-readiness")
94
+ }).pipe(effect.Effect.flatMap(() => loadWithRetry));
95
+ };
96
+
97
+ //#endregion
98
+ exports.isRemoteEntryBody = isRemoteEntryBody;
99
+ exports.loadRemoteWithRetry = loadRemoteWithRetry;
100
+ exports.purgeRemoteEntryCache = purgeRemoteEntryCache;
101
+ exports.sleep = sleep;
102
+ exports.waitForRemoteEntryReady = waitForRemoteEntryReady;
103
+ //# sourceMappingURL=remote-entry.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-entry.cjs","names":["Schedule","Duration","Effect","classifyPluginFailure","deepestErrorStack","toPluginRuntimeError"],"sources":["../src/remote-entry.ts"],"sourcesContent":["/**\n * Remote-entry loading resilience — the ONE contract every MF consumer in\n * the dev stack shares (plugin dev serves, the host's plugin loader).\n *\n * Two failure classes it exists for:\n * 1. Cold compile: remoteEntry.js 404s until the remote's first build —\n * the 404 body gets eval'd as JS (\"Unexpected identifier 'Found'\").\n * 2. Watch-rebuild poisoning: a failed entry load is memoized forever in\n * `globalThis.__GLOBAL_LOADING_REMOTE_ENTRY__` (runtime-core never evicts\n * rejections), so retries without a purge await the same failure.\n */\nimport { Duration, Effect, Schedule } from \"effect\";\nimport type { PluginRuntimeError } from \"./runtime/errors\";\nimport {\n classifyPluginFailure,\n deepestErrorStack,\n type PluginFailureClassification,\n toPluginRuntimeError,\n} from \"./runtime/errors\";\n\nconst POLL_INTERVAL_MS = 300;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst RETRY_BASE_DELAY_MS = 500;\nconst RETRY_MAX_DELAY_MS = 5_000;\n\nexport const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Deletes the failed remote's keys from the process-global entry-load cache.\n * Deleting a RESOLVED entry would force a wasteful re-eval, so only call\n * this from failure paths.\n */\nexport const purgeRemoteEntryCache = (remoteUrl: string): void => {\n const globalLoading = (globalThis as Record<string, unknown>).__GLOBAL_LOADING_REMOTE_ENTRY__ as\n | Record<string, unknown>\n | undefined;\n if (!globalLoading) return;\n for (const key of Object.keys(globalLoading)) {\n if (key.endsWith(`:${remoteUrl}`)) delete globalLoading[key];\n }\n};\n\nexport const isRemoteEntryBody = (body: string): boolean =>\n !/not found|<(!doctype|html)/i.test(body.slice(0, 64));\n\nexport const waitForRemoteEntryReady = async (\n label: string,\n url: string,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n): Promise<void> => {\n const deadline = Date.now() + timeoutMs;\n let announced = false;\n while (Date.now() < deadline) {\n const res = await fetch(url).catch(() => null);\n if (res?.ok && isRemoteEntryBody(await res.text().catch(() => \"\"))) return;\n if (!announced) {\n announced = true;\n console.log(`⏳ waiting for ${label} to compile (remote entry not ready)…`);\n }\n await sleep(POLL_INTERVAL_MS);\n }\n throw new Error(`${label} remote entry never became ready at ${url}`);\n};\n\nexport interface PluginLoadAttemptInfo {\n attempt: number;\n classification: PluginFailureClassification;\n}\n\nexport interface PluginLoadRetryOptions<T> {\n /** short plugin key for log prefixes (host entry key or plugin id) */\n label: string;\n remoteUrl: string;\n load: () => Promise<T>;\n /** wall-clock budget for the retry loop */\n timeoutMs?: number;\n /**\n * Budget for the readiness poll (cold-compile wait) — defaults to the\n * retry budget, preserving the historical behavior. Pass `0` to skip the\n * poll (useful when the caller stubs `load` and does not need network).\n */\n readinessTimeoutMs?: number;\n /**\n * Observes every failed attempt (including non-retryable ones that end the\n * loop). Defaults to classification-deduped console.error under the\n * `[Plugins]` prefix.\n */\n onAttempt?: ((error: unknown, info: PluginLoadAttemptInfo) => void) | undefined;\n}\n\n/**\n * The one plugin-load retry policy: poll the remote entry until it is actually\n * served, then load with capped exponential backoff — purging the poisoned\n * global entry cache between attempts so each retry genuinely re-fetches.\n *\n * Recovery policy is `classifyPluginFailure().retryable`: transient network\n * conditions retry until the wall-clock budget runs out; permanent failures\n * (MF identity skew, schema validation, dead remotes, TLS verification) fail\n * fast on the first attempt instead of burning the budget silently.\n *\n * Fails with the ORIGINAL error from `load` — the host builds\n * `PluginBootstrapError.cause` from it and classification walks it.\n */\nexport const loadRemoteWithRetry = <T>(\n options: PluginLoadRetryOptions<T>,\n): Effect.Effect<T, PluginRuntimeError> => {\n const {\n label,\n remoteUrl,\n load,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n readinessTimeoutMs,\n onAttempt,\n } = options;\n const readinessTimeout = readinessTimeoutMs ?? timeoutMs;\n\n const schedule = Schedule.exponential(Duration.millis(RETRY_BASE_DELAY_MS)).pipe(\n Schedule.modifyDelay(({ output }) =>\n Effect.succeed(\n Duration.toMillis(output) > RETRY_MAX_DELAY_MS\n ? Duration.millis(RETRY_MAX_DELAY_MS)\n : output,\n ),\n ),\n Schedule.upTo({ duration: Duration.millis(timeoutMs) }),\n );\n\n let attempt = 0;\n let lastSignature: string | undefined;\n const reportFailure = (error: unknown): void => {\n attempt += 1;\n purgeRemoteEntryCache(remoteUrl);\n const classification = classifyPluginFailure(error);\n if (onAttempt) {\n onAttempt(error, { attempt, classification });\n return;\n }\n const signature = `${classification.kind}::${classification.message}`;\n if (signature === lastSignature) return;\n lastSignature = signature;\n // The message alone is rarely enough, and the wrapper's own stack points\n // at the wrapping frames — log the DEEPEST cause's stack (the original\n // throw site inside the evaluated remote entry).\n const stack = deepestErrorStack(error);\n console.error(\n `[Plugins][${label}] ❌ ${classification.kind} failure (attempt ${attempt}): ${classification.message}` +\n (classification.suggestion ? `\\n[Plugins][${label}] → ${classification.suggestion}` : \"\") +\n (stack ? `\\n[Plugins][${label}] ${stack}` : \"\"),\n );\n };\n\n const loadWithRetry = Effect.tryPromise({\n try: load,\n catch: (error) => toPluginRuntimeError(error, label, undefined, \"remote-entry-load\"),\n }).pipe(\n Effect.tapError((error) => Effect.sync(() => reportFailure(error))),\n Effect.retry({ schedule, while: (error) => classifyPluginFailure(error).retryable }),\n );\n\n // readinessTimeoutMs: 0 skips the cold-compile poll (tests stub `load` and\n // must not race a 1ms wall-clock window against the event loop).\n if (readinessTimeout === 0) {\n return loadWithRetry;\n }\n\n return Effect.tryPromise({\n try: () => waitForRemoteEntryReady(label, remoteUrl, readinessTimeout),\n catch: (error) => toPluginRuntimeError(error, label, undefined, \"remote-entry-readiness\"),\n }).pipe(Effect.flatMap(() => loadWithRetry));\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;AAE3B,MAAa,SAAS,OACpB,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;;;;;;AAOlD,MAAa,yBAAyB,cAA4B;CAChE,MAAM,gBAAiB,WAAuC;CAG9D,IAAI,CAAC,eAAe;CACpB,KAAK,MAAM,OAAO,OAAO,KAAK,aAAa,GACzC,IAAI,IAAI,SAAS,IAAI,WAAW,GAAG,OAAO,cAAc;AAE5D;AAEA,MAAa,qBAAqB,SAChC,CAAC,8BAA8B,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC;AAEvD,MAAa,0BAA0B,OACrC,OACA,KACA,YAAY,uBACM;CAClB,MAAM,WAAW,KAAK,IAAI,IAAI;CAC9B,IAAI,YAAY;CAChB,OAAO,KAAK,IAAI,IAAI,UAAU;EAC5B,MAAM,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,IAAI;EAC7C,IAAI,KAAK,MAAM,kBAAkB,MAAM,IAAI,KAAK,CAAC,CAAC,YAAY,EAAE,CAAC,GAAG;EACpE,IAAI,CAAC,WAAW;GACd,YAAY;GACZ,QAAQ,IAAI,iBAAiB,MAAM,sCAAsC;EAC3E;EACA,MAAM,MAAM,gBAAgB;CAC9B;CACA,MAAM,IAAI,MAAM,GAAG,MAAM,sCAAsC,KAAK;AACtE;;;;;;;;;;;;;;AAyCA,MAAa,uBACX,YACyC;CACzC,MAAM,EACJ,OACA,WACA,MACA,YAAY,oBACZ,oBACA,cACE;CACJ,MAAM,mBAAmB,sBAAsB;CAE/C,MAAM,WAAWA,gBAAS,YAAYC,gBAAS,OAAO,mBAAmB,CAAC,CAAC,CAAC,KAC1ED,gBAAS,aAAa,EAAE,aACtBE,cAAO,QACLD,gBAAS,SAAS,MAAM,IAAI,qBACxBA,gBAAS,OAAO,kBAAkB,IAClC,MACN,CACF,GACAD,gBAAS,KAAK,EAAE,UAAUC,gBAAS,OAAO,SAAS,EAAE,CAAC,CACxD;CAEA,IAAI,UAAU;CACd,IAAI;CACJ,MAAM,iBAAiB,UAAyB;EAC9C,WAAW;EACX,sBAAsB,SAAS;EAC/B,MAAM,iBAAiBE,qCAAsB,KAAK;EAClD,IAAI,WAAW;GACb,UAAU,OAAO;IAAE;IAAS;GAAe,CAAC;GAC5C;EACF;EACA,MAAM,YAAY,GAAG,eAAe,KAAK,IAAI,eAAe;EAC5D,IAAI,cAAc,eAAe;EACjC,gBAAgB;EAIhB,MAAM,QAAQC,iCAAkB,KAAK;EACrC,QAAQ,MACN,aAAa,MAAM,MAAM,eAAe,KAAK,oBAAoB,QAAQ,KAAK,eAAe,aAC1F,eAAe,aAAa,eAAe,MAAM,MAAM,eAAe,eAAe,OACrF,QAAQ,eAAe,MAAM,IAAI,UAAU,GAChD;CACF;CAEA,MAAM,gBAAgBF,cAAO,WAAW;EACtC,KAAK;EACL,QAAQ,UAAUG,oCAAqB,OAAO,OAAO,QAAW,mBAAmB;CACrF,CAAC,CAAC,CAAC,KACDH,cAAO,UAAU,UAAUA,cAAO,WAAW,cAAc,KAAK,CAAC,CAAC,GAClEA,cAAO,MAAM;EAAE;EAAU,QAAQ,UAAUC,qCAAsB,KAAK,CAAC,CAAC;CAAU,CAAC,CACrF;CAIA,IAAI,qBAAqB,GACvB,OAAO;CAGT,OAAOD,cAAO,WAAW;EACvB,WAAW,wBAAwB,OAAO,WAAW,gBAAgB;EACrE,QAAQ,UAAUG,oCAAqB,OAAO,OAAO,QAAW,wBAAwB;CAC1F,CAAC,CAAC,CAAC,KAAKH,cAAO,cAAc,aAAa,CAAC;AAC7C"}
@@ -0,0 +1,53 @@
1
+ import { PluginFailureClassification, PluginRuntimeError } from "./runtime/errors.cjs";
2
+ import { Effect } from "effect";
3
+ //#region src/remote-entry.d.ts
4
+ declare const sleep: (ms: number) => Promise<void>;
5
+ /**
6
+ * Deletes the failed remote's keys from the process-global entry-load cache.
7
+ * Deleting a RESOLVED entry would force a wasteful re-eval, so only call
8
+ * this from failure paths.
9
+ */
10
+ declare const purgeRemoteEntryCache: (remoteUrl: string) => void;
11
+ declare const isRemoteEntryBody: (body: string) => boolean;
12
+ declare const waitForRemoteEntryReady: (label: string, url: string, timeoutMs?: number) => Promise<void>;
13
+ interface PluginLoadAttemptInfo {
14
+ attempt: number;
15
+ classification: PluginFailureClassification;
16
+ }
17
+ interface PluginLoadRetryOptions<T> {
18
+ /** short plugin key for log prefixes (host entry key or plugin id) */
19
+ label: string;
20
+ remoteUrl: string;
21
+ load: () => Promise<T>;
22
+ /** wall-clock budget for the retry loop */
23
+ timeoutMs?: number;
24
+ /**
25
+ * Budget for the readiness poll (cold-compile wait) — defaults to the
26
+ * retry budget, preserving the historical behavior. Pass `0` to skip the
27
+ * poll (useful when the caller stubs `load` and does not need network).
28
+ */
29
+ readinessTimeoutMs?: number;
30
+ /**
31
+ * Observes every failed attempt (including non-retryable ones that end the
32
+ * loop). Defaults to classification-deduped console.error under the
33
+ * `[Plugins]` prefix.
34
+ */
35
+ onAttempt?: ((error: unknown, info: PluginLoadAttemptInfo) => void) | undefined;
36
+ }
37
+ /**
38
+ * The one plugin-load retry policy: poll the remote entry until it is actually
39
+ * served, then load with capped exponential backoff — purging the poisoned
40
+ * global entry cache between attempts so each retry genuinely re-fetches.
41
+ *
42
+ * Recovery policy is `classifyPluginFailure().retryable`: transient network
43
+ * conditions retry until the wall-clock budget runs out; permanent failures
44
+ * (MF identity skew, schema validation, dead remotes, TLS verification) fail
45
+ * fast on the first attempt instead of burning the budget silently.
46
+ *
47
+ * Fails with the ORIGINAL error from `load` — the host builds
48
+ * `PluginBootstrapError.cause` from it and classification walks it.
49
+ */
50
+ declare const loadRemoteWithRetry: <T>(options: PluginLoadRetryOptions<T>) => Effect.Effect<T, PluginRuntimeError>;
51
+ //#endregion
52
+ export { PluginLoadAttemptInfo, PluginLoadRetryOptions, isRemoteEntryBody, loadRemoteWithRetry, purgeRemoteEntryCache, sleep, waitForRemoteEntryReady };
53
+ //# sourceMappingURL=remote-entry.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-entry.d.cts","names":[],"sources":["../src/remote-entry.ts"],"mappings":";;;cAyBa,QAAK,eAAiB;;;;;;cAQtB,wBAAqB;cAUrB,oBAAiB;cAGjB,0BAAuB,eACrB,aACF,uBAEV;UAec;EACf;EACA,gBAAgB;;UAGD,uBAAuB;;EAEtC;EACA;EACA,YAAY,QAAQ;;EAEpB;;;;;;EAMA;;;;;;EAMA,cAAc,gBAAgB,MAAM;;;;;;;;;;;;;;;cAgBzB,sBAAuB,GAAC,SAC1B,uBAAuB,OAC/B,OAAO,OAAO,GAAG"}
@@ -0,0 +1,53 @@
1
+ import { PluginFailureClassification, PluginRuntimeError } from "./runtime/errors.mjs";
2
+ import { Effect } from "effect";
3
+ //#region src/remote-entry.d.ts
4
+ declare const sleep: (ms: number) => Promise<void>;
5
+ /**
6
+ * Deletes the failed remote's keys from the process-global entry-load cache.
7
+ * Deleting a RESOLVED entry would force a wasteful re-eval, so only call
8
+ * this from failure paths.
9
+ */
10
+ declare const purgeRemoteEntryCache: (remoteUrl: string) => void;
11
+ declare const isRemoteEntryBody: (body: string) => boolean;
12
+ declare const waitForRemoteEntryReady: (label: string, url: string, timeoutMs?: number) => Promise<void>;
13
+ interface PluginLoadAttemptInfo {
14
+ attempt: number;
15
+ classification: PluginFailureClassification;
16
+ }
17
+ interface PluginLoadRetryOptions<T> {
18
+ /** short plugin key for log prefixes (host entry key or plugin id) */
19
+ label: string;
20
+ remoteUrl: string;
21
+ load: () => Promise<T>;
22
+ /** wall-clock budget for the retry loop */
23
+ timeoutMs?: number;
24
+ /**
25
+ * Budget for the readiness poll (cold-compile wait) — defaults to the
26
+ * retry budget, preserving the historical behavior. Pass `0` to skip the
27
+ * poll (useful when the caller stubs `load` and does not need network).
28
+ */
29
+ readinessTimeoutMs?: number;
30
+ /**
31
+ * Observes every failed attempt (including non-retryable ones that end the
32
+ * loop). Defaults to classification-deduped console.error under the
33
+ * `[Plugins]` prefix.
34
+ */
35
+ onAttempt?: ((error: unknown, info: PluginLoadAttemptInfo) => void) | undefined;
36
+ }
37
+ /**
38
+ * The one plugin-load retry policy: poll the remote entry until it is actually
39
+ * served, then load with capped exponential backoff — purging the poisoned
40
+ * global entry cache between attempts so each retry genuinely re-fetches.
41
+ *
42
+ * Recovery policy is `classifyPluginFailure().retryable`: transient network
43
+ * conditions retry until the wall-clock budget runs out; permanent failures
44
+ * (MF identity skew, schema validation, dead remotes, TLS verification) fail
45
+ * fast on the first attempt instead of burning the budget silently.
46
+ *
47
+ * Fails with the ORIGINAL error from `load` — the host builds
48
+ * `PluginBootstrapError.cause` from it and classification walks it.
49
+ */
50
+ declare const loadRemoteWithRetry: <T>(options: PluginLoadRetryOptions<T>) => Effect.Effect<T, PluginRuntimeError>;
51
+ //#endregion
52
+ export { PluginLoadAttemptInfo, PluginLoadRetryOptions, isRemoteEntryBody, loadRemoteWithRetry, purgeRemoteEntryCache, sleep, waitForRemoteEntryReady };
53
+ //# sourceMappingURL=remote-entry.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-entry.d.mts","names":[],"sources":["../src/remote-entry.ts"],"mappings":";;;cAyBa,QAAK,eAAiB;;;;;;cAQtB,wBAAqB;cAUrB,oBAAiB;cAGjB,0BAAuB,eACrB,aACF,uBAEV;UAec;EACf;EACA,gBAAgB;;UAGD,uBAAuB;;EAEtC;EACA;EACA,YAAY,QAAQ;;EAEpB;;;;;;EAMA;;;;;;EAMA,cAAc,gBAAgB,MAAM;;;;;;;;;;;;;;;cAgBzB,sBAAuB,GAAC,SAC1B,uBAAuB,OAC/B,OAAO,OAAO,GAAG"}
@@ -0,0 +1,98 @@
1
+ import { classifyPluginFailure, deepestErrorStack, toPluginRuntimeError } from "./runtime/errors.mjs";
2
+ import { Duration, Effect, Schedule } from "effect";
3
+
4
+ //#region src/remote-entry.ts
5
+ /**
6
+ * Remote-entry loading resilience — the ONE contract every MF consumer in
7
+ * the dev stack shares (plugin dev serves, the host's plugin loader).
8
+ *
9
+ * Two failure classes it exists for:
10
+ * 1. Cold compile: remoteEntry.js 404s until the remote's first build —
11
+ * the 404 body gets eval'd as JS ("Unexpected identifier 'Found'").
12
+ * 2. Watch-rebuild poisoning: a failed entry load is memoized forever in
13
+ * `globalThis.__GLOBAL_LOADING_REMOTE_ENTRY__` (runtime-core never evicts
14
+ * rejections), so retries without a purge await the same failure.
15
+ */
16
+ const POLL_INTERVAL_MS = 300;
17
+ const DEFAULT_TIMEOUT_MS = 12e4;
18
+ const RETRY_BASE_DELAY_MS = 500;
19
+ const RETRY_MAX_DELAY_MS = 5e3;
20
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
21
+ /**
22
+ * Deletes the failed remote's keys from the process-global entry-load cache.
23
+ * Deleting a RESOLVED entry would force a wasteful re-eval, so only call
24
+ * this from failure paths.
25
+ */
26
+ const purgeRemoteEntryCache = (remoteUrl) => {
27
+ const globalLoading = globalThis.__GLOBAL_LOADING_REMOTE_ENTRY__;
28
+ if (!globalLoading) return;
29
+ for (const key of Object.keys(globalLoading)) if (key.endsWith(`:${remoteUrl}`)) delete globalLoading[key];
30
+ };
31
+ const isRemoteEntryBody = (body) => !/not found|<(!doctype|html)/i.test(body.slice(0, 64));
32
+ const waitForRemoteEntryReady = async (label, url, timeoutMs = DEFAULT_TIMEOUT_MS) => {
33
+ const deadline = Date.now() + timeoutMs;
34
+ let announced = false;
35
+ while (Date.now() < deadline) {
36
+ const res = await fetch(url).catch(() => null);
37
+ if (res?.ok && isRemoteEntryBody(await res.text().catch(() => ""))) return;
38
+ if (!announced) {
39
+ announced = true;
40
+ console.log(`⏳ waiting for ${label} to compile (remote entry not ready)…`);
41
+ }
42
+ await sleep(POLL_INTERVAL_MS);
43
+ }
44
+ throw new Error(`${label} remote entry never became ready at ${url}`);
45
+ };
46
+ /**
47
+ * The one plugin-load retry policy: poll the remote entry until it is actually
48
+ * served, then load with capped exponential backoff — purging the poisoned
49
+ * global entry cache between attempts so each retry genuinely re-fetches.
50
+ *
51
+ * Recovery policy is `classifyPluginFailure().retryable`: transient network
52
+ * conditions retry until the wall-clock budget runs out; permanent failures
53
+ * (MF identity skew, schema validation, dead remotes, TLS verification) fail
54
+ * fast on the first attempt instead of burning the budget silently.
55
+ *
56
+ * Fails with the ORIGINAL error from `load` — the host builds
57
+ * `PluginBootstrapError.cause` from it and classification walks it.
58
+ */
59
+ const loadRemoteWithRetry = (options) => {
60
+ const { label, remoteUrl, load, timeoutMs = DEFAULT_TIMEOUT_MS, readinessTimeoutMs, onAttempt } = options;
61
+ const readinessTimeout = readinessTimeoutMs ?? timeoutMs;
62
+ const schedule = Schedule.exponential(Duration.millis(RETRY_BASE_DELAY_MS)).pipe(Schedule.modifyDelay(({ output }) => Effect.succeed(Duration.toMillis(output) > RETRY_MAX_DELAY_MS ? Duration.millis(RETRY_MAX_DELAY_MS) : output)), Schedule.upTo({ duration: Duration.millis(timeoutMs) }));
63
+ let attempt = 0;
64
+ let lastSignature;
65
+ const reportFailure = (error) => {
66
+ attempt += 1;
67
+ purgeRemoteEntryCache(remoteUrl);
68
+ const classification = classifyPluginFailure(error);
69
+ if (onAttempt) {
70
+ onAttempt(error, {
71
+ attempt,
72
+ classification
73
+ });
74
+ return;
75
+ }
76
+ const signature = `${classification.kind}::${classification.message}`;
77
+ if (signature === lastSignature) return;
78
+ lastSignature = signature;
79
+ const stack = deepestErrorStack(error);
80
+ console.error(`[Plugins][${label}] ❌ ${classification.kind} failure (attempt ${attempt}): ${classification.message}` + (classification.suggestion ? `\n[Plugins][${label}] → ${classification.suggestion}` : "") + (stack ? `\n[Plugins][${label}] ${stack}` : ""));
81
+ };
82
+ const loadWithRetry = Effect.tryPromise({
83
+ try: load,
84
+ catch: (error) => toPluginRuntimeError(error, label, void 0, "remote-entry-load")
85
+ }).pipe(Effect.tapError((error) => Effect.sync(() => reportFailure(error))), Effect.retry({
86
+ schedule,
87
+ while: (error) => classifyPluginFailure(error).retryable
88
+ }));
89
+ if (readinessTimeout === 0) return loadWithRetry;
90
+ return Effect.tryPromise({
91
+ try: () => waitForRemoteEntryReady(label, remoteUrl, readinessTimeout),
92
+ catch: (error) => toPluginRuntimeError(error, label, void 0, "remote-entry-readiness")
93
+ }).pipe(Effect.flatMap(() => loadWithRetry));
94
+ };
95
+
96
+ //#endregion
97
+ export { isRemoteEntryBody, loadRemoteWithRetry, purgeRemoteEntryCache, sleep, waitForRemoteEntryReady };
98
+ //# sourceMappingURL=remote-entry.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-entry.mjs","names":[],"sources":["../src/remote-entry.ts"],"sourcesContent":["/**\n * Remote-entry loading resilience — the ONE contract every MF consumer in\n * the dev stack shares (plugin dev serves, the host's plugin loader).\n *\n * Two failure classes it exists for:\n * 1. Cold compile: remoteEntry.js 404s until the remote's first build —\n * the 404 body gets eval'd as JS (\"Unexpected identifier 'Found'\").\n * 2. Watch-rebuild poisoning: a failed entry load is memoized forever in\n * `globalThis.__GLOBAL_LOADING_REMOTE_ENTRY__` (runtime-core never evicts\n * rejections), so retries without a purge await the same failure.\n */\nimport { Duration, Effect, Schedule } from \"effect\";\nimport type { PluginRuntimeError } from \"./runtime/errors\";\nimport {\n classifyPluginFailure,\n deepestErrorStack,\n type PluginFailureClassification,\n toPluginRuntimeError,\n} from \"./runtime/errors\";\n\nconst POLL_INTERVAL_MS = 300;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst RETRY_BASE_DELAY_MS = 500;\nconst RETRY_MAX_DELAY_MS = 5_000;\n\nexport const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Deletes the failed remote's keys from the process-global entry-load cache.\n * Deleting a RESOLVED entry would force a wasteful re-eval, so only call\n * this from failure paths.\n */\nexport const purgeRemoteEntryCache = (remoteUrl: string): void => {\n const globalLoading = (globalThis as Record<string, unknown>).__GLOBAL_LOADING_REMOTE_ENTRY__ as\n | Record<string, unknown>\n | undefined;\n if (!globalLoading) return;\n for (const key of Object.keys(globalLoading)) {\n if (key.endsWith(`:${remoteUrl}`)) delete globalLoading[key];\n }\n};\n\nexport const isRemoteEntryBody = (body: string): boolean =>\n !/not found|<(!doctype|html)/i.test(body.slice(0, 64));\n\nexport const waitForRemoteEntryReady = async (\n label: string,\n url: string,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n): Promise<void> => {\n const deadline = Date.now() + timeoutMs;\n let announced = false;\n while (Date.now() < deadline) {\n const res = await fetch(url).catch(() => null);\n if (res?.ok && isRemoteEntryBody(await res.text().catch(() => \"\"))) return;\n if (!announced) {\n announced = true;\n console.log(`⏳ waiting for ${label} to compile (remote entry not ready)…`);\n }\n await sleep(POLL_INTERVAL_MS);\n }\n throw new Error(`${label} remote entry never became ready at ${url}`);\n};\n\nexport interface PluginLoadAttemptInfo {\n attempt: number;\n classification: PluginFailureClassification;\n}\n\nexport interface PluginLoadRetryOptions<T> {\n /** short plugin key for log prefixes (host entry key or plugin id) */\n label: string;\n remoteUrl: string;\n load: () => Promise<T>;\n /** wall-clock budget for the retry loop */\n timeoutMs?: number;\n /**\n * Budget for the readiness poll (cold-compile wait) — defaults to the\n * retry budget, preserving the historical behavior. Pass `0` to skip the\n * poll (useful when the caller stubs `load` and does not need network).\n */\n readinessTimeoutMs?: number;\n /**\n * Observes every failed attempt (including non-retryable ones that end the\n * loop). Defaults to classification-deduped console.error under the\n * `[Plugins]` prefix.\n */\n onAttempt?: ((error: unknown, info: PluginLoadAttemptInfo) => void) | undefined;\n}\n\n/**\n * The one plugin-load retry policy: poll the remote entry until it is actually\n * served, then load with capped exponential backoff — purging the poisoned\n * global entry cache between attempts so each retry genuinely re-fetches.\n *\n * Recovery policy is `classifyPluginFailure().retryable`: transient network\n * conditions retry until the wall-clock budget runs out; permanent failures\n * (MF identity skew, schema validation, dead remotes, TLS verification) fail\n * fast on the first attempt instead of burning the budget silently.\n *\n * Fails with the ORIGINAL error from `load` — the host builds\n * `PluginBootstrapError.cause` from it and classification walks it.\n */\nexport const loadRemoteWithRetry = <T>(\n options: PluginLoadRetryOptions<T>,\n): Effect.Effect<T, PluginRuntimeError> => {\n const {\n label,\n remoteUrl,\n load,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n readinessTimeoutMs,\n onAttempt,\n } = options;\n const readinessTimeout = readinessTimeoutMs ?? timeoutMs;\n\n const schedule = Schedule.exponential(Duration.millis(RETRY_BASE_DELAY_MS)).pipe(\n Schedule.modifyDelay(({ output }) =>\n Effect.succeed(\n Duration.toMillis(output) > RETRY_MAX_DELAY_MS\n ? Duration.millis(RETRY_MAX_DELAY_MS)\n : output,\n ),\n ),\n Schedule.upTo({ duration: Duration.millis(timeoutMs) }),\n );\n\n let attempt = 0;\n let lastSignature: string | undefined;\n const reportFailure = (error: unknown): void => {\n attempt += 1;\n purgeRemoteEntryCache(remoteUrl);\n const classification = classifyPluginFailure(error);\n if (onAttempt) {\n onAttempt(error, { attempt, classification });\n return;\n }\n const signature = `${classification.kind}::${classification.message}`;\n if (signature === lastSignature) return;\n lastSignature = signature;\n // The message alone is rarely enough, and the wrapper's own stack points\n // at the wrapping frames — log the DEEPEST cause's stack (the original\n // throw site inside the evaluated remote entry).\n const stack = deepestErrorStack(error);\n console.error(\n `[Plugins][${label}] ❌ ${classification.kind} failure (attempt ${attempt}): ${classification.message}` +\n (classification.suggestion ? `\\n[Plugins][${label}] → ${classification.suggestion}` : \"\") +\n (stack ? `\\n[Plugins][${label}] ${stack}` : \"\"),\n );\n };\n\n const loadWithRetry = Effect.tryPromise({\n try: load,\n catch: (error) => toPluginRuntimeError(error, label, undefined, \"remote-entry-load\"),\n }).pipe(\n Effect.tapError((error) => Effect.sync(() => reportFailure(error))),\n Effect.retry({ schedule, while: (error) => classifyPluginFailure(error).retryable }),\n );\n\n // readinessTimeoutMs: 0 skips the cold-compile poll (tests stub `load` and\n // must not race a 1ms wall-clock window against the event loop).\n if (readinessTimeout === 0) {\n return loadWithRetry;\n }\n\n return Effect.tryPromise({\n try: () => waitForRemoteEntryReady(label, remoteUrl, readinessTimeout),\n catch: (error) => toPluginRuntimeError(error, label, undefined, \"remote-entry-readiness\"),\n }).pipe(Effect.flatMap(() => loadWithRetry));\n};\n"],"mappings":";;;;;;;;;;;;;;;AAoBA,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;AAE3B,MAAa,SAAS,OACpB,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;;;;;;AAOlD,MAAa,yBAAyB,cAA4B;CAChE,MAAM,gBAAiB,WAAuC;CAG9D,IAAI,CAAC,eAAe;CACpB,KAAK,MAAM,OAAO,OAAO,KAAK,aAAa,GACzC,IAAI,IAAI,SAAS,IAAI,WAAW,GAAG,OAAO,cAAc;AAE5D;AAEA,MAAa,qBAAqB,SAChC,CAAC,8BAA8B,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC;AAEvD,MAAa,0BAA0B,OACrC,OACA,KACA,YAAY,uBACM;CAClB,MAAM,WAAW,KAAK,IAAI,IAAI;CAC9B,IAAI,YAAY;CAChB,OAAO,KAAK,IAAI,IAAI,UAAU;EAC5B,MAAM,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,IAAI;EAC7C,IAAI,KAAK,MAAM,kBAAkB,MAAM,IAAI,KAAK,CAAC,CAAC,YAAY,EAAE,CAAC,GAAG;EACpE,IAAI,CAAC,WAAW;GACd,YAAY;GACZ,QAAQ,IAAI,iBAAiB,MAAM,sCAAsC;EAC3E;EACA,MAAM,MAAM,gBAAgB;CAC9B;CACA,MAAM,IAAI,MAAM,GAAG,MAAM,sCAAsC,KAAK;AACtE;;;;;;;;;;;;;;AAyCA,MAAa,uBACX,YACyC;CACzC,MAAM,EACJ,OACA,WACA,MACA,YAAY,oBACZ,oBACA,cACE;CACJ,MAAM,mBAAmB,sBAAsB;CAE/C,MAAM,WAAW,SAAS,YAAY,SAAS,OAAO,mBAAmB,CAAC,CAAC,CAAC,KAC1E,SAAS,aAAa,EAAE,aACtB,OAAO,QACL,SAAS,SAAS,MAAM,IAAI,qBACxB,SAAS,OAAO,kBAAkB,IAClC,MACN,CACF,GACA,SAAS,KAAK,EAAE,UAAU,SAAS,OAAO,SAAS,EAAE,CAAC,CACxD;CAEA,IAAI,UAAU;CACd,IAAI;CACJ,MAAM,iBAAiB,UAAyB;EAC9C,WAAW;EACX,sBAAsB,SAAS;EAC/B,MAAM,iBAAiB,sBAAsB,KAAK;EAClD,IAAI,WAAW;GACb,UAAU,OAAO;IAAE;IAAS;GAAe,CAAC;GAC5C;EACF;EACA,MAAM,YAAY,GAAG,eAAe,KAAK,IAAI,eAAe;EAC5D,IAAI,cAAc,eAAe;EACjC,gBAAgB;EAIhB,MAAM,QAAQ,kBAAkB,KAAK;EACrC,QAAQ,MACN,aAAa,MAAM,MAAM,eAAe,KAAK,oBAAoB,QAAQ,KAAK,eAAe,aAC1F,eAAe,aAAa,eAAe,MAAM,MAAM,eAAe,eAAe,OACrF,QAAQ,eAAe,MAAM,IAAI,UAAU,GAChD;CACF;CAEA,MAAM,gBAAgB,OAAO,WAAW;EACtC,KAAK;EACL,QAAQ,UAAU,qBAAqB,OAAO,OAAO,QAAW,mBAAmB;CACrF,CAAC,CAAC,CAAC,KACD,OAAO,UAAU,UAAU,OAAO,WAAW,cAAc,KAAK,CAAC,CAAC,GAClE,OAAO,MAAM;EAAE;EAAU,QAAQ,UAAU,sBAAsB,KAAK,CAAC,CAAC;CAAU,CAAC,CACrF;CAIA,IAAI,qBAAqB,GACvB,OAAO;CAGT,OAAO,OAAO,WAAW;EACvB,WAAW,wBAAwB,OAAO,WAAW,gBAAgB;EACrE,QAAQ,UAAU,qBAAqB,OAAO,OAAO,QAAW,wBAAwB;CAC1F,CAAC,CAAC,CAAC,KAAK,OAAO,cAAc,aAAa,CAAC;AAC7C"}