@hachej/boring-ui-cli 0.1.25 → 0.1.27

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 (152) hide show
  1. package/dist/server/cli.js +445 -38
  2. package/dist/server/pluginDiscovery.js +36 -0
  3. package/dist/server/pluginFrontRuntime.js +1458 -0
  4. package/package.json +7 -7
  5. package/public/assets/DebugDrawer-DXh0KPGA.js +1 -0
  6. package/public/assets/_baseUniq-BWJxxSUa.js +1 -0
  7. package/public/assets/angular-ts-BrjP3tb8.js +1 -1
  8. package/public/assets/angular-ts-BwZT4LLn.js +1 -1
  9. package/public/assets/apl-CORt7UWP.js +1 -1
  10. package/public/assets/apl-dKokRX4l.js +1 -1
  11. package/public/assets/arc-3T01ytO-.js +1 -0
  12. package/public/assets/architectureDiagram-Q4EWVU46-9WH21jtf.js +36 -0
  13. package/public/assets/astro-CbQHKStN.js +1 -1
  14. package/public/assets/astro-HNnZUWAn.js +1 -1
  15. package/public/assets/blade-BjGOyj-B.js +1 -1
  16. package/public/assets/blade-D4QpJJKB.js +1 -1
  17. package/public/assets/{blockDiagram-DXYQGD6D-DEkkj6Fh.js → blockDiagram-DXYQGD6D-B4wNk-sf.js} +5 -5
  18. package/public/assets/{c4Diagram-AHTNJAMY-CEZo72O7.js → c4Diagram-AHTNJAMY-66ixuAQP.js} +1 -1
  19. package/public/assets/channel-BgfjfIcN.js +1 -0
  20. package/public/assets/{chunk-4BX2VUAB-kct9rMc4.js → chunk-4BX2VUAB-C9hPcWjG.js} +1 -1
  21. package/public/assets/{chunk-4TB4RGXK-przR2bht.js → chunk-4TB4RGXK-ByIBLKfi.js} +3 -3
  22. package/public/assets/{chunk-55IACEB6-BtFWJCbx.js → chunk-55IACEB6-DYbtgO3B.js} +1 -1
  23. package/public/assets/{chunk-EDXVE4YY-wnitHF3B.js → chunk-EDXVE4YY-C1oGFCjz.js} +1 -1
  24. package/public/assets/{chunk-FMBD7UC4-8PDhdiie.js → chunk-FMBD7UC4-DLUzRKA6.js} +1 -1
  25. package/public/assets/{chunk-OYMX7WX6-TDftW5K8.js → chunk-OYMX7WX6-B-APDmKY.js} +2 -2
  26. package/public/assets/{chunk-QZHKN3VN-B5vnQXaM.js → chunk-QZHKN3VN-CIMiA_vz.js} +1 -1
  27. package/public/assets/{chunk-YZCP3GAM-CGg2izn8.js → chunk-YZCP3GAM-DZKjSraN.js} +1 -1
  28. package/public/assets/classDiagram-6PBFFD2Q-B5aNMtYL.js +1 -0
  29. package/public/assets/classDiagram-v2-HSJHXN6E-B5aNMtYL.js +1 -0
  30. package/public/assets/clone-aG0J-FrF.js +1 -0
  31. package/public/assets/cobol-nBiQ_Alo.js +1 -1
  32. package/public/assets/cobol-nwyudZeR.js +1 -1
  33. package/public/assets/{cose-bilkent-S5V4N54A-CjXKbjHV.js → cose-bilkent-S5V4N54A-CAGQph2O.js} +1 -1
  34. package/public/assets/crystal-DNxU26gB.js +1 -1
  35. package/public/assets/crystal-tKQVLTB8.js +1 -1
  36. package/public/assets/{dagre-KV5264BT-D-wBcwT7.js → dagre-KV5264BT-BD_8gyj4.js} +2 -2
  37. package/public/assets/diagram-5BDNPKRD-CIiGMv_J.js +10 -0
  38. package/public/assets/diagram-G4DWMVQ6-mepU3-dT.js +24 -0
  39. package/public/assets/diagram-MMDJMWI5-C6pYVXeW.js +43 -0
  40. package/public/assets/diagram-TYMM5635-CCXRPJYR.js +24 -0
  41. package/public/assets/edge-BkV0erSs.js +1 -1
  42. package/public/assets/edge-FbVlp4U3.js +1 -1
  43. package/public/assets/elixir-CDX3lj18.js +1 -1
  44. package/public/assets/elixir-CkH2-t6x.js +1 -1
  45. package/public/assets/{erDiagram-SMLLAGMA-CUWacc8b.js → erDiagram-SMLLAGMA-BSkTBfKB.js} +2 -2
  46. package/public/assets/erb-B12qg9BL.js +1 -1
  47. package/public/assets/erb-BYCe7drp.js +1 -1
  48. package/public/assets/{flowDiagram-DWJPFMVM-DVIhUX7Z.js → flowDiagram-DWJPFMVM-afgN6tlz.js} +4 -4
  49. package/public/assets/{ganttDiagram-T4ZO3ILL-g3ncLabH.js → ganttDiagram-T4ZO3ILL-CyGogAyi.js} +3 -3
  50. package/public/assets/gitGraphDiagram-UUTBAWPF-Bydmm0-D.js +106 -0
  51. package/public/assets/glimmer-js-ByusRIyA.js +1 -1
  52. package/public/assets/glimmer-js-Rg0-pVw9.js +1 -1
  53. package/public/assets/glimmer-ts-BfAWNZQY.js +1 -1
  54. package/public/assets/glimmer-ts-U6CK756n.js +1 -1
  55. package/public/assets/{graph-CDl50skT.js → graph-BTS0qkSn.js} +1 -1
  56. package/public/assets/hack-CaT9iCJl.js +1 -1
  57. package/public/assets/hack-i7_Ulhet.js +1 -1
  58. package/public/assets/haml-B8DHNrY2.js +1 -1
  59. package/public/assets/haml-D5jkg6IW.js +1 -1
  60. package/public/assets/handlebars-BL8al0AC.js +1 -1
  61. package/public/assets/handlebars-BpdQsYii.js +1 -1
  62. package/public/assets/highlighted-body-OFNGDK62-0ppq6hNS.js +1 -0
  63. package/public/assets/html-GMplVEZG.js +1 -1
  64. package/public/assets/html-derivative-BFtXZ54Q.js +1 -1
  65. package/public/assets/html-derivative-DlHx6ybY.js +1 -1
  66. package/public/assets/html-pp8916En.js +1 -1
  67. package/public/assets/index-CQUfa58M.js +1669 -0
  68. package/public/assets/index-iIIavXI3.css +1 -0
  69. package/public/assets/infoDiagram-42DDH7IO-nAJG4YWr.js +2 -0
  70. package/public/assets/{ishikawaDiagram-UXIWVN3A-BbjR1GdS.js → ishikawaDiagram-UXIWVN3A-BoovJnmR.js} +4 -4
  71. package/public/assets/jinja-4LBKfQ-Z.js +1 -1
  72. package/public/assets/jinja-f2NsQr07.js +1 -1
  73. package/public/assets/{journeyDiagram-VCZTEJTY-DJHw1u4a.js → journeyDiagram-VCZTEJTY-CclhGI24.js} +4 -4
  74. package/public/assets/just-Cw27pwNe.js +1 -1
  75. package/public/assets/just-VxiPbLrw.js +1 -1
  76. package/public/assets/{kanban-definition-6JOO6SKY-agbEFS20.js → kanban-definition-6JOO6SKY-hMdWNWdk.js} +7 -7
  77. package/public/assets/{layout-BtrYpvUW.js → layout-Cg2As0V6.js} +1 -1
  78. package/public/assets/{linear-C4IQ6Sya.js → linear-M4VCZAw5.js} +1 -1
  79. package/public/assets/liquid-C0sCDyMI.js +1 -1
  80. package/public/assets/liquid-DYVedYrR.js +1 -1
  81. package/public/assets/marko-CnJfTvn9.js +1 -1
  82. package/public/assets/marko-DjSrsDqO.js +1 -1
  83. package/public/assets/mdc-BMNejdWA.js +1 -1
  84. package/public/assets/mdc-DTYItulj.js +1 -1
  85. package/public/assets/{min-DDWmdqiX.js → min-BcXYHhif.js} +1 -1
  86. package/public/assets/{mindmap-definition-QFDTVHPH-By9_uC_s.js → mindmap-definition-QFDTVHPH-TuJ7IAaH.js} +8 -8
  87. package/public/assets/nim-BIad80T-.js +1 -1
  88. package/public/assets/nim-CVrawwO9.js +1 -1
  89. package/public/assets/perl-C0TMdlhV.js +1 -1
  90. package/public/assets/perl-NvoQZIq0.js +1 -1
  91. package/public/assets/php-Dhbhpdrm.js +1 -1
  92. package/public/assets/php-R6g_5hLQ.js +1 -1
  93. package/public/assets/pieDiagram-DEJITSTG-lAM0NHTC.js +30 -0
  94. package/public/assets/pug-CGlum2m_.js +1 -1
  95. package/public/assets/pug-DKIMFp6K.js +1 -1
  96. package/public/assets/{quadrantDiagram-34T5L4WZ-BszNW2Gc.js → quadrantDiagram-34T5L4WZ-B6gfAy9l.js} +3 -3
  97. package/public/assets/razor-BDqjjVU7.js +1 -1
  98. package/public/assets/razor-Uh8Bk_45.js +1 -1
  99. package/public/assets/{requirementDiagram-MS252O5E-fdyCkRN5.js → requirementDiagram-MS252O5E-Bk0PCi5Z.js} +1 -1
  100. package/public/assets/rst-BrH8l1NY.js +1 -1
  101. package/public/assets/rst-CRjBmOyv.js +1 -1
  102. package/public/assets/ruby-Dw2BHqvy.js +1 -1
  103. package/public/assets/ruby-Wjq7vjNf.js +1 -1
  104. package/public/assets/{sankeyDiagram-XADWPNL6-tg2HOfl2.js → sankeyDiagram-XADWPNL6-B8VanxBi.js} +2 -2
  105. package/public/assets/{sequenceDiagram-FGHM5R23-GF78cQ5y.js → sequenceDiagram-FGHM5R23-BrOE0QFA.js} +5 -5
  106. package/public/assets/soy-8wufbnw4.js +1 -1
  107. package/public/assets/soy-Brmx7dQM.js +1 -1
  108. package/public/assets/{stateDiagram-FHFEXIEX-C709lyuE.js → stateDiagram-FHFEXIEX-D5UQqHX2.js} +1 -1
  109. package/public/assets/stateDiagram-v2-QKLJ7IA2-B0HrEW73.js +1 -0
  110. package/public/assets/svelte-C_ipcX3V.js +1 -1
  111. package/public/assets/svelte-Cy7k_4gC.js +1 -1
  112. package/public/assets/templ-DhtptRzy.js +1 -1
  113. package/public/assets/templ-P3uqSqPl.js +1 -1
  114. package/public/assets/{timeline-definition-GMOUNBTQ-D2y-83yP.js → timeline-definition-GMOUNBTQ-DP5C3gVY.js} +3 -3
  115. package/public/assets/ts-tags-DQrlYJgV.js +1 -1
  116. package/public/assets/ts-tags-zn1MmPIZ.js +1 -1
  117. package/public/assets/twig-DNn4PbVi.js +1 -1
  118. package/public/assets/twig-xg9kU7Mw.js +1 -1
  119. package/public/assets/vennDiagram-DHZGUBPP-ZQZ-YemX.js +34 -0
  120. package/public/assets/vue-D2xRrEX4.js +1 -1
  121. package/public/assets/vue-DN_0RTcg.js +1 -1
  122. package/public/assets/vue-vine-BoDAl6tE.js +1 -1
  123. package/public/assets/vue-vine-CQOfvN7w.js +1 -1
  124. package/public/assets/{wardley-RL74JXVD-NEg4mPyC.js → wardley-RL74JXVD-CFgev8RN.js} +1 -1
  125. package/public/assets/{wardleyDiagram-NUSXRM2D-BbXAj99t.js → wardleyDiagram-NUSXRM2D-1Lc9gN2h.js} +2 -2
  126. package/public/assets/{xychartDiagram-5P7HB3ND-C8Y3cC5f.js → xychartDiagram-5P7HB3ND-ChVtQTqG.js} +3 -3
  127. package/public/index.html +2 -2
  128. package/templates/front-canonical.tsx +4 -2
  129. package/public/assets/CodeEditor-DQqOn4xz-BnYIpEQ0.js +0 -31
  130. package/public/assets/FileTree-DjPzfDMq-jOFMfLV-.js +0 -21
  131. package/public/assets/MarkdownEditor-BbSy0bLV-_8qFqqna.js +0 -254
  132. package/public/assets/_baseUniq-DysFpiFw.js +0 -1
  133. package/public/assets/arc-D0sqkACA.js +0 -1
  134. package/public/assets/architectureDiagram-Q4EWVU46-DnyELkA2.js +0 -36
  135. package/public/assets/channel-Ci4DEwaO.js +0 -1
  136. package/public/assets/classDiagram-6PBFFD2Q-0PAZNzgC.js +0 -1
  137. package/public/assets/classDiagram-v2-HSJHXN6E-0PAZNzgC.js +0 -1
  138. package/public/assets/clone-_lPYOTLf.js +0 -1
  139. package/public/assets/diagram-5BDNPKRD-Ch4e4G6h.js +0 -10
  140. package/public/assets/diagram-G4DWMVQ6-a_PPD1uY.js +0 -24
  141. package/public/assets/diagram-MMDJMWI5-D9Lkv8IB.js +0 -43
  142. package/public/assets/diagram-TYMM5635-BlfaVidf.js +0 -24
  143. package/public/assets/gitGraphDiagram-UUTBAWPF-DpPqglKx.js +0 -106
  144. package/public/assets/highlighted-body-OFNGDK62-BcVhfY0-.js +0 -1
  145. package/public/assets/index-C9S_GD0T.js +0 -1330
  146. package/public/assets/index-CrFvzn5a.js +0 -9
  147. package/public/assets/index-Css1pwyY.css +0 -1
  148. package/public/assets/index-Vcq4gwWv.js +0 -1
  149. package/public/assets/infoDiagram-42DDH7IO-COfGVlDB.js +0 -2
  150. package/public/assets/pieDiagram-DEJITSTG-DfzJSvMR.js +0 -30
  151. package/public/assets/stateDiagram-v2-QKLJ7IA2-BbgcVrKo.js +0 -1
  152. package/public/assets/vennDiagram-DHZGUBPP-BOsWwU4c.js +0 -34
