apphx 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +52 -138
  2. package/benchmarks/peer-context-worker.mjs +186 -0
  3. package/benchmarks/peer-context.mjs +150 -0
  4. package/dist/apple-types.js.map +1 -1
  5. package/dist/cli.js +373 -121
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config.js +42 -4
  8. package/dist/config.js.map +1 -1
  9. package/dist/dependencies.js +455 -16
  10. package/dist/dependencies.js.map +1 -1
  11. package/dist/export-options.js +44 -2
  12. package/dist/export-options.js.map +1 -1
  13. package/dist/flutter.js +13 -25
  14. package/dist/flutter.js.map +1 -1
  15. package/dist/go-dependencies.js +389 -0
  16. package/dist/go-dependencies.js.map +1 -0
  17. package/dist/inbox.js +7 -8
  18. package/dist/inbox.js.map +1 -1
  19. package/dist/integrations.js +29 -29
  20. package/dist/integrations.js.map +1 -1
  21. package/dist/isolation.js +190 -0
  22. package/dist/isolation.js.map +1 -0
  23. package/dist/macos.js +57 -0
  24. package/dist/macos.js.map +1 -0
  25. package/dist/mygo-build.js +55 -0
  26. package/dist/mygo-build.js.map +1 -0
  27. package/dist/policy.js +35 -0
  28. package/dist/policy.js.map +1 -0
  29. package/dist/preflight.js +1 -1
  30. package/dist/process.js +3 -3
  31. package/dist/project-profiles.js +163 -0
  32. package/dist/project-profiles.js.map +1 -0
  33. package/dist/proof.js +33 -0
  34. package/dist/proof.js.map +1 -0
  35. package/dist/remote.js +1 -1
  36. package/dist/runner.js +269 -287
  37. package/dist/runner.js.map +1 -1
  38. package/dist/scheduler.js +0 -3
  39. package/dist/scheduler.js.map +1 -1
  40. package/dist/schemas.js +156 -44
  41. package/dist/schemas.js.map +1 -1
  42. package/dist/store.js +166 -44
  43. package/dist/store.js.map +1 -1
  44. package/dist/vendor/obfuscator/dist/cli.js +72 -0
  45. package/dist/vendor/obfuscator/dist/cli.js.map +1 -0
  46. package/dist/vendor/obfuscator/dist/code.js +585 -0
  47. package/dist/vendor/obfuscator/dist/code.js.map +1 -0
  48. package/dist/vendor/obfuscator/dist/common.js +262 -0
  49. package/dist/vendor/obfuscator/dist/common.js.map +1 -0
  50. package/dist/vendor/obfuscator/dist/config.js +123 -0
  51. package/dist/vendor/obfuscator/dist/config.js.map +1 -0
  52. package/dist/vendor/obfuscator/dist/content.js +89 -0
  53. package/dist/vendor/obfuscator/dist/content.js.map +1 -0
  54. package/dist/vendor/obfuscator/dist/obfuscate.js +93 -0
  55. package/dist/vendor/obfuscator/dist/obfuscate.js.map +1 -0
  56. package/dist/vendor/obfuscator/dist/png.js +283 -0
  57. package/dist/vendor/obfuscator/dist/png.js.map +1 -0
  58. package/dist/vendor/obfuscator/dist/project.js +159 -0
  59. package/dist/vendor/obfuscator/dist/project.js.map +1 -0
  60. package/dist/vendor/obfuscator/dist/resources.js +335 -0
  61. package/dist/vendor/obfuscator/dist/resources.js.map +1 -0
  62. package/dist/vendor/obfuscator/package.json +22 -0
  63. package/dist/workspace.js +113 -0
  64. package/dist/workspace.js.map +1 -0
  65. package/evals/control.mjs +73 -0
  66. package/evals/expectations.mjs +37 -0
  67. package/evals/host.mjs +51 -0
  68. package/evals/oracle.mjs +80 -0
  69. package/evals/policy.mjs +268 -0
  70. package/examples/android-java.factory.json +18 -0
  71. package/examples/android-java.json +16 -0
  72. package/examples/existing-project.factory.json +18 -0
  73. package/examples/existing-project.json +17 -0
  74. package/examples/expo.eas.json +17 -0
  75. package/examples/expo.factory.json +18 -0
  76. package/examples/expo.json +16 -0
  77. package/examples/factory.json +0 -2
  78. package/examples/flutter.factory.json +13 -0
  79. package/examples/flutter.json +16 -0
  80. package/examples/luggage.json +5 -3
  81. package/examples/mygo-web.factory.json +18 -0
  82. package/examples/mygo-web.json +16 -0
  83. package/examples/mygo.factory.json +18 -0
  84. package/examples/mygo.json +16 -0
  85. package/examples/react-native.factory.json +18 -0
  86. package/examples/react-native.json +16 -0
  87. package/examples/remote.json +1 -1
  88. package/examples/runtime/android-java/app/build.gradle +7 -0
  89. package/examples/runtime/android-java/app/src/main/AndroidManifest.xml +8 -0
  90. package/examples/runtime/android-java/app/src/main/java/dev/apphx/runtime/MainActivity.java +44 -0
  91. package/examples/runtime/android-java/sdk-build.mjs +29 -0
  92. package/examples/runtime/android-java/settings.gradle +2 -0
  93. package/examples/runtime/android-ui.mjs +70 -0
  94. package/examples/runtime/mygo-native/go.mod +13 -0
  95. package/examples/runtime/mygo-native/go.sum +10 -0
  96. package/examples/runtime/mygo-native/main.go +173 -0
  97. package/examples/runtime/mygo-native/main_test.go +14 -0
  98. package/examples/runtime/mygo-native/mygo.json +1 -0
  99. package/examples/runtime/mygo-web/bun.lock +15 -0
  100. package/examples/runtime/mygo-web/go.mod +9 -0
  101. package/examples/runtime/mygo-web/go.sum +8 -0
  102. package/examples/runtime/mygo-web/index.html +1 -0
  103. package/examples/runtime/mygo-web/main.go +60 -0
  104. package/examples/runtime/mygo-web/mygo.config.ts +10 -0
  105. package/examples/runtime/mygo-web/package.json +1 -0
  106. package/examples/runtime/mygo-web/src/main.ts +23 -0
  107. package/examples/runtime/mygo-web/src/mygo.ts +14 -0
  108. package/examples/swift.factory.json +18 -0
  109. package/examples/swift.json +16 -0
  110. package/examples/uni-app.factory.json +18 -0
  111. package/examples/uni-app.json +16 -0
  112. package/package.json +12 -5
  113. package/scripts/bundle-obfuscator.mjs +19 -0
  114. package/scripts/runtime-smoke.mjs +209 -0
  115. package/skills/apphx/SKILL.md +180 -0
  116. package/skills/apphx/resources/google-play-research-result.json +6 -0
  117. package/skills/apphx/resources/local-research-result.json +6 -0
  118. package/skills/{apple-app-factory → apphx}/resources/requirement.json +5 -8
  119. package/skills/apphx/resources/research-result.json +12 -0
  120. package/skills/apphx/resources/review-result.json +7 -0
  121. package/dist/apple.js +0 -900
  122. package/dist/apple.js.map +0 -1
  123. package/skills/apple-app-factory/SKILL.md +0 -103
  124. package/skills/apple-app-factory/resources/research-result.json +0 -21
  125. package/skills/apple-app-factory/resources/review-result.json +0 -7
  126. /package/skills/{apple-app-factory → apphx}/LICENSE +0 -0
