@hachej/boring-ui-cli 0.1.24 → 0.1.26
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.
- package/dist/server/cli.js +465 -44
- package/dist/server/pluginDiscovery.js +36 -0
- package/dist/server/pluginFrontRuntime.js +1458 -0
- package/package.json +7 -7
- package/public/assets/DebugDrawer-kRj0QGoh.js +1 -0
- package/public/assets/_baseUniq-BaoahSEi.js +1 -0
- package/public/assets/angular-ts-BrjP3tb8.js +1 -1
- package/public/assets/angular-ts-BwZT4LLn.js +1 -1
- package/public/assets/apl-CORt7UWP.js +1 -1
- package/public/assets/apl-dKokRX4l.js +1 -1
- package/public/assets/arc-BGT9Yoor.js +1 -0
- package/public/assets/architectureDiagram-Q4EWVU46-8Fsx8Bw5.js +36 -0
- package/public/assets/astro-CbQHKStN.js +1 -1
- package/public/assets/astro-HNnZUWAn.js +1 -1
- package/public/assets/blade-BjGOyj-B.js +1 -1
- package/public/assets/blade-D4QpJJKB.js +1 -1
- package/public/assets/{blockDiagram-DXYQGD6D-DEkkj6Fh.js → blockDiagram-DXYQGD6D-CSD_8P6t.js} +5 -5
- package/public/assets/{c4Diagram-AHTNJAMY-CEZo72O7.js → c4Diagram-AHTNJAMY-BANFpiWc.js} +1 -1
- package/public/assets/channel-BdMajkgC.js +1 -0
- package/public/assets/{chunk-4BX2VUAB-kct9rMc4.js → chunk-4BX2VUAB-DwXbCZ7y.js} +1 -1
- package/public/assets/{chunk-4TB4RGXK-przR2bht.js → chunk-4TB4RGXK-Dk2hp0kY.js} +3 -3
- package/public/assets/{chunk-55IACEB6-BtFWJCbx.js → chunk-55IACEB6-CFeiGe8t.js} +1 -1
- package/public/assets/{chunk-EDXVE4YY-wnitHF3B.js → chunk-EDXVE4YY-B0SJv4ej.js} +1 -1
- package/public/assets/{chunk-FMBD7UC4-8PDhdiie.js → chunk-FMBD7UC4-BakGwea6.js} +1 -1
- package/public/assets/{chunk-OYMX7WX6-TDftW5K8.js → chunk-OYMX7WX6-Dq0T_fTM.js} +2 -2
- package/public/assets/{chunk-QZHKN3VN-B5vnQXaM.js → chunk-QZHKN3VN-B5OglTTC.js} +1 -1
- package/public/assets/{chunk-YZCP3GAM-CGg2izn8.js → chunk-YZCP3GAM-NDaB-kfw.js} +1 -1
- package/public/assets/classDiagram-6PBFFD2Q-B-lcy3NJ.js +1 -0
- package/public/assets/classDiagram-v2-HSJHXN6E-B-lcy3NJ.js +1 -0
- package/public/assets/clone-CQaScT6j.js +1 -0
- package/public/assets/cobol-nBiQ_Alo.js +1 -1
- package/public/assets/cobol-nwyudZeR.js +1 -1
- package/public/assets/{cose-bilkent-S5V4N54A-CjXKbjHV.js → cose-bilkent-S5V4N54A-C_XiHCxn.js} +1 -1
- package/public/assets/crystal-DNxU26gB.js +1 -1
- package/public/assets/crystal-tKQVLTB8.js +1 -1
- package/public/assets/{dagre-KV5264BT-D-wBcwT7.js → dagre-KV5264BT-DeKC0ZTS.js} +2 -2
- package/public/assets/diagram-5BDNPKRD-BI21qA6c.js +10 -0
- package/public/assets/diagram-G4DWMVQ6-DXlN6VCm.js +24 -0
- package/public/assets/diagram-MMDJMWI5-BCJkUUZU.js +43 -0
- package/public/assets/diagram-TYMM5635-C-KOEBZV.js +24 -0
- package/public/assets/edge-BkV0erSs.js +1 -1
- package/public/assets/edge-FbVlp4U3.js +1 -1
- package/public/assets/elixir-CDX3lj18.js +1 -1
- package/public/assets/elixir-CkH2-t6x.js +1 -1
- package/public/assets/{erDiagram-SMLLAGMA-CUWacc8b.js → erDiagram-SMLLAGMA-_vucZwTa.js} +2 -2
- package/public/assets/erb-B12qg9BL.js +1 -1
- package/public/assets/erb-BYCe7drp.js +1 -1
- package/public/assets/{flowDiagram-DWJPFMVM-DVIhUX7Z.js → flowDiagram-DWJPFMVM-CWye_WgN.js} +4 -4
- package/public/assets/{ganttDiagram-T4ZO3ILL-g3ncLabH.js → ganttDiagram-T4ZO3ILL-Cg6T_N_V.js} +3 -3
- package/public/assets/gitGraphDiagram-UUTBAWPF-CI6tYFw1.js +106 -0
- package/public/assets/glimmer-js-ByusRIyA.js +1 -1
- package/public/assets/glimmer-js-Rg0-pVw9.js +1 -1
- package/public/assets/glimmer-ts-BfAWNZQY.js +1 -1
- package/public/assets/glimmer-ts-U6CK756n.js +1 -1
- package/public/assets/{graph-CDl50skT.js → graph-Ci4pDE3A.js} +1 -1
- package/public/assets/hack-CaT9iCJl.js +1 -1
- package/public/assets/hack-i7_Ulhet.js +1 -1
- package/public/assets/haml-B8DHNrY2.js +1 -1
- package/public/assets/haml-D5jkg6IW.js +1 -1
- package/public/assets/handlebars-BL8al0AC.js +1 -1
- package/public/assets/handlebars-BpdQsYii.js +1 -1
- package/public/assets/highlighted-body-OFNGDK62-xGLlGy3l.js +1 -0
- package/public/assets/html-GMplVEZG.js +1 -1
- package/public/assets/html-derivative-BFtXZ54Q.js +1 -1
- package/public/assets/html-derivative-DlHx6ybY.js +1 -1
- package/public/assets/html-pp8916En.js +1 -1
- package/public/assets/index-BNz332Xu.js +1669 -0
- package/public/assets/index-D5qFagsx.css +1 -0
- package/public/assets/infoDiagram-42DDH7IO-CigcPMhk.js +2 -0
- package/public/assets/{ishikawaDiagram-UXIWVN3A-BbjR1GdS.js → ishikawaDiagram-UXIWVN3A-CDZTRajH.js} +4 -4
- package/public/assets/jinja-4LBKfQ-Z.js +1 -1
- package/public/assets/jinja-f2NsQr07.js +1 -1
- package/public/assets/{journeyDiagram-VCZTEJTY-DJHw1u4a.js → journeyDiagram-VCZTEJTY-C9MHFDJ6.js} +4 -4
- package/public/assets/just-Cw27pwNe.js +1 -1
- package/public/assets/just-VxiPbLrw.js +1 -1
- package/public/assets/{kanban-definition-6JOO6SKY-agbEFS20.js → kanban-definition-6JOO6SKY-Hpr02Gir.js} +7 -7
- package/public/assets/{layout-BtrYpvUW.js → layout-uDkQb6eL.js} +1 -1
- package/public/assets/{linear-C4IQ6Sya.js → linear-D9bVI_aq.js} +1 -1
- package/public/assets/liquid-C0sCDyMI.js +1 -1
- package/public/assets/liquid-DYVedYrR.js +1 -1
- package/public/assets/marko-CnJfTvn9.js +1 -1
- package/public/assets/marko-DjSrsDqO.js +1 -1
- package/public/assets/mdc-BMNejdWA.js +1 -1
- package/public/assets/mdc-DTYItulj.js +1 -1
- package/public/assets/{min-DDWmdqiX.js → min-T0CPelcS.js} +1 -1
- package/public/assets/{mindmap-definition-QFDTVHPH-By9_uC_s.js → mindmap-definition-QFDTVHPH-BxDJYpMb.js} +8 -8
- package/public/assets/nim-BIad80T-.js +1 -1
- package/public/assets/nim-CVrawwO9.js +1 -1
- package/public/assets/perl-C0TMdlhV.js +1 -1
- package/public/assets/perl-NvoQZIq0.js +1 -1
- package/public/assets/php-Dhbhpdrm.js +1 -1
- package/public/assets/php-R6g_5hLQ.js +1 -1
- package/public/assets/pieDiagram-DEJITSTG-C214NdS2.js +30 -0
- package/public/assets/pug-CGlum2m_.js +1 -1
- package/public/assets/pug-DKIMFp6K.js +1 -1
- package/public/assets/{quadrantDiagram-34T5L4WZ-BszNW2Gc.js → quadrantDiagram-34T5L4WZ-B4hldTdV.js} +3 -3
- package/public/assets/razor-BDqjjVU7.js +1 -1
- package/public/assets/razor-Uh8Bk_45.js +1 -1
- package/public/assets/{requirementDiagram-MS252O5E-fdyCkRN5.js → requirementDiagram-MS252O5E-Cv-IZSGz.js} +1 -1
- package/public/assets/rst-BrH8l1NY.js +1 -1
- package/public/assets/rst-CRjBmOyv.js +1 -1
- package/public/assets/ruby-Dw2BHqvy.js +1 -1
- package/public/assets/ruby-Wjq7vjNf.js +1 -1
- package/public/assets/{sankeyDiagram-XADWPNL6-tg2HOfl2.js → sankeyDiagram-XADWPNL6-C52Wx9t_.js} +2 -2
- package/public/assets/{sequenceDiagram-FGHM5R23-GF78cQ5y.js → sequenceDiagram-FGHM5R23-C9gRx_I2.js} +5 -5
- package/public/assets/soy-8wufbnw4.js +1 -1
- package/public/assets/soy-Brmx7dQM.js +1 -1
- package/public/assets/{stateDiagram-FHFEXIEX-C709lyuE.js → stateDiagram-FHFEXIEX-BVweWFAE.js} +1 -1
- package/public/assets/stateDiagram-v2-QKLJ7IA2-CftkpTOS.js +1 -0
- package/public/assets/svelte-C_ipcX3V.js +1 -1
- package/public/assets/svelte-Cy7k_4gC.js +1 -1
- package/public/assets/templ-DhtptRzy.js +1 -1
- package/public/assets/templ-P3uqSqPl.js +1 -1
- package/public/assets/{timeline-definition-GMOUNBTQ-D2y-83yP.js → timeline-definition-GMOUNBTQ-Boc8Q03t.js} +3 -3
- package/public/assets/ts-tags-DQrlYJgV.js +1 -1
- package/public/assets/ts-tags-zn1MmPIZ.js +1 -1
- package/public/assets/twig-DNn4PbVi.js +1 -1
- package/public/assets/twig-xg9kU7Mw.js +1 -1
- package/public/assets/vennDiagram-DHZGUBPP-BE8XjSYZ.js +34 -0
- package/public/assets/vue-D2xRrEX4.js +1 -1
- package/public/assets/vue-DN_0RTcg.js +1 -1
- package/public/assets/vue-vine-BoDAl6tE.js +1 -1
- package/public/assets/vue-vine-CQOfvN7w.js +1 -1
- package/public/assets/{wardley-RL74JXVD-NEg4mPyC.js → wardley-RL74JXVD-MQjS4K_T.js} +1 -1
- package/public/assets/{wardleyDiagram-NUSXRM2D-BbXAj99t.js → wardleyDiagram-NUSXRM2D-CKUw97nD.js} +2 -2
- package/public/assets/{xychartDiagram-5P7HB3ND-C8Y3cC5f.js → xychartDiagram-5P7HB3ND-OkP2T3uc.js} +3 -3
- package/public/index.html +2 -2
- package/templates/front-canonical.tsx +4 -2
- package/public/assets/CodeEditor-DQqOn4xz-BnYIpEQ0.js +0 -31
- package/public/assets/FileTree-DjPzfDMq-jOFMfLV-.js +0 -21
- package/public/assets/MarkdownEditor-BbSy0bLV-_8qFqqna.js +0 -254
- package/public/assets/_baseUniq-DysFpiFw.js +0 -1
- package/public/assets/arc-D0sqkACA.js +0 -1
- package/public/assets/architectureDiagram-Q4EWVU46-DnyELkA2.js +0 -36
- package/public/assets/channel-Ci4DEwaO.js +0 -1
- package/public/assets/classDiagram-6PBFFD2Q-0PAZNzgC.js +0 -1
- package/public/assets/classDiagram-v2-HSJHXN6E-0PAZNzgC.js +0 -1
- package/public/assets/clone-_lPYOTLf.js +0 -1
- package/public/assets/diagram-5BDNPKRD-Ch4e4G6h.js +0 -10
- package/public/assets/diagram-G4DWMVQ6-a_PPD1uY.js +0 -24
- package/public/assets/diagram-MMDJMWI5-D9Lkv8IB.js +0 -43
- package/public/assets/diagram-TYMM5635-BlfaVidf.js +0 -24
- package/public/assets/gitGraphDiagram-UUTBAWPF-DpPqglKx.js +0 -106
- package/public/assets/highlighted-body-OFNGDK62-BcVhfY0-.js +0 -1
- package/public/assets/index-C9S_GD0T.js +0 -1330
- package/public/assets/index-CrFvzn5a.js +0 -9
- package/public/assets/index-Css1pwyY.css +0 -1
- package/public/assets/index-Vcq4gwWv.js +0 -1
- package/public/assets/infoDiagram-42DDH7IO-COfGVlDB.js +0 -2
- package/public/assets/pieDiagram-DEJITSTG-DfzJSvMR.js +0 -30
- package/public/assets/stateDiagram-v2-QKLJ7IA2-BbgcVrKo.js +0 -1
- 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
|
+
};
|