apphx 1.1.0 → 2.0.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.
- package/README.md +50 -182
- package/benchmarks/peer-context-worker.mjs +186 -0
- package/benchmarks/peer-context.mjs +150 -0
- package/dist/apple-types.js.map +1 -1
- package/dist/cli.js +370 -118
- package/dist/cli.js.map +1 -1
- package/dist/config.js +42 -4
- package/dist/config.js.map +1 -1
- package/dist/dependencies.js +455 -16
- package/dist/dependencies.js.map +1 -1
- package/dist/export-options.js +44 -2
- package/dist/export-options.js.map +1 -1
- package/dist/flutter.js +13 -25
- package/dist/flutter.js.map +1 -1
- package/dist/go-dependencies.js +389 -0
- package/dist/go-dependencies.js.map +1 -0
- package/dist/inbox.js +7 -8
- package/dist/inbox.js.map +1 -1
- package/dist/integrations.js +30 -3
- package/dist/integrations.js.map +1 -1
- package/dist/isolation.js +190 -0
- package/dist/isolation.js.map +1 -0
- package/dist/macos.js +57 -0
- package/dist/macos.js.map +1 -0
- package/dist/mygo-build.js +55 -0
- package/dist/mygo-build.js.map +1 -0
- package/dist/policy.js +22 -0
- package/dist/policy.js.map +1 -0
- package/dist/preflight.js +1 -1
- package/dist/process.js +3 -3
- package/dist/project-profiles.js +163 -0
- package/dist/project-profiles.js.map +1 -0
- package/dist/proof.js +33 -0
- package/dist/proof.js.map +1 -0
- package/dist/remote.js +1 -1
- package/dist/runner.js +269 -287
- package/dist/runner.js.map +1 -1
- package/dist/scheduler.js +0 -3
- package/dist/scheduler.js.map +1 -1
- package/dist/schemas.js +156 -44
- package/dist/schemas.js.map +1 -1
- package/dist/store.js +166 -44
- package/dist/store.js.map +1 -1
- package/dist/vendor/obfuscator/dist/cli.js +72 -0
- package/dist/vendor/obfuscator/dist/cli.js.map +1 -0
- package/dist/vendor/obfuscator/dist/code.js +585 -0
- package/dist/vendor/obfuscator/dist/code.js.map +1 -0
- package/dist/vendor/obfuscator/dist/common.js +262 -0
- package/dist/vendor/obfuscator/dist/common.js.map +1 -0
- package/dist/vendor/obfuscator/dist/config.js +123 -0
- package/dist/vendor/obfuscator/dist/config.js.map +1 -0
- package/dist/vendor/obfuscator/dist/content.js +89 -0
- package/dist/vendor/obfuscator/dist/content.js.map +1 -0
- package/dist/vendor/obfuscator/dist/obfuscate.js +93 -0
- package/dist/vendor/obfuscator/dist/obfuscate.js.map +1 -0
- package/dist/vendor/obfuscator/dist/png.js +283 -0
- package/dist/vendor/obfuscator/dist/png.js.map +1 -0
- package/dist/vendor/obfuscator/dist/project.js +159 -0
- package/dist/vendor/obfuscator/dist/project.js.map +1 -0
- package/dist/vendor/obfuscator/dist/resources.js +335 -0
- package/dist/vendor/obfuscator/dist/resources.js.map +1 -0
- package/dist/vendor/obfuscator/package.json +22 -0
- package/dist/workspace.js +113 -0
- package/dist/workspace.js.map +1 -0
- package/evals/control.mjs +73 -0
- package/evals/expectations.mjs +37 -0
- package/evals/host.mjs +51 -0
- package/evals/oracle.mjs +80 -0
- package/evals/policy.mjs +268 -0
- package/examples/android-java.factory.json +18 -0
- package/examples/android-java.json +16 -0
- package/examples/existing-project.factory.json +18 -0
- package/examples/existing-project.json +17 -0
- package/examples/expo.eas.json +17 -0
- package/examples/expo.factory.json +18 -0
- package/examples/expo.json +16 -0
- package/examples/factory.json +0 -2
- package/examples/flutter.factory.json +13 -0
- package/examples/flutter.json +16 -0
- package/examples/luggage.json +5 -3
- package/examples/mygo-web.factory.json +18 -0
- package/examples/mygo-web.json +16 -0
- package/examples/mygo.factory.json +18 -0
- package/examples/mygo.json +16 -0
- package/examples/react-native.factory.json +18 -0
- package/examples/react-native.json +16 -0
- package/examples/runtime/android-java/app/build.gradle +7 -0
- package/examples/runtime/android-java/app/src/main/AndroidManifest.xml +8 -0
- package/examples/runtime/android-java/app/src/main/java/dev/apphx/runtime/MainActivity.java +44 -0
- package/examples/runtime/android-java/sdk-build.mjs +29 -0
- package/examples/runtime/android-java/settings.gradle +2 -0
- package/examples/runtime/android-ui.mjs +70 -0
- package/examples/runtime/mygo-native/go.mod +13 -0
- package/examples/runtime/mygo-native/go.sum +10 -0
- package/examples/runtime/mygo-native/main.go +173 -0
- package/examples/runtime/mygo-native/main_test.go +14 -0
- package/examples/runtime/mygo-native/mygo.json +1 -0
- package/examples/runtime/mygo-web/bun.lock +15 -0
- package/examples/runtime/mygo-web/go.mod +9 -0
- package/examples/runtime/mygo-web/go.sum +8 -0
- package/examples/runtime/mygo-web/index.html +1 -0
- package/examples/runtime/mygo-web/main.go +60 -0
- package/examples/runtime/mygo-web/mygo.config.ts +10 -0
- package/examples/runtime/mygo-web/package.json +1 -0
- package/examples/runtime/mygo-web/src/main.ts +23 -0
- package/examples/runtime/mygo-web/src/mygo.ts +14 -0
- package/examples/swift.factory.json +18 -0
- package/examples/swift.json +16 -0
- package/examples/uni-app.factory.json +18 -0
- package/examples/uni-app.json +16 -0
- package/package.json +12 -5
- package/scripts/bundle-obfuscator.mjs +19 -0
- package/scripts/runtime-smoke.mjs +209 -0
- package/skills/apphx/SKILL.md +94 -17
- package/skills/apphx/resources/google-play-research-result.json +6 -0
- package/skills/apphx/resources/local-research-result.json +6 -0
- package/skills/apphx/resources/requirement.json +5 -8
- package/skills/apphx/resources/research-result.json +5 -14
- package/skills/apphx/resources/review-result.json +1 -1
- package/dist/apple.js +0 -900
- package/dist/apple.js.map +0 -1
package/dist/dependencies.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { canonicalPath, inside } from "./config.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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
|
|
43
|
-
const manifest = join(
|
|
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(
|
|
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}`) ||
|
|
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 ?
|
|
209
|
+
pathDependency(name, overridden ? overrides[name] : dependency, overridden ? project : directory);
|
|
88
210
|
}
|
|
89
211
|
}
|
|
90
|
-
visit(
|
|
91
|
-
const lockFile = join(
|
|
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 },
|
|
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(
|
|
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) ===
|
|
235
|
+
visit(packageRoot, canonicalPath(packageRoot) === canonicalPath(project));
|
|
114
236
|
}
|
|
115
237
|
}
|
|
116
|
-
function
|
|
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
|
-
|
|
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
|