@@ -1,8 +1,9 @@
1
1
  import { canonicalPath, inside } from "./config.js";
2
- import { createHash } from "node:crypto";
3
- import { isAbsolute, join, relative, resolve, sep } from "node:path";
4
- import { existsSync, readFileSync } from "node:fs";
2
+ import { goDependencyInputs } from "./go-dependencies.js";
3
+ import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
4
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
5
  import { fileURLToPath, pathToFileURL } from "node:url";
6
+ import { createHash } from "node:crypto";
6
7
  import { parseDocument } from "yaml";
7
8
  // src/dependencies.ts
8
9
  const GENERATED_PATHS = /* @__PURE__ */ new Set([
@@ -19,8 +20,43 @@ const GENERATED_PATHS = /* @__PURE__ */ new Set([
19
20
  "ios/Flutter/flutter_export_environment.sh",
20
21
  "ios/Flutter/ephemeral"
21
22
  ]);
22
- function isGeneratedPath(path) {
23
+ function isGeneratedPath(path, framework) {
23
24
  let candidate = sep === "/" ? path : path.split(sep).join("/");
25
+ candidate = candidate.replace(/^\.\//, "").replace(/\/$/, "");
26
+ const parts = candidate.split("/");
27
+ if (framework === "mygo") return [
28
+ ".git",
29
+ ".idea",
30
+ ".factory-input",
31
+ ".factory-cache",
32
+ "build",
33
+ "dist"
34
+ ].includes(parts[0]) || parts.includes(".mygo") || parts.includes("node_modules") || parts.some((part, index) => part === ".yarn" && [
35
+ "cache",
36
+ "unplugged",
37
+ "install-state.gz"
38
+ ].includes(parts[index + 1] ?? ""));
39
+ if (framework === "flutter" && (parts.includes(".dart_tool") || parts.includes(".gradle"))) return true;
40
+ if ([
41
+ ".git",
42
+ ".idea",
43
+ ".factory-input",
44
+ ".factory-cache",
45
+ "build"
46
+ ].includes(parts[0])) return true;
47
+ if (framework && framework !== "flutter") {
48
+ const javascript = framework === "react-native" || framework === "expo" || framework === "uni-app";
49
+ if (javascript && parts.includes("node_modules")) return true;
50
+ if (framework !== "swift" && parts.includes(".gradle")) return true;
51
+ if (framework !== "android-java" && parts.includes("Pods")) return true;
52
+ if (framework !== "android-java" && (parts.includes(".build") || parts[0] === "DerivedData")) return true;
53
+ if (framework !== "swift" && /^(?:android\/)?(?:app\/)?build(?:\/|$)/.test(candidate)) return true;
54
+ if (framework !== "android-java" && /^ios\/(?:build|DerivedData)(?:\/|$)/.test(candidate)) return true;
55
+ if (javascript && /^\.yarn\/(?:cache|unplugged|install-state\.gz)(?:\/|$)/.test(candidate)) return true;
56
+ if (framework === "expo" && /^\.expo(?:\/|$)/.test(candidate)) return true;
57
+ if (framework === "uni-app" && /^(?:unpackage|dist)(?:\/|$)/.test(candidate)) return true;
58
+ return false;
59
+ }
24
60
  while (candidate) {
25
61
  if (GENERATED_PATHS.has(candidate)) return true;
26
62
  const slash = candidate.lastIndexOf("/");
@@ -29,6 +65,92 @@ function isGeneratedPath(path) {
29
65
  }
30
66
  return false;
31
67
  }
68
+ function manifestCode(path, ruby = false) {
69
+ return readFileSync(path, "utf8").replace(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\/\*[\s\S]*?\*\/|\/\/[^\r\n]*|#[^\r\n]*/g, (token) => {
70
+ if (token.startsWith("\"") || token.startsWith("'") || token.startsWith("#") && !ruby) return token;
71
+ return " ";
72
+ });
73
+ }
74
+ const GRADLE_TOKENS = /"""[\s\S]*?"""|'''[\s\S]*?'''|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\/\*[\s\S]*?\*\/|\/\/[^\r\n]*/g;
75
+ function gradleCode(path) {
76
+ return readFileSync(path, "utf8").replace(GRADLE_TOKENS, (token) => token.startsWith("/") ? token.replace(/[^\r\n]/g, " ") : token);
77
+ }
78
+ function gradlePattern(expression) {
79
+ return new RegExp(`${GRADLE_TOKENS.source}|${expression.source}`, "g");
80
+ }
81
+ /** Workspace-relative build outputs for statically declared, real Gradle modules. */
82
+ function generatedModulePaths(root, framework, directory = ".") {
83
+ if (![
84
+ "android-java",
85
+ "react-native",
86
+ "expo",
87
+ "uni-app"
88
+ ].includes(framework)) return /* @__PURE__ */ new Set();
89
+ const outputs = /* @__PURE__ */ new Set();
90
+ const visited = /* @__PURE__ */ new Set();
91
+ function underOutput(path) {
92
+ const portable = path.split(sep).join("/");
93
+ for (const output of outputs) if (portable === output || portable.startsWith(`${output}/`)) return true;
94
+ return false;
95
+ }
96
+ function registerBuildOutput(absolute) {
97
+ for (const build of ["build.gradle", "build.gradle.kts"]) {
98
+ const manifest = join(absolute, build);
99
+ const stat = lstatSync(manifest, { throwIfNoEntry: false });
100
+ if (stat?.isSymbolicLink()) throw new Error(`Gradle module manifest cannot be a symlink: ${manifest}`);
101
+ if (stat?.isFile()) outputs.add(relative(root, join(absolute, "build")).split(sep).join("/"));
102
+ }
103
+ }
104
+ function visit(current) {
105
+ current = resolve(current);
106
+ if (visited.has(current)) return;
107
+ if (current !== resolve(root)) inside(root, current);
108
+ const path = relative(root, current);
109
+ if (generatedInProject(path, framework, directory) || underOutput(path)) return;
110
+ if (path !== relative(canonicalPath(root), canonicalPath(current))) throw new Error(`Gradle build directory cannot be a source symlink: ${current}`);
111
+ visited.add(current);
112
+ registerBuildOutput(current);
113
+ for (const name of ["settings.gradle", "settings.gradle.kts"]) {
114
+ const settings = join(current, name);
115
+ const stat = lstatSync(settings, { throwIfNoEntry: false });
116
+ if (!stat) continue;
117
+ if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(`Gradle settings must be a regular source file: ${settings}`);
118
+ const text = gradleCode(settings);
119
+ const modules = /* @__PURE__ */ new Map();
120
+ for (const include of text.matchAll(gradlePattern(/\binclude\s*(?:\(([^)]*)\)|([^\r\n;]+))/))) {
121
+ const argumentsText = include[1] ?? include[2];
122
+ if (argumentsText === void 0 || !/^\s*(?:(?:"[:\w.-]+"|'[:\w.-]+')\s*,?\s*)+$/.test(argumentsText)) continue;
123
+ for (const match of argumentsText.matchAll(/["'](:?[\w.-]+(?::[\w.-]+)*)["']/g)) {
124
+ const id = match[1].replace(/^:/, "");
125
+ modules.set(id, id.split(":").join("/"));
126
+ }
127
+ }
128
+ for (const match of text.matchAll(gradlePattern(/\bproject\s*\(\s*["'](:?[\w.-]+(?::[\w.-]+)*)["']\s*\)\s*\.projectDir\s*=\s*file\s*\(\s*(?:"([^"\\$]+)"|'([^'\\]+)')\s*\)/))) {
129
+ if (match[1] === void 0) continue;
130
+ const id = match[1].replace(/^:/, "");
131
+ if (modules.has(id)) modules.set(id, match[2] ?? match[3]);
132
+ }
133
+ for (const module of modules.values()) {
134
+ if (isAbsolute(module)) throw new Error(`Gradle module requires a workspace-relative path: ${module}`);
135
+ const absolute = resolve(current, module);
136
+ inside(root, absolute);
137
+ if (relative(resolve(root), absolute) !== relative(canonicalPath(root), canonicalPath(absolute))) throw new Error(`Gradle module directory cannot be a source symlink: ${module}`);
138
+ registerBuildOutput(absolute);
139
+ }
140
+ for (const match of text.matchAll(gradlePattern(/\bincludeBuild\s*\(?\s*(?:"([^"\\$]+)"|'([^'\\]+)')/))) {
141
+ const included = match[1] ?? match[2];
142
+ if (included === void 0) continue;
143
+ if (isAbsolute(included)) throw new Error(`Included Gradle build requires a workspace-relative path: ${included}`);
144
+ visit(resolve(current, included));
145
+ }
146
+ }
147
+ }
148
+ visit(root);
149
+ const project = projectRoot(root, directory);
150
+ visit(project);
151
+ visit(join(project, "android"));
152
+ return outputs;
153
+ }
32
154
  function mapping(value, path) {
33
155
  if (value === void 0 || value === null) return {};
34
156
  if (typeof value !== "object" || Array.isArray(value)) throw new Error(`Expected a YAML mapping in ${path}; run flutter pub get after repairing the dependency metadata`);
@@ -39,12 +161,12 @@ function yaml(path) {
39
161
  if (document.errors.length) throw new Error(`Cannot parse dependency metadata at ${path}: ${document.errors.map((error) => error.message).join("; ")}`);
40
162
  return mapping(document.toJS(), path);
41
163
  }
42
- function validateLocalDependencies(root) {
43
- const manifest = join(root, "pubspec.yaml");
164
+ function validateFlutterDependencies(root, project = root) {
165
+ const manifest = join(project, "pubspec.yaml");
44
166
  if (!existsSync(manifest)) return;
45
167
  const workspace = canonicalPath(root);
46
168
  const rootSpec = yaml(manifest);
47
- const overridesFile = join(root, "pubspec_overrides.yaml");
169
+ const overridesFile = join(project, "pubspec_overrides.yaml");
48
170
  const overrides = {
49
171
  ...mapping(rootSpec.dependency_overrides, manifest),
50
172
  ...mapping(existsSync(overridesFile) ? yaml(overridesFile).dependency_overrides : void 0, overridesFile)
@@ -56,7 +178,7 @@ function validateLocalDependencies(root) {
56
178
  const absolute = resolve(path);
57
179
  const lexical = relative(resolve(root), absolute);
58
180
  const actual = relative(workspace, canonicalPath(absolute));
59
- if (isAbsolute(lexical) || lexical === ".." || lexical.startsWith(`..${sep}`) || isAbsolute(actual) || actual === ".." || actual.startsWith(`..${sep}`) || isGeneratedPath(lexical) || isGeneratedPath(actual)) throw new Error(`Local dependency ${path} is outside fingerprinted workspace source; move the package into a non-generated workspace directory and run flutter pub get`);
181
+ if (isAbsolute(lexical) || lexical === ".." || lexical.startsWith(`..${sep}`) || isAbsolute(actual) || actual === ".." || actual.startsWith(`..${sep}`) || lexical !== actual || generatedInProject(lexical, "flutter", relative(root, project)) || generatedInProject(actual, "flutter", relative(root, project))) throw new Error(`Local dependency ${path} is outside fingerprinted workspace source; move the package into a non-generated workspace directory and run flutter pub get`);
60
182
  if (lexical) inside(root, absolute);
61
183
  return absolute;
62
184
  }
@@ -84,20 +206,20 @@ function validateLocalDependencies(root) {
84
206
  };
85
207
  for (const [name, dependency] of Object.entries(dependencies)) {
86
208
  const overridden = Object.hasOwn(overrides, name);
87
- pathDependency(name, overridden ? overrides[name] : dependency, overridden ? root : directory);
209
+ pathDependency(name, overridden ? overrides[name] : dependency, overridden ? project : directory);
88
210
  }
89
211
  }
90
- visit(root, true);
91
- const lockFile = join(root, "pubspec.lock");
212
+ visit(project, true);
213
+ const lockFile = join(project, "pubspec.lock");
92
214
  if (existsSync(lockFile)) {
93
215
  const packages = mapping(yaml(lockFile).packages, lockFile);
94
216
  for (const [name, value] of Object.entries(packages)) {
95
217
  const entry = mapping(value, lockFile);
96
- if (entry.source === "path") pathDependency(name, { path: mapping(entry.description, lockFile).path }, root);
218
+ if (entry.source === "path") pathDependency(name, { path: mapping(entry.description, lockFile).path }, project);
97
219
  else if (entry.source === "hosted" || entry.source === "sdk" || entry.source === "git") immutablePackages.add(name);
98
220
  }
99
221
  }
100
- const packageConfig = join(root, ".dart_tool", "package_config.json");
222
+ const packageConfig = join(project, ".dart_tool", "package_config.json");
101
223
  if (!existsSync(packageConfig)) return;
102
224
  const packages = mapping(JSON.parse(readFileSync(packageConfig, "utf8")), packageConfig).packages;
103
225
  if (!Array.isArray(packages)) throw new Error(`Invalid package resolution at ${packageConfig}; run flutter pub get before verification`);
@@ -110,10 +232,10 @@ function validateLocalDependencies(root) {
110
232
  const packageRoot = owned(fileURLToPath(uri));
111
233
  const declared = localPackages.get(entry.name);
112
234
  if (declared && canonicalPath(declared) !== canonicalPath(packageRoot)) throw new Error(`Resolved local dependency ${entry.name} differs from its declared path; run flutter pub get before verification`);
113
- visit(packageRoot, canonicalPath(packageRoot) === workspace);
235
+ visit(packageRoot, canonicalPath(packageRoot) === canonicalPath(project));
114
236
  }
115
237
  }
116
- function dependencyDigest(root) {
238
+ function flutterDependencyDigest(root) {
117
239
  const path = join(root, ".dart_tool", "package_config.json");
118
240
  const hash = createHash("sha256").update("factory-dependency-resolution-v1\0");
119
241
  if (!existsSync(path)) return hash.update("missing").digest("hex");
@@ -152,6 +274,323 @@ function dependencyDigest(root) {
152
274
  packages
153
275
  })).digest("hex");
154
276
  }
155
- export { GENERATED_PATHS, dependencyDigest, isGeneratedPath, validateLocalDependencies };
277
+ function projectRoot(root, directory = ".") {
278
+ if (isAbsolute(directory)) throw new Error("Project directory must be workspace-relative");
279
+ const project = resolve(root, directory);
280
+ if (project !== resolve(root)) inside(root, project);
281
+ return project;
282
+ }
283
+ function generatedInProject(path, framework, directory = ".") {
284
+ if (isGeneratedPath(path, framework)) return true;
285
+ const nested = relative(directory, path);
286
+ return nested !== ".." && !nested.startsWith(`..${sep}`) && !isAbsolute(nested) && isGeneratedPath(nested, framework);
287
+ }
288
+ function sourceFiles(root, framework, activeDirectory, resolutions, moduleOutputs) {
289
+ const files = [];
290
+ function visit(directory) {
291
+ for (const entry of readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
292
+ const absolute = join(directory, entry.name);
293
+ const path = relative(root, absolute);
294
+ const portable = path.split(sep).join("/");
295
+ if (generatedInProject(path, framework, activeDirectory) || moduleOutputs.some((output) => portable === output || portable.startsWith(`${output}/`))) {
296
+ if (entry.isDirectory()) {
297
+ const records = entry.name === "node_modules" ? [
298
+ ".package-lock.json",
299
+ ".modules.yaml",
300
+ ".pnpm/lock.yaml"
301
+ ] : entry.name === "Pods" ? ["Manifest.lock"] : entry.name === ".build" ? ["workspace-state.json"] : entry.name === "build" ? ["SourcePackages/workspace-state.json", "DerivedData/SourcePackages/workspace-state.json"] : entry.name === "DerivedData" ? ["SourcePackages/workspace-state.json"] : [];
302
+ for (const record of records) {
303
+ const metadata = join(absolute, record);
304
+ const stat = lstatSync(metadata, { throwIfNoEntry: false });
305
+ if (stat && relative(canonicalPath(root), canonicalPath(metadata)) !== relative(resolve(root), metadata)) throw new Error(`Dependency resolution metadata cannot traverse a source symlink: ${metadata}`);
306
+ if (stat?.isSymbolicLink()) throw new Error(`Dependency resolution metadata cannot be a symlink: ${metadata}`);
307
+ if (stat) resolutions.push(join(path, record));
308
+ }
309
+ }
310
+ if (entry.isFile() && entry.name === "install-state.gz") resolutions.push(path);
311
+ continue;
312
+ }
313
+ const stat = lstatSync(absolute);
314
+ if (stat.isSymbolicLink()) throw new Error(`Source symlinks are not trusted dependency inputs: ${path}`);
315
+ if (stat.isDirectory()) visit(absolute);
316
+ else if (stat.isFile()) files.push(path);
317
+ else throw new Error(`Dependency input must be a regular source file: ${path}`);
318
+ }
319
+ }
320
+ visit(root);
321
+ return files;
322
+ }
323
+ function object(value, path) {
324
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`Invalid dependency metadata at ${path}`);
325
+ return value;
326
+ }
327
+ function dependencyInputs(root, framework, activeDirectory = ".") {
328
+ projectRoot(root, activeDirectory);
329
+ const resolutions = [];
330
+ const moduleOutputs = [...generatedModulePaths(root, framework, activeDirectory)];
331
+ const files = sourceFiles(root, framework, activeDirectory, resolutions, moduleOutputs);
332
+ const go = framework === "mygo" ? goDependencyInputs(root, activeDirectory, (path) => generatedInProject(path, framework, activeDirectory)) : void 0;
333
+ const javascriptActive = framework !== "mygo" || ["package.json", "frontend/package.json"].some((path) => existsSync(join(root, activeDirectory, path)));
334
+ const local = new Set(go?.local);
335
+ const packages = /* @__PURE__ */ new Map();
336
+ for (const path of files.filter((path) => javascriptActive && basename(path) === "package.json")) {
337
+ const pkg = object(JSON.parse(readFileSync(join(root, path), "utf8")), path);
338
+ if (typeof pkg.name === "string") packages.set(pkg.name, dirname(path));
339
+ }
340
+ function owned(value, directory, native = false) {
341
+ if (!value || isAbsolute(value) || /^[a-z]:[\\/]/i.test(value)) throw new Error(`Local dependency requires a workspace-relative path: ${value}`);
342
+ const absolute = resolve(root, directory, value);
343
+ const path = relative(resolve(root), absolute);
344
+ if (path === ".." || path.startsWith(`..${sep}`) || isAbsolute(path)) throw new Error(`Local dependency escapes fingerprinted workspace source: ${value}`);
345
+ if (path) inside(root, absolute);
346
+ const portable = path.split(sep).join("/");
347
+ if (generatedInProject(path, framework, activeDirectory) || moduleOutputs.some((output) => portable === output || portable.startsWith(`${output}/`))) {
348
+ if (native && path.split(sep).includes("node_modules")) return;
349
+ throw new Error(`Local dependency is outside fingerprinted workspace source: ${value}`);
350
+ }
351
+ if (!existsSync(absolute)) {
352
+ if (native) return;
353
+ throw new Error(`Local dependency does not exist: ${value}`);
354
+ }
355
+ if (path !== relative(canonicalPath(root), canonicalPath(absolute)) || lstatSync(absolute).isSymbolicLink()) throw new Error(`Local dependency source symlinks are not trusted: ${value}`);
356
+ local.add(path);
357
+ }
358
+ function reference(value, directory, name) {
359
+ if (typeof value !== "string") return;
360
+ const match = /^(?:file|link|portal):(.*)$/.exec(value);
361
+ if (match) owned(decodeURIComponent(match[1]), directory);
362
+ else if (value.startsWith(".") || isAbsolute(value) || /^[a-z]:[\\/]/i.test(value)) owned(value, directory);
363
+ else if (value.startsWith("workspace:")) {
364
+ const target = name ? packages.get(name) : void 0;
365
+ if (target === void 0) throw new Error(`Workspace dependency ${name ?? value} has no owned package manifest`);
366
+ owned(relative(resolve(root, directory), resolve(root, target)) || ".", directory);
367
+ }
368
+ }
369
+ function declarations(value, directory) {
370
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
371
+ for (const [name, child] of Object.entries(value)) if (typeof child === "object") declarations(child, directory);
372
+ else reference(child, directory, name);
373
+ }
374
+ function npmResolutions(value, directory) {
375
+ if (!value || typeof value !== "object") return;
376
+ const entry = value;
377
+ if (entry.link === true && typeof entry.resolved === "string") owned(entry.resolved.replace(/^(?:file|link):/, ""), directory);
378
+ for (const [key, child] of Object.entries(entry)) if (key === "resolved" || key === "version") reference(child, directory);
379
+ else if (child && typeof child === "object") npmResolutions(child, directory);
380
+ }
381
+ function pnpmResolutions(lock, directory, path) {
382
+ for (const [importer, value] of Object.entries(mapping(lock.importers, path))) {
383
+ const base = relative(root, resolve(root, directory, importer));
384
+ const spec = mapping(value, path);
385
+ for (const field of [
386
+ "dependencies",
387
+ "devDependencies",
388
+ "optionalDependencies"
389
+ ]) for (const [dependency, value] of Object.entries(mapping(spec[field], path))) if (typeof value === "string") reference(value, base, dependency);
390
+ else {
391
+ const resolution = mapping(value, path);
392
+ reference(resolution.specifier, base, dependency);
393
+ reference(resolution.version, base, dependency);
394
+ }
395
+ }
396
+ for (const field of ["packages", "snapshots"]) for (const [key, value] of Object.entries(mapping(lock[field], path))) {
397
+ const local = /(?:file|link|portal):([^(:]+)/.exec(key);
398
+ if (local) owned(decodeURIComponent(local[1]), directory);
399
+ reference(mapping(mapping(value, path).resolution, path).tarball, directory);
400
+ }
401
+ }
402
+ for (const path of files) {
403
+ const name = basename(path);
404
+ const directory = dirname(path);
405
+ if (framework === "mygo" && (!javascriptActive || ![
406
+ "package.json",
407
+ "package-lock.json",
408
+ "npm-shrinkwrap.json",
409
+ "pnpm-lock.yaml",
410
+ "yarn.lock",
411
+ "bun.lock"
412
+ ].includes(name))) continue;
413
+ if (name === "package.json") {
414
+ const pkg = object(JSON.parse(readFileSync(join(root, path), "utf8")), path);
415
+ for (const field of [
416
+ "dependencies",
417
+ "devDependencies",
418
+ "optionalDependencies",
419
+ "peerDependencies",
420
+ "overrides",
421
+ "resolutions"
422
+ ]) declarations(pkg[field], directory);
423
+ } else if (name === "package-lock.json" || name === "npm-shrinkwrap.json") npmResolutions(object(JSON.parse(readFileSync(join(root, path), "utf8")), path), directory);
424
+ else if (name === "pnpm-lock.yaml") pnpmResolutions(yaml(join(root, path)), directory, path);
425
+ else if (["yarn.lock", "bun.lock"].includes(name)) {
426
+ const text = readFileSync(join(root, path), "utf8");
427
+ for (const match of text.matchAll(/(?:file|link|portal):([^\s"'`,}\]]+)/g)) owned(decodeURIComponent(match[1].split("::")[0]), directory);
428
+ } else if (name === "Package.swift") {
429
+ const text = manifestCode(join(root, path));
430
+ for (const match of text.matchAll(/\.package\s*\(\s*(?:name\s*:\s*"[^"]*"\s*,\s*)?path\s*:\s*"([^"]+)"/g)) owned(match[1], directory);
431
+ for (const match of text.matchAll(/\burl\s*:\s*"([^"]+)"/g)) reference(match[1], directory);
432
+ } else if (name === "Podfile" || name.endsWith(".podspec")) {
433
+ const text = manifestCode(join(root, path), true);
434
+ for (const match of text.matchAll(/(?::(?:path|podspec)\s*=>\s*|(?:path|podspec)\s*:\s*)["']([^"']+)["']/g)) owned(match[1], directory, true);
435
+ for (const match of text.matchAll(/(?::git\s*=>\s*|git\s*:\s*)["']([^"']+)["']/g)) reference(match[1], directory);
436
+ } else if (name === "Podfile.lock") {
437
+ const lock = yaml(join(root, path));
438
+ for (const value of Object.values(mapping(lock["EXTERNAL SOURCES"], path))) {
439
+ const external = mapping(value, path);
440
+ for (const field of [":path", ":podspec"]) if (typeof external[field] === "string") owned(external[field], directory, true);
441
+ reference(external[":git"], directory);
442
+ }
443
+ } else if (name === "project.pbxproj") {
444
+ const text = manifestCode(join(root, path));
445
+ for (const match of text.matchAll(/\brelativePath\s*=\s*"?([^;"\r\n]+)"?\s*;/g)) owned(match[1].trim(), dirname(directory));
446
+ } else if (/^(?:settings|build)\.gradle(?:\.kts)?$/.test(name)) {
447
+ const text = gradleCode(join(root, path));
448
+ for (const match of text.matchAll(gradlePattern(/\b(?:includeBuild\s*\(?\s*|projectDir\s*=\s*file\s*\(\s*|fileTree\s*\(\s*(?:dir\s*[:=]\s*)?)(?:"([^"\\$]+)"|'([^'\\]+)')/))) {
449
+ const value = match[1] ?? match[2];
450
+ if (value !== void 0) owned(value, directory, true);
451
+ }
452
+ const collections = gradlePattern(/\b(files|dirs)\b\s*(?:(\()|(?=["']))/);
453
+ const literalPath = /"([^"\\$]*)"|'([^'\\]*)'/y;
454
+ for (let match; match = collections.exec(text);) {
455
+ if (!match[1]) continue;
456
+ let offset = collections.lastIndex;
457
+ const parenthesized = Boolean(match[2]);
458
+ const whitespace = parenthesized ? /\s/ : /[ \t]/;
459
+ while (true) {
460
+ while (offset < text.length && whitespace.test(text[offset])) offset++;
461
+ if (parenthesized && text[offset] === ")") {
462
+ offset++;
463
+ break;
464
+ }
465
+ if (!parenthesized && (offset === text.length || /[\r\n;}]/.test(text[offset]))) break;
466
+ literalPath.lastIndex = offset;
467
+ const literal = literalPath.exec(text);
468
+ if (!literal) throw new Error(`Cannot prove Gradle ${match[1]} collection in ${path}: expected literal paths`);
469
+ owned(literal[1] ?? literal[2], directory, true);
470
+ offset = literalPath.lastIndex;
471
+ while (offset < text.length && whitespace.test(text[offset])) offset++;
472
+ if (text[offset] === ",") {
473
+ offset++;
474
+ while (offset < text.length && /\s/.test(text[offset])) offset++;
475
+ continue;
476
+ }
477
+ if (parenthesized && text[offset] === ")") {
478
+ offset++;
479
+ break;
480
+ }
481
+ if (!parenthesized && (offset === text.length || /[\r\n;}]/.test(text[offset]))) break;
482
+ throw new Error(`Cannot prove Gradle ${match[1]} collection in ${path}: expected literal paths`);
483
+ }
484
+ collections.lastIndex = offset;
485
+ }
486
+ for (const match of text.matchAll(gradlePattern(/\burl\s*(?:=\s*)?(?:uri\s*\(?\s*)?(?:"([^"\\$]+)"|'([^'\\]+)')/))) {
487
+ const value = match[1] ?? match[2];
488
+ if (value === void 0) continue;
489
+ if (/^[a-z][a-z0-9+.-]*:/i.test(value)) reference(value, directory);
490
+ else owned(value, directory, true);
491
+ }
492
+ }
493
+ }
494
+ for (const path of resolutions) {
495
+ const absolute = join(root, path);
496
+ if (!existsSync(absolute)) continue;
497
+ inside(root, absolute);
498
+ if (!lstatSync(absolute).isFile() || lstatSync(absolute).isSymbolicLink()) throw new Error(`Dependency resolution metadata must be a regular file: ${path}`);
499
+ if (basename(path) === ".package-lock.json") npmResolutions(object(JSON.parse(readFileSync(absolute, "utf8")), path), dirname(dirname(path)));
500
+ else if (path.endsWith(`${sep}.pnpm${sep}lock.yaml`)) pnpmResolutions(yaml(absolute), dirname(dirname(dirname(path))), path);
501
+ else if (basename(path) === "workspace-state.json") {
502
+ const metadata = object(JSON.parse(readFileSync(absolute, "utf8")), path);
503
+ const state = metadata.object === void 0 ? metadata : object(metadata.object, path);
504
+ if (Array.isArray(state.dependencies)) for (const value of state.dependencies) {
505
+ const reference = object(object(value, path).packageRef, path);
506
+ if ((reference.kind === "fileSystem" || reference.kind === "localSourceControl") && typeof reference.location === "string") owned((isAbsolute(reference.location) ? relative(resolve(root), reference.location) : join(activeDirectory, reference.location)) || ".", ".");
507
+ }
508
+ }
509
+ }
510
+ return {
511
+ files,
512
+ local,
513
+ resolutions,
514
+ ...go ? { goFiles: go.files } : {}
515
+ };
516
+ }
517
+ /** Validate declarations before any source is granted verification authority. */
518
+ function validateLocalDependencies(root, framework, directory = ".") {
519
+ if (framework === void 0 || framework === "flutter") {
520
+ validateFlutterDependencies(root, projectRoot(root, directory));
521
+ return;
522
+ }
523
+ dependencyInputs(root, framework, directory);
524
+ }
525
+ function resolutionMetadata(path, framework) {
526
+ const name = basename(path);
527
+ if (framework === "mygo") return [
528
+ "package.json",
529
+ "package-lock.json",
530
+ "npm-shrinkwrap.json",
531
+ "yarn.lock",
532
+ "pnpm-lock.yaml",
533
+ "bun.lock",
534
+ "bun.lockb",
535
+ ".pnp.cjs"
536
+ ].includes(name);
537
+ return [
538
+ "package.json",
539
+ "package-lock.json",
540
+ "npm-shrinkwrap.json",
541
+ "yarn.lock",
542
+ "pnpm-lock.yaml",
543
+ "bun.lock",
544
+ "bun.lockb",
545
+ "Podfile",
546
+ "Podfile.lock",
547
+ "Package.swift",
548
+ "Package.resolved",
549
+ "Cartfile",
550
+ "Cartfile.resolved",
551
+ "project.pbxproj",
552
+ "gradle.properties",
553
+ "gradle.lockfile",
554
+ "gradle-wrapper.properties",
555
+ "verification-metadata.xml",
556
+ "libs.versions.toml",
557
+ ".pnp.cjs"
558
+ ].includes(name) || /\.(?:gradle(?:\.kts)?|podspec)$/.test(name) || path.includes("dependency-locks/");
559
+ }
560
+ function dependencyDigest(root, framework, directory = ".") {
561
+ if (framework === void 0 || framework === "flutter") return flutterDependencyDigest(projectRoot(root, directory));
562
+ const { files, local, resolutions, goFiles } = dependencyInputs(root, framework, directory);
563
+ const hash = createHash("sha256").update(`factory-dependency-resolution-v2\0${framework}\0`);
564
+ function add(path) {
565
+ const content = readFileSync(join(root, path));
566
+ hash.update(`${path.split(sep).join("/")}\0${content.length}\0`).update(content).update("\0");
567
+ }
568
+ const localDirectories = [...local];
569
+ for (const path of files) if (goFiles?.has(path) || resolutionMetadata(path, framework) || localDirectories.some((directory) => !directory || path === directory || path.startsWith(`${directory}${sep}`))) add(path);
570
+ if (goFiles) {
571
+ const sourcePaths = new Set(files);
572
+ for (const path of [...goFiles].sort()) {
573
+ if (sourcePaths.has(path)) continue;
574
+ const absolute = inside(root, path);
575
+ const stat = lstatSync(absolute);
576
+ if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(`Generated frontend dependency must be a regular owned file: ${path}`);
577
+ add(path);
578
+ }
579
+ }
580
+ const installationRecords = [join(directory, ".yarn/install-state.gz"), join(directory, ".pnp.data.json")];
581
+ if (framework === "mygo") installationRecords.push(join(directory, "frontend/.yarn/install-state.gz"), join(directory, "frontend/.pnp.data.json"));
582
+ for (const path of [.../* @__PURE__ */ new Set([...resolutions, ...installationRecords])].sort()) {
583
+ const absolute = join(root, path);
584
+ if (!existsSync(absolute)) {
585
+ hash.update(`${path}\0missing\0`);
586
+ continue;
587
+ }
588
+ inside(root, absolute);
589
+ if (!lstatSync(absolute).isFile() || lstatSync(absolute).isSymbolicLink()) throw new Error(`Dependency resolution metadata must be a regular file: ${path}`);
590
+ add(path);
591
+ }
592
+ return hash.digest("hex");
593
+ }
594
+ export { GENERATED_PATHS, dependencyDigest, generatedModulePaths, isGeneratedPath, validateLocalDependencies };
156
595
 
157
596
  //# sourceMappingURL=dependencies.js.map