@tamer4lynx/cli 0.0.18 → 0.0.19

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 (2) hide show
  1. package/dist/index.js +571 -497
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -7,8 +7,8 @@ process.on("warning", (w) => {
7
7
  });
8
8
 
9
9
  // index.ts
10
- import fs30 from "fs";
11
- import path31 from "path";
10
+ import fs31 from "fs";
11
+ import path32 from "path";
12
12
  import { program } from "commander";
13
13
 
14
14
  // src/common/cliVersion.ts
@@ -1581,8 +1581,8 @@ object GeneratedLynxExtensions {
1581
1581
  var create_default = create;
1582
1582
 
1583
1583
  // src/android/autolink.ts
1584
- import fs8 from "fs";
1585
- import path8 from "path";
1584
+ import fs9 from "fs";
1585
+ import path9 from "path";
1586
1586
  import { execSync as execSync2 } from "child_process";
1587
1587
 
1588
1588
  // src/common/discoverModules.ts
@@ -1974,6 +1974,69 @@ ${createDelayedBody}
1974
1974
  `;
1975
1975
  }
1976
1976
 
1977
+ // src/android/cleanTamerAndroidCaches.ts
1978
+ import fs8 from "fs";
1979
+ import path8 from "path";
1980
+ var MARKER_REL = path8.join(".tamer", "android-lib-versions.json");
1981
+ function readTamerPackageVersions(projectRoot) {
1982
+ const out = {};
1983
+ const nm = path8.join(projectRoot, "node_modules", "@tamer4lynx");
1984
+ if (!fs8.existsSync(nm)) return out;
1985
+ for (const name of fs8.readdirSync(nm, { withFileTypes: true })) {
1986
+ if (!name.isDirectory()) continue;
1987
+ const pj = path8.join(nm, name.name, "package.json");
1988
+ if (!fs8.existsSync(pj)) continue;
1989
+ try {
1990
+ const v = JSON.parse(fs8.readFileSync(pj, "utf-8")).version;
1991
+ if (typeof v === "string") out[name.name] = v;
1992
+ } catch {
1993
+ }
1994
+ }
1995
+ return out;
1996
+ }
1997
+ function versionsEqual(a, b) {
1998
+ const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
1999
+ for (const k of keys) {
2000
+ if (a[k] !== b[k]) return false;
2001
+ }
2002
+ return true;
2003
+ }
2004
+ function cleanTamerAndroidLibBuildsIfVersionsChanged(projectRoot) {
2005
+ const markerPath = path8.join(projectRoot, MARKER_REL);
2006
+ let previous = {};
2007
+ if (fs8.existsSync(markerPath)) {
2008
+ try {
2009
+ previous = JSON.parse(fs8.readFileSync(markerPath, "utf-8"));
2010
+ } catch {
2011
+ previous = {};
2012
+ }
2013
+ }
2014
+ const current = readTamerPackageVersions(projectRoot);
2015
+ if (versionsEqual(previous, current)) return;
2016
+ const nm = path8.join(projectRoot, "node_modules", "@tamer4lynx");
2017
+ if (fs8.existsSync(nm)) {
2018
+ for (const name of fs8.readdirSync(nm, { withFileTypes: true })) {
2019
+ if (!name.isDirectory()) continue;
2020
+ const buildDir = path8.join(nm, name.name, "android", "build");
2021
+ if (fs8.existsSync(buildDir)) {
2022
+ fs8.rmSync(buildDir, { recursive: true, force: true });
2023
+ }
2024
+ }
2025
+ }
2026
+ fs8.mkdirSync(path8.dirname(markerPath), { recursive: true });
2027
+ fs8.writeFileSync(markerPath, JSON.stringify(current, null, 0), "utf-8");
2028
+ console.log(
2029
+ "\u2139\uFE0F Cleared @tamer4lynx Android library build dirs (linked package versions changed)."
2030
+ );
2031
+ }
2032
+ function invalidateTamerAndroidLibVersionMarker(projectRoot) {
2033
+ const markerPath = path8.join(projectRoot, MARKER_REL);
2034
+ try {
2035
+ if (fs8.existsSync(markerPath)) fs8.unlinkSync(markerPath);
2036
+ } catch {
2037
+ }
2038
+ }
2039
+
1977
2040
  // src/android/autolink.ts
1978
2041
  var TAMER_DEV_CLIENT_FALLBACK = {
1979
2042
  name: "@tamer4lynx/tamer-dev-client",
@@ -2019,11 +2082,11 @@ var autolink = (opts) => {
2019
2082
  const packageName = config.android.packageName;
2020
2083
  const projectRoot = resolved.projectRoot;
2021
2084
  function updateGeneratedSection(filePath, newContent, startMarker, endMarker) {
2022
- if (!fs8.existsSync(filePath)) {
2085
+ if (!fs9.existsSync(filePath)) {
2023
2086
  console.warn(`\u26A0\uFE0F File not found, skipping update: ${filePath}`);
2024
2087
  return;
2025
2088
  }
2026
- let fileContent = fs8.readFileSync(filePath, "utf8");
2089
+ let fileContent = fs9.readFileSync(filePath, "utf8");
2027
2090
  const escapedStartMarker = startMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2028
2091
  const escapedEndMarker = endMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2029
2092
  const regex = new RegExp(`${escapedStartMarker}[\\s\\S]*?${escapedEndMarker}`, "g");
@@ -2033,16 +2096,16 @@ ${endMarker}`;
2033
2096
  if (regex.test(fileContent)) {
2034
2097
  fileContent = fileContent.replace(regex, replacementBlock);
2035
2098
  } else {
2036
- console.warn(`\u26A0\uFE0F Could not find autolink markers in ${path8.basename(filePath)}. Appending to the end of the file.`);
2099
+ console.warn(`\u26A0\uFE0F Could not find autolink markers in ${path9.basename(filePath)}. Appending to the end of the file.`);
2037
2100
  fileContent += `
2038
2101
  ${replacementBlock}
2039
2102
  `;
2040
2103
  }
2041
- fs8.writeFileSync(filePath, fileContent);
2042
- console.log(`\u2705 Updated autolinked section in ${path8.basename(filePath)}`);
2104
+ fs9.writeFileSync(filePath, fileContent);
2105
+ console.log(`\u2705 Updated autolinked section in ${path9.basename(filePath)}`);
2043
2106
  }
