codeplay-common 4.9.9 → 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,98 +1,98 @@
1
- const { spawn } = require("child_process");
2
- const path = require("path");
3
- const fs = require("fs");
4
-
5
- // Paths
6
- //const projectRoot = path.join(__dirname, "..");
7
- const projectRoot = process.cwd();
8
- const androidPath = path.join(projectRoot, "android");
9
- const capacitorConfigPath = path.join(projectRoot, "capacitor.config.json");
10
-
11
- // Read capacitor.config.json
12
- if (!fs.existsSync(capacitorConfigPath)) {
13
- console.error("❌ capacitor.config.json not found!");
14
- process.exit(1);
15
- }
16
-
17
- const config = JSON.parse(fs.readFileSync(capacitorConfigPath, "utf8"));
18
-
19
- const buildOptions = config.android?.buildOptions;
20
-
21
- if (!buildOptions) {
22
- console.error("❌ android.buildOptions not found in capacitor.config.json");
23
- process.exit(1);
24
- }
25
-
26
- // Resolve keystore path (relative → absolute)
27
-
28
-
29
- const storePassword = buildOptions.keystorePassword;
30
- const keyAlias = buildOptions.keystoreAlias;
31
- const keyPassword = buildOptions.keystoreAliasPassword;
32
-
33
-
34
-
35
-
36
-
37
-
38
- const rawKeystorePath = buildOptions.keystorePath;
39
-
40
- // First try normal resolve
41
- let keystorePath = path.resolve(projectRoot, rawKeystorePath);
42
-
43
- // If not found, try trimming one "../"
44
- if (!fs.existsSync(keystorePath)) {
45
- console.log("⚠️ Keystore not found, attempting path correction...");
46
-
47
- const trimmedPath = rawKeystorePath.replace(/^\.\.\//, "");
48
- keystorePath = path.resolve(projectRoot, trimmedPath);
49
- }
50
-
51
- // Final validation
52
- if (!fs.existsSync(keystorePath)) {
53
- console.error("❌ Keystore file not found even after correction:", keystorePath);
54
- process.exit(1);
55
- }
56
-
57
- console.log("🔐 Using keystore:", keystorePath);
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
- // Gradle args
69
- const isWindows = process.platform === "win32";
70
- const command = isWindows ? "cmd.exe" : "sh";
71
- const args = [
72
- ...(isWindows ? ["/c", "gradlew.bat"] : ["./gradlew"]),
73
- "assembleRelease",
74
- `-Pandroid.injected.signing.store.file=${keystorePath}`,
75
- `-Pandroid.injected.signing.store.password=${storePassword}`,
76
- `-Pandroid.injected.signing.key.alias=${keyAlias}`,
77
- `-Pandroid.injected.signing.key.password=${keyPassword}`,
78
- ];
79
-
80
- // Run Gradle
81
- const gradle = spawn(command, args, {
82
- cwd: androidPath,
83
- stdio: "inherit",
84
- });
85
-
86
- gradle.on("error", (error) => {
87
- console.error("❌ Could not start Gradle:", error.message);
88
- process.exit(1);
89
- });
90
-
91
- gradle.on("close", (code) => {
92
- if (code === 0) {
93
- console.log("\n✅ Signed APK Generated Successfully!");
94
- } else {
95
- console.log("\n❌ Build Failed with code:", code);
96
- process.exit(code ?? 1);
97
- }
1
+ const { spawn } = require("child_process");
2
+ const path = require("path");
3
+ const fs = require("fs");
4
+
5
+ // Paths
6
+ //const projectRoot = path.join(__dirname, "..");
7
+ const projectRoot = process.cwd();
8
+ const androidPath = path.join(projectRoot, "android");
9
+ const capacitorConfigPath = path.join(projectRoot, "capacitor.config.json");
10
+
11
+ // Read capacitor.config.json
12
+ if (!fs.existsSync(capacitorConfigPath)) {
13
+ console.error("❌ capacitor.config.json not found!");
14
+ process.exit(1);
15
+ }
16
+
17
+ const config = JSON.parse(fs.readFileSync(capacitorConfigPath, "utf8"));
18
+
19
+ const buildOptions = config.android?.buildOptions;
20
+
21
+ if (!buildOptions) {
22
+ console.error("❌ android.buildOptions not found in capacitor.config.json");
23
+ process.exit(1);
24
+ }
25
+
26
+ // Resolve keystore path (relative → absolute)
27
+
28
+
29
+ const storePassword = buildOptions.keystorePassword;
30
+ const keyAlias = buildOptions.keystoreAlias;
31
+ const keyPassword = buildOptions.keystoreAliasPassword;
32
+
33
+
34
+
35
+
36
+
37
+
38
+ const rawKeystorePath = buildOptions.keystorePath;
39
+
40
+ // First try normal resolve
41
+ let keystorePath = path.resolve(projectRoot, rawKeystorePath);
42
+
43
+ // If not found, try trimming one "../"
44
+ if (!fs.existsSync(keystorePath)) {
45
+ console.log("⚠️ Keystore not found, attempting path correction...");
46
+
47
+ const trimmedPath = rawKeystorePath.replace(/^\.\.\//, "");
48
+ keystorePath = path.resolve(projectRoot, trimmedPath);
49
+ }
50
+
51
+ // Final validation
52
+ if (!fs.existsSync(keystorePath)) {
53
+ console.error("❌ Keystore file not found even after correction:", keystorePath);
54
+ process.exit(1);
55
+ }
56
+
57
+ console.log("🔐 Using keystore:", keystorePath);
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+ // Gradle args
69
+ const isWindows = process.platform === "win32";
70
+ const command = isWindows ? "cmd.exe" : "sh";
71
+ const args = [
72
+ ...(isWindows ? ["/c", "gradlew.bat"] : ["./gradlew"]),
73
+ "assembleRelease",
74
+ `-Pandroid.injected.signing.store.file=${keystorePath}`,
75
+ `-Pandroid.injected.signing.store.password=${storePassword}`,
76
+ `-Pandroid.injected.signing.key.alias=${keyAlias}`,
77
+ `-Pandroid.injected.signing.key.password=${keyPassword}`,
78
+ ];
79
+
80
+ // Run Gradle
81
+ const gradle = spawn(command, args, {
82
+ cwd: androidPath,
83
+ stdio: "inherit",
84
+ });
85
+
86
+ gradle.on("error", (error) => {
87
+ console.error("❌ Could not start Gradle:", error.message);
88
+ process.exit(1);
89
+ });
90
+
91
+ gradle.on("close", (code) => {
92
+ if (code === 0) {
93
+ console.log("\n✅ Signed APK Generated Successfully!");
94
+ } else {
95
+ console.log("\n❌ Build Failed with code:", code);
96
+ process.exit(code ?? 1);
97
+ }
98
98
  });
@@ -1,11 +1,83 @@
1
- // Version 6.5: migrate AdMob files to ZIP-distributed folder packages.
2
- // 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.
3
4
  const fs = require('fs');
4
5
  const path = require('path');
5
6
  const plist = require('plist');
6
7
 
7
8
 
8
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
+
9
81
  const { execSync } = require("child_process");
10
82
 
11
83
 
@@ -95,10 +167,10 @@ const getLatestVersionedFileName = (dir, pattern) => {
95
167
  /* const requiredPlugins = [
96
168
 
97
169
  { pattern: /backbutton-(\d+\.\d+)\.js$/, minVersion: '2.0', required: true, baseDir: 'js',mandatoryUpdate: true},
98
-
170
+
99
171
 
100
172
  { pattern: /common-(\d+\.\d+)(?:-beta-(\d+))?\.js$/, minVersion: '6.0', required: true, baseDir: 'js',mandatoryUpdate: true },
101
-
173
+
102
174
  { pattern: /localization_settings-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js',mandatoryUpdate: false },
103
175
  { pattern: /localization-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js',mandatoryUpdate: true },
104
176
  { pattern: /localNotification-(\d+\.\d+)\.js$/, minVersion: '2.2', required: true, baseDir: 'js',mandatoryUpdate: false },
@@ -110,8 +182,8 @@ const getLatestVersionedFileName = (dir, pattern) => {
110
182
  { pattern: /video-player-(\d+\.\d+)\.js$/, minVersion: '1.5', required: true, baseDir: 'js',mandatoryUpdate: false },
111
183
  { pattern: /image-cropper-(\d+\.\d+)\.js$/, minVersion: '1.1', required: true, baseDir: 'js',mandatoryUpdate: false },
112
184
  { pattern: /common-(\d+\.\d+)\.less$/, minVersion: '1.6', required: true, baseDir: 'assets/css',mandatoryUpdate: false },
113
-
114
-
185
+
186
+
115
187
  // New folders
116
188
  { pattern: /IAP-(\d+\.\d+)$/, minVersion: '2.8', isFolder: true , required: true, baseDir: 'js/Ads',mandatoryUpdate: true },
117
189
  { pattern: /editor-(\d+\.\d+)$/, minVersion: '1.9', isFolder: true, required: true, baseDir: 'js',mandatoryUpdate: true },
@@ -120,11 +192,11 @@ const getLatestVersionedFileName = (dir, pattern) => {
120
192
 
121
193
 
122
194
  { pattern: /certificatejs-(\d+\.\d+)$/, minVersion: '1.6', isFolder: true , required: true, baseDir: 'certificate',mandatoryUpdate: true }
123
-
195
+
124
196
  ]; */
125
197
 
126
198
 
127
- const ROOT_DIR = path.join(__dirname, "..", "src");
199
+ const ROOT_DIR = path.join(process.cwd(), "src");
128
200
 
129
201
  function requireOrInstall(packageName) {
130
202
  try {
@@ -230,6 +302,7 @@ function loadRequiredPlugins() {
230
302
  !/^admob-(?:emi-)?nextgen$/i.test(String(p.name || '')) && !/^onesignal(?:-|$)/i.test(String(p.name || '')) && !/onesignal/i.test(String(p.pattern || ''))
231
303
  ).map(p => ({
232
304
  ...p,
305
+ minVersion: p.name === 'IAP' && compareVersionStrings(p.minVersion, '4.6') < 0 ? '4.6' : p.minVersion,
233
306
  pattern: new RegExp(p.pattern)
234
307
  }));
235
308
 
@@ -289,6 +362,12 @@ function getLatestVersion(packageName) {
289
362
 
290
363
  function checkPackageVersion() {
291
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
+ }
292
371
  const installedVersion = getInstalledVersion(packageName);
293
372
  const latestVersion = getLatestVersion(packageName);
294
373
 
@@ -298,9 +377,9 @@ function checkPackageVersion() {
298
377
  return;
299
378
  }
300
379
 
301
- if (installedVersion !== latestVersion) {
380
+ if (compareVersionStrings(installedVersion, latestVersion) < 0) {
302
381
  console.log(`${packageName} is outdated (installed: ${installedVersion}, latest: ${latestVersion}). Updating now...`);
303
-
382
+
304
383
  try {
305
384
  execSync(`npm uninstall ${packageName}`, { stdio: 'inherit' });
306
385
  execSync(`npm install ${packageName}`, { stdio: 'inherit' });
@@ -424,7 +503,7 @@ const checkAppUniqueId=()=>{
424
503
 
425
504
  if (!orientation) {
426
505
  logErrorMessage+='❌ Missing android.ORIENTATION option in capacitor.config.json.\n';
427
- }
506
+ }
428
507
 
429
508
  else if(orientation!="portrait" && orientation!="landscape" && orientation!="auto")
430
509
  {
@@ -555,34 +634,19 @@ const getRelativeModuleImport = (sourceFile, targetFile) => {
555
634
  return relativePath;
556
635
  };
557
636
 
558
- const addInitIapToExistingImport = (content) => {
559
- const iapImportRegex = /import\s*\{([^}]*)\}\s*from\s*(['"])([^'"]*\/IAP-\d+(?:\.\d+)*\/IAP-check-And-LoadAd\.js)\2\s*;?/;
560
- const match = content.match(iapImportRegex);
561
- if (!match || /\binitIAP\b/.test(match[1])) return content;
562
-
563
- const importNames = match[1].trim();
564
- const nextImportNames = importNames ? `initIAP, ${importNames}` : "initIAP";
565
- return content.replace(iapImportRegex, `import { ${nextImportNames} } from ${match[2]}${match[3]}${match[2]};`);
566
- };
567
-
568
- const addInitIapCall = (content) => {
569
- if (/\binitIAP\s*\(/.test(content)) return content;
570
-
571
- const pageInitRegex = /(\$on\(\s*(['"])pageInit\2\s*,\s*(?:async\s*)?\(\)\s*=>\s*\{\s*\r?\n)/;
572
- if (pageInitRegex.test(content)) {
573
- return content.replace(pageInitRegex, "$1 initIAP();\n");
574
- }
575
-
576
- const componentStartRegex = /(export\s+default\s*\([^)]*\)\s*=>\s*\{\s*\r?\n)/;
577
- if (componentStartRegex.test(content)) {
578
- return content.replace(componentStartRegex, "$1 initIAP();\n");
579
- }
580
-
581
- throw new Error("Unable to find a Framework7 component or pageInit lifecycle for IAP startup.");
582
- };
583
-
584
637
  const ensureIapStartupOnHomePage = () => {
585
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
+ }
586
650
  if (!isAnyIapStoreEnabled(iapConfig)) {
587
651
  console.log("ℹ️ IAP is disabled for every store. AdMob will use direct startup.");
588
652
  return;
@@ -605,26 +669,15 @@ const ensureIapStartupOnHomePage = () => {
605
669
 
606
670
  let content = fs.readFileSync(homePagePath, "utf8");
607
671
  const originalContent = content;
608
- const staticInitImportRegex = /import\s*\{[^}]*\binitIAP\b[^}]*\}\s*from\s*['"][^'"]*\/IAP-\d+(?:\.\d+)*\/IAP-check-And-LoadAd\.js['"]\s*;?/;
609
-
610
- content = addInitIapToExistingImport(content);
611
-
612
- if (!staticInitImportRegex.test(content)) {
613
- const relativeImport = getRelativeModuleImport(homePagePath, iapModulePath);
614
- const scriptStartRegex = /<script>\s*\r?\n/;
615
- if (!scriptStartRegex.test(content)) {
616
- throw new Error(`Unable to find the script block in ${homePagePath}`);
617
- }
618
- content = content.replace(scriptStartRegex, `$&import { initIAP } from '${relativeImport}';\n`);
619
- }
620
-
621
- 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));
622
675
 
623
676
  if (content !== originalContent) {
624
677
  fs.writeFileSync(homePagePath, content, "utf8");
625
- 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)}.`);
626
679
  } else {
627
- 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)}.`);
628
681
  }
629
682
  };
630
683
 
@@ -647,7 +700,7 @@ const ensureIapStartupOnHomePage = () => {
647
700
  //const path = require("path");
648
701
  //const { execSync } = require("child_process");
649
702
 
650
- const jsDir = path.join(__dirname, "..", "src", "js");
703
+ const jsDir = path.join(process.cwd(), "src", "js");
651
704
 
652
705
  // 🔍 Detect OLD structure
653
706
  const oldEditorDirs = fs.readdirSync(jsDir)
@@ -792,9 +845,9 @@ else {
792
845
 
793
846
  const saveToGalleryAndSaveFileCheck_iOS = async () => {
794
847
 
795
- const SOURCE_ROOT = path.resolve(__dirname, '../src');
848
+ const SOURCE_ROOT = path.resolve(process.cwd(), 'src');
796
849
  const EXCLUDED_DIR = path.join(SOURCE_ROOT, 'js', 'Ads');
797
- 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');
798
851
 
799
852
 
800
853
  // Skip comments and unrelated strings; match static/dynamic imports, re-exports and require.
@@ -816,7 +869,7 @@ You are using a direct version-based path like: editor/editor-x.x/editor.js
816
869
 
817
870
  👉 Please use the proper alias or updated import method.
818
871
  Example: import { ... } from '@editor'
819
-
872
+
820
873
  ⚠️ Do not use version-based paths in imports.
821
874
  👉 Please add this manually in vite.config.js:
822
875
 
@@ -838,7 +891,7 @@ const SKIP_FILES = [
838
891
 
839
892
 
840
893
 
841
- const STATUSBAR_FILE = path.resolve(__dirname, '../src/js/common-configuration.js');
894
+ const STATUSBAR_FILE = path.resolve(process.cwd(), 'src/js/common-configuration.js');
842
895
  const STATUSBAR_REGEX = /^(?!\s*\/\/).*Capacitor\.Plugins\.StatusBar\.setStyle/m;
843
896
 
844
897
 
@@ -1151,7 +1204,7 @@ const routes = [
1151
1204
  },
1152
1205
  }]
1153
1206
  `);
1154
-
1207
+
1155
1208
  badImports.forEach(({ line, number }) => {
1156
1209
  console.error(`${number}: ${line}`);
1157
1210
  });
@@ -1394,8 +1447,8 @@ if (updated) {
1394
1447
 
1395
1448
  // Build the path dynamically like you requested
1396
1449
  const gradlePath = path.join(
1397
- process.cwd(),
1398
- 'android',
1450
+ process.cwd(),
1451
+ 'android',
1399
1452
  'build.gradle'
1400
1453
  );
1401
1454
 
@@ -1614,11 +1667,11 @@ function validateAndroidBuildOptions() {
1614
1667
  const targetAppId=config.appId
1615
1668
 
1616
1669
  const buildOptions = config.android?.buildOptions;
1617
-
1670
+
1618
1671
  if (!buildOptions) {
1619
1672
  console.log('❌ Missing android.buildOptions in capacitor.config.json.');
1620
1673
  process.exit(1);
1621
- }
1674
+ }
1622
1675
 
1623
1676
  const requiredProps = [
1624
1677
  'keystorePath',
@@ -1630,13 +1683,13 @@ function validateAndroidBuildOptions() {
1630
1683
  ];
1631
1684
 
1632
1685
  const missing = requiredProps.filter(prop => !buildOptions[prop]);
1633
-
1686
+
1634
1687
  if (missing.length > 0) {
1635
1688
  console.log('❌ Missing properties android.buildOptions in capacitor.config.json.');
1636
1689
  process.exit(1);
1637
1690
  }
1638
1691
 
1639
-
1692
+
1640
1693
  const keystorePath=buildOptions.keystorePath
1641
1694
  const keyFileName = path.basename(keystorePath);
1642
1695
 
@@ -1826,7 +1879,7 @@ try {
1826
1879
  _admobConfig = getAdMobConfig();
1827
1880
 
1828
1881
 
1829
-
1882
+
1830
1883
 
1831
1884
 
1832
1885
  // Proceed only if ADMOB_ENABLED is true
@@ -2170,7 +2223,9 @@ async function syncAgentInstructions() {
2170
2223
 
2171
2224
  const zipName = `${AGENT_INSTRUCTIONS_ZIP_PREFIX}-${remoteVersion}.zip`;
2172
2225
  const url = `https://htmlcodeplay.com/code-play-plugin/${zipName}`;
2173
- 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-"));
2174
2229
  const zipPath = path.join(tempRoot, zipName);
2175
2230
  const extractPath = path.join(tempRoot, "extracted");
2176
2231
 
@@ -2248,7 +2303,9 @@ if (baseName === "common") {
2248
2303
  if (ext === ".less") pluginKey = "common-less";
2249
2304
  }
2250
2305
 
2251
- const latestVersion = versions[pluginKey];
2306
+ const advertisedVersion = versions[pluginKey];
2307
+ const latestVersion = baseName === 'IAP' && (!advertisedVersion || compareVersionStrings(advertisedVersion, pluginDef.minVersion) < 0)
2308
+ ? pluginDef.minVersion : advertisedVersion;
2252
2309
 
2253
2310
  if (!latestVersion) {
2254
2311
  console.log(`❌ No version entry for ${baseName}`);
@@ -2269,12 +2326,17 @@ if (pluginDef.isFolder) {
2269
2326
 
2270
2327
  if (!(await urlExists(url))) return false;
2271
2328
 
2272
- fs.rmSync(oldPath, { recursive: true, force: true });
2273
-
2274
- await downloadAndExtractZip(url, newPath);
2275
-
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;
2276
2337
  updateImports(pluginInfo.name, `${baseName}-${latestVersion}`);
2277
2338
 
2339
+ fs.rmSync(oldPath, { recursive: true, force: true });
2278
2340
  // ✅ ADD THIS
2279
2341
  writeUpdateLine(`${pluginInfo.name} -> ${baseName}-${latestVersion}`);
2280
2342
 
@@ -2352,7 +2414,7 @@ existingFiles.forEach(file => {
2352
2414
  fs.unlinkSync(oldPath);
2353
2415
 
2354
2416
  console.log(`🗑 Removed old file → ${file}`);
2355
- //writeUpdateLine(`Removed old file: ${file}`);
2417
+ //writeUpdateLine(`Removed old file: ${file}`);
2356
2418
  }
2357
2419
 
2358
2420
  });
@@ -2415,7 +2477,7 @@ async function loadPluginVersions() {
2415
2477
  if (!plugin.name) return;
2416
2478
 
2417
2479
  if (versions[plugin.name]) {
2418
- plugin.minVersion = versions[plugin.name];
2480
+ if (compareVersionStrings(versions[plugin.name], plugin.minVersion) > 0) plugin.minVersion = versions[plugin.name];
2419
2481
  }
2420
2482
 
2421
2483
  });
@@ -2518,7 +2580,7 @@ function checkPlugins() {
2518
2580
  } */
2519
2581
 
2520
2582
 
2521
-
2583
+
2522
2584
 
2523
2585
 
2524
2586
  if (hasMandatoryUpdate) {
@@ -2625,14 +2687,14 @@ if (hasMandatoryUpdate) {
2625
2687
 
2626
2688
 
2627
2689
 
2628
- const localizationBaseDir = path.join(__dirname, "..", "src", "js", "localization");
2690
+ const localizationBaseDir = path.join(process.cwd(), "src", "js", "localization");
2629
2691
 
2630
2692
  // ======================================================
2631
2693
  // 🌐 LOCALIZATION CHECK (FULLY DYNAMIC)
2632
2694
  // ======================================================
2633
2695
 
2634
2696
 
2635
- const jsRootDir = path.join(__dirname, "..", "src", "js");
2697
+ const jsRootDir = path.join(process.cwd(), "src", "js");
2636
2698
 
2637
2699
  // 🔍 Detect OLD localization files in root js/
2638
2700
  const oldLocalizationFiles = fs.readdirSync(jsRootDir)
@@ -3713,6 +3775,7 @@ const ensureIapDesignInCapacitorConfig = (iapConfig) => {
3713
3775
  };
3714
3776
 
3715
3777
  const getCurrentStoreId = () => {
3778
+ if (buildPlatform === 'ios') return 6;
3716
3779
  const envStoreId = Number(process.env.VITE_STORE_ID);
3717
3780
  if (Number.isInteger(envStoreId) && envStoreId > 0) return envStoreId;
3718
3781
 
@@ -3900,6 +3963,17 @@ const syncRevenueCatIapSetup = () => {
3900
3963
  console.log(`ℹ️ RevenueCat package is not required for store id ${storeId}.`);
3901
3964
  }
3902
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);
3903
3977
  ensureAndroidPermission("com.android.vending.BILLING", store?.usesGoogleBilling === true && revenueCatEnabledForCurrentStore);
3904
3978
 
3905
3979
  if (iapConfig.samsung === true && revenueCatEnabled) {
@@ -4169,8 +4243,8 @@ const validateAndRestoreSignDetails=()=>{
4169
4243
 
4170
4244
 
4171
4245
  const filePath = path.join(
4172
- __dirname,
4173
- "../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"
4174
4248
  );
4175
4249
 
4176
4250
  const SYSTEM_BARS_PATCH_MARKER = "CODEPLAY_KEYBOARD_INSET_FIX";
@@ -4388,17 +4462,23 @@ function syncAndroidCordovaResourceKeep() {
4388
4462
  }
4389
4463
 
4390
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
+ }
4391
4471
  validateThemeFolderLocation();
4392
4472
  validateAndRestoreSignDetails();
4393
4473
  ensureMandatoryCapacitorPlugins();
4394
4474
 
4395
4475
  await loadPluginVersions(); // 🔥 NEW
4396
4476
  await syncAgentInstructions();
4477
+ await checkPlugins();
4397
4478
 
4398
4479
  await syncAdmobNextgenMigration();
4399
4480
  syncCodeplaySystemBarsPackage();
4400
4481
  syncSystemBarsSafeAreaMigration();
4401
- await checkPlugins();
4402
4482
  ensureIapStartupOnHomePage();
4403
4483
  syncRevenueCatIapSetup();
4404
4484
  syncFirebaseAnalyticsSetup();