codeplay-common 4.9.7 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,83 @@
1
- // Version 6.5: recreate failed WebViews, defer background recovery, and suppress foreground recovery toasts.
1
+ // Version 6.7: deferred IAP startup, ownership-safe ads, and unified store preparation.
2
+ // Version 6.7: migrate AdMob files to ZIP-distributed folder packages.
3
+ // Version 6.7: recreate failed WebViews, defer background recovery, and suppress foreground recovery toasts.
2
4
  const fs = require('fs');
3
5
  const path = require('path');
4
6
  const plist = require('plist');
5
7
 
6
8
 
7
9
 
10
+ // BEGIN IAP BUILD MIGRATION
11
+ const updateBuildEntrypoints = (scripts) => require('./project-scripts.cjs').updateBuildScripts(scripts);
12
+
13
+ const migrateIapStartup = (content, bootstrapImport) => {
14
+ let next = content;
15
+ const bootstrapPattern = /import\s*\{\s*initIAP\s*\}\s*from\s*['"][^'"]*\/IAP-bootstrap\.js['"]\s*;?/;
16
+ if (!bootstrapPattern.test(next)) {
17
+ next = next.replace(/import\s*\{([^}]*)\}\s*from\s*(['"])([^'"]*\/IAP-\d+(?:\.\d+)*\/IAP-check-And-LoadAd\.js)\2\s*;?/g,
18
+ (match, names, quote, source) => {
19
+ const imports = names.split(',').map(name => name.trim()).filter(Boolean);
20
+ if (!imports.includes('initIAP')) return match;
21
+ const remaining = imports.filter(name => name !== 'initIAP');
22
+ return remaining.length ? `import { ${remaining.join(', ')} } from ${quote}${source}${quote};` : '';
23
+ });
24
+ next = next.replace(/<script>\s*\r?\n/, match => `${match}import { initIAP } from '${bootstrapImport}';\n`);
25
+ }
26
+ if (!/\binitIAP\s*\(/.test(next)) {
27
+ const component = /(export\s+default\s*\([^)]*\)\s*=>\s*\{\s*\r?\n)/;
28
+ if (!component.test(next)) throw new Error('Unsupported Home component: cannot schedule IAP safely.');
29
+ next = next.replace(component, '$1 initIAP();\n');
30
+ }
31
+ return next;
32
+ };
33
+ // Identify existing Pro controls without replacing an application's Home layout.
34
+ // App-specific controls can opt in with the codeplay-iap-pro class.
35
+ const migrateHomeIapVisibility = (content, stateImport) => {
36
+ const template = content.match(/<template>[\s\S]*?<\/template>/);
37
+ if (!template) return content;
38
+ const stack = [];
39
+ const controls = new Map();
40
+ const tags = /<!--[\s\S]*?-->|<\/?[\w-]+\b(?:[^"'<>]|"[^"]*"|'[^']*')*>/g;
41
+ for (const match of template[0].matchAll(tags)) {
42
+ const tag = match[0];
43
+ if (tag.startsWith('<!--')) continue;
44
+ const name = tag.match(/^<\/?([\w-]+)/)[1].toLowerCase();
45
+ if (tag.startsWith('</')) {
46
+ const index = stack.map(item => item.name).lastIndexOf(name);
47
+ if (index >= 0) stack.length = index;
48
+ continue;
49
+ }
50
+ const classMatch = tag.match(/\bclass\s*=\s*(["'])([\s\S]*?)\1/);
51
+ const classes = classMatch?.[2] || '';
52
+ const node = { name, tag, index: template.index + match.index, classes };
53
+ const clickable = item => /(?:@click|\bdata-iap-pro)\s*=/.test(item.tag) || ['a', 'button'].includes(item.name) || /\b(?:link|codeplay-iap-pro)\b/.test(item.classes);
54
+ if (/\b(?:trPro|clsSubscribeBtn|codeplay-iap-pro)\b/.test(classes) || /@click\s*=\s*["']\$\{\s*subscribeOrProLink\s*\}/.test(tag)) {
55
+ const control = clickable(node) ? node : [...stack].reverse().find(clickable) || (/\bclsSubscribeBtn\b/.test(classes) ? node : null);
56
+ if (control) controls.set(control.index, control);
57
+ }
58
+ if (!/\/\s*>$/.test(tag) && !['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'].includes(name)) stack.push(node);
59
+ }
60
+ if (!controls.size) return content;
61
+ const gate = stateImport ? "${isIAPEnabledForCurrentStore() ? '' : 'display-none'}" : "${false ? '' : 'display-none'}";
62
+ let next = content;
63
+ for (const control of [...controls.values()].sort((a, b) => b.index - a.index)) {
64
+ const classes = control.classes
65
+ .replace(/\$\{(?:isIAPEnabledForCurrentStore\(\)|false)\s*\?\s*''\s*:\s*'display-none'\s*\}/g, '')
66
+ .split(/\s+/).filter(name => name && !/^hideFor(?:Mi|Vivo|Huawei|Oppo|Amazon|Samsung|Playstore|PlayStore|Ios|iOS)$/.test(name) && name !== 'codeplay-iap-pro');
67
+ const value = `${classes.join(' ')} codeplay-iap-pro ${gate}`.trim();
68
+ const tag = /\bclass\s*=/.test(control.tag)
69
+ ? control.tag.replace(/\bclass\s*=\s*(["'])([\s\S]*?)\1/, () => `class="${value}"`)
70
+ : control.tag.replace(/^<[\w-]+/, match => `${match} class="${value}"`);
71
+ next = next.slice(0, control.index) + tag + next.slice(control.index + control.tag.length);
72
+ }
73
+ if (stateImport && !/import\s*\{[^}]*\bisIAPEnabledForCurrentStore\b[^}]*\}\s*from\s*['"][^'"]*\/IAP-state\.js['"]/.test(next)) {
74
+ if (!next.includes('<script>')) throw new Error('Cannot migrate Pro visibility: Home has no script block.');
75
+ next = next.replace(/<script>(?:[ \t]*\r?\n)?/, match => `${match}import { isIAPEnabledForCurrentStore } from '${stateImport}';\n`);
76
+ }
77
+ return next;
78
+ };
79
+ // END IAP BUILD MIGRATION
80
+
8
81
  const { execSync } = require("child_process");
9
82
 
10
83
 
@@ -42,8 +115,7 @@ const configPath = path.join(process.cwd(), 'capacitor.config.json');
42
115
  const packageJsonPath = path.join(process.cwd(), 'package.json');
43
116
  const updateLogFile = path.join(process.cwd(), "", "plugin-update-log.txt");
44
117
  const ONESIGNAL_PLUGIN_NAME = "onesignal-cordova-plugin";
45
- const ADMOB_NEXTGEN_PLUGIN_NAME = "admob-emi-nextgen";
46
- const ADMOB_NEXTGEN_FILE_PATTERN = new RegExp(`^${ADMOB_NEXTGEN_PLUGIN_NAME}-\\d+(?:\\.\\d+)*\\.js$`);
118
+ const { syncAdmobNextgen, syncImports: syncAdmobImports, findLatestPackage: findAdmobPackage } = require('./sync-admob-nextgen.js');
47
119
  const ADMOB_NEXTGEN_CONFIG = "admob-ad-configuration-nextgen.json";
48
120
  const ADMOB_OLD_CONFIG = "admob-ad-configuration.json";
49
121
  const AGENT_INSTRUCTIONS_VERSION_KEY = "agents";
@@ -90,17 +162,15 @@ const getLatestVersionedFileName = (dir, pattern) => {
90
162
  })[0];
91
163
  };
92
164
 
93
- const getLatestAdmobNextgenFileName = (adsDir) =>
94
- getLatestVersionedFileName(adsDir, ADMOB_NEXTGEN_FILE_PATTERN);
95
165
 
96
166
  // Expected plugin list with minimum versions
97
167
  /* const requiredPlugins = [
98
168
 
99
169
  { pattern: /backbutton-(\d+\.\d+)\.js$/, minVersion: '2.0', required: true, baseDir: 'js',mandatoryUpdate: true},
100
-
170
+
101
171
 
102
172
  { pattern: /common-(\d+\.\d+)(?:-beta-(\d+))?\.js$/, minVersion: '6.0', required: true, baseDir: 'js',mandatoryUpdate: true },
103
-
173
+
104
174
  { pattern: /localization_settings-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js',mandatoryUpdate: false },
105
175
  { pattern: /localization-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js',mandatoryUpdate: true },
106
176
  { pattern: /localNotification-(\d+\.\d+)\.js$/, minVersion: '2.2', required: true, baseDir: 'js',mandatoryUpdate: false },
@@ -112,8 +182,8 @@ const getLatestAdmobNextgenFileName = (adsDir) =>
112
182
  { pattern: /video-player-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js',mandatoryUpdate: false },
113
183
  { pattern: /image-cropper-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js',mandatoryUpdate: false },
114
184
  { pattern: /common-(\d+\.\d+)\.less$/, minVersion: '1.6', required: true, baseDir: 'assets/css',mandatoryUpdate: false },
115
-
116
-
185
+
186
+
117
187
  // New folders
118
188
  { pattern: /IAP-(\d+\.\d+)$/, minVersion: '2.8', isFolder: true , required: true, baseDir: 'js/Ads',mandatoryUpdate: true },
119
189
  { pattern: /editor-(\d+\.\d+)$/, minVersion: '1.9', isFolder: true, required: true, baseDir: 'js',mandatoryUpdate: true },
@@ -122,11 +192,11 @@ const getLatestAdmobNextgenFileName = (adsDir) =>
122
192
 
123
193
 
124
194
  { pattern: /certificatejs-(\d+\.\d+)$/, minVersion: '1.6', isFolder: true , required: true, baseDir: 'certificate',mandatoryUpdate: true }
125
-
195
+
126
196
  ]; */
127
197
 
128
198
 
129
- const ROOT_DIR = path.join(__dirname, "..", "src");
199
+ const ROOT_DIR = path.join(process.cwd(), "src");
130
200
 
131
201
  function requireOrInstall(packageName) {
132
202
  try {
@@ -229,9 +299,10 @@ function loadRequiredPlugins() {
229
299
 
230
300
  // OneSignal is retired; do not download its legacy helpers again.
231
301
  return json.plugins.filter(p =>
232
- !/^onesignal(?:-|$)/i.test(String(p.name || '')) && !/onesignal/i.test(String(p.pattern || ''))
302
+ !/^admob-(?:emi-)?nextgen$/i.test(String(p.name || '')) && !/^onesignal(?:-|$)/i.test(String(p.name || '')) && !/onesignal/i.test(String(p.pattern || ''))
233
303
  ).map(p => ({
234
304
  ...p,
305
+ minVersion: p.name === 'IAP' && compareVersionStrings(p.minVersion, '4.6') < 0 ? '4.6' : p.minVersion,
235
306
  pattern: new RegExp(p.pattern)
236
307
  }));
237
308
 
@@ -291,6 +362,12 @@ function getLatestVersion(packageName) {
291
362
 
292
363
  function checkPackageVersion() {
293
364
  const packageName = 'codeplay-common';
365
+ const appPackage = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
366
+ const declaredPackage = appPackage.dependencies?.[packageName] || appPackage.devDependencies?.[packageName] || '';
367
+ if (/^(?:file:|link:|workspace:)/.test(declaredPackage)) {
368
+ console.log('Using local codeplay-common source; skipping registry replacement.');
369
+ return;
370
+ }
294
371
  const installedVersion = getInstalledVersion(packageName);
295
372
  const latestVersion = getLatestVersion(packageName);
296
373
 
@@ -300,9 +377,9 @@ function checkPackageVersion() {
300
377
  return;
301
378
  }
302
379
 
303
- if (installedVersion !== latestVersion) {
380
+ if (compareVersionStrings(installedVersion, latestVersion) < 0) {
304
381
  console.log(`${packageName} is outdated (installed: ${installedVersion}, latest: ${latestVersion}). Updating now...`);
305
-
382
+
306
383
  try {
307
384
  execSync(`npm uninstall ${packageName}`, { stdio: 'inherit' });
308
385
  execSync(`npm install ${packageName}`, { stdio: 'inherit' });
@@ -426,7 +503,7 @@ const checkAppUniqueId=()=>{
426
503
 
427
504
  if (!orientation) {
428
505
  logErrorMessage+='❌ Missing android.ORIENTATION option in capacitor.config.json.\n';
429
- }
506
+ }
430
507
 
431
508
  else if(orientation!="portrait" && orientation!="landscape" && orientation!="auto")
432
509
  {
@@ -557,34 +634,19 @@ const getRelativeModuleImport = (sourceFile, targetFile) => {
557
634
  return relativePath;
558
635
  };
559
636
 
560
- const addInitIapToExistingImport = (content) => {
561
- const iapImportRegex = /import\s*\{([^}]*)\}\s*from\s*(['"])([^'"]*\/IAP-\d+(?:\.\d+)*\/IAP-check-And-LoadAd\.js)\2\s*;?/;
562
- const match = content.match(iapImportRegex);
563
- if (!match || /\binitIAP\b/.test(match[1])) return content;
564
-
565
- const importNames = match[1].trim();
566
- const nextImportNames = importNames ? `initIAP, ${importNames}` : "initIAP";
567
- return content.replace(iapImportRegex, `import { ${nextImportNames} } from ${match[2]}${match[3]}${match[2]};`);
568
- };
569
-
570
- const addInitIapCall = (content) => {
571
- if (/\binitIAP\s*\(/.test(content)) return content;
572
-
573
- const pageInitRegex = /(\$on\(\s*(['"])pageInit\2\s*,\s*(?:async\s*)?\(\)\s*=>\s*\{\s*\r?\n)/;
574
- if (pageInitRegex.test(content)) {
575
- return content.replace(pageInitRegex, "$1 initIAP();\n");
576
- }
577
-
578
- const componentStartRegex = /(export\s+default\s*\([^)]*\)\s*=>\s*\{\s*\r?\n)/;
579
- if (componentStartRegex.test(content)) {
580
- return content.replace(componentStartRegex, "$1 initIAP();\n");
581
- }
582
-
583
- throw new Error("Unable to find a Framework7 component or pageInit lifecycle for IAP startup.");
584
- };
585
-
586
637
  const ensureIapStartupOnHomePage = () => {
587
638
  const iapConfig = getAdmobIapConfig()?.IAP;
639
+ const visibilityHome = getHomePagePath();
640
+ const visibilityIap = getLatestIapDir();
641
+ const statePath = visibilityIap && path.join(visibilityIap, 'IAP-state.js');
642
+ if (visibilityHome) {
643
+ const original = fs.readFileSync(visibilityHome, 'utf8');
644
+ const next = migrateHomeIapVisibility(original, statePath && fs.existsSync(statePath) ? getRelativeModuleImport(visibilityHome, statePath) : null);
645
+ if (next !== original) {
646
+ fs.writeFileSync(visibilityHome, next, 'utf8');
647
+ console.log(`✅ Updated shared Pro-button visibility in ${path.relative(process.cwd(), visibilityHome)}.`);
648
+ }
649
+ }
588
650
  if (!isAnyIapStoreEnabled(iapConfig)) {
589
651
  console.log("ℹ️ IAP is disabled for every store. AdMob will use direct startup.");
590
652
  return;
@@ -607,26 +669,15 @@ const ensureIapStartupOnHomePage = () => {
607
669
 
608
670
  let content = fs.readFileSync(homePagePath, "utf8");
609
671
  const originalContent = content;
610
- const staticInitImportRegex = /import\s*\{[^}]*\binitIAP\b[^}]*\}\s*from\s*['"][^'"]*\/IAP-\d+(?:\.\d+)*\/IAP-check-And-LoadAd\.js['"]\s*;?/;
611
-
612
- content = addInitIapToExistingImport(content);
613
-
614
- if (!staticInitImportRegex.test(content)) {
615
- const relativeImport = getRelativeModuleImport(homePagePath, iapModulePath);
616
- const scriptStartRegex = /<script>\s*\r?\n/;
617
- if (!scriptStartRegex.test(content)) {
618
- throw new Error(`Unable to find the script block in ${homePagePath}`);
619
- }
620
- content = content.replace(scriptStartRegex, `$&import { initIAP } from '${relativeImport}';\n`);
621
- }
622
-
623
- content = addInitIapCall(content);
672
+ const bootstrapPath = path.join(latestIapDir, 'IAP-bootstrap.js');
673
+ if (!fs.existsSync(bootstrapPath)) throw new Error('IAP 4.6 or newer is required for deferred startup.');
674
+ content = migrateIapStartup(content, getRelativeModuleImport(homePagePath, bootstrapPath));
624
675
 
625
676
  if (content !== originalContent) {
626
677
  fs.writeFileSync(homePagePath, content, "utf8");
627
- console.log(`✅ Added direct IAP startup to ${path.relative(process.cwd(), homePagePath)}.`);
678
+ console.log(`✅ Added background IAP startup to ${path.relative(process.cwd(), homePagePath)}.`);
628
679
  } else {
629
- console.log(`ℹ️ Direct IAP startup already configured in ${path.relative(process.cwd(), homePagePath)}.`);
680
+ console.log(`ℹ️ Background IAP startup already configured in ${path.relative(process.cwd(), homePagePath)}.`);
630
681
  }
631
682
  };
632
683
 
@@ -649,7 +700,7 @@ const ensureIapStartupOnHomePage = () => {
649
700
  //const path = require("path");
650
701
  //const { execSync } = require("child_process");
651
702
 
652
- const jsDir = path.join(__dirname, "..", "src", "js");
703
+ const jsDir = path.join(process.cwd(), "src", "js");
653
704
 
654
705
  // 🔍 Detect OLD structure
655
706
  const oldEditorDirs = fs.readdirSync(jsDir)
@@ -794,9 +845,9 @@ else {
794
845
 
795
846
  const saveToGalleryAndSaveFileCheck_iOS = async () => {
796
847
 
797
- const SOURCE_ROOT = path.resolve(__dirname, '../src');
848
+ const SOURCE_ROOT = path.resolve(process.cwd(), 'src');
798
849
  const EXCLUDED_DIR = path.join(SOURCE_ROOT, 'js', 'Ads');
799
- const ANDROID_MANIFEST_PATH = path.resolve(__dirname, '../android/app/src/main/AndroidManifest.xml');
850
+ const ANDROID_MANIFEST_PATH = path.resolve(process.cwd(), 'android/app/src/main/AndroidManifest.xml');
800
851
 
801
852
 
802
853
  // Skip comments and unrelated strings; match static/dynamic imports, re-exports and require.
@@ -818,7 +869,7 @@ You are using a direct version-based path like: editor/editor-x.x/editor.js
818
869
 
819
870
  👉 Please use the proper alias or updated import method.
820
871
  Example: import { ... } from '@editor'
821
-
872
+
822
873
  ⚠️ Do not use version-based paths in imports.
823
874
  👉 Please add this manually in vite.config.js:
824
875
 
@@ -840,7 +891,7 @@ const SKIP_FILES = [
840
891
 
841
892
 
842
893
 
843
- const STATUSBAR_FILE = path.resolve(__dirname, '../src/js/common-configuration.js');
894
+ const STATUSBAR_FILE = path.resolve(process.cwd(), 'src/js/common-configuration.js');
844
895
  const STATUSBAR_REGEX = /^(?!\s*\/\/).*Capacitor\.Plugins\.StatusBar\.setStyle/m;
845
896
 
846
897
 
@@ -1153,7 +1204,7 @@ const routes = [
1153
1204
  },
1154
1205
  }]
1155
1206
  `);
1156
-
1207
+
1157
1208
  badImports.forEach(({ line, number }) => {
1158
1209
  console.error(`${number}: ${line}`);
1159
1210
  });
@@ -1396,8 +1447,8 @@ if (updated) {
1396
1447
 
1397
1448
  // Build the path dynamically like you requested
1398
1449
  const gradlePath = path.join(
1399
- process.cwd(),
1400
- 'android',
1450
+ process.cwd(),
1451
+ 'android',
1401
1452
  'build.gradle'
1402
1453
  );
1403
1454
 
@@ -1616,11 +1667,11 @@ function validateAndroidBuildOptions() {
1616
1667
  const targetAppId=config.appId
1617
1668
 
1618
1669
  const buildOptions = config.android?.buildOptions;
1619
-
1670
+
1620
1671
  if (!buildOptions) {
1621
1672
  console.log('❌ Missing android.buildOptions in capacitor.config.json.');
1622
1673
  process.exit(1);
1623
- }
1674
+ }
1624
1675
 
1625
1676
  const requiredProps = [
1626
1677
  'keystorePath',
@@ -1632,13 +1683,13 @@ function validateAndroidBuildOptions() {
1632
1683
  ];
1633
1684
 
1634
1685
  const missing = requiredProps.filter(prop => !buildOptions[prop]);
1635
-
1686
+
1636
1687
  if (missing.length > 0) {
1637
1688
  console.log('❌ Missing properties android.buildOptions in capacitor.config.json.');
1638
1689
  process.exit(1);
1639
1690
  }
1640
1691
 
1641
-
1692
+
1642
1693
  const keystorePath=buildOptions.keystorePath
1643
1694
  const keyFileName = path.basename(keystorePath);
1644
1695
 
@@ -1828,7 +1879,7 @@ try {
1828
1879
  _admobConfig = getAdMobConfig();
1829
1880
 
1830
1881
 
1831
-
1882
+
1832
1883
 
1833
1884
 
1834
1885
  // Proceed only if ADMOB_ENABLED is true
@@ -2172,7 +2223,9 @@ async function syncAgentInstructions() {
2172
2223
 
2173
2224
  const zipName = `${AGENT_INSTRUCTIONS_ZIP_PREFIX}-${remoteVersion}.zip`;
2174
2225
  const url = `https://htmlcodeplay.com/code-play-plugin/${zipName}`;
2175
- const tempRoot = fs.mkdtempSync(path.join(require("os").tmpdir(), "codeplay-agents-"));
2226
+ const agentTempRoot = path.join(process.cwd(), "agent-temp", "agent-instructions");
2227
+ fs.mkdirSync(agentTempRoot, { recursive: true });
2228
+ const tempRoot = fs.mkdtempSync(path.join(agentTempRoot, "codeplay-agents-"));
2176
2229
  const zipPath = path.join(tempRoot, zipName);
2177
2230
  const extractPath = path.join(tempRoot, "extracted");
2178
2231
 
@@ -2218,15 +2271,7 @@ async function syncAgentInstructions() {
2218
2271
 
2219
2272
 
2220
2273
 
2221
- async function getLatestRemoteAdmobNextgenFileName() {
2222
- const versions = await fetchVersions();
2223
- if (!versions || typeof versions !== "object") return null;
2224
-
2225
- const remoteVersion = String(versions[ADMOB_NEXTGEN_PLUGIN_NAME] || "").trim();
2226
- if (!/^\d+(?:\.\d+)*$/.test(remoteVersion)) return null;
2227
2274
 
2228
- return `${ADMOB_NEXTGEN_PLUGIN_NAME}-${remoteVersion}.js`;
2229
- }
2230
2275
 
2231
2276
  async function autoUpdatePlugin(pluginDef, pluginInfo) {
2232
2277
 
@@ -2258,7 +2303,9 @@ if (baseName === "common") {
2258
2303
  if (ext === ".less") pluginKey = "common-less";
2259
2304
  }
2260
2305
 
2261
- const latestVersion = versions[pluginKey];
2306
+ const advertisedVersion = versions[pluginKey];
2307
+ const latestVersion = baseName === 'IAP' && (!advertisedVersion || compareVersionStrings(advertisedVersion, pluginDef.minVersion) < 0)
2308
+ ? pluginDef.minVersion : advertisedVersion;
2262
2309
 
2263
2310
  if (!latestVersion) {
2264
2311
  console.log(`❌ No version entry for ${baseName}`);
@@ -2279,12 +2326,17 @@ if (pluginDef.isFolder) {
2279
2326
 
2280
2327
  if (!(await urlExists(url))) return false;
2281
2328
 
2282
- fs.rmSync(oldPath, { recursive: true, force: true });
2283
-
2284
- await downloadAndExtractZip(url, newPath);
2285
-
2329
+ const stagingRoot = path.join(process.cwd(), 'agent-temp', 'iap-package-sync');
2330
+ fs.mkdirSync(stagingRoot, { recursive: true });
2331
+ const staging = fs.mkdtempSync(path.join(stagingRoot, 'package-'));
2332
+ await downloadAndExtractZip(url, staging);
2333
+ if (baseName === 'IAP' && ['IAP-check-And-LoadAd.js', 'IAP-bootstrap.js', 'IAP-state.js', 'IAP-store.js'].some(name => !fs.existsSync(path.join(staging, name)))) throw new Error('Invalid IAP archive');
2334
+ if (fs.existsSync(newPath)) throw new Error('Replacement folder already exists; existing sources preserved.');
2335
+ fs.renameSync(staging, newPath);
2336
+ sourceFileInventory = null;
2286
2337
  updateImports(pluginInfo.name, `${baseName}-${latestVersion}`);
2287
2338
 
2339
+ fs.rmSync(oldPath, { recursive: true, force: true });
2288
2340
  // ✅ ADD THIS
2289
2341
  writeUpdateLine(`${pluginInfo.name} -> ${baseName}-${latestVersion}`);
2290
2342
 
@@ -2362,7 +2414,7 @@ existingFiles.forEach(file => {
2362
2414
  fs.unlinkSync(oldPath);
2363
2415
 
2364
2416
  console.log(`🗑 Removed old file → ${file}`);
2365
- //writeUpdateLine(`Removed old file: ${file}`);
2417
+ //writeUpdateLine(`Removed old file: ${file}`);
2366
2418
  }
2367
2419
 
2368
2420
  });
@@ -2425,7 +2477,7 @@ async function loadPluginVersions() {
2425
2477
  if (!plugin.name) return;
2426
2478
 
2427
2479
  if (versions[plugin.name]) {
2428
- plugin.minVersion = versions[plugin.name];
2480
+ if (compareVersionStrings(versions[plugin.name], plugin.minVersion) > 0) plugin.minVersion = versions[plugin.name];
2429
2481
  }
2430
2482
 
2431
2483
  });
@@ -2528,7 +2580,7 @@ function checkPlugins() {
2528
2580
  } */
2529
2581
 
2530
2582
 
2531
-
2583
+
2532
2584
 
2533
2585
 
2534
2586
  if (hasMandatoryUpdate) {
@@ -2635,14 +2687,14 @@ if (hasMandatoryUpdate) {
2635
2687
 
2636
2688
 
2637
2689
 
2638
- const localizationBaseDir = path.join(__dirname, "..", "src", "js", "localization");
2690
+ const localizationBaseDir = path.join(process.cwd(), "src", "js", "localization");
2639
2691
 
2640
2692
  // ======================================================
2641
2693
  // 🌐 LOCALIZATION CHECK (FULLY DYNAMIC)
2642
2694
  // ======================================================
2643
2695
 
2644
2696
 
2645
- const jsRootDir = path.join(__dirname, "..", "src", "js");
2697
+ const jsRootDir = path.join(process.cwd(), "src", "js");
2646
2698
 
2647
2699
  // 🔍 Detect OLD localization files in root js/
2648
2700
  const oldLocalizationFiles = fs.readdirSync(jsRootDir)
@@ -2870,53 +2922,8 @@ const checkAndupdateDropInViteConfig = () => {
2870
2922
  };
2871
2923
 
2872
2924
  const syncAdmobAliasInViteConfig = () => {
2873
- const possibleFiles = [
2874
- "vite.config.mjs",
2875
- "vite.config.js"
2876
- ];
2877
-
2878
- const viteConfigPath = possibleFiles
2879
- .map(file => path.join(process.cwd(), file))
2880
- .find(filePath => fs.existsSync(filePath));
2881
-
2882
- if (!viteConfigPath) {
2883
- console.warn("⚠️ No vite config found. Skipping AdMob alias sync.");
2884
- return;
2885
- }
2886
-
2887
- const adsDir = path.join(process.cwd(), "src", "js", "Ads");
2888
-
2889
- if (!fs.existsSync(adsDir)) {
2890
- console.log("ℹ️ Ads folder not found. Keeping existing @admob alias.");
2891
- return;
2892
- }
2893
-
2894
- const nextGenFile = getLatestAdmobNextgenFileName(adsDir);
2895
-
2896
- if (!nextGenFile) {
2897
- console.log("ℹ️ No admob-emi-nextgen-x.x.js file found. Keeping existing @admob alias.");
2898
- return;
2899
- }
2900
-
2901
- const nextGenRelativePath = `./src/js/Ads/${nextGenFile}`;
2902
-
2903
- let viteContent = fs.readFileSync(viteConfigPath, "utf8");
2904
- const nextGenAliasLine = ` '@admob': path.resolve(__dirname, '${nextGenRelativePath}'),`;
2905
- const aliasLineRegex = /[ \t]*['"]@admob['"]:\s*path\.resolve\(__dirname,\s*['"][^'"]+['"]\),/;
2906
-
2907
- if (aliasLineRegex.test(viteContent) && viteContent.includes(nextGenAliasLine)) {
2908
- console.log(`ℹ️ @admob already points to ${nextGenRelativePath}.`);
2909
- return;
2910
- }
2911
-
2912
- if (!aliasLineRegex.test(viteContent)) {
2913
- console.warn("⚠️ @admob alias line not found in vite config. Skipping alias sync.");
2914
- return;
2915
- }
2916
-
2917
- viteContent = viteContent.replace(aliasLineRegex, nextGenAliasLine);
2918
- fs.writeFileSync(viteConfigPath, viteContent, "utf8");
2919
- console.log(`✅ @admob alias updated to ${nextGenRelativePath}`);
2925
+ const latest = findAdmobPackage(path.join(process.cwd(), 'src', 'js', 'Ads'));
2926
+ if (latest) syncAdmobImports(process.cwd(), latest);
2920
2927
  };
2921
2928
 
2922
2929
 
@@ -3271,34 +3278,13 @@ const ensureAdmobNextgenFiles = async () => {
3271
3278
  return;
3272
3279
  }
3273
3280
 
3274
- const localNextgenFile = getLatestAdmobNextgenFileName(adsDir);
3275
- const remoteNextgenFile = await getLatestRemoteAdmobNextgenFileName();
3276
- const nextgenFile = (() => {
3277
- if (!localNextgenFile) return remoteNextgenFile;
3278
- if (!remoteNextgenFile) return localNextgenFile;
3279
-
3280
- const localVersion = extractVersionFromFileName(localNextgenFile);
3281
- const remoteVersion = extractVersionFromFileName(remoteNextgenFile);
3282
- return compareVersionStrings(remoteVersion, localVersion) > 0 ? remoteNextgenFile : localNextgenFile;
3283
- })();
3284
-
3285
- if (!nextgenFile) {
3286
- console.error("❌ No local or remote admob-emi-nextgen file found for sync.");
3287
- process.exit(1);
3288
- }
3289
-
3290
- const nextgenPath = path.join(adsDir, nextgenFile);
3281
+ await syncAdmobNextgen({ projectRoot: process.cwd(), versions: await fetchVersions() });
3282
+ sourceFileInventory = null;
3291
3283
  const nextgenConfigPath = path.join(adsDir, ADMOB_NEXTGEN_CONFIG);
3292
3284
  const oldConfigPath = path.join(adsDir, ADMOB_OLD_CONFIG);
3293
- const tempTemplatePath = path.join(adsDir, `${ADMOB_NEXTGEN_CONFIG}.template`);
3294
-
3295
- if (!fs.existsSync(nextgenPath)) {
3296
- const downloaded = await downloadOptionalCodeplayFile(nextgenFile, nextgenPath);
3297
- if (!downloaded) {
3298
- console.error(`❌ ${nextgenFile} missing and live download failed.`);
3299
- process.exit(1);
3300
- }
3301
- }
3285
+ const templateDir = path.join(process.cwd(), 'agent-temp', 'admob-config-sync');
3286
+ fs.mkdirSync(templateDir, { recursive: true });
3287
+ const tempTemplatePath = path.join(templateDir, ADMOB_NEXTGEN_CONFIG);
3302
3288
 
3303
3289
  let templateConfig = ADMOB_NEXTGEN_DEFAULTS;
3304
3290
 
@@ -3789,6 +3775,7 @@ const ensureIapDesignInCapacitorConfig = (iapConfig) => {
3789
3775
  };
3790
3776
 
3791
3777
  const getCurrentStoreId = () => {
3778
+ if (buildPlatform === 'ios') return 6;
3792
3779
  const envStoreId = Number(process.env.VITE_STORE_ID);
3793
3780
  if (Number.isInteger(envStoreId) && envStoreId > 0) return envStoreId;
3794
3781
 
@@ -3976,6 +3963,17 @@ const syncRevenueCatIapSetup = () => {
3976
3963
  console.log(`ℹ️ RevenueCat package is not required for store id ${storeId}.`);
3977
3964
  }
3978
3965
 
3966
+ const samsungEnabled = storeId === 2 && iapConfig.samsung === true;
3967
+ const samsungPackagePath = path.join(process.cwd(), 'node_modules', 'cordova-plugin-samsungiap', 'package.json');
3968
+ const samsungVersion = fs.existsSync(samsungPackagePath) ? JSON.parse(fs.readFileSync(samsungPackagePath, 'utf8')).version : '0';
3969
+ if (samsungEnabled && (!isPackageAvailable('cordova-plugin-samsungiap') || compareVersionStrings(samsungVersion, '2.0.0') < 0)) {
3970
+ runNpmCommand('npm install cordova-plugin-samsungiap@^2.0.0');
3971
+ packageChanged = true;
3972
+ } else if (!samsungEnabled && isPackageAvailable('cordova-plugin-samsungiap')) {
3973
+ runNpmCommand('npm uninstall cordova-plugin-samsungiap');
3974
+ packageChanged = true;
3975
+ }
3976
+ ensureAndroidPermission('com.samsung.android.iap.permission.BILLING', samsungEnabled);
3979
3977
  ensureAndroidPermission("com.android.vending.BILLING", store?.usesGoogleBilling === true && revenueCatEnabledForCurrentStore);
3980
3978
 
3981
3979
  if (iapConfig.samsung === true && revenueCatEnabled) {
@@ -4245,8 +4243,8 @@ const validateAndRestoreSignDetails=()=>{
4245
4243
 
4246
4244
 
4247
4245
  const filePath = path.join(
4248
- __dirname,
4249
- "../node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java"
4246
+ process.cwd(),
4247
+ "node_modules/@capacitor/android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java"
4250
4248
  );
4251
4249
 
4252
4250
  const SYSTEM_BARS_PATCH_MARKER = "CODEPLAY_KEYBOARD_INSET_FIX";
@@ -4464,17 +4462,23 @@ function syncAndroidCordovaResourceKeep() {
4464
4462
  }
4465
4463
 
4466
4464
  async function main() {
4465
+ const buildPackage = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
4466
+ const buildScripts = updateBuildEntrypoints(buildPackage.scripts || {});
4467
+ if (JSON.stringify(buildScripts) !== JSON.stringify(buildPackage.scripts)) {
4468
+ buildPackage.scripts = buildScripts;
4469
+ fs.writeFileSync(packageJsonPath, JSON.stringify(buildPackage, null, 2) + '\n');
4470
+ }
4467
4471
  validateThemeFolderLocation();
4468
4472
  validateAndRestoreSignDetails();
4469
4473
  ensureMandatoryCapacitorPlugins();
4470
4474
 
4471
4475
  await loadPluginVersions(); // 🔥 NEW
4472
4476
  await syncAgentInstructions();
4477
+ await checkPlugins();
4473
4478
 
4474
4479
  await syncAdmobNextgenMigration();
4475
4480
  syncCodeplaySystemBarsPackage();
4476
4481
  syncSystemBarsSafeAreaMigration();
4477
- await checkPlugins();
4478
4482
  ensureIapStartupOnHomePage();
4479
4483
  syncRevenueCatIapSetup();
4480
4484
  syncFirebaseAnalyticsSetup();