@locode2/harness 0.1.0-preview.1

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 (162) hide show
  1. package/README.md +27 -0
  2. package/THIRD_PARTY_LICENSES.txt +32769 -0
  3. package/THIRD_PARTY_NOTICES.md +13 -0
  4. package/apps/host/dist/document-parser-worker.js +215 -0
  5. package/apps/host/dist/frontend-runtime-worker.js +1755 -0
  6. package/apps/host/dist/index.js +23380 -0
  7. package/apps/web/dist/assets/LocalMonacoEditor-CXkKwxiP.css +1 -0
  8. package/apps/web/dist/assets/LocalMonacoEditor-WKAHQmzG.js +940 -0
  9. package/apps/web/dist/assets/LogsView-BFFFSFB-.js +1 -0
  10. package/apps/web/dist/assets/PluginsView-DM_ZnENS.js +1 -0
  11. package/apps/web/dist/assets/SettingsView-Cb5Q4ile.js +1 -0
  12. package/apps/web/dist/assets/abap-DLDM7-KI.js +1 -0
  13. package/apps/web/dist/assets/apex-DNDY2TF8.js +1 -0
  14. package/apps/web/dist/assets/azcli-Y6nb8tq_.js +1 -0
  15. package/apps/web/dist/assets/bat-BwHxbl9M.js +1 -0
  16. package/apps/web/dist/assets/bicep-CFznDFnq.js +2 -0
  17. package/apps/web/dist/assets/cameligo-Bf6VGUru.js +1 -0
  18. package/apps/web/dist/assets/clojure-Dnu-v4kV.js +1 -0
  19. package/apps/web/dist/assets/codicon-Brq4_Ui5.ttf +0 -0
  20. package/apps/web/dist/assets/coffee-Bd8akH9Z.js +1 -0
  21. package/apps/web/dist/assets/cpp-BbWJElDN.js +1 -0
  22. package/apps/web/dist/assets/csharp-Co3qMtFm.js +1 -0
  23. package/apps/web/dist/assets/csp-D-4FJmMZ.js +1 -0
  24. package/apps/web/dist/assets/css-DdJfP1eB.js +3 -0
  25. package/apps/web/dist/assets/css.worker-11mhRRLC.js +97 -0
  26. package/apps/web/dist/assets/cssMode-L5f5LAEn.js +1 -0
  27. package/apps/web/dist/assets/cypher-cTPe9QuQ.js +1 -0
  28. package/apps/web/dist/assets/dart-BOtBlQCF.js +1 -0
  29. package/apps/web/dist/assets/dockerfile-BG73LgW2.js +1 -0
  30. package/apps/web/dist/assets/ecl-BEgZUVRK.js +1 -0
  31. package/apps/web/dist/assets/editor.worker-DysRFSaV.js +30 -0
  32. package/apps/web/dist/assets/elixir-BkW5O-1t.js +1 -0
  33. package/apps/web/dist/assets/flow9-BeJ5waoc.js +1 -0
  34. package/apps/web/dist/assets/freemarker2-UOwftS75.js +3 -0
  35. package/apps/web/dist/assets/fsharp-PahG7c26.js +1 -0
  36. package/apps/web/dist/assets/go-acbASCJo.js +1 -0
  37. package/apps/web/dist/assets/graphql-BxJiqAUM.js +1 -0
  38. package/apps/web/dist/assets/handlebars-C_E41ffr.js +1 -0
  39. package/apps/web/dist/assets/hcl-DtV1sZF8.js +1 -0
  40. package/apps/web/dist/assets/html-HsrixH2x.js +1 -0
  41. package/apps/web/dist/assets/html.worker-C8Tw8ncf.js +474 -0
  42. package/apps/web/dist/assets/htmlMode-DipGmEOi.js +1 -0
  43. package/apps/web/dist/assets/index-COUF63cT.js +213 -0
  44. package/apps/web/dist/assets/index-SNSmF2-c.css +1 -0
  45. package/apps/web/dist/assets/ini-Kd9XrMLS.js +1 -0
  46. package/apps/web/dist/assets/java-CXBNlu9o.js +1 -0
  47. package/apps/web/dist/assets/javascript-D36WEqF9.js +1 -0
  48. package/apps/web/dist/assets/json.worker-BzkYZgJ7.js +62 -0
  49. package/apps/web/dist/assets/jsonMode-D9BrIats.js +7 -0
  50. package/apps/web/dist/assets/julia-cl7-CwDS.js +1 -0
  51. package/apps/web/dist/assets/kotlin-s7OhZKlX.js +1 -0
  52. package/apps/web/dist/assets/less-9HpZscsL.js +2 -0
  53. package/apps/web/dist/assets/lexon-OrD6JF1K.js +1 -0
  54. package/apps/web/dist/assets/liquid-DES89Tjc.js +1 -0
  55. package/apps/web/dist/assets/lock-keyhole-jo-FJcGq.js +1 -0
  56. package/apps/web/dist/assets/lspLanguageFeatures-YKkgcn4q.js +4 -0
  57. package/apps/web/dist/assets/lua-Cyyb5UIc.js +1 -0
  58. package/apps/web/dist/assets/m3-B8OfTtLu.js +1 -0
  59. package/apps/web/dist/assets/markdown-BFxVWTOG.js +1 -0
  60. package/apps/web/dist/assets/mdx-D1Bfax8e.js +1 -0
  61. package/apps/web/dist/assets/mips-CiqrrVzr.js +1 -0
  62. package/apps/web/dist/assets/msdax-DmeGPVcC.js +1 -0
  63. package/apps/web/dist/assets/mysql-C_tMU-Nz.js +1 -0
  64. package/apps/web/dist/assets/objective-c-BDtDVThU.js +1 -0
  65. package/apps/web/dist/assets/pascal-vHIfCaH5.js +1 -0
  66. package/apps/web/dist/assets/pascaligo-DtZ0uQbO.js +1 -0
  67. package/apps/web/dist/assets/perl-Ub6l9XKa.js +1 -0
  68. package/apps/web/dist/assets/pgsql-BlNEE0v7.js +1 -0
  69. package/apps/web/dist/assets/php-BBUBE1dy.js +1 -0
  70. package/apps/web/dist/assets/pla-DSh2-awV.js +1 -0
  71. package/apps/web/dist/assets/postiats-CocnycG-.js +1 -0
  72. package/apps/web/dist/assets/powerquery-tScXyioY.js +1 -0
  73. package/apps/web/dist/assets/powershell-COWaemsV.js +1 -0
  74. package/apps/web/dist/assets/protobuf-Brw8urJB.js +2 -0
  75. package/apps/web/dist/assets/pug-8SOpv6rk.js +1 -0
  76. package/apps/web/dist/assets/python-DKhPhY0N.js +1 -0
  77. package/apps/web/dist/assets/qsharp-Bw9ernYp.js +1 -0
  78. package/apps/web/dist/assets/r-j7ic8hl3.js +1 -0
  79. package/apps/web/dist/assets/razor-Cha8qrI6.js +1 -0
  80. package/apps/web/dist/assets/redis-Bu5POkcn.js +1 -0
  81. package/apps/web/dist/assets/redshift-Bs9aos_-.js +1 -0
  82. package/apps/web/dist/assets/restructuredtext-CqXO7rUv.js +1 -0
  83. package/apps/web/dist/assets/ruby-zBfavPgS.js +1 -0
  84. package/apps/web/dist/assets/rust-BzKRNQWT.js +1 -0
  85. package/apps/web/dist/assets/sb-BBc9UKZt.js +1 -0
  86. package/apps/web/dist/assets/scala-D9hQfWCl.js +1 -0
  87. package/apps/web/dist/assets/scheme-BPhDTwHR.js +1 -0
  88. package/apps/web/dist/assets/scss-CBJaRo0y.js +3 -0
  89. package/apps/web/dist/assets/shell-DiJ1NA_G.js +1 -0
  90. package/apps/web/dist/assets/solidity-Db0IVjzk.js +1 -0
  91. package/apps/web/dist/assets/sophia-CnS9iZB_.js +1 -0
  92. package/apps/web/dist/assets/sparql-CJmd_6j2.js +1 -0
  93. package/apps/web/dist/assets/sql-ClhHkBeG.js +1 -0
  94. package/apps/web/dist/assets/st-CHwy0fLd.js +1 -0
  95. package/apps/web/dist/assets/swift-CnmFD0ga.js +1 -0
  96. package/apps/web/dist/assets/systemverilog-Bs9z6M-B.js +1 -0
  97. package/apps/web/dist/assets/tcl-Dm6ycUr_.js +1 -0
  98. package/apps/web/dist/assets/ts.worker-Bupg6llt.js +67722 -0
  99. package/apps/web/dist/assets/tsMode-CEhssXv8.js +11 -0
  100. package/apps/web/dist/assets/twig-Csy3S7wG.js +1 -0
  101. package/apps/web/dist/assets/typescript-DWqmw58R.js +1 -0
  102. package/apps/web/dist/assets/typespec-Btyra-wh.js +1 -0
  103. package/apps/web/dist/assets/vb-Db0cS2oM.js +1 -0
  104. package/apps/web/dist/assets/wgsl-DumH7NcR.js +298 -0
  105. package/apps/web/dist/assets/workers-BmZvNFRq.js +1 -0
  106. package/apps/web/dist/assets/xml-WQTAFV9b.js +1 -0
  107. package/apps/web/dist/assets/yaml-Ke2Lf79d.js +1 -0
  108. package/apps/web/dist/index.html +15 -0
  109. package/bin/launch.mjs +71 -0
  110. package/bin/locode-mini.mjs +12 -0
  111. package/bin/options.mjs +49 -0
  112. package/bin/restore-assets.mjs +40 -0
  113. package/bundled-plugins/fast-frontend/.gitignore +2 -0
  114. package/bundled-plugins/fast-frontend/README.md +29 -0
  115. package/bundled-plugins/fast-frontend/harness.plugin.json +123 -0
  116. package/bundled-plugins/fast-frontend/runtimes/antd-admin/PUBLIC_CONTRACT.md +139 -0
  117. package/bundled-plugins/fast-frontend/runtimes/antd-admin/contracts/app.schema.json +24 -0
  118. package/bundled-plugins/fast-frontend/runtimes/antd-admin/contracts/route.schema.json +52 -0
  119. package/bundled-plugins/fast-frontend/runtimes/antd-admin/examples/app.json +9 -0
  120. package/bundled-plugins/fast-frontend/runtimes/antd-admin/examples/route.json +8 -0
  121. package/bundled-plugins/fast-frontend/runtimes/antd-admin/index.html +17 -0
  122. package/bundled-plugins/fast-frontend/runtimes/antd-admin/package.json +31 -0
  123. package/bundled-plugins/fast-frontend/runtimes/antd-admin/scripts/smoke-build.mjs +40 -0
  124. package/bundled-plugins/fast-frontend/runtimes/antd-admin/src/inspector/index.ts +612 -0
  125. package/bundled-plugins/fast-frontend/runtimes/antd-admin/src/main.tsx +17 -0
  126. package/bundled-plugins/fast-frontend/runtimes/antd-admin/src/sdk/index.ts +1028 -0
  127. package/bundled-plugins/fast-frontend/runtimes/antd-admin/src/shell/RuntimeApp.tsx +380 -0
  128. package/bundled-plugins/fast-frontend/runtimes/antd-admin/src/shell/runtime.css +246 -0
  129. package/bundled-plugins/fast-frontend/runtimes/antd-admin/tsconfig.json +20 -0
  130. package/bundled-plugins/fast-frontend/runtimes/antd-admin/types/runtime.consumer-smoke.ts +70 -0
  131. package/bundled-plugins/fast-frontend/runtimes/antd-admin/types/runtime.d.ts +227 -0
  132. package/bundled-plugins/fast-frontend/templates/antd-admin/.gitignore +9 -0
  133. package/bundled-plugins/fast-frontend/templates/antd-admin/.harness/template.json +10 -0
  134. package/bundled-plugins/fast-frontend/templates/antd-admin/index.html +13 -0
  135. package/bundled-plugins/fast-frontend/templates/antd-admin/package.json +34 -0
  136. package/bundled-plugins/fast-frontend/templates/antd-admin/pnpm-lock.yaml +1451 -0
  137. package/bundled-plugins/fast-frontend/templates/antd-admin/pnpm-workspace.yaml +4 -0
  138. package/bundled-plugins/fast-frontend/templates/antd-admin/public/generated/.gitkeep +1 -0
  139. package/bundled-plugins/fast-frontend/templates/antd-admin/scripts/validate-routes.mjs +153 -0
  140. package/bundled-plugins/fast-frontend/templates/antd-admin/src/app/App.tsx +27 -0
  141. package/bundled-plugins/fast-frontend/templates/antd-admin/src/app/NotFoundPage.tsx +15 -0
  142. package/bundled-plugins/fast-frontend/templates/antd-admin/src/features/.gitkeep +1 -0
  143. package/bundled-plugins/fast-frontend/templates/antd-admin/src/layouts/MainLayout.tsx +77 -0
  144. package/bundled-plugins/fast-frontend/templates/antd-admin/src/main.tsx +14 -0
  145. package/bundled-plugins/fast-frontend/templates/antd-admin/src/mocks/dashboard.ts +48 -0
  146. package/bundled-plugins/fast-frontend/templates/antd-admin/src/pages/dashboard/page.tsx +151 -0
  147. package/bundled-plugins/fast-frontend/templates/antd-admin/src/pages/dashboard/route.ts +9 -0
  148. package/bundled-plugins/fast-frontend/templates/antd-admin/src/routes/definition.ts +21 -0
  149. package/bundled-plugins/fast-frontend/templates/antd-admin/src/routes/menu.tsx +44 -0
  150. package/bundled-plugins/fast-frontend/templates/antd-admin/src/routes/registry.ts +48 -0
  151. package/bundled-plugins/fast-frontend/templates/antd-admin/src/routes/router.tsx +34 -0
  152. package/bundled-plugins/fast-frontend/templates/antd-admin/src/shared/components/business/.gitkeep +1 -0
  153. package/bundled-plugins/fast-frontend/templates/antd-admin/src/shared/components/business/BusinessProviders.tsx +9 -0
  154. package/bundled-plugins/fast-frontend/templates/antd-admin/src/styles/global.css +180 -0
  155. package/bundled-plugins/fast-frontend/templates/antd-admin/src/vite-env.d.ts +1 -0
  156. package/bundled-plugins/fast-frontend/templates/antd-admin/tsconfig.app.json +24 -0
  157. package/bundled-plugins/fast-frontend/templates/antd-admin/tsconfig.json +7 -0
  158. package/bundled-plugins/fast-frontend/templates/antd-admin/tsconfig.node.json +17 -0
  159. package/bundled-plugins/fast-frontend/templates/antd-admin/vite.config.ts +39 -0
  160. package/npm-shrinkwrap.json +9737 -0
  161. package/package.json +80 -0
  162. package/release-integrity.json +168 -0
