@tamer4lynx/cli 0.0.2 → 0.0.4

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 (3) hide show
  1. package/README.md +11 -10
  2. package/dist/index.js +477 -1781
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,12 +7,12 @@ process.on("warning", (w) => {
7
7
  });
8
8
 
9
9
  // index.ts
10
- import fs25 from "fs";
11
- import path25 from "path";
10
+ import fs23 from "fs";
11
+ import path24 from "path";
12
12
  import { program } from "commander";
13
13
 
14
14
  // package.json
15
- var version = "0.0.2";
15
+ var version = "0.0.4";
16
16
 
17
17
  // src/android/create.ts
18
18
  import fs3 from "fs";
@@ -213,6 +213,7 @@ function findRepoRoot(start2) {
213
213
  }
214
214
  function findDevAppPackage(projectRoot) {
215
215
  const candidates = [
216
+ path2.join(projectRoot, "node_modules", "@tamer4lynx", "tamer-dev-app"),
216
217
  path2.join(projectRoot, "node_modules", "tamer-dev-app"),
217
218
  path2.join(projectRoot, "packages", "tamer-dev-app"),
218
219
  path2.join(path2.dirname(projectRoot), "tamer-dev-app")
@@ -226,6 +227,7 @@ function findDevAppPackage(projectRoot) {
226
227
  }
227
228
  function findDevClientPackage(projectRoot) {
228
229
  const candidates = [
230
+ path2.join(projectRoot, "node_modules", "@tamer4lynx", "tamer-dev-client"),
229
231
  path2.join(projectRoot, "node_modules", "tamer-dev-client"),
230
232
  path2.join(projectRoot, "packages", "tamer-dev-client"),
231
233
  path2.join(path2.dirname(projectRoot), "tamer-dev-client")
@@ -264,11 +266,8 @@ function resolveHostPaths(cwd = process.cwd()) {
264
266
  const lynxBundlePath = path2.join(lynxProjectDir, bundleRoot, bundleFile);
265
267
  const androidDir = path2.join(projectRoot, androidDirRel);
266
268
  const devMode = resolveDevMode(config);
267
- const devAppDir = devMode === "embedded" ? findDevAppPackage(projectRoot) : null;
268
269
  const devClientPkg = findDevClientPackage(projectRoot);
269
- const devClientBundleInClient = devClientPkg ? path2.join(devClientPkg, DEFAULT_BUNDLE_ROOT, "dev-client.lynx.bundle") : null;
270
- const devClientBundleInApp = devAppDir ? path2.join(devAppDir, DEFAULT_BUNDLE_ROOT, "dev-client.lynx.bundle") : null;
271
- const devClientBundlePath = devMode === "embedded" ? devClientBundleInClient && fs2.existsSync(devClientBundleInClient) ? devClientBundleInClient : devClientBundleInApp ?? void 0 : void 0;
270
+ const devClientBundlePath = devClientPkg ? path2.join(devClientPkg, DEFAULT_BUNDLE_ROOT, "dev-client.lynx.bundle") : void 0;
272
271
  return {
273
272
  projectRoot,
274
273
  androidDir,
@@ -321,33 +320,6 @@ function resolveIconPaths(projectRoot, config) {
321
320
  }
322
321
  return Object.keys(out).length ? out : null;
323
322
  }
324
- function resolveDevAppPaths(repoRoot) {
325
- const devAppDir = path2.join(repoRoot, "packages", "tamer-dev-app");
326
- const configPath = path2.join(devAppDir, "tamer.config.json");
327
- if (!fs2.existsSync(configPath)) {
328
- throw new Error("packages/tamer-dev-app/tamer.config.json not found.");
329
- }
330
- const config = JSON.parse(fs2.readFileSync(configPath, "utf8"));
331
- const packageName = config.android?.packageName ?? "com.nanofuxion.tamerdevapp";
332
- const androidDirRel = config.paths?.androidDir ?? "android";
333
- const androidDir = path2.join(devAppDir, androidDirRel);
334
- const devClientDir = findDevClientPackage(repoRoot) ?? path2.join(repoRoot, "packages", "tamer-dev-client");
335
- const lynxBundlePath = path2.join(devClientDir, DEFAULT_BUNDLE_ROOT, "dev-client.lynx.bundle");
336
- return {
337
- projectRoot: devAppDir,
338
- androidDir,
339
- iosDir: path2.join(devAppDir, "ios"),
340
- androidAppDir: path2.join(androidDir, "app"),
341
- androidAssetsDir: path2.join(androidDir, "app", "src", "main", "assets"),
342
- androidKotlinDir: path2.join(androidDir, "app", "src", "main", "kotlin", packageName.replace(/\./g, "/")),
343
- lynxProjectDir: devClientDir,
344
- lynxBundlePath,
345
- lynxBundleFile: "dev-client.lynx.bundle",
346
- devMode: "embedded",
347
- devClientBundlePath: void 0,
348
- config
349
- };
350
- }
351
323
 
352
324
  // src/explorer/ref.ts
353
325
  var LYNX_RAW_BASE = "https://raw.githubusercontent.com/lynx-family/lynx/develop/explorer";
@@ -919,30 +891,13 @@ function readAndSubstituteTemplate(templatePath, vars) {
919
891
  raw
920
892
  );
921
893
  }
922
- function findRepoRoot2(start2) {
923
- let dir = path3.resolve(start2);
924
- const root = path3.parse(dir).root;
925
- while (dir !== root) {
926
- const pkgPath = path3.join(dir, "package.json");
927
- if (fs3.existsSync(pkgPath)) {
928
- try {
929
- const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf8"));
930
- if (pkg.workspaces) return dir;
931
- } catch {
932
- }
933
- }
934
- dir = path3.dirname(dir);
935
- }
936
- return start2;
937
- }
938
894
  var create = async (opts = {}) => {
939
895
  const target = opts.target ?? "host";
940
896
  const origCwd = process.cwd();
941
897
  if (target === "dev-app") {
942
- const repoRoot = findRepoRoot2(origCwd);
943
- const devAppDir = path3.join(repoRoot, "packages", "tamer-dev-app");
944
- if (!fs3.existsSync(path3.join(devAppDir, "tamer.config.json"))) {
945
- console.error("\u274C packages/tamer-dev-app/tamer.config.json not found.");
898
+ const devAppDir = findDevAppPackage(origCwd) ?? findDevAppPackage(findRepoRoot(origCwd));
899
+ if (!devAppDir || !fs3.existsSync(path3.join(devAppDir, "tamer.config.json"))) {
900
+ console.error("\u274C tamer-dev-app not found. Add @tamer4lynx/tamer-dev-app to dependencies.");
946
901
  process.exit(1);
947
902
  }
948
903
  process.chdir(devAppDir);
@@ -980,7 +935,7 @@ var create = async (opts = {}) => {
980
935
  const assetsDir = path3.join(mainDir, "assets");
981
936
  const themesDir = path3.join(mainDir, "res", "values");
982
937
  const gradleDir = path3.join(rootDir, "gradle");
983
- function writeFile3(filePath, content, options) {
938
+ function writeFile2(filePath, content, options) {
984
939
  fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
985
940
  fs3.writeFileSync(
986
941
  filePath,
@@ -993,7 +948,7 @@ var create = async (opts = {}) => {
993
948
  fs3.rmSync(rootDir, { recursive: true, force: true });
994
949
  }
995
950
  console.log(`\u{1F680} Creating a new Tamer4Lynx project in: ${rootDir}`);
996
- writeFile3(
951
+ writeFile2(
997
952
  path3.join(gradleDir, "libs.versions.toml"),
998
953
  `
999
954
  [versions]
@@ -1052,7 +1007,7 @@ kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "ko
1052
1007
  kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
1053
1008
  `
1054
1009
  );
1055
- writeFile3(
1010
+ writeFile2(
1056
1011
  path3.join(rootDir, "settings.gradle.kts"),
1057
1012
  `
1058
1013
  pluginManagement {
@@ -1086,7 +1041,7 @@ println("If you have native modules please run tamer android link")
1086
1041
  // GENERATED AUTOLINK END
1087
1042
  `
1088
1043
  );
1089
- writeFile3(
1044
+ writeFile2(
1090
1045
  path3.join(rootDir, "build.gradle.kts"),
1091
1046
  `
1092
1047
  // Top-level build file where you can add configuration options common to all sub-projects/modules.
@@ -1098,7 +1053,7 @@ plugins {
1098
1053
  }
1099
1054
  `
1100
1055
  );
1101
- writeFile3(
1056
+ writeFile2(
1102
1057
  path3.join(rootDir, "gradle.properties"),
1103
1058
  `
1104
1059
  org.gradle.jvmargs=-Xmx2048m
@@ -1107,7 +1062,7 @@ kotlin.code.style=official
1107
1062
  android.enableJetifier=true
1108
1063
  `
1109
1064
  );
1110
- writeFile3(
1065
+ writeFile2(
1111
1066
  path3.join(appDir, "build.gradle.kts"),
1112
1067
  `
1113
1068
  plugins {
@@ -1197,7 +1152,7 @@ dependencies {
1197
1152
  }
1198
1153
  `
1199
1154
  );
1200
- writeFile3(
1155
+ writeFile2(
1201
1156
  path3.join(themesDir, "themes.xml"),
1202
1157
  `
1203
1158
  <resources>
@@ -1231,7 +1186,7 @@ dependencies {
1231
1186
  <uses-permission android:name="android.permission.CAMERA" />` : ` <uses-permission android:name="android.permission.INTERNET" />`;
1232
1187
  const iconPaths = resolveIconPaths(process.cwd(), config);
1233
1188
  const manifestIconAttrs = iconPaths ? ' android:icon="@mipmap/ic_launcher"\n android:roundIcon="@mipmap/ic_launcher"\n' : "";
1234
- writeFile3(
1189
+ writeFile2(
1235
1190
  path3.join(mainDir, "AndroidManifest.xml"),
1236
1191
  `
1237
1192
  <manifest xmlns:android="http://schemas.android.com/apk/res/android">
@@ -1245,7 +1200,7 @@ ${manifestIconAttrs} android:usesCleartextTraffic="true"
1245
1200
  </manifest>
1246
1201
  `
1247
1202
  );
1248
- writeFile3(
1203
+ writeFile2(
1249
1204
  path3.join(kotlinGeneratedDir, "GeneratedLynxExtensions.kt"),
1250
1205
  `
1251
1206
  package ${packageName}.generated
@@ -1282,7 +1237,7 @@ object GeneratedLynxExtensions {
1282
1237
  ]) {
1283
1238
  const srcPath = path3.join(templateDir, src);
1284
1239
  if (fs3.existsSync(srcPath)) {
1285
- writeFile3(dst, readAndSubstituteTemplate(srcPath, templateVars));
1240
+ writeFile2(dst, readAndSubstituteTemplate(srcPath, templateVars));
1286
1241
  }
1287
1242
  }
1288
1243
  } else {
@@ -1290,9 +1245,9 @@ object GeneratedLynxExtensions {
1290
1245
  fetchAndPatchApplication(vars),
1291
1246
  fetchAndPatchTemplateProvider(vars)
1292
1247
  ]);
1293
- writeFile3(path3.join(javaDir, "App.java"), applicationSource);
1294
- writeFile3(path3.join(javaDir, "TemplateProvider.java"), templateProviderSource);
1295
- writeFile3(path3.join(kotlinDir, "MainActivity.kt"), getStandaloneMainActivity(vars));
1248
+ writeFile2(path3.join(javaDir, "App.java"), applicationSource);
1249
+ writeFile2(path3.join(javaDir, "TemplateProvider.java"), templateProviderSource);
1250
+ writeFile2(path3.join(kotlinDir, "MainActivity.kt"), getStandaloneMainActivity(vars));
1296
1251
  if (hasDevLauncher) {
1297
1252
  if (devClientPkg) {
1298
1253
  const templateDir = path3.join(devClientPkg, "android", "templates");
@@ -1303,15 +1258,15 @@ object GeneratedLynxExtensions {
1303
1258
  ]) {
1304
1259
  const srcPath = path3.join(templateDir, src);
1305
1260
  if (fs3.existsSync(srcPath)) {
1306
- writeFile3(dst, readAndSubstituteTemplate(srcPath, templateVars));
1261
+ writeFile2(dst, readAndSubstituteTemplate(srcPath, templateVars));
1307
1262
  }
1308
1263
  }
1309
1264
  } else {
1310
- writeFile3(path3.join(kotlinDir, "ProjectActivity.kt"), getProjectActivity(vars));
1265
+ writeFile2(path3.join(kotlinDir, "ProjectActivity.kt"), getProjectActivity(vars));
1311
1266
  const devClientManagerSource = getDevClientManager(vars);
1312
1267
  if (devClientManagerSource) {
1313
- writeFile3(path3.join(kotlinDir, "DevClientManager.kt"), devClientManagerSource);
1314
- writeFile3(path3.join(kotlinDir, "DevServerPrefs.kt"), getDevServerPrefs(vars));
1268
+ writeFile2(path3.join(kotlinDir, "DevClientManager.kt"), devClientManagerSource);
1269
+ writeFile2(path3.join(kotlinDir, "DevServerPrefs.kt"), getDevServerPrefs(vars));
1315
1270
  }
1316
1271
  }
1317
1272
  }
@@ -1348,7 +1303,7 @@ object GeneratedLynxExtensions {
1348
1303
  if (androidSdk) {
1349
1304
  try {
1350
1305
  const sdkDirContent = `sdk.dir=${androidSdk.replace(/\\/g, "/")}`;
1351
- writeFile3(path3.join(rootDir, "local.properties"), sdkDirContent);
1306
+ writeFile2(path3.join(rootDir, "local.properties"), sdkDirContent);
1352
1307
  console.log("\u{1F4E6} Created local.properties from tamer.config.json.");
1353
1308
  } catch (err) {
1354
1309
  console.error(`\u274C Failed to create local.properties: ${err.message}`);
@@ -1697,6 +1652,17 @@ ${createDelayedBody}
1697
1652
  }
1698
1653
 
1699
1654
  // src/android/autolink.ts
1655
+ var TAMER_DEV_CLIENT_FALLBACK = {
1656
+ name: "@tamer4lynx/tamer-dev-client",
1657
+ packagePath: "",
1658
+ config: {
1659
+ android: {
1660
+ moduleClassName: "com.nanofuxion.tamerdevclient.DevClientModule",
1661
+ sourceDir: "android",
1662
+ permissions: ["CAMERA", "ACCESS_NETWORK_STATE", "ACCESS_WIFI_STATE"]
1663
+ }
1664
+ }
1665
+ };
1700
1666
  var REQUIRED_CATALOG_ENTRIES = {
1701
1667
  "androidx.biometric": {
1702
1668
  versionRef: "biometric",
@@ -1761,12 +1727,11 @@ ${replacementBlock}
1761
1727
  androidPackages.forEach((pkg) => {
1762
1728
  const gradleProjectName = pkg.name.replace(/^@/, "").replace(/\//g, "_");
1763
1729
  const sourceDir = pkg.config.android?.sourceDir || "android";
1764
- const projectPath = path6.join(pkg.packagePath, sourceDir).replace(/\\/g, "/");
1765
- const relativePath = path6.relative(appAndroidPath, projectPath).replace(/\\/g, "/");
1730
+ const projectPath = path6.resolve(pkg.packagePath, sourceDir).replace(/\\/g, "/");
1766
1731
  scriptContent += `
1767
1732
  include(":${gradleProjectName}")`;
1768
1733
  scriptContent += `
1769
- project(":${gradleProjectName}").projectDir = file("${relativePath}")`;
1734
+ project(":${gradleProjectName}").projectDir = file("${projectPath}")`;
1770
1735
  });
1771
1736
  } else {
1772
1737
  scriptContent += `
@@ -1844,7 +1809,19 @@ ${generateActivityLifecycleKotlin(packages, projectPackage)}`;
1844
1809
  }
1845
1810
  function run() {
1846
1811
  console.log("\u{1F50E} Finding Lynx extension packages (lynx.ext.json / tamer.json)...");
1847
- const packages = discoverModules(projectRoot).filter((p) => p.config.android);
1812
+ let packages = discoverModules(projectRoot).filter((p) => p.config.android);
1813
+ const isDevApp = packageName === "com.nanofuxion.tamerdevapp";
1814
+ const devClientScoped = path6.join(projectRoot, "node_modules", "@tamer4lynx", "tamer-dev-client");
1815
+ const devClientFlat = path6.join(projectRoot, "node_modules", "tamer-dev-client");
1816
+ const devClientPath = fs6.existsSync(path6.join(devClientScoped, "android")) ? devClientScoped : fs6.existsSync(path6.join(devClientFlat, "android")) ? devClientFlat : null;
1817
+ const hasDevClient = packages.some((p) => p.name === "@tamer4lynx/tamer-dev-client" || p.name === "tamer-dev-client");
1818
+ if (isDevApp && devClientPath && !hasDevClient) {
1819
+ packages = [{
1820
+ ...TAMER_DEV_CLIENT_FALLBACK,
1821
+ packagePath: devClientPath
1822
+ }, ...packages];
1823
+ console.log("\u2139\uFE0F Added tamer-dev-client (fallback for dev-app; lynx.ext.json missing in published package).");
1824
+ }
1848
1825
  if (packages.length > 0) {
1849
1826
  console.log(`Found ${packages.length} package(s): ${packages.map((p) => p.name).join(", ")}`);
1850
1827
  } else {
@@ -2013,7 +1990,10 @@ async function syncDevClient(opts) {
2013
1990
  console.error("\u274C Android project not found. Run `tamer android create` first.");
2014
1991
  process.exit(1);
2015
1992
  }
2016
- const devMode = opts?.forceProduction ? "standalone" : resolved.devMode;
1993
+ const devClientPkg = findDevClientPackage(resolved.projectRoot);
1994
+ const includeDevClient = opts?.includeDevClient ?? (opts?.forceProduction ? false : !!devClientPkg);
1995
+ const hasDevClient = includeDevClient && devClientPkg;
1996
+ const devMode = hasDevClient ? "embedded" : "standalone";
2017
1997
  const devServer = config.devServer ? {
2018
1998
  host: config.devServer.host ?? "10.0.2.2",
2019
1999
  port: config.devServer.port ?? config.devServer.httpPort ?? 3e3
@@ -2027,8 +2007,6 @@ async function syncDevClient(opts) {
2027
2007
  const appDir = path8.join(rootDir, "app");
2028
2008
  const mainDir = path8.join(appDir, "src", "main");
2029
2009
  const manifestPath = path8.join(mainDir, "AndroidManifest.xml");
2030
- const devClientPkg = findDevClientPackage(resolved.projectRoot);
2031
- const hasDevClient = devMode === "embedded" && devClientPkg;
2032
2010
  if (hasDevClient) {
2033
2011
  const templateDir = path8.join(devClientPkg, "android", "templates");
2034
2012
  const templateVars = { PACKAGE_NAME: packageName, APP_NAME: appName };
@@ -2087,71 +2065,41 @@ $1$2`);
2087
2065
  );
2088
2066
  }
2089
2067
  fs8.writeFileSync(manifestPath, manifest);
2090
- console.log('\u2705 Synced (dev client disabled - set dev.mode: "embedded" in tamer.config.json to enable)');
2068
+ console.log("\u2705 Synced (dev client disabled - use -d for debug build with dev client)");
2091
2069
  }
2092
2070
  }
2093
2071
  var syncDevClient_default = syncDevClient;
2094
2072
 
2095
2073
  // src/android/bundle.ts
2096
- function findRepoRoot3(start2) {
2097
- let dir = path9.resolve(start2);
2098
- const root = path9.parse(dir).root;
2099
- while (dir !== root) {
2100
- const pkgPath = path9.join(dir, "package.json");
2101
- if (fs9.existsSync(pkgPath)) {
2102
- try {
2103
- const pkg = JSON.parse(fs9.readFileSync(pkgPath, "utf8"));
2104
- if (pkg.workspaces) return dir;
2105
- } catch {
2106
- }
2107
- }
2108
- dir = path9.dirname(dir);
2109
- }
2110
- return start2;
2111
- }
2112
2074
  async function bundleAndDeploy(opts = {}) {
2113
- const target = opts.target ?? "host";
2114
2075
  const release = opts.release === true;
2115
- const origCwd = process.cwd();
2116
2076
  let resolved;
2117
2077
  try {
2118
- if (target === "dev-app") {
2119
- const repoRoot = findRepoRoot3(origCwd);
2120
- resolved = resolveDevAppPaths(repoRoot);
2121
- const devAppDir = resolved.projectRoot;
2122
- const androidDir = resolved.androidDir;
2123
- if (!fs9.existsSync(androidDir)) {
2124
- console.log("\u{1F4F1} Creating Tamer Dev App Android project...");
2125
- await create_default({ target: "dev-app" });
2126
- }
2127
- process.chdir(devAppDir);
2128
- } else {
2129
- resolved = resolveHostPaths();
2130
- }
2078
+ resolved = resolveHostPaths();
2131
2079
  } catch (error) {
2132
2080
  console.error(`\u274C Error loading configuration: ${error.message}`);
2133
2081
  process.exit(1);
2134
2082
  }
2135
- const { lynxProjectDir, lynxBundlePath, androidAssetsDir, devClientBundlePath, devMode } = resolved;
2083
+ const { projectRoot, lynxProjectDir, lynxBundlePath, androidAssetsDir, devClientBundlePath } = resolved;
2084
+ const devClientPkg = findDevClientPackage(projectRoot);
2085
+ const includeDevClient = !release && !!devClientPkg;
2136
2086
  const destinationDir = androidAssetsDir;
2137
2087
  autolink_default();
2138
- if (release) {
2139
- await syncDevClient_default({ forceProduction: true });
2140
- } else if (devMode === "embedded") {
2141
- await syncDevClient_default();
2142
- }
2143
- try {
2144
- console.log("\u{1F4E6} Building Lynx bundle...");
2145
- execSync2("npm run build", { stdio: "inherit", cwd: lynxProjectDir });
2146
- console.log("\u2705 Build completed successfully.");
2147
- } catch (error) {
2148
- console.error("\u274C Build process failed.");
2149
- process.exit(1);
2150
- }
2151
- if (target === "dev-app") {
2152
- process.chdir(origCwd);
2088
+ await syncDevClient_default({ includeDevClient });
2089
+ const bundleExists = fs9.existsSync(lynxBundlePath);
2090
+ if (!bundleExists) {
2091
+ try {
2092
+ console.log("\u{1F4E6} Building Lynx bundle...");
2093
+ execSync2("npm run build", { stdio: "inherit", cwd: lynxProjectDir });
2094
+ console.log("\u2705 Build completed successfully.");
2095
+ } catch (error) {
2096
+ console.error("\u274C Build process failed.");
2097
+ process.exit(1);
2098
+ }
2099
+ } else {
2100
+ console.log("\u{1F4E6} Using pre-built Lynx bundle.");
2153
2101
  }
2154
- if (target !== "dev-app" && !release && devMode === "embedded" && devClientBundlePath && !fs9.existsSync(devClientBundlePath)) {
2102
+ if (includeDevClient && devClientBundlePath && !fs9.existsSync(devClientBundlePath)) {
2155
2103
  const devClientDir = path9.dirname(path9.dirname(devClientBundlePath));
2156
2104
  try {
2157
2105
  console.log("\u{1F4E6} Building dev launcher (tamer-dev-client)...");
@@ -2164,13 +2112,13 @@ async function bundleAndDeploy(opts = {}) {
2164
2112
  }
2165
2113
  try {
2166
2114
  fs9.mkdirSync(destinationDir, { recursive: true });
2167
- if (target !== "dev-app" && release) {
2115
+ if (release) {
2168
2116
  const devClientAsset = path9.join(destinationDir, "dev-client.lynx.bundle");
2169
2117
  if (fs9.existsSync(devClientAsset)) {
2170
2118
  fs9.rmSync(devClientAsset);
2171
2119
  console.log(`\u2728 Removed dev-client.lynx.bundle from assets (production build)`);
2172
2120
  }
2173
- } else if (target !== "dev-app" && devMode === "embedded" && devClientBundlePath && fs9.existsSync(devClientBundlePath)) {
2121
+ } else if (includeDevClient && devClientBundlePath && fs9.existsSync(devClientBundlePath)) {
2174
2122
  fs9.copyFileSync(devClientBundlePath, path9.join(destinationDir, "dev-client.lynx.bundle"));
2175
2123
  console.log(`\u2728 Copied dev-client.lynx.bundle to assets`);
2176
2124
  }
@@ -2189,29 +2137,16 @@ async function bundleAndDeploy(opts = {}) {
2189
2137
  var bundle_default = bundleAndDeploy;
2190
2138
 
2191
2139
  // src/android/build.ts
2192
- import fs10 from "fs";
2193
2140
  import path10 from "path";
2194
2141
  import { execSync as execSync3 } from "child_process";
2195
- function findRepoRoot4(start2) {
2196
- let dir = path10.resolve(start2);
2197
- const root = path10.parse(dir).root;
2198
- while (dir !== root) {
2199
- const pkgPath = path10.join(dir, "package.json");
2200
- if (fs10.existsSync(pkgPath)) {
2201
- try {
2202
- const pkg = JSON.parse(fs10.readFileSync(pkgPath, "utf8"));
2203
- if (pkg.workspaces) return dir;
2204
- } catch {
2205
- }
2206
- }
2207
- dir = path10.dirname(dir);
2208
- }
2209
- return start2;
2210
- }
2211
2142
  async function buildApk(opts = {}) {
2212
- const target = opts.target ?? "host";
2213
- const resolved = target === "dev-app" ? resolveDevAppPaths(findRepoRoot4(process.cwd())) : resolveHostPaths();
2214
- await bundle_default({ target, release: opts.release });
2143
+ let resolved;
2144
+ try {
2145
+ resolved = resolveHostPaths();
2146
+ } catch (error) {
2147
+ throw error;
2148
+ }
2149
+ await bundle_default({ release: opts.release });
2215
2150
  const androidDir = resolved.androidDir;
2216
2151
  const gradlew = path10.join(androidDir, process.platform === "win32" ? "gradlew.bat" : "gradlew");
2217
2152
  const variant = opts.release ? "Release" : "Debug";
@@ -2238,12 +2173,12 @@ async function buildApk(opts = {}) {
2238
2173
  var build_default = buildApk;
2239
2174
 
2240
2175
  // src/ios/create.ts
2241
- import fs12 from "fs";
2176
+ import fs11 from "fs";
2242
2177
  import path12 from "path";
2243
2178
 
2244
2179
  // src/ios/getPod.ts
2245
2180
  import { execSync as execSync4 } from "child_process";
2246
- import fs11 from "fs";
2181
+ import fs10 from "fs";
2247
2182
  import path11 from "path";
2248
2183
  function isCocoaPodsInstalled() {
2249
2184
  try {
@@ -2267,7 +2202,7 @@ async function setupCocoaPods(rootDir) {
2267
2202
  try {
2268
2203
  console.log("\u{1F4E6} CocoaPods is installed. Proceeding with dependency installation...");
2269
2204
  const podfilePath = path11.join(rootDir, "Podfile");
2270
- if (!fs11.existsSync(podfilePath)) {
2205
+ if (!fs10.existsSync(podfilePath)) {
2271
2206
  throw new Error(`Podfile not found at ${podfilePath}`);
2272
2207
  }
2273
2208
  console.log(`\u{1F680} Executing pod install in: ${rootDir}`);
@@ -2285,14 +2220,14 @@ async function setupCocoaPods(rootDir) {
2285
2220
  // src/ios/create.ts
2286
2221
  import { randomBytes } from "crypto";
2287
2222
  function readAndSubstituteTemplate3(templatePath, vars) {
2288
- const raw = fs12.readFileSync(templatePath, "utf-8");
2223
+ const raw = fs11.readFileSync(templatePath, "utf-8");
2289
2224
  return Object.entries(vars).reduce(
2290
2225
  (s, [k, v]) => s.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v),
2291
2226
  raw
2292
2227
  );
2293
2228
  }
2294
2229
  var create2 = () => {
2295
- const generateId2 = () => randomBytes(12).toString("hex").toUpperCase();
2230
+ const generateId = () => randomBytes(12).toString("hex").toUpperCase();
2296
2231
  let appName;
2297
2232
  let bundleId;
2298
2233
  let config;
@@ -2312,49 +2247,49 @@ var create2 = () => {
2312
2247
  const projectDir = path12.join(rootDir, appName);
2313
2248
  const xcodeprojDir = path12.join(rootDir, `${appName}.xcodeproj`);
2314
2249
  const bridgingHeader = `${appName}-Bridging-Header.h`;
2315
- function writeFile3(filePath, content) {
2316
- fs12.mkdirSync(path12.dirname(filePath), { recursive: true });
2317
- fs12.writeFileSync(filePath, content.trimStart(), "utf8");
2250
+ function writeFile2(filePath, content) {
2251
+ fs11.mkdirSync(path12.dirname(filePath), { recursive: true });
2252
+ fs11.writeFileSync(filePath, content.trimStart(), "utf8");
2318
2253
  }
2319
- if (fs12.existsSync(rootDir)) {
2254
+ if (fs11.existsSync(rootDir)) {
2320
2255
  console.log(`\u{1F9F9} Removing existing directory: ${rootDir}`);
2321
- fs12.rmSync(rootDir, { recursive: true, force: true });
2256
+ fs11.rmSync(rootDir, { recursive: true, force: true });
2322
2257
  }
2323
2258
  console.log(`\u{1F680} Creating a new Tamer4Lynx project in: ${rootDir}`);
2324
2259
  const ids = {
2325
- project: generateId2(),
2326
- mainGroup: generateId2(),
2327
- appGroup: generateId2(),
2328
- productsGroup: generateId2(),
2329
- frameworksGroup: generateId2(),
2330
- appFile: generateId2(),
2331
- appDelegateRef: generateId2(),
2332
- sceneDelegateRef: generateId2(),
2333
- sceneDelegateBaseRef: generateId2(),
2334
- viewControllerRef: generateId2(),
2335
- assetsRef: generateId2(),
2336
- lynxProviderRef: generateId2(),
2337
- lynxInitRef: generateId2(),
2338
- bridgingHeaderRef: generateId2(),
2339
- nativeTarget: generateId2(),
2340
- appDelegateBuildFile: generateId2(),
2341
- sceneDelegateBuildFile: generateId2(),
2342
- sceneDelegateSourceBuildFile: generateId2(),
2343
- viewControllerBuildFile: generateId2(),
2344
- lynxProviderBuildFile: generateId2(),
2345
- lynxInitBuildFile: generateId2(),
2346
- assetsBuildFile: generateId2(),
2347
- frameworksBuildPhase: generateId2(),
2348
- resourcesBuildPhase: generateId2(),
2349
- sourcesBuildPhase: generateId2(),
2350
- projectBuildConfigList: generateId2(),
2351
- targetBuildConfigList: generateId2(),
2352
- projectDebugConfig: generateId2(),
2353
- projectReleaseConfig: generateId2(),
2354
- targetDebugConfig: generateId2(),
2355
- targetReleaseConfig: generateId2()
2260
+ project: generateId(),
2261
+ mainGroup: generateId(),
2262
+ appGroup: generateId(),
2263
+ productsGroup: generateId(),
2264
+ frameworksGroup: generateId(),
2265
+ appFile: generateId(),
2266
+ appDelegateRef: generateId(),
2267
+ sceneDelegateRef: generateId(),
2268
+ sceneDelegateBaseRef: generateId(),
2269
+ viewControllerRef: generateId(),
2270
+ assetsRef: generateId(),
2271
+ lynxProviderRef: generateId(),
2272
+ lynxInitRef: generateId(),
2273
+ bridgingHeaderRef: generateId(),
2274
+ nativeTarget: generateId(),
2275
+ appDelegateBuildFile: generateId(),
2276
+ sceneDelegateBuildFile: generateId(),
2277
+ sceneDelegateSourceBuildFile: generateId(),
2278
+ viewControllerBuildFile: generateId(),
2279
+ lynxProviderBuildFile: generateId(),
2280
+ lynxInitBuildFile: generateId(),
2281
+ assetsBuildFile: generateId(),
2282
+ frameworksBuildPhase: generateId(),
2283
+ resourcesBuildPhase: generateId(),
2284
+ sourcesBuildPhase: generateId(),
2285
+ projectBuildConfigList: generateId(),
2286
+ targetBuildConfigList: generateId(),
2287
+ projectDebugConfig: generateId(),
2288
+ projectReleaseConfig: generateId(),
2289
+ targetDebugConfig: generateId(),
2290
+ targetReleaseConfig: generateId()
2356
2291
  };
2357
- writeFile3(path12.join(rootDir, "Podfile"), `
2292
+ writeFile2(path12.join(rootDir, "Podfile"), `
2358
2293
  source 'https://cdn.cocoapods.org/'
2359
2294
 
2360
2295
  platform :ios, '13.0'
@@ -2417,12 +2352,12 @@ end
2417
2352
  const templateDir = path12.join(hostPkg, "ios", "templates");
2418
2353
  for (const f of ["AppDelegate.swift", "SceneDelegate.swift", "ViewController.swift", "LynxProvider.swift", "LynxInitProcessor.swift"]) {
2419
2354
  const srcPath = path12.join(templateDir, f);
2420
- if (fs12.existsSync(srcPath)) {
2421
- writeFile3(path12.join(projectDir, f), readAndSubstituteTemplate3(srcPath, templateVars));
2355
+ if (fs11.existsSync(srcPath)) {
2356
+ writeFile2(path12.join(projectDir, f), readAndSubstituteTemplate3(srcPath, templateVars));
2422
2357
  }
2423
2358
  }
2424
2359
  } else {
2425
- writeFile3(path12.join(projectDir, "AppDelegate.swift"), `
2360
+ writeFile2(path12.join(projectDir, "AppDelegate.swift"), `
2426
2361
  import UIKit
2427
2362
 
2428
2363
  @UIApplicationMain
@@ -2437,7 +2372,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
2437
2372
  }
2438
2373
  }
2439
2374
  `);
2440
- writeFile3(path12.join(projectDir, "SceneDelegate.swift"), `
2375
+ writeFile2(path12.join(projectDir, "SceneDelegate.swift"), `
2441
2376
  import UIKit
2442
2377
 
2443
2378
  class SceneDelegate: UIResponder, UIWindowSceneDelegate {
@@ -2451,7 +2386,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
2451
2386
  }
2452
2387
  }
2453
2388
  `);
2454
- writeFile3(path12.join(projectDir, "ViewController.swift"), `
2389
+ writeFile2(path12.join(projectDir, "ViewController.swift"), `
2455
2390
  import UIKit
2456
2391
  import Lynx
2457
2392
  import tamerinsets
@@ -2521,7 +2456,7 @@ class ViewController: UIViewController {
2521
2456
  }
2522
2457
  }
2523
2458
  `);
2524
- writeFile3(path12.join(projectDir, "LynxProvider.swift"), `
2459
+ writeFile2(path12.join(projectDir, "LynxProvider.swift"), `
2525
2460
  import Foundation
2526
2461
 
2527
2462
  class LynxProvider: NSObject, LynxTemplateProvider {
@@ -2540,7 +2475,7 @@ class LynxProvider: NSObject, LynxTemplateProvider {
2540
2475
  }
2541
2476
  }
2542
2477
  `);
2543
- writeFile3(path12.join(projectDir, "LynxInitProcessor.swift"), `
2478
+ writeFile2(path12.join(projectDir, "LynxInitProcessor.swift"), `
2544
2479
  // Copyright 2024 The Lynx Authors. All rights reserved.
2545
2480
  // Licensed under the Apache License Version 2.0 that can be found in the
2546
2481
  // LICENSE file in the root directory of this source tree.
@@ -2580,7 +2515,7 @@ final class LynxInitProcessor {
2580
2515
  }
2581
2516
  `);
2582
2517
  }
2583
- writeFile3(path12.join(projectDir, bridgingHeader), `
2518
+ writeFile2(path12.join(projectDir, bridgingHeader), `
2584
2519
  #import <Lynx/LynxConfig.h>
2585
2520
  #import <Lynx/LynxEnv.h>
2586
2521
  #import <Lynx/LynxTemplateProvider.h>
@@ -2589,7 +2524,7 @@ final class LynxInitProcessor {
2589
2524
  #import <SDWebImage/SDWebImage.h>
2590
2525
  #import <SDWebImageWebPCoder/SDWebImageWebPCoder.h>
2591
2526
  `);
2592
- writeFile3(path12.join(projectDir, "Info.plist"), `
2527
+ writeFile2(path12.join(projectDir, "Info.plist"), `
2593
2528
  <?xml version="1.0" encoding="UTF-8"?>
2594
2529
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2595
2530
  <plist version="1.0">
@@ -2648,38 +2583,38 @@ final class LynxInitProcessor {
2648
2583
  </plist>
2649
2584
  `);
2650
2585
  const appIconDir = path12.join(projectDir, "Assets.xcassets", "AppIcon.appiconset");
2651
- fs12.mkdirSync(appIconDir, { recursive: true });
2586
+ fs11.mkdirSync(appIconDir, { recursive: true });
2652
2587
  const iconPaths = resolveIconPaths(process.cwd(), config);
2653
2588
  if (iconPaths?.ios) {
2654
- const entries = fs12.readdirSync(iconPaths.ios, { withFileTypes: true });
2589
+ const entries = fs11.readdirSync(iconPaths.ios, { withFileTypes: true });
2655
2590
  for (const e of entries) {
2656
2591
  const dest = path12.join(appIconDir, e.name);
2657
2592
  if (e.isDirectory()) {
2658
- fs12.cpSync(path12.join(iconPaths.ios, e.name), dest, { recursive: true });
2593
+ fs11.cpSync(path12.join(iconPaths.ios, e.name), dest, { recursive: true });
2659
2594
  } else {
2660
- fs12.copyFileSync(path12.join(iconPaths.ios, e.name), dest);
2595
+ fs11.copyFileSync(path12.join(iconPaths.ios, e.name), dest);
2661
2596
  }
2662
2597
  }
2663
2598
  console.log("\u2705 Copied iOS icon from tamer.config.json icon.ios");
2664
2599
  } else if (iconPaths?.source) {
2665
2600
  const ext = path12.extname(iconPaths.source) || ".png";
2666
2601
  const icon1024 = `Icon-1024${ext}`;
2667
- fs12.copyFileSync(iconPaths.source, path12.join(appIconDir, icon1024));
2668
- writeFile3(path12.join(appIconDir, "Contents.json"), JSON.stringify({
2602
+ fs11.copyFileSync(iconPaths.source, path12.join(appIconDir, icon1024));
2603
+ writeFile2(path12.join(appIconDir, "Contents.json"), JSON.stringify({
2669
2604
  images: [{ filename: icon1024, idiom: "universal", platform: "ios", size: "1024x1024" }],
2670
2605
  info: { author: "xcode", version: 1 }
2671
2606
  }, null, 2));
2672
2607
  console.log("\u2705 Copied app icon from tamer.config.json icon.source");
2673
2608
  } else {
2674
- writeFile3(path12.join(appIconDir, "Contents.json"), `
2609
+ writeFile2(path12.join(appIconDir, "Contents.json"), `
2675
2610
  {
2676
2611
  "images" : [ { "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" } ],
2677
2612
  "info" : { "author" : "xcode", "version" : 1 }
2678
2613
  }
2679
2614
  `);
2680
2615
  }
2681
- fs12.mkdirSync(xcodeprojDir, { recursive: true });
2682
- writeFile3(path12.join(xcodeprojDir, "project.pbxproj"), `
2616
+ fs11.mkdirSync(xcodeprojDir, { recursive: true });
2617
+ writeFile2(path12.join(xcodeprojDir, "project.pbxproj"), `
2683
2618
  // !$*UTF8*$!
2684
2619
  {
2685
2620
  archiveVersion = 1;
@@ -2965,7 +2900,7 @@ final class LynxInitProcessor {
2965
2900
  var create_default2 = create2;
2966
2901
 
2967
2902
  // src/ios/autolink.ts
2968
- import fs13 from "fs";
2903
+ import fs12 from "fs";
2969
2904
  import path13 from "path";
2970
2905
  import { execSync as execSync5 } from "child_process";
2971
2906
  var autolink2 = () => {
@@ -2979,11 +2914,11 @@ var autolink2 = () => {
2979
2914
  const projectRoot = resolved.projectRoot;
2980
2915
  const iosProjectPath = resolved.iosDir;
2981
2916
  function updateGeneratedSection(filePath, newContent, startMarker, endMarker) {
2982
- if (!fs13.existsSync(filePath)) {
2917
+ if (!fs12.existsSync(filePath)) {
2983
2918
  console.warn(`\u26A0\uFE0F File not found, skipping update: ${filePath}`);
2984
2919
  return;
2985
2920
  }
2986
- let fileContent = fs13.readFileSync(filePath, "utf8");
2921
+ let fileContent = fs12.readFileSync(filePath, "utf8");
2987
2922
  const escapedStartMarker = startMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2988
2923
  const escapedEndMarker = endMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2989
2924
  const regex = new RegExp(`${escapedStartMarker}[\\s\\S]*?${escapedEndMarker}`, "g");
@@ -3008,15 +2943,15 @@ ${replacementBlock}
3008
2943
  ${replacementBlock}
3009
2944
  `;
3010
2945
  }
3011
- fs13.writeFileSync(filePath, fileContent, "utf8");
2946
+ fs12.writeFileSync(filePath, fileContent, "utf8");
3012
2947
  console.log(`\u2705 Updated autolinked section in ${path13.basename(filePath)}`);
3013
2948
  }
3014
2949
  function resolvePodName(pkg) {
3015
2950
  const podspecDir = pkg.config.ios?.podspecPath || ".";
3016
2951
  const fullPodspecDir = path13.join(pkg.packagePath, podspecDir);
3017
- if (fs13.existsSync(fullPodspecDir)) {
2952
+ if (fs12.existsSync(fullPodspecDir)) {
3018
2953
  try {
3019
- const files = fs13.readdirSync(fullPodspecDir);
2954
+ const files = fs12.readdirSync(fullPodspecDir);
3020
2955
  const podspecFile = files.find((f) => f.endsWith(".podspec"));
3021
2956
  if (podspecFile) return podspecFile.replace(".podspec", "");
3022
2957
  } catch {
@@ -3050,7 +2985,7 @@ ${replacementBlock}
3050
2985
  candidatePaths.push(path13.join(iosProjectPath, appNameFromConfig, "LynxInitProcessor.swift"));
3051
2986
  }
3052
2987
  candidatePaths.push(path13.join(iosProjectPath, "LynxInitProcessor.swift"));
3053
- const found = candidatePaths.find((p) => fs13.existsSync(p));
2988
+ const found = candidatePaths.find((p) => fs12.existsSync(p));
3054
2989
  const lynxInitPath = found ?? candidatePaths[0];
3055
2990
  const iosPackages = packages.filter((p) => getIosModuleClassNames(p.config.ios).length > 0 || Object.keys(getIosElements(p.config.ios)).length > 0);
3056
2991
  function updateImportsSection(filePath, pkgs) {
@@ -3065,7 +3000,7 @@ ${replacementBlock}
3065
3000
  const podName = resolvePodName(pkg);
3066
3001
  return `import ${podName}`;
3067
3002
  }).join("\n");
3068
- const fileContent = fs13.readFileSync(filePath, "utf8");
3003
+ const fileContent = fs12.readFileSync(filePath, "utf8");
3069
3004
  if (fileContent.indexOf(startMarker) !== -1) {
3070
3005
  updateGeneratedSection(filePath, imports, startMarker, endMarker);
3071
3006
  return;
@@ -3102,7 +3037,7 @@ ${after}`;
3102
3037
  ${fileContent}`;
3103
3038
  }
3104
3039
  }
3105
- fs13.writeFileSync(filePath, newContent, "utf8");
3040
+ fs12.writeFileSync(filePath, newContent, "utf8");
3106
3041
  console.log(`\u2705 Updated imports in ${path13.basename(filePath)}`);
3107
3042
  }
3108
3043
  updateImportsSection(lynxInitPath, iosPackages);
@@ -3133,10 +3068,10 @@ ${fileContent}`;
3133
3068
  candidates.push(path13.join(iosProjectPath, appNameFromConfig, "Info.plist"));
3134
3069
  }
3135
3070
  candidates.push(path13.join(iosProjectPath, "Info.plist"));
3136
- return candidates.find((p) => fs13.existsSync(p)) ?? null;
3071
+ return candidates.find((p) => fs12.existsSync(p)) ?? null;
3137
3072
  }
3138
3073
  function readPlistXml(plistPath) {
3139
- return fs13.readFileSync(plistPath, "utf8");
3074
+ return fs12.readFileSync(plistPath, "utf8");
3140
3075
  }
3141
3076
  function syncInfoPlistPermissions(packages) {
3142
3077
  const plistPath = findInfoPlist();
@@ -3167,7 +3102,7 @@ ${fileContent}`;
3167
3102
  added++;
3168
3103
  }
3169
3104
  if (added > 0) {
3170
- fs13.writeFileSync(plistPath, plist, "utf8");
3105
+ fs12.writeFileSync(plistPath, plist, "utf8");
3171
3106
  console.log(`\u2705 Synced ${added} Info.plist permission description(s)`);
3172
3107
  }
3173
3108
  }
@@ -3214,12 +3149,12 @@ ${schemesXml}
3214
3149
  $1`
3215
3150
  );
3216
3151
  }
3217
- fs13.writeFileSync(plistPath, plist, "utf8");
3152
+ fs12.writeFileSync(plistPath, plist, "utf8");
3218
3153
  console.log(`\u2705 Synced ${urlSchemes.length} iOS URL scheme(s) into Info.plist`);
3219
3154
  }
3220
3155
  function runPodInstall(forcePath) {
3221
3156
  const podfilePath = forcePath ?? path13.join(iosProjectPath, "Podfile");
3222
- if (!fs13.existsSync(podfilePath)) {
3157
+ if (!fs12.existsSync(podfilePath)) {
3223
3158
  console.log("\u2139\uFE0F No Podfile found in ios directory; skipping `pod install`.");
3224
3159
  return;
3225
3160
  }
@@ -3248,7 +3183,7 @@ $1`
3248
3183
  const appNameFromConfig = resolved.config.ios?.appName;
3249
3184
  if (appNameFromConfig) {
3250
3185
  const appPodfile = path13.join(iosProjectPath, appNameFromConfig, "Podfile");
3251
- if (fs13.existsSync(appPodfile)) {
3186
+ if (fs12.existsSync(appPodfile)) {
3252
3187
  runPodInstall(appPodfile);
3253
3188
  console.log("\u2728 Autolinking complete for iOS.");
3254
3189
  return;
@@ -3262,12 +3197,12 @@ $1`
3262
3197
  var autolink_default2 = autolink2;
3263
3198
 
3264
3199
  // src/ios/bundle.ts
3265
- import fs15 from "fs";
3200
+ import fs14 from "fs";
3266
3201
  import path15 from "path";
3267
3202
  import { execSync as execSync6 } from "child_process";
3268
3203
 
3269
3204
  // src/ios/syncHost.ts
3270
- import fs14 from "fs";
3205
+ import fs13 from "fs";
3271
3206
  import path14 from "path";
3272
3207
  import crypto from "crypto";
3273
3208
  function deterministicUUID(seed) {
@@ -3316,7 +3251,7 @@ function getLaunchScreenStoryboard() {
3316
3251
  `;
3317
3252
  }
3318
3253
  function addLaunchScreenToXcodeProject(pbxprojPath, appName) {
3319
- let content = fs14.readFileSync(pbxprojPath, "utf8");
3254
+ let content = fs13.readFileSync(pbxprojPath, "utf8");
3320
3255
  if (content.includes("LaunchScreen.storyboard")) return;
3321
3256
  const baseFileRefUUID = deterministicUUID(`launchScreenBase:${appName}`);
3322
3257
  const variantGroupUUID = deterministicUUID(`launchScreenGroup:${appName}`);
@@ -3353,11 +3288,11 @@ function addLaunchScreenToXcodeProject(pbxprojPath, appName) {
3353
3288
  );
3354
3289
  content = content.replace(groupPattern, `$1
3355
3290
  ${variantGroupUUID} /* LaunchScreen.storyboard */,`);
3356
- fs14.writeFileSync(pbxprojPath, content, "utf8");
3291
+ fs13.writeFileSync(pbxprojPath, content, "utf8");
3357
3292
  console.log("\u2705 Registered LaunchScreen.storyboard in Xcode project");
3358
3293
  }
3359
3294
  function addSwiftSourceToXcodeProject(pbxprojPath, appName, filename) {
3360
- let content = fs14.readFileSync(pbxprojPath, "utf8");
3295
+ let content = fs13.readFileSync(pbxprojPath, "utf8");
3361
3296
  const escaped = filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3362
3297
  if (new RegExp(`path = ${escaped};`).test(content)) return;
3363
3298
  const fileRefUUID = deterministicUUID(`fileRef:${appName}:${filename}`);
@@ -3382,11 +3317,11 @@ function addSwiftSourceToXcodeProject(pbxprojPath, appName, filename) {
3382
3317
  );
3383
3318
  content = content.replace(groupPattern, `$1
3384
3319
  ${fileRefUUID} /* ${filename} */,`);
3385
- fs14.writeFileSync(pbxprojPath, content, "utf8");
3320
+ fs13.writeFileSync(pbxprojPath, content, "utf8");
3386
3321
  console.log(`\u2705 Registered ${filename} in Xcode project sources`);
3387
3322
  }
3388
3323
  function addResourceToXcodeProject(pbxprojPath, appName, filename) {
3389
- let content = fs14.readFileSync(pbxprojPath, "utf8");
3324
+ let content = fs13.readFileSync(pbxprojPath, "utf8");
3390
3325
  const escaped = filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3391
3326
  if (new RegExp(`path = ${escaped};`).test(content)) return;
3392
3327
  const fileRefUUID = deterministicUUID(`fileRef:${appName}:${filename}`);
@@ -3411,12 +3346,12 @@ function addResourceToXcodeProject(pbxprojPath, appName, filename) {
3411
3346
  );
3412
3347
  content = content.replace(groupPattern, `$1
3413
3348
  ${fileRefUUID} /* ${filename} */,`);
3414
- fs14.writeFileSync(pbxprojPath, content, "utf8");
3349
+ fs13.writeFileSync(pbxprojPath, content, "utf8");
3415
3350
  console.log(`\u2705 Registered ${filename} in Xcode project resources`);
3416
3351
  }
3417
3352
  function writeFile(filePath, content) {
3418
- fs14.mkdirSync(path14.dirname(filePath), { recursive: true });
3419
- fs14.writeFileSync(filePath, content, "utf8");
3353
+ fs13.mkdirSync(path14.dirname(filePath), { recursive: true });
3354
+ fs13.writeFileSync(filePath, content, "utf8");
3420
3355
  }
3421
3356
  function getAppDelegateSwift() {
3422
3357
  return `import UIKit
@@ -3626,8 +3561,8 @@ class ViewController: UIViewController {
3626
3561
  `;
3627
3562
  }
3628
3563
  function patchInfoPlist(infoPlistPath) {
3629
- if (!fs14.existsSync(infoPlistPath)) return;
3630
- let content = fs14.readFileSync(infoPlistPath, "utf8");
3564
+ if (!fs13.existsSync(infoPlistPath)) return;
3565
+ let content = fs13.readFileSync(infoPlistPath, "utf8");
3631
3566
  content = content.replace(/\s*<key>UIMainStoryboardFile<\/key>\s*<string>[^<]*<\/string>/g, "");
3632
3567
  if (!content.includes("UILaunchStoryboardName")) {
3633
3568
  content = content.replace("</dict>\n</plist>", ` <key>UILaunchStoryboardName</key>
@@ -3659,7 +3594,7 @@ function patchInfoPlist(infoPlistPath) {
3659
3594
  </plist>`);
3660
3595
  console.log("\u2705 Added UIApplicationSceneManifest to Info.plist");
3661
3596
  }
3662
- fs14.writeFileSync(infoPlistPath, content, "utf8");
3597
+ fs13.writeFileSync(infoPlistPath, content, "utf8");
3663
3598
  }
3664
3599
  function getSimpleLynxProviderSwift() {
3665
3600
  return `import Foundation
@@ -3685,8 +3620,8 @@ class LynxProvider: NSObject, LynxTemplateProvider {
3685
3620
  function readTemplateOrFallback(devClientPkg, templateName, fallback, vars = {}) {
3686
3621
  if (devClientPkg) {
3687
3622
  const tplPath = path14.join(devClientPkg, "ios", "templates", templateName);
3688
- if (fs14.existsSync(tplPath)) {
3689
- let content = fs14.readFileSync(tplPath, "utf8");
3623
+ if (fs13.existsSync(tplPath)) {
3624
+ let content = fs13.readFileSync(tplPath, "utf8");
3690
3625
  for (const [k, v] of Object.entries(vars)) {
3691
3626
  content = content.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v);
3692
3627
  }
@@ -3698,15 +3633,15 @@ function readTemplateOrFallback(devClientPkg, templateName, fallback, vars = {})
3698
3633
  function syncHostIos(opts) {
3699
3634
  const resolved = resolveHostPaths();
3700
3635
  const appName = resolved.config.ios?.appName;
3701
- const devMode = resolveDevMode(resolved.config);
3702
3636
  const release = opts?.release === true;
3703
- const useDevClient = devMode === "embedded" && !release;
3637
+ const devClientPkg = findDevClientPackage(resolved.projectRoot);
3638
+ const useDevClient = opts?.includeDevClient ?? (!release && !!devClientPkg);
3704
3639
  if (!appName) {
3705
3640
  throw new Error('"ios.appName" must be defined in tamer.config.json');
3706
3641
  }
3707
3642
  const projectDir = path14.join(resolved.iosDir, appName);
3708
3643
  const infoPlistPath = path14.join(projectDir, "Info.plist");
3709
- if (!fs14.existsSync(projectDir)) {
3644
+ if (!fs13.existsSync(projectDir)) {
3710
3645
  throw new Error(`iOS project not found at ${projectDir}. Run \`tamer ios create\` first.`);
3711
3646
  }
3712
3647
  const pbxprojPath = path14.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
@@ -3715,35 +3650,35 @@ function syncHostIos(opts) {
3715
3650
  patchInfoPlist(infoPlistPath);
3716
3651
  writeFile(path14.join(projectDir, "AppDelegate.swift"), getAppDelegateSwift());
3717
3652
  writeFile(path14.join(projectDir, "SceneDelegate.swift"), getSceneDelegateSwift());
3718
- if (!fs14.existsSync(launchScreenPath)) {
3719
- fs14.mkdirSync(baseLprojDir, { recursive: true });
3653
+ if (!fs13.existsSync(launchScreenPath)) {
3654
+ fs13.mkdirSync(baseLprojDir, { recursive: true });
3720
3655
  writeFile(launchScreenPath, getLaunchScreenStoryboard());
3721
3656
  addLaunchScreenToXcodeProject(pbxprojPath, appName);
3722
3657
  }
3723
3658
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "SceneDelegate.swift");
3724
3659
  if (useDevClient) {
3725
- const devClientPkg = findDevClientPackage(resolved.projectRoot);
3660
+ const devClientPkg2 = findDevClientPackage(resolved.projectRoot);
3726
3661
  const segment = resolved.lynxProjectDir.split("/").filter(Boolean).pop() ?? "";
3727
3662
  const tplVars = { PROJECT_BUNDLE_SEGMENT: segment };
3728
3663
  writeFile(path14.join(projectDir, "ViewController.swift"), getDevViewControllerSwift());
3729
3664
  writeFile(path14.join(projectDir, "LynxProvider.swift"), getSimpleLynxProviderSwift());
3730
3665
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "LynxProvider.swift");
3731
- const devTPContent = readTemplateOrFallback(devClientPkg, "DevTemplateProvider.swift", "", tplVars);
3666
+ const devTPContent = readTemplateOrFallback(devClientPkg2, "DevTemplateProvider.swift", "", tplVars);
3732
3667
  if (devTPContent) {
3733
3668
  writeFile(path14.join(projectDir, "DevTemplateProvider.swift"), devTPContent);
3734
3669
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "DevTemplateProvider.swift");
3735
3670
  }
3736
- const projectVCContent = readTemplateOrFallback(devClientPkg, "ProjectViewController.swift", "", tplVars);
3671
+ const projectVCContent = readTemplateOrFallback(devClientPkg2, "ProjectViewController.swift", "", tplVars);
3737
3672
  if (projectVCContent) {
3738
3673
  writeFile(path14.join(projectDir, "ProjectViewController.swift"), projectVCContent);
3739
3674
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "ProjectViewController.swift");
3740
3675
  }
3741
- const devCMContent = readTemplateOrFallback(devClientPkg, "DevClientManager.swift", "", tplVars);
3676
+ const devCMContent = readTemplateOrFallback(devClientPkg2, "DevClientManager.swift", "", tplVars);
3742
3677
  if (devCMContent) {
3743
3678
  writeFile(path14.join(projectDir, "DevClientManager.swift"), devCMContent);
3744
3679
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "DevClientManager.swift");
3745
3680
  }
3746
- const qrContent = readTemplateOrFallback(devClientPkg, "QRScannerViewController.swift", "", tplVars);
3681
+ const qrContent = readTemplateOrFallback(devClientPkg2, "QRScannerViewController.swift", "", tplVars);
3747
3682
  if (qrContent) {
3748
3683
  writeFile(path14.join(projectDir, "QRScannerViewController.swift"), qrContent);
3749
3684
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "QRScannerViewController.swift");
@@ -3760,7 +3695,6 @@ var syncHost_default = syncHostIos;
3760
3695
 
3761
3696
  // src/ios/bundle.ts
3762
3697
  function bundleAndDeploy2(opts = {}) {
3763
- const target = opts.target ?? "host";
3764
3698
  const release = opts.release === true;
3765
3699
  let resolved;
3766
3700
  try {
@@ -3772,16 +3706,13 @@ function bundleAndDeploy2(opts = {}) {
3772
3706
  console.error(`\u274C Error loading configuration: ${error.message}`);
3773
3707
  process.exit(1);
3774
3708
  }
3709
+ const devClientPkg = findDevClientPackage(resolved.projectRoot);
3710
+ const includeDevClient = !release && !!devClientPkg;
3775
3711
  const appName = resolved.config.ios.appName;
3776
3712
  const sourceBundlePath = resolved.lynxBundlePath;
3777
3713
  const destinationDir = path15.join(resolved.iosDir, appName);
3778
3714
  const destinationBundlePath = path15.join(destinationDir, resolved.lynxBundleFile);
3779
- const devMode = resolveDevMode(resolved.config);
3780
- if (target === "dev-app") {
3781
- console.error("\u274C iOS dev-app target not yet implemented.");
3782
- process.exit(1);
3783
- }
3784
- syncHost_default({ release });
3715
+ syncHost_default({ release, includeDevClient });
3785
3716
  autolink_default2();
3786
3717
  try {
3787
3718
  console.log("\u{1F4E6} Building Lynx bundle...");
@@ -3792,11 +3723,11 @@ function bundleAndDeploy2(opts = {}) {
3792
3723
  process.exit(1);
3793
3724
  }
3794
3725
  try {
3795
- if (!fs15.existsSync(sourceBundlePath)) {
3726
+ if (!fs14.existsSync(sourceBundlePath)) {
3796
3727
  console.error(`\u274C Build output not found at: ${sourceBundlePath}`);
3797
3728
  process.exit(1);
3798
3729
  }
3799
- if (!fs15.existsSync(destinationDir)) {
3730
+ if (!fs14.existsSync(destinationDir)) {
3800
3731
  console.error(`Destination directory not found at: ${destinationDir}`);
3801
3732
  process.exit(1);
3802
3733
  }
@@ -3805,39 +3736,29 @@ function bundleAndDeploy2(opts = {}) {
3805
3736
  copyDistAssets(distDir, destinationDir, resolved.lynxBundleFile);
3806
3737
  console.log(`\u2728 Successfully copied bundle to: ${destinationBundlePath}`);
3807
3738
  const pbxprojPath = path15.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
3808
- if (fs15.existsSync(pbxprojPath)) {
3739
+ if (fs14.existsSync(pbxprojPath)) {
3809
3740
  const skip = /* @__PURE__ */ new Set([".rspeedy", "stats.json"]);
3810
- for (const entry of fs15.readdirSync(distDir)) {
3811
- if (skip.has(entry) || fs15.statSync(path15.join(distDir, entry)).isDirectory()) continue;
3741
+ for (const entry of fs14.readdirSync(distDir)) {
3742
+ if (skip.has(entry) || fs14.statSync(path15.join(distDir, entry)).isDirectory()) continue;
3812
3743
  addResourceToXcodeProject(pbxprojPath, appName, entry);
3813
3744
  }
3814
3745
  }
3815
- if (devMode === "embedded") {
3746
+ if (includeDevClient && devClientPkg) {
3816
3747
  const devClientBundle = path15.join(destinationDir, "dev-client.lynx.bundle");
3817
- if (!release) {
3818
- const devClientPkg = findDevClientPackage(resolved.projectRoot);
3819
- if (devClientPkg) {
3820
- console.log("\u{1F4E6} Building dev-client bundle...");
3821
- try {
3822
- execSync6("npm run build", { stdio: "inherit", cwd: devClientPkg });
3823
- } catch {
3824
- console.warn("\u26A0\uFE0F dev-client build failed; skipping dev-client bundle");
3825
- }
3826
- const builtBundle = path15.join(devClientPkg, "dist", "dev-client.lynx.bundle");
3827
- if (fs15.existsSync(builtBundle)) {
3828
- fs15.copyFileSync(builtBundle, devClientBundle);
3829
- console.log("\u2728 Copied dev-client.lynx.bundle to iOS project");
3830
- const pbxprojPath2 = path15.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
3831
- if (fs15.existsSync(pbxprojPath2)) {
3832
- addResourceToXcodeProject(pbxprojPath2, appName, "dev-client.lynx.bundle");
3833
- }
3834
- }
3835
- }
3836
- } else {
3837
- if (!fs15.existsSync(devClientBundle)) {
3838
- fs15.writeFileSync(devClientBundle, "");
3748
+ console.log("\u{1F4E6} Building dev-client bundle...");
3749
+ try {
3750
+ execSync6("npm run build", { stdio: "inherit", cwd: devClientPkg });
3751
+ } catch {
3752
+ console.warn("\u26A0\uFE0F dev-client build failed; skipping dev-client bundle");
3753
+ }
3754
+ const builtBundle = path15.join(devClientPkg, "dist", "dev-client.lynx.bundle");
3755
+ if (fs14.existsSync(builtBundle)) {
3756
+ fs14.copyFileSync(builtBundle, devClientBundle);
3757
+ console.log("\u2728 Copied dev-client.lynx.bundle to iOS project");
3758
+ const pbxprojPath2 = path15.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
3759
+ if (fs14.existsSync(pbxprojPath2)) {
3760
+ addResourceToXcodeProject(pbxprojPath2, appName, "dev-client.lynx.bundle");
3839
3761
  }
3840
- console.log("\u2139\uFE0F Skipped dev-client bundle (release build)");
3841
3762
  }
3842
3763
  }
3843
3764
  } catch (error) {
@@ -3849,1327 +3770,103 @@ function bundleAndDeploy2(opts = {}) {
3849
3770
  var bundle_default2 = bundleAndDeploy2;
3850
3771
 
3851
3772
  // src/ios/build.ts
3852
- import fs17 from "fs";
3853
- import path17 from "path";
3854
- import { execSync as execSync8 } from "child_process";
3855
-
3856
- // src/ios/syncDevClient.ts
3857
- import fs16 from "fs";
3773
+ import fs15 from "fs";
3858
3774
  import path16 from "path";
3859
3775
  import { execSync as execSync7 } from "child_process";
3860
- import { randomBytes as randomBytes2 } from "crypto";
3861
- function readAndSubstituteTemplate4(templatePath, vars) {
3862
- const raw = fs16.readFileSync(templatePath, "utf-8");
3863
- return Object.entries(vars).reduce(
3864
- (s, [k, v]) => s.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v),
3865
- raw
3866
- );
3867
- }
3868
- var APP_NAME = "TamerDevApp";
3869
- var BUNDLE_ID = "com.nanofuxion.tamerdevapp";
3870
- var BRIDGING_HEADER = `${APP_NAME}-Bridging-Header.h`;
3871
- function generateId() {
3872
- return randomBytes2(12).toString("hex").toUpperCase();
3873
- }
3874
- function writeFile2(filePath, content) {
3875
- fs16.mkdirSync(path16.dirname(filePath), { recursive: true });
3876
- fs16.writeFileSync(filePath, content, "utf8");
3776
+ function findBootedSimulator() {
3777
+ try {
3778
+ const out = execSync7("xcrun simctl list devices --json", { encoding: "utf8" });
3779
+ const json = JSON.parse(out);
3780
+ for (const runtimes of Object.values(json.devices)) {
3781
+ for (const device of runtimes) {
3782
+ if (device.state === "Booted") return device.udid;
3783
+ }
3784
+ }
3785
+ } catch {
3786
+ }
3787
+ return null;
3877
3788
  }
3878
- function getAppDelegateSwift2() {
3879
- return `import UIKit
3880
-
3881
- @UIApplicationMain
3882
- class AppDelegate: UIResponder, UIApplicationDelegate {
3883
- var window: UIWindow?
3884
-
3885
- func application(
3886
- _ application: UIApplication,
3887
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
3888
- ) -> Bool {
3889
- LynxInitProcessor.shared.setupEnvironment()
3890
-
3891
- window = UIWindow(frame: UIScreen.main.bounds)
3892
- window?.rootViewController = DevLauncherViewController()
3893
- window?.makeKeyAndVisible()
3894
- return true
3789
+ async function buildIpa(opts = {}) {
3790
+ const resolved = resolveHostPaths();
3791
+ if (!resolved.config.ios?.appName) {
3792
+ throw new Error('"ios.appName" must be defined in tamer.config.json');
3793
+ }
3794
+ const appName = resolved.config.ios.appName;
3795
+ const bundleId = resolved.config.ios.bundleId;
3796
+ const iosDir = resolved.iosDir;
3797
+ const configuration = opts.release ? "Release" : "Debug";
3798
+ bundle_default2({ release: opts.release });
3799
+ const scheme = appName;
3800
+ const workspacePath = path16.join(iosDir, `${appName}.xcworkspace`);
3801
+ const projectPath = path16.join(iosDir, `${appName}.xcodeproj`);
3802
+ const xcproject = fs15.existsSync(workspacePath) ? workspacePath : projectPath;
3803
+ const flag = xcproject.endsWith(".xcworkspace") ? "-workspace" : "-project";
3804
+ const derivedDataPath = path16.join(iosDir, "build");
3805
+ const sdk = opts.install ? "iphonesimulator" : "iphoneos";
3806
+ console.log(`
3807
+ \u{1F528} Building ${configuration} (${sdk})...`);
3808
+ execSync7(
3809
+ `xcodebuild ${flag} "${xcproject}" -scheme "${scheme}" -configuration ${configuration} -sdk ${sdk} -derivedDataPath "${derivedDataPath}"`,
3810
+ { stdio: "inherit", cwd: iosDir }
3811
+ );
3812
+ console.log(`\u2705 Build completed.`);
3813
+ if (opts.install) {
3814
+ const appGlob = path16.join(
3815
+ derivedDataPath,
3816
+ "Build",
3817
+ "Products",
3818
+ `${configuration}-iphonesimulator`,
3819
+ `${appName}.app`
3820
+ );
3821
+ if (!fs15.existsSync(appGlob)) {
3822
+ console.error(`\u274C Built app not found at: ${appGlob}`);
3823
+ process.exit(1);
3895
3824
  }
3896
-
3897
- func application(_ app: UIApplication, open url: URL,
3898
- options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
3899
- if url.scheme == "tamerdevapp", let host = url.host, host == "project" {
3900
- presentProjectViewController()
3901
- }
3902
- return true
3825
+ const udid = findBootedSimulator();
3826
+ if (!udid) {
3827
+ console.error("\u274C No booted simulator found. Start one with: xcrun simctl boot <udid>");
3828
+ process.exit(1);
3903
3829
  }
3904
-
3905
- @objc func presentProjectViewController() {
3906
- guard let root = window?.rootViewController else { return }
3907
- let projectVC = ProjectViewController()
3908
- projectVC.modalPresentationStyle = .fullScreen
3909
- root.present(projectVC, animated: true)
3830
+ console.log(`\u{1F4F2} Installing on simulator ${udid}...`);
3831
+ execSync7(`xcrun simctl install "${udid}" "${appGlob}"`, { stdio: "inherit" });
3832
+ if (bundleId) {
3833
+ console.log(`\u{1F680} Launching ${bundleId}...`);
3834
+ execSync7(`xcrun simctl launch "${udid}" "${bundleId}"`, { stdio: "inherit" });
3835
+ console.log("\u2705 App launched.");
3836
+ } else {
3837
+ console.log('\u2705 App installed. (Set "ios.bundleId" in tamer.config.json to auto-launch.)');
3910
3838
  }
3839
+ }
3911
3840
  }
3912
- `;
3913
- }
3914
- function getDevLauncherViewControllerSwift() {
3915
- return `import UIKit
3916
- import Lynx
3917
- import tamerdevclient
3918
- import tamerinsets
3919
-
3920
- class DevLauncherViewController: UIViewController {
3921
- private var lynxView: LynxView?
3922
-
3923
- override func viewDidLoad() {
3924
- super.viewDidLoad()
3925
- view.backgroundColor = .black
3926
- edgesForExtendedLayout = .all
3927
- extendedLayoutIncludesOpaqueBars = true
3928
- additionalSafeAreaInsets = .zero
3929
- view.insetsLayoutMarginsFromSafeArea = false
3930
- view.preservesSuperviewLayoutMargins = false
3931
- viewRespectsSystemMinimumLayoutMargins = false
3932
- setupLynxView()
3933
- setupDevClientModule()
3934
- }
3935
-
3936
- override func viewDidLayoutSubviews() {
3937
- super.viewDidLayoutSubviews()
3938
- if let lynxView = lynxView {
3939
- applyFullscreenLayout(to: lynxView)
3940
- }
3941
- }
3841
+ var build_default2 = buildIpa;
3942
3842
 
3943
- override func viewSafeAreaInsetsDidChange() {
3944
- super.viewSafeAreaInsetsDidChange()
3945
- TamerInsetsModule.reRequestInsets()
3946
- }
3947
-
3948
- override var preferredStatusBarStyle: UIStatusBarStyle { .lightContent }
3949
-
3950
- private func setupLynxView() {
3951
- let size = fullscreenBounds().size
3952
- let lv = LynxView { builder in
3953
- builder.config = LynxConfig(provider: DevTemplateProvider())
3954
- builder.screenSize = size
3955
- builder.fontScale = 1.0
3956
- }
3957
- lv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
3958
- lv.insetsLayoutMarginsFromSafeArea = false
3959
- lv.preservesSuperviewLayoutMargins = false
3960
- view.addSubview(lv)
3961
- applyFullscreenLayout(to: lv)
3962
- lv.loadTemplate(fromURL: "dev-client.lynx.bundle", initData: nil)
3963
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { [weak self, weak lv] in
3964
- guard let self, let lv else { return }
3965
- self.logViewport("devclient post-load", lynxView: lv)
3966
- self.applyFullscreenLayout(to: lv)
3967
- }
3968
- self.lynxView = lv
3969
- }
3970
-
3971
- private func applyFullscreenLayout(to lynxView: LynxView) {
3972
- let bounds = fullscreenBounds()
3973
- let size = bounds.size
3974
- lynxView.frame = bounds
3975
- lynxView.updateScreenMetrics(withWidth: size.width, height: size.height)
3976
- lynxView.updateViewport(withPreferredLayoutWidth: size.width, preferredLayoutHeight: size.height, needLayout: true)
3977
- lynxView.preferredLayoutWidth = size.width
3978
- lynxView.preferredLayoutHeight = size.height
3979
- lynxView.layoutWidthMode = .exact
3980
- lynxView.layoutHeightMode = .exact
3981
- logViewport("devclient apply", lynxView: lynxView)
3982
- }
3983
-
3984
- private func fullscreenBounds() -> CGRect {
3985
- let bounds = view.bounds
3986
- if bounds.width > 0, bounds.height > 0 {
3987
- return bounds
3988
- }
3989
- return UIScreen.main.bounds
3990
- }
3991
-
3992
- private func logViewport(_ label: String, lynxView: LynxView) {
3993
- let rootWidth = lynxView.rootWidth()
3994
- let rootHeight = lynxView.rootHeight()
3995
- let intrinsic = lynxView.intrinsicContentSize
3996
- NSLog("[DevLauncher] %@ view=%@ safe=%@ lynxFrame=%@ lynxBounds=%@ root=%0.2fx%0.2f intrinsic=%@", label, NSCoder.string(for: view.bounds), NSCoder.string(for: view.safeAreaInsets), NSCoder.string(for: lynxView.frame), NSCoder.string(for: lynxView.bounds), rootWidth, rootHeight, NSCoder.string(for: intrinsic))
3997
- }
3998
-
3999
- private func setupDevClientModule() {
4000
- DevClientModule.presentQRScanner = { [weak self] completion in
4001
- let scanner = QRScannerViewController()
4002
- scanner.onResult = { url in
4003
- scanner.dismiss(animated: true) { completion(url) }
4004
- }
4005
- scanner.modalPresentationStyle = .fullScreen
4006
- self?.present(scanner, animated: true)
4007
- }
4008
-
4009
- DevClientModule.reloadProjectHandler = { [weak self] in
4010
- guard let self = self else { return }
4011
- let projectVC = ProjectViewController()
4012
- projectVC.modalPresentationStyle = .fullScreen
4013
- self.present(projectVC, animated: true)
4014
- }
4015
- }
4016
- }
4017
- `;
4018
- }
4019
- function getProjectViewControllerSwift() {
4020
- return `import UIKit
4021
- import Lynx
4022
- import tamerinsets
4023
-
4024
- class ProjectViewController: UIViewController {
4025
- private var lynxView: LynxView?
4026
- private var devClientManager: DevClientManager?
4027
-
4028
- override func viewDidLoad() {
4029
- super.viewDidLoad()
4030
- view.backgroundColor = .black
4031
- edgesForExtendedLayout = .all
4032
- extendedLayoutIncludesOpaqueBars = true
4033
- additionalSafeAreaInsets = .zero
4034
- view.insetsLayoutMarginsFromSafeArea = false
4035
- view.preservesSuperviewLayoutMargins = false
4036
- viewRespectsSystemMinimumLayoutMargins = false
4037
- setupLynxView()
4038
- devClientManager = DevClientManager(onReload: { [weak self] in
4039
- self?.reloadLynxView()
4040
- })
4041
- devClientManager?.connect()
4042
- }
4043
-
4044
- override func viewDidLayoutSubviews() {
4045
- super.viewDidLayoutSubviews()
4046
- if let lynxView = lynxView {
4047
- applyFullscreenLayout(to: lynxView)
4048
- }
4049
- }
4050
-
4051
- override func viewSafeAreaInsetsDidChange() {
4052
- super.viewSafeAreaInsetsDidChange()
4053
- TamerInsetsModule.reRequestInsets()
4054
- }
4055
-
4056
- override var preferredStatusBarStyle: UIStatusBarStyle { .lightContent }
4057
-
4058
- private func buildLynxView() -> LynxView {
4059
- let size = fullscreenBounds().size
4060
- let lv = LynxView { builder in
4061
- builder.config = LynxConfig(provider: DevTemplateProvider())
4062
- builder.screenSize = size
4063
- builder.fontScale = 1.0
4064
- }
4065
- lv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
4066
- lv.insetsLayoutMarginsFromSafeArea = false
4067
- lv.preservesSuperviewLayoutMargins = false
4068
- applyFullscreenLayout(to: lv)
4069
- return lv
4070
- }
4071
-
4072
- private func setupLynxView() {
4073
- let lv = buildLynxView()
4074
- view.addSubview(lv)
4075
- lv.loadTemplate(fromURL: "main.lynx.bundle", initData: nil)
4076
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { [weak self, weak lv] in
4077
- guard let self, let lv else { return }
4078
- self.logViewport("project post-load", lynxView: lv)
4079
- self.applyFullscreenLayout(to: lv)
4080
- }
4081
- self.lynxView = lv
4082
- }
4083
-
4084
- private func reloadLynxView() {
4085
- lynxView?.removeFromSuperview()
4086
- lynxView = nil
4087
- setupLynxView()
4088
- }
4089
-
4090
- private func applyFullscreenLayout(to lynxView: LynxView) {
4091
- let bounds = fullscreenBounds()
4092
- let size = bounds.size
4093
- lynxView.frame = bounds
4094
- lynxView.updateScreenMetrics(withWidth: size.width, height: size.height)
4095
- lynxView.updateViewport(withPreferredLayoutWidth: size.width, preferredLayoutHeight: size.height, needLayout: true)
4096
- lynxView.preferredLayoutWidth = size.width
4097
- lynxView.preferredLayoutHeight = size.height
4098
- lynxView.layoutWidthMode = .exact
4099
- lynxView.layoutHeightMode = .exact
4100
- logViewport("project apply", lynxView: lynxView)
4101
- }
4102
-
4103
- private func fullscreenBounds() -> CGRect {
4104
- let bounds = view.bounds
4105
- if bounds.width > 0, bounds.height > 0 {
4106
- return bounds
4107
- }
4108
- return UIScreen.main.bounds
4109
- }
4110
-
4111
- private func logViewport(_ label: String, lynxView: LynxView) {
4112
- let rootWidth = lynxView.rootWidth()
4113
- let rootHeight = lynxView.rootHeight()
4114
- let intrinsic = lynxView.intrinsicContentSize
4115
- NSLog("[ProjectVC] %@ view=%@ safe=%@ lynxFrame=%@ lynxBounds=%@ root=%0.2fx%0.2f intrinsic=%@", label, NSCoder.string(for: view.bounds), NSCoder.string(for: view.safeAreaInsets), NSCoder.string(for: lynxView.frame), NSCoder.string(for: lynxView.bounds), rootWidth, rootHeight, NSCoder.string(for: intrinsic))
4116
- }
4117
-
4118
- override func viewWillDisappear(_ animated: Bool) {
4119
- super.viewWillDisappear(animated)
4120
- if isBeingDismissed || isMovingFromParent {
4121
- devClientManager?.disconnect()
4122
- }
4123
- }
4124
- }
4125
- `;
4126
- }
4127
- function getDevTemplateProviderSwift() {
4128
- return `import Foundation
4129
- import Lynx
4130
- import tamerdevclient
4131
-
4132
- class DevTemplateProvider: NSObject, LynxTemplateProvider {
4133
- private static let devClientBundle = "dev-client.lynx.bundle"
4134
-
4135
- func loadTemplate(withUrl url: String!, onComplete callback: LynxTemplateLoadBlock!) {
4136
- DispatchQueue.global(qos: .background).async {
4137
- // dev-client.lynx.bundle always loads from the embedded asset
4138
- if url == Self.devClientBundle || url?.hasSuffix("/" + Self.devClientBundle) == true {
4139
- self.loadFromBundle(url: Self.devClientBundle, callback: callback)
4140
- return
4141
- }
4142
-
4143
- // Try the dev server first
4144
- if let devUrl = DevServerPrefs.getUrl(), !devUrl.isEmpty {
4145
- let origin: String
4146
- if let parsed = URL(string: devUrl) {
4147
- let scheme = parsed.scheme ?? "http"
4148
- let host = parsed.host ?? "localhost"
4149
- let port = parsed.port.map { ":\\($0)" } ?? ""
4150
- origin = "\\(scheme)://\\(host)\\(port)"
4151
- } else {
4152
- origin = devUrl
4153
- }
4154
-
4155
- let candidates = ["/\\(url!)", "/tamer-dev-app/\\(url!)"]
4156
- for candidate in candidates {
4157
- if let data = self.httpFetch(url: origin + candidate) {
4158
- callback?(data, nil)
4159
- return
4160
- }
4161
- }
4162
- }
4163
-
4164
- // Fall back to embedded bundle
4165
- self.loadFromBundle(url: url, callback: callback)
4166
- }
4167
- }
4168
-
4169
- private func loadFromBundle(url: String?, callback: LynxTemplateLoadBlock!) {
4170
- guard let url = url,
4171
- let bundleUrl = Bundle.main.url(forResource: url, withExtension: nil),
4172
- let data = try? Data(contentsOf: bundleUrl) else {
4173
- let err = NSError(domain: "DevTemplateProvider", code: 404,
4174
- userInfo: [NSLocalizedDescriptionKey: "Bundle not found: \\(url ?? "nil")"])
4175
- callback?(nil, err)
4176
- return
4177
- }
4178
- callback?(data, nil)
4179
- }
4180
-
4181
- private func httpFetch(url: String) -> Data? {
4182
- guard let u = URL(string: url) else { return nil }
4183
- var req = URLRequest(url: u)
4184
- req.timeoutInterval = 10
4185
- var result: Data?
4186
- let sem = DispatchSemaphore(value: 0)
4187
- URLSession.shared.dataTask(with: req) { data, response, _ in
4188
- if let http = response as? HTTPURLResponse, http.statusCode == 200 {
4189
- result = data
4190
- }
4191
- sem.signal()
4192
- }.resume()
4193
- sem.wait()
4194
- return result
4195
- }
4196
- }
4197
- `;
4198
- }
4199
- function getDevClientManagerSwift() {
4200
- return `import Foundation
4201
- import tamerdevclient
4202
-
4203
- class DevClientManager {
4204
- private var webSocketTask: URLSessionWebSocketTask?
4205
- private let onReload: () -> Void
4206
- private var session: URLSession?
4207
-
4208
- init(onReload: @escaping () -> Void) {
4209
- self.onReload = onReload
4210
- }
4211
-
4212
- func connect() {
4213
- guard let devUrl = DevServerPrefs.getUrl(), !devUrl.isEmpty else { return }
4214
- guard let base = URL(string: devUrl) else { return }
4215
-
4216
- let scheme = (base.scheme == "https") ? "wss" : "ws"
4217
- let host = base.host ?? "localhost"
4218
- let port = base.port.map { ":\\($0)" } ?? ""
4219
- let rawPath = base.path.isEmpty ? "/" : base.path
4220
- let dir = rawPath.hasSuffix("/") ? rawPath : rawPath + "/"
4221
- guard let wsUrl = URL(string: "\\(scheme)://\\(host)\\(port)\\(dir)__hmr") else { return }
4222
-
4223
- session = URLSession(configuration: .default)
4224
- let task = session!.webSocketTask(with: wsUrl)
4225
- webSocketTask = task
4226
- task.resume()
4227
- receive()
4228
- }
4229
-
4230
- private func receive() {
4231
- webSocketTask?.receive { [weak self] result in
4232
- guard let self = self else { return }
4233
- switch result {
4234
- case .success(let msg):
4235
- if case .string(let text) = msg, text.contains("\\"type\\":\\"reload\\"") {
4236
- DispatchQueue.main.async { self.onReload() }
4237
- }
4238
- self.receive()
4239
- case .failure:
4240
- break
4241
- }
4242
- }
4243
- }
4244
-
4245
- func disconnect() {
4246
- webSocketTask?.cancel(with: .normalClosure, reason: nil)
4247
- webSocketTask = nil
4248
- session = nil
4249
- }
4250
- }
4251
- `;
4252
- }
4253
- function getQRScannerViewControllerSwift() {
4254
- return `import UIKit
4255
- import AVFoundation
4256
-
4257
- class QRScannerViewController: UIViewController {
4258
- var onResult: ((String?) -> Void)?
4259
-
4260
- private var captureSession: AVCaptureSession?
4261
- private var previewLayer: AVCaptureVideoPreviewLayer?
4262
-
4263
- override func viewDidLoad() {
4264
- super.viewDidLoad()
4265
- view.backgroundColor = .black
4266
- setupCamera()
4267
- addCancelButton()
4268
- }
4269
-
4270
- private func setupCamera() {
4271
- let session = AVCaptureSession()
4272
- guard let device = AVCaptureDevice.default(for: .video),
4273
- let input = try? AVCaptureDeviceInput(device: device) else {
4274
- onResult?(nil)
4275
- return
4276
- }
4277
-
4278
- let output = AVCaptureMetadataOutput()
4279
- session.addInput(input)
4280
- session.addOutput(output)
4281
- output.setMetadataObjectsDelegate(self, queue: .main)
4282
- output.metadataObjectTypes = [.qr]
4283
-
4284
- let preview = AVCaptureVideoPreviewLayer(session: session)
4285
- preview.frame = view.layer.bounds
4286
- preview.videoGravity = .resizeAspectFill
4287
- view.layer.insertSublayer(preview, at: 0)
4288
- previewLayer = preview
4289
-
4290
- DispatchQueue.global(qos: .userInitiated).async { session.startRunning() }
4291
- captureSession = session
4292
- }
4293
-
4294
- private func addCancelButton() {
4295
- let btn = UIButton(type: .system)
4296
- btn.setTitle("Cancel", for: .normal)
4297
- btn.setTitleColor(.white, for: .normal)
4298
- btn.titleLabel?.font = .systemFont(ofSize: 18, weight: .medium)
4299
- btn.addTarget(self, action: #selector(cancel), for: .touchUpInside)
4300
- btn.translatesAutoresizingMaskIntoConstraints = false
4301
- view.addSubview(btn)
4302
- NSLayoutConstraint.activate([
4303
- btn.centerXAnchor.constraint(equalTo: view.centerXAnchor),
4304
- btn.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -24),
4305
- ])
4306
- }
4307
-
4308
- @objc private func cancel() {
4309
- captureSession?.stopRunning()
4310
- onResult?(nil)
4311
- }
4312
-
4313
- override func viewWillAppear(_ animated: Bool) {
4314
- super.viewWillAppear(animated)
4315
- if captureSession?.isRunning == false {
4316
- DispatchQueue.global(qos: .userInitiated).async { self.captureSession?.startRunning() }
4317
- }
4318
- }
4319
-
4320
- override func viewWillDisappear(_ animated: Bool) {
4321
- super.viewWillDisappear(animated)
4322
- captureSession?.stopRunning()
4323
- }
4324
- }
4325
-
4326
- extension QRScannerViewController: AVCaptureMetadataOutputObjectsDelegate {
4327
- func metadataOutput(_ output: AVCaptureMetadataOutput,
4328
- didOutput objects: [AVMetadataObject],
4329
- from connection: AVCaptureConnection) {
4330
- captureSession?.stopRunning()
4331
- if let obj = objects.first as? AVMetadataMachineReadableCodeObject,
4332
- let value = obj.stringValue {
4333
- onResult?(value)
4334
- }
4335
- }
4336
- }
4337
- `;
4338
- }
4339
- function getLynxInitProcessorSwift() {
4340
- return `// Copyright 2024 The Lynx Authors. All rights reserved.
4341
- // Licensed under the Apache License Version 2.0 that can be found in the
4342
- // LICENSE file in the root directory of this source tree.
4343
-
4344
- import Foundation
4345
-
4346
- // GENERATED IMPORTS START
4347
- // This section is automatically generated by Tamer4Lynx.
4348
- // Manual edits will be overwritten.
4349
- // GENERATED IMPORTS END
4350
-
4351
- final class LynxInitProcessor {
4352
- static let shared = LynxInitProcessor()
4353
- private init() {}
4354
-
4355
- func setupEnvironment() {
4356
- TamerIconElement.registerFonts()
4357
- setupLynxEnv()
4358
- setupLynxService()
4359
- }
4360
-
4361
- private func setupLynxEnv() {
4362
- let env = LynxEnv.sharedInstance()
4363
- let globalConfig = LynxConfig(provider: env.config.templateProvider)
4364
-
4365
- // GENERATED AUTOLINK START
4366
-
4367
- // GENERATED AUTOLINK END
4368
-
4369
- env.prepareConfig(globalConfig)
4370
- }
4371
-
4372
- private func setupLynxService() {
4373
- let webPCoder = SDImageWebPCoder.shared
4374
- SDImageCodersManager.shared.addCoder(webPCoder)
4375
- }
4376
- }
4377
- `;
4378
- }
4379
- function getBridgingHeader() {
4380
- return `#import <Lynx/LynxConfig.h>
4381
- #import <Lynx/LynxEnv.h>
4382
- #import <Lynx/LynxTemplateProvider.h>
4383
- #import <Lynx/LynxView.h>
4384
- #import <Lynx/LynxModule.h>
4385
- #import <SDWebImage/SDWebImage.h>
4386
- #import <SDWebImageWebPCoder/SDWebImageWebPCoder.h>
4387
- `;
4388
- }
4389
- function getInfoPlist() {
4390
- return `<?xml version="1.0" encoding="UTF-8"?>
4391
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4392
- <plist version="1.0">
4393
- <dict>
4394
- <key>CFBundleDevelopmentRegion</key>
4395
- <string>$(DEVELOPMENT_LANGUAGE)</string>
4396
- <key>CFBundleExecutable</key>
4397
- <string>$(EXECUTABLE_NAME)</string>
4398
- <key>CFBundleIdentifier</key>
4399
- <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
4400
- <key>CFBundleInfoDictionaryVersion</key>
4401
- <string>6.0</string>
4402
- <key>CFBundleName</key>
4403
- <string>$(PRODUCT_NAME)</string>
4404
- <key>CFBundlePackageType</key>
4405
- <string>APPL</string>
4406
- <key>CFBundleShortVersionString</key>
4407
- <string>1.0</string>
4408
- <key>CFBundleVersion</key>
4409
- <string>1</string>
4410
- <key>UILaunchStoryboardName</key>
4411
- <string>LaunchScreen</string>
4412
- <key>CFBundleURLTypes</key>
4413
- <array>
4414
- <dict>
4415
- <key>CFBundleURLSchemes</key>
4416
- <array>
4417
- <string>tamerdevapp</string>
4418
- </array>
4419
- </dict>
4420
- </array>
4421
- <key>NSCameraUsageDescription</key>
4422
- <string>Used to scan QR codes for connecting to the dev server</string>
4423
- <key>NSLocalNetworkUsageDescription</key>
4424
- <string>Used to discover Tamer dev servers on your local network</string>
4425
- <key>NSBonjourServices</key>
4426
- <array>
4427
- <string>_tamer._tcp.</string>
4428
- </array>
4429
- <key>NSAppTransportSecurity</key>
4430
- <dict>
4431
- <key>NSAllowsArbitraryLoads</key>
4432
- <true/>
4433
- </dict>
4434
- <key>UIRequiredDeviceCapabilities</key>
4435
- <array>
4436
- <string>armv7</string>
4437
- </array>
4438
- <key>UISupportedInterfaceOrientations</key>
4439
- <array>
4440
- <string>UIInterfaceOrientationPortrait</string>
4441
- <string>UIInterfaceOrientationLandscapeLeft</string>
4442
- <string>UIInterfaceOrientationLandscapeRight</string>
4443
- </array>
4444
- </dict>
4445
- </plist>
4446
- `;
4447
- }
4448
- function getPodfile() {
4449
- return `source 'https://cdn.cocoapods.org/'
4450
-
4451
- platform :ios, '13.0'
4452
-
4453
- target '${APP_NAME}' do
4454
- pod 'Lynx', '3.6.0', :subspecs => [
4455
- 'Framework',
4456
- ], :modular_headers => true
4457
-
4458
- pod 'PrimJS', '3.6.1', :subspecs => ['quickjs', 'napi']
4459
-
4460
- pod 'LynxService', '3.6.0', :subspecs => [
4461
- 'Image',
4462
- 'Log',
4463
- 'Http',
4464
- ]
4465
- pod 'SDWebImage','5.15.5'
4466
- pod 'SDWebImageWebPCoder', '0.11.0'
4467
-
4468
- # GENERATED AUTOLINK DEPENDENCIES START
4469
- # This section is automatically generated by Tamer4Lynx.
4470
- # Manual edits will be overwritten.
4471
- # GENERATED AUTOLINK DEPENDENCIES END
4472
- end
4473
-
4474
- post_install do |installer|
4475
- installer.pods_project.targets.each do |target|
4476
- target.build_configurations.each do |config|
4477
- config.build_settings['CLANG_CXX_LANGUAGE_STANDARD'] = 'gnu++17'
4478
- config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
4479
- config.build_settings['SWIFT_ENABLE_EXPLICIT_MODULES'] = 'NO'
4480
- end
4481
-
4482
- if target.name == 'Lynx'
4483
- target.build_configurations.each do |config|
4484
- flags = [
4485
- '-Wno-vla-extension',
4486
- '-Wno-vla',
4487
- '-Wno-error=vla-extension',
4488
- '-Wno-deprecated-declarations',
4489
- '-Wno-deprecated',
4490
- '-Wno-deprecated-implementations',
4491
- '-Wno-macro-redefined',
4492
- '-Wno-enum-compare',
4493
- '-Wno-enum-compare-conditional',
4494
- '-Wno-enum-conversion'
4495
- ].join(' ')
4496
-
4497
- config.build_settings['OTHER_CPLUSPLUSFLAGS'] = "$(inherited) #{flags}"
4498
- config.build_settings['OTHER_CFLAGS'] = "$(inherited) #{flags}"
4499
- config.build_settings['CLANG_WARN_VLA'] = 'NO'
4500
- config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'NO'
4501
- config.build_settings['CLANG_WARN_ENUM_CONVERSION'] = 'NO'
4502
- end
4503
- end
4504
- end
4505
- end
4506
- `;
4507
- }
4508
- function getMainStoryboard() {
4509
- return `<?xml version="1.0" encoding="UTF-8"?>
4510
- <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0"
4511
- toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none"
4512
- useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"
4513
- initialViewController="BYZ-38-t0r">
4514
- <dependencies>
4515
- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
4516
- <capability name="Safe area layout guides" minToolsVersion="9.0"/>
4517
- </dependencies>
4518
- <scenes>
4519
- <scene sceneID="tne-QT-ifu">
4520
- <objects>
4521
- <viewController id="BYZ-38-t0r" customClass="DevLauncherViewController"
4522
- customModuleProvider="target" sceneMemberID="viewController">
4523
- <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
4524
- <rect key="frame" x="0.0" y="0.0" width="390" height="844"/>
4525
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
4526
- <color key="backgroundColor" systemColor="systemBackgroundColor"/>
4527
- <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
4528
- </view>
4529
- </viewController>
4530
- <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr"
4531
- sceneMemberID="firstResponder"/>
4532
- </objects>
4533
- </scene>
4534
- </scenes>
4535
- </document>
4536
- `;
4537
- }
4538
- function getLaunchScreenStoryboard2() {
4539
- return `<?xml version="1.0" encoding="UTF-8"?>
4540
- <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0"
4541
- toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none"
4542
- useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES"
4543
- colorMatched="YES" initialViewController="01J-lp-oVM">
4544
- <device id="retina6_12" orientation="portrait" appearance="light"/>
4545
- <dependencies>
4546
- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
4547
- <capability name="Safe area layout guides" minToolsVersion="9.0"/>
4548
- <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
4549
- </dependencies>
4550
- <scenes>
4551
- <scene sceneID="EHf-IW-A2E">
4552
- <objects>
4553
- <viewController id="01J-lp-oVM" sceneMemberID="viewController">
4554
- <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
4555
- <rect key="frame" x="0.0" y="0.0" width="390" height="844"/>
4556
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
4557
- <subviews>
4558
- <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Bg9-1M-mhb">
4559
- <rect key="frame" x="0.0" y="0.0" width="390" height="844"/>
4560
- <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
4561
- </view>
4562
- </subviews>
4563
- <viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
4564
- <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
4565
- <constraints>
4566
- <constraint firstItem="Bg9-1M-mhb" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="3M4-v9-a3l"/>
4567
- <constraint firstItem="Bg9-1M-mhb" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="Sbc-LM-HvA"/>
4568
- <constraint firstItem="Bg9-1M-mhb" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="cJ0-4h-f4M"/>
4569
- <constraint firstAttribute="trailing" secondItem="Bg9-1M-mhb" secondAttribute="trailing" id="g0s-pf-rxW"/>
4570
- </constraints>
4571
- </view>
4572
- </viewController>
4573
- <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
4574
- </objects>
4575
- </scene>
4576
- </scenes>
4577
- </document>
4578
- `;
4579
- }
4580
- function generatePbxproj(ids) {
4581
- return `// !$*UTF8*$!
4582
- {
4583
- archiveVersion = 1;
4584
- classes = {};
4585
- objectVersion = 56;
4586
- objects = {
4587
- /* Begin PBXBuildFile section */
4588
- ${ids.appDelegateBuildFile} /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ${ids.appDelegateRef}; };
4589
- ${ids.devLauncherBuildFile} /* DevLauncherViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ${ids.devLauncherRef}; };
4590
- ${ids.projectVCBuildFile} /* ProjectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ${ids.projectVCRef}; };
4591
- ${ids.templateProviderBuildFile} /* DevTemplateProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = ${ids.templateProviderRef}; };
4592
- ${ids.devClientManagerBuildFile} /* DevClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ${ids.devClientManagerRef}; };
4593
- ${ids.devServerPrefsBuildFile} /* DevServerPrefs in Sources (via DevClientModule) */ = {isa = PBXBuildFile; fileRef = ${ids.devClientModuleRef}; };
4594
- ${ids.qrScannerBuildFile} /* QRScannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ${ids.qrScannerRef}; };
4595
- ${ids.lynxInitBuildFile} /* LynxInitProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ${ids.lynxInitRef}; };
4596
- ${ids.mainStoryboardBuildFile} /* Base in Resources */ = {isa = PBXBuildFile; fileRef = ${ids.mainStoryboardBaseRef}; };
4597
- ${ids.launchStoryboardBuildFile} /* Base in Resources */ = {isa = PBXBuildFile; fileRef = ${ids.launchStoryboardBaseRef}; };
4598
- ${ids.assetsBuildFile} /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ${ids.assetsRef}; };
4599
- ${ids.bundleBuildFile} /* dev-client.lynx.bundle in Resources */ = {isa = PBXBuildFile; fileRef = ${ids.bundleRef}; };
4600
- /* End PBXBuildFile section */
4601
-
4602
- /* Begin PBXFileReference section */
4603
- ${ids.appFile} /* ${APP_NAME}.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "${APP_NAME}.app"; sourceTree = BUILT_PRODUCTS_DIR; };
4604
- ${ids.appDelegateRef} /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate.swift"; sourceTree = "<group>"; };
4605
- ${ids.devLauncherRef} /* DevLauncherViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DevLauncherViewController.swift"; sourceTree = "<group>"; };
4606
- ${ids.projectVCRef} /* ProjectViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProjectViewController.swift"; sourceTree = "<group>"; };
4607
- ${ids.templateProviderRef} /* DevTemplateProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DevTemplateProvider.swift"; sourceTree = "<group>"; };
4608
- ${ids.devClientManagerRef} /* DevClientManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DevClientManager.swift"; sourceTree = "<group>"; };
4609
- ${ids.devClientModuleRef} /* DevClientModule (via pod) */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DevClientModule.swift"; sourceTree = "<group>"; };
4610
- ${ids.qrScannerRef} /* QRScannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QRScannerViewController.swift"; sourceTree = "<group>"; };
4611
- ${ids.lynxInitRef} /* LynxInitProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LynxInitProcessor.swift"; sourceTree = "<group>"; };
4612
- ${ids.bridgingHeaderRef} /* ${BRIDGING_HEADER} */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "${BRIDGING_HEADER}"; sourceTree = "<group>"; };
4613
- ${ids.mainStoryboardBaseRef} /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = "Base.lproj/Main.storyboard"; sourceTree = "<group>"; };
4614
- ${ids.launchStoryboardBaseRef} /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = "Base.lproj/LaunchScreen.storyboard"; sourceTree = "<group>"; };
4615
- ${ids.assetsRef} /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Assets.xcassets"; sourceTree = "<group>"; };
4616
- ${ids.bundleRef} /* dev-client.lynx.bundle */ = {isa = PBXFileReference; lastKnownFileType = "file"; path = "dev-client.lynx.bundle"; sourceTree = "<group>"; };
4617
- /* End PBXFileReference section */
4618
-
4619
- /* Begin PBXFrameworksBuildPhase section */
4620
- ${ids.frameworksBuildPhase} /* Frameworks */ = {
4621
- isa = PBXFrameworksBuildPhase;
4622
- buildActionMask = 2147483647;
4623
- files = (
4624
- );
4625
- runOnlyForDeploymentPostprocessing = 0;
4626
- };
4627
- /* End PBXFrameworksBuildPhase section */
4628
-
4629
- /* Begin PBXGroup section */
4630
- ${ids.mainGroup} = {
4631
- isa = PBXGroup;
4632
- children = (
4633
- ${ids.appGroup} /* ${APP_NAME} */,
4634
- ${ids.productsGroup} /* Products */,
4635
- ${ids.frameworksGroup} /* Frameworks */,
4636
- );
4637
- sourceTree = "<group>";
4638
- };
4639
- ${ids.productsGroup} /* Products */ = {
4640
- isa = PBXGroup;
4641
- children = (
4642
- ${ids.appFile} /* ${APP_NAME}.app */,
4643
- );
4644
- name = Products;
4645
- sourceTree = "<group>";
4646
- };
4647
- ${ids.frameworksGroup} /* Frameworks */ = {
4648
- isa = PBXGroup;
4649
- children = (
4650
- );
4651
- name = Frameworks;
4652
- sourceTree = "<group>";
4653
- };
4654
- ${ids.appGroup} /* ${APP_NAME} */ = {
4655
- isa = PBXGroup;
4656
- children = (
4657
- ${ids.appDelegateRef} /* AppDelegate.swift */,
4658
- ${ids.devLauncherRef} /* DevLauncherViewController.swift */,
4659
- ${ids.projectVCRef} /* ProjectViewController.swift */,
4660
- ${ids.templateProviderRef} /* DevTemplateProvider.swift */,
4661
- ${ids.devClientManagerRef} /* DevClientManager.swift */,
4662
- ${ids.devClientModuleRef} /* DevClientModule.swift */,
4663
- ${ids.qrScannerRef} /* QRScannerViewController.swift */,
4664
- ${ids.lynxInitRef} /* LynxInitProcessor.swift */,
4665
- ${ids.bridgingHeaderRef} /* ${BRIDGING_HEADER} */,
4666
- ${ids.mainStoryboardRef} /* Main.storyboard */,
4667
- ${ids.launchStoryboardRef} /* LaunchScreen.storyboard */,
4668
- ${ids.assetsRef} /* Assets.xcassets */,
4669
- ${ids.bundleRef} /* dev-client.lynx.bundle */,
4670
- );
4671
- path = "${APP_NAME}";
4672
- sourceTree = "<group>";
4673
- };
4674
- /* End PBXGroup section */
4675
-
4676
- /* Begin PBXNativeTarget section */
4677
- ${ids.nativeTarget} /* ${APP_NAME} */ = {
4678
- isa = PBXNativeTarget;
4679
- buildConfigurationList = ${ids.targetBuildConfigList};
4680
- buildPhases = (
4681
- ${ids.sourcesBuildPhase} /* Sources */,
4682
- ${ids.frameworksBuildPhase} /* Frameworks */,
4683
- ${ids.resourcesBuildPhase} /* Resources */,
4684
- ${ids.fontCopyScriptPhase} /* [Tamer] Copy Icon Fonts */,
4685
- );
4686
- buildRules = (
4687
- );
4688
- dependencies = (
4689
- );
4690
- name = "${APP_NAME}";
4691
- productName = "${APP_NAME}";
4692
- productReference = ${ids.appFile};
4693
- productType = "com.apple.product-type.application";
4694
- };
4695
- /* End PBXNativeTarget section */
4696
-
4697
- /* Begin PBXProject section */
4698
- ${ids.project} /* Project object */ = {
4699
- isa = PBXProject;
4700
- attributes = {
4701
- LastUpgradeCheck = 1530;
4702
- };
4703
- buildConfigurationList = ${ids.projectBuildConfigList};
4704
- compatibilityVersion = "Xcode 14.0";
4705
- developmentRegion = en;
4706
- hasScannedForEncodings = 0;
4707
- knownRegions = (
4708
- en,
4709
- Base,
4710
- );
4711
- mainGroup = ${ids.mainGroup};
4712
- productRefGroup = ${ids.productsGroup} /* Products */;
4713
- projectDirPath = "";
4714
- projectRoot = "";
4715
- targets = (
4716
- ${ids.nativeTarget} /* ${APP_NAME} */,
4717
- );
4718
- };
4719
- /* End PBXProject section */
4720
-
4721
- /* Begin PBXResourcesBuildPhase section */
4722
- ${ids.resourcesBuildPhase} /* Resources */ = {
4723
- isa = PBXResourcesBuildPhase;
4724
- buildActionMask = 2147483647;
4725
- files = (
4726
- ${ids.assetsBuildFile} /* Assets.xcassets in Resources */,
4727
- ${ids.mainStoryboardBuildFile} /* Base in Resources */,
4728
- ${ids.launchStoryboardBuildFile} /* Base in Resources */,
4729
- ${ids.bundleBuildFile} /* dev-client.lynx.bundle in Resources */,
4730
- );
4731
- runOnlyForDeploymentPostprocessing = 0;
4732
- };
4733
- /* End PBXResourcesBuildPhase section */
4734
-
4735
- /* Begin PBXShellScriptBuildPhase section */
4736
- ${ids.fontCopyScriptPhase} /* [Tamer] Copy Icon Fonts */ = {
4737
- isa = PBXShellScriptBuildPhase;
4738
- buildActionMask = 2147483647;
4739
- files = (
4740
- );
4741
- inputPaths = (
4742
- );
4743
- name = "[Tamer] Copy Icon Fonts";
4744
- outputPaths = (
4745
- );
4746
- runOnlyForDeploymentPostprocessing = 0;
4747
- shellPath = /bin/sh;
4748
- shellScript = "FONTS_SRC=\\"${SRCROOT}/../../tamer-icons/fonts\\"\\nif [ -d \\"$FONTS_SRC\\" ]; then\\n cp -f \\"$FONTS_SRC/MaterialSymbolsOutlined.ttf\\" \\"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/\\" 2>/dev/null || true\\n cp -f \\"$FONTS_SRC/fa-solid-900.ttf\\" \\"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/\\" 2>/dev/null || true\\nfi\\nCP_SRC=\\"${SRCROOT}/../../tamer-icons/android/src/main/assets/fonts/material-codepoints.txt\\"\\n[ -f \\"$CP_SRC\\" ] && cp -f \\"$CP_SRC\\" \\"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/\\" 2>/dev/null || true\\n";
4749
- };
4750
- /* End PBXShellScriptBuildPhase section */
4751
-
4752
- /* Begin PBXSourcesBuildPhase section */
4753
- ${ids.sourcesBuildPhase} /* Sources */ = {
4754
- isa = PBXSourcesBuildPhase;
4755
- buildActionMask = 2147483647;
4756
- files = (
4757
- ${ids.appDelegateBuildFile} /* AppDelegate.swift in Sources */,
4758
- ${ids.devLauncherBuildFile} /* DevLauncherViewController.swift in Sources */,
4759
- ${ids.projectVCBuildFile} /* ProjectViewController.swift in Sources */,
4760
- ${ids.templateProviderBuildFile} /* DevTemplateProvider.swift in Sources */,
4761
- ${ids.devClientManagerBuildFile} /* DevClientManager.swift in Sources */,
4762
- ${ids.qrScannerBuildFile} /* QRScannerViewController.swift in Sources */,
4763
- ${ids.lynxInitBuildFile} /* LynxInitProcessor.swift in Sources */,
4764
- );
4765
- runOnlyForDeploymentPostprocessing = 0;
4766
- };
4767
- /* End PBXSourcesBuildPhase section */
4768
-
4769
- /* Begin PBXVariantGroup section */
4770
- ${ids.mainStoryboardRef} /* Main.storyboard */ = {
4771
- isa = PBXVariantGroup;
4772
- children = (
4773
- ${ids.mainStoryboardBaseRef} /* Base */,
4774
- );
4775
- name = "Main.storyboard";
4776
- sourceTree = "<group>";
4777
- };
4778
- ${ids.launchStoryboardRef} /* LaunchScreen.storyboard */ = {
4779
- isa = PBXVariantGroup;
4780
- children = (
4781
- ${ids.launchStoryboardBaseRef} /* Base */,
4782
- );
4783
- name = "LaunchScreen.storyboard";
4784
- sourceTree = "<group>";
4785
- };
4786
- /* End PBXVariantGroup section */
4787
-
4788
- /* Begin XCBuildConfiguration section */
4789
- ${ids.projectDebugConfig} /* Debug */ = {
4790
- isa = XCBuildConfiguration;
4791
- buildSettings = {
4792
- ALWAYS_SEARCH_USER_PATHS = NO;
4793
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
4794
- CLANG_CXX_LIBRARY = "libc++";
4795
- CLANG_ENABLE_MODULES = YES;
4796
- CLANG_ENABLE_OBJC_ARC = YES;
4797
- COPY_PHASE_STRIP = NO;
4798
- DEBUG_INFORMATION_FORMAT = dwarf;
4799
- GCC_C_LANGUAGE_STANDARD = gnu11;
4800
- GCC_NO_COMMON_BLOCKS = YES;
4801
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
4802
- SDKROOT = iphoneos;
4803
- SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
4804
- SWIFT_OPTIMIZATION_LEVEL = "-Onone";
4805
- };
4806
- name = Debug;
4807
- };
4808
- ${ids.projectReleaseConfig} /* Release */ = {
4809
- isa = XCBuildConfiguration;
4810
- buildSettings = {
4811
- ALWAYS_SEARCH_USER_PATHS = NO;
4812
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
4813
- CLANG_CXX_LIBRARY = "libc++";
4814
- CLANG_ENABLE_MODULES = YES;
4815
- CLANG_ENABLE_OBJC_ARC = YES;
4816
- COPY_PHASE_STRIP = NO;
4817
- DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
4818
- GCC_C_LANGUAGE_STANDARD = gnu11;
4819
- GCC_NO_COMMON_BLOCKS = YES;
4820
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
4821
- SDKROOT = iphoneos;
4822
- SWIFT_COMPILATION_MODE = wholemodule;
4823
- SWIFT_OPTIMIZATION_LEVEL = "-O";
4824
- };
4825
- name = Release;
4826
- };
4827
- ${ids.targetDebugConfig} /* Debug */ = {
4828
- isa = XCBuildConfiguration;
4829
- buildSettings = {
4830
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
4831
- CURRENT_PROJECT_VERSION = 1;
4832
- INFOPLIST_FILE = "${APP_NAME}/Info.plist";
4833
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
4834
- MARKETING_VERSION = "1.0";
4835
- PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}";
4836
- PRODUCT_NAME = "$(TARGET_NAME)";
4837
- SWIFT_OBJC_BRIDGING_HEADER = "${APP_NAME}/${BRIDGING_HEADER}";
4838
- SWIFT_VERSION = 5.0;
4839
- TARGETED_DEVICE_FAMILY = "1,2";
4840
- };
4841
- name = Debug;
4842
- };
4843
- ${ids.targetReleaseConfig} /* Release */ = {
4844
- isa = XCBuildConfiguration;
4845
- buildSettings = {
4846
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
4847
- CURRENT_PROJECT_VERSION = 1;
4848
- INFOPLIST_FILE = "${APP_NAME}/Info.plist";
4849
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
4850
- MARKETING_VERSION = "1.0";
4851
- PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}";
4852
- PRODUCT_NAME = "$(TARGET_NAME)";
4853
- SWIFT_OBJC_BRIDGING_HEADER = "${APP_NAME}/${BRIDGING_HEADER}";
4854
- SWIFT_VERSION = 5.0;
4855
- TARGETED_DEVICE_FAMILY = "1,2";
4856
- };
4857
- name = Release;
4858
- };
4859
- /* End XCBuildConfiguration section */
4860
-
4861
- /* Begin XCConfigurationList section */
4862
- ${ids.projectBuildConfigList} = {
4863
- isa = XCConfigurationList;
4864
- buildConfigurations = (
4865
- ${ids.projectDebugConfig} /* Debug */,
4866
- ${ids.projectReleaseConfig} /* Release */,
4867
- );
4868
- defaultConfigurationIsVisible = 0;
4869
- defaultConfigurationName = Release;
4870
- };
4871
- ${ids.targetBuildConfigList} = {
4872
- isa = XCConfigurationList;
4873
- buildConfigurations = (
4874
- ${ids.targetDebugConfig} /* Debug */,
4875
- ${ids.targetReleaseConfig} /* Release */,
4876
- );
4877
- defaultConfigurationIsVisible = 0;
4878
- defaultConfigurationName = Release;
4879
- };
4880
- /* End XCConfigurationList section */
4881
- };
4882
- rootObject = ${ids.project} /* Project object */;
4883
- }
4884
- `;
4885
- }
4886
- async function createDevAppProject(iosDir, repoRoot) {
4887
- const projectDir = path16.join(iosDir, APP_NAME);
4888
- const xcodeprojDir = path16.join(iosDir, `${APP_NAME}.xcodeproj`);
4889
- if (fs16.existsSync(iosDir)) {
4890
- fs16.rmSync(iosDir, { recursive: true, force: true });
4891
- }
4892
- console.log(`\u{1F680} Creating TamerDevApp iOS project at: ${iosDir}`);
4893
- const ids = {};
4894
- const idKeys = [
4895
- "project",
4896
- "mainGroup",
4897
- "appGroup",
4898
- "productsGroup",
4899
- "frameworksGroup",
4900
- "appFile",
4901
- "appDelegateRef",
4902
- "devLauncherRef",
4903
- "projectVCRef",
4904
- "templateProviderRef",
4905
- "devClientManagerRef",
4906
- "devClientModuleRef",
4907
- "qrScannerRef",
4908
- "lynxInitRef",
4909
- "bridgingHeaderRef",
4910
- "mainStoryboardRef",
4911
- "mainStoryboardBaseRef",
4912
- "launchStoryboardRef",
4913
- "launchStoryboardBaseRef",
4914
- "assetsRef",
4915
- "bundleRef",
4916
- "nativeTarget",
4917
- "appDelegateBuildFile",
4918
- "devLauncherBuildFile",
4919
- "projectVCBuildFile",
4920
- "templateProviderBuildFile",
4921
- "devClientManagerBuildFile",
4922
- "devServerPrefsBuildFile",
4923
- "qrScannerBuildFile",
4924
- "lynxInitBuildFile",
4925
- "mainStoryboardBuildFile",
4926
- "launchStoryboardBuildFile",
4927
- "assetsBuildFile",
4928
- "bundleBuildFile",
4929
- "frameworksBuildPhase",
4930
- "resourcesBuildPhase",
4931
- "sourcesBuildPhase",
4932
- "fontCopyScriptPhase",
4933
- "projectBuildConfigList",
4934
- "targetBuildConfigList",
4935
- "projectDebugConfig",
4936
- "projectReleaseConfig",
4937
- "targetDebugConfig",
4938
- "targetReleaseConfig"
4939
- ];
4940
- for (const k of idKeys) ids[k] = generateId();
4941
- writeFile2(path16.join(iosDir, "Podfile"), getPodfile());
4942
- writeFile2(path16.join(projectDir, "AppDelegate.swift"), getAppDelegateSwift2());
4943
- const devClientPkg = findDevClientPackage(repoRoot);
4944
- const templateDir = devClientPkg ? path16.join(devClientPkg, "ios", "templates") : null;
4945
- const templateVars = { PROJECT_BUNDLE_SEGMENT: "tamer-dev-app" };
4946
- const templateFiles = [
4947
- "DevLauncherViewController.swift",
4948
- "ProjectViewController.swift",
4949
- "DevTemplateProvider.swift",
4950
- "DevClientManager.swift",
4951
- "QRScannerViewController.swift",
4952
- "LynxInitProcessor.swift"
4953
- ];
4954
- for (const f of templateFiles) {
4955
- const src = templateDir ? path16.join(templateDir, f) : null;
4956
- if (src && fs16.existsSync(src)) {
4957
- writeFile2(path16.join(projectDir, f), readAndSubstituteTemplate4(src, templateVars));
4958
- } else {
4959
- const fallback = (() => {
4960
- switch (f) {
4961
- case "DevLauncherViewController.swift":
4962
- return getDevLauncherViewControllerSwift();
4963
- case "ProjectViewController.swift":
4964
- return getProjectViewControllerSwift();
4965
- case "DevTemplateProvider.swift":
4966
- return getDevTemplateProviderSwift();
4967
- case "DevClientManager.swift":
4968
- return getDevClientManagerSwift();
4969
- case "QRScannerViewController.swift":
4970
- return getQRScannerViewControllerSwift();
4971
- case "LynxInitProcessor.swift":
4972
- return getLynxInitProcessorSwift();
4973
- default:
4974
- return "";
4975
- }
4976
- })();
4977
- if (fallback) writeFile2(path16.join(projectDir, f), fallback);
4978
- }
4979
- }
4980
- writeFile2(path16.join(projectDir, BRIDGING_HEADER), getBridgingHeader());
4981
- writeFile2(path16.join(projectDir, "Info.plist"), getInfoPlist());
4982
- writeFile2(path16.join(projectDir, "Base.lproj", "Main.storyboard"), getMainStoryboard());
4983
- writeFile2(path16.join(projectDir, "Base.lproj", "LaunchScreen.storyboard"), getLaunchScreenStoryboard2());
4984
- writeFile2(
4985
- path16.join(projectDir, "Assets.xcassets", "AppIcon.appiconset", "Contents.json"),
4986
- JSON.stringify({ images: [{ idiom: "universal", platform: "ios", size: "1024x1024" }], info: { author: "xcode", version: 1 } }, null, 2)
4987
- );
4988
- writeFile2(
4989
- path16.join(projectDir, "Assets.xcassets", "Contents.json"),
4990
- JSON.stringify({ info: { author: "xcode", version: 1 } }, null, 2)
4991
- );
4992
- writeFile2(path16.join(projectDir, "dev-client.lynx.bundle"), "");
4993
- fs16.mkdirSync(xcodeprojDir, { recursive: true });
4994
- writeFile2(path16.join(xcodeprojDir, "project.pbxproj"), generatePbxproj(ids));
4995
- console.log(`\u2705 TamerDevApp iOS project created at ${iosDir}`);
4996
- await setupCocoaPods(iosDir);
4997
- }
4998
- async function syncDevClientIos() {
4999
- let resolved;
5000
- let repoRoot;
5001
- try {
5002
- repoRoot = findRepoRoot(process.cwd());
5003
- resolved = resolveDevAppPaths(repoRoot);
5004
- } catch (e) {
5005
- console.error(`\u274C ${e.message}`);
5006
- process.exit(1);
5007
- }
5008
- const iosDir = resolved.iosDir;
5009
- const workspacePath = path16.join(iosDir, `${APP_NAME}.xcworkspace`);
5010
- const projectDir = path16.join(iosDir, APP_NAME);
5011
- const hasCommittedSource = fs16.existsSync(path16.join(projectDir, "AppDelegate.swift"));
5012
- if (!hasCommittedSource) {
5013
- await createDevAppProject(iosDir, repoRoot);
5014
- } else if (!fs16.existsSync(workspacePath)) {
5015
- await setupCocoaPods(iosDir);
5016
- console.log(`\u2139\uFE0F iOS dev-app project exists; ran pod install`);
5017
- } else {
5018
- console.log(`\u2139\uFE0F iOS dev-app project already exists at ${iosDir}`);
5019
- }
5020
- const prev = process.cwd();
5021
- process.chdir(resolved.projectRoot);
5022
- try {
5023
- autolink_default2();
5024
- } finally {
5025
- process.chdir(prev);
5026
- }
5027
- const devClientDir = resolved.lynxProjectDir;
5028
- console.log("\u{1F4E6} Building dev-client Lynx bundle...");
5029
- execSync7("npm run build", { stdio: "inherit", cwd: devClientDir });
5030
- const bundleSrc = resolved.lynxBundlePath;
5031
- const bundleDst = path16.join(iosDir, APP_NAME, "dev-client.lynx.bundle");
5032
- if (fs16.existsSync(bundleSrc)) {
5033
- fs16.copyFileSync(bundleSrc, bundleDst);
5034
- console.log(`\u2728 Copied dev-client.lynx.bundle to iOS project`);
5035
- } else {
5036
- console.warn(`\u26A0\uFE0F Bundle not found at ${bundleSrc}`);
5037
- }
5038
- }
5039
- var syncDevClient_default2 = syncDevClientIos;
5040
-
5041
- // src/ios/build.ts
5042
- var DEV_APP_NAME = "TamerDevApp";
5043
- var SIMULATOR_ID = "A07F36D8-873A-41E0-8B90-3DF328A6B614";
5044
- function findBootedSimulator() {
5045
- try {
5046
- const out = execSync8("xcrun simctl list devices --json", { encoding: "utf8" });
5047
- const json = JSON.parse(out);
5048
- for (const runtimes of Object.values(json.devices)) {
5049
- for (const device of runtimes) {
5050
- if (device.state === "Booted") return device.udid;
5051
- }
5052
- }
5053
- } catch {
5054
- }
5055
- return null;
5056
- }
5057
- async function buildIpa(opts = {}) {
5058
- const target = opts.target ?? "host";
5059
- const resolved = resolveHostPaths();
5060
- if (!resolved.config.ios?.appName) {
5061
- throw new Error('"ios.appName" must be defined in tamer.config.json');
5062
- }
5063
- if (target === "dev-app") {
5064
- await buildIosDevApp(opts.install, opts.release);
5065
- return;
5066
- }
5067
- const appName = resolved.config.ios.appName;
5068
- const bundleId = resolved.config.ios.bundleId;
5069
- const iosDir = resolved.iosDir;
5070
- const configuration = opts.release ? "Release" : "Debug";
5071
- bundle_default2({ target, release: opts.release });
5072
- const scheme = appName;
5073
- const workspacePath = path17.join(iosDir, `${appName}.xcworkspace`);
5074
- const projectPath = path17.join(iosDir, `${appName}.xcodeproj`);
5075
- const xcproject = fs17.existsSync(workspacePath) ? workspacePath : projectPath;
5076
- const flag = xcproject.endsWith(".xcworkspace") ? "-workspace" : "-project";
5077
- const derivedDataPath = path17.join(iosDir, "build");
5078
- const sdk = opts.install ? "iphonesimulator" : "iphoneos";
5079
- console.log(`
5080
- \u{1F528} Building ${configuration} (${sdk})...`);
5081
- execSync8(
5082
- `xcodebuild ${flag} "${xcproject}" -scheme "${scheme}" -configuration ${configuration} -sdk ${sdk} -derivedDataPath "${derivedDataPath}"`,
5083
- { stdio: "inherit", cwd: iosDir }
5084
- );
5085
- console.log(`\u2705 Build completed.`);
5086
- if (opts.install) {
5087
- const appGlob = path17.join(
5088
- derivedDataPath,
5089
- "Build",
5090
- "Products",
5091
- `${configuration}-iphonesimulator`,
5092
- `${appName}.app`
5093
- );
5094
- if (!fs17.existsSync(appGlob)) {
5095
- console.error(`\u274C Built app not found at: ${appGlob}`);
5096
- process.exit(1);
5097
- }
5098
- const udid = findBootedSimulator();
5099
- if (!udid) {
5100
- console.error("\u274C No booted simulator found. Start one with: xcrun simctl boot <udid>");
5101
- process.exit(1);
5102
- }
5103
- console.log(`\u{1F4F2} Installing on simulator ${udid}...`);
5104
- execSync8(`xcrun simctl install "${udid}" "${appGlob}"`, { stdio: "inherit" });
5105
- if (bundleId) {
5106
- console.log(`\u{1F680} Launching ${bundleId}...`);
5107
- execSync8(`xcrun simctl launch "${udid}" "${bundleId}"`, { stdio: "inherit" });
5108
- console.log("\u2705 App launched.");
5109
- } else {
5110
- console.log('\u2705 App installed. (Set "ios.bundleId" in tamer.config.json to auto-launch.)');
5111
- }
5112
- }
5113
- }
5114
- async function buildIosDevApp(install, release) {
5115
- const repoRoot = findRepoRoot(process.cwd());
5116
- const resolved = resolveDevAppPaths(repoRoot);
5117
- const iosDir = resolved.iosDir;
5118
- const configuration = release ? "Release" : "Debug";
5119
- await syncDevClient_default2();
5120
- const workspacePath = path17.join(iosDir, `${DEV_APP_NAME}.xcworkspace`);
5121
- const projectPath = path17.join(iosDir, `${DEV_APP_NAME}.xcodeproj`);
5122
- const xcproject = fs17.existsSync(workspacePath) ? workspacePath : projectPath;
5123
- const flag = xcproject.endsWith(".xcworkspace") ? "workspace" : "project";
5124
- console.log(`
5125
- \u{1F528} Building TamerDevApp for simulator (${configuration})...`);
5126
- execSync8(
5127
- `xcodebuild -${flag} "${xcproject}" -scheme "${DEV_APP_NAME}" -configuration ${configuration} -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 16 Pro,OS=18.5" -derivedDataPath build`,
5128
- { stdio: "inherit", cwd: iosDir }
5129
- );
5130
- console.log("\u2705 TamerDevApp built successfully.");
5131
- if (install) {
5132
- const appPath = path17.join(iosDir, "build", "Build", "Products", `${configuration}-iphonesimulator`, `${DEV_APP_NAME}.app`);
5133
- console.log("\n\u{1F4F2} Installing to simulator...");
5134
- try {
5135
- execSync8(`xcrun simctl boot "${SIMULATOR_ID}" 2>/dev/null`);
5136
- } catch {
5137
- }
5138
- execSync8(`xcrun simctl install "${SIMULATOR_ID}" "${appPath}"`, { stdio: "inherit" });
5139
- execSync8(`xcrun simctl launch "${SIMULATOR_ID}" "com.nanofuxion.tamerdevapp"`, { stdio: "inherit" });
5140
- execSync8("open -a Simulator", { stdio: "inherit" });
5141
- console.log("\u2705 TamerDevApp launched in simulator.");
5142
- }
5143
- }
5144
- var build_default2 = buildIpa;
5145
-
5146
- // src/common/init.ts
5147
- import fs18 from "fs";
5148
- import path18 from "path";
5149
- import readline from "readline";
5150
- var rl = readline.createInterface({
5151
- input: process.stdin,
5152
- output: process.stdout,
5153
- terminal: false
5154
- });
5155
- function ask(question) {
5156
- return new Promise((resolve) => {
5157
- rl.question(question, (answer) => resolve(answer.trim()));
5158
- });
5159
- }
5160
- async function init() {
5161
- console.log("Tamer4Lynx Init: Let's set up your tamer.config.json\n");
5162
- const androidAppName = await ask("Android app name: ");
5163
- const androidPackageName = await ask("Android package name (e.g. com.example.app): ");
5164
- let androidSdk = await ask("Android SDK path (e.g. ~/Library/Android/sdk or $ANDROID_HOME): ");
5165
- if (androidSdk.startsWith("$") && /^[A-Z0-9_]+$/.test(androidSdk.slice(1))) {
5166
- const envVar = androidSdk.slice(1);
5167
- const envValue = process.env[envVar];
5168
- if (envValue) {
5169
- androidSdk = envValue;
5170
- console.log(`Resolved ${androidSdk} from $${envVar}`);
5171
- } else {
5172
- console.warn(`Environment variable $${envVar} not found. SDK path will be left as-is.`);
3843
+ // src/common/init.ts
3844
+ import fs16 from "fs";
3845
+ import path17 from "path";
3846
+ import readline from "readline";
3847
+ var rl = readline.createInterface({
3848
+ input: process.stdin,
3849
+ output: process.stdout,
3850
+ terminal: false
3851
+ });
3852
+ function ask(question) {
3853
+ return new Promise((resolve) => {
3854
+ rl.question(question, (answer) => resolve(answer.trim()));
3855
+ });
3856
+ }
3857
+ async function init() {
3858
+ console.log("Tamer4Lynx Init: Let's set up your tamer.config.json\n");
3859
+ const androidAppName = await ask("Android app name: ");
3860
+ const androidPackageName = await ask("Android package name (e.g. com.example.app): ");
3861
+ let androidSdk = await ask("Android SDK path (e.g. ~/Library/Android/sdk or $ANDROID_HOME): ");
3862
+ if (androidSdk.startsWith("$") && /^[A-Z0-9_]+$/.test(androidSdk.slice(1))) {
3863
+ const envVar = androidSdk.slice(1);
3864
+ const envValue = process.env[envVar];
3865
+ if (envValue) {
3866
+ androidSdk = envValue;
3867
+ console.log(`Resolved ${androidSdk} from $${envVar}`);
3868
+ } else {
3869
+ console.warn(`Environment variable $${envVar} not found. SDK path will be left as-is.`);
5173
3870
  }
5174
3871
  }
5175
3872
  const useSame = await ask("Use same name and bundle ID for iOS as Android? (y/N): ");
@@ -5196,8 +3893,8 @@ async function init() {
5196
3893
  paths: { androidDir: "android", iosDir: "ios" }
5197
3894
  };
5198
3895
  if (lynxProject) config.lynxProject = lynxProject;
5199
- const configPath = path18.join(process.cwd(), "tamer.config.json");
5200
- fs18.writeFileSync(configPath, JSON.stringify(config, null, 2));
3896
+ const configPath = path17.join(process.cwd(), "tamer.config.json");
3897
+ fs16.writeFileSync(configPath, JSON.stringify(config, null, 2));
5201
3898
  console.log(`
5202
3899
  \u2705 Generated tamer.config.json at ${configPath}`);
5203
3900
  rl.close();
@@ -5205,8 +3902,8 @@ async function init() {
5205
3902
  var init_default = init;
5206
3903
 
5207
3904
  // src/common/create.ts
5208
- import fs19 from "fs";
5209
- import path19 from "path";
3905
+ import fs17 from "fs";
3906
+ import path18 from "path";
5210
3907
  import readline2 from "readline";
5211
3908
  var rl2 = readline2.createInterface({ input: process.stdin, output: process.stdout, terminal: false });
5212
3909
  function ask2(question) {
@@ -5236,13 +3933,13 @@ async function create3() {
5236
3933
  const simpleModuleName = extName.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("") + "Module";
5237
3934
  const fullModuleClassName = `${packageName}.${simpleModuleName}`;
5238
3935
  const cwd = process.cwd();
5239
- const root = path19.join(cwd, extName);
5240
- if (fs19.existsSync(root)) {
3936
+ const root = path18.join(cwd, extName);
3937
+ if (fs17.existsSync(root)) {
5241
3938
  console.error(`\u274C Directory ${extName} already exists.`);
5242
3939
  rl2.close();
5243
3940
  process.exit(1);
5244
3941
  }
5245
- fs19.mkdirSync(root, { recursive: true });
3942
+ fs17.mkdirSync(root, { recursive: true });
5246
3943
  const lynxExt = {
5247
3944
  platforms: {
5248
3945
  android: {
@@ -5257,7 +3954,7 @@ async function create3() {
5257
3954
  web: {}
5258
3955
  }
5259
3956
  };
5260
- fs19.writeFileSync(path19.join(root, "lynx.ext.json"), JSON.stringify(lynxExt, null, 2));
3957
+ fs17.writeFileSync(path18.join(root, "lynx.ext.json"), JSON.stringify(lynxExt, null, 2));
5261
3958
  const pkg = {
5262
3959
  name: extName,
5263
3960
  version: "0.0.1",
@@ -5270,17 +3967,17 @@ async function create3() {
5270
3967
  engines: { node: ">=18" }
5271
3968
  };
5272
3969
  if (includeModule) pkg.types = "src/index.d.ts";
5273
- fs19.writeFileSync(path19.join(root, "package.json"), JSON.stringify(pkg, null, 2));
3970
+ fs17.writeFileSync(path18.join(root, "package.json"), JSON.stringify(pkg, null, 2));
5274
3971
  const pkgPath = packageName.replace(/\./g, "/");
5275
3972
  if (includeModule) {
5276
- fs19.mkdirSync(path19.join(root, "src"), { recursive: true });
5277
- fs19.writeFileSync(path19.join(root, "src", "index.d.ts"), `/** @lynxmodule */
3973
+ fs17.mkdirSync(path18.join(root, "src"), { recursive: true });
3974
+ fs17.writeFileSync(path18.join(root, "src", "index.d.ts"), `/** @lynxmodule */
5278
3975
  export declare class ${simpleModuleName} {
5279
3976
  // Add your module methods here
5280
3977
  }
5281
3978
  `);
5282
- fs19.mkdirSync(path19.join(root, "android", "src", "main", "kotlin", pkgPath), { recursive: true });
5283
- fs19.writeFileSync(path19.join(root, "android", "build.gradle.kts"), `plugins {
3979
+ fs17.mkdirSync(path18.join(root, "android", "src", "main", "kotlin", pkgPath), { recursive: true });
3980
+ fs17.writeFileSync(path18.join(root, "android", "build.gradle.kts"), `plugins {
5284
3981
  id("com.android.library")
5285
3982
  id("org.jetbrains.kotlin.android")
5286
3983
  }
@@ -5301,7 +3998,7 @@ dependencies {
5301
3998
  implementation(libs.lynx.jssdk)
5302
3999
  }
5303
4000
  `);
5304
- fs19.writeFileSync(path19.join(root, "android", "src", "main", "AndroidManifest.xml"), `<?xml version="1.0" encoding="utf-8"?>
4001
+ fs17.writeFileSync(path18.join(root, "android", "src", "main", "AndroidManifest.xml"), `<?xml version="1.0" encoding="utf-8"?>
5305
4002
  <manifest />
5306
4003
  `);
5307
4004
  const ktContent = `package ${packageName}
@@ -5318,8 +4015,8 @@ class ${simpleModuleName}(context: Context) : LynxModule(context) {
5318
4015
  }
5319
4016
  }
5320
4017
  `;
5321
- fs19.writeFileSync(path19.join(root, "android", "src", "main", "kotlin", pkgPath, `${simpleModuleName}.kt`), ktContent);
5322
- fs19.mkdirSync(path19.join(root, "ios", extName, extName, "Classes"), { recursive: true });
4018
+ fs17.writeFileSync(path18.join(root, "android", "src", "main", "kotlin", pkgPath, `${simpleModuleName}.kt`), ktContent);
4019
+ fs17.mkdirSync(path18.join(root, "ios", extName, extName, "Classes"), { recursive: true });
5323
4020
  const podspec = `Pod::Spec.new do |s|
5324
4021
  s.name = '${extName}'
5325
4022
  s.version = '0.0.1'
@@ -5333,7 +4030,7 @@ class ${simpleModuleName}(context: Context) : LynxModule(context) {
5333
4030
  s.dependency 'Lynx'
5334
4031
  end
5335
4032
  `;
5336
- fs19.writeFileSync(path19.join(root, "ios", extName, `${extName}.podspec`), podspec);
4033
+ fs17.writeFileSync(path18.join(root, "ios", extName, `${extName}.podspec`), podspec);
5337
4034
  const swiftContent = `import Foundation
5338
4035
 
5339
4036
  @objc public class ${simpleModuleName}: NSObject {
@@ -5342,16 +4039,16 @@ end
5342
4039
  }
5343
4040
  }
5344
4041
  `;
5345
- fs19.writeFileSync(path19.join(root, "ios", extName, extName, "Classes", `${simpleModuleName}.swift`), swiftContent);
4042
+ fs17.writeFileSync(path18.join(root, "ios", extName, extName, "Classes", `${simpleModuleName}.swift`), swiftContent);
5346
4043
  }
5347
- fs19.writeFileSync(path19.join(root, "index.js"), `'use strict';
4044
+ fs17.writeFileSync(path18.join(root, "index.js"), `'use strict';
5348
4045
  module.exports = {};
5349
4046
  `);
5350
- fs19.writeFileSync(path19.join(root, "tsconfig.json"), JSON.stringify({
4047
+ fs17.writeFileSync(path18.join(root, "tsconfig.json"), JSON.stringify({
5351
4048
  compilerOptions: { target: "ES2020", module: "ESNext", moduleResolution: "bundler", strict: true },
5352
4049
  include: ["src"]
5353
4050
  }, null, 2));
5354
- fs19.writeFileSync(path19.join(root, "README.md"), `# ${extName}
4051
+ fs17.writeFileSync(path18.join(root, "README.md"), `# ${extName}
5355
4052
 
5356
4053
  Lynx extension for ${extName}.
5357
4054
 
@@ -5376,8 +4073,8 @@ This package uses \`lynx.ext.json\` (RFC-compliant) for autolinking.
5376
4073
  var create_default3 = create3;
5377
4074
 
5378
4075
  // src/common/codegen.ts
5379
- import fs20 from "fs";
5380
- import path20 from "path";
4076
+ import fs18 from "fs";
4077
+ import path19 from "path";
5381
4078
  function codegen() {
5382
4079
  const cwd = process.cwd();
5383
4080
  const config = loadExtensionConfig(cwd);
@@ -5385,9 +4082,9 @@ function codegen() {
5385
4082
  console.error("\u274C No lynx.ext.json or tamer.json found. Run from an extension package root.");
5386
4083
  process.exit(1);
5387
4084
  }
5388
- const srcDir = path20.join(cwd, "src");
5389
- const generatedDir = path20.join(cwd, "generated");
5390
- fs20.mkdirSync(generatedDir, { recursive: true });
4085
+ const srcDir = path19.join(cwd, "src");
4086
+ const generatedDir = path19.join(cwd, "generated");
4087
+ fs18.mkdirSync(generatedDir, { recursive: true });
5391
4088
  const dtsFiles = findDtsFiles(srcDir);
5392
4089
  const modules = extractLynxModules(dtsFiles);
5393
4090
  if (modules.length === 0) {
@@ -5397,28 +4094,28 @@ function codegen() {
5397
4094
  for (const mod of modules) {
5398
4095
  const tsContent = `export type { ${mod} } from '../src/index.js';
5399
4096
  `;
5400
- const outPath = path20.join(generatedDir, `${mod}.ts`);
5401
- fs20.writeFileSync(outPath, tsContent);
4097
+ const outPath = path19.join(generatedDir, `${mod}.ts`);
4098
+ fs18.writeFileSync(outPath, tsContent);
5402
4099
  console.log(`\u2705 Generated ${outPath}`);
5403
4100
  }
5404
4101
  if (config.android) {
5405
- const androidGenerated = path20.join(cwd, "android", "src", "main", "kotlin", config.android.moduleClassName.replace(/\./g, "/").replace(/[^/]+$/, ""), "generated");
5406
- fs20.mkdirSync(androidGenerated, { recursive: true });
4102
+ const androidGenerated = path19.join(cwd, "android", "src", "main", "kotlin", config.android.moduleClassName.replace(/\./g, "/").replace(/[^/]+$/, ""), "generated");
4103
+ fs18.mkdirSync(androidGenerated, { recursive: true });
5407
4104
  console.log(`\u2139\uFE0F Android generated dir: ${androidGenerated} (spec generation coming soon)`);
5408
4105
  }
5409
4106
  if (config.ios) {
5410
- const iosGenerated = path20.join(cwd, "ios", "generated");
5411
- fs20.mkdirSync(iosGenerated, { recursive: true });
4107
+ const iosGenerated = path19.join(cwd, "ios", "generated");
4108
+ fs18.mkdirSync(iosGenerated, { recursive: true });
5412
4109
  console.log(`\u2139\uFE0F iOS generated dir: ${iosGenerated} (spec generation coming soon)`);
5413
4110
  }
5414
4111
  console.log("\u2728 Codegen complete.");
5415
4112
  }
5416
4113
  function findDtsFiles(dir) {
5417
4114
  const result = [];
5418
- if (!fs20.existsSync(dir)) return result;
5419
- const entries = fs20.readdirSync(dir, { withFileTypes: true });
4115
+ if (!fs18.existsSync(dir)) return result;
4116
+ const entries = fs18.readdirSync(dir, { withFileTypes: true });
5420
4117
  for (const e of entries) {
5421
- const full = path20.join(dir, e.name);
4118
+ const full = path19.join(dir, e.name);
5422
4119
  if (e.isDirectory()) result.push(...findDtsFiles(full));
5423
4120
  else if (e.name.endsWith(".d.ts")) result.push(full);
5424
4121
  }
@@ -5428,7 +4125,7 @@ function extractLynxModules(files) {
5428
4125
  const modules = [];
5429
4126
  const seen = /* @__PURE__ */ new Set();
5430
4127
  for (const file of files) {
5431
- const content = fs20.readFileSync(file, "utf8");
4128
+ const content = fs18.readFileSync(file, "utf8");
5432
4129
  const regex = /\/\*\*\s*@lynxmodule\s*\*\/\s*export\s+declare\s+class\s+(\w+)/g;
5433
4130
  let m;
5434
4131
  while ((m = regex.exec(content)) !== null) {
@@ -5444,10 +4141,10 @@ var codegen_default = codegen;
5444
4141
 
5445
4142
  // src/common/devServer.ts
5446
4143
  import { spawn } from "child_process";
5447
- import fs21 from "fs";
4144
+ import fs19 from "fs";
5448
4145
  import http from "http";
5449
4146
  import os3 from "os";
5450
- import path21 from "path";
4147
+ import path20 from "path";
5451
4148
  import { WebSocketServer } from "ws";
5452
4149
  var DEFAULT_PORT = 3e3;
5453
4150
  function getLanIp() {
@@ -5465,13 +4162,13 @@ async function startDevServer(opts) {
5465
4162
  const verbose = opts?.verbose ?? false;
5466
4163
  const resolved = resolveHostPaths();
5467
4164
  const { projectRoot, lynxProjectDir, lynxBundlePath, lynxBundleFile, config } = resolved;
5468
- const distDir = path21.dirname(lynxBundlePath);
4165
+ const distDir = path20.dirname(lynxBundlePath);
5469
4166
  const port = config.devServer?.port ?? config.devServer?.httpPort ?? DEFAULT_PORT;
5470
4167
  let buildProcess = null;
5471
4168
  function detectPackageManager2(cwd) {
5472
- const dir = path21.resolve(cwd);
5473
- if (fs21.existsSync(path21.join(dir, "pnpm-lock.yaml"))) return { cmd: "pnpm", args: ["run", "build"] };
5474
- if (fs21.existsSync(path21.join(dir, "bun.lockb")) || fs21.existsSync(path21.join(dir, "bun.lock"))) return { cmd: "bun", args: ["run", "build"] };
4169
+ const dir = path20.resolve(cwd);
4170
+ if (fs19.existsSync(path20.join(dir, "pnpm-lock.yaml"))) return { cmd: "pnpm", args: ["run", "build"] };
4171
+ if (fs19.existsSync(path20.join(dir, "bun.lockb")) || fs19.existsSync(path20.join(dir, "bun.lock"))) return { cmd: "bun", args: ["run", "build"] };
5475
4172
  return { cmd: "npm", args: ["run", "build"] };
5476
4173
  }
5477
4174
  function runBuild() {
@@ -5493,20 +4190,20 @@ async function startDevServer(opts) {
5493
4190
  });
5494
4191
  });
5495
4192
  }
5496
- const projectName = path21.basename(lynxProjectDir);
4193
+ const projectName = path20.basename(lynxProjectDir);
5497
4194
  const basePath = `/${projectName}`;
5498
4195
  const iconPaths = resolveIconPaths(projectRoot, config);
5499
4196
  let iconFilePath = null;
5500
- if (iconPaths?.source && fs21.statSync(iconPaths.source).isFile()) {
4197
+ if (iconPaths?.source && fs19.statSync(iconPaths.source).isFile()) {
5501
4198
  iconFilePath = iconPaths.source;
5502
4199
  } else if (iconPaths?.android) {
5503
- const androidIcon = path21.join(iconPaths.android, "mipmap-xxxhdpi", "ic_launcher.png");
5504
- if (fs21.existsSync(androidIcon)) iconFilePath = androidIcon;
4200
+ const androidIcon = path20.join(iconPaths.android, "mipmap-xxxhdpi", "ic_launcher.png");
4201
+ if (fs19.existsSync(androidIcon)) iconFilePath = androidIcon;
5505
4202
  } else if (iconPaths?.ios) {
5506
- const iosIcon = path21.join(iconPaths.ios, "Icon-1024.png");
5507
- if (fs21.existsSync(iosIcon)) iconFilePath = iosIcon;
4203
+ const iosIcon = path20.join(iconPaths.ios, "Icon-1024.png");
4204
+ if (fs19.existsSync(iosIcon)) iconFilePath = iosIcon;
5508
4205
  }
5509
- const iconExt = iconFilePath ? path21.extname(iconFilePath) || ".png" : "";
4206
+ const iconExt = iconFilePath ? path20.extname(iconFilePath) || ".png" : "";
5510
4207
  const iconMime = {
5511
4208
  ".png": "image/png",
5512
4209
  ".jpg": "image/jpeg",
@@ -5545,7 +4242,7 @@ async function startDevServer(opts) {
5545
4242
  return;
5546
4243
  }
5547
4244
  if (iconFilePath && (reqPath === `${basePath}/icon` || reqPath === `${basePath}/icon${iconExt}`)) {
5548
- fs21.readFile(iconFilePath, (err, data) => {
4245
+ fs19.readFile(iconFilePath, (err, data) => {
5549
4246
  if (err) {
5550
4247
  res.writeHead(404);
5551
4248
  res.end();
@@ -5563,14 +4260,14 @@ async function startDevServer(opts) {
5563
4260
  reqPath = basePath + (reqPath.startsWith("/") ? reqPath : "/" + reqPath);
5564
4261
  }
5565
4262
  const relPath = reqPath.replace(basePath, "").replace(/^\//, "") || lynxBundleFile;
5566
- const filePath = path21.resolve(distDir, relPath);
5567
- const distResolved = path21.resolve(distDir);
5568
- if (!filePath.startsWith(distResolved + path21.sep) && filePath !== distResolved) {
4263
+ const filePath = path20.resolve(distDir, relPath);
4264
+ const distResolved = path20.resolve(distDir);
4265
+ if (!filePath.startsWith(distResolved + path20.sep) && filePath !== distResolved) {
5569
4266
  res.writeHead(403);
5570
4267
  res.end();
5571
4268
  return;
5572
4269
  }
5573
- fs21.readFile(filePath, (err, data) => {
4270
+ fs19.readFile(filePath, (err, data) => {
5574
4271
  if (err) {
5575
4272
  res.writeHead(404);
5576
4273
  res.end("Not found");
@@ -5624,10 +4321,10 @@ async function startDevServer(opts) {
5624
4321
  }
5625
4322
  if (chokidar) {
5626
4323
  const watchPaths = [
5627
- path21.join(lynxProjectDir, "src"),
5628
- path21.join(lynxProjectDir, "lynx.config.ts"),
5629
- path21.join(lynxProjectDir, "lynx.config.js")
5630
- ].filter((p) => fs21.existsSync(p));
4324
+ path20.join(lynxProjectDir, "src"),
4325
+ path20.join(lynxProjectDir, "lynx.config.ts"),
4326
+ path20.join(lynxProjectDir, "lynx.config.js")
4327
+ ].filter((p) => fs19.existsSync(p));
5631
4328
  if (watchPaths.length > 0) {
5632
4329
  const watcher = chokidar.watch(watchPaths, { ignoreInitial: true });
5633
4330
  watcher.on("change", async () => {
@@ -5706,10 +4403,10 @@ async function start(opts) {
5706
4403
  var start_default = start;
5707
4404
 
5708
4405
  // src/common/injectHost.ts
5709
- import fs22 from "fs";
5710
- import path22 from "path";
4406
+ import fs20 from "fs";
4407
+ import path21 from "path";
5711
4408
  function readAndSubstitute(templatePath, vars) {
5712
- const raw = fs22.readFileSync(templatePath, "utf-8");
4409
+ const raw = fs20.readFileSync(templatePath, "utf-8");
5713
4410
  return Object.entries(vars).reduce(
5714
4411
  (s, [k, v]) => s.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v),
5715
4412
  raw
@@ -5730,32 +4427,32 @@ async function injectHostAndroid(opts) {
5730
4427
  process.exit(1);
5731
4428
  }
5732
4429
  const androidDir = config.paths?.androidDir ?? "android";
5733
- const rootDir = path22.join(projectRoot, androidDir);
4430
+ const rootDir = path21.join(projectRoot, androidDir);
5734
4431
  const packagePath = packageName.replace(/\./g, "/");
5735
- const javaDir = path22.join(rootDir, "app", "src", "main", "java", packagePath);
5736
- const kotlinDir = path22.join(rootDir, "app", "src", "main", "kotlin", packagePath);
5737
- if (!fs22.existsSync(javaDir) || !fs22.existsSync(kotlinDir)) {
4432
+ const javaDir = path21.join(rootDir, "app", "src", "main", "java", packagePath);
4433
+ const kotlinDir = path21.join(rootDir, "app", "src", "main", "kotlin", packagePath);
4434
+ if (!fs20.existsSync(javaDir) || !fs20.existsSync(kotlinDir)) {
5738
4435
  console.error("\u274C Android project not found. Run `t4l android create` first or ensure android/ exists.");
5739
4436
  process.exit(1);
5740
4437
  }
5741
- const templateDir = path22.join(hostPkg, "android", "templates");
4438
+ const templateDir = path21.join(hostPkg, "android", "templates");
5742
4439
  const vars = { PACKAGE_NAME: packageName, APP_NAME: appName };
5743
4440
  const files = [
5744
- { src: "App.java", dst: path22.join(javaDir, "App.java") },
5745
- { src: "TemplateProvider.java", dst: path22.join(javaDir, "TemplateProvider.java") },
5746
- { src: "MainActivity.kt", dst: path22.join(kotlinDir, "MainActivity.kt") }
4441
+ { src: "App.java", dst: path21.join(javaDir, "App.java") },
4442
+ { src: "TemplateProvider.java", dst: path21.join(javaDir, "TemplateProvider.java") },
4443
+ { src: "MainActivity.kt", dst: path21.join(kotlinDir, "MainActivity.kt") }
5747
4444
  ];
5748
4445
  for (const { src, dst } of files) {
5749
- const srcPath = path22.join(templateDir, src);
5750
- if (!fs22.existsSync(srcPath)) continue;
5751
- if (fs22.existsSync(dst) && !opts?.force) {
5752
- console.log(`\u23ED\uFE0F Skipping ${path22.basename(dst)} (use --force to overwrite)`);
4446
+ const srcPath = path21.join(templateDir, src);
4447
+ if (!fs20.existsSync(srcPath)) continue;
4448
+ if (fs20.existsSync(dst) && !opts?.force) {
4449
+ console.log(`\u23ED\uFE0F Skipping ${path21.basename(dst)} (use --force to overwrite)`);
5753
4450
  continue;
5754
4451
  }
5755
4452
  const content = readAndSubstitute(srcPath, vars);
5756
- fs22.mkdirSync(path22.dirname(dst), { recursive: true });
5757
- fs22.writeFileSync(dst, content);
5758
- console.log(`\u2705 Injected ${path22.basename(dst)}`);
4453
+ fs20.mkdirSync(path21.dirname(dst), { recursive: true });
4454
+ fs20.writeFileSync(dst, content);
4455
+ console.log(`\u2705 Injected ${path21.basename(dst)}`);
5759
4456
  }
5760
4457
  }
5761
4458
  async function injectHostIos(opts) {
@@ -5773,13 +4470,13 @@ async function injectHostIos(opts) {
5773
4470
  process.exit(1);
5774
4471
  }
5775
4472
  const iosDir = config.paths?.iosDir ?? "ios";
5776
- const rootDir = path22.join(projectRoot, iosDir);
5777
- const projectDir = path22.join(rootDir, appName);
5778
- if (!fs22.existsSync(projectDir)) {
4473
+ const rootDir = path21.join(projectRoot, iosDir);
4474
+ const projectDir = path21.join(rootDir, appName);
4475
+ if (!fs20.existsSync(projectDir)) {
5779
4476
  console.error("\u274C iOS project not found. Run `t4l ios create` first or ensure ios/ exists.");
5780
4477
  process.exit(1);
5781
4478
  }
5782
- const templateDir = path22.join(hostPkg, "ios", "templates");
4479
+ const templateDir = path21.join(hostPkg, "ios", "templates");
5783
4480
  const vars = { PACKAGE_NAME: bundleId, APP_NAME: appName, BUNDLE_ID: bundleId };
5784
4481
  const files = [
5785
4482
  "AppDelegate.swift",
@@ -5789,23 +4486,23 @@ async function injectHostIos(opts) {
5789
4486
  "LynxInitProcessor.swift"
5790
4487
  ];
5791
4488
  for (const f of files) {
5792
- const srcPath = path22.join(templateDir, f);
5793
- const dstPath = path22.join(projectDir, f);
5794
- if (!fs22.existsSync(srcPath)) continue;
5795
- if (fs22.existsSync(dstPath) && !opts?.force) {
4489
+ const srcPath = path21.join(templateDir, f);
4490
+ const dstPath = path21.join(projectDir, f);
4491
+ if (!fs20.existsSync(srcPath)) continue;
4492
+ if (fs20.existsSync(dstPath) && !opts?.force) {
5796
4493
  console.log(`\u23ED\uFE0F Skipping ${f} (use --force to overwrite)`);
5797
4494
  continue;
5798
4495
  }
5799
4496
  const content = readAndSubstitute(srcPath, vars);
5800
- fs22.writeFileSync(dstPath, content);
4497
+ fs20.writeFileSync(dstPath, content);
5801
4498
  console.log(`\u2705 Injected ${f}`);
5802
4499
  }
5803
4500
  }
5804
4501
 
5805
4502
  // src/common/buildEmbeddable.ts
5806
- import fs23 from "fs";
5807
- import path23 from "path";
5808
- import { execSync as execSync9 } from "child_process";
4503
+ import fs21 from "fs";
4504
+ import path22 from "path";
4505
+ import { execSync as execSync8 } from "child_process";
5809
4506
  var EMBEDDABLE_DIR = "embeddable";
5810
4507
  var LIB_PACKAGE = "com.tamer.embeddable";
5811
4508
  var GRADLE_VERSION = "8.14.2";
@@ -5879,14 +4576,14 @@ object LynxEmbeddable {
5879
4576
  }
5880
4577
  `;
5881
4578
  function generateAndroidLibrary(outDir, androidDir, projectRoot, lynxBundlePath, lynxBundleFile, modules, abiFilters) {
5882
- const libDir = path23.join(androidDir, "lib");
5883
- const libSrcMain = path23.join(libDir, "src", "main");
5884
- const assetsDir = path23.join(libSrcMain, "assets");
5885
- const kotlinDir = path23.join(libSrcMain, "kotlin", LIB_PACKAGE.replace(/\./g, "/"));
5886
- const generatedDir = path23.join(kotlinDir, "generated");
5887
- fs23.mkdirSync(path23.join(androidDir, "gradle"), { recursive: true });
5888
- fs23.mkdirSync(generatedDir, { recursive: true });
5889
- fs23.mkdirSync(assetsDir, { recursive: true });
4579
+ const libDir = path22.join(androidDir, "lib");
4580
+ const libSrcMain = path22.join(libDir, "src", "main");
4581
+ const assetsDir = path22.join(libSrcMain, "assets");
4582
+ const kotlinDir = path22.join(libSrcMain, "kotlin", LIB_PACKAGE.replace(/\./g, "/"));
4583
+ const generatedDir = path22.join(kotlinDir, "generated");
4584
+ fs21.mkdirSync(path22.join(androidDir, "gradle"), { recursive: true });
4585
+ fs21.mkdirSync(generatedDir, { recursive: true });
4586
+ fs21.mkdirSync(assetsDir, { recursive: true });
5890
4587
  const androidModules = modules.filter((m) => m.config.android);
5891
4588
  const abiList = abiFilters.map((a) => `"${a}"`).join(", ");
5892
4589
  const settingsContent = `pluginManagement {
@@ -5906,7 +4603,7 @@ include(":lib")
5906
4603
  ${androidModules.map((p) => {
5907
4604
  const gradleName = p.name.replace(/^@/, "").replace(/\//g, "_");
5908
4605
  const sourceDir = p.config.android?.sourceDir || "android";
5909
- const absPath = path23.join(p.packagePath, sourceDir).replace(/\\/g, "/");
4606
+ const absPath = path22.join(p.packagePath, sourceDir).replace(/\\/g, "/");
5910
4607
  return `include(":${gradleName}")
5911
4608
  project(":${gradleName}").projectDir = file("${absPath}")`;
5912
4609
  }).join("\n")}
@@ -5953,10 +4650,10 @@ dependencies {
5953
4650
  ${libDeps}
5954
4651
  }
5955
4652
  `;
5956
- fs23.writeFileSync(path23.join(androidDir, "gradle", "libs.versions.toml"), LIBS_VERSIONS_TOML);
5957
- fs23.writeFileSync(path23.join(androidDir, "settings.gradle.kts"), settingsContent);
5958
- fs23.writeFileSync(
5959
- path23.join(androidDir, "build.gradle.kts"),
4653
+ fs21.writeFileSync(path22.join(androidDir, "gradle", "libs.versions.toml"), LIBS_VERSIONS_TOML);
4654
+ fs21.writeFileSync(path22.join(androidDir, "settings.gradle.kts"), settingsContent);
4655
+ fs21.writeFileSync(
4656
+ path22.join(androidDir, "build.gradle.kts"),
5960
4657
  `plugins {
5961
4658
  alias(libs.plugins.android.library) apply false
5962
4659
  alias(libs.plugins.kotlin.android) apply false
@@ -5964,26 +4661,26 @@ ${libDeps}
5964
4661
  }
5965
4662
  `
5966
4663
  );
5967
- fs23.writeFileSync(
5968
- path23.join(androidDir, "gradle.properties"),
4664
+ fs21.writeFileSync(
4665
+ path22.join(androidDir, "gradle.properties"),
5969
4666
  `org.gradle.jvmargs=-Xmx2048m
5970
4667
  android.useAndroidX=true
5971
4668
  kotlin.code.style=official
5972
4669
  `
5973
4670
  );
5974
- fs23.writeFileSync(path23.join(libDir, "build.gradle.kts"), libBuildContent);
5975
- fs23.writeFileSync(
5976
- path23.join(libSrcMain, "AndroidManifest.xml"),
4671
+ fs21.writeFileSync(path22.join(libDir, "build.gradle.kts"), libBuildContent);
4672
+ fs21.writeFileSync(
4673
+ path22.join(libSrcMain, "AndroidManifest.xml"),
5977
4674
  '<?xml version="1.0" encoding="utf-8"?>\n<manifest />'
5978
4675
  );
5979
- fs23.copyFileSync(lynxBundlePath, path23.join(assetsDir, lynxBundleFile));
5980
- fs23.writeFileSync(path23.join(kotlinDir, "LynxEmbeddable.kt"), LYNX_EMBEDDABLE_KT);
5981
- fs23.writeFileSync(
5982
- path23.join(generatedDir, "GeneratedLynxExtensions.kt"),
4676
+ fs21.copyFileSync(lynxBundlePath, path22.join(assetsDir, lynxBundleFile));
4677
+ fs21.writeFileSync(path22.join(kotlinDir, "LynxEmbeddable.kt"), LYNX_EMBEDDABLE_KT);
4678
+ fs21.writeFileSync(
4679
+ path22.join(generatedDir, "GeneratedLynxExtensions.kt"),
5983
4680
  generateLynxExtensionsKotlin(modules, LIB_PACKAGE)
5984
4681
  );
5985
- fs23.writeFileSync(
5986
- path23.join(generatedDir, "GeneratedActivityLifecycle.kt"),
4682
+ fs21.writeFileSync(
4683
+ path22.join(generatedDir, "GeneratedActivityLifecycle.kt"),
5987
4684
  generateActivityLifecycleKotlin(modules, LIB_PACKAGE)
5988
4685
  );
5989
4686
  }
@@ -5991,21 +4688,21 @@ async function buildEmbeddable(opts = {}) {
5991
4688
  const resolved = resolveHostPaths();
5992
4689
  const { lynxProjectDir, lynxBundlePath, lynxBundleFile, projectRoot, config } = resolved;
5993
4690
  console.log("\u{1F4E6} Building Lynx project (release)...");
5994
- execSync9("npm run build", { stdio: "inherit", cwd: lynxProjectDir });
5995
- if (!fs23.existsSync(lynxBundlePath)) {
4691
+ execSync8("npm run build", { stdio: "inherit", cwd: lynxProjectDir });
4692
+ if (!fs21.existsSync(lynxBundlePath)) {
5996
4693
  console.error(`\u274C Bundle not found at ${lynxBundlePath}`);
5997
4694
  process.exit(1);
5998
4695
  }
5999
- const outDir = path23.join(projectRoot, EMBEDDABLE_DIR);
6000
- fs23.mkdirSync(outDir, { recursive: true });
6001
- const distDir = path23.dirname(lynxBundlePath);
4696
+ const outDir = path22.join(projectRoot, EMBEDDABLE_DIR);
4697
+ fs21.mkdirSync(outDir, { recursive: true });
4698
+ const distDir = path22.dirname(lynxBundlePath);
6002
4699
  copyDistAssets(distDir, outDir, lynxBundleFile);
6003
4700
  const modules = discoverModules(projectRoot);
6004
4701
  const androidModules = modules.filter((m) => m.config.android);
6005
4702
  const abiFilters = resolveAbiFilters(config);
6006
- const androidDir = path23.join(outDir, "android");
6007
- if (fs23.existsSync(androidDir)) fs23.rmSync(androidDir, { recursive: true });
6008
- fs23.mkdirSync(androidDir, { recursive: true });
4703
+ const androidDir = path22.join(outDir, "android");
4704
+ if (fs21.existsSync(androidDir)) fs21.rmSync(androidDir, { recursive: true });
4705
+ fs21.mkdirSync(androidDir, { recursive: true });
6009
4706
  generateAndroidLibrary(
6010
4707
  outDir,
6011
4708
  androidDir,
@@ -6015,23 +4712,23 @@ async function buildEmbeddable(opts = {}) {
6015
4712
  modules,
6016
4713
  abiFilters
6017
4714
  );
6018
- const gradlewPath = path23.join(androidDir, "gradlew");
4715
+ const gradlewPath = path22.join(androidDir, "gradlew");
6019
4716
  const devAppDir = findDevAppPackage(projectRoot);
6020
4717
  const existingGradleDirs = [
6021
- path23.join(projectRoot, "android"),
6022
- devAppDir ? path23.join(devAppDir, "android") : null
4718
+ path22.join(projectRoot, "android"),
4719
+ devAppDir ? path22.join(devAppDir, "android") : null
6023
4720
  ].filter(Boolean);
6024
4721
  let hasWrapper = false;
6025
4722
  for (const d of existingGradleDirs) {
6026
- if (fs23.existsSync(path23.join(d, "gradlew"))) {
4723
+ if (fs21.existsSync(path22.join(d, "gradlew"))) {
6027
4724
  for (const name of ["gradlew", "gradlew.bat", "gradle"]) {
6028
- const src = path23.join(d, name);
6029
- if (fs23.existsSync(src)) {
6030
- const dest = path23.join(androidDir, name);
6031
- if (fs23.statSync(src).isDirectory()) {
6032
- fs23.cpSync(src, dest, { recursive: true });
4725
+ const src = path22.join(d, name);
4726
+ if (fs21.existsSync(src)) {
4727
+ const dest = path22.join(androidDir, name);
4728
+ if (fs21.statSync(src).isDirectory()) {
4729
+ fs21.cpSync(src, dest, { recursive: true });
6033
4730
  } else {
6034
- fs23.copyFileSync(src, dest);
4731
+ fs21.copyFileSync(src, dest);
6035
4732
  }
6036
4733
  }
6037
4734
  }
@@ -6045,15 +4742,15 @@ async function buildEmbeddable(opts = {}) {
6045
4742
  }
6046
4743
  try {
6047
4744
  console.log("\u{1F4E6} Building Android AAR...");
6048
- execSync9("./gradlew :lib:assembleRelease", { cwd: androidDir, stdio: "inherit" });
4745
+ execSync8("./gradlew :lib:assembleRelease", { cwd: androidDir, stdio: "inherit" });
6049
4746
  } catch (e) {
6050
4747
  console.error("\u274C Android AAR build failed. Run manually: cd embeddable/android && ./gradlew :lib:assembleRelease");
6051
4748
  throw e;
6052
4749
  }
6053
- const aarSrc = path23.join(androidDir, "lib", "build", "outputs", "aar", "lib-release.aar");
6054
- const aarDest = path23.join(outDir, "tamer-embeddable.aar");
6055
- if (fs23.existsSync(aarSrc)) {
6056
- fs23.copyFileSync(aarSrc, aarDest);
4750
+ const aarSrc = path22.join(androidDir, "lib", "build", "outputs", "aar", "lib-release.aar");
4751
+ const aarDest = path22.join(outDir, "tamer-embeddable.aar");
4752
+ if (fs21.existsSync(aarSrc)) {
4753
+ fs21.copyFileSync(aarSrc, aarDest);
6057
4754
  console.log(` - tamer-embeddable.aar`);
6058
4755
  }
6059
4756
  const snippetAndroid = `// Add to your app's build.gradle:
@@ -6064,7 +4761,7 @@ async function buildEmbeddable(opts = {}) {
6064
4761
  // LynxEmbeddable.init(applicationContext)
6065
4762
  // val lynxView = LynxEmbeddable.buildLynxView(containerViewGroup)
6066
4763
  `;
6067
- fs23.writeFileSync(path23.join(outDir, "snippet-android.kt"), snippetAndroid);
4764
+ fs21.writeFileSync(path22.join(outDir, "snippet-android.kt"), snippetAndroid);
6068
4765
  generateIosPod(outDir, projectRoot, lynxBundlePath, lynxBundleFile, modules);
6069
4766
  const readme = `# Embeddable Lynx Bundle
6070
4767
 
@@ -6095,7 +4792,7 @@ Add the \`Podfile.snippet\` entries to your Podfile (inside your app target), th
6095
4792
 
6096
4793
  - [Embedding LynxView](https://lynxjs.org/guide/embed-lynx-to-native)
6097
4794
  `;
6098
- fs23.writeFileSync(path23.join(outDir, "README.md"), readme);
4795
+ fs21.writeFileSync(path22.join(outDir, "README.md"), readme);
6099
4796
  console.log(`
6100
4797
  \u2705 Embeddable output at ${outDir}/`);
6101
4798
  console.log(" - main.lynx.bundle");
@@ -6107,20 +4804,20 @@ Add the \`Podfile.snippet\` entries to your Podfile (inside your app target), th
6107
4804
  console.log(" - README.md");
6108
4805
  }
6109
4806
  function generateIosPod(outDir, projectRoot, lynxBundlePath, lynxBundleFile, modules) {
6110
- const iosDir = path23.join(outDir, "ios");
6111
- const podDir = path23.join(iosDir, "TamerEmbeddable");
6112
- const resourcesDir = path23.join(podDir, "Resources");
6113
- fs23.mkdirSync(resourcesDir, { recursive: true });
6114
- fs23.copyFileSync(lynxBundlePath, path23.join(resourcesDir, lynxBundleFile));
4807
+ const iosDir = path22.join(outDir, "ios");
4808
+ const podDir = path22.join(iosDir, "TamerEmbeddable");
4809
+ const resourcesDir = path22.join(podDir, "Resources");
4810
+ fs21.mkdirSync(resourcesDir, { recursive: true });
4811
+ fs21.copyFileSync(lynxBundlePath, path22.join(resourcesDir, lynxBundleFile));
6115
4812
  const iosModules = modules.filter((m) => m.config.ios);
6116
4813
  const podDeps = iosModules.map((p) => {
6117
4814
  const podspecPath = p.config.ios?.podspecPath || ".";
6118
- const podspecDir = path23.join(p.packagePath, podspecPath);
6119
- if (!fs23.existsSync(podspecDir)) return null;
6120
- const files = fs23.readdirSync(podspecDir);
4815
+ const podspecDir = path22.join(p.packagePath, podspecPath);
4816
+ if (!fs21.existsSync(podspecDir)) return null;
4817
+ const files = fs21.readdirSync(podspecDir);
6121
4818
  const podspecFile = files.find((f) => f.endsWith(".podspec"));
6122
4819
  const podName = podspecFile ? podspecFile.replace(".podspec", "") : p.name.split("/").pop().replace(/-/g, "");
6123
- const absPath = path23.resolve(podspecDir);
4820
+ const absPath = path22.resolve(podspecDir);
6124
4821
  return { podName, absPath };
6125
4822
  }).filter(Boolean);
6126
4823
  const podDepLines = podDeps.map((d) => ` s.dependency '${d.podName}'`).join("\n");
@@ -6160,9 +4857,9 @@ end
6160
4857
  });
6161
4858
  const swiftImports = iosModules.map((p) => {
6162
4859
  const podspecPath = p.config.ios?.podspecPath || ".";
6163
- const podspecDir = path23.join(p.packagePath, podspecPath);
6164
- if (!fs23.existsSync(podspecDir)) return null;
6165
- const files = fs23.readdirSync(podspecDir);
4860
+ const podspecDir = path22.join(p.packagePath, podspecPath);
4861
+ if (!fs21.existsSync(podspecDir)) return null;
4862
+ const files = fs21.readdirSync(podspecDir);
6166
4863
  const podspecFile = files.find((f) => f.endsWith(".podspec"));
6167
4864
  return podspecFile ? podspecFile.replace(".podspec", "") : null;
6168
4865
  }).filter(Boolean);
@@ -6181,17 +4878,17 @@ ${regBlock}
6181
4878
  }
6182
4879
  }
6183
4880
  `;
6184
- fs23.writeFileSync(path23.join(iosDir, "TamerEmbeddable.podspec"), podspecContent);
6185
- fs23.writeFileSync(path23.join(podDir, "LynxEmbeddable.swift"), lynxEmbeddableSwift);
6186
- const absIosDir = path23.resolve(iosDir);
4881
+ fs21.writeFileSync(path22.join(iosDir, "TamerEmbeddable.podspec"), podspecContent);
4882
+ fs21.writeFileSync(path22.join(podDir, "LynxEmbeddable.swift"), lynxEmbeddableSwift);
4883
+ const absIosDir = path22.resolve(iosDir);
6187
4884
  const podfileSnippet = `# Paste into your app target in Podfile:
6188
4885
 
6189
4886
  pod 'TamerEmbeddable', :path => '${absIosDir}'
6190
4887
  ${podDeps.map((d) => `pod '${d.podName}', :path => '${d.absPath}'`).join("\n")}
6191
4888
  `;
6192
- fs23.writeFileSync(path23.join(iosDir, "Podfile.snippet"), podfileSnippet);
6193
- fs23.writeFileSync(
6194
- path23.join(outDir, "snippet-ios.swift"),
4889
+ fs21.writeFileSync(path22.join(iosDir, "Podfile.snippet"), podfileSnippet);
4890
+ fs21.writeFileSync(
4891
+ path22.join(outDir, "snippet-ios.swift"),
6195
4892
  `// Add LynxEmbeddable.initEnvironment() in your AppDelegate/SceneDelegate before presenting LynxView.
6196
4893
  // Then create LynxView with your bundle URL (main.lynx.bundle is in the pod resources).
6197
4894
  `
@@ -6199,9 +4896,9 @@ ${podDeps.map((d) => `pod '${d.podName}', :path => '${d.absPath}'`).join("\n")}
6199
4896
  }
6200
4897
 
6201
4898
  // src/common/add.ts
6202
- import fs24 from "fs";
6203
- import path24 from "path";
6204
- import { execSync as execSync10 } from "child_process";
4899
+ import fs22 from "fs";
4900
+ import path23 from "path";
4901
+ import { execSync as execSync9 } from "child_process";
6205
4902
  var CORE_PACKAGES = [
6206
4903
  "@tamer4lynx/tamer-app-shell",
6207
4904
  "@tamer4lynx/tamer-screen",
@@ -6213,15 +4910,15 @@ var CORE_PACKAGES = [
6213
4910
  "@tamer4lynx/tamer-icons"
6214
4911
  ];
6215
4912
  function detectPackageManager(cwd) {
6216
- const dir = path24.resolve(cwd);
6217
- if (fs24.existsSync(path24.join(dir, "pnpm-lock.yaml"))) return "pnpm";
6218
- if (fs24.existsSync(path24.join(dir, "bun.lockb"))) return "bun";
4913
+ const dir = path23.resolve(cwd);
4914
+ if (fs22.existsSync(path23.join(dir, "pnpm-lock.yaml"))) return "pnpm";
4915
+ if (fs22.existsSync(path23.join(dir, "bun.lockb"))) return "bun";
6219
4916
  return "npm";
6220
4917
  }
6221
4918
  function runInstall(cwd, packages, pm) {
6222
4919
  const args = pm === "npm" ? ["install", ...packages] : ["add", ...packages];
6223
4920
  const cmd = pm === "npm" ? "npm" : pm === "pnpm" ? "pnpm" : "bun";
6224
- execSync10(`${cmd} ${args.join(" ")}`, { stdio: "inherit", cwd });
4921
+ execSync9(`${cmd} ${args.join(" ")}`, { stdio: "inherit", cwd });
6225
4922
  }
6226
4923
  function addCore() {
6227
4924
  const { lynxProjectDir } = resolveHostPaths();
@@ -6267,12 +4964,12 @@ android.command("create").option("-t, --target <target>", "Create target: host (
6267
4964
  android.command("link").description("Link native modules to the Android project").action(() => {
6268
4965
  autolink_default();
6269
4966
  });
6270
- android.command("bundle").option("-t, --target <target>", "Bundle target: host (default) or dev-app", "host").option("-d, --debug", "Build debug (development) bundle").option("-r, --release", "Build release (production) bundle").description("Build Lynx bundle and copy to Android assets (runs autolink first)").action(async (opts) => {
4967
+ android.command("bundle").option("-d, --debug", "Debug bundle with dev client embedded (default)").option("-r, --release", "Release bundle without dev client").description("Build Lynx bundle and copy to Android assets (runs autolink first)").action(async (opts) => {
6271
4968
  validateDebugRelease(opts.debug, opts.release);
6272
4969
  const release = opts.release === true;
6273
- await bundle_default({ target: opts.target, release });
4970
+ await bundle_default({ release });
6274
4971
  });
6275
- var androidBuildCmd = android.command("build").option("-i, --install", "Install APK to connected device and launch app after building").option("-t, --target <target>", "Build target: host (default) or dev-app", "host").option("-e, --embeddable", "Build for embedding in existing app (host only). Use with --release for production-ready embeddable.").option("-d, --debug", "Build debug (development) APK").option("-r, --release", "Build release (production) APK").description("Build APK (autolink + bundle + gradle)").action(async () => {
4972
+ var androidBuildCmd = android.command("build").option("-i, --install", "Install APK to connected device and launch app after building").option("-e, --embeddable", "Build for embedding in existing app (host only). Use with --release for production-ready embeddable.").option("-d, --debug", "Debug APK with dev client embedded (default)").option("-r, --release", "Release APK without dev client").description("Build APK (autolink + bundle + gradle)").action(async () => {
6276
4973
  const opts = androidBuildCmd.opts();
6277
4974
  validateDebugRelease(opts.debug, opts.release);
6278
4975
  const release = opts.release === true;
@@ -6280,7 +4977,7 @@ var androidBuildCmd = android.command("build").option("-i, --install", "Install
6280
4977
  await buildEmbeddable({ release: true });
6281
4978
  return;
6282
4979
  }
6283
- await build_default({ install: opts.install, target: opts.target, release });
4980
+ await build_default({ install: opts.install, release });
6284
4981
  });
6285
4982
  android.command("sync").description("Sync dev client files (TemplateProvider, MainActivity, DevClientManager) from tamer.config.json").action(async () => {
6286
4983
  await syncDevClient_default();
@@ -6298,12 +4995,12 @@ ios.command("inject").option("-f, --force", "Overwrite existing files").descript
6298
4995
  ios.command("link").description("Link native modules to the iOS project").action(() => {
6299
4996
  autolink_default2();
6300
4997
  });
6301
- ios.command("bundle").option("-t, --target <target>", "Bundle target: host (default) or dev-app", "host").option("-d, --debug", "Build debug (development) bundle").option("-r, --release", "Build release (production) bundle").description("Build Lynx bundle and copy to iOS project (runs autolink first)").action((opts) => {
4998
+ ios.command("bundle").option("-d, --debug", "Debug bundle with dev client embedded (default)").option("-r, --release", "Release bundle without dev client").description("Build Lynx bundle and copy to iOS project (runs autolink first)").action((opts) => {
6302
4999
  validateDebugRelease(opts.debug, opts.release);
6303
5000
  const release = opts.release === true;
6304
- bundle_default2({ target: opts.target, release });
5001
+ bundle_default2({ release });
6305
5002
  });
6306
- var iosBuildCmd = ios.command("build").option("-t, --target <target>", "Build target: host (default) or dev-app", "host").option("-e, --embeddable", "Output bundle + code snippets to embeddable/ for adding LynxView to an existing app. Use with --release.").option("-i, --install", "Install and launch on booted simulator after building").option("-d, --debug", "Build debug (development) configuration").option("-r, --release", "Build release (production) configuration").description("Build iOS app (autolink + bundle + xcodebuild)").action(async () => {
5003
+ var iosBuildCmd = ios.command("build").option("-e, --embeddable", "Output bundle + code snippets to embeddable/ for adding LynxView to an existing app. Use with --release.").option("-i, --install", "Install and launch on booted simulator after building").option("-d, --debug", "Debug build with dev client embedded (default)").option("-r, --release", "Release build without dev client").description("Build iOS app (autolink + bundle + xcodebuild)").action(async () => {
6307
5004
  const opts = iosBuildCmd.opts();
6308
5005
  validateDebugRelease(opts.debug, opts.release);
6309
5006
  const release = opts.release === true;
@@ -6311,7 +5008,7 @@ var iosBuildCmd = ios.command("build").option("-t, --target <target>", "Build ta
6311
5008
  await buildEmbeddable({ release: true });
6312
5009
  return;
6313
5010
  }
6314
- await build_default2({ target: opts.target, install: opts.install, release });
5011
+ await build_default2({ install: opts.install, release });
6315
5012
  });
6316
5013
  var linkCmd = program.command("link").option("-i, --ios", "Link iOS native modules").option("-a, --android", "Link Android native modules").option("-b, --both", "Link both iOS and Android native modules").option("-s, --silent", "Run in silent mode without outputting messages").description("Link native modules to the project").action(() => {
6317
5014
  const opts = linkCmd.opts();
@@ -6337,7 +5034,7 @@ var linkCmd = program.command("link").option("-i, --ios", "Link iOS native modul
6337
5034
  program.command("start").option("-v, --verbose", "Show all logs (native + JS); default shows JS only").description("Start dev server with HMR and WebSocket support (Expo-like)").action(async (opts) => {
6338
5035
  await start_default({ verbose: opts.verbose });
6339
5036
  });
6340
- var buildCmd = program.command("build").option("-p, --platform <platform>", "android, ios, or all (default: all)", "all").option("-t, --target <target>", "host or dev-app (default: dev-app)", "dev-app").option("-e, --embeddable", "Output bundle + code snippets to embeddable/ for adding LynxView to an existing app. Use with --release.").option("-d, --debug", "Debug build (default)").option("-r, --release", "Release build").option("-i, --install", "Install after building").description("Build app (unified: delegates to android/ios build)").action(async () => {
5037
+ var buildCmd = program.command("build").option("-p, --platform <platform>", "android, ios, or all (default: all)", "all").option("-e, --embeddable", "Output bundle + code snippets to embeddable/ for adding LynxView to an existing app. Use with --release.").option("-d, --debug", "Debug build with dev client embedded (default)").option("-r, --release", "Release build without dev client").option("-i, --install", "Install after building").description("Build app (unified: delegates to android/ios build)").action(async () => {
6341
5038
  const opts = buildCmd.opts();
6342
5039
  validateDebugRelease(opts.debug, opts.release);
6343
5040
  const release = opts.release === true;
@@ -6347,23 +5044,22 @@ var buildCmd = program.command("build").option("-p, --platform <platform>", "and
6347
5044
  }
6348
5045
  const p = opts.platform?.toLowerCase();
6349
5046
  const platform = p === "ios" || p === "android" ? p : "all";
6350
- const target = opts.target ?? "dev-app";
6351
5047
  if (platform === "android" || platform === "all") {
6352
- await build_default({ install: opts.install, target, release });
5048
+ await build_default({ install: opts.install, release });
6353
5049
  }
6354
5050
  if (platform === "ios" || platform === "all") {
6355
- await build_default2({ target, install: opts.install, release });
5051
+ await build_default2({ install: opts.install, release });
6356
5052
  }
6357
5053
  });
6358
- program.command("build-dev-app").option("-p, --platform <platform>", "Platform: android, ios, or all (default)", "all").option("-i, --install", "Install APK to connected device and launch app after building").description("(Deprecated) Use: t4l build --platform <platform> --install").action(async (opts) => {
6359
- console.warn("\u26A0\uFE0F build-dev-app is deprecated. Use: t4l build --platform <platform> [--install]");
5054
+ program.command("build-dev-app").option("-p, --platform <platform>", "Platform: android, ios, or all (default)", "all").option("-i, --install", "Install APK to connected device and launch app after building").description("(Deprecated) Use: t4l build -p android -d --install").action(async (opts) => {
5055
+ console.warn("\u26A0\uFE0F build-dev-app is deprecated. Use: t4l build -p android -d [--install]");
6360
5056
  const p = opts.platform?.toLowerCase();
6361
5057
  const platform = p === "ios" || p === "android" ? p : "all";
6362
5058
  if (platform === "android" || platform === "all") {
6363
- await build_default({ install: opts.install, target: "dev-app", release: false });
5059
+ await build_default({ install: opts.install, release: false });
6364
5060
  }
6365
5061
  if (platform === "ios" || platform === "all") {
6366
- await build_default2({ target: "dev-app", install: opts.install, release: false });
5062
+ await build_default2({ install: opts.install, release: false });
6367
5063
  }
6368
5064
  });
6369
5065
  program.command("add [packages...]").description("Add @tamer4lynx packages to the Lynx project. Future: will track versions for compatibility (Expo-style).").action((packages) => add(packages));
@@ -6373,10 +5069,10 @@ program.command("codegen").description("Generate code from @lynxmodule declarati
6373
5069
  codegen_default();
6374
5070
  });
6375
5071
  program.command("autolink-toggle").alias("autolink").description("Toggle autolink on/off in tamer.config.json (controls postinstall linking)").action(async () => {
6376
- const configPath = path25.join(process.cwd(), "tamer.config.json");
5072
+ const configPath = path24.join(process.cwd(), "tamer.config.json");
6377
5073
  let config = {};
6378
- if (fs25.existsSync(configPath)) {
6379
- config = JSON.parse(fs25.readFileSync(configPath, "utf8"));
5074
+ if (fs23.existsSync(configPath)) {
5075
+ config = JSON.parse(fs23.readFileSync(configPath, "utf8"));
6380
5076
  }
6381
5077
  if (config.autolink) {
6382
5078
  delete config.autolink;
@@ -6385,7 +5081,7 @@ program.command("autolink-toggle").alias("autolink").description("Toggle autolin
6385
5081
  config.autolink = true;
6386
5082
  console.log("Autolink enabled in tamer.config.json");
6387
5083
  }
6388
- fs25.writeFileSync(configPath, JSON.stringify(config, null, 2));
5084
+ fs23.writeFileSync(configPath, JSON.stringify(config, null, 2));
6389
5085
  console.log(`Updated ${configPath}`);
6390
5086
  });
6391
5087
  if (process.argv.length <= 2 || process.argv.length === 3 && process.argv[2] === "init") {