@@ -0,0 +1,1458 @@
1
+ #!/usr/bin/env node
2
+ import { builtinModules } from "node:module";
3
+ import { existsSync, lstatSync, readdirSync, readFileSync, statSync } from "node:fs";
4
+ import { readFile, realpath, stat } from "node:fs/promises";
5
+ import { dirname, extname, isAbsolute, posix, relative, resolve as resolvePath } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import react from "@vitejs/plugin-react";
8
+ import { ErrorCode } from "@hachej/boring-agent/shared";
9
+ import ts from "typescript";
10
+ import { createServer } from "vite";
11
+ const PLUGIN_FRONT_RUNTIME_BASE_PATH = "/api/v1/agent-plugins/runtime";
12
+ const HOST_SINGLETON_MODULES = [
13
+ "react",
14
+ "react-dom",
15
+ "react-dom/client",
16
+ "react/jsx-runtime",
17
+ "react/jsx-dev-runtime",
18
+ "@hachej/boring-workspace",
19
+ "@hachej/boring-workspace/plugin",
20
+ "@hachej/boring-workspace/events"
21
+ ];
22
+ const DEFAULT_MAX_TRANSFORM_CONCURRENCY = 8;
23
+ const RUNTIME_PREFIX = "[plugin-front-runtime]";
24
+ const NODE_BUILTIN_MODULES = new Set(builtinModules.flatMap((name) => [name, `node:${name}`]));
25
+ const PRIVATE_FILE_NAMES = /* @__PURE__ */ new Set([
26
+ "package.json",
27
+ "package-lock.json",
28
+ "pnpm-lock.yaml",
29
+ "yarn.lock",
30
+ "bun.lock",
31
+ "bun.lockb",
32
+ ".npmrc",
33
+ ".pnpmrc",
34
+ ".yarnrc",
35
+ ".yarnrc.yml"
36
+ ]);
37
+ const IMPORT_RESOLVE_EXTENSIONS = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".css", ".json", ".svg"];
38
+ const RUNTIME_ASSET_EXTENSIONS = /* @__PURE__ */ new Set([".avif", ".gif", ".ico", ".jpg", ".jpeg", ".png", ".svg", ".webp", ".woff", ".woff2"]);
39
+ const DIRECTORY_INDEX_CANDIDATES = [
40
+ "index.ts",
41
+ "index.tsx",
42
+ "index.js",
43
+ "index.jsx",
44
+ "index.mjs",
45
+ "index.cjs",
46
+ "index.css"
47
+ ];
48
+ const SAFE_SEGMENT_RE = /^[A-Za-z0-9_][A-Za-z0-9._:-]*$/;
49
+ const RUNTIME_SINGLETON_ID_PREFIX = "\0boring-runtime-singleton:";
50
+ const RUNTIME_SINGLETON_GLOBAL = "__BORING_RUNTIME_SINGLETONS__";
51
+ const WORKSPACE_ROOT_SINGLETON_EXPORTS = [
52
+ "bootstrap",
53
+ "PluginError",
54
+ "CatalogRegistry",
55
+ "useCommands",
56
+ "useActivePanels",
57
+ "useCatalogs",
58
+ "PluginErrorBoundary",
59
+ "PluginErrorProvider",
60
+ "usePluginErrors",
61
+ "filesystemPlugin",
62
+ "emitFilesystemAgentFileChange",
63
+ "useAutoOpenAgentFiles",
64
+ "onFilesystemChanged",
65
+ "useFilePane",
66
+ "WorkspaceFilesProvider",
67
+ "useApiBaseUrl",
68
+ "useHasWorkspaceFilesProvider",
69
+ "useWorkspaceRequestId",
70
+ "filesystemEvents",
71
+ "cn",
72
+ "PanelRegistry",
73
+ "CommandRegistry",
74
+ "SurfaceResolverRegistry",
75
+ "RegistryProvider",
76
+ "useRegistry",
77
+ "useCommandRegistry",
78
+ "useCatalogRegistry",
79
+ "useSurfaceResolverRegistry",
80
+ "WORKSPACE_OPEN_PATH_SURFACE_KIND",
81
+ "getFileIcon",
82
+ "DockviewShell",
83
+ "PanelChrome",
84
+ "useDockviewApi",
85
+ "IdeLayout",
86
+ "buildIdeLayout",
87
+ "ChatLayout",
88
+ "buildChatLayout",
89
+ "TopBar",
90
+ "ResponsiveDockviewShell",
91
+ "useEditorLifecycle",
92
+ "buildFullPagePanelHref",
93
+ "useFullPagePanelHref",
94
+ "usePanelRenderMode",
95
+ "useIsFullPagePanel",
96
+ "useViewportBreakpoint",
97
+ "useResponsiveSidebarCollapse",
98
+ "useArtifactPanels",
99
+ "useArtifactRouting",
100
+ "useKeyboardShortcuts",
101
+ "formatShortcut",
102
+ "CommandPalette",
103
+ "WorkspaceLoadingState",
104
+ "ArtifactSurfacePane",
105
+ "EmptyPane",
106
+ "CodeEditorPane",
107
+ "FileTreePane",
108
+ "FileTreeView",
109
+ "MarkdownEditorPane",
110
+ "definePanel",
111
+ "createShadcnTheme",
112
+ "events",
113
+ "useEvent",
114
+ "userMeta",
115
+ "agentMeta",
116
+ "emitAgentData",
117
+ "toast",
118
+ "Toaster",
119
+ "dismissToast",
120
+ "createBridge",
121
+ "createBridgeClient",
122
+ "postUiCommand",
123
+ "UI_COMMAND_EVENT",
124
+ "openFileSchema",
125
+ "openPanelSchema",
126
+ "closePanelSchema",
127
+ "notificationSchema",
128
+ "navigateToLineSchema",
129
+ "expandToFileSchema",
130
+ "MAX_PANELS",
131
+ "PanelErrorBoundary",
132
+ "CodeEditor",
133
+ "FileTree",
134
+ "MarkdownEditor",
135
+ "SessionList",
136
+ "SessionBrowser",
137
+ "SurfaceShell",
138
+ "WorkbenchLeftPane",
139
+ "WorkspaceProvider",
140
+ "ThemeProvider",
141
+ "useTheme",
142
+ "useWorkspaceBridge",
143
+ "useWorkspaceContext",
144
+ "useWorkspaceContextOptional",
145
+ "useWorkspaceChatPanel",
146
+ "useWorkspaceAttention",
147
+ "createWorkspaceStore",
148
+ "bindStore",
149
+ "useActiveFile",
150
+ "useActivePanel",
151
+ "useSidebarState",
152
+ "useSetSidebar",
153
+ "useOpenPanels",
154
+ "useDirtyFiles",
155
+ "useThemePreference",
156
+ "useHydrationComplete",
157
+ "useResetLayout"
158
+ ];
159
+ const WORKSPACE_PLUGIN_SINGLETON_EXPORTS = [
160
+ "captureFrontPlugin",
161
+ "createCapturingBoringFrontAPI",
162
+ "definePlugin",
163
+ "validateBoringPluginManifest",
164
+ "isSafePluginRelativePath",
165
+ "isValidBoringPluginId",
166
+ "WORKSPACE_OPEN_PATH_SURFACE_KIND"
167
+ ];
168
+ const WORKSPACE_EVENTS_SINGLETON_EXPORTS = [
169
+ "events",
170
+ "userMeta",
171
+ "agentMeta",
172
+ "remoteMeta",
173
+ "workspaceEvents",
174
+ "WORKSPACE_PLUGIN_ID",
175
+ "WORKSPACE_UI_COMMAND_EVENT",
176
+ "WORKSPACE_EDITOR_SAVE_START_EVENT",
177
+ "WORKSPACE_EDITOR_SAVE_END_EVENT",
178
+ "WORKSPACE_PANEL_UPDATE_EVENT",
179
+ "WORKSPACE_PANEL_CLOSE_EVENT",
180
+ "WORKSPACE_AGENT_DATA_EVENT",
181
+ "useEvent",
182
+ "emitAgentData"
183
+ ];
184
+ const RUNTIME_SINGLETON_EXPORTS = {
185
+ react: [
186
+ "Activity",
187
+ "Children",
188
+ "Component",
189
+ "Fragment",
190
+ "Profiler",
191
+ "PureComponent",
192
+ "StrictMode",
193
+ "Suspense",
194
+ "__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE",
195
+ "__COMPILER_RUNTIME",
196
+ "act",
197
+ "cache",
198
+ "cacheSignal",
199
+ "captureOwnerStack",
200
+ "cloneElement",
201
+ "createContext",
202
+ "createElement",
203
+ "createRef",
204
+ "forwardRef",
205
+ "isValidElement",
206
+ "lazy",
207
+ "memo",
208
+ "startTransition",
209
+ "unstable_useCacheRefresh",
210
+ "use",
211
+ "useActionState",
212
+ "useCallback",
213
+ "useContext",
214
+ "useDebugValue",
215
+ "useDeferredValue",
216
+ "useEffect",
217
+ "useEffectEvent",
218
+ "useId",
219
+ "useImperativeHandle",
220
+ "useInsertionEffect",
221
+ "useLayoutEffect",
222
+ "useMemo",
223
+ "useOptimistic",
224
+ "useReducer",
225
+ "useRef",
226
+ "useState",
227
+ "useSyncExternalStore",
228
+ "useTransition",
229
+ "version"
230
+ ],
231
+ "react-dom": [
232
+ "__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE",
233
+ "createPortal",
234
+ "flushSync",
235
+ "preconnect",
236
+ "prefetchDNS",
237
+ "preinit",
238
+ "preinitModule",
239
+ "preload",
240
+ "preloadModule",
241
+ "requestFormReset",
242
+ "unstable_batchedUpdates",
243
+ "useFormState",
244
+ "useFormStatus",
245
+ "version"
246
+ ],
247
+ "react-dom/client": ["createRoot", "hydrateRoot", "version"],
248
+ "react/jsx-runtime": ["Fragment", "jsx", "jsxs"],
249
+ "react/jsx-dev-runtime": ["Fragment", "jsxDEV"],
250
+ "@hachej/boring-workspace": WORKSPACE_ROOT_SINGLETON_EXPORTS,
251
+ "@hachej/boring-workspace/plugin": WORKSPACE_PLUGIN_SINGLETON_EXPORTS,
252
+ "@hachej/boring-workspace/events": WORKSPACE_EVENTS_SINGLETON_EXPORTS
253
+ };
254
+ class PluginFrontRuntimeError extends Error {
255
+ constructor(code, statusCode, stage, message, details) {
256
+ super(message);
257
+ this.code = code;
258
+ this.statusCode = statusCode;
259
+ this.stage = stage;
260
+ this.details = details;
261
+ }
262
+ code;
263
+ statusCode;
264
+ stage;
265
+ details;
266
+ }
267
+ function diagnostic(entry) {
268
+ return { prefix: RUNTIME_PREFIX, ...entry };
269
+ }
270
+ function ensureSafeId(kind, value) {
271
+ const trimmed = value.trim();
272
+ if (!trimmed) {
273
+ throw new PluginFrontRuntimeError(
274
+ ErrorCode.enum.PATH_NOT_FOUND,
275
+ 404,
276
+ "validate",
277
+ `${kind} id is required`,
278
+ { [kind === "workspace" ? "workspaceId" : "pluginId"]: value }
279
+ );
280
+ }
281
+ if (trimmed.includes("\0")) {
282
+ throw new PluginFrontRuntimeError(
283
+ ErrorCode.enum.PATH_NULL_BYTE,
284
+ 400,
285
+ "validate",
286
+ `${kind} id contains a null byte`,
287
+ { [kind === "workspace" ? "workspaceId" : "pluginId"]: value }
288
+ );
289
+ }
290
+ if (!SAFE_SEGMENT_RE.test(trimmed)) {
291
+ throw new PluginFrontRuntimeError(
292
+ ErrorCode.enum.PATH_ESCAPE,
293
+ 403,
294
+ "validate",
295
+ `invalid ${kind} id`,
296
+ { [kind === "workspace" ? "workspaceId" : "pluginId"]: value }
297
+ );
298
+ }
299
+ return trimmed;
300
+ }
301
+ function normalizeRequestSubpath(raw) {
302
+ const value = raw.trim().replaceAll("\\", "/");
303
+ if (!value) {
304
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PATH_NOT_FOUND, 404, "validate", "plugin runtime path is required");
305
+ }
306
+ if (value.includes("\0")) {
307
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PATH_NULL_BYTE, 400, "validate", "plugin runtime path contains a null byte", { path: raw });
308
+ }
309
+ if (value.startsWith("/") || /^[A-Za-z]:\//.test(value) || isAbsolute(value)) {
310
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PATH_ABSOLUTE, 400, "validate", "plugin runtime path must be relative", { path: raw });
311
+ }
312
+ const segments = value.split("/");
313
+ if (segments.some((segment) => segment === "" || segment === "." || segment === "..")) {
314
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PATH_ESCAPE, 403, "validate", "plugin runtime path contains dot segments", { path: raw });
315
+ }
316
+ for (const segment of segments) {
317
+ const lower = segment.toLowerCase();
318
+ if (segment.startsWith(".") || lower === "node_modules" || lower === ".ds_store" || PRIVATE_FILE_NAMES.has(lower) || lower.startsWith(".env")) {
319
+ throw new PluginFrontRuntimeError(
320
+ ErrorCode.enum.PLUGIN_RUNTIME_PRIVATE_FILE,
321
+ 403,
322
+ "validate",
323
+ "plugin runtime path targets a disallowed private file",
324
+ { path: raw }
325
+ );
326
+ }
327
+ }
328
+ return value;
329
+ }
330
+ function assertRuntimeFrontEntrySubpath(frontEntrySubpath) {
331
+ if (frontEntrySubpath.startsWith("front/")) return;
332
+ throw new PluginFrontRuntimeError(
333
+ ErrorCode.enum.PLUGIN_RUNTIME_PRIVATE_FILE,
334
+ 403,
335
+ "validate",
336
+ "native runtime plugin fronts must live under the front/ directory",
337
+ { frontEntrySubpath }
338
+ );
339
+ }
340
+ function normalizeSearch(search) {
341
+ if (!search) return "";
342
+ const raw = search.startsWith("?") ? search.slice(1) : search;
343
+ if (!raw) return "";
344
+ const params = new URLSearchParams(raw);
345
+ params.delete("v");
346
+ params.delete("t");
347
+ const stable = [...params.entries()].sort(([aKey, aValue], [bKey, bValue]) => {
348
+ if (aKey === bKey) return aValue.localeCompare(bValue);
349
+ return aKey.localeCompare(bKey);
350
+ });
351
+ if (stable.length === 0) return "";
352
+ const normalized = new URLSearchParams();
353
+ for (const [key, value] of stable) normalized.append(key, value);
354
+ const text = normalized.toString();
355
+ return text ? `?${text}` : "";
356
+ }
357
+ function normalizeBasePath(basePath) {
358
+ const trimmed = basePath.trim();
359
+ if (!trimmed.startsWith("/")) throw new Error(`plugin front runtime basePath must start with '/': ${basePath}`);
360
+ return trimmed.replace(/\/+$/, "") || "/";
361
+ }
362
+ function encodeRuntimeSubpath(subpath) {
363
+ return subpath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
364
+ }
365
+ function buildRuntimeUrl(basePath, workspaceId, pluginId, revision, subpath) {
366
+ return `${basePath}/${encodeURIComponent(workspaceId)}/${encodeURIComponent(pluginId)}/${revision}/${encodeRuntimeSubpath(subpath)}`;
367
+ }
368
+ function isWithin(root, candidate) {
369
+ const rel = relative(root, candidate);
370
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
371
+ }
372
+ function buildViteProxyUrl(basePath, targetPath) {
373
+ return `${basePath}/__vite/proxy/${encodeURIComponent(targetPath.slice(1))}`;
374
+ }
375
+ function buildViteSingletonUrl(basePath, source) {
376
+ return `${basePath}/__vite/singleton/${encodeURIComponent(source)}`;
377
+ }
378
+ function optimizedDependencySingletonSource(targetPath) {
379
+ const cleanPath = targetPath.split("?")[0];
380
+ const normalizedPath = cleanPath.replaceAll("\\", "/");
381
+ const workspaceSingletonByPath = [
382
+ ["/packages/workspace/dist/workspace.js", "@hachej/boring-workspace"],
383
+ ["/packages/workspace/src/index.ts", "@hachej/boring-workspace"],
384
+ ["/packages/workspace/dist/plugin.js", "@hachej/boring-workspace/plugin"],
385
+ ["/packages/workspace/src/plugin.ts", "@hachej/boring-workspace/plugin"],
386
+ ["/packages/workspace/dist/events.js", "@hachej/boring-workspace/events"],
387
+ ["/packages/workspace/src/front/events/index.ts", "@hachej/boring-workspace/events"]
388
+ ];
389
+ for (const [suffix, source] of workspaceSingletonByPath) {
390
+ if (normalizedPath.endsWith(suffix)) return source;
391
+ }
392
+ const fileName = normalizedPath.slice(normalizedPath.lastIndexOf("/") + 1);
393
+ const sourceByFileName = {
394
+ "react.js": "react",
395
+ "react-dom.js": "react-dom",
396
+ "react-dom_client.js": "react-dom/client",
397
+ "react_jsx-runtime.js": "react/jsx-runtime",
398
+ "react_jsx-dev-runtime.js": "react/jsx-dev-runtime"
399
+ };
400
+ return sourceByFileName[fileName];
401
+ }
402
+ function rewriteViteSupportSpecifier(specifier, basePath) {
403
+ if (specifier === "/@vite/client") return `${basePath}/__vite/client`;
404
+ if (specifier === "/@vite/env" || specifier === "@vite/env") return `${basePath}/__vite/env`;
405
+ if (specifier.startsWith("/@fs/") && specifier.includes("/vite/dist/client/env.mjs")) return `${basePath}/__vite/env`;
406
+ if (!/^\/(?:@id|node_modules|packages)\//.test(specifier)) return void 0;
407
+ const singletonSource = optimizedDependencySingletonSource(specifier);
408
+ return singletonSource ? buildViteSingletonUrl(basePath, singletonSource) : buildViteProxyUrl(basePath, specifier);
409
+ }
410
+ function rewriteViteSupportUrls(code, basePath) {
411
+ const sourceFile = ts.createSourceFile("runtime-plugin-output.js", code, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
412
+ const replacements = [];
413
+ const mintedPaths = [];
414
+ const queueReplacement = (literal) => {
415
+ const rewritten2 = rewriteViteSupportSpecifier(literal.text, basePath);
416
+ if (!rewritten2) return;
417
+ replacements.push({
418
+ start: literal.getStart(sourceFile) + 1,
419
+ end: literal.getEnd() - 1,
420
+ value: rewritten2
421
+ });
422
+ mintedPaths.push(rewritten2);
423
+ };
424
+ const visit = (node) => {
425
+ if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier)) {
426
+ queueReplacement(node.moduleSpecifier);
427
+ } else if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteralLike(node.moduleSpecifier)) {
428
+ queueReplacement(node.moduleSpecifier);
429
+ } else if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments.length === 1 && ts.isStringLiteralLike(node.arguments[0])) {
430
+ queueReplacement(node.arguments[0]);
431
+ }
432
+ ts.forEachChild(node, visit);
433
+ };
434
+ visit(sourceFile);
435
+ if (replacements.length === 0) return { code, mintedPaths: [] };
436
+ let rewritten = code;
437
+ for (const replacement of replacements.sort((a, b) => b.start - a.start)) {
438
+ rewritten = `${rewritten.slice(0, replacement.start)}${replacement.value}${rewritten.slice(replacement.end)}`;
439
+ }
440
+ return { code: rewritten, mintedPaths };
441
+ }
442
+ function isImplicitViteSupportPath(path, basePath) {
443
+ return path === `${basePath}/__vite/env`;
444
+ }
445
+ async function resolveRealLike(path) {
446
+ const suffix = [];
447
+ let current = path;
448
+ while (true) {
449
+ try {
450
+ const real = await realpath(current);
451
+ return resolvePath(real, ...suffix.reverse());
452
+ } catch (error) {
453
+ const code = error.code;
454
+ if (code && code !== "ENOENT") throw error;
455
+ const parent = dirname(current);
456
+ if (parent === current) return path;
457
+ suffix.push(current.slice(parent.length + 1));
458
+ current = parent;
459
+ }
460
+ }
461
+ }
462
+ async function canonicalAllowedRoots(record) {
463
+ const pluginRootReal = await resolveRealLike(record.rootDir);
464
+ const roots = [record.frontRootDir, record.sharedRootDir];
465
+ const resolved = [];
466
+ for (const root of roots) {
467
+ const realRoot = await resolveRealLike(root);
468
+ if (!isWithin(pluginRootReal, realRoot)) {
469
+ throw new PluginFrontRuntimeError(
470
+ ErrorCode.enum.PATH_SYMLINK_ESCAPE,
471
+ 403,
472
+ "validate",
473
+ "plugin runtime allowed root resolves outside the plugin root",
474
+ { root, resolvedPath: realRoot, pluginRoot: record.rootDir }
475
+ );
476
+ }
477
+ if (!resolved.includes(realRoot)) resolved.push(realRoot);
478
+ }
479
+ return resolved;
480
+ }
481
+ async function resolveFileWithinPlugin(record, subpath) {
482
+ const resolvedPath = resolvePath(record.rootDir, subpath);
483
+ if (!isWithin(record.rootDir, resolvedPath)) {
484
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PATH_ESCAPE, 403, "validate", "plugin runtime path escapes plugin root", {
485
+ path: subpath,
486
+ rootDir: record.rootDir
487
+ });
488
+ }
489
+ if (!isWithin(record.frontRootDir, resolvedPath) && !isWithin(record.sharedRootDir, resolvedPath)) {
490
+ throw new PluginFrontRuntimeError(
491
+ ErrorCode.enum.PLUGIN_RUNTIME_PRIVATE_FILE,
492
+ 403,
493
+ "validate",
494
+ "plugin runtime path is outside the allowed front/shared subtree",
495
+ {
496
+ path: subpath,
497
+ frontRootDir: record.frontRootDir,
498
+ sharedRootDir: record.sharedRootDir
499
+ }
500
+ );
501
+ }
502
+ const realLike = await resolveRealLike(resolvedPath);
503
+ const allowedRealRoots = await canonicalAllowedRoots(record);
504
+ if (!allowedRealRoots.some((root) => isWithin(root, realLike))) {
505
+ throw new PluginFrontRuntimeError(
506
+ ErrorCode.enum.PATH_SYMLINK_ESCAPE,
507
+ 403,
508
+ "validate",
509
+ "plugin runtime path resolves outside the allowed subtree",
510
+ { path: subpath, resolvedPath: realLike }
511
+ );
512
+ }
513
+ try {
514
+ const stats = await stat(resolvedPath);
515
+ if (!stats.isFile()) {
516
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PATH_NOT_FOUND, 404, "validate", "plugin runtime path is not a file", {
517
+ path: subpath,
518
+ resolvedPath
519
+ });
520
+ }
521
+ } catch (error) {
522
+ if (error instanceof PluginFrontRuntimeError) throw error;
523
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PATH_NOT_FOUND, 404, "validate", "plugin runtime file not found", {
524
+ path: subpath,
525
+ resolvedPath
526
+ });
527
+ }
528
+ return resolvedPath;
529
+ }
530
+ function snapshotRuntimeSourceFiles(pluginRoot) {
531
+ const snapshot = /* @__PURE__ */ new Map();
532
+ const visit = (dir, subpathPrefix) => {
533
+ if (!existsSync(dir)) return;
534
+ try {
535
+ if (lstatSync(dir).isSymbolicLink()) return;
536
+ } catch {
537
+ return;
538
+ }
539
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
540
+ const subpath = `${subpathPrefix}/${entry.name}`;
541
+ let normalized;
542
+ try {
543
+ normalized = normalizeRequestSubpath(subpath);
544
+ } catch {
545
+ continue;
546
+ }
547
+ const path = resolvePath(pluginRoot, normalized);
548
+ try {
549
+ if (lstatSync(path).isSymbolicLink()) continue;
550
+ } catch {
551
+ continue;
552
+ }
553
+ if (entry.isDirectory()) {
554
+ visit(path, normalized);
555
+ continue;
556
+ }
557
+ if (!entry.isFile()) continue;
558
+ try {
559
+ const stats = statSync(path);
560
+ if (!stats.isFile()) continue;
561
+ snapshot.set(normalized, readFileSync(path));
562
+ } catch {
563
+ }
564
+ }
565
+ };
566
+ visit(resolvePath(pluginRoot, "front"), "front");
567
+ visit(resolvePath(pluginRoot, "shared"), "shared");
568
+ return snapshot;
569
+ }
570
+ async function resolveImportSubpath(record, importerPath, source) {
571
+ const relativeBase = posix.dirname(importerPath);
572
+ const rawTarget = normalizeRequestSubpath(posix.normalize(posix.join(relativeBase, source)).replaceAll("\\", "/"));
573
+ const hasExtension = extname(rawTarget) !== "";
574
+ const candidates = /* @__PURE__ */ new Set();
575
+ candidates.add(rawTarget);
576
+ if (!hasExtension) {
577
+ for (const suffix of IMPORT_RESOLVE_EXTENSIONS) {
578
+ if (suffix) candidates.add(`${rawTarget}${suffix}`);
579
+ }
580
+ for (const indexFile of DIRECTORY_INDEX_CANDIDATES) {
581
+ candidates.add(`${rawTarget}/${indexFile}`);
582
+ }
583
+ }
584
+ for (const candidate of candidates) {
585
+ if (record.sourceSnapshot.has(candidate)) return candidate;
586
+ }
587
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PATH_NOT_FOUND, 404, "resolve", "plugin runtime import not found in tracked revision", {
588
+ importerPath,
589
+ source
590
+ });
591
+ }
592
+ function parseRevision(raw) {
593
+ const revision = typeof raw === "number" ? raw : Number(raw);
594
+ if (!Number.isInteger(revision) || revision < 1) {
595
+ throw new PluginFrontRuntimeError(
596
+ ErrorCode.enum.PLUGIN_RUNTIME_REVISION_MISMATCH,
597
+ 409,
598
+ "validate",
599
+ "plugin runtime revision must be a positive integer",
600
+ { revision: raw }
601
+ );
602
+ }
603
+ return revision;
604
+ }
605
+ function isRuntimePathImport(source, basePath) {
606
+ return source === basePath || source.startsWith(`${basePath}/`);
607
+ }
608
+ function isUnsafeAbsoluteImport(source, basePath) {
609
+ return source.startsWith("/@fs/") || source.startsWith("//") || /^[A-Za-z][A-Za-z0-9+.-]*:/.test(source) || source.startsWith("/") && !isRuntimePathImport(source, basePath) || isAbsolute(source);
610
+ }
611
+ function isBareImport(source) {
612
+ return !source.startsWith(".") && !source.startsWith("/") && !source.startsWith("file://");
613
+ }
614
+ function stripBlockComments(sourceText) {
615
+ return sourceText.replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, " "));
616
+ }
617
+ function scriptKindForPath(path) {
618
+ const extension = extname(path).toLowerCase();
619
+ if (extension === ".tsx") return ts.ScriptKind.TSX;
620
+ if (extension === ".ts" || extension === ".mts" || extension === ".cts") return ts.ScriptKind.TS;
621
+ if (extension === ".jsx") return ts.ScriptKind.JSX;
622
+ return ts.ScriptKind.JS;
623
+ }
624
+ function runtimeAssetContentType(path) {
625
+ switch (extname(path).toLowerCase()) {
626
+ case ".avif":
627
+ return "image/avif";
628
+ case ".gif":
629
+ return "image/gif";
630
+ case ".ico":
631
+ return "image/x-icon";
632
+ case ".jpg":
633
+ case ".jpeg":
634
+ return "image/jpeg";
635
+ case ".png":
636
+ return "image/png";
637
+ case ".svg":
638
+ return "image/svg+xml";
639
+ case ".webp":
640
+ return "image/webp";
641
+ case ".woff":
642
+ return "font/woff";
643
+ case ".woff2":
644
+ return "font/woff2";
645
+ default:
646
+ return "application/octet-stream";
647
+ }
648
+ }
649
+ function runtimeAssetModuleCode(path, bytes) {
650
+ const dataUrl = `data:${runtimeAssetContentType(path)};base64,${Buffer.from(bytes).toString("base64")}`;
651
+ return `export default ${JSON.stringify(dataUrl)};`;
652
+ }
653
+ function validateSourceImports(sourceText, importer, basePath) {
654
+ const reject = (source) => {
655
+ throw new PluginFrontRuntimeError(
656
+ ErrorCode.enum.PLUGIN_RUNTIME_UNSAFE_IMPORT,
657
+ 400,
658
+ "resolve",
659
+ "plugin runtime import bypasses the host runtime URL space",
660
+ { importer, source }
661
+ );
662
+ };
663
+ const isUnsafeSpecifier = (specifier) => specifier.startsWith("node:") || NODE_BUILTIN_MODULES.has(specifier) || isUnsafeAbsoluteImport(specifier, basePath) || isBareImport(specifier) && !HOST_SINGLETON_MODULES.includes(specifier);
664
+ const isImportMetaGlobCall = (expression) => {
665
+ if (!ts.isPropertyAccessExpression(expression)) return false;
666
+ if (expression.name.text !== "glob" && expression.name.text !== "globEager") return false;
667
+ return ts.isMetaProperty(expression.expression) && expression.expression.keywordToken === ts.SyntaxKind.ImportKeyword && expression.expression.name.text === "meta";
668
+ };
669
+ const extension = extname(importer).toLowerCase();
670
+ if (extension === ".css") {
671
+ const sanitizedCss = stripBlockComments(sourceText);
672
+ const cssImportPattern = /^\s*@import\s+(?:url\(\s*(?:["']([^"']+)["']|([^\s)"']+))\s*\)|["']([^"']+)["'])/gm;
673
+ let cssMatch;
674
+ while ((cssMatch = cssImportPattern.exec(sanitizedCss)) !== null) {
675
+ const specifier = cssMatch[1] ?? cssMatch[2] ?? cssMatch[3] ?? "";
676
+ if (isUnsafeSpecifier(specifier)) reject(specifier);
677
+ }
678
+ return;
679
+ }
680
+ const sourceFile = ts.createSourceFile(importer, sourceText, ts.ScriptTarget.Latest, true, scriptKindForPath(importer));
681
+ const visit = (node) => {
682
+ if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier)) {
683
+ const specifier = node.moduleSpecifier.text;
684
+ if (isUnsafeSpecifier(specifier)) reject(specifier);
685
+ }
686
+ if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteralLike(node.moduleSpecifier)) {
687
+ const specifier = node.moduleSpecifier.text;
688
+ if (isUnsafeSpecifier(specifier)) reject(specifier);
689
+ }
690
+ if (ts.isCallExpression(node) && isImportMetaGlobCall(node.expression)) {
691
+ reject("import.meta.glob");
692
+ }
693
+ if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments.length > 0) {
694
+ const argument = node.arguments[0];
695
+ if (ts.isStringLiteral(argument)) {
696
+ const specifier = argument.text;
697
+ if (isUnsafeSpecifier(specifier)) reject(specifier);
698
+ } else {
699
+ reject("computed-import");
700
+ }
701
+ }
702
+ ts.forEachChild(node, visit);
703
+ };
704
+ visit(sourceFile);
705
+ }
706
+ function packageRootFromRuntimeFile() {
707
+ return resolvePath(dirname(fileURLToPath(import.meta.url)), "..", "..");
708
+ }
709
+ function findWorkspaceRoot(from) {
710
+ let current = from;
711
+ while (true) {
712
+ if (existsSync(resolvePath(current, "pnpm-workspace.yaml"))) return current;
713
+ const parent = dirname(current);
714
+ if (parent === current) return from;
715
+ current = parent;
716
+ }
717
+ }
718
+ function virtualSingletonId(source) {
719
+ return `${RUNTIME_SINGLETON_ID_PREFIX}${source}`;
720
+ }
721
+ function sourceFromVirtualSingletonId(id) {
722
+ if (!id.startsWith(RUNTIME_SINGLETON_ID_PREFIX)) return void 0;
723
+ const source = id.slice(RUNTIME_SINGLETON_ID_PREFIX.length);
724
+ return HOST_SINGLETON_MODULES.includes(source) ? source : void 0;
725
+ }
726
+ function runtimeSingletonExportExpression(source, name) {
727
+ const key = JSON.stringify(name);
728
+ if (source === "react/jsx-dev-runtime") {
729
+ if (name === "Fragment") return `(singleton[${key}] ?? singleton.default?.[${key}] ?? singletons?.react?.Fragment)`;
730
+ if (name === "jsxDEV") {
731
+ return `(singleton[${key}] ?? singleton.default?.[${key}] ?? ((type, props, key) => singletons.react.createElement(type, key === undefined ? props : { ...props, key })))`;
732
+ }
733
+ }
734
+ if (source === "react/jsx-runtime") {
735
+ if (name === "Fragment") return `(singleton[${key}] ?? singleton.default?.[${key}] ?? singletons?.react?.Fragment)`;
736
+ if (name === "jsx" || name === "jsxs") {
737
+ return `(singleton[${key}] ?? singleton.default?.[${key}] ?? ((type, props, key) => singletons.react.createElement(type, key === undefined ? props : { ...props, key })))`;
738
+ }
739
+ }
740
+ return `singleton[${key}]`;
741
+ }
742
+ function runtimeSingletonModuleCode(source) {
743
+ const exports = RUNTIME_SINGLETON_EXPORTS[source];
744
+ if (!exports) return void 0;
745
+ const exportLines = exports.map((name) => `export const ${name} = normalized[${JSON.stringify(name)}];`);
746
+ const normalizedAssignments = exports.map((name) => ` ${JSON.stringify(name)}: ${runtimeSingletonExportExpression(source, name)},`);
747
+ return [
748
+ `const singletons = globalThis[${JSON.stringify(RUNTIME_SINGLETON_GLOBAL)}];`,
749
+ `const singleton = singletons && singletons[${JSON.stringify(source)}];`,
750
+ `if (!singleton) throw new Error(${JSON.stringify(`missing runtime singleton: ${source}`)});`,
751
+ "const normalized = {",
752
+ " ...singleton,",
753
+ ...normalizedAssignments,
754
+ "};",
755
+ "export default normalized;",
756
+ ...exportLines
757
+ ].join("\n");
758
+ }
759
+ function __testingRuntimeSingletonModuleCode(source) {
760
+ return runtimeSingletonModuleCode(source);
761
+ }
762
+ function createRuntimeSingletonResolve(repoRoot) {
763
+ const alias = [];
764
+ const localWorkspaceAliases = [
765
+ ["@hachej/boring-workspace/plugin", resolvePath(repoRoot, "packages", "workspace", "dist", "plugin.js"), resolvePath(repoRoot, "packages", "workspace", "src", "plugin.ts")],
766
+ ["@hachej/boring-workspace/events", resolvePath(repoRoot, "packages", "workspace", "dist", "events.js"), resolvePath(repoRoot, "packages", "workspace", "src", "front", "events", "index.ts")],
767
+ ["@hachej/boring-workspace", resolvePath(repoRoot, "packages", "workspace", "dist", "workspace.js"), resolvePath(repoRoot, "packages", "workspace", "src", "index.ts")]
768
+ ];
769
+ for (const [specifier, builtReplacement, sourceReplacement] of localWorkspaceAliases) {
770
+ const replacement = existsSync(builtReplacement) ? builtReplacement : sourceReplacement;
771
+ if (existsSync(replacement)) alias.push({ find: new RegExp(`^${specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`), replacement });
772
+ }
773
+ return {
774
+ alias,
775
+ dedupe: ["react", "react-dom"]
776
+ };
777
+ }
778
+ class TransformLimiter {
779
+ constructor(max) {
780
+ this.max = max;
781
+ }
782
+ max;
783
+ active = 0;
784
+ queue = [];
785
+ async run(fn) {
786
+ await this.acquire();
787
+ try {
788
+ return await fn();
789
+ } finally {
790
+ this.release();
791
+ }
792
+ }
793
+ async acquire() {
794
+ if (this.active < this.max) {
795
+ this.active += 1;
796
+ return;
797
+ }
798
+ await new Promise((resolve) => {
799
+ this.queue.push(() => {
800
+ this.active += 1;
801
+ resolve();
802
+ });
803
+ });
804
+ }
805
+ release() {
806
+ this.active = Math.max(0, this.active - 1);
807
+ const next = this.queue.shift();
808
+ if (next) next();
809
+ }
810
+ }
811
+ async function createPluginFrontRuntimeHost(options = {}) {
812
+ const basePath = normalizeBasePath(options.basePath ?? PLUGIN_FRONT_RUNTIME_BASE_PATH);
813
+ const emit = (entry) => {
814
+ options.onDiagnostic?.(diagnostic(entry));
815
+ };
816
+ const packageRoot = packageRootFromRuntimeFile();
817
+ const repoRoot = findWorkspaceRoot(packageRoot);
818
+ const singletonResolve = createRuntimeSingletonResolve(repoRoot);
819
+ const trackedWorkspaces = /* @__PURE__ */ new Map();
820
+ const trackedPluginRevisions = /* @__PURE__ */ new Map();
821
+ const transformCache = /* @__PURE__ */ new Map();
822
+ const mintedSupportPathsByCacheKey = /* @__PURE__ */ new Map();
823
+ const mintedSupportPathRefCounts = /* @__PURE__ */ new Map();
824
+ const limiter = new TransformLimiter(Math.max(1, options.maxTransformConcurrency ?? DEFAULT_MAX_TRANSFORM_CONCURRENCY));
825
+ let closed = false;
826
+ const vite = await createServer({
827
+ appType: "custom",
828
+ configFile: false,
829
+ logLevel: "silent",
830
+ root: repoRoot,
831
+ plugins: [
832
+ react(),
833
+ {
834
+ name: "boring-cli-plugin-front-runtime",
835
+ async resolveId(source, importer) {
836
+ if (isRuntimePathImport(source, basePath)) {
837
+ return stripCacheBustSearch(source);
838
+ }
839
+ const importerContext = importer ? parseRuntimeContext(importer, basePath) : null;
840
+ if (!importerContext) return null;
841
+ if (source.startsWith("node:") || NODE_BUILTIN_MODULES.has(source)) {
842
+ throw new PluginFrontRuntimeError(
843
+ ErrorCode.enum.PLUGIN_RUNTIME_UNSAFE_IMPORT,
844
+ 400,
845
+ "resolve",
846
+ "Node built-in modules are not available in runtime plugin fronts",
847
+ { source, importer }
848
+ );
849
+ }
850
+ if (isUnsafeAbsoluteImport(source, basePath)) {
851
+ throw new PluginFrontRuntimeError(
852
+ ErrorCode.enum.PLUGIN_RUNTIME_UNSAFE_IMPORT,
853
+ 400,
854
+ "resolve",
855
+ "plugin runtime import bypasses the host runtime URL space",
856
+ { source, importer }
857
+ );
858
+ }
859
+ if (isBareImport(source)) {
860
+ if (HOST_SINGLETON_MODULES.includes(source)) {
861
+ const code = runtimeSingletonModuleCode(source);
862
+ return code ? virtualSingletonId(source) : source;
863
+ }
864
+ throw new PluginFrontRuntimeError(
865
+ ErrorCode.enum.PLUGIN_RUNTIME_UNSAFE_IMPORT,
866
+ 400,
867
+ "resolve",
868
+ "runtime plugin fronts may only import allowlisted host singleton packages",
869
+ { source, importer }
870
+ );
871
+ }
872
+ if (!source.startsWith(".") && !source.startsWith("..")) return null;
873
+ const tracked = getTrackedPluginRevision(importerContext.workspaceId, importerContext.pluginId, importerContext.revision);
874
+ const importedSubpath = await resolveImportSubpath(tracked, importerContext.subpath, source);
875
+ const url = buildRuntimeUrl(basePath, tracked.workspaceId, tracked.pluginId, importerContext.revision, importedSubpath);
876
+ return RUNTIME_ASSET_EXTENSIONS.has(extname(importedSubpath).toLowerCase()) ? `${url}?module` : url;
877
+ },
878
+ async load(id) {
879
+ const singletonSource = sourceFromVirtualSingletonId(id);
880
+ if (singletonSource) return runtimeSingletonModuleCode(singletonSource) ?? null;
881
+ const context = parseRuntimeContext(id, basePath);
882
+ if (!context) return null;
883
+ const tracked = getTrackedPluginRevision(context.workspaceId, context.pluginId, context.revision);
884
+ const snapshotBytes = tracked.sourceSnapshot.get(context.subpath);
885
+ if (snapshotBytes === void 0) {
886
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PATH_NOT_FOUND, 404, "validate", "plugin runtime file was not captured in this revision", {
887
+ workspaceId: context.workspaceId,
888
+ pluginId: context.pluginId,
889
+ requestedRevision: context.revision,
890
+ path: context.subpath
891
+ });
892
+ }
893
+ const resolvedPath = resolvePath(tracked.rootDir, context.subpath);
894
+ if (RUNTIME_ASSET_EXTENSIONS.has(extname(context.subpath).toLowerCase())) {
895
+ return runtimeAssetModuleCode(context.subpath, snapshotBytes ?? await readFile(resolvedPath));
896
+ }
897
+ const sourceText = snapshotBytes ? Buffer.from(snapshotBytes).toString("utf8") : await readFile(resolvedPath, "utf8");
898
+ validateSourceImports(sourceText, context.subpath, basePath);
899
+ return sourceText;
900
+ }
901
+ }
902
+ ],
903
+ resolve: {
904
+ alias: singletonResolve.alias,
905
+ dedupe: [...singletonResolve.dedupe]
906
+ },
907
+ server: {
908
+ middlewareMode: true,
909
+ hmr: false
910
+ }
911
+ });
912
+ function getTrackedPlugin(workspaceId, pluginId) {
913
+ const plugins = trackedWorkspaces.get(workspaceId);
914
+ const tracked = plugins?.get(pluginId);
915
+ if (!tracked) {
916
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PATH_NOT_FOUND, 404, "validate", "plugin runtime record not found", {
917
+ workspaceId,
918
+ pluginId
919
+ });
920
+ }
921
+ return tracked;
922
+ }
923
+ function getTrackedPluginRevision(workspaceId, pluginId, revision) {
924
+ const tracked = trackedPluginRevisions.get(workspaceId)?.get(pluginId)?.get(revision);
925
+ if (!tracked) {
926
+ const current = trackedWorkspaces.get(workspaceId)?.get(pluginId);
927
+ if (!current) {
928
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PATH_NOT_FOUND, 404, "validate", "plugin runtime record not found", {
929
+ workspaceId,
930
+ pluginId,
931
+ requestedRevision: revision
932
+ });
933
+ }
934
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PLUGIN_RUNTIME_REVISION_MISMATCH, 409, "validate", "plugin runtime revision is no longer tracked", {
935
+ workspaceId,
936
+ pluginId,
937
+ requestedRevision: revision,
938
+ currentRevision: current.revision
939
+ });
940
+ }
941
+ return tracked;
942
+ }
943
+ function storeTrackedPlugin(record, entryUrl) {
944
+ const workspacePlugins = trackedWorkspaces.get(record.workspaceId) ?? /* @__PURE__ */ new Map();
945
+ trackedWorkspaces.set(record.workspaceId, workspacePlugins);
946
+ workspacePlugins.set(record.pluginId, record);
947
+ const workspaceRevisions = trackedPluginRevisions.get(record.workspaceId) ?? /* @__PURE__ */ new Map();
948
+ trackedPluginRevisions.set(record.workspaceId, workspaceRevisions);
949
+ const pluginRevisions = workspaceRevisions.get(record.pluginId) ?? /* @__PURE__ */ new Map();
950
+ workspaceRevisions.set(record.pluginId, pluginRevisions);
951
+ pluginRevisions.set(record.revision, record);
952
+ emit({
953
+ level: "info",
954
+ stage: "track",
955
+ outcome: "tracked",
956
+ msg: "tracked runtime plugin revision",
957
+ workspaceId: record.workspaceId,
958
+ pluginId: record.pluginId,
959
+ revision: record.revision,
960
+ requestedPath: record.frontEntrySubpath,
961
+ details: {
962
+ rootDir: record.rootDir,
963
+ frontRootDir: record.frontRootDir,
964
+ sharedRootDir: record.sharedRootDir,
965
+ entryUrl
966
+ }
967
+ });
968
+ }
969
+ function dropMintedSupportPaths(cacheKey) {
970
+ const minted = mintedSupportPathsByCacheKey.get(cacheKey);
971
+ if (!minted) return;
972
+ mintedSupportPathsByCacheKey.delete(cacheKey);
973
+ for (const path of minted) {
974
+ const next = (mintedSupportPathRefCounts.get(path) ?? 0) - 1;
975
+ if (next <= 0) mintedSupportPathRefCounts.delete(path);
976
+ else mintedSupportPathRefCounts.set(path, next);
977
+ }
978
+ }
979
+ function recordMintedSupportPaths(cacheKey, paths) {
980
+ dropMintedSupportPaths(cacheKey);
981
+ const unique = [...new Set(paths)];
982
+ mintedSupportPathsByCacheKey.set(cacheKey, unique);
983
+ for (const path of unique) {
984
+ mintedSupportPathRefCounts.set(path, (mintedSupportPathRefCounts.get(path) ?? 0) + 1);
985
+ }
986
+ }
987
+ function isMintedSupportPath(path) {
988
+ return isImplicitViteSupportPath(path, basePath) || (mintedSupportPathRefCounts.get(path) ?? 0) > 0;
989
+ }
990
+ async function invalidateMatching(predicate) {
991
+ for (const [cacheKey, entry] of [...transformCache.entries()]) {
992
+ const context = parseRuntimeContext(entry.runtimeId, basePath);
993
+ if (!context) continue;
994
+ const tracked = trackedWorkspaces.get(context.workspaceId)?.get(context.pluginId);
995
+ const validated = {
996
+ workspaceId: context.workspaceId,
997
+ pluginId: context.pluginId,
998
+ revision: context.revision,
999
+ requestedPath: context.subpath,
1000
+ resolvedPath: "",
1001
+ runtimeId: entry.runtimeId,
1002
+ cacheKey,
1003
+ tracked: tracked ?? {
1004
+ workspaceId: context.workspaceId,
1005
+ pluginId: context.pluginId,
1006
+ revision: context.revision,
1007
+ rootDir: "",
1008
+ frontEntrySubpath: context.subpath,
1009
+ frontRootDir: "",
1010
+ sharedRootDir: "",
1011
+ sourceSnapshot: /* @__PURE__ */ new Map()
1012
+ }
1013
+ };
1014
+ if (!predicate(validated)) continue;
1015
+ transformCache.delete(cacheKey);
1016
+ dropMintedSupportPaths(cacheKey);
1017
+ const moduleNode = vite.moduleGraph.getModuleById(entry.runtimeId);
1018
+ if (moduleNode) vite.moduleGraph.invalidateModule(moduleNode);
1019
+ emit({
1020
+ level: "info",
1021
+ stage: "cleanup",
1022
+ outcome: "disposed",
1023
+ msg: "disposed runtime transform cache entry",
1024
+ workspaceId: context.workspaceId,
1025
+ pluginId: context.pluginId,
1026
+ revision: context.revision,
1027
+ requestedPath: context.subpath
1028
+ });
1029
+ }
1030
+ }
1031
+ async function validateRequest(request) {
1032
+ if (closed) {
1033
+ throw new PluginFrontRuntimeError(ErrorCode.enum.INTERNAL_ERROR, 503, "serve", "plugin front runtime host is closed");
1034
+ }
1035
+ const workspaceId = ensureSafeId("workspace", request.workspaceId);
1036
+ const pluginId = ensureSafeId("plugin", request.pluginId);
1037
+ const revision = parseRevision(request.revision);
1038
+ const requestedPath = normalizeRequestSubpath(request.subpath);
1039
+ const tracked = getTrackedPluginRevision(workspaceId, pluginId, revision);
1040
+ if (!tracked.sourceSnapshot.has(requestedPath)) {
1041
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PATH_NOT_FOUND, 404, "validate", "plugin runtime file was not captured in this revision", {
1042
+ workspaceId,
1043
+ pluginId,
1044
+ requestedRevision: revision,
1045
+ path: requestedPath
1046
+ });
1047
+ }
1048
+ const resolvedPath = resolvePath(tracked.rootDir, requestedPath);
1049
+ const runtimeId = `${buildRuntimeUrl(basePath, workspaceId, pluginId, revision, requestedPath)}${normalizeSearch(request.search)}`;
1050
+ const cacheKey = `${workspaceId}:${pluginId}:${revision}:${requestedPath}${normalizeSearch(request.search)}`;
1051
+ return { workspaceId, pluginId, revision, requestedPath, resolvedPath, runtimeId, cacheKey, tracked };
1052
+ }
1053
+ function toApiError(error, request) {
1054
+ if (error instanceof PluginFrontRuntimeError) {
1055
+ return {
1056
+ statusCode: error.statusCode,
1057
+ body: {
1058
+ error: {
1059
+ code: error.code,
1060
+ message: error.message,
1061
+ ...error.details ? { details: error.details } : {}
1062
+ }
1063
+ }
1064
+ };
1065
+ }
1066
+ const message = error instanceof Error ? error.message : String(error);
1067
+ return {
1068
+ statusCode: 500,
1069
+ body: {
1070
+ error: {
1071
+ code: ErrorCode.enum.PLUGIN_RUNTIME_TRANSFORM_FAILED,
1072
+ message,
1073
+ ...request ? { details: { workspaceId: request.workspaceId, pluginId: request.pluginId, revision: request.revision, path: request.requestedPath } } : {}
1074
+ }
1075
+ }
1076
+ };
1077
+ }
1078
+ async function serve(request) {
1079
+ const startedAt = Date.now();
1080
+ let validated;
1081
+ try {
1082
+ const resolved = await validateRequest(request);
1083
+ validated = resolved;
1084
+ const cached = transformCache.get(resolved.cacheKey);
1085
+ if (cached) {
1086
+ emit({
1087
+ level: "info",
1088
+ stage: "cache",
1089
+ outcome: "cache-hit",
1090
+ msg: "served runtime module from transform cache",
1091
+ workspaceId: resolved.workspaceId,
1092
+ pluginId: resolved.pluginId,
1093
+ revision: resolved.revision,
1094
+ requestedPath: resolved.requestedPath,
1095
+ resolvedPath: resolved.resolvedPath
1096
+ });
1097
+ return await cached.promise;
1098
+ }
1099
+ emit({
1100
+ level: "info",
1101
+ stage: "cache",
1102
+ outcome: "cache-miss",
1103
+ msg: "runtime transform cache miss",
1104
+ workspaceId: resolved.workspaceId,
1105
+ pluginId: resolved.pluginId,
1106
+ revision: resolved.revision,
1107
+ requestedPath: resolved.requestedPath,
1108
+ resolvedPath: resolved.resolvedPath
1109
+ });
1110
+ const runtimeRequest = resolved;
1111
+ const promise = limiter.run(async () => {
1112
+ const transformStartedAt = Date.now();
1113
+ try {
1114
+ if (RUNTIME_ASSET_EXTENSIONS.has(extname(runtimeRequest.requestedPath).toLowerCase())) {
1115
+ const snapshotBytes = runtimeRequest.tracked.sourceSnapshot.get(runtimeRequest.requestedPath);
1116
+ if (snapshotBytes === void 0) {
1117
+ throw new PluginFrontRuntimeError(ErrorCode.enum.PATH_NOT_FOUND, 404, "validate", "plugin runtime file was not captured in this revision");
1118
+ }
1119
+ const bytes = snapshotBytes;
1120
+ const assetAsModule = new URLSearchParams((request.search ?? "").replace(/^\?/, "")).has("module");
1121
+ return {
1122
+ body: assetAsModule ? runtimeAssetModuleCode(runtimeRequest.requestedPath, bytes) : bytes,
1123
+ contentType: assetAsModule ? "application/javascript; charset=utf-8" : runtimeAssetContentType(runtimeRequest.requestedPath),
1124
+ cacheKey: runtimeRequest.cacheKey
1125
+ };
1126
+ }
1127
+ const transformed = await vite.transformRequest(runtimeRequest.runtimeId);
1128
+ if (!transformed?.code) {
1129
+ throw new PluginFrontRuntimeError(
1130
+ ErrorCode.enum.PLUGIN_RUNTIME_TRANSFORM_FAILED,
1131
+ 500,
1132
+ "transform",
1133
+ "plugin runtime transform returned no module code",
1134
+ { runtimeId: runtimeRequest.runtimeId }
1135
+ );
1136
+ }
1137
+ emit({
1138
+ level: "info",
1139
+ stage: "transform",
1140
+ outcome: "served",
1141
+ msg: "transformed runtime plugin module",
1142
+ workspaceId: runtimeRequest.workspaceId,
1143
+ pluginId: runtimeRequest.pluginId,
1144
+ revision: runtimeRequest.revision,
1145
+ requestedPath: runtimeRequest.requestedPath,
1146
+ resolvedPath: runtimeRequest.resolvedPath,
1147
+ durationMs: Date.now() - transformStartedAt
1148
+ });
1149
+ const rewritten = rewriteViteSupportUrls(transformed.code, basePath);
1150
+ recordMintedSupportPaths(runtimeRequest.cacheKey, rewritten.mintedPaths);
1151
+ return {
1152
+ body: rewritten.code,
1153
+ contentType: "application/javascript; charset=utf-8",
1154
+ cacheKey: runtimeRequest.cacheKey
1155
+ };
1156
+ } catch (error) {
1157
+ if (error instanceof PluginFrontRuntimeError) throw error;
1158
+ throw new PluginFrontRuntimeError(
1159
+ ErrorCode.enum.PLUGIN_RUNTIME_TRANSFORM_FAILED,
1160
+ 500,
1161
+ "transform",
1162
+ error instanceof Error ? error.message : String(error),
1163
+ { runtimeId: runtimeRequest.runtimeId }
1164
+ );
1165
+ }
1166
+ });
1167
+ transformCache.set(runtimeRequest.cacheKey, { runtimeId: runtimeRequest.runtimeId, promise });
1168
+ const response = await promise;
1169
+ emit({
1170
+ level: "info",
1171
+ stage: "serve",
1172
+ outcome: "served",
1173
+ msg: "served runtime plugin module",
1174
+ workspaceId: runtimeRequest.workspaceId,
1175
+ pluginId: runtimeRequest.pluginId,
1176
+ revision: runtimeRequest.revision,
1177
+ requestedPath: runtimeRequest.requestedPath,
1178
+ resolvedPath: runtimeRequest.resolvedPath,
1179
+ durationMs: Date.now() - startedAt
1180
+ });
1181
+ return response;
1182
+ } catch (error) {
1183
+ if (validated) {
1184
+ transformCache.delete(validated.cacheKey);
1185
+ dropMintedSupportPaths(validated.cacheKey);
1186
+ }
1187
+ const apiError = toApiError(error, validated);
1188
+ emit({
1189
+ level: apiError.statusCode >= 500 ? "error" : "warn",
1190
+ stage: error instanceof PluginFrontRuntimeError ? error.stage : "transform",
1191
+ outcome: "rejected",
1192
+ msg: apiError.body.error.message,
1193
+ workspaceId: validated?.workspaceId ?? request.workspaceId,
1194
+ pluginId: validated?.pluginId ?? request.pluginId,
1195
+ revision: validated?.revision ?? (typeof request.revision === "number" ? request.revision : Number(request.revision) || void 0),
1196
+ requestedPath: validated?.requestedPath ?? request.subpath,
1197
+ resolvedPath: validated?.resolvedPath,
1198
+ durationMs: Date.now() - startedAt,
1199
+ code: apiError.body.error.code,
1200
+ details: apiError.body.error.details
1201
+ });
1202
+ throw error;
1203
+ }
1204
+ }
1205
+ async function forwardToVite(request, reply) {
1206
+ reply.hijack();
1207
+ await new Promise((resolve, reject) => {
1208
+ vite.middlewares(request.raw, reply.raw, (error) => {
1209
+ if (error) reject(error);
1210
+ else resolve();
1211
+ });
1212
+ });
1213
+ if (!reply.raw.writableEnded) {
1214
+ reply.raw.statusCode = 404;
1215
+ reply.raw.end();
1216
+ }
1217
+ }
1218
+ async function registerRoutes(app) {
1219
+ app.get(`${basePath}/:workspaceId/:pluginId/:revision/*`, async (request, reply) => {
1220
+ const params = request.params;
1221
+ try {
1222
+ const response = await serve({
1223
+ workspaceId: params.workspaceId,
1224
+ pluginId: params.pluginId,
1225
+ revision: params.revision,
1226
+ subpath: params["*"],
1227
+ search: request.raw.url?.includes("?") ? request.raw.url.slice(request.raw.url.indexOf("?")) : ""
1228
+ });
1229
+ return reply.type(response.contentType).send(response.body);
1230
+ } catch (error) {
1231
+ const apiError = toApiError(error);
1232
+ return reply.code(apiError.statusCode).send(apiError.body);
1233
+ }
1234
+ });
1235
+ app.get(`${basePath}/__vite/client`, async (request, reply) => {
1236
+ const mintedPath = request.raw.url?.split("?")[0] ?? `${basePath}/__vite/client`;
1237
+ if (!isMintedSupportPath(mintedPath)) {
1238
+ const apiError = toApiError(new PluginFrontRuntimeError(
1239
+ ErrorCode.enum.PATH_NOT_FOUND,
1240
+ 404,
1241
+ "validate",
1242
+ "vite support path was not minted by a validated runtime module",
1243
+ { targetPath: mintedPath }
1244
+ ));
1245
+ return reply.code(apiError.statusCode).send(apiError.body);
1246
+ }
1247
+ const transformed = await vite.transformRequest("/@vite/client");
1248
+ if (transformed?.code) {
1249
+ const rewritten = rewriteViteSupportUrls(transformed.code, basePath);
1250
+ recordMintedSupportPaths("__vite:client", rewritten.mintedPaths);
1251
+ return reply.type("application/javascript; charset=utf-8").send(rewritten.code);
1252
+ }
1253
+ request.raw.url = "/@vite/client";
1254
+ await forwardToVite(request, reply);
1255
+ });
1256
+ app.get(`${basePath}/__vite/env`, async (request, reply) => {
1257
+ request.raw.url = "/@vite/env";
1258
+ await forwardToVite(request, reply);
1259
+ });
1260
+ app.get(`${basePath}/__vite/singleton/*`, async (request, reply) => {
1261
+ const { "*": encodedSource } = request.params;
1262
+ const mintedPath = request.raw.url?.split("?")[0] ?? `${basePath}/__vite/singleton/${encodedSource}`;
1263
+ if (!isMintedSupportPath(mintedPath)) {
1264
+ const apiError = toApiError(new PluginFrontRuntimeError(
1265
+ ErrorCode.enum.PATH_NOT_FOUND,
1266
+ 404,
1267
+ "validate",
1268
+ "vite singleton path was not minted by a validated runtime module",
1269
+ { targetPath: mintedPath }
1270
+ ));
1271
+ return reply.code(apiError.statusCode).send(apiError.body);
1272
+ }
1273
+ const source = decodeURIComponent(encodedSource);
1274
+ const singletonSource = HOST_SINGLETON_MODULES.includes(source) ? source : void 0;
1275
+ const code = singletonSource ? runtimeSingletonModuleCode(singletonSource) : void 0;
1276
+ if (!code) {
1277
+ const apiError = toApiError(new PluginFrontRuntimeError(
1278
+ ErrorCode.enum.PLUGIN_RUNTIME_UNSAFE_IMPORT,
1279
+ 400,
1280
+ "validate",
1281
+ "unsupported runtime singleton path",
1282
+ { source }
1283
+ ));
1284
+ return reply.code(apiError.statusCode).send(apiError.body);
1285
+ }
1286
+ return reply.type("application/javascript; charset=utf-8").send(code);
1287
+ });
1288
+ app.get(`${basePath}/__vite/proxy/*`, async (request, reply) => {
1289
+ const { "*": encodedTarget } = request.params;
1290
+ const mintedPath = request.raw.url?.split("?")[0] ?? `${basePath}/__vite/proxy/${encodedTarget}`;
1291
+ if (!isMintedSupportPath(mintedPath)) {
1292
+ const apiError = toApiError(new PluginFrontRuntimeError(
1293
+ ErrorCode.enum.PATH_NOT_FOUND,
1294
+ 404,
1295
+ "validate",
1296
+ "vite support path was not minted by a validated runtime module",
1297
+ { targetPath: mintedPath }
1298
+ ));
1299
+ return reply.code(apiError.statusCode).send(apiError.body);
1300
+ }
1301
+ const targetPath = `/${decodeURIComponent(encodedTarget)}`;
1302
+ if (!/^\/(?:@id|node_modules|packages)\//.test(targetPath)) {
1303
+ const apiError = toApiError(new PluginFrontRuntimeError(
1304
+ ErrorCode.enum.PLUGIN_RUNTIME_UNSAFE_IMPORT,
1305
+ 400,
1306
+ "validate",
1307
+ "unsupported Vite support path",
1308
+ { targetPath }
1309
+ ));
1310
+ return reply.code(apiError.statusCode).send(apiError.body);
1311
+ }
1312
+ const search = request.raw.url?.includes("?") ? request.raw.url.slice(request.raw.url.indexOf("?")) : "";
1313
+ const transformed = await vite.transformRequest(`${targetPath}${normalizeSearch(search)}`);
1314
+ if (transformed?.code) {
1315
+ const rewritten = rewriteViteSupportUrls(transformed.code, basePath);
1316
+ recordMintedSupportPaths(`support:${mintedPath}`, rewritten.mintedPaths);
1317
+ return reply.type("application/javascript; charset=utf-8").send(rewritten.code);
1318
+ }
1319
+ request.raw.url = targetPath;
1320
+ await forwardToVite(request, reply);
1321
+ });
1322
+ app.addHook("onClose", async () => {
1323
+ await close();
1324
+ });
1325
+ }
1326
+ async function invalidatePlugin(workspaceId, pluginId, keepRevision) {
1327
+ await invalidateMatching((entry) => entry.workspaceId === workspaceId && entry.pluginId === pluginId && (keepRevision === void 0 || entry.revision !== keepRevision));
1328
+ }
1329
+ async function disposeWorkspace(workspaceId) {
1330
+ trackedWorkspaces.delete(workspaceId);
1331
+ trackedPluginRevisions.delete(workspaceId);
1332
+ await invalidateMatching((entry) => entry.workspaceId === workspaceId);
1333
+ }
1334
+ async function close() {
1335
+ if (closed) return;
1336
+ closed = true;
1337
+ trackedWorkspaces.clear();
1338
+ trackedPluginRevisions.clear();
1339
+ transformCache.clear();
1340
+ mintedSupportPathsByCacheKey.clear();
1341
+ mintedSupportPathRefCounts.clear();
1342
+ emit({
1343
+ level: "info",
1344
+ stage: "cleanup",
1345
+ outcome: "closed",
1346
+ msg: "closed plugin front runtime host"
1347
+ });
1348
+ await vite.close();
1349
+ }
1350
+ function trackPlugin(args) {
1351
+ const workspaceId = ensureSafeId("workspace", args.workspaceId);
1352
+ const pluginId = ensureSafeId("plugin", args.plugin.id);
1353
+ const revision = parseRevision(args.revision);
1354
+ const frontEntrySubpath = normalizeRequestSubpath(args.frontEntrySubpath);
1355
+ assertRuntimeFrontEntrySubpath(frontEntrySubpath);
1356
+ const entryUrl = buildRuntimeUrl(basePath, workspaceId, pluginId, revision, frontEntrySubpath);
1357
+ const rootDir = resolvePath(args.plugin.rootDir);
1358
+ storeTrackedPlugin({
1359
+ workspaceId,
1360
+ pluginId,
1361
+ revision,
1362
+ rootDir,
1363
+ frontEntrySubpath,
1364
+ frontRootDir: resolvePath(
1365
+ rootDir,
1366
+ frontEntrySubpath === "front" || frontEntrySubpath.startsWith("front/") ? "front" : dirname(frontEntrySubpath)
1367
+ ),
1368
+ sharedRootDir: resolvePath(rootDir, "shared"),
1369
+ sourceSnapshot: snapshotRuntimeSourceFiles(rootDir)
1370
+ }, entryUrl);
1371
+ return entryUrl;
1372
+ }
1373
+ function createFrontTargetResolver(workspaceId) {
1374
+ return (plugin, context) => {
1375
+ if (!plugin.frontPath) return void 0;
1376
+ const frontEntrySubpath = normalizeRequestSubpath(context.frontEntrySubpath);
1377
+ if (!frontEntrySubpath.startsWith("front/")) return void 0;
1378
+ return {
1379
+ kind: "native",
1380
+ entryUrl: trackPlugin({
1381
+ workspaceId,
1382
+ plugin,
1383
+ revision: context.revision,
1384
+ frontEntrySubpath
1385
+ }),
1386
+ revision: context.revision,
1387
+ trust: "local-trusted-native"
1388
+ };
1389
+ };
1390
+ }
1391
+ function untrackPlugin(workspaceId, pluginId) {
1392
+ const tracked = trackedWorkspaces.get(workspaceId)?.get(pluginId);
1393
+ trackedWorkspaces.get(workspaceId)?.delete(pluginId);
1394
+ trackedPluginRevisions.get(workspaceId)?.delete(pluginId);
1395
+ emit({
1396
+ level: "info",
1397
+ stage: "cleanup",
1398
+ outcome: "disposed",
1399
+ msg: "untracked runtime plugin revision",
1400
+ workspaceId,
1401
+ pluginId,
1402
+ revision: tracked?.revision,
1403
+ requestedPath: tracked?.frontEntrySubpath,
1404
+ details: tracked ? {
1405
+ rootDir: tracked.rootDir,
1406
+ frontRootDir: tracked.frontRootDir,
1407
+ sharedRootDir: tracked.sharedRootDir
1408
+ } : void 0
1409
+ });
1410
+ void invalidatePlugin(workspaceId, pluginId);
1411
+ }
1412
+ return {
1413
+ basePath,
1414
+ singletonModules: HOST_SINGLETON_MODULES,
1415
+ createFrontTargetResolver,
1416
+ trackPlugin,
1417
+ untrackPlugin,
1418
+ invalidatePlugin,
1419
+ disposeWorkspace,
1420
+ serve,
1421
+ registerRoutes,
1422
+ close
1423
+ };
1424
+ }
1425
+ function stripCacheBustSearch(id) {
1426
+ const parsed = new URL(id, "http://runtime.local");
1427
+ const search = normalizeSearch(parsed.search);
1428
+ return `${parsed.pathname}${search}`;
1429
+ }
1430
+ function parseRuntimeContext(id, basePath) {
1431
+ let parsed;
1432
+ try {
1433
+ parsed = new URL(id, "http://runtime.local");
1434
+ } catch {
1435
+ return null;
1436
+ }
1437
+ if (!parsed.pathname.startsWith(`${basePath}/`)) return null;
1438
+ const raw = parsed.pathname.slice(basePath.length + 1);
1439
+ const parts = raw.split("/");
1440
+ if (parts.length < 4) return null;
1441
+ const [workspaceId, pluginId, revisionRaw, ...subpathParts] = parts;
1442
+ try {
1443
+ return {
1444
+ workspaceId: ensureSafeId("workspace", decodeURIComponent(workspaceId)),
1445
+ pluginId: ensureSafeId("plugin", decodeURIComponent(pluginId)),
1446
+ revision: parseRevision(decodeURIComponent(revisionRaw)),
1447
+ subpath: normalizeRequestSubpath(subpathParts.map((part) => decodeURIComponent(part)).join("/"))
1448
+ };
1449
+ } catch {
1450
+ return null;
1451
+ }
1452
+ }
1453
+ export {
1454
+ HOST_SINGLETON_MODULES,
1455
+ PLUGIN_FRONT_RUNTIME_BASE_PATH,
1456
+ __testingRuntimeSingletonModuleCode,
1457
+ createPluginFrontRuntimeHost
1458
+ };