2044
2107
  function updateSettingsGradle(packages) {
2045
- const settingsFilePath = path8.join(appAndroidPath, "settings.gradle.kts");
2108
+ const settingsFilePath = path9.join(appAndroidPath, "settings.gradle.kts");
2046
2109
  let scriptContent = `// This section is automatically generated by Tamer4Lynx.
2047
2110
  // Manual edits will be overwritten.`;
2048
2111
  const androidPackages = packages.filter((p) => p.config.android);
@@ -2050,7 +2113,7 @@ ${replacementBlock}
2050
2113
  androidPackages.forEach((pkg) => {
2051
2114
  const gradleProjectName = pkg.name.replace(/^@/, "").replace(/\//g, "_");
2052
2115
  const sourceDir = pkg.config.android?.sourceDir || "android";
2053
- const projectPath = path8.resolve(pkg.packagePath, sourceDir).replace(/\\/g, "/");
2116
+ const projectPath = path9.resolve(pkg.packagePath, sourceDir).replace(/\\/g, "/");
2054
2117
  scriptContent += `
2055
2118
  include(":${gradleProjectName}")`;
2056
2119
  scriptContent += `
@@ -2063,7 +2126,7 @@ println("No native modules found by Tamer4Lynx autolinker.")`;
2063
2126
  updateGeneratedSection(settingsFilePath, scriptContent.trim(), "// GENERATED AUTOLINK START", "// GENERATED AUTOLINK END");
2064
2127
  }
2065
2128
  function updateAppBuildGradle(packages) {
2066
- const appBuildGradlePath = path8.join(appAndroidPath, "app", "build.gradle.kts");
2129
+ const appBuildGradlePath = path9.join(appAndroidPath, "app", "build.gradle.kts");
2067
2130
  const androidPackages = packages.filter((p) => p.config.android);
2068
2131
  const implementationLines = androidPackages.map((p) => {
2069
2132
  const gradleProjectName = p.name.replace(/^@/, "").replace(/\//g, "_");
@@ -2081,35 +2144,35 @@ ${implementationLines || " // No native dependencies found to link."}`;
2081
2144
  }
2082
2145
  function generateKotlinExtensionsFile(packages, projectPackage) {
2083
2146
  const packagePath = projectPackage.replace(/\./g, "/");
2084
- const generatedDir = path8.join(appAndroidPath, "app", "src", "main", "kotlin", packagePath, "generated");
2085
- const kotlinExtensionsPath = path8.join(generatedDir, "GeneratedLynxExtensions.kt");
2147
+ const generatedDir = path9.join(appAndroidPath, "app", "src", "main", "kotlin", packagePath, "generated");
2148
+ const kotlinExtensionsPath = path9.join(generatedDir, "GeneratedLynxExtensions.kt");
2086
2149
  const content = `/**
2087
2150
  * This file is generated by the Tamer4Lynx autolinker.
2088
2151
  * Do not edit this file manually.
2089
2152
  */
2090
2153
  ${generateLynxExtensionsKotlin(packages, projectPackage)}`;
2091
- fs8.mkdirSync(generatedDir, { recursive: true });
2092
- fs8.writeFileSync(kotlinExtensionsPath, content.trimStart());
2154
+ fs9.mkdirSync(generatedDir, { recursive: true });
2155
+ fs9.writeFileSync(kotlinExtensionsPath, content.trimStart());
2093
2156
  console.log(`\u2705 Generated Kotlin extensions at ${kotlinExtensionsPath}`);
2094
2157
  }
2095
2158
  function generateActivityLifecycleFile(packages, projectPackage) {
2096
2159
  const packageKotlinPath = projectPackage.replace(/\./g, "/");
2097
- const generatedDir = path8.join(appAndroidPath, "app", "src", "main", "kotlin", packageKotlinPath, "generated");
2098
- const outputPath = path8.join(generatedDir, "GeneratedActivityLifecycle.kt");
2160
+ const generatedDir = path9.join(appAndroidPath, "app", "src", "main", "kotlin", packageKotlinPath, "generated");
2161
+ const outputPath = path9.join(generatedDir, "GeneratedActivityLifecycle.kt");
2099
2162
  const content = `/**
2100
2163
  * This file is generated by the Tamer4Lynx autolinker.
2101
2164
  * Do not edit this file manually.
2102
2165
  */
2103
2166
  ${generateActivityLifecycleKotlin(packages, projectPackage)}`;
2104
- fs8.mkdirSync(generatedDir, { recursive: true });
2105
- fs8.writeFileSync(outputPath, content);
2167
+ fs9.mkdirSync(generatedDir, { recursive: true });
2168
+ fs9.writeFileSync(outputPath, content);
2106
2169
  console.log(`\u2705 Generated activity lifecycle patches at ${outputPath}`);
2107
2170
  }
2108
2171
  function syncDeepLinkIntentFilters() {
2109
2172
  const deepLinks = config.android?.deepLinks;
2110
2173
  if (!deepLinks || deepLinks.length === 0) return;
2111
- const manifestPath = path8.join(appAndroidPath, "app", "src", "main", "AndroidManifest.xml");
2112
- if (!fs8.existsSync(manifestPath)) return;
2174
+ const manifestPath = path9.join(appAndroidPath, "app", "src", "main", "AndroidManifest.xml");
2175
+ if (!fs9.existsSync(manifestPath)) return;
2113
2176
  const intentFilters = deepLinks.map((link) => {
2114
2177
  const dataAttrs = [
2115
2178
  `android:scheme="${link.scheme}"`,
@@ -2134,9 +2197,9 @@ ${generateActivityLifecycleKotlin(packages, projectPackage)}`;
2134
2197
  console.log("\u{1F50E} Finding Lynx extension packages (lynx.ext.json / tamer.json)...");
2135
2198
  let packages = discoverModules(projectRoot).filter((p) => p.config.android);
2136
2199
  const includeDevClient = opts?.includeDevClient === true;
2137
- const devClientScoped = path8.join(projectRoot, "node_modules", "@tamer4lynx", "tamer-dev-client");
2138
- const devClientFlat = path8.join(projectRoot, "node_modules", "tamer-dev-client");
2139
- const devClientPath = fs8.existsSync(path8.join(devClientScoped, "android")) ? devClientScoped : fs8.existsSync(path8.join(devClientFlat, "android")) ? devClientFlat : null;
2200
+ const devClientScoped = path9.join(projectRoot, "node_modules", "@tamer4lynx", "tamer-dev-client");
2201
+ const devClientFlat = path9.join(projectRoot, "node_modules", "tamer-dev-client");
2202
+ const devClientPath = fs9.existsSync(path9.join(devClientScoped, "android")) ? devClientScoped : fs9.existsSync(path9.join(devClientFlat, "android")) ? devClientFlat : null;
2140
2203
  const hasDevClient = packages.some((p) => p.name === "@tamer4lynx/tamer-dev-client" || p.name === "tamer-dev-client");
2141
2204
  if (includeDevClient && devClientPath && !hasDevClient) {
2142
2205
  packages = [{
@@ -2160,11 +2223,12 @@ ${generateActivityLifecycleKotlin(packages, projectPackage)}`;
2160
2223
  ensureXElementDeps();
2161
2224
  ensureReleaseSigning();
2162
2225
  runGradleSync();
2226
+ invalidateTamerAndroidLibVersionMarker(projectRoot);
2163
2227
  console.log("\u2728 Autolinking complete.");
2164
2228
  }
2165
2229
  function runGradleSync() {
2166
- const gradlew = path8.join(appAndroidPath, process.platform === "win32" ? "gradlew.bat" : "gradlew");
2167
- if (!fs8.existsSync(gradlew)) return;
2230
+ const gradlew = path9.join(appAndroidPath, process.platform === "win32" ? "gradlew.bat" : "gradlew");
2231
+ if (!fs9.existsSync(gradlew)) return;
2168
2232
  try {
2169
2233
  console.log("\u2139\uFE0F Running Gradle sync in android directory...");
2170
2234
  execSync2(process.platform === "win32" ? "gradlew.bat projects" : "./gradlew projects", {
@@ -2178,14 +2242,14 @@ ${generateActivityLifecycleKotlin(packages, projectPackage)}`;
2178
2242
  }
2179
2243
  }
2180
2244
  function syncVersionCatalog(packages) {
2181
- const libsTomlPath = path8.join(appAndroidPath, "gradle", "libs.versions.toml");
2182
- if (!fs8.existsSync(libsTomlPath)) return;
2245
+ const libsTomlPath = path9.join(appAndroidPath, "gradle", "libs.versions.toml");
2246
+ if (!fs9.existsSync(libsTomlPath)) return;
2183
2247
  const requiredAliases = /* @__PURE__ */ new Set();
2184
2248
  const requiredPluginAliases = /* @__PURE__ */ new Set();
2185
2249
  for (const pkg of packages) {
2186
- const buildPath = path8.join(pkg.packagePath, pkg.config.android?.sourceDir || "android", "build.gradle.kts");
2187
- if (!fs8.existsSync(buildPath)) continue;
2188
- const content = fs8.readFileSync(buildPath, "utf8");
2250
+ const buildPath = path9.join(pkg.packagePath, pkg.config.android?.sourceDir || "android", "build.gradle.kts");
2251
+ if (!fs9.existsSync(buildPath)) continue;
2252
+ const content = fs9.readFileSync(buildPath, "utf8");
2189
2253
  for (const m of content.matchAll(/libs\.([\w.]+)/g)) {
2190
2254
  const alias = m[1];
2191
2255
  if (alias && alias in REQUIRED_CATALOG_ENTRIES) requiredAliases.add(alias);
@@ -2196,7 +2260,7 @@ ${generateActivityLifecycleKotlin(packages, projectPackage)}`;
2196
2260
  }
2197
2261
  }
2198
2262
  if (requiredAliases.size === 0 && requiredPluginAliases.size === 0) return;
2199
- let toml = fs8.readFileSync(libsTomlPath, "utf8");
2263
+ let toml = fs9.readFileSync(libsTomlPath, "utf8");
2200
2264
  let updated = false;
2201
2265
  for (const alias of requiredAliases) {
2202
2266
  const entry = REQUIRED_CATALOG_ENTRIES[alias];
@@ -2220,14 +2284,14 @@ ${generateActivityLifecycleKotlin(packages, projectPackage)}`;
2220
2284
  updated = true;
2221
2285
  }
2222
2286
  if (updated) {
2223
- fs8.writeFileSync(libsTomlPath, toml);
2287
+ fs9.writeFileSync(libsTomlPath, toml);
2224
2288
  console.log("\u2705 Synced version catalog (libs.versions.toml) for linked modules.");
2225
2289
  }
2226
2290
  }
2227
2291
  function ensureXElementDeps() {
2228
- const libsTomlPath = path8.join(appAndroidPath, "gradle", "libs.versions.toml");
2229
- if (fs8.existsSync(libsTomlPath)) {
2230
- let toml = fs8.readFileSync(libsTomlPath, "utf8");
2292
+ const libsTomlPath = path9.join(appAndroidPath, "gradle", "libs.versions.toml");
2293
+ if (fs9.existsSync(libsTomlPath)) {
2294
+ let toml = fs9.readFileSync(libsTomlPath, "utf8");
2231
2295
  let updated = false;
2232
2296
  if (!toml.includes("lynx-xelement =")) {
2233
2297
  toml = toml.replace(
@@ -2239,13 +2303,13 @@ lynx-xelement-input = { module = "org.lynxsdk.lynx:xelement-input", version.ref
2239
2303
  updated = true;
2240
2304
  }
2241
2305
  if (updated) {
2242
- fs8.writeFileSync(libsTomlPath, toml);
2306
+ fs9.writeFileSync(libsTomlPath, toml);
2243
2307
  console.log("\u2705 Added XElement entries to version catalog.");
2244
2308
  }
2245
2309
  }
2246
- const appBuildPath = path8.join(appAndroidPath, "app", "build.gradle.kts");
2247
- if (fs8.existsSync(appBuildPath)) {
2248
- let content = fs8.readFileSync(appBuildPath, "utf8");
2310
+ const appBuildPath = path9.join(appAndroidPath, "app", "build.gradle.kts");
2311
+ if (fs9.existsSync(appBuildPath)) {
2312
+ let content = fs9.readFileSync(appBuildPath, "utf8");
2249
2313
  if (!content.includes("lynx.xelement")) {
2250
2314
  content = content.replace(
2251
2315
  /(implementation\(libs\.lynx\.service\.http\))/,
@@ -2253,15 +2317,15 @@ lynx-xelement-input = { module = "org.lynxsdk.lynx:xelement-input", version.ref
2253
2317
  implementation(libs.lynx.xelement)
2254
2318
  implementation(libs.lynx.xelement.input)`
2255
2319
  );
2256
- fs8.writeFileSync(appBuildPath, content);
2320
+ fs9.writeFileSync(appBuildPath, content);
2257
2321
  console.log("\u2705 Added XElement dependencies to app build.gradle.kts.");
2258
2322
  }
2259
2323
  }
2260
2324
  }
2261
2325
  function ensureReleaseSigning() {
2262
- const appBuildPath = path8.join(appAndroidPath, "app", "build.gradle.kts");
2263
- if (!fs8.existsSync(appBuildPath)) return;
2264
- let content = fs8.readFileSync(appBuildPath, "utf8");
2326
+ const appBuildPath = path9.join(appAndroidPath, "app", "build.gradle.kts");
2327
+ if (!fs9.existsSync(appBuildPath)) return;
2328
+ let content = fs9.readFileSync(appBuildPath, "utf8");
2265
2329
  if (content.includes('signingConfig = signingConfigs.getByName("debug")')) return;
2266
2330
  const releaseBlock = /(release\s*\{)([\s\S]*?)(\n \}\s*\n(\s*\}|\s*compileOptions))/;
2267
2331
  const match = content.match(releaseBlock);
@@ -2272,13 +2336,13 @@ lynx-xelement-input = { module = "org.lynxsdk.lynx:xelement-input", version.ref
2272
2336
  signingConfig = signingConfigs.getByName("debug")
2273
2337
  $3`
2274
2338
  );
2275
- fs8.writeFileSync(appBuildPath, content);
2339
+ fs9.writeFileSync(appBuildPath, content);
2276
2340
  console.log("\u2705 Set release signing to debug so installRelease works without a keystore.");
2277
2341
  }
2278
2342
  }
2279
2343
  function syncManifestPermissions(packages) {
2280
- const manifestPath = path8.join(appAndroidPath, "app", "src", "main", "AndroidManifest.xml");
2281
- if (!fs8.existsSync(manifestPath)) return;
2344
+ const manifestPath = path9.join(appAndroidPath, "app", "src", "main", "AndroidManifest.xml");
2345
+ if (!fs9.existsSync(manifestPath)) return;
2282
2346
  const allPermissions = /* @__PURE__ */ new Set();
2283
2347
  for (const pkg of packages) {
2284
2348
  const perms = pkg.config.android?.permissions;
@@ -2290,7 +2354,7 @@ lynx-xelement-input = { module = "org.lynxsdk.lynx:xelement-input", version.ref
2290
2354
  }
2291
2355
  }
2292
2356
  if (allPermissions.size === 0) return;
2293
- let manifest = fs8.readFileSync(manifestPath, "utf8");
2357
+ let manifest = fs9.readFileSync(manifestPath, "utf8");
2294
2358
  const existingMatch = [...manifest.matchAll(/<uses-permission android:name="(android\.permission\.\w+)"\s*\/>/g)];
2295
2359
  const existing = new Set(existingMatch.map((m) => m[1]));
2296
2360
  const toAdd = [...allPermissions].filter((p) => !existing.has(p));
@@ -2301,7 +2365,7 @@ lynx-xelement-input = { module = "org.lynxsdk.lynx:xelement-input", version.ref
2301
2365
  `${newLines}
2302
2366
  $1$2`
2303
2367
  );
2304
- fs8.writeFileSync(manifestPath, manifest);
2368
+ fs9.writeFileSync(manifestPath, manifest);
2305
2369
  console.log(`\u2705 Synced manifest permissions: ${toAdd.map((p) => p.split(".").pop()).join(", ")}`);
2306
2370
  }
2307
2371
  run();
@@ -2309,26 +2373,26 @@ $1$2`
2309
2373
  var autolink_default = autolink;
2310
2374
 
2311
2375
  // src/android/bundle.ts
2312
- import fs12 from "fs";
2313
- import path12 from "path";
2376
+ import fs13 from "fs";
2377
+ import path13 from "path";
2314
2378
  import { execSync as execSync3 } from "child_process";
2315
2379
 
2316
2380
  // src/common/copyDistAssets.ts
2317
- import fs9 from "fs";
2318
- import path9 from "path";
2381
+ import fs10 from "fs";
2382
+ import path10 from "path";
2319
2383
  var SKIP = /* @__PURE__ */ new Set([".rspeedy", "stats.json"]);
2320
2384
  function copyDistAssets(distDir, destDir, bundleFile) {
2321
- if (!fs9.existsSync(distDir)) return;
2322
- for (const entry of fs9.readdirSync(distDir)) {
2385
+ if (!fs10.existsSync(distDir)) return;
2386
+ for (const entry of fs10.readdirSync(distDir)) {
2323
2387
  if (SKIP.has(entry)) continue;
2324
- const src = path9.join(distDir, entry);
2325
- const dest = path9.join(destDir, entry);
2326
- const stat = fs9.statSync(src);
2388
+ const src = path10.join(distDir, entry);
2389
+ const dest = path10.join(destDir, entry);
2390
+ const stat = fs10.statSync(src);
2327
2391
  if (stat.isDirectory()) {
2328
- fs9.mkdirSync(dest, { recursive: true });
2392
+ fs10.mkdirSync(dest, { recursive: true });
2329
2393
  copyDistAssets(src, dest, bundleFile);
2330
2394
  } else {
2331
- fs9.copyFileSync(src, dest);
2395
+ fs10.copyFileSync(src, dest);
2332
2396
  if (entry !== bundleFile) {
2333
2397
  console.log(`\u2728 Copied asset: ${entry}`);
2334
2398
  }
@@ -2337,8 +2401,8 @@ function copyDistAssets(distDir, destDir, bundleFile) {
2337
2401
  }
2338
2402
 
2339
2403
  // src/common/tsconfigUtils.ts
2340
- import fs10 from "fs";
2341
- import path10 from "path";
2404
+ import fs11 from "fs";
2405
+ import path11 from "path";
2342
2406
  function stripJsonCommentsAndTrailingCommas(str) {
2343
2407
  return str.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "").replace(/,\s*([\]}])/g, "$1");
2344
2408
  }
@@ -2350,19 +2414,19 @@ function parseTsconfigJson(raw) {
2350
2414
  }
2351
2415
  }
2352
2416
  function readTsconfig(filePath) {
2353
- if (!fs10.existsSync(filePath)) return null;
2417
+ if (!fs11.existsSync(filePath)) return null;
2354
2418
  try {
2355
- return parseTsconfigJson(fs10.readFileSync(filePath, "utf-8"));
2419
+ return parseTsconfigJson(fs11.readFileSync(filePath, "utf-8"));
2356
2420
  } catch {
2357
2421
  return null;
2358
2422
  }
2359
2423
  }
2360
2424
  function resolveExtends(tsconfig, dir) {
2361
2425
  if (!tsconfig.extends) return tsconfig;
2362
- const basePath = path10.resolve(dir, tsconfig.extends);
2426
+ const basePath = path11.resolve(dir, tsconfig.extends);
2363
2427
  const base = readTsconfig(basePath);
2364
2428
  if (!base) return tsconfig;
2365
- const baseDir = path10.dirname(basePath);
2429
+ const baseDir = path11.dirname(basePath);
2366
2430
  const resolved = resolveExtends(base, baseDir);
2367
2431
  return {
2368
2432
  ...resolved,
@@ -2371,13 +2435,13 @@ function resolveExtends(tsconfig, dir) {
2371
2435
  };
2372
2436
  }
2373
2437
  function fixTsconfigReferencesForBuild(tsconfigPath) {
2374
- const dir = path10.dirname(tsconfigPath);
2438
+ const dir = path11.dirname(tsconfigPath);
2375
2439
  const tsconfig = readTsconfig(tsconfigPath);
2376
2440
  if (!tsconfig?.references?.length) return false;
2377
2441
  const refsWithNoEmit = [];
2378
2442
  for (const ref of tsconfig.references) {
2379
- const refPath = path10.resolve(dir, ref.path);
2380
- const refConfigPath = refPath.endsWith(".json") ? refPath : path10.join(refPath, "tsconfig.json");
2443
+ const refPath = path11.resolve(dir, ref.path);
2444
+ const refConfigPath = refPath.endsWith(".json") ? refPath : path11.join(refPath, "tsconfig.json");
2381
2445
  const refConfig = readTsconfig(refConfigPath);
2382
2446
  if (refConfig?.compilerOptions?.noEmit === true) {
2383
2447
  refsWithNoEmit.push(refConfigPath);
@@ -2392,16 +2456,16 @@ function fixTsconfigReferencesForBuild(tsconfigPath) {
2392
2456
  const includes = [];
2393
2457
  const compilerOpts = { ...tsconfig.compilerOptions };
2394
2458
  for (const ref of tsconfig.references) {
2395
- const refPath = path10.resolve(dir, ref.path);
2396
- const refConfigPath = refPath.endsWith(".json") ? refPath : path10.join(refPath, "tsconfig.json");
2459
+ const refPath = path11.resolve(dir, ref.path);
2460
+ const refConfigPath = refPath.endsWith(".json") ? refPath : path11.join(refPath, "tsconfig.json");
2397
2461
  const refConfig = readTsconfig(refConfigPath);
2398
2462
  if (!refConfig) continue;
2399
- const refDir = path10.dirname(refConfigPath);
2463
+ const refDir = path11.dirname(refConfigPath);
2400
2464
  const resolved = resolveExtends(refConfig, refDir);
2401
2465
  const inc = resolved.include;
2402
2466
  if (inc) {
2403
2467
  const arr = Array.isArray(inc) ? inc : [inc];
2404
- const baseDir = path10.relative(dir, refDir);
2468
+ const baseDir = path11.relative(dir, refDir);
2405
2469
  for (const p of arr) {
2406
2470
  const clean = typeof p === "string" && p.startsWith("./") ? p.slice(2) : p;
2407
2471
  includes.push(!baseDir || baseDir === "." ? clean : `${baseDir}/${clean}`);
@@ -2419,7 +2483,7 @@ function fixTsconfigReferencesForBuild(tsconfigPath) {
2419
2483
  if (includes.length > 0) merged.include = [...new Set(includes)];
2420
2484
  compilerOpts.noEmit = true;
2421
2485
  merged.compilerOptions = compilerOpts;
2422
- fs10.writeFileSync(tsconfigPath, JSON.stringify(merged, null, 2));
2486
+ fs11.writeFileSync(tsconfigPath, JSON.stringify(merged, null, 2));
2423
2487
  return true;
2424
2488
  }
2425
2489
  function addTamerTypesInclude(tsconfigPath, tamerTypesInclude) {
@@ -2430,23 +2494,23 @@ function addTamerTypesInclude(tsconfigPath, tamerTypesInclude) {
2430
2494
  if (arr.some((p) => (typeof p === "string" ? p : "").includes("tamer-"))) return false;
2431
2495
  arr.push(tamerTypesInclude);
2432
2496
  tsconfig.include = arr;
2433
- fs10.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
2497
+ fs11.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
2434
2498
  return true;
2435
2499
  }
2436
2500
 
2437
2501
  // src/android/syncDevClient.ts
2438
- import fs11 from "fs";
2439
- import path11 from "path";
2502
+ import fs12 from "fs";
2503
+ import path12 from "path";
2440
2504
  function readAndSubstituteTemplate2(templatePath, vars) {
2441
- const raw = fs11.readFileSync(templatePath, "utf-8");
2505
+ const raw = fs12.readFileSync(templatePath, "utf-8");
2442
2506
  return Object.entries(vars).reduce(
2443
2507
  (s, [k, v]) => s.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v),
2444
2508
  raw
2445
2509
  );
2446
2510
  }
2447
2511
  function patchAppLogService(appPath) {
2448
- if (!fs11.existsSync(appPath)) return;
2449
- const raw = fs11.readFileSync(appPath, "utf-8");
2512
+ if (!fs12.existsSync(appPath)) return;
2513
+ const raw = fs12.readFileSync(appPath, "utf-8");
2450
2514
  const patched = raw.replace(
2451
2515
  /private void initLynxService\(\)\s*\{[\s\S]*?\n\s*}\s*\n\s*private void initFresco\(\)/,
2452
2516
  `private void initLynxService() {
@@ -2465,7 +2529,7 @@ function patchAppLogService(appPath) {
2465
2529
  private void initFresco()`
2466
2530
  );
2467
2531
  if (patched !== raw) {
2468
- fs11.writeFileSync(appPath, patched);
2532
+ fs12.writeFileSync(appPath, patched);
2469
2533
  }
2470
2534
  }
2471
2535
  async function syncDevClient(opts) {
@@ -2480,9 +2544,9 @@ async function syncDevClient(opts) {
2480
2544
  const packageName = config.android?.packageName;
2481
2545
  const appName = config.android?.appName;
2482
2546
  const packagePath = packageName.replace(/\./g, "/");
2483
- const javaDir = path11.join(rootDir, "app", "src", "main", "java", packagePath);
2484
- const kotlinDir = path11.join(rootDir, "app", "src", "main", "kotlin", packagePath);
2485
- if (!fs11.existsSync(javaDir) || !fs11.existsSync(kotlinDir)) {
2547
+ const javaDir = path12.join(rootDir, "app", "src", "main", "java", packagePath);
2548
+ const kotlinDir = path12.join(rootDir, "app", "src", "main", "kotlin", packagePath);
2549
+ if (!fs12.existsSync(javaDir) || !fs12.existsSync(kotlinDir)) {
2486
2550
  console.error("\u274C Android project not found. Run `tamer android create` first.");
2487
2551
  process.exit(1);
2488
2552
  }
@@ -2498,14 +2562,14 @@ async function syncDevClient(opts) {
2498
2562
  const [templateProviderSource] = await Promise.all([
2499
2563
  fetchAndPatchTemplateProvider(vars)
2500
2564
  ]);
2501
- fs11.writeFileSync(path11.join(javaDir, "TemplateProvider.java"), templateProviderSource);
2502
- fs11.writeFileSync(path11.join(kotlinDir, "MainActivity.kt"), getStandaloneMainActivity(vars));
2503
- patchAppLogService(path11.join(javaDir, "App.java"));
2504
- const appDir = path11.join(rootDir, "app");
2505
- const mainDir = path11.join(appDir, "src", "main");
2506
- const manifestPath = path11.join(mainDir, "AndroidManifest.xml");
2565
+ fs12.writeFileSync(path12.join(javaDir, "TemplateProvider.java"), templateProviderSource);
2566
+ fs12.writeFileSync(path12.join(kotlinDir, "MainActivity.kt"), getStandaloneMainActivity(vars));
2567
+ patchAppLogService(path12.join(javaDir, "App.java"));
2568
+ const appDir = path12.join(rootDir, "app");
2569
+ const mainDir = path12.join(appDir, "src", "main");
2570
+ const manifestPath = path12.join(mainDir, "AndroidManifest.xml");
2507
2571
  if (hasDevClient) {
2508
- const templateDir = path11.join(devClientPkg, "android", "templates");
2572
+ const templateDir = path12.join(devClientPkg, "android", "templates");
2509
2573
  const templateVars = { PACKAGE_NAME: packageName, APP_NAME: appName };
2510
2574
  const devClientFiles = [
2511
2575
  "DevClientManager.kt",
@@ -2514,13 +2578,13 @@ async function syncDevClient(opts) {
2514
2578
  "PortraitCaptureActivity.kt"
2515
2579
  ];
2516
2580
  for (const f of devClientFiles) {
2517
- const src = path11.join(templateDir, f);
2518
- if (fs11.existsSync(src)) {
2581
+ const src = path12.join(templateDir, f);
2582
+ if (fs12.existsSync(src)) {
2519
2583
  const content = readAndSubstituteTemplate2(src, templateVars);
2520
- fs11.writeFileSync(path11.join(kotlinDir, f), content);
2584
+ fs12.writeFileSync(path12.join(kotlinDir, f), content);
2521
2585
  }
2522
2586
  }
2523
- let manifest = fs11.readFileSync(manifestPath, "utf-8");
2587
+ let manifest = fs12.readFileSync(manifestPath, "utf-8");
2524
2588
  const projectActivityEntry = ' <activity android:name=".ProjectActivity" android:exported="false" android:taskAffinity="" android:launchMode="singleTask" android:documentLaunchMode="always" android:windowSoftInputMode="adjustResize" />';
2525
2589
  const portraitCaptureEntry = ' <activity android:name=".PortraitCaptureActivity" android:screenOrientation="portrait" android:stateNotNeeded="true" android:theme="@style/zxing_CaptureTheme" android:windowSoftInputMode="stateAlwaysHidden" />';
2526
2590
  if (!manifest.includes("ProjectActivity")) {
@@ -2542,16 +2606,16 @@ $1$2`);
2542
2606
  '$1 android:windowSoftInputMode="adjustResize"$2'
2543
2607
  );
2544
2608
  }
2545
- fs11.writeFileSync(manifestPath, manifest);
2609
+ fs12.writeFileSync(manifestPath, manifest);
2546
2610
  console.log("\u2705 Synced dev client (TemplateProvider, MainActivity, ProjectActivity, DevClientManager)");
2547
2611
  } else {
2548
2612
  for (const f of ["DevClientManager.kt", "DevServerPrefs.kt", "ProjectActivity.kt", "PortraitCaptureActivity.kt", "DevLauncherActivity.kt"]) {
2549
2613
  try {
2550
- fs11.rmSync(path11.join(kotlinDir, f));
2614
+ fs12.rmSync(path12.join(kotlinDir, f));
2551
2615
  } catch {
2552
2616
  }
2553
2617
  }
2554
- let manifest = fs11.readFileSync(manifestPath, "utf-8");
2618
+ let manifest = fs12.readFileSync(manifestPath, "utf-8");
2555
2619
  manifest = manifest.replace(/\s*<activity android:name="\.ProjectActivity"[^\/]*\/>\n?/g, "");
2556
2620
  manifest = manifest.replace(/\s*<activity android:name="\.PortraitCaptureActivity"[^\/]*\/>\n?/g, "");
2557
2621
  const mainActivityTag = manifest.match(/<activity[^>]*android:name="\.MainActivity"[^>]*>/);
@@ -2561,7 +2625,7 @@ $1$2`);
2561
2625
  '$1 android:windowSoftInputMode="adjustResize"$2'
2562
2626
  );
2563
2627
  }
2564
- fs11.writeFileSync(manifestPath, manifest);
2628
+ fs12.writeFileSync(manifestPath, manifest);
2565
2629
  console.log("\u2705 Synced (dev client disabled - use -d for debug build with dev client)");
2566
2630
  }
2567
2631
  }
@@ -2585,15 +2649,15 @@ async function bundleAndDeploy(opts = {}) {
2585
2649
  await syncDevClient_default({ includeDevClient });
2586
2650
  const iconPaths = resolveIconPaths(projectRoot, resolved.config);
2587
2651
  if (iconPaths) {
2588
- const resDir = path12.join(resolved.androidAppDir, "src", "main", "res");
2652
+ const resDir = path13.join(resolved.androidAppDir, "src", "main", "res");
2589
2653
  if (applyAndroidLauncherIcons(resDir, iconPaths)) {
2590
2654
  console.log("\u2705 Synced Android launcher icon(s) from tamer.config.json");
2591
- ensureAndroidManifestLauncherIcon(path12.join(resolved.androidAppDir, "src", "main", "AndroidManifest.xml"));
2655
+ ensureAndroidManifestLauncherIcon(path13.join(resolved.androidAppDir, "src", "main", "AndroidManifest.xml"));
2592
2656
  }
2593
2657
  }
2594
2658
  try {
2595
- const lynxTsconfig = path12.join(lynxProjectDir, "tsconfig.json");
2596
- if (fs12.existsSync(lynxTsconfig)) {
2659
+ const lynxTsconfig = path13.join(lynxProjectDir, "tsconfig.json");
2660
+ if (fs13.existsSync(lynxTsconfig)) {
2597
2661
  fixTsconfigReferencesForBuild(lynxTsconfig);
2598
2662
  }
2599
2663
  console.log("\u{1F4E6} Building Lynx bundle...");
@@ -2603,8 +2667,8 @@ async function bundleAndDeploy(opts = {}) {
2603
2667
  console.error("\u274C Build process failed.");
2604
2668
  process.exit(1);
2605
2669
  }
2606
- if (includeDevClient && devClientBundlePath && !fs12.existsSync(devClientBundlePath)) {
2607
- const devClientDir = path12.dirname(path12.dirname(devClientBundlePath));
2670
+ if (includeDevClient && devClientBundlePath && !fs13.existsSync(devClientBundlePath)) {
2671
+ const devClientDir = path13.dirname(path13.dirname(devClientBundlePath));
2608
2672
  try {
2609
2673
  console.log("\u{1F4E6} Building dev launcher (tamer-dev-client)...");
2610
2674
  execSync3("npm run build", { stdio: "inherit", cwd: devClientDir });
@@ -2615,22 +2679,22 @@ async function bundleAndDeploy(opts = {}) {
2615
2679
  }
2616
2680
  }
2617
2681
  try {
2618
- fs12.mkdirSync(destinationDir, { recursive: true });
2682
+ fs13.mkdirSync(destinationDir, { recursive: true });
2619
2683
  if (release) {
2620
- const devClientAsset = path12.join(destinationDir, "dev-client.lynx.bundle");
2621
- if (fs12.existsSync(devClientAsset)) {
2622
- fs12.rmSync(devClientAsset);
2684
+ const devClientAsset = path13.join(destinationDir, "dev-client.lynx.bundle");
2685
+ if (fs13.existsSync(devClientAsset)) {
2686
+ fs13.rmSync(devClientAsset);
2623
2687
  console.log(`\u2728 Removed dev-client.lynx.bundle from assets (production build)`);
2624
2688
  }
2625
- } else if (includeDevClient && devClientBundlePath && fs12.existsSync(devClientBundlePath)) {
2626
- fs12.copyFileSync(devClientBundlePath, path12.join(destinationDir, "dev-client.lynx.bundle"));
2689
+ } else if (includeDevClient && devClientBundlePath && fs13.existsSync(devClientBundlePath)) {
2690
+ fs13.copyFileSync(devClientBundlePath, path13.join(destinationDir, "dev-client.lynx.bundle"));
2627
2691
  console.log(`\u2728 Copied dev-client.lynx.bundle to assets`);
2628
2692
  }
2629
- if (!fs12.existsSync(lynxBundlePath)) {
2693
+ if (!fs13.existsSync(lynxBundlePath)) {
2630
2694
  console.error(`\u274C Build output not found at: ${lynxBundlePath}`);
2631
2695
  process.exit(1);
2632
2696
  }
2633
- const distDir = path12.dirname(lynxBundlePath);
2697
+ const distDir = path13.dirname(lynxBundlePath);
2634
2698
  copyDistAssets(distDir, destinationDir, resolved.lynxBundleFile);
2635
2699
  console.log(`\u2728 Copied ${resolved.lynxBundleFile} to assets`);
2636
2700
  } catch (error) {
@@ -2641,7 +2705,7 @@ async function bundleAndDeploy(opts = {}) {
2641
2705
  var bundle_default = bundleAndDeploy;
2642
2706
 
2643
2707
  // src/android/build.ts
2644
- import path13 from "path";
2708
+ import path14 from "path";
2645
2709
  import { execSync as execSync4 } from "child_process";
2646
2710
  async function buildApk(opts = {}) {
2647
2711
  let resolved;
@@ -2652,12 +2716,17 @@ async function buildApk(opts = {}) {
2652
2716
  }
2653
2717
  const release = opts.release === true || opts.production === true;
2654
2718
  await bundle_default({ release, production: opts.production });
2655
- const androidDir = resolved.androidDir;
2656
- const gradlew = path13.join(androidDir, process.platform === "win32" ? "gradlew.bat" : "gradlew");
2719
+ const { androidDir, projectRoot } = resolved;
2720
+ cleanTamerAndroidLibBuildsIfVersionsChanged(projectRoot);
2721
+ const gradlew = path14.join(androidDir, process.platform === "win32" ? "gradlew.bat" : "gradlew");
2657
2722
  const variant = release ? "Release" : "Debug";
2658
2723
  const task = opts.install ? `install${variant}` : `assemble${variant}`;
2659
2724
  console.log(`
2660
2725
  \u{1F528} Building ${variant.toLowerCase()} APK${opts.install ? " and installing" : ""}...`);
2726
+ if (opts.clean === true) {
2727
+ console.log("\u2139\uFE0F Running Gradle clean (--clean)...");
2728
+ execSync4(`"${gradlew}" clean`, { stdio: "inherit", cwd: androidDir });
2729
+ }
2661
2730
  execSync4(`"${gradlew}" ${task}`, { stdio: "inherit", cwd: androidDir });
2662
2731
  console.log(`\u2705 APK ${opts.install ? "installed" : "built"} successfully.`);
2663
2732
  if (opts.install) {
@@ -2678,13 +2747,13 @@ async function buildApk(opts = {}) {
2678
2747
  var build_default = buildApk;
2679
2748
 
2680
2749
  // src/ios/create.ts
2681
- import fs14 from "fs";
2682
- import path15 from "path";
2750
+ import fs15 from "fs";
2751
+ import path16 from "path";
2683
2752
 
2684
2753
  // src/ios/getPod.ts
2685
2754
  import { execSync as execSync5 } from "child_process";
2686
- import fs13 from "fs";
2687
- import path14 from "path";
2755
+ import fs14 from "fs";
2756
+ import path15 from "path";
2688
2757
  function isCocoaPodsInstalled() {
2689
2758
  try {
2690
2759
  execSync5("command -v pod >/dev/null 2>&1");
@@ -2706,8 +2775,8 @@ async function setupCocoaPods(rootDir) {
2706
2775
  }
2707
2776
  try {
2708
2777
  console.log("\u{1F4E6} CocoaPods is installed. Proceeding with dependency installation...");
2709
- const podfilePath = path14.join(rootDir, "Podfile");
2710
- if (!fs13.existsSync(podfilePath)) {
2778
+ const podfilePath = path15.join(rootDir, "Podfile");
2779
+ if (!fs14.existsSync(podfilePath)) {
2711
2780
  throw new Error(`Podfile not found at ${podfilePath}`);
2712
2781
  }
2713
2782
  console.log(`\u{1F680} Executing pod install in: ${rootDir}`);
@@ -2733,7 +2802,7 @@ async function setupCocoaPods(rootDir) {
2733
2802
  // src/ios/create.ts
2734
2803
  import { randomBytes } from "crypto";
2735
2804
  function readAndSubstituteTemplate3(templatePath, vars) {
2736
- const raw = fs14.readFileSync(templatePath, "utf-8");
2805
+ const raw = fs15.readFileSync(templatePath, "utf-8");
2737
2806
  return Object.entries(vars).reduce(
2738
2807
  (s, [k, v]) => s.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v),
2739
2808
  raw
@@ -2756,17 +2825,17 @@ var create2 = () => {
2756
2825
  process.exit(1);
2757
2826
  }
2758
2827
  const iosDir = config.paths?.iosDir ?? "ios";
2759
- const rootDir = path15.join(process.cwd(), iosDir);
2760
- const projectDir = path15.join(rootDir, appName);
2761
- const xcodeprojDir = path15.join(rootDir, `${appName}.xcodeproj`);
2828
+ const rootDir = path16.join(process.cwd(), iosDir);
2829
+ const projectDir = path16.join(rootDir, appName);
2830
+ const xcodeprojDir = path16.join(rootDir, `${appName}.xcodeproj`);
2762
2831
  const bridgingHeader = `${appName}-Bridging-Header.h`;
2763
2832
  function writeFile2(filePath, content) {
2764
- fs14.mkdirSync(path15.dirname(filePath), { recursive: true });
2765
- fs14.writeFileSync(filePath, content.trimStart(), "utf8");
2833
+ fs15.mkdirSync(path16.dirname(filePath), { recursive: true });
2834
+ fs15.writeFileSync(filePath, content.trimStart(), "utf8");
2766
2835
  }
2767
- if (fs14.existsSync(rootDir)) {
2836
+ if (fs15.existsSync(rootDir)) {
2768
2837
  console.log(`\u{1F9F9} Removing existing directory: ${rootDir}`);
2769
- fs14.rmSync(rootDir, { recursive: true, force: true });
2838
+ fs15.rmSync(rootDir, { recursive: true, force: true });
2770
2839
  }
2771
2840
  console.log(`\u{1F680} Creating a new Tamer4Lynx project in: ${rootDir}`);
2772
2841
  const ids = {
@@ -2802,7 +2871,7 @@ var create2 = () => {
2802
2871
  targetDebugConfig: generateId(),
2803
2872
  targetReleaseConfig: generateId()
2804
2873
  };
2805
- writeFile2(path15.join(rootDir, "Podfile"), `
2874
+ writeFile2(path16.join(rootDir, "Podfile"), `
2806
2875
  source 'https://cdn.cocoapods.org/'
2807
2876
 
2808
2877
  install! 'cocoapods', :incremental_installation => true, :generate_multiple_pod_projects => true
@@ -2889,15 +2958,15 @@ end
2889
2958
  const hostPkg = findTamerHostPackage(process.cwd());
2890
2959
  const templateVars = { PACKAGE_NAME: bundleId, APP_NAME: appName, BUNDLE_ID: bundleId };
2891
2960
  if (hostPkg) {
2892
- const templateDir = path15.join(hostPkg, "ios", "templates");
2961
+ const templateDir = path16.join(hostPkg, "ios", "templates");
2893
2962
  for (const f of ["AppDelegate.swift", "SceneDelegate.swift", "ViewController.swift", "LynxProvider.swift", "LynxInitProcessor.swift"]) {
2894
- const srcPath = path15.join(templateDir, f);
2895
- if (fs14.existsSync(srcPath)) {
2896
- writeFile2(path15.join(projectDir, f), readAndSubstituteTemplate3(srcPath, templateVars));
2963
+ const srcPath = path16.join(templateDir, f);
2964
+ if (fs15.existsSync(srcPath)) {
2965
+ writeFile2(path16.join(projectDir, f), readAndSubstituteTemplate3(srcPath, templateVars));
2897
2966
  }
2898
2967
  }
2899
2968
  } else {
2900
- writeFile2(path15.join(projectDir, "AppDelegate.swift"), `
2969
+ writeFile2(path16.join(projectDir, "AppDelegate.swift"), `
2901
2970
  import UIKit
2902
2971
 
2903
2972
  @UIApplicationMain
@@ -2912,7 +2981,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
2912
2981
  }
2913
2982
  }
2914
2983
  `);
2915
- writeFile2(path15.join(projectDir, "SceneDelegate.swift"), `
2984
+ writeFile2(path16.join(projectDir, "SceneDelegate.swift"), `
2916
2985
  import UIKit
2917
2986
 
2918
2987
  class SceneDelegate: UIResponder, UIWindowSceneDelegate {
@@ -2926,7 +2995,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
2926
2995
  }
2927
2996
  }
2928
2997
  `);
2929
- writeFile2(path15.join(projectDir, "ViewController.swift"), `
2998
+ writeFile2(path16.join(projectDir, "ViewController.swift"), `
2930
2999
  import UIKit
2931
3000
  import Lynx
2932
3001
  import tamerinsets
@@ -2999,7 +3068,7 @@ class ViewController: UIViewController {
2999
3068
  }
3000
3069
  }
3001
3070
  `);
3002
- writeFile2(path15.join(projectDir, "LynxProvider.swift"), `
3071
+ writeFile2(path16.join(projectDir, "LynxProvider.swift"), `
3003
3072
  import Foundation
3004
3073
 
3005
3074
  class LynxProvider: NSObject, LynxTemplateProvider {
@@ -3018,7 +3087,7 @@ class LynxProvider: NSObject, LynxTemplateProvider {
3018
3087
  }
3019
3088
  }
3020
3089
  `);
3021
- writeFile2(path15.join(projectDir, "LynxInitProcessor.swift"), `
3090
+ writeFile2(path16.join(projectDir, "LynxInitProcessor.swift"), `
3022
3091
  // Copyright 2024 The Lynx Authors. All rights reserved.
3023
3092
  // Licensed under the Apache License Version 2.0 that can be found in the
3024
3093
  // LICENSE file in the root directory of this source tree.
@@ -3058,7 +3127,7 @@ final class LynxInitProcessor {
3058
3127
  }
3059
3128
  `);
3060
3129
  }
3061
- writeFile2(path15.join(projectDir, bridgingHeader), `
3130
+ writeFile2(path16.join(projectDir, bridgingHeader), `
3062
3131
  #import <Lynx/LynxConfig.h>
3063
3132
  #import <Lynx/LynxEnv.h>
3064
3133
  #import <Lynx/LynxTemplateProvider.h>
@@ -3067,7 +3136,7 @@ final class LynxInitProcessor {
3067
3136
  #import <SDWebImage/SDWebImage.h>
3068
3137
  #import <SDWebImageWebPCoder/SDWebImageWebPCoder.h>
3069
3138
  `);
3070
- writeFile2(path15.join(projectDir, "Info.plist"), `
3139
+ writeFile2(path16.join(projectDir, "Info.plist"), `
3071
3140
  <?xml version="1.0" encoding="UTF-8"?>
3072
3141
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3073
3142
  <plist version="1.0">
@@ -3125,21 +3194,21 @@ final class LynxInitProcessor {
3125
3194
  </dict>
3126
3195
  </plist>
3127
3196
  `);
3128
- const appIconDir = path15.join(projectDir, "Assets.xcassets", "AppIcon.appiconset");
3129
- fs14.mkdirSync(appIconDir, { recursive: true });
3197
+ const appIconDir = path16.join(projectDir, "Assets.xcassets", "AppIcon.appiconset");
3198
+ fs15.mkdirSync(appIconDir, { recursive: true });
3130
3199
  const iconPaths = resolveIconPaths(process.cwd(), config);
3131
3200
  if (applyIosAppIconAssets(appIconDir, iconPaths)) {
3132
3201
  console.log(iconPaths?.ios ? "\u2705 Copied iOS icon from tamer.config.json icon.ios" : "\u2705 Copied app icon from tamer.config.json icon.source");
3133
3202
  } else {
3134
- writeFile2(path15.join(appIconDir, "Contents.json"), `
3203
+ writeFile2(path16.join(appIconDir, "Contents.json"), `
3135
3204
  {
3136
3205
  "images" : [ { "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" } ],
3137
3206
  "info" : { "author" : "xcode", "version" : 1 }
3138
3207
  }
3139
3208
  `);
3140
3209
  }
3141
- fs14.mkdirSync(xcodeprojDir, { recursive: true });
3142
- writeFile2(path15.join(xcodeprojDir, "project.pbxproj"), `
3210
+ fs15.mkdirSync(xcodeprojDir, { recursive: true });
3211
+ writeFile2(path16.join(xcodeprojDir, "project.pbxproj"), `
3143
3212
  // !$*UTF8*$!
3144
3213
  {
3145
3214
  archiveVersion = 1;
@@ -3425,8 +3494,8 @@ final class LynxInitProcessor {
3425
3494
  var create_default2 = create2;
3426
3495
 
3427
3496
  // src/ios/autolink.ts
3428
- import fs16 from "fs";
3429
- import path17 from "path";
3497
+ import fs17 from "fs";
3498
+ import path18 from "path";
3430
3499
  import { execSync as execSync6 } from "child_process";
3431
3500
 
3432
3501
  // src/common/hostNativeModulesManifest.ts
@@ -3437,8 +3506,8 @@ function buildHostNativeModulesManifestJson(moduleClassNames) {
3437
3506
  }
3438
3507
 
3439
3508
  // src/ios/syncHost.ts
3440
- import fs15 from "fs";
3441
- import path16 from "path";
3509
+ import fs16 from "fs";
3510
+ import path17 from "path";
3442
3511
  import crypto from "crypto";
3443
3512
  function deterministicUUID(seed) {
3444
3513
  return crypto.createHash("sha256").update(seed).digest("hex").substring(0, 24).toUpperCase();
@@ -3486,7 +3555,7 @@ function getLaunchScreenStoryboard() {
3486
3555
  `;
3487
3556
  }
3488
3557
  function addLaunchScreenToXcodeProject(pbxprojPath, appName) {
3489
- let content = fs15.readFileSync(pbxprojPath, "utf8");
3558
+ let content = fs16.readFileSync(pbxprojPath, "utf8");
3490
3559
  if (content.includes("LaunchScreen.storyboard")) return;
3491
3560
  const baseFileRefUUID = deterministicUUID(`launchScreenBase:${appName}`);
3492
3561
  const variantGroupUUID = deterministicUUID(`launchScreenGroup:${appName}`);
@@ -3523,11 +3592,11 @@ function addLaunchScreenToXcodeProject(pbxprojPath, appName) {
3523
3592
  );
3524
3593
  content = content.replace(groupPattern, `$1
3525
3594
  ${variantGroupUUID} /* LaunchScreen.storyboard */,`);
3526
- fs15.writeFileSync(pbxprojPath, content, "utf8");
3595
+ fs16.writeFileSync(pbxprojPath, content, "utf8");
3527
3596
  console.log("\u2705 Registered LaunchScreen.storyboard in Xcode project");
3528
3597
  }
3529
3598
  function addSwiftSourceToXcodeProject(pbxprojPath, appName, filename) {
3530
- let content = fs15.readFileSync(pbxprojPath, "utf8");
3599
+ let content = fs16.readFileSync(pbxprojPath, "utf8");
3531
3600
  const escaped = filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3532
3601
  if (new RegExp(`path = "?${escaped}"?;`).test(content)) return;
3533
3602
  const fileRefUUID = deterministicUUID(`fileRef:${appName}:${filename}`);
@@ -3552,11 +3621,11 @@ function addSwiftSourceToXcodeProject(pbxprojPath, appName, filename) {
3552
3621
  );
3553
3622
  content = content.replace(groupPattern, `$1
3554
3623
  ${fileRefUUID} /* ${filename} */,`);
3555
- fs15.writeFileSync(pbxprojPath, content, "utf8");
3624
+ fs16.writeFileSync(pbxprojPath, content, "utf8");
3556
3625
  console.log(`\u2705 Registered ${filename} in Xcode project sources`);
3557
3626
  }
3558
3627
  function addResourceToXcodeProject(pbxprojPath, appName, filename) {
3559
- let content = fs15.readFileSync(pbxprojPath, "utf8");
3628
+ let content = fs16.readFileSync(pbxprojPath, "utf8");
3560
3629
  const escaped = filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3561
3630
  if (new RegExp(`path = "?${escaped}"?;`).test(content)) return;
3562
3631
  const fileRefUUID = deterministicUUID(`fileRef:${appName}:${filename}`);
@@ -3581,12 +3650,12 @@ function addResourceToXcodeProject(pbxprojPath, appName, filename) {
3581
3650
  );
3582
3651
  content = content.replace(groupPattern, `$1
3583
3652
  ${fileRefUUID} /* ${filename} */,`);
3584
- fs15.writeFileSync(pbxprojPath, content, "utf8");
3653
+ fs16.writeFileSync(pbxprojPath, content, "utf8");
3585
3654
  console.log(`\u2705 Registered ${filename} in Xcode project resources`);
3586
3655
  }
3587
3656
  function writeFile(filePath, content) {
3588
- fs15.mkdirSync(path16.dirname(filePath), { recursive: true });
3589
- fs15.writeFileSync(filePath, content, "utf8");
3657
+ fs16.mkdirSync(path17.dirname(filePath), { recursive: true });
3658
+ fs16.writeFileSync(filePath, content, "utf8");
3590
3659
  }
3591
3660
  function getAppDelegateSwift() {
3592
3661
  return `import UIKit
@@ -3803,8 +3872,8 @@ class ViewController: UIViewController {
3803
3872
  `;
3804
3873
  }
3805
3874
  function patchInfoPlist(infoPlistPath) {
3806
- if (!fs15.existsSync(infoPlistPath)) return;
3807
- let content = fs15.readFileSync(infoPlistPath, "utf8");
3875
+ if (!fs16.existsSync(infoPlistPath)) return;
3876
+ let content = fs16.readFileSync(infoPlistPath, "utf8");
3808
3877
  content = content.replace(/\s*<key>UIMainStoryboardFile<\/key>\s*<string>[^<]*<\/string>/g, "");
3809
3878
  if (!content.includes("UILaunchStoryboardName")) {
3810
3879
  content = content.replace("</dict>\n</plist>", ` <key>UILaunchStoryboardName</key>
@@ -3836,7 +3905,7 @@ function patchInfoPlist(infoPlistPath) {
3836
3905
  </plist>`);
3837
3906
  console.log("\u2705 Added UIApplicationSceneManifest to Info.plist");
3838
3907
  }
3839
- fs15.writeFileSync(infoPlistPath, content, "utf8");
3908
+ fs16.writeFileSync(infoPlistPath, content, "utf8");
3840
3909
  }
3841
3910
  function getSimpleLynxProviderSwift() {
3842
3911
  return `import Foundation
@@ -3861,9 +3930,9 @@ class LynxProvider: NSObject, LynxTemplateProvider {
3861
3930
  }
3862
3931
  function readTemplateOrFallback(devClientPkg, templateName, fallback, vars = {}) {
3863
3932
  if (devClientPkg) {
3864
- const tplPath = path16.join(devClientPkg, "ios", "templates", templateName);
3865
- if (fs15.existsSync(tplPath)) {
3866
- let content = fs15.readFileSync(tplPath, "utf8");
3933
+ const tplPath = path17.join(devClientPkg, "ios", "templates", templateName);
3934
+ if (fs16.existsSync(tplPath)) {
3935
+ let content = fs16.readFileSync(tplPath, "utf8");
3867
3936
  for (const [k, v] of Object.entries(vars)) {
3868
3937
  content = content.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v);
3869
3938
  }
@@ -3881,19 +3950,19 @@ function syncHostIos(opts) {
3881
3950
  if (!appName) {
3882
3951
  throw new Error('"ios.appName" must be defined in tamer.config.json');
3883
3952
  }
3884
- const projectDir = path16.join(resolved.iosDir, appName);
3885
- const infoPlistPath = path16.join(projectDir, "Info.plist");
3886
- if (!fs15.existsSync(projectDir)) {
3953
+ const projectDir = path17.join(resolved.iosDir, appName);
3954
+ const infoPlistPath = path17.join(projectDir, "Info.plist");
3955
+ if (!fs16.existsSync(projectDir)) {
3887
3956
  throw new Error(`iOS project not found at ${projectDir}. Run \`tamer ios create\` first.`);
3888
3957
  }
3889
- const pbxprojPath = path16.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
3890
- const baseLprojDir = path16.join(projectDir, "Base.lproj");
3891
- const launchScreenPath = path16.join(baseLprojDir, "LaunchScreen.storyboard");
3958
+ const pbxprojPath = path17.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
3959
+ const baseLprojDir = path17.join(projectDir, "Base.lproj");
3960
+ const launchScreenPath = path17.join(baseLprojDir, "LaunchScreen.storyboard");
3892
3961
  patchInfoPlist(infoPlistPath);
3893
- writeFile(path16.join(projectDir, "AppDelegate.swift"), getAppDelegateSwift());
3894
- writeFile(path16.join(projectDir, "SceneDelegate.swift"), getSceneDelegateSwift());
3895
- if (!fs15.existsSync(launchScreenPath)) {
3896
- fs15.mkdirSync(baseLprojDir, { recursive: true });
3962
+ writeFile(path17.join(projectDir, "AppDelegate.swift"), getAppDelegateSwift());
3963
+ writeFile(path17.join(projectDir, "SceneDelegate.swift"), getSceneDelegateSwift());
3964
+ if (!fs16.existsSync(launchScreenPath)) {
3965
+ fs16.mkdirSync(baseLprojDir, { recursive: true });
3897
3966
  writeFile(launchScreenPath, getLaunchScreenStoryboard());
3898
3967
  addLaunchScreenToXcodeProject(pbxprojPath, appName);
3899
3968
  }
@@ -3902,33 +3971,33 @@ function syncHostIos(opts) {
3902
3971
  const devClientPkg2 = findDevClientPackage(resolved.projectRoot);
3903
3972
  const segment = resolved.lynxProjectDir.split("/").filter(Boolean).pop() ?? "";
3904
3973
  const tplVars = { PROJECT_BUNDLE_SEGMENT: segment };
3905
- writeFile(path16.join(projectDir, "ViewController.swift"), getDevViewControllerSwift());
3906
- writeFile(path16.join(projectDir, "LynxProvider.swift"), getSimpleLynxProviderSwift());
3974
+ writeFile(path17.join(projectDir, "ViewController.swift"), getDevViewControllerSwift());
3975
+ writeFile(path17.join(projectDir, "LynxProvider.swift"), getSimpleLynxProviderSwift());
3907
3976
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "LynxProvider.swift");
3908
3977
  const devTPContent = readTemplateOrFallback(devClientPkg2, "DevTemplateProvider.swift", "", tplVars);
3909
3978
  if (devTPContent) {
3910
- writeFile(path16.join(projectDir, "DevTemplateProvider.swift"), devTPContent);
3979
+ writeFile(path17.join(projectDir, "DevTemplateProvider.swift"), devTPContent);
3911
3980
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "DevTemplateProvider.swift");
3912
3981
  }
3913
3982
  const projectVCContent = readTemplateOrFallback(devClientPkg2, "ProjectViewController.swift", "", tplVars);
3914
3983
  if (projectVCContent) {
3915
- writeFile(path16.join(projectDir, "ProjectViewController.swift"), projectVCContent);
3984
+ writeFile(path17.join(projectDir, "ProjectViewController.swift"), projectVCContent);
3916
3985
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "ProjectViewController.swift");
3917
3986
  }
3918
3987
  const devCMContent = readTemplateOrFallback(devClientPkg2, "DevClientManager.swift", "", tplVars);
3919
3988
  if (devCMContent) {
3920
- writeFile(path16.join(projectDir, "DevClientManager.swift"), devCMContent);
3989
+ writeFile(path17.join(projectDir, "DevClientManager.swift"), devCMContent);
3921
3990
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "DevClientManager.swift");
3922
3991
  }
3923
3992
  const qrContent = readTemplateOrFallback(devClientPkg2, "QRScannerViewController.swift", "", tplVars);
3924
3993
  if (qrContent) {
3925
- writeFile(path16.join(projectDir, "QRScannerViewController.swift"), qrContent);
3994
+ writeFile(path17.join(projectDir, "QRScannerViewController.swift"), qrContent);
3926
3995
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "QRScannerViewController.swift");
3927
3996
  }
3928
3997
  console.log("\u2705 Synced iOS host app (embedded dev mode) \u2014 ViewController, DevTemplateProvider, ProjectViewController, DevClientManager, QRScannerViewController");
3929
3998
  } else {
3930
- writeFile(path16.join(projectDir, "ViewController.swift"), getViewControllerSwift());
3931
- writeFile(path16.join(projectDir, "LynxProvider.swift"), getSimpleLynxProviderSwift());
3999
+ writeFile(path17.join(projectDir, "ViewController.swift"), getViewControllerSwift());
4000
+ writeFile(path17.join(projectDir, "LynxProvider.swift"), getSimpleLynxProviderSwift());
3932
4001
  addSwiftSourceToXcodeProject(pbxprojPath, appName, "LynxProvider.swift");
3933
4002
  console.log("\u2705 Synced iOS host app controller files");
3934
4003
  }
@@ -3947,11 +4016,11 @@ var autolink2 = (syncHostOpts) => {
3947
4016
  const projectRoot = resolved.projectRoot;
3948
4017
  const iosProjectPath = resolved.iosDir;
3949
4018
  function updateGeneratedSection(filePath, newContent, startMarker, endMarker) {
3950
- if (!fs16.existsSync(filePath)) {
4019
+ if (!fs17.existsSync(filePath)) {
3951
4020
  console.warn(`\u26A0\uFE0F File not found, skipping update: ${filePath}`);
3952
4021
  return;
3953
4022
  }
3954
- let fileContent = fs16.readFileSync(filePath, "utf8");
4023
+ let fileContent = fs17.readFileSync(filePath, "utf8");
3955
4024
  const escapedStartMarker = startMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3956
4025
  const escapedEndMarker = endMarker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3957
4026
  const regex = new RegExp(`${escapedStartMarker}[\\s\\S]*?${escapedEndMarker}`, "g");
@@ -3971,33 +4040,33 @@ ${replacementBlock}
3971
4040
  `;
3972
4041
  }
3973
4042
  } else {
3974
- console.warn(`\u26A0\uFE0F Could not find autolink markers in ${path17.basename(filePath)}. Appending to the end of the file.`);
4043
+ console.warn(`\u26A0\uFE0F Could not find autolink markers in ${path18.basename(filePath)}. Appending to the end of the file.`);
3975
4044
  fileContent += `
3976
4045
  ${replacementBlock}
3977
4046
  `;
3978
4047
  }
3979
- fs16.writeFileSync(filePath, fileContent, "utf8");
3980
- console.log(`\u2705 Updated autolinked section in ${path17.basename(filePath)}`);
4048
+ fs17.writeFileSync(filePath, fileContent, "utf8");
4049
+ console.log(`\u2705 Updated autolinked section in ${path18.basename(filePath)}`);
3981
4050
  }
3982
4051
  function resolvePodDirectory(pkg) {
3983
- const configuredDir = path17.join(pkg.packagePath, pkg.config.ios?.podspecPath || ".");
3984
- if (fs16.existsSync(configuredDir)) {
4052
+ const configuredDir = path18.join(pkg.packagePath, pkg.config.ios?.podspecPath || ".");
4053
+ if (fs17.existsSync(configuredDir)) {
3985
4054
  return configuredDir;
3986
4055
  }
3987
- const iosDir = path17.join(pkg.packagePath, "ios");
3988
- if (fs16.existsSync(iosDir)) {
4056
+ const iosDir = path18.join(pkg.packagePath, "ios");
4057
+ if (fs17.existsSync(iosDir)) {
3989
4058
  const stack = [iosDir];
3990
4059
  while (stack.length > 0) {
3991
4060
  const current = stack.pop();
3992
4061
  try {
3993
- const entries = fs16.readdirSync(current, { withFileTypes: true });
4062
+ const entries = fs17.readdirSync(current, { withFileTypes: true });
3994
4063
  const podspec = entries.find((entry) => entry.isFile() && entry.name.endsWith(".podspec"));
3995
4064
  if (podspec) {
3996
4065
  return current;
3997
4066
  }
3998
4067
  for (const entry of entries) {
3999
4068
  if (entry.isDirectory()) {
4000
- stack.push(path17.join(current, entry.name));
4069
+ stack.push(path18.join(current, entry.name));
4001
4070
  }
4002
4071
  }
4003
4072
  } catch {
@@ -4008,9 +4077,9 @@ ${replacementBlock}
4008
4077
  }
4009
4078
  function resolvePodName(pkg) {
4010
4079
  const fullPodspecDir = resolvePodDirectory(pkg);
4011
- if (fs16.existsSync(fullPodspecDir)) {
4080
+ if (fs17.existsSync(fullPodspecDir)) {
4012
4081
  try {
4013
- const files = fs16.readdirSync(fullPodspecDir);
4082
+ const files = fs17.readdirSync(fullPodspecDir);
4014
4083
  const podspecFile = files.find((f) => f.endsWith(".podspec"));
4015
4084
  if (podspecFile) return podspecFile.replace(".podspec", "");
4016
4085
  } catch {
@@ -4019,13 +4088,13 @@ ${replacementBlock}
4019
4088
  return pkg.name.split("/").pop().replace(/-/g, "");
4020
4089
  }
4021
4090
  function updatePodfile(packages) {
4022
- const podfilePath = path17.join(iosProjectPath, "Podfile");
4091
+ const podfilePath = path18.join(iosProjectPath, "Podfile");
4023
4092
  let scriptContent = ` # This section is automatically generated by Tamer4Lynx.
4024
4093
  # Manual edits will be overwritten.`;
4025
4094
  const iosPackages = packages.filter((p) => p.config.ios);
4026
4095
  if (iosPackages.length > 0) {
4027
4096
  iosPackages.forEach((pkg) => {
4028
- const relativePath = path17.relative(iosProjectPath, resolvePodDirectory(pkg));
4097
+ const relativePath = path18.relative(iosProjectPath, resolvePodDirectory(pkg));
4029
4098
  const podName = resolvePodName(pkg);
4030
4099
  scriptContent += `
4031
4100
  pod '${podName}', :path => '${relativePath}'`;
@@ -4037,9 +4106,9 @@ ${replacementBlock}
4037
4106
  updateGeneratedSection(podfilePath, scriptContent.trim(), "# GENERATED AUTOLINK DEPENDENCIES START", "# GENERATED AUTOLINK DEPENDENCIES END");
4038
4107
  }
4039
4108
  function ensureXElementPod() {
4040
- const podfilePath = path17.join(iosProjectPath, "Podfile");
4041
- if (!fs16.existsSync(podfilePath)) return;
4042
- let content = fs16.readFileSync(podfilePath, "utf8");
4109
+ const podfilePath = path18.join(iosProjectPath, "Podfile");
4110
+ if (!fs17.existsSync(podfilePath)) return;
4111
+ let content = fs17.readFileSync(podfilePath, "utf8");
4043
4112
  if (content.includes("pod 'XElement'")) return;
4044
4113
  const lynxVersionMatch = content.match(/pod\s+'Lynx',\s*'([^']+)'/);
4045
4114
  const lynxVersion = lynxVersionMatch?.[1] ?? "3.6.0";
@@ -4064,7 +4133,7 @@ ${replacementBlock}
4064
4133
  `;
4065
4134
  }
4066
4135
  }
4067
- fs16.writeFileSync(podfilePath, content, "utf8");
4136
+ fs17.writeFileSync(podfilePath, content, "utf8");
4068
4137
  console.log(`\u2705 Added XElement pod (v${lynxVersion}) to Podfile`);
4069
4138
  }
4070
4139
  function ensureLynxDevToolPods(packages) {
@@ -4072,9 +4141,9 @@ ${replacementBlock}
4072
4141
  (p) => p.name === "@tamer4lynx/tamer-dev-client" || p.name === "tamer-dev-client"
4073
4142
  );
4074
4143
  if (!hasDevClient) return;
4075
- const podfilePath = path17.join(iosProjectPath, "Podfile");
4076
- if (!fs16.existsSync(podfilePath)) return;
4077
- let content = fs16.readFileSync(podfilePath, "utf8");
4144
+ const podfilePath = path18.join(iosProjectPath, "Podfile");
4145
+ if (!fs17.existsSync(podfilePath)) return;
4146
+ let content = fs17.readFileSync(podfilePath, "utf8");
4078
4147
  if (content.includes("pod 'LynxDevtool'") || content.includes('pod "LynxDevtool"')) return;
4079
4148
  const lynxVersionMatch = content.match(/pod\s+'Lynx',\s*'([^']+)'/);
4080
4149
  const lynxVersion = lynxVersionMatch?.[1] ?? "3.6.0";
@@ -4102,13 +4171,13 @@ ${replacementBlock}
4102
4171
  `;
4103
4172
  }
4104
4173
  }
4105
- fs16.writeFileSync(podfilePath, content, "utf8");
4174
+ fs17.writeFileSync(podfilePath, content, "utf8");
4106
4175
  console.log(`\u2705 Added Lynx DevTool pods (v${lynxVersion}) to Podfile`);
4107
4176
  }
4108
4177
  function ensureLynxPatchInPodfile() {
4109
- const podfilePath = path17.join(iosProjectPath, "Podfile");
4110
- if (!fs16.existsSync(podfilePath)) return;
4111
- let content = fs16.readFileSync(podfilePath, "utf8");
4178
+ const podfilePath = path18.join(iosProjectPath, "Podfile");
4179
+ if (!fs17.existsSync(podfilePath)) return;
4180
+ let content = fs17.readFileSync(podfilePath, "utf8");
4112
4181
  if (content.includes("content.gsub(/\\btypeof\\(/, '__typeof__(')")) return;
4113
4182
  const patch = `
4114
4183
  Dir.glob(File.join(installer.sandbox.root, 'Lynx/platform/darwin/**/*.{m,mm}')).each do |lynx_source|
@@ -4120,13 +4189,13 @@ ${replacementBlock}
4120
4189
  end`;
4121
4190
  content = content.replace(/(\n end\s*\n)(end\s*)$/, `$1${patch}
4122
4191
  $2`);
4123
- fs16.writeFileSync(podfilePath, content, "utf8");
4192
+ fs17.writeFileSync(podfilePath, content, "utf8");
4124
4193
  console.log("\u2705 Added Lynx typeof patch to Podfile post_install.");
4125
4194
  }
4126
4195
  function ensurePodBuildSettings() {
4127
- const podfilePath = path17.join(iosProjectPath, "Podfile");
4128
- if (!fs16.existsSync(podfilePath)) return;
4129
- let content = fs16.readFileSync(podfilePath, "utf8");
4196
+ const podfilePath = path18.join(iosProjectPath, "Podfile");
4197
+ if (!fs17.existsSync(podfilePath)) return;
4198
+ let content = fs17.readFileSync(podfilePath, "utf8");
4130
4199
  let changed = false;
4131
4200
  if (!content.includes("CLANG_ENABLE_EXPLICIT_MODULES")) {
4132
4201
  content = content.replace(
@@ -4169,7 +4238,7 @@ $2`);
4169
4238
  changed = true;
4170
4239
  }
4171
4240
  if (changed) {
4172
- fs16.writeFileSync(podfilePath, content, "utf8");
4241
+ fs17.writeFileSync(podfilePath, content, "utf8");
4173
4242
  console.log("\u2705 Added Xcode compatibility build settings to Podfile post_install.");
4174
4243
  }
4175
4244
  }
@@ -4177,10 +4246,10 @@ $2`);
4177
4246
  const appNameFromConfig = resolved.config.ios?.appName;
4178
4247
  const candidatePaths = [];
4179
4248
  if (appNameFromConfig) {
4180
- candidatePaths.push(path17.join(iosProjectPath, appNameFromConfig, "LynxInitProcessor.swift"));
4249
+ candidatePaths.push(path18.join(iosProjectPath, appNameFromConfig, "LynxInitProcessor.swift"));
4181
4250
  }
4182
- candidatePaths.push(path17.join(iosProjectPath, "LynxInitProcessor.swift"));
4183
- const found = candidatePaths.find((p) => fs16.existsSync(p));
4251
+ candidatePaths.push(path18.join(iosProjectPath, "LynxInitProcessor.swift"));
4252
+ const found = candidatePaths.find((p) => fs17.existsSync(p));
4184
4253
  const lynxInitPath = found ?? candidatePaths[0];
4185
4254
  const iosPackages = packages.filter((p) => getIosModuleClassNames(p.config.ios).length > 0 || Object.keys(getIosElements(p.config.ios)).length > 0);
4186
4255
  const seenModules = /* @__PURE__ */ new Set();
@@ -4219,7 +4288,7 @@ $2`);
4219
4288
  const podName = resolvePodName(pkg);
4220
4289
  return `import ${podName}`;
4221
4290
  }).join("\n");
4222
- const fileContent = fs16.readFileSync(filePath, "utf8");
4291
+ const fileContent = fs17.readFileSync(filePath, "utf8");
4223
4292
  if (fileContent.indexOf(startMarker) !== -1) {
4224
4293
  updateGeneratedSection(filePath, imports, startMarker, endMarker);
4225
4294
  return;
@@ -4256,8 +4325,8 @@ ${after}`;
4256
4325
  ${fileContent}`;
4257
4326
  }
4258
4327
  }
4259
- fs16.writeFileSync(filePath, newContent, "utf8");
4260
- console.log(`\u2705 Updated imports in ${path17.basename(filePath)}`);
4328
+ fs17.writeFileSync(filePath, newContent, "utf8");
4329
+ console.log(`\u2705 Updated imports in ${path18.basename(filePath)}`);
4261
4330
  }
4262
4331
  updateImportsSection(lynxInitPath, importPackages);
4263
4332
  if (importPackages.length === 0) {
@@ -4301,7 +4370,7 @@ ${androidNames.map((n) => ` "${n.replace(/\\/g, "\\\\").replace(/"/g,
4301
4370
  } else {
4302
4371
  devClientSupportedBody = " // @tamer4lynx/tamer-dev-client not linked";
4303
4372
  }
4304
- if (fs16.readFileSync(lynxInitPath, "utf8").includes("GENERATED DEV_CLIENT_SUPPORTED START")) {
4373
+ if (fs17.readFileSync(lynxInitPath, "utf8").includes("GENERATED DEV_CLIENT_SUPPORTED START")) {
4305
4374
  updateGeneratedSection(lynxInitPath, devClientSupportedBody, "// GENERATED DEV_CLIENT_SUPPORTED START", "// GENERATED DEV_CLIENT_SUPPORTED END");
4306
4375
  }
4307
4376
  }
@@ -4309,13 +4378,13 @@ ${androidNames.map((n) => ` "${n.replace(/\\/g, "\\\\").replace(/"/g,
4309
4378
  const appNameFromConfig = resolved.config.ios?.appName;
4310
4379
  const candidates = [];
4311
4380
  if (appNameFromConfig) {
4312
- candidates.push(path17.join(iosProjectPath, appNameFromConfig, "Info.plist"));
4381
+ candidates.push(path18.join(iosProjectPath, appNameFromConfig, "Info.plist"));
4313
4382
  }
4314
- candidates.push(path17.join(iosProjectPath, "Info.plist"));
4315
- return candidates.find((p) => fs16.existsSync(p)) ?? null;
4383
+ candidates.push(path18.join(iosProjectPath, "Info.plist"));
4384
+ return candidates.find((p) => fs17.existsSync(p)) ?? null;
4316
4385
  }
4317
4386
  function readPlistXml(plistPath) {
4318
- return fs16.readFileSync(plistPath, "utf8");
4387
+ return fs17.readFileSync(plistPath, "utf8");
4319
4388
  }
4320
4389
  function syncInfoPlistPermissions(packages) {
4321
4390
  const plistPath = findInfoPlist();
@@ -4346,7 +4415,7 @@ ${androidNames.map((n) => ` "${n.replace(/\\/g, "\\\\").replace(/"/g,
4346
4415
  added++;
4347
4416
  }
4348
4417
  if (added > 0) {
4349
- fs16.writeFileSync(plistPath, plist, "utf8");
4418
+ fs17.writeFileSync(plistPath, plist, "utf8");
4350
4419
  console.log(`\u2705 Synced ${added} Info.plist permission description(s)`);
4351
4420
  }
4352
4421
  }
@@ -4393,16 +4462,16 @@ ${schemesXml}
4393
4462
  $1`
4394
4463
  );
4395
4464
  }
4396
- fs16.writeFileSync(plistPath, plist, "utf8");
4465
+ fs17.writeFileSync(plistPath, plist, "utf8");
4397
4466
  console.log(`\u2705 Synced ${urlSchemes.length} iOS URL scheme(s) into Info.plist`);
4398
4467
  }
4399
4468
  function runPodInstall(forcePath) {
4400
- const podfilePath = forcePath ?? path17.join(iosProjectPath, "Podfile");
4401
- if (!fs16.existsSync(podfilePath)) {
4469
+ const podfilePath = forcePath ?? path18.join(iosProjectPath, "Podfile");
4470
+ if (!fs17.existsSync(podfilePath)) {
4402
4471
  console.log("\u2139\uFE0F No Podfile found in ios directory; skipping `pod install`.");
4403
4472
  return;
4404
4473
  }
4405
- const cwd = path17.dirname(podfilePath);
4474
+ const cwd = path18.dirname(podfilePath);
4406
4475
  try {
4407
4476
  console.log(`\u2139\uFE0F Running \`pod install\` in ${cwd}...`);
4408
4477
  try {
@@ -4437,8 +4506,8 @@ $1`
4437
4506
  syncInfoPlistUrlSchemes();
4438
4507
  const appNameFromConfig = resolved.config.ios?.appName;
4439
4508
  if (appNameFromConfig) {
4440
- const appPodfile = path17.join(iosProjectPath, appNameFromConfig, "Podfile");
4441
- if (fs16.existsSync(appPodfile)) {
4509
+ const appPodfile = path18.join(iosProjectPath, appNameFromConfig, "Podfile");
4510
+ if (fs17.existsSync(appPodfile)) {
4442
4511
  runPodInstall(appPodfile);
4443
4512
  console.log("\u2728 Autolinking complete for iOS.");
4444
4513
  return;
@@ -4453,13 +4522,13 @@ $1`
4453
4522
  const appFolder = resolved.config.ios?.appName;
4454
4523
  if (!hasDevClient || !appFolder) return;
4455
4524
  const androidNames = getDedupedAndroidModuleClassNames(allPkgs);
4456
- const appDir = path17.join(iosProjectPath, appFolder);
4457
- fs16.mkdirSync(appDir, { recursive: true });
4458
- const manifestPath = path17.join(appDir, TAMER_HOST_NATIVE_MODULES_FILENAME);
4459
- fs16.writeFileSync(manifestPath, buildHostNativeModulesManifestJson(androidNames), "utf8");
4525
+ const appDir = path18.join(iosProjectPath, appFolder);
4526
+ fs17.mkdirSync(appDir, { recursive: true });
4527
+ const manifestPath = path18.join(appDir, TAMER_HOST_NATIVE_MODULES_FILENAME);
4528
+ fs17.writeFileSync(manifestPath, buildHostNativeModulesManifestJson(androidNames), "utf8");
4460
4529
  console.log(`\u2705 Wrote ${TAMER_HOST_NATIVE_MODULES_FILENAME} (native module ids for dev-client checks)`);
4461
- const pbxprojPath = path17.join(iosProjectPath, `${appFolder}.xcodeproj`, "project.pbxproj");
4462
- if (fs16.existsSync(pbxprojPath)) {
4530
+ const pbxprojPath = path18.join(iosProjectPath, `${appFolder}.xcodeproj`, "project.pbxproj");
4531
+ if (fs17.existsSync(pbxprojPath)) {
4463
4532
  addResourceToXcodeProject(pbxprojPath, appFolder, TAMER_HOST_NATIVE_MODULES_FILENAME);
4464
4533
  }
4465
4534
  }
@@ -4468,8 +4537,8 @@ $1`
4468
4537
  var autolink_default2 = autolink2;
4469
4538
 
4470
4539
  // src/ios/bundle.ts
4471
- import fs17 from "fs";
4472
- import path18 from "path";
4540
+ import fs18 from "fs";
4541
+ import path19 from "path";
4473
4542
  import { execSync as execSync7 } from "child_process";
4474
4543
  function bundleAndDeploy2(opts = {}) {
4475
4544
  const release = opts.release === true || opts.production === true;
@@ -4487,19 +4556,19 @@ function bundleAndDeploy2(opts = {}) {
4487
4556
  const includeDevClient = !release && !!devClientPkg;
4488
4557
  const appName = resolved.config.ios.appName;
4489
4558
  const sourceBundlePath = resolved.lynxBundlePath;
4490
- const destinationDir = path18.join(resolved.iosDir, appName);
4491
- const destinationBundlePath = path18.join(destinationDir, resolved.lynxBundleFile);
4559
+ const destinationDir = path19.join(resolved.iosDir, appName);
4560
+ const destinationBundlePath = path19.join(destinationDir, resolved.lynxBundleFile);
4492
4561
  autolink_default2({ release, includeDevClient });
4493
4562
  const iconPaths = resolveIconPaths(resolved.projectRoot, resolved.config);
4494
4563
  if (iconPaths) {
4495
- const appIconDir = path18.join(destinationDir, "Assets.xcassets", "AppIcon.appiconset");
4564
+ const appIconDir = path19.join(destinationDir, "Assets.xcassets", "AppIcon.appiconset");
4496
4565
  if (applyIosAppIconAssets(appIconDir, iconPaths)) {
4497
4566
  console.log("\u2705 Synced iOS AppIcon from tamer.config.json");
4498
4567
  }
4499
4568
  }
4500
4569
  try {
4501
- const lynxTsconfig = path18.join(resolved.lynxProjectDir, "tsconfig.json");
4502
- if (fs17.existsSync(lynxTsconfig)) {
4570
+ const lynxTsconfig = path19.join(resolved.lynxProjectDir, "tsconfig.json");
4571
+ if (fs18.existsSync(lynxTsconfig)) {
4503
4572
  fixTsconfigReferencesForBuild(lynxTsconfig);
4504
4573
  }
4505
4574
  console.log("\u{1F4E6} Building Lynx bundle...");
@@ -4510,40 +4579,40 @@ function bundleAndDeploy2(opts = {}) {
4510
4579
  process.exit(1);
4511
4580
  }
4512
4581
  try {
4513
- if (!fs17.existsSync(sourceBundlePath)) {
4582
+ if (!fs18.existsSync(sourceBundlePath)) {
4514
4583
  console.error(`\u274C Build output not found at: ${sourceBundlePath}`);
4515
4584
  process.exit(1);
4516
4585
  }
4517
- if (!fs17.existsSync(destinationDir)) {
4586
+ if (!fs18.existsSync(destinationDir)) {
4518
4587
  console.error(`Destination directory not found at: ${destinationDir}`);
4519
4588
  process.exit(1);
4520
4589
  }
4521
- const distDir = path18.dirname(sourceBundlePath);
4590
+ const distDir = path19.dirname(sourceBundlePath);
4522
4591
  console.log(`\u{1F69A} Copying bundle and assets to iOS project...`);
4523
4592
  copyDistAssets(distDir, destinationDir, resolved.lynxBundleFile);
4524
4593
  console.log(`\u2728 Successfully copied bundle to: ${destinationBundlePath}`);
4525
- const pbxprojPath = path18.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
4526
- if (fs17.existsSync(pbxprojPath)) {
4594
+ const pbxprojPath = path19.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
4595
+ if (fs18.existsSync(pbxprojPath)) {
4527
4596
  const skip = /* @__PURE__ */ new Set([".rspeedy", "stats.json"]);
4528
- for (const entry of fs17.readdirSync(distDir)) {
4529
- if (skip.has(entry) || fs17.statSync(path18.join(distDir, entry)).isDirectory()) continue;
4597
+ for (const entry of fs18.readdirSync(distDir)) {
4598
+ if (skip.has(entry) || fs18.statSync(path19.join(distDir, entry)).isDirectory()) continue;
4530
4599
  addResourceToXcodeProject(pbxprojPath, appName, entry);
4531
4600
  }
4532
4601
  }
4533
4602
  if (includeDevClient && devClientPkg) {
4534
- const devClientBundle = path18.join(destinationDir, "dev-client.lynx.bundle");
4603
+ const devClientBundle = path19.join(destinationDir, "dev-client.lynx.bundle");
4535
4604
  console.log("\u{1F4E6} Building dev-client bundle...");
4536
4605
  try {
4537
4606
  execSync7("npm run build", { stdio: "inherit", cwd: devClientPkg });
4538
4607
  } catch {
4539
4608
  console.warn("\u26A0\uFE0F dev-client build failed; skipping dev-client bundle");
4540
4609
  }
4541
- const builtBundle = path18.join(devClientPkg, "dist", "dev-client.lynx.bundle");
4542
- if (fs17.existsSync(builtBundle)) {
4543
- fs17.copyFileSync(builtBundle, devClientBundle);
4610
+ const builtBundle = path19.join(devClientPkg, "dist", "dev-client.lynx.bundle");
4611
+ if (fs18.existsSync(builtBundle)) {
4612
+ fs18.copyFileSync(builtBundle, devClientBundle);
4544
4613
  console.log("\u2728 Copied dev-client.lynx.bundle to iOS project");
4545
- const pbxprojPath2 = path18.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
4546
- if (fs17.existsSync(pbxprojPath2)) {
4614
+ const pbxprojPath2 = path19.join(resolved.iosDir, `${appName}.xcodeproj`, "project.pbxproj");
4615
+ if (fs18.existsSync(pbxprojPath2)) {
4547
4616
  addResourceToXcodeProject(pbxprojPath2, appName, "dev-client.lynx.bundle");
4548
4617
  }
4549
4618
  }
@@ -4557,8 +4626,8 @@ function bundleAndDeploy2(opts = {}) {
4557
4626
  var bundle_default2 = bundleAndDeploy2;
4558
4627
 
4559
4628
  // src/ios/build.ts
4560
- import fs18 from "fs";
4561
- import path19 from "path";
4629
+ import fs19 from "fs";
4630
+ import path20 from "path";
4562
4631
  import os3 from "os";
4563
4632
  import { randomBytes as randomBytes2 } from "crypto";
4564
4633
  import { execSync as execSync8 } from "child_process";
@@ -4579,14 +4648,14 @@ function findBootedSimulator() {
4579
4648
  return null;
4580
4649
  }
4581
4650
  function findFirstConnectedIosDeviceUdid() {
4582
- const jsonPath = path19.join(os3.tmpdir(), `t4l-devicectl-${randomBytes2(8).toString("hex")}.json`);
4651
+ const jsonPath = path20.join(os3.tmpdir(), `t4l-devicectl-${randomBytes2(8).toString("hex")}.json`);
4583
4652
  try {
4584
4653
  execSync8(`xcrun devicectl list devices --json-output "${jsonPath}"`, {
4585
4654
  stdio: ["pipe", "pipe", "pipe"]
4586
4655
  });
4587
- if (!fs18.existsSync(jsonPath)) return null;
4588
- const raw = fs18.readFileSync(jsonPath, "utf8");
4589
- fs18.unlinkSync(jsonPath);
4656
+ if (!fs19.existsSync(jsonPath)) return null;
4657
+ const raw = fs19.readFileSync(jsonPath, "utf8");
4658
+ fs19.unlinkSync(jsonPath);
4590
4659
  const data = JSON.parse(raw);
4591
4660
  const devices = data.result?.devices ?? [];
4592
4661
  for (const d of devices) {
@@ -4597,7 +4666,7 @@ function findFirstConnectedIosDeviceUdid() {
4597
4666
  }
4598
4667
  } catch {
4599
4668
  try {
4600
- if (fs18.existsSync(jsonPath)) fs18.unlinkSync(jsonPath);
4669
+ if (fs19.existsSync(jsonPath)) fs19.unlinkSync(jsonPath);
4601
4670
  } catch {
4602
4671
  }
4603
4672
  }
@@ -4615,11 +4684,11 @@ async function buildIpa(opts = {}) {
4615
4684
  const configuration = release ? "Release" : "Debug";
4616
4685
  bundle_default2({ release, production: opts.production });
4617
4686
  const scheme = appName;
4618
- const workspacePath = path19.join(iosDir, `${appName}.xcworkspace`);
4619
- const projectPath = path19.join(iosDir, `${appName}.xcodeproj`);
4620
- const xcproject = fs18.existsSync(workspacePath) ? workspacePath : projectPath;
4687
+ const workspacePath = path20.join(iosDir, `${appName}.xcworkspace`);
4688
+ const projectPath = path20.join(iosDir, `${appName}.xcodeproj`);
4689
+ const xcproject = fs19.existsSync(workspacePath) ? workspacePath : projectPath;
4621
4690
  const flag = xcproject.endsWith(".xcworkspace") ? "-workspace" : "-project";
4622
- const derivedDataPath = path19.join(iosDir, "build");
4691
+ const derivedDataPath = path20.join(iosDir, "build");
4623
4692
  const production = opts.production === true;
4624
4693
  const sdk = production ? "iphoneos" : opts.install ? "iphonesimulator" : "iphoneos";
4625
4694
  const signingArgs = production || opts.install ? "" : " CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO";
@@ -4638,14 +4707,14 @@ async function buildIpa(opts = {}) {
4638
4707
  console.log(`\u2705 Build completed.`);
4639
4708
  if (opts.install) {
4640
4709
  if (production) {
4641
- const appPath = path19.join(
4710
+ const appPath = path20.join(
4642
4711
  derivedDataPath,
4643
4712
  "Build",
4644
4713
  "Products",
4645
4714
  `${configuration}-iphoneos`,
4646
4715
  `${appName}.app`
4647
4716
  );
4648
- if (!fs18.existsSync(appPath)) {
4717
+ if (!fs19.existsSync(appPath)) {
4649
4718
  console.error(`\u274C Built app not found at: ${appPath}`);
4650
4719
  process.exit(1);
4651
4720
  }
@@ -4675,14 +4744,14 @@ async function buildIpa(opts = {}) {
4675
4744
  console.log('\u2705 App installed. (Set "ios.bundleId" in tamer.config.json to auto-launch.)');
4676
4745
  }
4677
4746
  } else {
4678
- const appGlob = path19.join(
4747
+ const appGlob = path20.join(
4679
4748
  derivedDataPath,
4680
4749
  "Build",
4681
4750
  "Products",
4682
4751
  `${configuration}-iphonesimulator`,
4683
4752
  `${appName}.app`
4684
4753
  );
4685
- if (!fs18.existsSync(appGlob)) {
4754
+ if (!fs19.existsSync(appGlob)) {
4686
4755
  console.error(`\u274C Built app not found at: ${appGlob}`);
4687
4756
  process.exit(1);
4688
4757
  }
@@ -4706,8 +4775,8 @@ async function buildIpa(opts = {}) {
4706
4775
  var build_default2 = buildIpa;
4707
4776
 
4708
4777
  // src/common/init.tsx
4709
- import fs19 from "fs";
4710
- import path20 from "path";
4778
+ import fs20 from "fs";
4779
+ import path21 from "path";
4711
4780
  import { useState as useState4, useEffect as useEffect2, useCallback as useCallback3 } from "react";
4712
4781
  import { render, Text as Text9, Box as Box8 } from "ink";
4713
4782
 
@@ -5025,22 +5094,22 @@ function InitWizard() {
5025
5094
  paths: { androidDir: "android", iosDir: "ios" }
5026
5095
  };
5027
5096
  if (lynxProject.trim()) config.lynxProject = lynxProject.trim();
5028
- const configPath = path20.join(process.cwd(), "tamer.config.json");
5029
- fs19.writeFileSync(configPath, JSON.stringify(config, null, 2));
5097
+ const configPath = path21.join(process.cwd(), "tamer.config.json");
5098
+ fs20.writeFileSync(configPath, JSON.stringify(config, null, 2));
5030
5099
  const lines = [`Generated tamer.config.json at ${configPath}`];
5031
5100
  const tamerTypesInclude = "node_modules/@tamer4lynx/tamer-*/src/**/*.d.ts";
5032
5101
  const tsconfigCandidates = lynxProject.trim() ? [
5033
- path20.join(process.cwd(), lynxProject.trim(), "tsconfig.json"),
5034
- path20.join(process.cwd(), "tsconfig.json")
5035
- ] : [path20.join(process.cwd(), "tsconfig.json")];
5102
+ path21.join(process.cwd(), lynxProject.trim(), "tsconfig.json"),
5103
+ path21.join(process.cwd(), "tsconfig.json")
5104
+ ] : [path21.join(process.cwd(), "tsconfig.json")];
5036
5105
  for (const tsconfigPath of tsconfigCandidates) {
5037
- if (!fs19.existsSync(tsconfigPath)) continue;
5106
+ if (!fs20.existsSync(tsconfigPath)) continue;
5038
5107
  try {
5039
5108
  if (fixTsconfigReferencesForBuild(tsconfigPath)) {
5040
- lines.push(`Flattened ${path20.relative(process.cwd(), tsconfigPath)} (fixed TS6310)`);
5109
+ lines.push(`Flattened ${path21.relative(process.cwd(), tsconfigPath)} (fixed TS6310)`);
5041
5110
  }
5042
5111
  if (addTamerTypesInclude(tsconfigPath, tamerTypesInclude)) {
5043
- lines.push(`Updated ${path20.relative(process.cwd(), tsconfigPath)} for tamer types`);
5112
+ lines.push(`Updated ${path21.relative(process.cwd(), tsconfigPath)} for tamer types`);
5044
5113
  }
5045
5114
  break;
5046
5115
  } catch (e) {
@@ -5200,8 +5269,8 @@ async function init() {
5200
5269
  }
5201
5270
 
5202
5271
  // src/common/create.ts
5203
- import fs20 from "fs";
5204
- import path21 from "path";
5272
+ import fs21 from "fs";
5273
+ import path22 from "path";
5205
5274
  import readline from "readline";
5206
5275
  var rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false });
5207
5276
  function ask(question) {
@@ -5263,13 +5332,13 @@ async function create3(opts) {
5263
5332
  const simpleModuleName = extName.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("") + "Module";
5264
5333
  const fullModuleClassName = `${packageName}.${simpleModuleName}`;
5265
5334
  const cwd = process.cwd();
5266
- const root = path21.join(cwd, extName);
5267
- if (fs20.existsSync(root)) {
5335
+ const root = path22.join(cwd, extName);
5336
+ if (fs21.existsSync(root)) {
5268
5337
  console.error(`\u274C Directory ${extName} already exists.`);
5269
5338
  rl.close();
5270
5339
  process.exit(1);
5271
5340
  }
5272
- fs20.mkdirSync(root, { recursive: true });
5341
+ fs21.mkdirSync(root, { recursive: true });
5273
5342
  const lynxExt = {
5274
5343
  platforms: {
5275
5344
  android: {
@@ -5284,7 +5353,7 @@ async function create3(opts) {
5284
5353
  web: {}
5285
5354
  }
5286
5355
  };
5287
- fs20.writeFileSync(path21.join(root, "lynx.ext.json"), JSON.stringify(lynxExt, null, 2));
5356
+ fs21.writeFileSync(path22.join(root, "lynx.ext.json"), JSON.stringify(lynxExt, null, 2));
5288
5357
  const pkg = {
5289
5358
  name: extName,
5290
5359
  version: "0.0.1",
@@ -5297,20 +5366,20 @@ async function create3(opts) {
5297
5366
  engines: { node: ">=18" }
5298
5367
  };
5299
5368
  if (includeModule) pkg.types = "src/index.d.ts";
5300
- fs20.writeFileSync(path21.join(root, "package.json"), JSON.stringify(pkg, null, 2));
5369
+ fs21.writeFileSync(path22.join(root, "package.json"), JSON.stringify(pkg, null, 2));
5301
5370
  const pkgPath = packageName.replace(/\./g, "/");
5302
5371
  const hasSrc = includeModule || includeElement || includeService;
5303
5372
  if (hasSrc) {
5304
- fs20.mkdirSync(path21.join(root, "src"), { recursive: true });
5373
+ fs21.mkdirSync(path22.join(root, "src"), { recursive: true });
5305
5374
  }
5306
5375
  if (includeModule) {
5307
- fs20.writeFileSync(path21.join(root, "src", "index.d.ts"), `/** @lynxmodule */
5376
+ fs21.writeFileSync(path22.join(root, "src", "index.d.ts"), `/** @lynxmodule */
5308
5377
  export declare class ${simpleModuleName} {
5309
5378
  // Add your module methods here
5310
5379
  }
5311
5380
  `);
5312
- fs20.mkdirSync(path21.join(root, "android", "src", "main", "kotlin", pkgPath), { recursive: true });
5313
- fs20.writeFileSync(path21.join(root, "android", "build.gradle.kts"), `plugins {
5381
+ fs21.mkdirSync(path22.join(root, "android", "src", "main", "kotlin", pkgPath), { recursive: true });
5382
+ fs21.writeFileSync(path22.join(root, "android", "build.gradle.kts"), `plugins {
5314
5383
  id("com.android.library")
5315
5384
  id("org.jetbrains.kotlin.android")
5316
5385
  }
@@ -5331,7 +5400,7 @@ dependencies {
5331
5400
  implementation(libs.lynx.jssdk)
5332
5401
  }
5333
5402
  `);
5334
- fs20.writeFileSync(path21.join(root, "android", "src", "main", "AndroidManifest.xml"), `<?xml version="1.0" encoding="utf-8"?>
5403
+ fs21.writeFileSync(path22.join(root, "android", "src", "main", "AndroidManifest.xml"), `<?xml version="1.0" encoding="utf-8"?>
5335
5404
  <manifest />
5336
5405
  `);
5337
5406
  const ktContent = `package ${packageName}
@@ -5348,8 +5417,8 @@ class ${simpleModuleName}(context: Context) : LynxModule(context) {
5348
5417
  }
5349
5418
  }
5350
5419
  `;
5351
- fs20.writeFileSync(path21.join(root, "android", "src", "main", "kotlin", pkgPath, `${simpleModuleName}.kt`), ktContent);
5352
- fs20.mkdirSync(path21.join(root, "ios", extName, extName, "Classes"), { recursive: true });
5420
+ fs21.writeFileSync(path22.join(root, "android", "src", "main", "kotlin", pkgPath, `${simpleModuleName}.kt`), ktContent);
5421
+ fs21.mkdirSync(path22.join(root, "ios", extName, extName, "Classes"), { recursive: true });
5353
5422
  const podspec = `Pod::Spec.new do |s|
5354
5423
  s.name = '${extName}'
5355
5424
  s.version = '0.0.1'
@@ -5363,7 +5432,7 @@ class ${simpleModuleName}(context: Context) : LynxModule(context) {
5363
5432
  s.dependency 'Lynx'
5364
5433
  end
5365
5434
  `;
5366
- fs20.writeFileSync(path21.join(root, "ios", extName, `${extName}.podspec`), podspec);
5435
+ fs21.writeFileSync(path22.join(root, "ios", extName, `${extName}.podspec`), podspec);
5367
5436
  const swiftContent = `import Foundation
5368
5437
 
5369
5438
  @objc public class ${simpleModuleName}: NSObject {
@@ -5372,18 +5441,18 @@ end
5372
5441
  }
5373
5442
  }
5374
5443
  `;
5375
- fs20.writeFileSync(path21.join(root, "ios", extName, extName, "Classes", `${simpleModuleName}.swift`), swiftContent);
5444
+ fs21.writeFileSync(path22.join(root, "ios", extName, extName, "Classes", `${simpleModuleName}.swift`), swiftContent);
5376
5445
  }
5377
5446
  if (includeElement && !includeModule) {
5378
5447
  const elementName = extName.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
5379
- fs20.writeFileSync(path21.join(root, "src", "index.tsx"), `import type { FC } from '@lynx-js/react';
5448
+ fs21.writeFileSync(path22.join(root, "src", "index.tsx"), `import type { FC } from '@lynx-js/react';
5380
5449
 
5381
5450
  export const ${elementName}: FC = () => {
5382
5451
  return null;
5383
5452
  };
5384
5453
  `);
5385
5454
  }
5386
- fs20.writeFileSync(path21.join(root, "index.js"), `'use strict';
5455
+ fs21.writeFileSync(path22.join(root, "index.js"), `'use strict';
5387
5456
  module.exports = {};
5388
5457
  `);
5389
5458
  const tsconfigCompiler = {
@@ -5396,11 +5465,11 @@ module.exports = {};
5396
5465
  tsconfigCompiler.jsx = "preserve";
5397
5466
  tsconfigCompiler.jsxImportSource = "@lynx-js/react";
5398
5467
  }
5399
- fs20.writeFileSync(path21.join(root, "tsconfig.json"), JSON.stringify({
5468
+ fs21.writeFileSync(path22.join(root, "tsconfig.json"), JSON.stringify({
5400
5469
  compilerOptions: tsconfigCompiler,
5401
5470
  include: includeElement ? ["src", "src/**/*.tsx"] : ["src"]
5402
5471
  }, null, 2));
5403
- fs20.writeFileSync(path21.join(root, "README.md"), `# ${extName}
5472
+ fs21.writeFileSync(path22.join(root, "README.md"), `# ${extName}
5404
5473
 
5405
5474
  Lynx extension for ${extName}.
5406
5475
 
@@ -5425,8 +5494,8 @@ This package uses \`lynx.ext.json\` (RFC-compliant) for autolinking.
5425
5494
  var create_default3 = create3;
5426
5495
 
5427
5496
  // src/common/codegen.ts
5428
- import fs21 from "fs";
5429
- import path22 from "path";
5497
+ import fs22 from "fs";
5498
+ import path23 from "path";
5430
5499
  function codegen() {
5431
5500
  const cwd = process.cwd();
5432
5501
  const config = loadExtensionConfig(cwd);
@@ -5434,9 +5503,9 @@ function codegen() {
5434
5503
  console.error("\u274C No lynx.ext.json or tamer.json found. Run from an extension package root.");
5435
5504
  process.exit(1);
5436
5505
  }
5437
- const srcDir = path22.join(cwd, "src");
5438
- const generatedDir = path22.join(cwd, "generated");
5439
- fs21.mkdirSync(generatedDir, { recursive: true });
5506
+ const srcDir = path23.join(cwd, "src");
5507
+ const generatedDir = path23.join(cwd, "generated");
5508
+ fs22.mkdirSync(generatedDir, { recursive: true });
5440
5509
  const dtsFiles = findDtsFiles(srcDir);
5441
5510
  const modules = extractLynxModules(dtsFiles);
5442
5511
  if (modules.length === 0) {
@@ -5446,28 +5515,28 @@ function codegen() {
5446
5515
  for (const mod of modules) {
5447
5516
  const tsContent = `export type { ${mod} } from '../src/index.js';
5448
5517
  `;
5449
- const outPath = path22.join(generatedDir, `${mod}.ts`);
5450
- fs21.writeFileSync(outPath, tsContent);
5518
+ const outPath = path23.join(generatedDir, `${mod}.ts`);
5519
+ fs22.writeFileSync(outPath, tsContent);
5451
5520
  console.log(`\u2705 Generated ${outPath}`);
5452
5521
  }
5453
5522
  if (config.android) {
5454
- const androidGenerated = path22.join(cwd, "android", "src", "main", "kotlin", config.android.moduleClassName.replace(/\./g, "/").replace(/[^/]+$/, ""), "generated");
5455
- fs21.mkdirSync(androidGenerated, { recursive: true });
5523
+ const androidGenerated = path23.join(cwd, "android", "src", "main", "kotlin", config.android.moduleClassName.replace(/\./g, "/").replace(/[^/]+$/, ""), "generated");
5524
+ fs22.mkdirSync(androidGenerated, { recursive: true });
5456
5525
  console.log(`\u2139\uFE0F Android generated dir: ${androidGenerated} (spec generation coming soon)`);
5457
5526
  }
5458
5527
  if (config.ios) {
5459
- const iosGenerated = path22.join(cwd, "ios", "generated");
5460
- fs21.mkdirSync(iosGenerated, { recursive: true });
5528
+ const iosGenerated = path23.join(cwd, "ios", "generated");
5529
+ fs22.mkdirSync(iosGenerated, { recursive: true });
5461
5530
  console.log(`\u2139\uFE0F iOS generated dir: ${iosGenerated} (spec generation coming soon)`);
5462
5531
  }
5463
5532
  console.log("\u2728 Codegen complete.");
5464
5533
  }
5465
5534
  function findDtsFiles(dir) {
5466
5535
  const result = [];
5467
- if (!fs21.existsSync(dir)) return result;
5468
- const entries = fs21.readdirSync(dir, { withFileTypes: true });
5536
+ if (!fs22.existsSync(dir)) return result;
5537
+ const entries = fs22.readdirSync(dir, { withFileTypes: true });
5469
5538
  for (const e of entries) {
5470
- const full = path22.join(dir, e.name);
5539
+ const full = path23.join(dir, e.name);
5471
5540
  if (e.isDirectory()) result.push(...findDtsFiles(full));
5472
5541
  else if (e.name.endsWith(".d.ts")) result.push(full);
5473
5542
  }
@@ -5477,7 +5546,7 @@ function extractLynxModules(files) {
5477
5546
  const modules = [];
5478
5547
  const seen = /* @__PURE__ */ new Set();
5479
5548
  for (const file of files) {
5480
- const content = fs21.readFileSync(file, "utf8");
5549
+ const content = fs22.readFileSync(file, "utf8");
5481
5550
  const regex = /\/\*\*\s*@lynxmodule\s*\*\/\s*export\s+declare\s+class\s+(\w+)/g;
5482
5551
  let m;
5483
5552
  while ((m = regex.exec(content)) !== null) {
@@ -5494,10 +5563,10 @@ var codegen_default = codegen;
5494
5563
  // src/common/devServer.tsx
5495
5564
  import { useState as useState5, useEffect as useEffect3, useRef, useCallback as useCallback4 } from "react";
5496
5565
  import { spawn } from "child_process";
5497
- import fs22 from "fs";
5566
+ import fs23 from "fs";
5498
5567
  import http from "http";
5499
5568
  import os4 from "os";
5500
- import path23 from "path";
5569
+ import path24 from "path";
5501
5570
  import { render as render2, useInput, useApp } from "ink";
5502
5571
  import { WebSocket, WebSocketServer } from "ws";
5503
5572
  import { jsx as jsx10 } from "react/jsx-runtime";
@@ -5515,13 +5584,13 @@ var STATIC_MIME = {
5515
5584
  ".pdf": "application/pdf"
5516
5585
  };
5517
5586
  function sendFileFromDisk(res, absPath) {
5518
- fs22.readFile(absPath, (err, data) => {
5587
+ fs23.readFile(absPath, (err, data) => {
5519
5588
  if (err) {
5520
5589
  res.writeHead(404);
5521
5590
  res.end("Not found");
5522
5591
  return;
5523
5592
  }
5524
- const ext = path23.extname(absPath).toLowerCase();
5593
+ const ext = path24.extname(absPath).toLowerCase();
5525
5594
  res.setHeader("Content-Type", STATIC_MIME[ext] ?? "application/octet-stream");
5526
5595
  res.setHeader("Access-Control-Allow-Origin", "*");
5527
5596
  res.end(data);
@@ -5558,9 +5627,9 @@ function getLanIp() {
5558
5627
  return "localhost";
5559
5628
  }
5560
5629
  function detectPackageManager(cwd) {
5561
- const dir = path23.resolve(cwd);
5562
- if (fs22.existsSync(path23.join(dir, "pnpm-lock.yaml"))) return { cmd: "pnpm", args: ["run", "build"] };
5563
- if (fs22.existsSync(path23.join(dir, "bun.lockb")) || fs22.existsSync(path23.join(dir, "bun.lock")))
5630
+ const dir = path24.resolve(cwd);
5631
+ if (fs23.existsSync(path24.join(dir, "pnpm-lock.yaml"))) return { cmd: "pnpm", args: ["run", "build"] };
5632
+ if (fs23.existsSync(path24.join(dir, "bun.lockb")) || fs23.existsSync(path24.join(dir, "bun.lock")))
5564
5633
  return { cmd: "bun", args: ["run", "build"] };
5565
5634
  return { cmd: "npm", args: ["run", "build"] };
5566
5635
  }
@@ -5647,8 +5716,8 @@ function DevServerApp({ verbose }) {
5647
5716
  try {
5648
5717
  const resolved = resolveHostPaths();
5649
5718
  const { projectRoot, lynxProjectDir, lynxBundlePath, lynxBundleFile, config } = resolved;
5650
- const distDir = path23.dirname(lynxBundlePath);
5651
- const projectName = path23.basename(lynxProjectDir);
5719
+ const distDir = path24.dirname(lynxBundlePath);
5720
+ const projectName = path24.basename(lynxProjectDir);
5652
5721
  const basePath = `/${projectName}`;
5653
5722
  setUi((s) => ({ ...s, projectName, lynxBundleFile }));
5654
5723
  const preferredPort = config.devServer?.port ?? config.devServer?.httpPort ?? DEFAULT_PORT;
@@ -5658,18 +5727,18 @@ function DevServerApp({ verbose }) {
5658
5727
  }
5659
5728
  const iconPaths = resolveIconPaths(projectRoot, config);
5660
5729
  let iconFilePath = null;
5661
- if (iconPaths?.source && fs22.statSync(iconPaths.source).isFile()) {
5730
+ if (iconPaths?.source && fs23.statSync(iconPaths.source).isFile()) {
5662
5731
  iconFilePath = iconPaths.source;
5663
- } else if (iconPaths?.androidAdaptiveForeground && fs22.statSync(iconPaths.androidAdaptiveForeground).isFile()) {
5732
+ } else if (iconPaths?.androidAdaptiveForeground && fs23.statSync(iconPaths.androidAdaptiveForeground).isFile()) {
5664
5733
  iconFilePath = iconPaths.androidAdaptiveForeground;
5665
5734
  } else if (iconPaths?.android) {
5666
- const androidIcon = path23.join(iconPaths.android, "mipmap-xxxhdpi", "ic_launcher.png");
5667
- if (fs22.existsSync(androidIcon)) iconFilePath = androidIcon;
5735
+ const androidIcon = path24.join(iconPaths.android, "mipmap-xxxhdpi", "ic_launcher.png");
5736
+ if (fs23.existsSync(androidIcon)) iconFilePath = androidIcon;
5668
5737
  } else if (iconPaths?.ios) {
5669
- const iosIcon = path23.join(iconPaths.ios, "Icon-1024.png");
5670
- if (fs22.existsSync(iosIcon)) iconFilePath = iosIcon;
5738
+ const iosIcon = path24.join(iconPaths.ios, "Icon-1024.png");
5739
+ if (fs23.existsSync(iosIcon)) iconFilePath = iosIcon;
5671
5740
  }
5672
- const iconExt = iconFilePath ? path23.extname(iconFilePath) || ".png" : "";
5741
+ const iconExt = iconFilePath ? path24.extname(iconFilePath) || ".png" : "";
5673
5742
  const runBuild = () => {
5674
5743
  return new Promise((resolve, reject) => {
5675
5744
  const { cmd, args } = detectPackageManager(lynxProjectDir);
@@ -5751,7 +5820,7 @@ function DevServerApp({ verbose }) {
5751
5820
  return;
5752
5821
  }
5753
5822
  if (iconFilePath && (reqPath === `${basePath}/icon` || reqPath === `${basePath}/icon${iconExt}`)) {
5754
- fs22.readFile(iconFilePath, (err, data) => {
5823
+ fs23.readFile(iconFilePath, (err, data) => {
5755
5824
  if (err) {
5756
5825
  res.writeHead(404);
5757
5826
  res.end();
@@ -5777,20 +5846,20 @@ function DevServerApp({ verbose }) {
5777
5846
  res.end();
5778
5847
  return;
5779
5848
  }
5780
- const safe = path23.normalize(rel).replace(/^(\.\.(\/|\\|$))+/, "");
5781
- if (path23.isAbsolute(safe) || safe.startsWith("..")) {
5849
+ const safe = path24.normalize(rel).replace(/^(\.\.(\/|\\|$))+/, "");
5850
+ if (path24.isAbsolute(safe) || safe.startsWith("..")) {
5782
5851
  res.writeHead(403);
5783
5852
  res.end();
5784
5853
  return;
5785
5854
  }
5786
- const allowedRoot = path23.resolve(lynxProjectDir, rootSub);
5787
- const abs = path23.resolve(allowedRoot, safe);
5788
- if (!abs.startsWith(allowedRoot + path23.sep) && abs !== allowedRoot) {
5855
+ const allowedRoot = path24.resolve(lynxProjectDir, rootSub);
5856
+ const abs = path24.resolve(allowedRoot, safe);
5857
+ if (!abs.startsWith(allowedRoot + path24.sep) && abs !== allowedRoot) {
5789
5858
  res.writeHead(403);
5790
5859
  res.end();
5791
5860
  return;
5792
5861
  }
5793
- if (!fs22.existsSync(abs) || !fs22.statSync(abs).isFile()) {
5862
+ if (!fs23.existsSync(abs) || !fs23.statSync(abs).isFile()) {
5794
5863
  res.writeHead(404);
5795
5864
  res.end("Not found");
5796
5865
  return;
@@ -5804,14 +5873,14 @@ function DevServerApp({ verbose }) {
5804
5873
  reqPath = basePath + (reqPath.startsWith("/") ? reqPath : "/" + reqPath);
5805
5874
  }
5806
5875
  const relPath = reqPath.replace(basePath, "").replace(/^\//, "") || lynxBundleFile;
5807
- const filePath = path23.resolve(distDir, relPath);
5808
- const distResolved = path23.resolve(distDir);
5809
- if (!filePath.startsWith(distResolved + path23.sep) && filePath !== distResolved) {
5876
+ const filePath = path24.resolve(distDir, relPath);
5877
+ const distResolved = path24.resolve(distDir);
5878
+ if (!filePath.startsWith(distResolved + path24.sep) && filePath !== distResolved) {
5810
5879
  res.writeHead(403);
5811
5880
  res.end();
5812
5881
  return;
5813
5882
  }
5814
- fs22.readFile(filePath, (err, data) => {
5883
+ fs23.readFile(filePath, (err, data) => {
5815
5884
  if (err) {
5816
5885
  res.writeHead(404);
5817
5886
  res.end("Not found");
@@ -5883,10 +5952,10 @@ function DevServerApp({ verbose }) {
5883
5952
  }
5884
5953
  if (chokidar) {
5885
5954
  const watchPaths = [
5886
- path23.join(lynxProjectDir, "src"),
5887
- path23.join(lynxProjectDir, "lynx.config.ts"),
5888
- path23.join(lynxProjectDir, "lynx.config.js")
5889
- ].filter((p) => fs22.existsSync(p));
5955
+ path24.join(lynxProjectDir, "src"),
5956
+ path24.join(lynxProjectDir, "lynx.config.ts"),
5957
+ path24.join(lynxProjectDir, "lynx.config.js")
5958
+ ].filter((p) => fs23.existsSync(p));
5890
5959
  if (watchPaths.length > 0) {
5891
5960
  const w = chokidar.watch(watchPaths, { ignoreInitial: true });
5892
5961
  w.on("change", async () => {
@@ -6006,10 +6075,10 @@ async function start(opts) {
6006
6075
  var start_default = start;
6007
6076
 
6008
6077
  // src/common/injectHost.ts
6009
- import fs23 from "fs";
6010
- import path24 from "path";
6078
+ import fs24 from "fs";
6079
+ import path25 from "path";
6011
6080
  function readAndSubstitute(templatePath, vars) {
6012
- const raw = fs23.readFileSync(templatePath, "utf-8");
6081
+ const raw = fs24.readFileSync(templatePath, "utf-8");
6013
6082
  return Object.entries(vars).reduce(
6014
6083
  (s, [k, v]) => s.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v),
6015
6084
  raw
@@ -6030,32 +6099,32 @@ async function injectHostAndroid(opts) {
6030
6099
  process.exit(1);
6031
6100
  }
6032
6101
  const androidDir = config.paths?.androidDir ?? "android";
6033
- const rootDir = path24.join(projectRoot, androidDir);
6102
+ const rootDir = path25.join(projectRoot, androidDir);
6034
6103
  const packagePath = packageName.replace(/\./g, "/");
6035
- const javaDir = path24.join(rootDir, "app", "src", "main", "java", packagePath);
6036
- const kotlinDir = path24.join(rootDir, "app", "src", "main", "kotlin", packagePath);
6037
- if (!fs23.existsSync(javaDir) || !fs23.existsSync(kotlinDir)) {
6104
+ const javaDir = path25.join(rootDir, "app", "src", "main", "java", packagePath);
6105
+ const kotlinDir = path25.join(rootDir, "app", "src", "main", "kotlin", packagePath);
6106
+ if (!fs24.existsSync(javaDir) || !fs24.existsSync(kotlinDir)) {
6038
6107
  console.error("\u274C Android project not found. Run `t4l android create` first or ensure android/ exists.");
6039
6108
  process.exit(1);
6040
6109
  }
6041
- const templateDir = path24.join(hostPkg, "android", "templates");
6110
+ const templateDir = path25.join(hostPkg, "android", "templates");
6042
6111
  const vars = { PACKAGE_NAME: packageName, APP_NAME: appName };
6043
6112
  const files = [
6044
- { src: "App.java", dst: path24.join(javaDir, "App.java") },
6045
- { src: "TemplateProvider.java", dst: path24.join(javaDir, "TemplateProvider.java") },
6046
- { src: "MainActivity.kt", dst: path24.join(kotlinDir, "MainActivity.kt") }
6113
+ { src: "App.java", dst: path25.join(javaDir, "App.java") },
6114
+ { src: "TemplateProvider.java", dst: path25.join(javaDir, "TemplateProvider.java") },
6115
+ { src: "MainActivity.kt", dst: path25.join(kotlinDir, "MainActivity.kt") }
6047
6116
  ];
6048
6117
  for (const { src, dst } of files) {
6049
- const srcPath = path24.join(templateDir, src);
6050
- if (!fs23.existsSync(srcPath)) continue;
6051
- if (fs23.existsSync(dst) && !opts?.force) {
6052
- console.log(`\u23ED\uFE0F Skipping ${path24.basename(dst)} (use --force to overwrite)`);
6118
+ const srcPath = path25.join(templateDir, src);
6119
+ if (!fs24.existsSync(srcPath)) continue;
6120
+ if (fs24.existsSync(dst) && !opts?.force) {
6121
+ console.log(`\u23ED\uFE0F Skipping ${path25.basename(dst)} (use --force to overwrite)`);
6053
6122
  continue;
6054
6123
  }
6055
6124
  const content = readAndSubstitute(srcPath, vars);
6056
- fs23.mkdirSync(path24.dirname(dst), { recursive: true });
6057
- fs23.writeFileSync(dst, content);
6058
- console.log(`\u2705 Injected ${path24.basename(dst)}`);
6125
+ fs24.mkdirSync(path25.dirname(dst), { recursive: true });
6126
+ fs24.writeFileSync(dst, content);
6127
+ console.log(`\u2705 Injected ${path25.basename(dst)}`);
6059
6128
  }
6060
6129
  }
6061
6130
  async function injectHostIos(opts) {
@@ -6073,13 +6142,13 @@ async function injectHostIos(opts) {
6073
6142
  process.exit(1);
6074
6143
  }
6075
6144
  const iosDir = config.paths?.iosDir ?? "ios";
6076
- const rootDir = path24.join(projectRoot, iosDir);
6077
- const projectDir = path24.join(rootDir, appName);
6078
- if (!fs23.existsSync(projectDir)) {
6145
+ const rootDir = path25.join(projectRoot, iosDir);
6146
+ const projectDir = path25.join(rootDir, appName);
6147
+ if (!fs24.existsSync(projectDir)) {
6079
6148
  console.error("\u274C iOS project not found. Run `t4l ios create` first or ensure ios/ exists.");
6080
6149
  process.exit(1);
6081
6150
  }
6082
- const templateDir = path24.join(hostPkg, "ios", "templates");
6151
+ const templateDir = path25.join(hostPkg, "ios", "templates");
6083
6152
  const vars = { PACKAGE_NAME: bundleId, APP_NAME: appName, BUNDLE_ID: bundleId };
6084
6153
  const files = [
6085
6154
  "AppDelegate.swift",
@@ -6089,22 +6158,22 @@ async function injectHostIos(opts) {
6089
6158
  "LynxInitProcessor.swift"
6090
6159
  ];
6091
6160
  for (const f of files) {
6092
- const srcPath = path24.join(templateDir, f);
6093
- const dstPath = path24.join(projectDir, f);
6094
- if (!fs23.existsSync(srcPath)) continue;
6095
- if (fs23.existsSync(dstPath) && !opts?.force) {
6161
+ const srcPath = path25.join(templateDir, f);
6162
+ const dstPath = path25.join(projectDir, f);
6163
+ if (!fs24.existsSync(srcPath)) continue;
6164
+ if (fs24.existsSync(dstPath) && !opts?.force) {
6096
6165
  console.log(`\u23ED\uFE0F Skipping ${f} (use --force to overwrite)`);
6097
6166
  continue;
6098
6167
  }
6099
6168
  const content = readAndSubstitute(srcPath, vars);
6100
- fs23.writeFileSync(dstPath, content);
6169
+ fs24.writeFileSync(dstPath, content);
6101
6170
  console.log(`\u2705 Injected ${f}`);
6102
6171
  }
6103
6172
  }
6104
6173
 
6105
6174
  // src/common/buildEmbeddable.ts
6106
- import fs24 from "fs";
6107
- import path25 from "path";
6175
+ import fs25 from "fs";
6176
+ import path26 from "path";
6108
6177
  import { execSync as execSync9 } from "child_process";
6109
6178
  var EMBEDDABLE_DIR = "embeddable";
6110
6179
  var LIB_PACKAGE = "com.tamer.embeddable";
@@ -6181,14 +6250,14 @@ object LynxEmbeddable {
6181
6250
  }
6182
6251
  `;
6183
6252
  function generateAndroidLibrary(outDir, androidDir, projectRoot, lynxBundlePath, lynxBundleFile, modules, abiFilters) {
6184
- const libDir = path25.join(androidDir, "lib");
6185
- const libSrcMain = path25.join(libDir, "src", "main");
6186
- const assetsDir = path25.join(libSrcMain, "assets");
6187
- const kotlinDir = path25.join(libSrcMain, "kotlin", LIB_PACKAGE.replace(/\./g, "/"));
6188
- const generatedDir = path25.join(kotlinDir, "generated");
6189
- fs24.mkdirSync(path25.join(androidDir, "gradle"), { recursive: true });
6190
- fs24.mkdirSync(generatedDir, { recursive: true });
6191
- fs24.mkdirSync(assetsDir, { recursive: true });
6253
+ const libDir = path26.join(androidDir, "lib");
6254
+ const libSrcMain = path26.join(libDir, "src", "main");
6255
+ const assetsDir = path26.join(libSrcMain, "assets");
6256
+ const kotlinDir = path26.join(libSrcMain, "kotlin", LIB_PACKAGE.replace(/\./g, "/"));
6257
+ const generatedDir = path26.join(kotlinDir, "generated");
6258
+ fs25.mkdirSync(path26.join(androidDir, "gradle"), { recursive: true });
6259
+ fs25.mkdirSync(generatedDir, { recursive: true });
6260
+ fs25.mkdirSync(assetsDir, { recursive: true });
6192
6261
  const androidModules = modules.filter((m) => m.config.android);
6193
6262
  const abiList = abiFilters.map((a) => `"${a}"`).join(", ");
6194
6263
  const settingsContent = `pluginManagement {
@@ -6208,7 +6277,7 @@ include(":lib")
6208
6277
  ${androidModules.map((p) => {
6209
6278
  const gradleName = p.name.replace(/^@/, "").replace(/\//g, "_");
6210
6279
  const sourceDir = p.config.android?.sourceDir || "android";
6211
- const absPath = path25.join(p.packagePath, sourceDir).replace(/\\/g, "/");
6280
+ const absPath = path26.join(p.packagePath, sourceDir).replace(/\\/g, "/");
6212
6281
  return `include(":${gradleName}")
6213
6282
  project(":${gradleName}").projectDir = file("${absPath}")`;
6214
6283
  }).join("\n")}
@@ -6257,10 +6326,10 @@ dependencies {
6257
6326
  ${libDeps}
6258
6327
  }
6259
6328
  `;
6260
- fs24.writeFileSync(path25.join(androidDir, "gradle", "libs.versions.toml"), LIBS_VERSIONS_TOML);
6261
- fs24.writeFileSync(path25.join(androidDir, "settings.gradle.kts"), settingsContent);
6262
- fs24.writeFileSync(
6263
- path25.join(androidDir, "build.gradle.kts"),
6329
+ fs25.writeFileSync(path26.join(androidDir, "gradle", "libs.versions.toml"), LIBS_VERSIONS_TOML);
6330
+ fs25.writeFileSync(path26.join(androidDir, "settings.gradle.kts"), settingsContent);
6331
+ fs25.writeFileSync(
6332
+ path26.join(androidDir, "build.gradle.kts"),
6264
6333
  `plugins {
6265
6334
  alias(libs.plugins.android.library) apply false
6266
6335
  alias(libs.plugins.kotlin.android) apply false
@@ -6268,26 +6337,26 @@ ${libDeps}
6268
6337
  }
6269
6338
  `
6270
6339
  );
6271
- fs24.writeFileSync(
6272
- path25.join(androidDir, "gradle.properties"),
6340
+ fs25.writeFileSync(
6341
+ path26.join(androidDir, "gradle.properties"),
6273
6342
  `org.gradle.jvmargs=-Xmx2048m
6274
6343
  android.useAndroidX=true
6275
6344
  kotlin.code.style=official
6276
6345
  `
6277
6346
  );
6278
- fs24.writeFileSync(path25.join(libDir, "build.gradle.kts"), libBuildContent);
6279
- fs24.writeFileSync(
6280
- path25.join(libSrcMain, "AndroidManifest.xml"),
6347
+ fs25.writeFileSync(path26.join(libDir, "build.gradle.kts"), libBuildContent);
6348
+ fs25.writeFileSync(
6349
+ path26.join(libSrcMain, "AndroidManifest.xml"),
6281
6350
  '<?xml version="1.0" encoding="utf-8"?>\n<manifest />'
6282
6351
  );
6283
- fs24.copyFileSync(lynxBundlePath, path25.join(assetsDir, lynxBundleFile));
6284
- fs24.writeFileSync(path25.join(kotlinDir, "LynxEmbeddable.kt"), LYNX_EMBEDDABLE_KT);
6285
- fs24.writeFileSync(
6286
- path25.join(generatedDir, "GeneratedLynxExtensions.kt"),
6352
+ fs25.copyFileSync(lynxBundlePath, path26.join(assetsDir, lynxBundleFile));
6353
+ fs25.writeFileSync(path26.join(kotlinDir, "LynxEmbeddable.kt"), LYNX_EMBEDDABLE_KT);
6354
+ fs25.writeFileSync(
6355
+ path26.join(generatedDir, "GeneratedLynxExtensions.kt"),
6287
6356
  generateLynxExtensionsKotlin(modules, LIB_PACKAGE)
6288
6357
  );
6289
- fs24.writeFileSync(
6290
- path25.join(generatedDir, "GeneratedActivityLifecycle.kt"),
6358
+ fs25.writeFileSync(
6359
+ path26.join(generatedDir, "GeneratedActivityLifecycle.kt"),
6291
6360
  generateActivityLifecycleKotlin(modules, LIB_PACKAGE)
6292
6361
  );
6293
6362
  }
@@ -6296,20 +6365,20 @@ async function buildEmbeddable(opts = {}) {
6296
6365
  const { lynxProjectDir, lynxBundlePath, lynxBundleFile, projectRoot, config } = resolved;
6297
6366
  console.log("\u{1F4E6} Building Lynx project (release)...");
6298
6367
  execSync9("npm run build", { stdio: "inherit", cwd: lynxProjectDir });
6299
- if (!fs24.existsSync(lynxBundlePath)) {
6368
+ if (!fs25.existsSync(lynxBundlePath)) {
6300
6369
  console.error(`\u274C Bundle not found at ${lynxBundlePath}`);
6301
6370
  process.exit(1);
6302
6371
  }
6303
- const outDir = path25.join(projectRoot, EMBEDDABLE_DIR);
6304
- fs24.mkdirSync(outDir, { recursive: true });
6305
- const distDir = path25.dirname(lynxBundlePath);
6372
+ const outDir = path26.join(projectRoot, EMBEDDABLE_DIR);
6373
+ fs25.mkdirSync(outDir, { recursive: true });
6374
+ const distDir = path26.dirname(lynxBundlePath);
6306
6375
  copyDistAssets(distDir, outDir, lynxBundleFile);
6307
6376
  const modules = discoverModules(projectRoot);
6308
6377
  const androidModules = modules.filter((m) => m.config.android);
6309
6378
  const abiFilters = resolveAbiFilters(config);
6310
- const androidDir = path25.join(outDir, "android");
6311
- if (fs24.existsSync(androidDir)) fs24.rmSync(androidDir, { recursive: true });
6312
- fs24.mkdirSync(androidDir, { recursive: true });
6379
+ const androidDir = path26.join(outDir, "android");
6380
+ if (fs25.existsSync(androidDir)) fs25.rmSync(androidDir, { recursive: true });
6381
+ fs25.mkdirSync(androidDir, { recursive: true });
6313
6382
  generateAndroidLibrary(
6314
6383
  outDir,
6315
6384
  androidDir,
@@ -6319,23 +6388,23 @@ async function buildEmbeddable(opts = {}) {
6319
6388
  modules,
6320
6389
  abiFilters
6321
6390
  );
6322
- const gradlewPath = path25.join(androidDir, "gradlew");
6391
+ const gradlewPath = path26.join(androidDir, "gradlew");
6323
6392
  const devAppDir = findDevAppPackage(projectRoot);
6324
6393
  const existingGradleDirs = [
6325
- path25.join(projectRoot, "android"),
6326
- devAppDir ? path25.join(devAppDir, "android") : null
6394
+ path26.join(projectRoot, "android"),
6395
+ devAppDir ? path26.join(devAppDir, "android") : null
6327
6396
  ].filter(Boolean);
6328
6397
  let hasWrapper = false;
6329
6398
  for (const d of existingGradleDirs) {
6330
- if (fs24.existsSync(path25.join(d, "gradlew"))) {
6399
+ if (fs25.existsSync(path26.join(d, "gradlew"))) {
6331
6400
  for (const name of ["gradlew", "gradlew.bat", "gradle"]) {
6332
- const src = path25.join(d, name);
6333
- if (fs24.existsSync(src)) {
6334
- const dest = path25.join(androidDir, name);
6335
- if (fs24.statSync(src).isDirectory()) {
6336
- fs24.cpSync(src, dest, { recursive: true });
6401
+ const src = path26.join(d, name);
6402
+ if (fs25.existsSync(src)) {
6403
+ const dest = path26.join(androidDir, name);
6404
+ if (fs25.statSync(src).isDirectory()) {
6405
+ fs25.cpSync(src, dest, { recursive: true });
6337
6406
  } else {
6338
- fs24.copyFileSync(src, dest);
6407
+ fs25.copyFileSync(src, dest);
6339
6408
  }
6340
6409
  }
6341
6410
  }
@@ -6354,10 +6423,10 @@ async function buildEmbeddable(opts = {}) {
6354
6423
  console.error("\u274C Android AAR build failed. Run manually: cd embeddable/android && ./gradlew :lib:assembleRelease");
6355
6424
  throw e;
6356
6425
  }
6357
- const aarSrc = path25.join(androidDir, "lib", "build", "outputs", "aar", "lib-release.aar");
6358
- const aarDest = path25.join(outDir, "tamer-embeddable.aar");
6359
- if (fs24.existsSync(aarSrc)) {
6360
- fs24.copyFileSync(aarSrc, aarDest);
6426
+ const aarSrc = path26.join(androidDir, "lib", "build", "outputs", "aar", "lib-release.aar");
6427
+ const aarDest = path26.join(outDir, "tamer-embeddable.aar");
6428
+ if (fs25.existsSync(aarSrc)) {
6429
+ fs25.copyFileSync(aarSrc, aarDest);
6361
6430
  console.log(` - tamer-embeddable.aar`);
6362
6431
  }
6363
6432
  const snippetAndroid = `// Add to your app's build.gradle:
@@ -6368,7 +6437,7 @@ async function buildEmbeddable(opts = {}) {
6368
6437
  // LynxEmbeddable.init(applicationContext)
6369
6438
  // val lynxView = LynxEmbeddable.buildLynxView(containerViewGroup)
6370
6439
  `;
6371
- fs24.writeFileSync(path25.join(outDir, "snippet-android.kt"), snippetAndroid);
6440
+ fs25.writeFileSync(path26.join(outDir, "snippet-android.kt"), snippetAndroid);
6372
6441
  generateIosPod(outDir, projectRoot, lynxBundlePath, lynxBundleFile, modules);
6373
6442
  const readme = `# Embeddable Lynx Bundle
6374
6443
 
@@ -6399,7 +6468,7 @@ Add the \`Podfile.snippet\` entries to your Podfile (inside your app target), th
6399
6468
 
6400
6469
  - [Embedding LynxView](https://lynxjs.org/guide/embed-lynx-to-native)
6401
6470
  `;
6402
- fs24.writeFileSync(path25.join(outDir, "README.md"), readme);
6471
+ fs25.writeFileSync(path26.join(outDir, "README.md"), readme);
6403
6472
  console.log(`
6404
6473
  \u2705 Embeddable output at ${outDir}/`);
6405
6474
  console.log(" - main.lynx.bundle");
@@ -6411,20 +6480,20 @@ Add the \`Podfile.snippet\` entries to your Podfile (inside your app target), th
6411
6480
  console.log(" - README.md");
6412
6481
  }
6413
6482
  function generateIosPod(outDir, projectRoot, lynxBundlePath, lynxBundleFile, modules) {
6414
- const iosDir = path25.join(outDir, "ios");
6415
- const podDir = path25.join(iosDir, "TamerEmbeddable");
6416
- const resourcesDir = path25.join(podDir, "Resources");
6417
- fs24.mkdirSync(resourcesDir, { recursive: true });
6418
- fs24.copyFileSync(lynxBundlePath, path25.join(resourcesDir, lynxBundleFile));
6483
+ const iosDir = path26.join(outDir, "ios");
6484
+ const podDir = path26.join(iosDir, "TamerEmbeddable");
6485
+ const resourcesDir = path26.join(podDir, "Resources");
6486
+ fs25.mkdirSync(resourcesDir, { recursive: true });
6487
+ fs25.copyFileSync(lynxBundlePath, path26.join(resourcesDir, lynxBundleFile));
6419
6488
  const iosModules = modules.filter((m) => m.config.ios);
6420
6489
  const podDeps = iosModules.map((p) => {
6421
6490
  const podspecPath = p.config.ios?.podspecPath || ".";
6422
- const podspecDir = path25.join(p.packagePath, podspecPath);
6423
- if (!fs24.existsSync(podspecDir)) return null;
6424
- const files = fs24.readdirSync(podspecDir);
6491
+ const podspecDir = path26.join(p.packagePath, podspecPath);
6492
+ if (!fs25.existsSync(podspecDir)) return null;
6493
+ const files = fs25.readdirSync(podspecDir);
6425
6494
  const podspecFile = files.find((f) => f.endsWith(".podspec"));
6426
6495
  const podName = podspecFile ? podspecFile.replace(".podspec", "") : p.name.split("/").pop().replace(/-/g, "");
6427
- const absPath = path25.resolve(podspecDir);
6496
+ const absPath = path26.resolve(podspecDir);
6428
6497
  return { podName, absPath };
6429
6498
  }).filter(Boolean);
6430
6499
  const podDepLines = podDeps.map((d) => ` s.dependency '${d.podName}'`).join("\n");
@@ -6464,9 +6533,9 @@ end
6464
6533
  });
6465
6534
  const swiftImports = iosModules.map((p) => {
6466
6535
  const podspecPath = p.config.ios?.podspecPath || ".";
6467
- const podspecDir = path25.join(p.packagePath, podspecPath);
6468
- if (!fs24.existsSync(podspecDir)) return null;
6469
- const files = fs24.readdirSync(podspecDir);
6536
+ const podspecDir = path26.join(p.packagePath, podspecPath);
6537
+ if (!fs25.existsSync(podspecDir)) return null;
6538
+ const files = fs25.readdirSync(podspecDir);
6470
6539
  const podspecFile = files.find((f) => f.endsWith(".podspec"));
6471
6540
  return podspecFile ? podspecFile.replace(".podspec", "") : null;
6472
6541
  }).filter(Boolean);
@@ -6485,17 +6554,17 @@ ${regBlock}
6485
6554
  }
6486
6555
  }
6487
6556
  `;
6488
- fs24.writeFileSync(path25.join(iosDir, "TamerEmbeddable.podspec"), podspecContent);
6489
- fs24.writeFileSync(path25.join(podDir, "LynxEmbeddable.swift"), lynxEmbeddableSwift);
6490
- const absIosDir = path25.resolve(iosDir);
6557
+ fs25.writeFileSync(path26.join(iosDir, "TamerEmbeddable.podspec"), podspecContent);
6558
+ fs25.writeFileSync(path26.join(podDir, "LynxEmbeddable.swift"), lynxEmbeddableSwift);
6559
+ const absIosDir = path26.resolve(iosDir);
6491
6560
  const podfileSnippet = `# Paste into your app target in Podfile:
6492
6561
 
6493
6562
  pod 'TamerEmbeddable', :path => '${absIosDir}'
6494
6563
  ${podDeps.map((d) => `pod '${d.podName}', :path => '${d.absPath}'`).join("\n")}
6495
6564
  `;
6496
- fs24.writeFileSync(path25.join(iosDir, "Podfile.snippet"), podfileSnippet);
6497
- fs24.writeFileSync(
6498
- path25.join(outDir, "snippet-ios.swift"),
6565
+ fs25.writeFileSync(path26.join(iosDir, "Podfile.snippet"), podfileSnippet);
6566
+ fs25.writeFileSync(
6567
+ path26.join(outDir, "snippet-ios.swift"),
6499
6568
  `// Add LynxEmbeddable.initEnvironment() in your AppDelegate/SceneDelegate before presenting LynxView.
6500
6569
  // Then create LynxView with your bundle URL (main.lynx.bundle is in the pod resources).
6501
6570
  `
@@ -6503,8 +6572,8 @@ ${podDeps.map((d) => `pod '${d.podName}', :path => '${d.absPath}'`).join("\n")}
6503
6572
  }
6504
6573
 
6505
6574
  // src/common/add.ts
6506
- import fs25 from "fs";
6507
- import path26 from "path";
6575
+ import fs26 from "fs";
6576
+ import path27 from "path";
6508
6577
  import { execFile, execSync as execSync10 } from "child_process";
6509
6578
  import { promisify } from "util";
6510
6579
  import semver from "semver";
@@ -6555,9 +6624,9 @@ async function normalizeTamerInstallSpec(pkg) {
6555
6624
  return `${pkg}@prerelease`;
6556
6625
  }
6557
6626
  function detectPackageManager2(cwd) {
6558
- const dir = path26.resolve(cwd);
6559
- if (fs25.existsSync(path26.join(dir, "pnpm-lock.yaml"))) return "pnpm";
6560
- if (fs25.existsSync(path26.join(dir, "bun.lockb"))) return "bun";
6627
+ const dir = path27.resolve(cwd);
6628
+ if (fs26.existsSync(path27.join(dir, "pnpm-lock.yaml"))) return "pnpm";
6629
+ if (fs26.existsSync(path27.join(dir, "bun.lockb"))) return "bun";
6561
6630
  return "npm";
6562
6631
  }
6563
6632
  function runInstall(cwd, packages, pm) {
@@ -6609,13 +6678,13 @@ async function add(packages = []) {
6609
6678
  // src/common/signing.tsx
6610
6679
  import { useState as useState6, useEffect as useEffect4, useRef as useRef2 } from "react";
6611
6680
  import { render as render3, Text as Text10, Box as Box9 } from "ink";
6612
- import fs28 from "fs";
6613
- import path29 from "path";
6681
+ import fs29 from "fs";
6682
+ import path30 from "path";
6614
6683
 
6615
6684
  // src/common/androidKeystore.ts
6616
6685
  import { execFileSync } from "child_process";
6617
- import fs26 from "fs";
6618
- import path27 from "path";
6686
+ import fs27 from "fs";
6687
+ import path28 from "path";
6619
6688
  function normalizeJavaHome(raw) {
6620
6689
  if (!raw) return void 0;
6621
6690
  const t = raw.trim().replace(/^["']|["']$/g, "");
@@ -6628,7 +6697,7 @@ function discoverJavaHomeMacOs() {
6628
6697
  encoding: "utf8",
6629
6698
  stdio: ["pipe", "pipe", "pipe"]
6630
6699
  }).trim().split("\n")[0]?.trim();
6631
- if (out && fs26.existsSync(path27.join(out, "bin", "keytool"))) return out;
6700
+ if (out && fs27.existsSync(path28.join(out, "bin", "keytool"))) return out;
6632
6701
  } catch {
6633
6702
  }
6634
6703
  return void 0;
@@ -6638,13 +6707,13 @@ function resolveKeytoolPath() {
6638
6707
  const win = process.platform === "win32";
6639
6708
  const keytoolName = win ? "keytool.exe" : "keytool";
6640
6709
  if (jh) {
6641
- const p = path27.join(jh, "bin", keytoolName);
6642
- if (fs26.existsSync(p)) return p;
6710
+ const p = path28.join(jh, "bin", keytoolName);
6711
+ if (fs27.existsSync(p)) return p;
6643
6712
  }
6644
6713
  const mac = discoverJavaHomeMacOs();
6645
6714
  if (mac) {
6646
- const p = path27.join(mac, "bin", keytoolName);
6647
- if (fs26.existsSync(p)) return p;
6715
+ const p = path28.join(mac, "bin", keytoolName);
6716
+ if (fs27.existsSync(p)) return p;
6648
6717
  }
6649
6718
  return "keytool";
6650
6719
  }
@@ -6659,16 +6728,16 @@ function keytoolAvailable() {
6659
6728
  };
6660
6729
  if (tryRun("keytool")) return true;
6661
6730
  const fromJavaHome = resolveKeytoolPath();
6662
- if (fromJavaHome !== "keytool" && fs26.existsSync(fromJavaHome)) {
6731
+ if (fromJavaHome !== "keytool" && fs27.existsSync(fromJavaHome)) {
6663
6732
  return tryRun(fromJavaHome);
6664
6733
  }
6665
6734
  return false;
6666
6735
  }
6667
6736
  function generateReleaseKeystore(opts) {
6668
6737
  const keytool = resolveKeytoolPath();
6669
- const dir = path27.dirname(opts.keystoreAbsPath);
6670
- fs26.mkdirSync(dir, { recursive: true });
6671
- if (fs26.existsSync(opts.keystoreAbsPath)) {
6738
+ const dir = path28.dirname(opts.keystoreAbsPath);
6739
+ fs27.mkdirSync(dir, { recursive: true });
6740
+ if (fs27.existsSync(opts.keystoreAbsPath)) {
6672
6741
  throw new Error(`Keystore already exists: ${opts.keystoreAbsPath}`);
6673
6742
  }
6674
6743
  if (!opts.storePassword || !opts.keyPassword) {
@@ -6706,13 +6775,13 @@ function generateReleaseKeystore(opts) {
6706
6775
  }
6707
6776
 
6708
6777
  // src/common/appendEnvFile.ts
6709
- import fs27 from "fs";
6710
- import path28 from "path";
6778
+ import fs28 from "fs";
6779
+ import path29 from "path";
6711
6780
  import { parse } from "dotenv";
6712
6781
  function keysDefinedInFile(filePath) {
6713
- if (!fs27.existsSync(filePath)) return /* @__PURE__ */ new Set();
6782
+ if (!fs28.existsSync(filePath)) return /* @__PURE__ */ new Set();
6714
6783
  try {
6715
- return new Set(Object.keys(parse(fs27.readFileSync(filePath, "utf8"))));
6784
+ return new Set(Object.keys(parse(fs28.readFileSync(filePath, "utf8"))));
6716
6785
  } catch {
6717
6786
  return /* @__PURE__ */ new Set();
6718
6787
  }
@@ -6727,11 +6796,11 @@ function formatEnvLine(key, value) {
6727
6796
  function appendEnvVarsIfMissing(projectRoot, vars) {
6728
6797
  const entries = Object.entries(vars).filter(([, v]) => v !== void 0 && v !== "");
6729
6798
  if (entries.length === 0) return null;
6730
- const envLocal = path28.join(projectRoot, ".env.local");
6731
- const envDefault = path28.join(projectRoot, ".env");
6799
+ const envLocal = path29.join(projectRoot, ".env.local");
6800
+ const envDefault = path29.join(projectRoot, ".env");
6732
6801
  let target;
6733
- if (fs27.existsSync(envLocal)) target = envLocal;
6734
- else if (fs27.existsSync(envDefault)) target = envDefault;
6802
+ if (fs28.existsSync(envLocal)) target = envLocal;
6803
+ else if (fs28.existsSync(envDefault)) target = envDefault;
6735
6804
  else target = envLocal;
6736
6805
  const existing = keysDefinedInFile(target);
6737
6806
  const lines = [];
@@ -6743,20 +6812,20 @@ function appendEnvVarsIfMissing(projectRoot, vars) {
6743
6812
  }
6744
6813
  if (lines.length === 0) {
6745
6814
  return {
6746
- file: path28.basename(target),
6815
+ file: path29.basename(target),
6747
6816
  keys: [],
6748
6817
  skippedAll: entries.length > 0
6749
6818
  };
6750
6819
  }
6751
6820
  let prefix = "";
6752
- if (fs27.existsSync(target)) {
6753
- const cur = fs27.readFileSync(target, "utf8");
6821
+ if (fs28.existsSync(target)) {
6822
+ const cur = fs28.readFileSync(target, "utf8");
6754
6823
  prefix = cur.length === 0 ? "" : cur.endsWith("\n") ? cur : `${cur}
6755
6824
  `;
6756
6825
  }
6757
6826
  const block = lines.join("\n") + "\n";
6758
- fs27.writeFileSync(target, prefix + block, "utf8");
6759
- return { file: path28.basename(target), keys: appendedKeys };
6827
+ fs28.writeFileSync(target, prefix + block, "utf8");
6828
+ return { file: path29.basename(target), keys: appendedKeys };
6760
6829
  }
6761
6830
 
6762
6831
  // src/common/signing.tsx
@@ -6862,7 +6931,7 @@ function SigningWizard({ platform: initialPlatform }) {
6862
6931
  try {
6863
6932
  const resolved = resolveHostPaths();
6864
6933
  const rel = state.android.genKeystorePath.trim() || "android/release.keystore";
6865
- abs = path29.isAbsolute(rel) ? rel : path29.join(resolved.projectRoot, rel);
6934
+ abs = path30.isAbsolute(rel) ? rel : path30.join(resolved.projectRoot, rel);
6866
6935
  const alias = state.android.keyAlias.trim() || "release";
6867
6936
  const pw = state.android.genPassword;
6868
6937
  const pkg = resolved.config.android?.packageName ?? "com.example.app";
@@ -6889,7 +6958,7 @@ function SigningWizard({ platform: initialPlatform }) {
6889
6958
  }));
6890
6959
  } catch (e) {
6891
6960
  const msg = e.message;
6892
- if (abs && fs28.existsSync(abs) && (msg.includes("already exists") || msg.includes("Keystore already exists"))) {
6961
+ if (abs && fs29.existsSync(abs) && (msg.includes("already exists") || msg.includes("Keystore already exists"))) {
6893
6962
  if (cancelled || runId !== generateRunId.current) return;
6894
6963
  const rel = state.android.genKeystorePath.trim() || "android/release.keystore";
6895
6964
  const alias = state.android.keyAlias.trim() || "release";
@@ -6929,11 +6998,11 @@ function SigningWizard({ platform: initialPlatform }) {
6929
6998
  const saveConfig = async () => {
6930
6999
  try {
6931
7000
  const resolved = resolveHostPaths();
6932
- const configPath = path29.join(resolved.projectRoot, "tamer.config.json");
7001
+ const configPath = path30.join(resolved.projectRoot, "tamer.config.json");
6933
7002
  let config = {};
6934
7003
  let androidEnvAppend = null;
6935
- if (fs28.existsSync(configPath)) {
6936
- config = JSON.parse(fs28.readFileSync(configPath, "utf8"));
7004
+ if (fs29.existsSync(configPath)) {
7005
+ config = JSON.parse(fs29.readFileSync(configPath, "utf8"));
6937
7006
  }
6938
7007
  if (state.platform === "android" || state.platform === "both") {
6939
7008
  config.android = config.android || {};
@@ -6960,10 +7029,10 @@ function SigningWizard({ platform: initialPlatform }) {
6960
7029
  ...state.ios.provisioningProfileSpecifier && { provisioningProfileSpecifier: state.ios.provisioningProfileSpecifier }
6961
7030
  };
6962
7031
  }
6963
- fs28.writeFileSync(configPath, JSON.stringify(config, null, 2));
6964
- const gitignorePath = path29.join(resolved.projectRoot, ".gitignore");
6965
- if (fs28.existsSync(gitignorePath)) {
6966
- let gitignore = fs28.readFileSync(gitignorePath, "utf8");
7032
+ fs29.writeFileSync(configPath, JSON.stringify(config, null, 2));
7033
+ const gitignorePath = path30.join(resolved.projectRoot, ".gitignore");
7034
+ if (fs29.existsSync(gitignorePath)) {
7035
+ let gitignore = fs29.readFileSync(gitignorePath, "utf8");
6967
7036
  const additions = [
6968
7037
  ".env.local",
6969
7038
  "*.jks",
@@ -6976,7 +7045,7 @@ ${addition}
6976
7045
  `;
6977
7046
  }
6978
7047
  }
6979
- fs28.writeFileSync(gitignorePath, gitignore);
7048
+ fs29.writeFileSync(gitignorePath, gitignore);
6980
7049
  }
6981
7050
  setState((s) => ({
6982
7051
  ...s,
@@ -7234,13 +7303,13 @@ async function signing(platform) {
7234
7303
  }
7235
7304
 
7236
7305
  // src/common/productionSigning.ts
7237
- import fs29 from "fs";
7238
- import path30 from "path";
7306
+ import fs30 from "fs";
7307
+ import path31 from "path";
7239
7308
  function isAndroidSigningConfigured(resolved) {
7240
7309
  const signing2 = resolved.config.android?.signing;
7241
7310
  const hasConfig = Boolean(signing2?.keystoreFile?.trim() && signing2?.keyAlias?.trim());
7242
- const signingProps = path30.join(resolved.androidDir, "signing.properties");
7243
- const hasProps = fs29.existsSync(signingProps);
7311
+ const signingProps = path31.join(resolved.androidDir, "signing.properties");
7312
+ const hasProps = fs30.existsSync(signingProps);
7244
7313
  return hasConfig || hasProps;
7245
7314
  }
7246
7315
  function isIosSigningConfigured(resolved) {
@@ -7312,7 +7381,7 @@ program.command("create <target>").description("Create a project or extension. T
7312
7381
  program.command("build [platform]").description("Build app. Platform: ios | android (default: both)").option("-e, --embeddable", "Output embeddable bundle + code for existing apps. Use with --release.").option("-d, --debug", "Debug build with dev client embedded (default)").option("-r, --release", "Release build without dev client (unsigned)").option("-p, --production", "Production build for app store (signed)").option(
7313
7382
  "-i, --install",
7314
7383
  "Install after build (iOS: simulator with -d; connected device with -p)"
7315
- ).action(async (platform, opts) => {
7384
+ ).option("-C, --clean", "Run Gradle clean before Android build (fixes stubborn caches)").action(async (platform, opts) => {
7316
7385
  validateBuildMode(opts.debug, opts.release, opts.production);
7317
7386
  const release = opts.release === true || opts.production === true;
7318
7387
  const production = opts.production === true;
@@ -7325,7 +7394,7 @@ program.command("build [platform]").description("Build app. Platform: ios | andr
7325
7394
  assertProductionSigningReady(p);
7326
7395
  }
7327
7396
  if (p === "android" || p === "all") {
7328
- await build_default({ install: opts.install, release, production });
7397
+ await build_default({ install: opts.install, release, production, clean: opts.clean });
7329
7398
  }
7330
7399
  if (p === "ios" || p === "all") {
7331
7400
  await build_default2({ install: opts.install, release, production });
@@ -7414,7 +7483,7 @@ program.command("signing [platform]").description("Configure Android and iOS sig
7414
7483
  program.command("codegen").description("Generate code from @lynxmodule declarations").action(() => {
7415
7484
  codegen_default();
7416
7485
  });
7417
- program.command("android <subcommand>").description("(Legacy) Use: t4l <command> android. e.g. t4l create android").option("-d, --debug", "Create: host project. Bundle/build: debug with dev client.").option("-r, --release", "Create: dev-app project. Bundle/build: release without dev client.").option("-p, --production", "Bundle/build: production for app store (signed)").option("-i, --install", "Install after build").option("-e, --embeddable", "Build embeddable").option("-f, --force", "Force (inject)").action(async (subcommand, opts) => {
7486
+ program.command("android <subcommand>").description("(Legacy) Use: t4l <command> android. e.g. t4l create android").option("-d, --debug", "Create: host project. Bundle/build: debug with dev client.").option("-r, --release", "Create: dev-app project. Bundle/build: release without dev client.").option("-p, --production", "Bundle/build: production for app store (signed)").option("-i, --install", "Install after build").option("-C, --clean", "Run Gradle clean before Android build").option("-e, --embeddable", "Build embeddable").option("-f, --force", "Force (inject)").action(async (subcommand, opts) => {
7418
7487
  const sub = subcommand?.toLowerCase();
7419
7488
  if (sub === "create") {
7420
7489
  if (opts.debug && opts.release) {
@@ -7440,7 +7509,12 @@ program.command("android <subcommand>").description("(Legacy) Use: t4l <command>
7440
7509
  if (opts.embeddable) await buildEmbeddable({ release: true });
7441
7510
  else {
7442
7511
  if (opts.production === true) assertProductionSigningReady("android");
7443
- await build_default({ install: opts.install, release, production: opts.production === true });
7512
+ await build_default({
7513
+ install: opts.install,
7514
+ release,
7515
+ production: opts.production === true,
7516
+ clean: opts.clean
7517
+ });
7444
7518
  }
7445
7519
  return;
7446
7520
  }
@@ -7489,10 +7563,10 @@ program.command("ios <subcommand>").description("(Legacy) Use: t4l <command> ios
7489
7563
  process.exit(1);
7490
7564
  });
7491
7565
  program.command("autolink-toggle").alias("autolink").description("Toggle autolink on/off in tamer.config.json (controls postinstall linking)").action(async () => {
7492
- const configPath = path31.join(process.cwd(), "tamer.config.json");
7566
+ const configPath = path32.join(process.cwd(), "tamer.config.json");
7493
7567
  let config = {};
7494
- if (fs30.existsSync(configPath)) {
7495
- config = JSON.parse(fs30.readFileSync(configPath, "utf8"));
7568
+ if (fs31.existsSync(configPath)) {
7569
+ config = JSON.parse(fs31.readFileSync(configPath, "utf8"));
7496
7570
  }
7497
7571
  if (config.autolink) {
7498
7572
  delete config.autolink;
@@ -7501,7 +7575,7 @@ program.command("autolink-toggle").alias("autolink").description("Toggle autolin
7501
7575
  config.autolink = true;
7502
7576
  console.log("Autolink enabled in tamer.config.json");
7503
7577
  }
7504
- fs30.writeFileSync(configPath, JSON.stringify(config, null, 2));
7578
+ fs31.writeFileSync(configPath, JSON.stringify(config, null, 2));
7505
7579
  console.log(`Updated ${configPath}`);
7506
7580
  });
7507
7581
  if (process.argv.length <= 2 || process.argv.length === 3 && process.argv[2] === "init") {