@@ -0,0 +1,1755 @@
1
+ // apps/host/src/runtime-diagnostics.ts
2
+ import { existsSync as existsSync3, realpathSync as realpathSync3 } from "node:fs";
3
+ import { dirname as dirname4, isAbsolute as isAbsolute3, join as join5, resolve as resolve3 } from "node:path";
4
+ import ts2 from "typescript";
5
+
6
+ // apps/host/src/runtime-business-source.ts
7
+ import { lstatSync, statSync } from "node:fs";
8
+ import { lstat, readdir } from "node:fs/promises";
9
+ import { extname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
10
+ function filesystemErrorCode(error) {
11
+ return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : void 0;
12
+ }
13
+ function normalizedRelative(value) {
14
+ const normalized = value.replaceAll("\\", "/").replace(/^\.\//u, "").replace(/\/+$/u, "");
15
+ return normalized === "." ? "" : normalized;
16
+ }
17
+ function containsPath(root, candidate) {
18
+ const nested = relative(root, candidate);
19
+ return nested === "" || !nested.startsWith(`..${sep}`) && nested !== ".." && !isAbsolute(nested);
20
+ }
21
+ function sameFilesystemPath(left, right) {
22
+ return filesystemPathKey(left) === filesystemPathKey(right);
23
+ }
24
+ function filesystemPathKey(value) {
25
+ const normalized = resolve(value).replaceAll("\\", "/").replace(/\/+$/u, "");
26
+ return process.platform === "win32" ? normalized.toLocaleLowerCase("en-US") : normalized;
27
+ }
28
+ function relativeProjectPath(projectRoot, path) {
29
+ if (!containsPath(projectRoot, path)) return void 0;
30
+ return normalizedRelative(relative(projectRoot, path));
31
+ }
32
+ function allowedProjectPath(args, candidate) {
33
+ const relativePath = relativeProjectPath(args.projectRoot, candidate);
34
+ if (relativePath === void 0) return false;
35
+ const key = process.platform === "win32" ? relativePath.toLocaleLowerCase("en-US") : relativePath;
36
+ return [...args.readablePaths, ...args.editablePaths].some((pattern) => {
37
+ const root = normalizedRelative(pattern.replace(/\/\*\*$/u, ""));
38
+ const rootKey = process.platform === "win32" ? root.toLocaleLowerCase("en-US") : root;
39
+ return key === rootKey || key.startsWith(`${rootKey}/`);
40
+ });
41
+ }
42
+ function assertRegularContainedFile(root, relativePath, label) {
43
+ const absolute = resolve(root, normalizedRelative(relativePath));
44
+ if (!containsPath(root, absolute)) throw new Error(`${label} escaped its root`);
45
+ const info = statSync(absolute);
46
+ if (!info.isFile()) throw new Error(`${label} is not a regular file`);
47
+ return absolute;
48
+ }
49
+ async function walkBusinessFiles(root) {
50
+ const files = [];
51
+ const queue = [root];
52
+ while (queue.length > 0) {
53
+ const directory = queue.shift();
54
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
55
+ const absolute = join(directory, entry.name);
56
+ if (entry.isSymbolicLink()) throw new Error(`\u4E1A\u52A1\u6E90\u7801\u4E0D\u80FD\u5305\u542B\u7B26\u53F7\u94FE\u63A5/junction\uFF1A${normalizedRelative(relative(root, absolute))}`);
57
+ if (entry.isDirectory()) queue.push(absolute);
58
+ else if (entry.isFile()) files.push(absolute);
59
+ }
60
+ }
61
+ return files;
62
+ }
63
+ function editableProjectPath(args, relativePath) {
64
+ const key = process.platform === "win32" ? relativePath.toLocaleLowerCase("en-US") : relativePath;
65
+ return args.editablePaths.some((pattern) => {
66
+ const root = normalizedRelative(pattern.replace(/\/\*\*$/u, ""));
67
+ const rootKey = process.platform === "win32" ? root.toLocaleLowerCase("en-US") : root;
68
+ return key === rootKey || key.startsWith(`${rootKey}/`);
69
+ });
70
+ }
71
+ async function businessFilesIfPresent(root) {
72
+ let info;
73
+ try {
74
+ info = await lstat(root);
75
+ } catch (error) {
76
+ if (filesystemErrorCode(error) === "ENOENT") return [];
77
+ throw error;
78
+ }
79
+ return info.isDirectory() && !info.isSymbolicLink() ? await walkBusinessFiles(root) : [];
80
+ }
81
+ function businessModuleCandidate(root, requested) {
82
+ const variants = extname(requested) ? [requested] : [
83
+ requested,
84
+ ...[".ts", ".tsx", ".js", ".jsx", ".json", ".css"].map((extension) => `${requested}${extension}`),
85
+ ...[".ts", ".tsx", ".js", ".jsx", ".json", ".css"].map((extension) => posix.join(requested, `index${extension}`))
86
+ ];
87
+ for (const relativePath of variants) {
88
+ const absolute = resolve(root, relativePath);
89
+ if (!containsPath(root, absolute)) continue;
90
+ const info = (() => {
91
+ try {
92
+ return lstatSync(absolute);
93
+ } catch (error) {
94
+ const code = filesystemErrorCode(error);
95
+ if (code === "ENOENT" || code === "ENOTDIR") return void 0;
96
+ throw error;
97
+ }
98
+ })();
99
+ if (info?.isFile() && !info.isSymbolicLink()) return normalizedRelative(relative(root, absolute));
100
+ }
101
+ return void 0;
102
+ }
103
+
104
+ // apps/host/src/runtime-code-diagnostics.ts
105
+ import { existsSync as existsSync2, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
106
+ import { readFile } from "node:fs/promises";
107
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join3, relative as relative2, resolve as resolve2 } from "node:path";
108
+ import ts from "typescript";
109
+
110
+ // apps/host/src/runtime-dependencies.ts
111
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
112
+ import { createRequire } from "node:module";
113
+ import { dirname, join as join2 } from "node:path";
114
+ import { fileURLToPath } from "node:url";
115
+ var VIRTUAL_REGISTRY = "virtual:harness-business-registry";
116
+ var requireFromHost = createRequire(import.meta.url);
117
+ var workerModulePath = fileURLToPath(import.meta.url);
118
+ var BUSINESS_DEPENDENCIES = /* @__PURE__ */ new Set([
119
+ "@ant-design/charts",
120
+ "@ant-design/icons",
121
+ "antd",
122
+ "dayjs",
123
+ "react",
124
+ "react-dom",
125
+ "react-router-dom"
126
+ ]);
127
+ function packageName(source) {
128
+ if (source.startsWith("@")) return source.split("/").slice(0, 2).join("/");
129
+ return source.split("/")[0];
130
+ }
131
+ function safeBareSpecifier(source, dependency) {
132
+ if (source.includes("\\") || source !== dependency && !source.startsWith(`${dependency}/`)) return false;
133
+ return !source.split("/").some((segment) => segment === "." || segment === "..");
134
+ }
135
+ function exactSpecifierPattern(source) {
136
+ return new RegExp(`^${source.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}$`, "u");
137
+ }
138
+ function fixedDependencyResolution(source, dependency) {
139
+ if (!safeBareSpecifier(source, dependency)) throw new Error(`\u4F9D\u8D56\u5B50\u8DEF\u5F84\u4E0D\u5B89\u5168\uFF1A${source}`);
140
+ const dependencyRoot = dependencyPackage(dependency).root;
141
+ const resolved = requireFromHost.resolve(source);
142
+ const canonical = realpathSync(resolved);
143
+ if (!containsPath(dependencyRoot, canonical)) throw new Error(`\u4F9D\u8D56\u5B50\u8DEF\u5F84\u9003\u9038\u56FA\u5B9A\u5305\u6839\uFF1A${source}`);
144
+ return resolved;
145
+ }
146
+ function browserDependencySource(source, dependency) {
147
+ if (dependency === "antd" && /^antd\/locale\/[a-zA-Z0-9_-]+$/u.test(source)) {
148
+ return source.replace(/^antd\/locale\//u, "antd/es/locale/");
149
+ }
150
+ return source;
151
+ }
152
+ function dependencyTypeTarget(source) {
153
+ try {
154
+ const resolved = requireFromHost.resolve(source);
155
+ return resolved;
156
+ } catch {
157
+ return void 0;
158
+ }
159
+ }
160
+ var dependencyPackageCache = /* @__PURE__ */ new Map();
161
+ function dependencyPackage(name) {
162
+ const cached = dependencyPackageCache.get(name);
163
+ if (cached) return cached;
164
+ try {
165
+ const packagePath = requireFromHost.resolve(`${name}/package.json`);
166
+ const value = JSON.parse(readFileSync(packagePath, "utf8"));
167
+ if (value.name === name && typeof value.version === "string") {
168
+ const result = { root: dirname(packagePath), version: value.version };
169
+ dependencyPackageCache.set(name, result);
170
+ return result;
171
+ }
172
+ } catch {
173
+ }
174
+ const entry = requireFromHost.resolve(name);
175
+ let current = dirname(entry);
176
+ for (; ; ) {
177
+ const packagePath = join2(current, "package.json");
178
+ if (existsSync(packagePath)) {
179
+ const value = JSON.parse(readFileSync(packagePath, "utf8"));
180
+ if (value.name === name && typeof value.version === "string") {
181
+ const result = { root: current, version: value.version };
182
+ dependencyPackageCache.set(name, result);
183
+ return result;
184
+ }
185
+ }
186
+ const parent = dirname(current);
187
+ if (parent === current) break;
188
+ current = parent;
189
+ }
190
+ throw new Error(`\u65E0\u6CD5\u5B9A\u4F4D Host \u56FA\u5B9A\u4F9D\u8D56\uFF1A${name}`);
191
+ }
192
+ function verifyDependencies(args) {
193
+ const roots = /* @__PURE__ */ new Set();
194
+ for (const [name, expected] of Object.entries(args.dependencies)) {
195
+ const actual = dependencyPackage(name);
196
+ if (actual.version !== expected) throw new Error(`Host \u56FA\u5B9A\u4F9D\u8D56\u7248\u672C\u4E0D\u5339\u914D\uFF1A${name} expected ${expected}, got ${actual.version}`);
197
+ roots.add(actual.root);
198
+ }
199
+ return [...roots];
200
+ }
201
+
202
+ // apps/host/src/runtime-code-diagnostics.ts
203
+ function safeGeneratedCssUrl(value) {
204
+ const trimmed = value.trim().replace(/^(["'])(.*)\1$/u, "$2");
205
+ if (trimmed.startsWith("data:")) return true;
206
+ if (!trimmed.startsWith("/generated/") || /[%\\?#\u0000-\u001F]/u.test(trimmed)) return false;
207
+ const segments = trimmed.slice("/generated/".length).split("/");
208
+ return segments.length > 0 && segments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
209
+ }
210
+ function cssBoundaryViolations(source) {
211
+ const withoutComments = source.replace(/\/\*[\s\S]*?\*\//gu, "");
212
+ const violations = [];
213
+ if (/@import\b/iu.test(withoutComments)) violations.push("\u4E1A\u52A1 CSS \u4E0D\u5141\u8BB8 @import");
214
+ for (const match of withoutComments.matchAll(/url\(\s*([^)]*?)\s*\)/giu)) {
215
+ if (!safeGeneratedCssUrl(match[1] ?? "")) {
216
+ violations.push("\u4E1A\u52A1 CSS \u7684 url() \u53EA\u5141\u8BB8 data: \u6216\u4E25\u683C\u7684 /generated/** \u8DEF\u5F84");
217
+ }
218
+ }
219
+ return violations;
220
+ }
221
+ function tsDiagnostic(args, diagnostic, virtualTargets = /* @__PURE__ */ new Map()) {
222
+ const target = diagnostic.file ? virtualTargets.get(filesystemPathKey(diagnostic.file.fileName)) : void 0;
223
+ const detail = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
224
+ const message = `${target ? "\u9ED8\u8BA4\u5BFC\u51FA\u4E0D\u7B26\u5408 Runtime \u7EC4\u4EF6\u5951\u7EA6\uFF1A" : ""}${detail}`.slice(0, 1e3);
225
+ const file = diagnostic.file;
226
+ if (!file || diagnostic.start === void 0) return { severity: "error", source: "typescript", code: diagnostic.code, message };
227
+ const start = file.getLineAndCharacterOfPosition(diagnostic.start);
228
+ const endPosition = diagnostic.start + (diagnostic.length ?? 0);
229
+ const end = file.getLineAndCharacterOfPosition(Math.min(endPosition, file.text.length));
230
+ return {
231
+ severity: diagnostic.category === ts.DiagnosticCategory.Error ? "error" : diagnostic.category === ts.DiagnosticCategory.Warning ? "warning" : "info",
232
+ source: "typescript",
233
+ code: diagnostic.code,
234
+ message,
235
+ ...(() => {
236
+ const reportedFile = target ?? file.fileName;
237
+ const projectPath = relativeProjectPath(args.projectRoot, reportedFile);
238
+ if (projectPath) return { path: projectPath.slice(0, 1024) };
239
+ return containsPath(args.runtimeRoot, resolve2(reportedFile)) ? { path: `runtime/${normalizedRelative(relative2(args.runtimeRoot, reportedFile))}`.slice(0, 1024) } : {};
240
+ })(),
241
+ line: start.line + 1,
242
+ column: start.character + 1,
243
+ endLine: end.line + 1,
244
+ endColumn: end.character + 1
245
+ };
246
+ }
247
+ function businessImportDiagnostics(args, businessFiles, compilerOptions) {
248
+ const diagnostics = [];
249
+ const add = (file, node, source, message) => {
250
+ const start = file.getLineAndCharacterOfPosition(node.getStart(file));
251
+ diagnostics.push({
252
+ severity: "error",
253
+ source: "typescript",
254
+ code: "HARNESS_IMPORT_BOUNDARY",
255
+ message: `${message}\uFF1A${source}`.slice(0, 1e3),
256
+ ...relativeProjectPath(args.projectRoot, file.fileName) ? { path: relativeProjectPath(args.projectRoot, file.fileName).slice(0, 1024) } : {},
257
+ line: start.line + 1,
258
+ column: start.character + 1
259
+ });
260
+ };
261
+ const validate = (file, node, source) => {
262
+ const normalizedSource = source.replaceAll("\\", "/");
263
+ if (normalizedSource === "@harness/frontend-runtime") return;
264
+ if (/^(?:node:|https?:|data:)/iu.test(normalizedSource)) {
265
+ add(file, node, source, "\u5FEB\u901F\u524D\u53F0\u4E0D\u5141\u8BB8\u5BFC\u5165");
266
+ return;
267
+ }
268
+ if (normalizedSource.startsWith(".") || normalizedSource.startsWith("/") || isAbsolute2(source)) {
269
+ if (normalizedSource.startsWith("/") || isAbsolute2(source)) {
270
+ add(file, node, source, "\u4E1A\u52A1\u4EE3\u7801\u4E0D\u80FD\u4F7F\u7528\u7EDD\u5BF9\u5BFC\u5165");
271
+ return;
272
+ }
273
+ const candidate = resolve2(dirname2(file.fileName), normalizedSource);
274
+ if (!allowedProjectPath(args, candidate)) {
275
+ add(file, node, source, "\u4E1A\u52A1\u5BFC\u5165\u8D8A\u8FC7\u53EF\u8BFB\u8FB9\u754C");
276
+ return;
277
+ }
278
+ const resolvedModule = ts.resolveModuleName(source, file.fileName, compilerOptions, ts.sys).resolvedModule;
279
+ if (resolvedModule && existsSync2(resolvedModule.resolvedFileName)) {
280
+ const canonical = realpathSync2(resolvedModule.resolvedFileName);
281
+ if (!allowedProjectPath(args, canonical)) add(file, node, source, "\u4E1A\u52A1\u5BFC\u5165 canonical \u8DEF\u5F84\u8D8A\u8FC7\u53EF\u8BFB\u8FB9\u754C");
282
+ }
283
+ return;
284
+ }
285
+ const dependency = packageName(normalizedSource);
286
+ if (!BUSINESS_DEPENDENCIES.has(dependency)) add(file, node, source, "Runtime \u672A\u5411\u4E1A\u52A1\u4EE3\u7801\u516C\u5F00\u4F9D\u8D56");
287
+ else if (!safeBareSpecifier(normalizedSource, dependency)) add(file, node, source, "\u4F9D\u8D56\u5B50\u8DEF\u5F84\u4E0D\u5B89\u5168");
288
+ };
289
+ for (const path of businessFiles) {
290
+ const file = ts.createSourceFile(
291
+ path,
292
+ readFileSync2(path, "utf8"),
293
+ ts.ScriptTarget.ESNext,
294
+ true,
295
+ path.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS
296
+ );
297
+ for (const reference of file.referencedFiles) {
298
+ validate(file, file, reference.fileName);
299
+ }
300
+ for (const reference of [...file.typeReferenceDirectives, ...file.libReferenceDirectives]) {
301
+ add(file, file, reference.fileName, "\u4E1A\u52A1\u4EE3\u7801\u4E0D\u5141\u8BB8\u4E09\u659C\u7EBF\u7C7B\u578B\u6216 lib \u5F15\u7528");
302
+ }
303
+ if (file.moduleName || file.amdDependencies.length > 0) {
304
+ add(file, file, file.moduleName ?? file.amdDependencies[0]?.name ?? "amd", "\u4E1A\u52A1\u4EE3\u7801\u4E0D\u5141\u8BB8 AMD \u6307\u4EE4");
305
+ }
306
+ const visit = (node) => {
307
+ if (ts.isMetaProperty(node) && node.keywordToken === ts.SyntaxKind.ImportKeyword) {
308
+ add(file, node, "import.meta", "\u4E1A\u52A1\u4EE3\u7801\u4E0D\u5141\u8BB8\u4F7F\u7528 import.meta\uFF08\u5305\u62EC glob \u4E0E\u57FA\u4E8E URL \u7684\u6587\u4EF6\u8BFB\u53D6\uFF09");
309
+ } else if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier && ts.isStringLiteralLike(node.moduleSpecifier)) {
310
+ validate(file, node.moduleSpecifier, node.moduleSpecifier.text);
311
+ } else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) && node.moduleReference.expression && ts.isStringLiteralLike(node.moduleReference.expression)) {
312
+ validate(file, node.moduleReference.expression, node.moduleReference.expression.text);
313
+ } else if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) && ts.isStringLiteralLike(node.argument.literal)) {
314
+ validate(file, node.argument.literal, node.argument.literal.text);
315
+ } else if (ts.isCallExpression(node) && node.arguments.length > 0 && ts.isStringLiteralLike(node.arguments[0]) && (node.expression.kind === ts.SyntaxKind.ImportKeyword || ts.isIdentifier(node.expression) && node.expression.text === "require")) {
316
+ validate(file, node.arguments[0], node.arguments[0].text);
317
+ }
318
+ if (diagnostics.length < 20) ts.forEachChild(node, visit);
319
+ };
320
+ visit(file);
321
+ if (diagnostics.length >= 20) break;
322
+ }
323
+ return diagnostics.slice(0, 20);
324
+ }
325
+ var WEB_STORAGE_NAMES = /* @__PURE__ */ new Set(["localStorage", "sessionStorage"]);
326
+ var WEB_STORAGE_GLOBALS = /* @__PURE__ */ new Set(["globalThis", "self", "window"]);
327
+ function businessWebStorageDiagnostics(args, program, businessFiles) {
328
+ const diagnostics = [];
329
+ const checker = program.getTypeChecker();
330
+ const add = (file, node, storageName) => {
331
+ if (diagnostics.length >= 20) return;
332
+ const start = file.getLineAndCharacterOfPosition(node.getStart(file));
333
+ diagnostics.push({
334
+ severity: "error",
335
+ source: "runtime",
336
+ code: "HARNESS_UNSAFE_WEB_STORAGE",
337
+ message: `\u5FEB\u901F\u524D\u53F0\u9884\u89C8\u4E0D\u80FD\u76F4\u63A5\u8BBF\u95EE ${storageName}\uFF1B\u8BF7\u4ECE @harness/frontend-runtime \u5BFC\u5165 getFrontendStorage(namespace) \u5E76\u4F7F\u7528\u5176\u540C\u6B65\u5B58\u50A8\u63A5\u53E3\u3002`,
338
+ ...relativeProjectPath(args.projectRoot, file.fileName) ? { path: relativeProjectPath(args.projectRoot, file.fileName).slice(0, 1024) } : {},
339
+ line: start.line + 1,
340
+ column: start.character + 1
341
+ });
342
+ };
343
+ const isBusinessDeclaration = (node) => {
344
+ const declarations = checker.getSymbolAtLocation(node)?.declarations ?? [];
345
+ return declarations.some((declaration) => containsPath(args.projectRoot, resolve2(declaration.getSourceFile().fileName)));
346
+ };
347
+ const isDomStorageProperty = (node) => {
348
+ const declarations = checker.getSymbolAtLocation(node)?.declarations ?? [];
349
+ return declarations.some((declaration) => /(?:^|[/\\])lib\.dom\.d\.ts$/u.test(declaration.getSourceFile().fileName));
350
+ };
351
+ const unwrapExpression = (expression) => {
352
+ let current = expression;
353
+ while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isTypeAssertionExpression(current) || ts.isNonNullExpression(current)) {
354
+ current = current.expression;
355
+ }
356
+ return current;
357
+ };
358
+ const referencesBrowserGlobal = (expression, seen = /* @__PURE__ */ new Set()) => {
359
+ const current = unwrapExpression(expression);
360
+ if (ts.isIdentifier(current)) {
361
+ const symbol = checker.getSymbolAtLocation(current);
362
+ if (WEB_STORAGE_GLOBALS.has(current.text) && !isBusinessDeclaration(current)) return true;
363
+ if (symbol === void 0 || seen.has(symbol)) return false;
364
+ seen.add(symbol);
365
+ for (const declaration of symbol.declarations ?? []) {
366
+ if (ts.isVariableDeclaration(declaration) && declaration.initializer !== void 0 && referencesBrowserGlobal(declaration.initializer, seen)) return true;
367
+ }
368
+ return false;
369
+ }
370
+ if (ts.isConditionalExpression(current)) {
371
+ return referencesBrowserGlobal(current.whenTrue, new Set(seen)) || referencesBrowserGlobal(current.whenFalse, new Set(seen));
372
+ }
373
+ if (ts.isBinaryExpression(current) && (current.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken || current.operatorToken.kind === ts.SyntaxKind.BarBarToken)) {
374
+ return referencesBrowserGlobal(current.left, new Set(seen)) || referencesBrowserGlobal(current.right, new Set(seen));
375
+ }
376
+ return false;
377
+ };
378
+ const destructuresGlobalStorage = (node) => {
379
+ const element = node.parent;
380
+ if (!ts.isBindingElement(element) || element.name !== node && element.propertyName !== node || !ts.isObjectBindingPattern(element.parent)) return false;
381
+ const declaration = element.parent.parent;
382
+ return ts.isVariableDeclaration(declaration) && declaration.initializer !== void 0 && referencesBrowserGlobal(declaration.initializer);
383
+ };
384
+ for (const path of businessFiles) {
385
+ const file = program.getSourceFile(path);
386
+ if (file === void 0) continue;
387
+ const visit = (node) => {
388
+ if (diagnostics.length >= 20) return;
389
+ if (ts.isIdentifier(node) && WEB_STORAGE_NAMES.has(node.text)) {
390
+ const parent = node.parent;
391
+ if (ts.isPropertyAccessExpression(parent) && parent.name === node) {
392
+ if (referencesBrowserGlobal(parent.expression) || isDomStorageProperty(node)) {
393
+ add(file, node, node.text);
394
+ }
395
+ } else if (destructuresGlobalStorage(node) || !isBusinessDeclaration(node)) {
396
+ add(file, node, node.text);
397
+ }
398
+ } else if (ts.isStringLiteralLike(node) && WEB_STORAGE_NAMES.has(node.text) && ts.isElementAccessExpression(node.parent) && node.parent.argumentExpression === node && referencesBrowserGlobal(node.parent.expression)) {
399
+ add(file, node, node.text);
400
+ }
401
+ ts.forEachChild(node, visit);
402
+ };
403
+ visit(file);
404
+ if (diagnostics.length >= 20) break;
405
+ }
406
+ return diagnostics;
407
+ }
408
+ async function businessCssDiagnostics(args) {
409
+ const srcRoot = join3(args.projectRoot, "src");
410
+ const files = await businessFilesIfPresent(srcRoot);
411
+ const diagnostics = [];
412
+ for (const path of files.filter((entry) => entry.endsWith(".css"))) {
413
+ for (const message of cssBoundaryViolations(await readFile(path, "utf8"))) {
414
+ diagnostics.push({
415
+ severity: "error",
416
+ source: "runtime",
417
+ code: "HARNESS_CSS_BOUNDARY",
418
+ message,
419
+ ...relativeProjectPath(args.projectRoot, path) ? { path: relativeProjectPath(args.projectRoot, path) } : {}
420
+ });
421
+ if (diagnostics.length >= 20) return diagnostics;
422
+ }
423
+ }
424
+ return diagnostics;
425
+ }
426
+
427
+ // apps/host/src/runtime-route-diagnostics.ts
428
+ import { statSync as statSync2 } from "node:fs";
429
+ import { lstat as lstat2, readFile as readFile2 } from "node:fs/promises";
430
+ import { dirname as dirname3, join as join4, relative as relative3, sep as sep2 } from "node:path";
431
+ var ROUTE_KEYS = /* @__PURE__ */ new Set(["id", "path", "title", "icon", "order", "parentId", "hidden"]);
432
+ var ROUTE_ICONS = /* @__PURE__ */ new Set([
433
+ "AppstoreOutlined",
434
+ "BarChartOutlined",
435
+ "DashboardOutlined",
436
+ "FileTextOutlined",
437
+ "FormOutlined",
438
+ "HomeOutlined",
439
+ "SettingOutlined",
440
+ "TeamOutlined",
441
+ "UnorderedListOutlined"
442
+ ]);
443
+ async function appManifest(args) {
444
+ const appPath = join4(args.projectRoot, "src", "app.json");
445
+ const relativePath = "src/app.json";
446
+ try {
447
+ const value = JSON.parse(await readFile2(appPath, "utf8"));
448
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("app.json \u5FC5\u987B\u662F\u5BF9\u8C61");
449
+ const raw = value;
450
+ const extra = Object.keys(raw).find((key) => !(/* @__PURE__ */ new Set(["name", "logo", "theme"])).has(key));
451
+ if (extra) throw new Error(`\u4E0D\u652F\u6301\u7684\u5E94\u7528\u5B57\u6BB5\uFF1A${extra}`);
452
+ if (raw.name !== void 0 && (typeof raw.name !== "string" || raw.name.trim().length === 0 || raw.name.length > 160)) {
453
+ throw new Error("name \u5FC5\u987B\u4E3A 1\u2013160 \u5B57\u7B26");
454
+ }
455
+ if (raw.logo !== void 0 && (typeof raw.logo !== "string" || raw.logo.length > 2048 || !/^\.\/generated\/(?:[^./][^/]*\/)*[^./][^/]*$/u.test(raw.logo))) {
456
+ throw new Error("logo \u5FC5\u987B\u662F ./generated/** \u4E0B\u7684\u76F8\u5BF9\u8D44\u6E90\u8DEF\u5F84");
457
+ }
458
+ if (raw.theme !== void 0) {
459
+ if (!raw.theme || typeof raw.theme !== "object" || Array.isArray(raw.theme)) throw new Error("theme \u5FC5\u987B\u662F\u5BF9\u8C61");
460
+ const theme = raw.theme;
461
+ const themeExtra = Object.keys(theme).find((key) => !(/* @__PURE__ */ new Set(["colorPrimary", "borderRadius", "compact"])).has(key));
462
+ if (themeExtra) throw new Error(`\u4E0D\u652F\u6301\u7684\u4E3B\u9898\u5B57\u6BB5\uFF1A${themeExtra}`);
463
+ if (theme.colorPrimary !== void 0 && (typeof theme.colorPrimary !== "string" || !/^#[0-9a-fA-F]{6}$/u.test(theme.colorPrimary))) {
464
+ throw new Error("theme.colorPrimary \u5FC5\u987B\u662F\u516D\u4F4D\u5341\u516D\u8FDB\u5236\u989C\u8272");
465
+ }
466
+ if (theme.borderRadius !== void 0 && (typeof theme.borderRadius !== "number" || !Number.isSafeInteger(theme.borderRadius) || theme.borderRadius < 0 || theme.borderRadius > 24)) {
467
+ throw new Error("theme.borderRadius \u5FC5\u987B\u662F 0\u201324 \u7684\u6574\u6570");
468
+ }
469
+ if (theme.compact !== void 0 && typeof theme.compact !== "boolean") throw new Error("theme.compact \u5FC5\u987B\u662F\u5E03\u5C14\u503C");
470
+ }
471
+ return { manifest: raw, diagnostics: [] };
472
+ } catch (error) {
473
+ const code = filesystemErrorCode(error);
474
+ if (code === "ENOENT") return { manifest: {}, diagnostics: [] };
475
+ if (code) throw error;
476
+ return {
477
+ manifest: {},
478
+ diagnostics: [{
479
+ severity: "error",
480
+ source: "route",
481
+ message: (error instanceof Error ? error.message : "app.json \u65E0\u6548").slice(0, 8e3),
482
+ path: relativePath
483
+ }]
484
+ };
485
+ }
486
+ }
487
+ async function routeRegistry(args) {
488
+ const diagnostics = [];
489
+ const pagesRoot = join4(args.projectRoot, "src", "pages");
490
+ const info = await lstat2(pagesRoot).catch((error) => {
491
+ if (filesystemErrorCode(error) === "ENOENT") return void 0;
492
+ throw error;
493
+ });
494
+ if (!info?.isDirectory() || info.isSymbolicLink()) return { routes: [], diagnostics };
495
+ const pageFiles = await walkBusinessFiles(pagesRoot);
496
+ const routeFiles = pageFiles.filter((path) => path.endsWith(`${sep2}route.json`));
497
+ const routeDirectories = new Set(routeFiles.map((path) => dirname3(path)));
498
+ for (const pagePath of pageFiles.filter((path) => path.endsWith(`${sep2}page.tsx`))) {
499
+ if (!routeDirectories.has(dirname3(pagePath))) diagnostics.push({
500
+ severity: "error",
501
+ source: "route",
502
+ code: "HARNESS_MISSING_ROUTE",
503
+ message: "\u9875\u9762\u7F3A\u5C11\u540C\u76EE\u5F55\u7684 route.json",
504
+ path: normalizedRelative(relative3(args.projectRoot, pagePath))
505
+ });
506
+ }
507
+ const routes = [];
508
+ const ids = /* @__PURE__ */ new Map();
509
+ const paths = /* @__PURE__ */ new Map();
510
+ for (const routePath of routeFiles) {
511
+ const path = normalizedRelative(relative3(args.projectRoot, routePath));
512
+ try {
513
+ const raw = JSON.parse(await readFile2(routePath, "utf8"));
514
+ const extra = Object.keys(raw).find((key) => !ROUTE_KEYS.has(key));
515
+ if (extra) throw new Error(`\u4E0D\u652F\u6301\u7684\u8DEF\u7531\u5B57\u6BB5\uFF1A${extra}`);
516
+ if (typeof raw.id !== "string" || raw.id.length > 160 || !/^[a-zA-Z0-9][a-zA-Z0-9._:-]*$/u.test(raw.id)) throw new Error("id \u5FC5\u987B\u662F 160 \u5B57\u7B26\u5185\u7684\u7A33\u5B9A\u6807\u8BC6\u7B26");
517
+ if (typeof raw.path !== "string" || raw.path.length > 1024 || !raw.path.startsWith("/") || raw.path.includes("..")) throw new Error("path \u5FC5\u987B\u662F 1024 \u5B57\u7B26\u5185\u3001\u4EE5 / \u5F00\u5934\u7684\u5E94\u7528\u8DEF\u7531");
518
+ if (typeof raw.title !== "string" || raw.title.trim().length === 0 || raw.title.length > 160) throw new Error("title \u5FC5\u987B\u4E3A 1\u2013160 \u5B57\u7B26");
519
+ if (raw.icon !== void 0 && (typeof raw.icon !== "string" || !ROUTE_ICONS.has(raw.icon))) throw new Error("icon \u4E0D\u5728 Runtime \u652F\u6301\u5217\u8868\u4E2D");
520
+ if (raw.parentId !== void 0 && (typeof raw.parentId !== "string" || raw.parentId.length > 160 || !/^[a-zA-Z0-9][a-zA-Z0-9._:-]*$/u.test(raw.parentId))) throw new Error("parentId \u5FC5\u987B\u662F\u6709\u6548\u8DEF\u7531\u6807\u8BC6\u7B26");
521
+ if (raw.order !== void 0 && (typeof raw.order !== "number" || !Number.isSafeInteger(raw.order))) throw new Error("order \u5FC5\u987B\u662F\u6574\u6570");
522
+ if (raw.hidden !== void 0 && typeof raw.hidden !== "boolean") throw new Error("hidden \u5FC5\u987B\u662F\u5E03\u5C14\u503C");
523
+ const pagePath = join4(dirname3(routePath), "page.tsx");
524
+ let pageInfo;
525
+ try {
526
+ pageInfo = statSync2(pagePath);
527
+ } catch (error) {
528
+ const code = filesystemErrorCode(error);
529
+ if (code === "ENOENT" || code === "ENOTDIR") throw new Error("\u540C\u76EE\u5F55\u7F3A\u5C11 page.tsx");
530
+ throw error;
531
+ }
532
+ if (!pageInfo.isFile()) throw new Error("\u540C\u76EE\u5F55\u7F3A\u5C11 page.tsx");
533
+ const priorId = ids.get(raw.id);
534
+ if (priorId) throw new Error(`\u8DEF\u7531 id \u4E0E ${priorId} \u91CD\u590D`);
535
+ const priorPath = paths.get(raw.path);
536
+ if (priorPath) throw new Error(`\u8DEF\u7531 path \u4E0E ${priorPath} \u91CD\u590D`);
537
+ ids.set(raw.id, path);
538
+ paths.set(raw.path, path);
539
+ routes.push({
540
+ metadata: {
541
+ id: raw.id,
542
+ path: raw.path,
543
+ title: raw.title,
544
+ ...typeof raw.icon === "string" ? { icon: raw.icon } : {},
545
+ ...typeof raw.order === "number" && Number.isFinite(raw.order) ? { order: raw.order } : {},
546
+ ...typeof raw.parentId === "string" ? { parentId: raw.parentId } : {},
547
+ ...typeof raw.hidden === "boolean" ? { hidden: raw.hidden } : {}
548
+ },
549
+ pagePath
550
+ });
551
+ } catch (error) {
552
+ if (filesystemErrorCode(error)) throw error;
553
+ diagnostics.push({
554
+ severity: "error",
555
+ source: "route",
556
+ message: (error instanceof Error ? error.message : "\u8DEF\u7531 JSON \u65E0\u6548").slice(0, 8e3),
557
+ path
558
+ });
559
+ }
560
+ }
561
+ for (const route of routes) {
562
+ if (route.metadata.parentId && !ids.has(route.metadata.parentId)) {
563
+ diagnostics.push({
564
+ severity: "error",
565
+ source: "route",
566
+ message: `parentId \u4E0D\u5B58\u5728\uFF1A${route.metadata.parentId}`,
567
+ ...ids.get(route.metadata.id) ? { path: ids.get(route.metadata.id) } : {}
568
+ });
569
+ continue;
570
+ }
571
+ const visited = /* @__PURE__ */ new Set();
572
+ let current = route.metadata;
573
+ while (current?.parentId) {
574
+ if (visited.has(current.id)) {
575
+ diagnostics.push({
576
+ severity: "error",
577
+ source: "route",
578
+ message: "\u8DEF\u7531\u7236\u5B50\u5173\u7CFB\u5B58\u5728\u5FAA\u73AF",
579
+ ...ids.get(route.metadata.id) ? { path: ids.get(route.metadata.id) } : {}
580
+ });
581
+ break;
582
+ }
583
+ visited.add(current.id);
584
+ current = routes.find((entry) => entry.metadata.id === current.parentId)?.metadata;
585
+ }
586
+ }
587
+ routes.sort((left, right) => (left.metadata.order ?? 0) - (right.metadata.order ?? 0) || left.metadata.id.localeCompare(right.metadata.id));
588
+ return { routes, diagnostics };
589
+ }
590
+
591
+ // apps/host/src/runtime-diagnostics.ts
592
+ var RuntimeDiagnosticEngine = class {
593
+ #previousProgram;
594
+ dispose() {
595
+ this.#previousProgram = void 0;
596
+ }
597
+ async collect(args) {
598
+ const [routes, app, css] = await Promise.all([routeRegistry(args), appManifest(args), businessCssDiagnostics(args)]);
599
+ const types = await this.#typeDiagnostics(args, routes.routes.map((route) => route.pagePath));
600
+ const diagnostics = [...routes.diagnostics, ...app.diagnostics, ...css, ...types];
601
+ if (routes.routes.length === 0 && !diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
602
+ diagnostics.push({ severity: "info", source: "runtime", code: "HARNESS_NO_PAGES", message: "\u5C1A\u65E0\u53EF\u9884\u89C8\u9875\u9762\u3002\u53EF\u4EE5\u7EE7\u7EED\u5BF9\u8BDD\uFF0C\u6309\u9700\u521B\u5EFA\u9875\u9762\u3002" });
603
+ }
604
+ return diagnostics.slice(0, 20).map((diagnostic) => ({
605
+ ...diagnostic,
606
+ message: diagnostic.message.slice(0, 1e3),
607
+ ...diagnostic.path ? { path: diagnostic.path.slice(0, 1024) } : {}
608
+ }));
609
+ }
610
+ async #typeDiagnostics(args, routedPages) {
611
+ const srcRoot = join5(args.projectRoot, "src");
612
+ const files = await businessFilesIfPresent(srcRoot);
613
+ const businessRootNames = files.filter((path) => /\.(?:ts|tsx)$/u.test(path));
614
+ const declarationFile = assertRegularContainedFile(args.runtimeRoot, args.typeDeclarations, "Runtime type declarations");
615
+ const options = {
616
+ allowJs: false,
617
+ esModuleInterop: true,
618
+ jsx: ts2.JsxEmit.ReactJSX,
619
+ module: ts2.ModuleKind.ESNext,
620
+ moduleResolution: ts2.ModuleResolutionKind.Bundler,
621
+ noEmit: true,
622
+ resolveJsonModule: true,
623
+ skipLibCheck: true,
624
+ strict: true,
625
+ target: ts2.ScriptTarget.ES2022,
626
+ types: []
627
+ };
628
+ const importDiagnostics = businessImportDiagnostics(args, businessRootNames, options);
629
+ if (importDiagnostics.length > 0) return importDiagnostics;
630
+ const host = ts2.createCompilerHost(options);
631
+ const resolutionCache = ts2.createModuleResolutionCache(args.projectRoot, host.getCanonicalFileName, options);
632
+ const virtualFiles = /* @__PURE__ */ new Map();
633
+ for (const [index, pagePath] of routedPages.entries()) {
634
+ virtualFiles.set(join5(dirname4(pagePath), ".harness-page-contract.ts"), {
635
+ source: [
636
+ 'import type { FrontendPageComponent } from "@harness/frontend-runtime";',
637
+ 'import Page from "./page";',
638
+ `const page${index}: FrontendPageComponent = Page;`,
639
+ ""
640
+ ].join("\n"),
641
+ target: pagePath
642
+ });
643
+ }
644
+ const providerPath = join5(args.projectRoot, "src", "app", "providers.tsx");
645
+ if (existsSync3(providerPath)) {
646
+ virtualFiles.set(join5(dirname4(providerPath), ".harness-provider-contract.ts"), {
647
+ source: [
648
+ 'import type { FrontendRuntimeProvider } from "@harness/frontend-runtime";',
649
+ 'import Provider from "./providers";',
650
+ "const provider: FrontendRuntimeProvider = Provider;",
651
+ ""
652
+ ].join("\n"),
653
+ target: providerPath
654
+ });
655
+ }
656
+ const virtualFilesByKey = new Map([...virtualFiles].map(([fileName, value]) => [filesystemPathKey(fileName), value]));
657
+ const originalFileExists = host.fileExists.bind(host);
658
+ const originalReadFile = host.readFile.bind(host);
659
+ const originalGetSourceFile = host.getSourceFile.bind(host);
660
+ host.fileExists = (fileName) => virtualFilesByKey.has(filesystemPathKey(fileName)) || originalFileExists(fileName);
661
+ host.readFile = (fileName) => virtualFilesByKey.get(filesystemPathKey(fileName))?.source ?? originalReadFile(fileName);
662
+ host.getSourceFile = (fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile) => {
663
+ const virtual = virtualFilesByKey.get(filesystemPathKey(fileName));
664
+ return virtual ? ts2.createSourceFile(fileName, virtual.source, languageVersionOrOptions, true, ts2.ScriptKind.TS) : originalGetSourceFile(fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile);
665
+ };
666
+ const originalResolve = host.resolveModuleNames;
667
+ host.resolveModuleNames = (moduleNames, containingFile, reusedNames, redirectedReference, compilerOptions) => moduleNames.map((moduleName) => {
668
+ const businessImporter = containsPath(args.projectRoot, resolve3(containingFile));
669
+ const runtimeImporter = containsPath(args.runtimeRoot, resolve3(containingFile));
670
+ if (moduleName === "@harness/frontend-runtime") {
671
+ return { resolvedFileName: declarationFile, extension: ts2.Extension.Dts };
672
+ }
673
+ if ((businessImporter || runtimeImporter) && !moduleName.startsWith(".") && !isAbsolute3(moduleName)) {
674
+ const dependency = packageName(moduleName);
675
+ if (moduleName === VIRTUAL_REGISTRY) return void 0;
676
+ if (businessImporter ? !BUSINESS_DEPENDENCIES.has(dependency) : !Object.hasOwn(args.dependencies, dependency)) return void 0;
677
+ if (!safeBareSpecifier(moduleName, dependency)) return void 0;
678
+ const fromHost = ts2.resolveModuleName(moduleName, workerModulePath, compilerOptions, ts2.sys, resolutionCache).resolvedModule;
679
+ if (fromHost) {
680
+ const roots = [dependencyPackage(dependency).root];
681
+ const typeDependency = dependency === "react" ? "@types/react" : dependency === "react-dom" ? "@types/react-dom" : dependency === "node" ? "@types/node" : void 0;
682
+ if (typeDependency && Object.hasOwn(args.dependencies, typeDependency)) roots.push(dependencyPackage(typeDependency).root);
683
+ const canonical = realpathSync3(fromHost.resolvedFileName);
684
+ if (roots.some((root) => containsPath(root, canonical))) return fromHost;
685
+ return void 0;
686
+ }
687
+ const target = dependencyTypeTarget(moduleName);
688
+ if (target) return { resolvedFileName: target, extension: ts2.Extension.Js };
689
+ }
690
+ const resolvedModule = originalResolve?.(moduleNames, containingFile, reusedNames, redirectedReference, compilerOptions)?.[moduleNames.indexOf(moduleName)] ?? ts2.resolveModuleName(moduleName, containingFile, compilerOptions, ts2.sys, resolutionCache).resolvedModule;
691
+ if (!businessImporter || !resolvedModule) return resolvedModule;
692
+ if (!moduleName.startsWith(".") && !isAbsolute3(moduleName)) return resolvedModule;
693
+ const candidate = resolve3(dirname4(containingFile), moduleName);
694
+ if (!allowedProjectPath(args, candidate) || !existsSync3(resolvedModule.resolvedFileName)) return void 0;
695
+ return allowedProjectPath(args, realpathSync3(resolvedModule.resolvedFileName)) ? resolvedModule : void 0;
696
+ });
697
+ const runtimeRootNames = (await walkBusinessFiles(args.runtimeRoot)).filter((path) => /\.(?:ts|tsx)$/u.test(path));
698
+ const rootNames = [.../* @__PURE__ */ new Set([...businessRootNames, ...runtimeRootNames, declarationFile, ...virtualFiles.keys()])];
699
+ const program = ts2.createProgram({ rootNames, options, host, ...this.#previousProgram ? { oldProgram: this.#previousProgram } : {} });
700
+ this.#previousProgram = program;
701
+ const virtualTargets = new Map([...virtualFiles].map(([path, value]) => [filesystemPathKey(path), value.target]));
702
+ return [
703
+ ...businessWebStorageDiagnostics(args, program, businessRootNames),
704
+ ...ts2.getPreEmitDiagnostics(program).map((diagnostic) => tsDiagnostic(args, diagnostic, virtualTargets))
705
+ ].slice(0, 20);
706
+ }
707
+ };
708
+
709
+ // apps/host/src/runtime-compile-session.ts
710
+ var RuntimeCompileSession = class {
711
+ constructor(publish) {
712
+ this.publish = publish;
713
+ }
714
+ publish;
715
+ #diagnostics = new RuntimeDiagnosticEngine();
716
+ #sourceRevision = 0;
717
+ #successfulSourceRevision;
718
+ #diagnosticsRevision = 0;
719
+ #tail = Promise.resolve();
720
+ get sourceRevision() {
721
+ return this.#sourceRevision;
722
+ }
723
+ get successfulSourceRevision() {
724
+ return this.#successfulSourceRevision;
725
+ }
726
+ async #inspect(args) {
727
+ const revision = ++this.#sourceRevision;
728
+ this.publish(this.#state("compiling", revision, [], false));
729
+ return { revision, diagnostics: await this.#diagnostics.collect(args) };
730
+ }
731
+ #enqueue(operation) {
732
+ const running = this.#tail.then(operation);
733
+ this.#tail = running.catch(() => void 0);
734
+ return running;
735
+ }
736
+ inspectCandidate(args) {
737
+ return this.#enqueue(() => this.#inspect(args));
738
+ }
739
+ compile(args) {
740
+ return this.#enqueue(async () => {
741
+ try {
742
+ const { revision, diagnostics } = await this.#inspect(args);
743
+ return this.finish(revision, diagnostics, !diagnostics.some((diagnostic) => diagnostic.severity === "error"));
744
+ } catch (error) {
745
+ this.reportRuntimeFailure(error);
746
+ throw error;
747
+ }
748
+ });
749
+ }
750
+ /** Only accept after the preview controller successfully publishes its candidate. */
751
+ finish(revision, diagnostics, accepted) {
752
+ if (accepted) this.#successfulSourceRevision = revision;
753
+ const state = this.#state(accepted ? "success" : "failed", revision, diagnostics, true);
754
+ this.publish(state);
755
+ return state;
756
+ }
757
+ reportRuntimeFailure(error) {
758
+ this.finish(++this.#sourceRevision, [{
759
+ severity: "error",
760
+ source: "runtime",
761
+ code: "HARNESS_RUNTIME_INFRASTRUCTURE",
762
+ message: (error instanceof Error ? error.message : String(error)).slice(0, 8e3)
763
+ }], false);
764
+ }
765
+ #state(status, revision, diagnostics, completed) {
766
+ return {
767
+ status,
768
+ sourceRevision: revision,
769
+ ...this.#successfulSourceRevision === void 0 ? {} : { successfulSourceRevision: this.#successfulSourceRevision },
770
+ diagnosticsRevision: completed ? ++this.#diagnosticsRevision : this.#diagnosticsRevision,
771
+ diagnostics,
772
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
773
+ };
774
+ }
775
+ async dispose() {
776
+ await this.#tail;
777
+ this.#diagnostics.dispose();
778
+ }
779
+ };
780
+
781
+ // apps/host/src/runtime-preview-contracts.ts
782
+ import "vite";
783
+ var RESOLVED_REGISTRY = "\0virtual:harness-business-registry";
784
+ var VIRTUAL_BUSINESS_PREFIX = "virtual:harness-business/";
785
+ var RESOLVED_BUSINESS_PREFIX = "/@harness-business/";
786
+ var EVENT_PREFIX = "HARNESS_RUNTIME_EVENT ";
787
+
788
+ // apps/host/src/runtime-export-assets.ts
789
+ import { realpathSync as realpathSync4 } from "node:fs";
790
+ import { copyFile, lstat as lstat3, mkdir, readFile as readFile3, writeFile } from "node:fs/promises";
791
+ import { dirname as dirname5, join as join6, relative as relative4 } from "node:path";
792
+ async function copyGeneratedAssets(args) {
793
+ if (!args.outDir) return;
794
+ const sourceRoot = join6(args.projectRoot, "public", "generated");
795
+ const info = await lstat3(sourceRoot).catch(() => void 0);
796
+ if (!info) return;
797
+ if (!info.isDirectory() || info.isSymbolicLink() || !sameFilesystemPath(sourceRoot, realpathSync4(sourceRoot))) {
798
+ throw new Error("public/generated \u5FC5\u987B\u662F\u672A\u91CD\u5B9A\u5411\u7684\u666E\u901A\u76EE\u5F55");
799
+ }
800
+ const destinationRoot = join6(args.outDir, "generated");
801
+ for (const source of await walkBusinessFiles(sourceRoot)) {
802
+ const canonical = realpathSync4(source);
803
+ if (!containsPath(sourceRoot, canonical) || !sameFilesystemPath(source, canonical)) {
804
+ throw new Error(`\u751F\u6210\u8D44\u6E90\u8D8A\u8FC7\u5141\u8BB8\u8FB9\u754C\uFF1A${normalizedRelative(relative4(sourceRoot, source))}`);
805
+ }
806
+ const destination = join6(destinationRoot, relative4(sourceRoot, source));
807
+ await mkdir(dirname5(destination), { recursive: true });
808
+ await copyFile(source, destination);
809
+ }
810
+ }
811
+ async function rewriteExportedGeneratedCss(args) {
812
+ if (!args.outDir) return;
813
+ const info = await lstat3(args.outDir).catch(() => void 0);
814
+ if (!info?.isDirectory() || info.isSymbolicLink()) return;
815
+ for (const path of await walkBusinessFiles(args.outDir)) {
816
+ if (!path.endsWith(".css")) continue;
817
+ const source = await readFile3(path, "utf8");
818
+ const rewritten = source.replace(
819
+ /url\(\s*(["']?)\/generated\/([^)'"\r\n]+)\1\s*\)/gu,
820
+ (_match, quote, asset) => `url(${quote}../generated/${asset}${quote})`
821
+ );
822
+ if (rewritten !== source) await writeFile(path, rewritten, "utf8");
823
+ }
824
+ }
825
+
826
+ // apps/host/src/runtime-vite-config.ts
827
+ import { existsSync as existsSync6, realpathSync as realpathSync7 } from "node:fs";
828
+ import { join as join9, resolve as resolve7 } from "node:path";
829
+ import react from "@vitejs/plugin-react";
830
+ import { normalizePath as normalizePath2 } from "vite";
831
+
832
+ // apps/host/src/runtime-vite-business.ts
833
+ import { readFile as readFile4 } from "node:fs/promises";
834
+ import { posix as posix2, resolve as resolve4 } from "node:path";
835
+ import "vite";
836
+
837
+ // apps/host/src/preview-jsx-instrumentation.ts
838
+ import { createHash } from "node:crypto";
839
+ import MagicString from "magic-string";
840
+ import ts3 from "typescript";
841
+
842
+ // apps/host/src/preview-source-token.ts
843
+ import { createHmac, timingSafeEqual } from "node:crypto";
844
+ var PREVIEW_SOURCE_ATTRIBUTE = "data-harness-source";
845
+ var PREVIEW_SOURCE_TOKEN_VERSION = 1;
846
+ function validDescriptor(value) {
847
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
848
+ const descriptor = value;
849
+ return descriptor.v === PREVIEW_SOURCE_TOKEN_VERSION && typeof descriptor.path === "string" && descriptor.path.length > 0 && descriptor.path.length <= 1024 && !descriptor.path.startsWith("/") && !descriptor.path.includes("\\") && !descriptor.path.split("/").some((segment) => segment === "" || segment === "." || segment === "..") && typeof descriptor.hash === "string" && /^[a-f0-9]{64}$/u.test(descriptor.hash) && Number.isSafeInteger(descriptor.startLine) && descriptor.startLine > 0 && Number.isSafeInteger(descriptor.endLine) && descriptor.endLine >= descriptor.startLine;
850
+ }
851
+ function signPreviewSourceToken(secret, descriptor) {
852
+ if (!validDescriptor(descriptor) || secret.length < 32) throw new Error("Invalid preview source token input");
853
+ const payload = Buffer.from(JSON.stringify(descriptor), "utf8").toString("base64url");
854
+ const signature = createHmac("sha256", secret).update(payload).digest("base64url");
855
+ return `${payload}.${signature}`;
856
+ }
857
+
858
+ // apps/host/src/preview-jsx-instrumentation.ts
859
+ function instrumentBusinessJsx(source, relativePath, secret) {
860
+ const sourceFile = ts3.createSourceFile(
861
+ relativePath,
862
+ source,
863
+ ts3.ScriptTarget.ESNext,
864
+ true,
865
+ relativePath.toLowerCase().endsWith(".tsx") ? ts3.ScriptKind.TSX : ts3.ScriptKind.JSX
866
+ );
867
+ const hash = createHash("sha256").update(source).digest("hex");
868
+ const magic = new MagicString(source);
869
+ const insertions = [];
870
+ const visit = (node) => {
871
+ if (ts3.isJsxOpeningElement(node) || ts3.isJsxSelfClosingElement(node)) {
872
+ for (const property of node.attributes.properties) {
873
+ if (ts3.isJsxAttribute(property) && property.name.getText(sourceFile) === PREVIEW_SOURCE_ATTRIBUTE) {
874
+ throw new Error(`\u4E1A\u52A1 JSX \u4E0D\u5141\u8BB8\u58F0\u660E\u4FDD\u7559\u5C5E\u6027 ${PREVIEW_SOURCE_ATTRIBUTE}\uFF1A${relativePath}`);
875
+ }
876
+ }
877
+ const rangeNode = ts3.isJsxOpeningElement(node) && ts3.isJsxElement(node.parent) ? node.parent : node;
878
+ const start = sourceFile.getLineAndCharacterOfPosition(rangeNode.getStart(sourceFile));
879
+ const end = sourceFile.getLineAndCharacterOfPosition(Math.max(rangeNode.getStart(sourceFile), rangeNode.getEnd() - 1));
880
+ const token = signPreviewSourceToken(secret, {
881
+ v: 1,
882
+ path: relativePath,
883
+ hash,
884
+ startLine: start.line + 1,
885
+ endLine: end.line + 1
886
+ });
887
+ insertions.push({
888
+ position: node.attributes.end,
889
+ text: ` ${PREVIEW_SOURCE_ATTRIBUTE}=${JSON.stringify(token)}`
890
+ });
891
+ }
892
+ ts3.forEachChild(node, visit);
893
+ };
894
+ visit(sourceFile);
895
+ for (const insertion of insertions.reverse()) magic.appendLeft(insertion.position, insertion.text);
896
+ return {
897
+ code: magic.toString(),
898
+ map: magic.generateMap({ source: relativePath, includeContent: true, hires: true })
899
+ };
900
+ }
901
+
902
+ // apps/host/src/runtime-business-ids.ts
903
+ import { isAbsolute as isAbsolute4 } from "node:path";
904
+ function virtualBusinessRelative(id) {
905
+ const clean = id.split("?", 1)[0];
906
+ const prefix = clean.startsWith(RESOLVED_BUSINESS_PREFIX) ? RESOLVED_BUSINESS_PREFIX : clean.startsWith(VIRTUAL_BUSINESS_PREFIX) ? VIRTUAL_BUSINESS_PREFIX : void 0;
907
+ if (!prefix) return void 0;
908
+ const candidate = normalizedRelative(clean.slice(prefix.length));
909
+ if (!candidate || candidate.startsWith("../") || candidate.includes("/../") || isAbsolute4(candidate)) return void 0;
910
+ return candidate;
911
+ }
912
+ function virtualBusinessId(relativePath) {
913
+ return `${VIRTUAL_BUSINESS_PREFIX}${normalizedRelative(relativePath)}`;
914
+ }
915
+
916
+ // apps/host/src/runtime-vite-business.ts
917
+ function businessSourcePlugin(args, sources) {
918
+ return {
919
+ name: "harness-accepted-business-source",
920
+ enforce: "pre",
921
+ resolveId(source, importer) {
922
+ const direct = virtualBusinessRelative(source);
923
+ if (direct) {
924
+ if (!allowedProjectPath(args, resolve4(args.projectRoot, direct))) throw new Error(`\u4E1A\u52A1\u6A21\u5757\u8D8A\u8FC7\u53EF\u8BFB\u8FB9\u754C\uFF1A${direct}`);
925
+ const resolved2 = businessModuleCandidate(sources.currentRoot, direct);
926
+ if (!resolved2) throw new Error(`\u5DF2\u63A5\u53D7\u7684\u4E1A\u52A1\u6A21\u5757\u4E0D\u5B58\u5728\uFF1A${direct}`);
927
+ return `${RESOLVED_BUSINESS_PREFIX}${resolved2}`;
928
+ }
929
+ const importerPath = importer ? virtualBusinessRelative(importer) : void 0;
930
+ if (!importerPath || !source.startsWith(".")) return null;
931
+ const requested = normalizedRelative(posix2.normalize(posix2.join(posix2.dirname(importerPath), source)));
932
+ if (!requested || requested.startsWith("../") || requested.includes("/../") || !allowedProjectPath(args, resolve4(args.projectRoot, requested))) {
933
+ throw new Error(`\u4E1A\u52A1\u5BFC\u5165\u8D8A\u8FC7\u53EF\u8BFB\u8FB9\u754C\uFF1A${source}`);
934
+ }
935
+ const resolved = businessModuleCandidate(sources.currentRoot, requested);
936
+ if (!resolved) return null;
937
+ return `${RESOLVED_BUSINESS_PREFIX}${resolved}`;
938
+ },
939
+ async load(id) {
940
+ const path = virtualBusinessRelative(id);
941
+ if (!path) return null;
942
+ const source = await readFile4(sources.acceptedPath(path), "utf8");
943
+ const secret = process.env.HARNESS_PREVIEW_SOURCE_TOKEN_SECRET;
944
+ if (!secret || !editableProjectPath(args, path) || !/\.(?:jsx|tsx)$/iu.test(path)) return source;
945
+ return instrumentBusinessJsx(source, path, secret);
946
+ }
947
+ };
948
+ }
949
+
950
+ // apps/host/src/runtime-vite-dependencies.ts
951
+ import { existsSync as existsSync4, lstatSync as lstatSync2, realpathSync as realpathSync5 } from "node:fs";
952
+ import { dirname as dirname7, isAbsolute as isAbsolute5, resolve as resolve5 } from "node:path";
953
+ import ts4 from "typescript";
954
+ import "vite";
955
+ var LAZY_PREBUNDLED_DEPENDENCY_SOURCES = ["@ant-design/charts"];
956
+ function dependencyResolver(args, sources) {
957
+ const sdkEntry = assertRegularContainedFile(args.runtimeRoot, args.runtimeSdkEntry, "Runtime SDK entry");
958
+ return {
959
+ name: "harness-runtime-import-boundary",
960
+ enforce: "pre",
961
+ transform(code, id) {
962
+ const cleanId = id.split("?", 1)[0];
963
+ const virtualPath = virtualBusinessRelative(cleanId);
964
+ const physicalBusiness = containsPath(args.projectRoot, cleanId);
965
+ if (!physicalBusiness && !virtualPath) return null;
966
+ const canonical = virtualPath && sources ? sources.acceptedPath(virtualPath) : realpathSync5(cleanId);
967
+ const relativePath = virtualPath ?? relativeProjectPath(args.projectRoot, canonical);
968
+ if (!relativePath || !allowedProjectPath(args, resolve5(args.projectRoot, relativePath))) {
969
+ throw new Error(`\u4E1A\u52A1\u6A21\u5757 canonical \u8DEF\u5F84\u8D8A\u8FC7\u53EF\u8BFB\u8FB9\u754C\uFF1A${cleanId}`);
970
+ }
971
+ if (/\.css$/iu.test(cleanId)) {
972
+ const violations = cssBoundaryViolations(code);
973
+ if (violations.length > 0) throw new Error(violations[0]);
974
+ return null;
975
+ }
976
+ if (/\.(?:[cm]?[jt]sx?)$/iu.test(cleanId)) {
977
+ const sourceFile = ts4.createSourceFile(
978
+ cleanId,
979
+ code,
980
+ ts4.ScriptTarget.ESNext,
981
+ true,
982
+ /x$/iu.test(cleanId) ? ts4.ScriptKind.TSX : ts4.ScriptKind.TS
983
+ );
984
+ let usesImportMeta = false;
985
+ const visit = (node) => {
986
+ if (ts4.isMetaProperty(node) && node.keywordToken === ts4.SyntaxKind.ImportKeyword) usesImportMeta = true;
987
+ if (!usesImportMeta) ts4.forEachChild(node, visit);
988
+ };
989
+ visit(sourceFile);
990
+ if (usesImportMeta) throw new Error("\u4E1A\u52A1\u4EE3\u7801\u4E0D\u5141\u8BB8\u4F7F\u7528 import.meta\uFF08\u5305\u62EC glob \u4E0E\u57FA\u4E8E URL \u7684\u6587\u4EF6\u8BFB\u53D6\uFF09");
991
+ }
992
+ return null;
993
+ },
994
+ async resolveId(source, importer) {
995
+ const resolveFixedDependency = async (dependency2) => {
996
+ fixedDependencyResolution(source, dependency2);
997
+ const browserSource = browserDependencySource(source, dependency2);
998
+ if (browserSource !== source) fixedDependencyResolution(browserSource, dependency2);
999
+ const resolved = await this.resolve(browserSource, workerModulePath, { skipSelf: true });
1000
+ if (!resolved || resolved.external) throw new Error(`Host \u65E0\u6CD5\u901A\u8FC7 Vite \u89E3\u6790\u56FA\u5B9A\u4F9D\u8D56\uFF1A${source}`);
1001
+ const cleanResolved = resolved.id.split(/[?#]/u, 1)[0];
1002
+ const absoluteResolved = resolve5(cleanResolved);
1003
+ if (containsPath(args.cacheDir, absoluteResolved)) return resolved;
1004
+ let canonical;
1005
+ try {
1006
+ canonical = realpathSync5(absoluteResolved);
1007
+ } catch (error) {
1008
+ throw new Error(`Vite \u8FD4\u56DE\u4E86\u65E0\u6548\u7684\u56FA\u5B9A\u4F9D\u8D56\u8DEF\u5F84\uFF1A${source}`, { cause: error });
1009
+ }
1010
+ if (!containsPath(dependencyPackage(dependency2).root, canonical)) {
1011
+ throw new Error(`Vite \u56FA\u5B9A\u4F9D\u8D56\u89E3\u6790\u8D8A\u8FC7\u5305\u6839\uFF1A${source}`);
1012
+ }
1013
+ return resolved;
1014
+ };
1015
+ const cleanImporter = importer?.split("?", 1)[0];
1016
+ const cleanSource = source.split(/[?#]/u, 1)[0];
1017
+ const virtualImporter = cleanImporter ? virtualBusinessRelative(cleanImporter) : void 0;
1018
+ const businessImporter = Boolean(virtualImporter || cleanImporter && containsPath(args.projectRoot, cleanImporter));
1019
+ const runtimeImporter = cleanImporter ? containsPath(args.runtimeRoot, cleanImporter) : false;
1020
+ if (source === VIRTUAL_REGISTRY) {
1021
+ if (businessImporter) throw new Error("\u4E1A\u52A1\u4EE3\u7801\u4E0D\u80FD\u5BFC\u5165 Runtime \u5185\u90E8\u6CE8\u518C\u8868");
1022
+ return RESOLVED_REGISTRY;
1023
+ }
1024
+ if (source === "@harness/frontend-runtime") return sdkEntry;
1025
+ if (!importer || importer.startsWith("\0") && !virtualImporter) return null;
1026
+ const resolvedImporter = virtualImporter && sources ? sources.acceptedPath(virtualImporter) : cleanImporter;
1027
+ const filesystemAbsoluteSource = /^(?:[a-zA-Z]:[\\/]|[\\/]{2})/u.test(cleanSource) || isAbsolute5(cleanSource) && existsSync4(cleanSource);
1028
+ if (filesystemAbsoluteSource) {
1029
+ let canonical;
1030
+ try {
1031
+ const info = lstatSync2(cleanSource);
1032
+ canonical = realpathSync5(cleanSource);
1033
+ if (!info.isFile() || info.isSymbolicLink() || !sameFilesystemPath(cleanSource, canonical)) {
1034
+ throw new Error("module path is redirected");
1035
+ }
1036
+ } catch (error) {
1037
+ throw new Error(`\u7EDD\u5BF9\u6A21\u5757\u8DEF\u5F84\u65E0\u6548\uFF1A${source}`, { cause: error });
1038
+ }
1039
+ const fixedAliasedDependency = LAZY_PREBUNDLED_DEPENDENCY_SOURCES.some((dependency2) => containsPath(dependencyPackage(dependency2).root, canonical));
1040
+ if ((businessImporter || runtimeImporter) && fixedAliasedDependency) {
1041
+ return null;
1042
+ }
1043
+ if (businessImporter) {
1044
+ const acceptedRelative = sources ? relativeProjectPath(sources.currentRoot, canonical) : void 0;
1045
+ if (!(acceptedRelative && allowedProjectPath(args, resolve5(args.projectRoot, acceptedRelative))) && !allowedProjectPath(args, canonical)) throw new Error(`\u4E1A\u52A1\u7EDD\u5BF9\u5BFC\u5165\u8D8A\u8FC7\u53EF\u8BFB\u8FB9\u754C\uFF1A${source}`);
1046
+ return null;
1047
+ }
1048
+ if (runtimeImporter) {
1049
+ if (!containsPath(args.runtimeRoot, canonical)) throw new Error(`Runtime \u7EDD\u5BF9\u5BFC\u5165\u8D8A\u8FC7\u8FD0\u884C\u65F6\u6839\uFF1A${source}`);
1050
+ return null;
1051
+ }
1052
+ return null;
1053
+ }
1054
+ if (!businessImporter && !runtimeImporter) {
1055
+ if (!source.startsWith(".") && !source.startsWith("/") && !source.startsWith("\0") && !/^(?:node:|https?:|data:)/iu.test(source)) {
1056
+ const dependency2 = packageName(source);
1057
+ if (Object.hasOwn(args.dependencies, dependency2)) return await resolveFixedDependency(dependency2);
1058
+ }
1059
+ return null;
1060
+ }
1061
+ if (runtimeImporter) {
1062
+ if (source === "vite/modulepreload-polyfill") return null;
1063
+ if (source.startsWith(".") || source.startsWith("/") || source.startsWith("\0")) return null;
1064
+ const dependency2 = packageName(source);
1065
+ if (!Object.hasOwn(args.dependencies, dependency2)) throw new Error(`Runtime \u4F7F\u7528\u4E86\u672A\u56FA\u5B9A\u4F9D\u8D56\uFF1A${dependency2}`);
1066
+ return await resolveFixedDependency(dependency2);
1067
+ }
1068
+ if (/^(?:node:|https?:|data:)/iu.test(source)) throw new Error(`\u5FEB\u901F\u524D\u53F0\u4E0D\u5141\u8BB8\u5BFC\u5165\uFF1A${source}`);
1069
+ if (source.startsWith(".") || source.startsWith("/")) {
1070
+ if (source.startsWith("/")) throw new Error(`\u4E1A\u52A1\u4EE3\u7801\u4E0D\u80FD\u5BFC\u5165 Runtime \u7EDD\u5BF9\u8DEF\u5F84\uFF1A${source}`);
1071
+ const candidate = resolve5(dirname7(resolvedImporter), source);
1072
+ const candidateRelative = sources ? relativeProjectPath(sources.currentRoot, candidate) : void 0;
1073
+ if (!(candidateRelative && allowedProjectPath(args, resolve5(args.projectRoot, candidateRelative))) && !allowedProjectPath(args, candidate)) throw new Error(`\u4E1A\u52A1\u5BFC\u5165\u8D8A\u8FC7\u53EF\u8BFB\u8FB9\u754C\uFF1A${source}`);
1074
+ const resolvedImport = await this.resolve(source, importer, { skipSelf: true });
1075
+ if (!resolvedImport || resolvedImport.external) return resolvedImport;
1076
+ if (virtualBusinessRelative(resolvedImport.id)) return resolvedImport;
1077
+ const resolvedPath = resolvedImport.id.split("?", 1)[0];
1078
+ const canonical = realpathSync5(resolvedPath);
1079
+ if (!allowedProjectPath(args, canonical)) throw new Error(`\u4E1A\u52A1\u5BFC\u5165 canonical \u8DEF\u5F84\u8D8A\u8FC7\u53EF\u8BFB\u8FB9\u754C\uFF1A${source}`);
1080
+ return resolvedImport;
1081
+ }
1082
+ const dependency = packageName(source);
1083
+ if (!BUSINESS_DEPENDENCIES.has(dependency)) throw new Error(`Runtime \u672A\u5411\u4E1A\u52A1\u4EE3\u7801\u516C\u5F00\u4F9D\u8D56\uFF1A${dependency}`);
1084
+ try {
1085
+ return await resolveFixedDependency(dependency);
1086
+ } catch (error) {
1087
+ throw new Error(`Host \u65E0\u6CD5\u89E3\u6790\u56FA\u5B9A\u4F9D\u8D56\uFF1A${source}`, { cause: error });
1088
+ }
1089
+ }
1090
+ };
1091
+ }
1092
+
1093
+ // apps/host/src/runtime-vite-registry.ts
1094
+ import { createReadStream, existsSync as existsSync5, lstatSync as lstatSync3, realpathSync as realpathSync6 } from "node:fs";
1095
+ import { extname as extname2, join as join8, resolve as resolve6 } from "node:path";
1096
+ import { normalizePath } from "vite";
1097
+ var GENERATED_CONTENT_TYPES = {
1098
+ ".gif": "image/gif",
1099
+ ".jpeg": "image/jpeg",
1100
+ ".jpg": "image/jpeg",
1101
+ ".json": "application/json; charset=utf-8",
1102
+ ".png": "image/png",
1103
+ ".svg": "image/svg+xml; charset=utf-8",
1104
+ ".txt": "text/plain; charset=utf-8",
1105
+ ".webp": "image/webp",
1106
+ ".woff": "font/woff",
1107
+ ".woff2": "font/woff2"
1108
+ };
1109
+ function registryPlugin(args, sources) {
1110
+ let server;
1111
+ return {
1112
+ name: "harness-business-registry",
1113
+ resolveId(source) {
1114
+ return source === VIRTUAL_REGISTRY ? RESOLVED_REGISTRY : null;
1115
+ },
1116
+ async load(id) {
1117
+ if (id !== RESOLVED_REGISTRY) return null;
1118
+ const sourceArgs = sources ? { ...args, projectRoot: sources.currentRoot } : args;
1119
+ const registry = await routeRegistry(sourceArgs);
1120
+ const app = await appManifest(sourceArgs);
1121
+ const providerPath = join8(sourceArgs.projectRoot, "src", "app", "providers.tsx");
1122
+ const stylesPath = join8(sourceArgs.projectRoot, "src", "styles.css");
1123
+ const routeSource = registry.routes.map((route) => `{ metadata: ${JSON.stringify(route.metadata)}, Component: lazy(() => import(${JSON.stringify(
1124
+ sources ? virtualBusinessId(relativeProjectPath(sourceArgs.projectRoot, route.pagePath)) : normalizePath(route.pagePath)
1125
+ )}).then((module) => ({ default: module.default }))) }`);
1126
+ return [
1127
+ `import { lazy } from "react";`,
1128
+ ...existsSync5(providerPath) ? [`const BusinessProvider = lazy(() => import(${JSON.stringify(
1129
+ sources ? virtualBusinessId("src/app/providers.tsx") : normalizePath(providerPath)
1130
+ )}).then((module) => ({ default: module.default })));`] : [],
1131
+ ...existsSync5(stylesPath) ? [`import ${JSON.stringify(
1132
+ sources ? virtualBusinessId("src/styles.css") : normalizePath(stylesPath)
1133
+ )};`] : [],
1134
+ `export const routes = [${routeSource.join(",\n")}];`,
1135
+ `export const appManifest = ${JSON.stringify(app.manifest)};`,
1136
+ existsSync5(providerPath) ? `export const providers = [BusinessProvider];` : `export const providers = [];`,
1137
+ `export const stylesUrl = undefined;`
1138
+ ].join("\n");
1139
+ },
1140
+ configureServer(viteServer) {
1141
+ server = viteServer;
1142
+ sources?.bindServer(viteServer);
1143
+ viteServer.watcher.add(args.projectRoot);
1144
+ viteServer.watcher.on("all", (event, path) => {
1145
+ if (!allowedProjectPath(args, resolve6(path))) return;
1146
+ sources?.recordFileChange(event, resolve6(path));
1147
+ });
1148
+ viteServer.middlewares.use((request, response, next) => {
1149
+ const pathname = new URL(request.url ?? "/", "http://127.0.0.1").pathname;
1150
+ if (!pathname.startsWith("/generated/")) {
1151
+ try {
1152
+ const requested = decodeURIComponent(pathname.replace(/^\/+/, ""));
1153
+ const publicRoot = join8(args.projectRoot, "public");
1154
+ const unmanaged = resolve6(publicRoot, requested);
1155
+ if (requested && !requested.includes("\\") && containsPath(publicRoot, unmanaged) && existsSync5(unmanaged)) {
1156
+ response.statusCode = 404;
1157
+ response.end("Not found");
1158
+ return;
1159
+ }
1160
+ } catch {
1161
+ response.statusCode = 404;
1162
+ response.end("Not found");
1163
+ return;
1164
+ }
1165
+ next();
1166
+ return;
1167
+ }
1168
+ try {
1169
+ const sourceRoot = join8(sources?.currentRoot ?? args.projectRoot, "public", "generated");
1170
+ if (!sameFilesystemPath(sourceRoot, realpathSync6(sourceRoot))) throw new Error("generated root redirected");
1171
+ const requested = decodeURIComponent(pathname.slice("/generated/".length));
1172
+ if (!requested || requested.includes("\\")) throw new Error("invalid generated path");
1173
+ const source = resolve6(sourceRoot, requested);
1174
+ const info = lstatSync3(source);
1175
+ const canonical = realpathSync6(source);
1176
+ if (!info.isFile() || info.isSymbolicLink() || !containsPath(sourceRoot, source) || !containsPath(sourceRoot, canonical) || !sameFilesystemPath(source, canonical)) {
1177
+ throw new Error("generated asset escaped its root");
1178
+ }
1179
+ response.statusCode = 200;
1180
+ response.setHeader("Cache-Control", "no-store");
1181
+ response.setHeader("Content-Type", GENERATED_CONTENT_TYPES[extname2(source).toLowerCase()] ?? "application/octet-stream");
1182
+ response.setHeader("X-Content-Type-Options", "nosniff");
1183
+ createReadStream(source).once("error", next).pipe(response);
1184
+ } catch {
1185
+ response.statusCode = 404;
1186
+ response.end("Not found");
1187
+ }
1188
+ });
1189
+ },
1190
+ closeBundle() {
1191
+ void server;
1192
+ }
1193
+ };
1194
+ }
1195
+ function runtimeStorageBootstrapPlugin() {
1196
+ return {
1197
+ name: "harness-runtime-storage-bootstrap",
1198
+ transformIndexHtml: {
1199
+ order: "pre",
1200
+ handler() {
1201
+ return [{
1202
+ tag: "script",
1203
+ injectTo: "head-prepend",
1204
+ children: [
1205
+ "(() => {",
1206
+ " const channelId = new URL(window.location.href).searchParams.get('__harness_runtime_storage_channel');",
1207
+ " const announce = () => {",
1208
+ " if (channelId && channelId.length <= 128) {",
1209
+ " window.parent.postMessage({ type: 'harness.runtime-storage.ready', version: 1, channelId }, '*');",
1210
+ " }",
1211
+ " };",
1212
+ " announce();",
1213
+ " window.addEventListener('load', announce, { once: true });",
1214
+ "})();"
1215
+ ].join("\n")
1216
+ }, {
1217
+ tag: "script",
1218
+ attrs: { type: "module", src: "/src/inspector/index.ts" },
1219
+ injectTo: "head"
1220
+ }];
1221
+ }
1222
+ }
1223
+ };
1224
+ }
1225
+
1226
+ // apps/host/src/runtime-vite-config.ts
1227
+ function config(args, sources) {
1228
+ assertRegularContainedFile(args.runtimeRoot, args.runtimeEntry, "Runtime entry");
1229
+ const dependencyRoots = verifyDependencies(args);
1230
+ return {
1231
+ root: args.runtimeRoot,
1232
+ configFile: false,
1233
+ appType: "spa",
1234
+ base: args.mode === "export" ? "./" : "/",
1235
+ cacheDir: args.cacheDir,
1236
+ publicDir: false,
1237
+ plugins: [
1238
+ ...sources ? [runtimeStorageBootstrapPlugin()] : [],
1239
+ ...sources ? [businessSourcePlugin(args, sources)] : [],
1240
+ dependencyResolver(args, sources),
1241
+ registryPlugin(args, sources),
1242
+ react()
1243
+ ],
1244
+ resolve: {
1245
+ // Vite resolves optimizeDeps.include from the Runtime root before user
1246
+ // plugins run. Exact aliases let that resolver reach Host-pinned package
1247
+ // entries without making arbitrary package subpaths available.
1248
+ alias: [
1249
+ ...LAZY_PREBUNDLED_DEPENDENCY_SOURCES.map((source) => ({
1250
+ find: exactSpecifierPattern(source),
1251
+ replacement: normalizePath2(fixedDependencyResolution(source, packageName(source)))
1252
+ })),
1253
+ {
1254
+ find: exactSpecifierPattern("tslib"),
1255
+ replacement: normalizePath2(requireFromHost.resolve("tslib/tslib.es6.mjs"))
1256
+ }
1257
+ ],
1258
+ dedupe: ["react", "react-dom", "react-router", "react-router-dom", "antd", "@ant-design/icons"]
1259
+ },
1260
+ // Business pages are exposed through a generated registry of lazy imports, so Vite's
1261
+ // initial HTML crawl cannot see the chart package. Pre-bundle that heavy dependency up
1262
+ // front; otherwise its first request can restart the optimizer while React.lazy is
1263
+ // fetching the page module and leave the browser holding a rejected dynamic import.
1264
+ optimizeDeps: {
1265
+ include: [...LAZY_PREBUNDLED_DEPENDENCY_SOURCES],
1266
+ rolldownOptions: {
1267
+ plugins: [
1268
+ {
1269
+ name: "harness-runtime-tslib-optimizer-resolution",
1270
+ resolveId(source) {
1271
+ if (source === "tslib") {
1272
+ return normalizePath2(requireFromHost.resolve("tslib/tslib.es6.mjs"));
1273
+ }
1274
+ return null;
1275
+ }
1276
+ }
1277
+ ]
1278
+ }
1279
+ },
1280
+ server: {
1281
+ host: args.host,
1282
+ port: args.port,
1283
+ strictPort: args.strictPort,
1284
+ headers: {
1285
+ // The workspace deliberately embeds the preview without
1286
+ // `allow-same-origin`, so every module request originates from the
1287
+ // opaque `null` origin. Keep the iframe isolated while allowing that
1288
+ // exact origin to load Vite's ESM graph.
1289
+ "Access-Control-Allow-Origin": "null",
1290
+ "Referrer-Policy": "no-referrer",
1291
+ "Content-Security-Policy": "default-src 'self'; base-uri 'none'; object-src 'none'; frame-src 'none'; form-action 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' ws://127.0.0.1:* ws://localhost:*"
1292
+ },
1293
+ fs: {
1294
+ strict: true,
1295
+ allow: [
1296
+ args.runtimeRoot,
1297
+ ...sources ? [] : args.readablePaths.flatMap((path) => {
1298
+ const candidate = resolve7(args.projectRoot, normalizedRelative(path.replace(/\/\*\*$/u, "")));
1299
+ return existsSync6(candidate) ? [realpathSync7(candidate)] : [];
1300
+ }),
1301
+ ...dependencyRoots
1302
+ ]
1303
+ }
1304
+ },
1305
+ build: {
1306
+ ...args.outDir ? { outDir: args.outDir } : {},
1307
+ emptyOutDir: false,
1308
+ sourcemap: args.mode !== "export",
1309
+ rollupOptions: { input: join9(args.runtimeRoot, "index.html") }
1310
+ }
1311
+ };
1312
+ }
1313
+
1314
+ // apps/host/src/runtime-worker-events.ts
1315
+ function publishPreviewUpdate(kind, paths, sourceRevision) {
1316
+ process.stdout.write(`${EVENT_PREFIX}${JSON.stringify({ type: "preview-update", kind, sourceRevision, paths: paths.slice(0, 100) })}
1317
+ `);
1318
+ }
1319
+
1320
+ // apps/host/src/runtime-preview-source.ts
1321
+ import { createHash as createHash3 } from "node:crypto";
1322
+ import { lstatSync as lstatSync4, realpathSync as realpathSync8 } from "node:fs";
1323
+ import { mkdir as mkdir3, readFile as readFile6 } from "node:fs/promises";
1324
+ import { join as join11, resolve as resolve9 } from "node:path";
1325
+ import "vite";
1326
+
1327
+ // apps/host/src/runtime-source-snapshot.ts
1328
+ import { createHash as createHash2, randomUUID } from "node:crypto";
1329
+ import { copyFile as copyFile2, lstat as lstat4, mkdir as mkdir2, readFile as readFile5, readdir as readdir2, rename, rm } from "node:fs/promises";
1330
+ import { dirname as dirname8, join as join10, relative as relative5, resolve as resolve8 } from "node:path";
1331
+ var MAX_PREVIEW_SOURCE_FILES = 2048;
1332
+ var MAX_PREVIEW_SOURCE_BYTES = 32 * 1024 * 1024;
1333
+ var MAX_PREVIEW_SNAPSHOTS = 8;
1334
+ async function businessSnapshotFiles(args, root) {
1335
+ const paths = /* @__PURE__ */ new Set();
1336
+ const roots = [...args.readablePaths, ...args.editablePaths].map((pattern) => normalizedRelative(pattern.replace(/\/\*\*$/u, ""))).filter((path) => path && path !== "README.md" && !path.startsWith(".harness/"));
1337
+ for (const relativeRoot of [...new Set(roots)]) {
1338
+ const absoluteRoot = resolve8(root, relativeRoot);
1339
+ if (!containsPath(root, absoluteRoot)) throw new Error("\u4E1A\u52A1\u5FEB\u7167\u8DEF\u5F84\u8D8A\u8FC7\u5DE5\u4F5C\u533A");
1340
+ const info = await lstat4(absoluteRoot).catch(() => void 0);
1341
+ if (!info) continue;
1342
+ if (info.isSymbolicLink()) throw new Error(`\u4E1A\u52A1\u6E90\u7801\u4E0D\u80FD\u5305\u542B\u7B26\u53F7\u94FE\u63A5/junction\uFF1A${relativeRoot}`);
1343
+ if (info.isFile()) {
1344
+ paths.add(normalizedRelative(relative5(root, absoluteRoot)));
1345
+ continue;
1346
+ }
1347
+ if (!info.isDirectory()) throw new Error(`\u4E1A\u52A1\u6E90\u7801\u5FC5\u987B\u662F\u666E\u901A\u6587\u4EF6\u6216\u76EE\u5F55\uFF1A${relativeRoot}`);
1348
+ for (const file of await walkBusinessFiles(absoluteRoot)) {
1349
+ paths.add(normalizedRelative(relative5(root, file)));
1350
+ }
1351
+ }
1352
+ return [...paths].sort((left, right) => left.localeCompare(right));
1353
+ }
1354
+ async function businessSnapshotHash(args, root, files) {
1355
+ const selected = files ?? await businessSnapshotFiles(args, root);
1356
+ const hash = createHash2("sha256");
1357
+ for (const path of selected) {
1358
+ hash.update(path);
1359
+ hash.update("\0");
1360
+ hash.update(await readFile5(join10(root, path)));
1361
+ hash.update("\0");
1362
+ }
1363
+ return hash.digest("hex");
1364
+ }
1365
+ function transientCaptureError(error) {
1366
+ const code = typeof error === "object" && error !== null && "code" in error ? String(error.code ?? "") : "";
1367
+ return code === "ENOENT" || code === "ENOTDIR" || code === "EBUSY" || code === "EPERM" || code === "EACCES";
1368
+ }
1369
+ async function captureBusinessCandidateAttempt(args, candidatesRoot) {
1370
+ if (process.env.NODE_ENV === "test" && process.env.HARNESS_TEST_PREVIEW_CAPTURE_ALWAYS_FAIL === "1") {
1371
+ throw Object.assign(new Error("Injected transient preview capture failure"), { code: "ENOENT" });
1372
+ }
1373
+ const files = await businessSnapshotFiles(args, args.projectRoot);
1374
+ if (files.length > MAX_PREVIEW_SOURCE_FILES) {
1375
+ throw new Error(`\u4E1A\u52A1\u9884\u89C8\u6E90\u7801\u8D85\u8FC7 ${MAX_PREVIEW_SOURCE_FILES} \u4E2A\u6587\u4EF6\u7684\u5B89\u5168\u4E0A\u9650`);
1376
+ }
1377
+ let totalBytes = 0;
1378
+ for (const path of files) {
1379
+ totalBytes += (await lstat4(join10(args.projectRoot, path))).size;
1380
+ if (totalBytes > MAX_PREVIEW_SOURCE_BYTES) {
1381
+ throw new Error(`\u4E1A\u52A1\u9884\u89C8\u6E90\u7801\u8D85\u8FC7 ${MAX_PREVIEW_SOURCE_BYTES / 1024 / 1024} MiB \u7684\u5B89\u5168\u4E0A\u9650`);
1382
+ }
1383
+ }
1384
+ const beforeHash = await businessSnapshotHash(args, args.projectRoot, files);
1385
+ const capturePauseMs = process.env.NODE_ENV === "test" ? Number.parseInt(process.env.HARNESS_TEST_PREVIEW_CAPTURE_PAUSE_AFTER_HASH_MS ?? "0", 10) : 0;
1386
+ if (Number.isSafeInteger(capturePauseMs) && capturePauseMs > 0 && capturePauseMs <= 1e3) {
1387
+ process.stdout.write(`${EVENT_PREFIX}${JSON.stringify({ type: "capture-test-window", hash: beforeHash })}
1388
+ `);
1389
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, capturePauseMs));
1390
+ }
1391
+ const candidateRoot = join10(candidatesRoot, beforeHash);
1392
+ const existing = await lstat4(candidateRoot).catch(() => void 0);
1393
+ if (existing?.isDirectory() && !existing.isSymbolicLink()) {
1394
+ const existingHash = await businessSnapshotHash(args, candidateRoot);
1395
+ const afterHash = await businessSnapshotHash(args, args.projectRoot);
1396
+ if (existingHash === beforeHash && afterHash === beforeHash) return { root: candidateRoot, hash: beforeHash, files };
1397
+ }
1398
+ const stagingRoot = join10(candidatesRoot, `.staging-${randomUUID()}`);
1399
+ try {
1400
+ await mkdir2(stagingRoot, { recursive: false });
1401
+ for (const path of files) {
1402
+ const destination = join10(stagingRoot, path);
1403
+ await mkdir2(dirname8(destination), { recursive: true });
1404
+ await copyFile2(join10(args.projectRoot, path), destination);
1405
+ }
1406
+ const [snapshotHash, afterHash] = await Promise.all([
1407
+ businessSnapshotHash(args, stagingRoot),
1408
+ businessSnapshotHash(args, args.projectRoot)
1409
+ ]);
1410
+ if (snapshotHash !== beforeHash || afterHash !== beforeHash) return void 0;
1411
+ try {
1412
+ await rename(stagingRoot, candidateRoot);
1413
+ } catch (error) {
1414
+ const adopted = await lstat4(candidateRoot).catch(() => void 0);
1415
+ if (!adopted?.isDirectory() || adopted.isSymbolicLink() || await businessSnapshotHash(args, candidateRoot) !== beforeHash) throw error;
1416
+ }
1417
+ return { root: candidateRoot, hash: beforeHash, files };
1418
+ } finally {
1419
+ await rm(stagingRoot, { recursive: true, force: true }).catch(() => void 0);
1420
+ }
1421
+ }
1422
+ async function captureBusinessCandidate(args) {
1423
+ const candidatesRoot = join10(args.cacheDir, "preview-sources");
1424
+ await mkdir2(candidatesRoot, { recursive: true });
1425
+ let lastTransientError;
1426
+ for (let attempt = 0; attempt < 3; attempt += 1) {
1427
+ try {
1428
+ const candidate = await captureBusinessCandidateAttempt(args, candidatesRoot);
1429
+ if (candidate) return candidate;
1430
+ } catch (error) {
1431
+ if (!transientCaptureError(error)) throw error;
1432
+ lastTransientError = error;
1433
+ }
1434
+ }
1435
+ const detail = lastTransientError instanceof Error ? `\uFF1A${lastTransientError.message}` : "";
1436
+ throw new Error(`\u6355\u83B7\u4E1A\u52A1\u6E90\u7801\u671F\u95F4\u6587\u4EF6\u6301\u7EED\u53D8\u5316\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5${detail}`, { cause: lastTransientError });
1437
+ }
1438
+ async function pruneBusinessCandidates(cacheDir, keep) {
1439
+ const root = join10(cacheDir, "preview-sources");
1440
+ const entries = await readdir2(root, { withFileTypes: true }).catch(() => []);
1441
+ const candidates = [];
1442
+ for (const entry of entries) {
1443
+ if (!entry.isDirectory() || entry.isSymbolicLink() || !/^[0-9a-f]{64}$/u.test(entry.name)) continue;
1444
+ const path = join10(root, entry.name);
1445
+ if (keep.has(path)) continue;
1446
+ const info = await lstat4(path).catch(() => void 0);
1447
+ if (info?.isDirectory() && !info.isSymbolicLink()) candidates.push({ path, updatedAt: info.mtimeMs });
1448
+ }
1449
+ candidates.sort((left, right) => right.updatedAt - left.updatedAt);
1450
+ for (const candidate of candidates.slice(Math.max(0, MAX_PREVIEW_SNAPSHOTS - keep.size))) {
1451
+ await rm(candidate.path, { recursive: true, force: true }).catch(() => void 0);
1452
+ }
1453
+ }
1454
+
1455
+ // apps/host/src/runtime-preview-source.ts
1456
+ var PreviewSourceController = class _PreviewSourceController {
1457
+ constructor(args, emptyRoot, compilation2) {
1458
+ this.args = args;
1459
+ this.compilation = compilation2;
1460
+ this.workingRoot = args.projectRoot;
1461
+ this.#currentRoot = emptyRoot;
1462
+ }
1463
+ args;
1464
+ compilation;
1465
+ workingRoot;
1466
+ #currentRoot;
1467
+ #server;
1468
+ #timer;
1469
+ #batchDepth = 0;
1470
+ #epoch = 0;
1471
+ #lastControlSequence = 0;
1472
+ #lastObservedHash;
1473
+ #acceptedFiles = /* @__PURE__ */ new Map();
1474
+ #pendingEvents = /* @__PURE__ */ new Map();
1475
+ #refreshTail = Promise.resolve();
1476
+ #disposed = false;
1477
+ async dispose() {
1478
+ this.#disposed = true;
1479
+ if (this.#timer) clearTimeout(this.#timer);
1480
+ this.#timer = void 0;
1481
+ await this.#refreshTail;
1482
+ }
1483
+ static async create(args, compilation2) {
1484
+ const emptyHash = createHash3("sha256").digest("hex");
1485
+ const emptyRoot = join11(args.cacheDir, "preview-sources", emptyHash);
1486
+ await mkdir3(emptyRoot, { recursive: true });
1487
+ return new _PreviewSourceController(args, emptyRoot, compilation2);
1488
+ }
1489
+ get currentRoot() {
1490
+ return this.#currentRoot;
1491
+ }
1492
+ get epoch() {
1493
+ return this.#epoch;
1494
+ }
1495
+ acceptedPath(relativePath) {
1496
+ const path = resolve9(this.#currentRoot, normalizedRelative(relativePath));
1497
+ if (!containsPath(this.#currentRoot, path)) throw new Error("\u4E1A\u52A1\u6A21\u5757\u8D8A\u8FC7\u5DF2\u63A5\u53D7\u5FEB\u7167");
1498
+ const info = lstatSync4(path);
1499
+ const canonical = realpathSync8(path);
1500
+ if (!info.isFile() || info.isSymbolicLink() || !containsPath(this.#currentRoot, canonical) || !sameFilesystemPath(path, canonical)) throw new Error("\u4E1A\u52A1\u6A21\u5757\u4E0D\u662F\u5DF2\u63A5\u53D7\u5FEB\u7167\u4E2D\u7684\u666E\u901A\u6587\u4EF6");
1501
+ return path;
1502
+ }
1503
+ bindServer(server) {
1504
+ this.#server = server;
1505
+ }
1506
+ recordFileChange(event, absolutePath) {
1507
+ const path = relativeProjectPath(this.args.projectRoot, absolutePath);
1508
+ if (!path) return;
1509
+ this.#pendingEvents.set(path, event);
1510
+ if (this.#batchDepth === 0) this.scheduleExternalChange();
1511
+ }
1512
+ scheduleExternalChange() {
1513
+ this.#schedule(250);
1514
+ }
1515
+ handleControl(message) {
1516
+ if (!Number.isSafeInteger(message.epoch) || message.epoch <= 0 || !Number.isSafeInteger(message.sequence) || message.sequence <= 0) return;
1517
+ if (message.epoch < this.#epoch || message.epoch === this.#epoch && message.sequence <= this.#lastControlSequence) return;
1518
+ if (message.epoch > this.#epoch) {
1519
+ this.#epoch = message.epoch;
1520
+ this.#lastControlSequence = 0;
1521
+ this.#batchDepth = 0;
1522
+ }
1523
+ this.#lastControlSequence = message.sequence;
1524
+ if (message.type === "batch.begin") {
1525
+ this.#batchDepth += 1;
1526
+ if (this.#timer) clearTimeout(this.#timer);
1527
+ this.#timer = void 0;
1528
+ return;
1529
+ }
1530
+ if (message.type === "batch.end") this.#batchDepth = Math.max(0, this.#batchDepth - 1);
1531
+ else this.#batchDepth = 0;
1532
+ if (this.#batchDepth === 0) this.#schedule(0);
1533
+ }
1534
+ refresh() {
1535
+ if (this.#disposed) return Promise.resolve();
1536
+ const running = this.#refreshTail.then(async () => {
1537
+ try {
1538
+ await this.#refreshNow();
1539
+ } catch (error) {
1540
+ this.#pendingEvents.clear();
1541
+ this.compilation.reportRuntimeFailure(error);
1542
+ }
1543
+ });
1544
+ this.#refreshTail = running;
1545
+ return running;
1546
+ }
1547
+ #schedule(delayMs) {
1548
+ if (this.#disposed || this.#batchDepth > 0) return;
1549
+ if (this.#timer) clearTimeout(this.#timer);
1550
+ this.#timer = setTimeout(() => {
1551
+ this.#timer = void 0;
1552
+ void this.refresh().catch((error) => {
1553
+ this.compilation.finish(this.compilation.sourceRevision, [{ severity: "error", source: "runtime", message: (error instanceof Error ? error.message : String(error)).slice(0, 8e3) }], false);
1554
+ });
1555
+ }, delayMs);
1556
+ this.#timer.unref();
1557
+ }
1558
+ async #refreshNow() {
1559
+ const candidate = await captureBusinessCandidate(this.args);
1560
+ if (candidate.hash === this.#lastObservedHash) {
1561
+ this.#pendingEvents.clear();
1562
+ return;
1563
+ }
1564
+ this.#lastObservedHash = candidate.hash;
1565
+ const { revision, diagnostics } = await this.compilation.inspectCandidate({ ...this.args, projectRoot: candidate.root });
1566
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
1567
+ this.compilation.finish(revision, diagnostics, false);
1568
+ await pruneBusinessCandidates(this.args.cacheDir, /* @__PURE__ */ new Set([this.#currentRoot, candidate.root]));
1569
+ this.#pendingEvents.clear();
1570
+ return;
1571
+ }
1572
+ const previousFiles = this.#acceptedFiles;
1573
+ const nextFiles = /* @__PURE__ */ new Map();
1574
+ for (const path of candidate.files) {
1575
+ nextFiles.set(path, createHash3("sha256").update(await readFile6(join11(candidate.root, path))).digest("hex"));
1576
+ }
1577
+ const changed = [.../* @__PURE__ */ new Set([...previousFiles.keys(), ...nextFiles.keys()])].filter((path) => previousFiles.get(path) !== nextFiles.get(path));
1578
+ const fileSetChanged = changed.some((path) => previousFiles.has(path) !== nextFiles.has(path));
1579
+ const hadAcceptedSource = this.compilation.successfulSourceRevision !== void 0;
1580
+ const previousRoot = this.#currentRoot;
1581
+ this.#currentRoot = candidate.root;
1582
+ this.#acceptedFiles = nextFiles;
1583
+ try {
1584
+ await this.#publishAcceptedChange(changed, hadAcceptedSource, fileSetChanged);
1585
+ } catch (error) {
1586
+ this.#currentRoot = previousRoot;
1587
+ this.#acceptedFiles = previousFiles;
1588
+ try {
1589
+ this.#server?.ws.send({ type: "full-reload" });
1590
+ } catch {
1591
+ }
1592
+ this.compilation.finish(revision, [{
1593
+ severity: "error",
1594
+ source: "runtime",
1595
+ code: "HARNESS_HMR_PUBLICATION",
1596
+ message: `Vite \u65E0\u6CD5\u8F7D\u5165\u5019\u9009\u7248\u672C\uFF0C\u5DF2\u6062\u590D\u6700\u8FD1\u53EF\u7528\u9884\u89C8\uFF1A${error instanceof Error ? error.message : String(error)}`.slice(0, 1e3)
1597
+ }], false);
1598
+ await pruneBusinessCandidates(this.args.cacheDir, /* @__PURE__ */ new Set([previousRoot, candidate.root]));
1599
+ this.#pendingEvents.clear();
1600
+ return;
1601
+ }
1602
+ this.compilation.finish(revision, diagnostics, true);
1603
+ await pruneBusinessCandidates(this.args.cacheDir, /* @__PURE__ */ new Set([candidate.root]));
1604
+ this.#pendingEvents.clear();
1605
+ }
1606
+ async #publishAcceptedChange(changed, hadAcceptedSource, fileSetChanged) {
1607
+ const server = this.#server;
1608
+ if (!server || changed.length === 0) return;
1609
+ if (process.env.NODE_ENV === "test" && process.env.HARNESS_TEST_PREVIEW_PUBLICATION_FAILURE_REVISION === String(this.compilation.sourceRevision)) {
1610
+ throw new Error("Injected preview publication failure");
1611
+ }
1612
+ const structural = !hadAcceptedSource || fileSetChanged || changed.some((path) => /(?:^|\/)(?:route|app)\.json$/u.test(path) || /(?:^|\/)providers\.tsx$/u.test(path) || path.startsWith("public/generated/") || this.#pendingEvents.get(path) === "add" || this.#pendingEvents.get(path) === "unlink");
1613
+ const registry = server.moduleGraph.getModuleById(RESOLVED_REGISTRY);
1614
+ if (registry && changed.some((path) => /(?:^|\/)(?:route|app)\.json$/u.test(path) || /(?:^|\/)providers\.tsx$/u.test(path))) server.moduleGraph.invalidateModule(registry);
1615
+ if (structural) {
1616
+ for (const path of changed) {
1617
+ const module = server.moduleGraph.getModuleById(`${RESOLVED_BUSINESS_PREFIX}${path}`);
1618
+ if (module) server.moduleGraph.invalidateModule(module);
1619
+ }
1620
+ server.ws.send({ type: "full-reload" });
1621
+ publishPreviewUpdate("full-reload", changed, this.compilation.sourceRevision);
1622
+ return;
1623
+ }
1624
+ for (const path of changed) {
1625
+ const module = server.moduleGraph.getModuleById(`${RESOLVED_BUSINESS_PREFIX}${path}`);
1626
+ if (!module) continue;
1627
+ server.moduleGraph.invalidateModule(module);
1628
+ await server.reloadModule(module);
1629
+ }
1630
+ publishPreviewUpdate("hmr", changed, this.compilation.sourceRevision);
1631
+ }
1632
+ };
1633
+
1634
+ // apps/host/src/runtime-worker-arguments.ts
1635
+ import { resolve as resolve10 } from "node:path";
1636
+ function valueAfter(argv, name, fallback) {
1637
+ const index = argv.indexOf(name);
1638
+ const value = index >= 0 ? argv[index + 1] : fallback;
1639
+ if (value === void 0) throw new Error(`Missing worker argument: ${name}`);
1640
+ return value;
1641
+ }
1642
+ function parseArguments(argv) {
1643
+ const mode = valueAfter(argv, "--mode");
1644
+ if (!["preview", "check", "export"].includes(mode)) throw new Error(`Invalid worker mode: ${mode}`);
1645
+ const port = Number.parseInt(valueAfter(argv, "--port", "0"), 10);
1646
+ if (!Number.isInteger(port) || port < 0 || port > 65535) throw new Error("Invalid preview port");
1647
+ const parseJson = (name, fallback) => JSON.parse(valueAfter(argv, name, fallback));
1648
+ return {
1649
+ mode,
1650
+ projectRoot: resolve10(valueAfter(argv, "--project-root")),
1651
+ runtimeRoot: resolve10(valueAfter(argv, "--runtime-root")),
1652
+ runtimeEntry: valueAfter(argv, "--runtime-entry"),
1653
+ runtimeSdkEntry: valueAfter(argv, "--runtime-sdk-entry"),
1654
+ typeDeclarations: valueAfter(argv, "--type-declarations"),
1655
+ cacheDir: resolve10(valueAfter(argv, "--cache-dir")),
1656
+ dependencies: parseJson("--dependencies", "{}"),
1657
+ readablePaths: parseJson("--readable-paths", "[]"),
1658
+ editablePaths: parseJson("--editable-paths", "[]"),
1659
+ ...argv.includes("--out-dir") ? { outDir: resolve10(valueAfter(argv, "--out-dir")) } : {},
1660
+ host: valueAfter(argv, "--host", "127.0.0.1"),
1661
+ port,
1662
+ strictPort: argv.includes("--strictPort")
1663
+ };
1664
+ }
1665
+
1666
+ // apps/host/src/frontend-runtime-worker.ts
1667
+ import { build as build2, createServer as createServer2 } from "vite";
1668
+
1669
+ // apps/host/src/runtime-timing.ts
1670
+ async function measureRuntimeStage(stage, operation) {
1671
+ if (process.env.HARNESS_RUNTIME_TRACE !== "1") return await operation();
1672
+ const start = performance.now();
1673
+ const startedAt = Date.now();
1674
+ let status = "failed";
1675
+ try {
1676
+ const result = await operation();
1677
+ status = "completed";
1678
+ return result;
1679
+ } finally {
1680
+ process.stderr.write(`HARNESS_RUNTIME_TIMING ${JSON.stringify({ stage, status, startedAt, finishedAt: Date.now(), durationMs: performance.now() - start })}
1681
+ `);
1682
+ }
1683
+ }
1684
+
1685
+ // apps/host/src/frontend-runtime-worker.ts
1686
+ function attachControlInput(controller) {
1687
+ process.stdin.setEncoding("utf8");
1688
+ let buffer = "";
1689
+ process.stdin.on("data", (chunk) => {
1690
+ buffer = `${buffer}${chunk}`;
1691
+ if (buffer.length > 128 * 1024) buffer = buffer.slice(-128 * 1024);
1692
+ const lines = buffer.split(/\r?\n/u);
1693
+ buffer = lines.pop() ?? "";
1694
+ for (const line of lines) {
1695
+ if (!line.trim()) continue;
1696
+ try {
1697
+ const value = JSON.parse(line);
1698
+ if ((value.type === "batch.begin" || value.type === "batch.end" || value.type === "flush") && typeof value.epoch === "number" && typeof value.sequence === "number") {
1699
+ controller.handleControl(value);
1700
+ }
1701
+ } catch {
1702
+ }
1703
+ }
1704
+ });
1705
+ }
1706
+ var compilation = new RuntimeCompileSession((state) => {
1707
+ process.stdout.write(`${EVENT_PREFIX}${JSON.stringify({ type: "compile-state", state })}
1708
+ `);
1709
+ });
1710
+ async function main() {
1711
+ const args = parseArguments(process.argv.slice(2));
1712
+ verifyDependencies(args);
1713
+ if (args.mode === "check") {
1714
+ const state = await measureRuntimeStage("typescript", () => compilation.compile(args));
1715
+ process.exitCode = state.status === "failed" ? 1 : 0;
1716
+ return;
1717
+ }
1718
+ if (args.mode === "export") {
1719
+ if (!args.outDir) throw new Error("Export requires --out-dir");
1720
+ const state = await measureRuntimeStage("typescript", () => compilation.compile(args));
1721
+ if (state.status === "failed") {
1722
+ process.exitCode = 1;
1723
+ return;
1724
+ }
1725
+ await measureRuntimeStage("vite-build", () => build2(config(args)));
1726
+ await measureRuntimeStage("export-assets", async () => {
1727
+ await rewriteExportedGeneratedCss(args);
1728
+ await copyGeneratedAssets(args);
1729
+ });
1730
+ return;
1731
+ }
1732
+ const sources = await PreviewSourceController.create(args, compilation);
1733
+ attachControlInput(sources);
1734
+ const server = await createServer2(config(args, sources));
1735
+ await server.listen();
1736
+ await sources.refresh();
1737
+ const close = async () => {
1738
+ await sources.dispose();
1739
+ await compilation.dispose();
1740
+ await server.close();
1741
+ process.exit(0);
1742
+ };
1743
+ process.once("SIGINT", () => {
1744
+ void close();
1745
+ });
1746
+ process.once("SIGTERM", () => {
1747
+ void close();
1748
+ });
1749
+ }
1750
+ void main().catch((error) => {
1751
+ compilation.reportRuntimeFailure(error);
1752
+ process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}
1753
+ `);
1754
+ process.exitCode = 1;
1755
+ });