diff --git a/bun.lock b/bun.lock index b81e47f..58849df 100644 --- a/bun.lock +++ b/bun.lock @@ -14,20 +14,30 @@ "expo-status-bar": "57.0.1", "expo-system-ui": "57.0.1", "nativewind": "4.2.6", - "officer-net": "file:./modules/officer-net", + "officer-net": "workspace:*", "react": "19.2.3", "react-native": "0.86.0", + "react-native-reanimated": "4.5.3", "react-native-safe-area-context": "5.7.0", "react-native-svg": "15.15.4", + "react-native-worklets": "0.11.3", }, "devDependencies": { "@babel/core": "7.29.7", + "@babel/plugin-transform-react-jsx": "7.29.7", "@types/react": "19.2.17", "babel-preset-expo": "57.0.4", "tailwindcss": "3.4.19", "typescript": "6.0.3", }, }, + "modules/officer-net": { + "name": "officer-net", + "version": "0.1.0", + "dependencies": { + "@expo/config-plugins": "^57.0.0", + }, + }, }, "packages": { "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], @@ -776,7 +786,7 @@ "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="], - "officer-net": ["officer-net@file:modules/officer-net", { "dependencies": { "@expo/config-plugins": "^57.0.0" } }], + "officer-net": ["officer-net@workspace:modules/officer-net"], "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], @@ -858,7 +868,7 @@ "react-native-svg": ["react-native-svg@15.15.4", "", { "dependencies": { "css-select": "^5.1.0", "css-tree": "^1.1.3", "warn-once": "0.1.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-boT/vIRgj6zZKBpfTPJJiYWMbZE9duBMOwPK6kCSTgxsS947IFMOq9OgIFkpWZTB7t229H24pDRkh3W9ZK/J1A=="], - "react-native-worklets": ["react-native-worklets@0.11.4", "", { "dependencies": { "@babel/generator": "^7.27.1", "@babel/plugin-transform-arrow-functions": "^7.27.1", "@babel/plugin-transform-class-properties": "^7.28.6", "@babel/plugin-transform-classes": "^7.28.6", "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", "@babel/plugin-transform-optional-chaining": "^7.28.6", "@babel/plugin-transform-shorthand-properties": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-unicode-regex": "^7.27.1", "@babel/preset-typescript": "^7.28.5", "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1", "convert-source-map": "^2.0.0", "semver": "^7.7.4" }, "peerDependencies": { "@babel/core": "*", "@react-native/metro-config": "*", "react": "*", "react-native": "0.83 - 0.86" } }, "sha512-yNiDDQAVvt1wacBhnrEMTBlQzZ8Y8Rg7Dbdxs/r595EXw4REsZGE5sY1Ug8SmlQUT+0L+fX7oY0SuCs0mjiA4w=="], + "react-native-worklets": ["react-native-worklets@0.11.3", "", { "dependencies": { "@babel/generator": "^7.27.1", "@babel/plugin-transform-arrow-functions": "^7.27.1", "@babel/plugin-transform-class-properties": "^7.28.6", "@babel/plugin-transform-classes": "^7.28.6", "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", "@babel/plugin-transform-optional-chaining": "^7.28.6", "@babel/plugin-transform-shorthand-properties": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-unicode-regex": "^7.27.1", "@babel/preset-typescript": "^7.28.5", "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1", "convert-source-map": "^2.0.0", "semver": "^7.7.4" }, "peerDependencies": { "@babel/core": "*", "@react-native/metro-config": "*", "react": "*", "react-native": "0.83 - 0.86" } }, "sha512-paLRlVFTrrGhkGTi37LHwtaJbrTftA54/zAx1h/ONSBw7XejZn6XUUl1KpM6EOMwsbw7TEGuxJXTAp/wkvuMqQ=="], "react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="], diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..1a349a0 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,14 @@ +# React Native needs a HOISTED node_modules, and declaring `workspaces` silently changes that. +# +# `modules/officer-net` is a workspace so that node_modules/officer-net is a SYMLINK rather than a +# copy — with a `file:` dependency bun copies the package, and an edit to the config plugin then has no +# effect on the next prebuild while looking like it should. That cost three builds to find. +# +# But adding `workspaces` also flips bun to its isolated linker: only direct dependencies sit at the +# top level and everything else is symlinked into node_modules/.bun. Metro cannot resolve that — the +# failure is `Unable to resolve module react-native-css-interop/jsx-runtime`, naming a package that is +# installed, present in the lockfile, and simply not where the resolver looks. +# +# So: workspaces for the link, hoisted for the layout. +[install] +linker = "hoisted" diff --git a/modules/officer-net/plugin/withNetworkExtensionTarget.js b/modules/officer-net/plugin/withNetworkExtensionTarget.js index 2ae62f7..f84b010 100644 --- a/modules/officer-net/plugin/withNetworkExtensionTarget.js +++ b/modules/officer-net/plugin/withNetworkExtensionTarget.js @@ -110,12 +110,26 @@ function withNeTarget(config, props) { proj.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid); proj.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', target.uuid); - // Link the vendored gomobile Tailscale core (TailCore.xcframework) into the NE. The `xcode` lib has - // no .xcframework extension mapping, so we MUST force lastKnownFileType='wrapper.xcframework' (else it - // types it 'unknown' and Xcode never resolves the per-slice module → "no such module 'TailCore'"). - // Path is relative to SOURCE_ROOT (the ios/ project dir) and includes a dirname so the search path - // is registered. Located in the package's libs/ (../../../ from apps/*/ios up to packages/). - proj.addFramework('../../../packages/officer-net/ios/libs/TailCore.xcframework', { + /* + Link the vendored gomobile Tailscale core (TailCore.xcframework) into the NE. + + The `xcode` lib has no .xcframework extension mapping, so lastKnownFileType MUST be forced to + 'wrapper.xcframework' — left as 'unknown', Xcode never resolves the per-slice module and the build + fails with "no such module 'TailCore'". + + The path is DERIVED, not written down. It used to read `../../../packages/officer-net/ios/libs`, + which encodes one repository's shape — correct from `apps//ios` in the monorepo, and wrong + everywhere else. Moving this module into a repo of its own produced exactly that: the Go core built + cleanly to the right place, then xcodebuild looked for it three levels up from a directory that no + longer had three levels, and reported a path belonging to neither repo. + + Computing it from this file's own location cannot drift: wherever the module sits relative to the + generated ios/ directory, `path.relative` says so. + */ + const libsDir = path.join(__dirname, '..', 'ios', 'libs'); + const iosDir = cfg.modRequest.platformProjectRoot; + const relLibs = path.relative(iosDir, libsDir); + proj.addFramework(path.join(relLibs, 'TailCore.xcframework'), { target: target.uuid, customFramework: true, lastKnownFileType: 'wrapper.xcframework', @@ -126,8 +140,8 @@ function withNeTarget(config, props) { }); const devTeam = mainAppDevTeam(proj); - // Framework search path → the package's libs dir (from ios/: up to the monorepo, into packages). - const fwSearch = '"$(PROJECT_DIR)/../../../packages/officer-net/ios/libs"'; + // Same derivation for the search path — the two must agree, and hardcoding either is how they stop. + const fwSearch = `"$(PROJECT_DIR)/${relLibs}"`; const configs = proj.pbxXCBuildConfigurationSection(); for (const key in configs) { diff --git a/package.json b/package.json index 75ddeda..3fe2bda 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,9 @@ "version": "1.0.0", "license": "MIT", "private": true, + "workspaces": [ + "modules/*" + ], "main": "src/index.tsx", "scripts": { "start": "expo start", @@ -21,14 +24,17 @@ "expo-status-bar": "57.0.1", "expo-system-ui": "57.0.1", "nativewind": "4.2.6", - "officer-net": "file:./modules/officer-net", + "officer-net": "workspace:*", "react": "19.2.3", "react-native": "0.86.0", + "react-native-reanimated": "4.5.3", "react-native-safe-area-context": "5.7.0", - "react-native-svg": "15.15.4" + "react-native-svg": "15.15.4", + "react-native-worklets": "0.11.3" }, "devDependencies": { "@babel/core": "7.29.7", + "@babel/plugin-transform-react-jsx": "7.29.7", "@types/react": "19.2.17", "babel-preset-expo": "57.0.4", "tailwindcss": "3.4.19",