customerio-expo-plugin 2.4.0 → 2.6.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.
Files changed (161) hide show
  1. package/package.json +13 -31
  2. package/plugin/app.plugin.js +1 -1
  3. package/plugin/lib/commonjs/android/withAndroidManifestUpdates.js +4 -4
  4. package/plugin/lib/commonjs/android/withAndroidManifestUpdates.js.map +1 -1
  5. package/plugin/lib/commonjs/android/withCIOAndroid.js +6 -1
  6. package/plugin/lib/commonjs/android/withCIOAndroid.js.map +1 -1
  7. package/plugin/lib/commonjs/android/withGoogleServicesJSON.js +1 -1
  8. package/plugin/lib/commonjs/android/withGoogleServicesJSON.js.map +1 -1
  9. package/plugin/lib/commonjs/android/withMainApplicationModifications.js +45 -0
  10. package/plugin/lib/commonjs/android/withMainApplicationModifications.js.map +1 -0
  11. package/plugin/lib/commonjs/android/withNotificationChannelMetadata.js +1 -1
  12. package/plugin/lib/commonjs/android/withNotificationChannelMetadata.js.map +1 -1
  13. package/plugin/lib/commonjs/android/withProjectStrings.js +14 -7
  14. package/plugin/lib/commonjs/android/withProjectStrings.js.map +1 -1
  15. package/plugin/lib/commonjs/helpers/constants/android.js +7 -1
  16. package/plugin/lib/commonjs/helpers/constants/android.js.map +1 -1
  17. package/plugin/lib/commonjs/helpers/constants/common.js +18 -0
  18. package/plugin/lib/commonjs/helpers/constants/common.js.map +1 -0
  19. package/plugin/lib/commonjs/helpers/constants/ios.js +7 -7
  20. package/plugin/lib/commonjs/helpers/constants/ios.js.map +1 -1
  21. package/plugin/lib/commonjs/helpers/native-files/android/CustomerIOSDKInitializer.kt +64 -0
  22. package/plugin/lib/commonjs/helpers/native-files/ios/CustomerIOSDKInitializer.swift +54 -0
  23. package/plugin/lib/commonjs/helpers/utils/injectCIOPodfileCode.js +2 -2
  24. package/plugin/lib/commonjs/helpers/utils/injectCIOPodfileCode.js.map +1 -1
  25. package/plugin/lib/commonjs/helpers/utils/patchPluginNativeCode.js +62 -0
  26. package/plugin/lib/commonjs/helpers/utils/patchPluginNativeCode.js.map +1 -0
  27. package/plugin/lib/commonjs/index.js +13 -2
  28. package/plugin/lib/commonjs/index.js.map +1 -1
  29. package/plugin/lib/commonjs/ios/utils.js +1 -1
  30. package/plugin/lib/commonjs/ios/utils.js.map +1 -1
  31. package/plugin/lib/commonjs/ios/withAppDelegateModifications.js +1 -1
  32. package/plugin/lib/commonjs/ios/withAppDelegateModifications.js.map +1 -1
  33. package/plugin/lib/commonjs/ios/withCIOIos.js +17 -11
  34. package/plugin/lib/commonjs/ios/withCIOIos.js.map +1 -1
  35. package/plugin/lib/commonjs/ios/withCIOIosSwift.js +133 -49
  36. package/plugin/lib/commonjs/ios/withCIOIosSwift.js.map +1 -1
  37. package/plugin/lib/commonjs/ios/withGoogleServicesJsonFile.js +1 -1
  38. package/plugin/lib/commonjs/ios/withGoogleServicesJsonFile.js.map +1 -1
  39. package/plugin/lib/commonjs/ios/withNotificationsXcodeProject.js +17 -19
  40. package/plugin/lib/commonjs/ios/withNotificationsXcodeProject.js.map +1 -1
  41. package/plugin/lib/commonjs/ios/withXcodeProject.js +1 -1
  42. package/plugin/lib/commonjs/ios/withXcodeProject.js.map +1 -1
  43. package/plugin/lib/commonjs/postInstallHelper.js.map +1 -1
  44. package/plugin/lib/commonjs/types/cio-types.js.map +1 -1
  45. package/plugin/lib/commonjs/utils/android.js +109 -0
  46. package/plugin/lib/commonjs/utils/android.js.map +1 -0
  47. package/plugin/lib/commonjs/utils/config.js +43 -0
  48. package/plugin/lib/commonjs/utils/config.js.map +1 -0
  49. package/plugin/lib/commonjs/utils/plugin.js +49 -0
  50. package/plugin/lib/commonjs/utils/plugin.js.map +1 -0
  51. package/plugin/lib/commonjs/utils/validation.js +44 -0
  52. package/plugin/lib/commonjs/utils/validation.js.map +1 -0
  53. package/plugin/lib/commonjs/utils/xcode.js +67 -0
  54. package/plugin/lib/commonjs/utils/xcode.js.map +1 -0
  55. package/plugin/lib/module/android/withAndroidManifestUpdates.js +4 -4
  56. package/plugin/lib/module/android/withAndroidManifestUpdates.js.map +1 -1
  57. package/plugin/lib/module/android/withCIOAndroid.js +6 -1
  58. package/plugin/lib/module/android/withCIOAndroid.js.map +1 -1
  59. package/plugin/lib/module/android/withGoogleServicesJSON.js +1 -1
  60. package/plugin/lib/module/android/withGoogleServicesJSON.js.map +1 -1
  61. package/plugin/lib/module/android/withMainApplicationModifications.js +38 -0
  62. package/plugin/lib/module/android/withMainApplicationModifications.js.map +1 -0
  63. package/plugin/lib/module/android/withNotificationChannelMetadata.js +1 -1
  64. package/plugin/lib/module/android/withNotificationChannelMetadata.js.map +1 -1
  65. package/plugin/lib/module/android/withProjectStrings.js +13 -6
  66. package/plugin/lib/module/android/withProjectStrings.js.map +1 -1
  67. package/plugin/lib/module/helpers/constants/android.js +6 -0
  68. package/plugin/lib/module/helpers/constants/android.js.map +1 -1
  69. package/plugin/lib/module/helpers/constants/common.js +12 -0
  70. package/plugin/lib/module/helpers/constants/common.js.map +1 -0
  71. package/plugin/lib/module/helpers/constants/ios.js +6 -6
  72. package/plugin/lib/module/helpers/constants/ios.js.map +1 -1
  73. package/plugin/lib/module/helpers/native-files/android/CustomerIOSDKInitializer.kt +64 -0
  74. package/plugin/lib/module/helpers/native-files/ios/CustomerIOSDKInitializer.swift +54 -0
  75. package/plugin/lib/module/helpers/utils/injectCIOPodfileCode.js +2 -2
  76. package/plugin/lib/module/helpers/utils/injectCIOPodfileCode.js.map +1 -1
  77. package/plugin/lib/module/helpers/utils/patchPluginNativeCode.js +57 -0
  78. package/plugin/lib/module/helpers/utils/patchPluginNativeCode.js.map +1 -0
  79. package/plugin/lib/module/index.js +14 -2
  80. package/plugin/lib/module/index.js.map +1 -1
  81. package/plugin/lib/module/ios/utils.js +1 -2
  82. package/plugin/lib/module/ios/utils.js.map +1 -1
  83. package/plugin/lib/module/ios/withAppDelegateModifications.js +3 -3
  84. package/plugin/lib/module/ios/withAppDelegateModifications.js.map +1 -1
  85. package/plugin/lib/module/ios/withCIOIos.js +17 -11
  86. package/plugin/lib/module/ios/withCIOIos.js.map +1 -1
  87. package/plugin/lib/module/ios/withCIOIosSwift.js +134 -50
  88. package/plugin/lib/module/ios/withCIOIosSwift.js.map +1 -1
  89. package/plugin/lib/module/ios/withGoogleServicesJsonFile.js +2 -2
  90. package/plugin/lib/module/ios/withGoogleServicesJsonFile.js.map +1 -1
  91. package/plugin/lib/module/ios/withNotificationsXcodeProject.js +18 -20
  92. package/plugin/lib/module/ios/withNotificationsXcodeProject.js.map +1 -1
  93. package/plugin/lib/module/ios/withXcodeProject.js +1 -1
  94. package/plugin/lib/module/ios/withXcodeProject.js.map +1 -1
  95. package/plugin/lib/module/postInstallHelper.js.map +1 -1
  96. package/plugin/lib/module/types/cio-types.js.map +1 -1
  97. package/plugin/lib/module/utils/android.js +100 -0
  98. package/plugin/lib/module/utils/android.js.map +1 -0
  99. package/plugin/lib/module/utils/config.js +38 -0
  100. package/plugin/lib/module/utils/config.js.map +1 -0
  101. package/plugin/lib/module/utils/plugin.js +38 -0
  102. package/plugin/lib/module/utils/plugin.js.map +1 -0
  103. package/plugin/lib/module/utils/validation.js +39 -0
  104. package/plugin/lib/module/utils/validation.js.map +1 -0
  105. package/plugin/lib/module/utils/xcode.js +60 -0
  106. package/plugin/lib/module/utils/xcode.js.map +1 -0
  107. package/plugin/lib/typescript/android/withCIOAndroid.d.ts +2 -2
  108. package/plugin/lib/typescript/android/withMainApplicationModifications.d.ts +3 -0
  109. package/plugin/lib/typescript/android/withProjectStrings.d.ts +2 -1
  110. package/plugin/lib/typescript/helpers/constants/android.d.ts +3 -0
  111. package/plugin/lib/typescript/helpers/constants/common.d.ts +11 -0
  112. package/plugin/lib/typescript/helpers/constants/ios.d.ts +3 -1
  113. package/plugin/lib/typescript/helpers/utils/patchPluginNativeCode.d.ts +7 -0
  114. package/plugin/lib/typescript/ios/utils.d.ts +2 -2
  115. package/plugin/lib/typescript/ios/withCIOIos.d.ts +2 -2
  116. package/plugin/lib/typescript/ios/withCIOIosSwift.d.ts +3 -3
  117. package/plugin/lib/typescript/types/cio-types.d.ts +46 -6
  118. package/plugin/lib/typescript/utils/android.d.ts +5 -0
  119. package/plugin/lib/typescript/utils/config.d.ts +8 -0
  120. package/plugin/lib/typescript/utils/plugin.d.ts +4 -0
  121. package/plugin/lib/typescript/utils/validation.d.ts +3 -0
  122. package/plugin/lib/typescript/utils/xcode.d.ts +28 -0
  123. package/plugin/src/android/withAndroidManifestUpdates.ts +5 -5
  124. package/plugin/src/android/withCIOAndroid.ts +7 -1
  125. package/plugin/src/android/withGoogleServicesJSON.ts +2 -2
  126. package/plugin/src/android/withMainApplicationModifications.ts +50 -0
  127. package/plugin/src/android/withNotificationChannelMetadata.ts +7 -3
  128. package/plugin/src/android/withProjectStrings.ts +20 -10
  129. package/plugin/src/helpers/constants/android.ts +7 -0
  130. package/plugin/src/helpers/constants/common.ts +12 -0
  131. package/plugin/src/helpers/constants/ios.ts +11 -13
  132. package/plugin/src/helpers/native-files/android/CustomerIOSDKInitializer.kt +64 -0
  133. package/plugin/src/helpers/native-files/ios/CustomerIOSDKInitializer.swift +54 -0
  134. package/plugin/src/helpers/utils/injectCIOPodfileCode.ts +8 -7
  135. package/plugin/src/helpers/utils/patchPluginNativeCode.ts +97 -0
  136. package/plugin/src/index.ts +18 -2
  137. package/plugin/src/ios/utils.ts +5 -5
  138. package/plugin/src/ios/withAppDelegateModifications.ts +11 -8
  139. package/plugin/src/ios/withCIOIos.ts +19 -11
  140. package/plugin/src/ios/withCIOIosSwift.ts +195 -73
  141. package/plugin/src/ios/withGoogleServicesJsonFile.ts +7 -10
  142. package/plugin/src/ios/withNotificationsXcodeProject.ts +25 -26
  143. package/plugin/src/ios/withXcodeProject.ts +1 -1
  144. package/plugin/src/postInstallHelper.js +1 -1
  145. package/plugin/src/types/cio-types.ts +48 -8
  146. package/plugin/src/utils/android.ts +112 -0
  147. package/plugin/src/utils/config.ts +53 -0
  148. package/plugin/src/utils/plugin.ts +46 -0
  149. package/plugin/src/utils/validation.ts +54 -0
  150. package/plugin/src/utils/xcode.ts +74 -0
  151. package/plugin/lib/commonjs/helpers/constants/globals.d.js +0 -2
  152. package/plugin/lib/commonjs/helpers/constants/globals.d.js.map +0 -1
  153. package/plugin/lib/commonjs/helpers/utils/pluginUtils.js +0 -26
  154. package/plugin/lib/commonjs/helpers/utils/pluginUtils.js.map +0 -1
  155. package/plugin/lib/module/helpers/constants/globals.d.js +0 -2
  156. package/plugin/lib/module/helpers/constants/globals.d.js.map +0 -1
  157. package/plugin/lib/module/helpers/utils/pluginUtils.js +0 -19
  158. package/plugin/lib/module/helpers/utils/pluginUtils.js.map +0 -1
  159. package/plugin/lib/typescript/helpers/utils/pluginUtils.d.ts +0 -4
  160. package/plugin/src/helpers/constants/globals.d.ts +0 -8
  161. package/plugin/src/helpers/utils/pluginUtils.ts +0 -22
@@ -1,11 +1,17 @@
1
- // properties set by the user in their app config file (e.g: app.json or app.plugin.js)
1
+ /**
2
+ * Properties set by the user in their app config file (e.g: app.json or app.plugin.js)
3
+ * @public
4
+ */
2
5
  export type CustomerIOPluginProperties = {
3
6
  // (iOS only) Environment name and bundle identifier
4
7
  devTeam: string;
5
8
  iosDeploymentTarget: string;
6
9
  };
7
10
 
8
- // Plugin options for pre-build
11
+ /**
12
+ * Plugin options for iOS platform configuration
13
+ * @public
14
+ */
9
15
  export type CustomerIOPluginOptionsIOS = {
10
16
  iosPath: string;
11
17
  devTeam?: string;
@@ -51,6 +57,10 @@ export type CustomerIOPluginOptionsIOS = {
51
57
  disableNotificationRegistration?: boolean;
52
58
  };
53
59
 
60
+ /**
61
+ * Plugin options for Android platform configuration
62
+ * @public
63
+ */
54
64
  export type CustomerIOPluginOptionsAndroid = {
55
65
  androidPath: string;
56
66
  googleServicesFile?: string;
@@ -64,11 +74,44 @@ export type CustomerIOPluginOptionsAndroid = {
64
74
  };
65
75
  };
66
76
 
77
+ /**
78
+ * SDK configuration options for auto initialization
79
+ * @public
80
+ */
81
+ export type NativeSDKConfig = {
82
+ cdpApiKey: string; // Required
83
+ region?: 'US' | 'EU'; // Default: 'US'. The workspace region set for your workspace on the Customer.io dashboard
84
+ autoTrackDeviceAttributes?: boolean; // Default: true
85
+ trackApplicationLifecycleEvents?: boolean; // Default: true
86
+ screenViewUse?: 'all' | 'inapp'; // Default: 'all'. 'all': sent to server + in-app messages, 'inapp': in-app messages only
87
+ logLevel?: 'none' | 'error' | 'info' | 'debug'; // Default: 'debug'. Controls SDK logging verbosity
88
+ siteId?: string; // Optional, if only siteId defined, migrationSiteId = siteId
89
+ migrationSiteId?: string; // Optional, if only migrationSiteId defined, siteId should be null
90
+ };
91
+
92
+ /**
93
+ * Combined plugin options for both iOS and Android platforms
94
+ * @public
95
+ */
67
96
  export type CustomerIOPluginOptions = {
97
+ config?: NativeSDKConfig; // If defined, enables auto initialization of native SDK
68
98
  android: CustomerIOPluginOptionsAndroid;
69
99
  ios: CustomerIOPluginOptionsIOS;
70
100
  };
71
101
 
102
+ /**
103
+ * Rich push configuration used to initialize Notification Service Extension (NSE) on the native side
104
+ * @public
105
+ */
106
+ export type RichPushConfig = {
107
+ cdpApiKey: string;
108
+ region?: string;
109
+ };
110
+
111
+ /**
112
+ * Push notification configuration options
113
+ * @public
114
+ */
72
115
  export type CustomerIOPluginPushNotificationOptions = {
73
116
  provider?: 'apn' | 'fcm';
74
117
  googleServicesFile?: string;
@@ -80,11 +123,8 @@ export type CustomerIOPluginPushNotificationOptions = {
80
123
  handleDeeplinkInKilledState?: boolean;
81
124
 
82
125
  /**
83
- * These values will be used to initialize the Notification Service Extension (NSE) on the native side.
84
- * They should match the values you use to initialize the SDK in your app
126
+ * Rich push config should match the values used to initialize SDK in the app.
127
+ * Optional if `config` is provided at the top level.
85
128
  */
86
- env: {
87
- cdpApiKey: string;
88
- region: string;
89
- };
129
+ env?: RichPushConfig;
90
130
  };
@@ -0,0 +1,112 @@
1
+ import type { ExportedConfigWithProps } from '@expo/config-plugins';
2
+ import type { ApplicationProjectFile } from '@expo/config-plugins/build/android/Paths';
3
+ import path from 'path';
4
+ import { FileManagement } from '../helpers/utils/fileManagement';
5
+ import { getAndroidNativeFilesPath } from './plugin';
6
+
7
+ // Generic utility to add import to Kotlin files
8
+ export const addImportToFile = (content: string, importStatement: string): string => {
9
+ if (content.includes(importStatement)) {
10
+ return content;
11
+ }
12
+
13
+ const importRegex = /^import\s+[^\s\n]+.*$/gm;
14
+ const imports = [...content.matchAll(importRegex)];
15
+
16
+ if (imports.length === 0) {
17
+ const packageRegex = /package\s+[^\s;]+[;\s]*\n/;
18
+ const packageMatch = content.match(packageRegex);
19
+ if (!packageMatch) return content;
20
+
21
+ const insertPosition = (packageMatch.index ?? 0) + packageMatch[0].length;
22
+ return content.substring(0, insertPosition) + `\n${importStatement}\n` + content.substring(insertPosition);
23
+ }
24
+
25
+ const lastImport = imports[imports.length - 1];
26
+ const insertPosition = (lastImport.index ?? 0) + lastImport[0].length;
27
+ return content.substring(0, insertPosition) + `\n\n${importStatement}` + content.substring(insertPosition);
28
+ };
29
+
30
+ // Find matching bracket position (simplified version inspired by the library method)
31
+ const findMatchingBracketPosition = (content: string, bracket: string, startPos: number): number => {
32
+ const openBracket = bracket;
33
+ const closeBracket = bracket === '{' ? '}' : bracket === '(' ? ')' : ']';
34
+
35
+ let depth = 0;
36
+ let foundOpen = false;
37
+
38
+ for (let i = startPos; i < content.length; i++) {
39
+ const char = content[i];
40
+ if (char === openBracket) {
41
+ foundOpen = true;
42
+ depth++;
43
+ } else if (char === closeBracket) {
44
+ depth--;
45
+ if (foundOpen && depth === 0) {
46
+ return i;
47
+ }
48
+ }
49
+ }
50
+ return -1;
51
+ };
52
+
53
+ // Generic utility to add code to end of a method (before closing brace)
54
+ export const addCodeToMethod = (content: string, methodRegex: RegExp, codeToAdd: string): string => {
55
+ const methodMatch = content.match(methodRegex);
56
+ if (!methodMatch || methodMatch.index === undefined) return content;
57
+
58
+ const methodStart = methodMatch.index;
59
+ const methodContent = methodMatch[0];
60
+
61
+ // Find the opening brace position within the method match
62
+ const openBraceIndex = methodContent.indexOf('{');
63
+ if (openBraceIndex === -1) return content;
64
+
65
+ // Find the matching closing brace
66
+ const absoluteOpenBracePos = methodStart + openBraceIndex;
67
+ const closeBracePos = findMatchingBracketPosition(content, '{', absoluteOpenBracePos);
68
+ if (closeBracePos === -1) return content;
69
+
70
+ // Detect indentation of the method's opening brace line
71
+ const linesUpToOpenBrace = content.substring(0, absoluteOpenBracePos).split('\n');
72
+ const braceLine = linesUpToOpenBrace[linesUpToOpenBrace.length - 1];
73
+ const methodIndentMatch = braceLine.match(/^(\s*)/);
74
+ const methodIndent = methodIndentMatch ? methodIndentMatch[1] : '';
75
+ // Assume one indentation level is two spaces if not detected from next line
76
+ let indentLevel = ' ';
77
+ // Try to detect indentation from the next line after the opening brace
78
+ const afterBrace = content.substring(absoluteOpenBracePos + 1, closeBracePos);
79
+ const afterBraceLines = afterBrace.split('\n').filter(l => l.trim().length > 0);
80
+ if (afterBraceLines.length > 0) {
81
+ const nextLineIndentMatch = afterBraceLines[0].match(/^(\s*)/);
82
+ if (nextLineIndentMatch && nextLineIndentMatch[1].length > methodIndent.length) {
83
+ indentLevel = nextLineIndentMatch[1].slice(methodIndent.length);
84
+ }
85
+ }
86
+ const finalIndent = methodIndent + indentLevel;
87
+ // Insert code just before the closing brace, with detected indentation
88
+ return content.substring(0, closeBracePos) + '\n' + finalIndent + codeToAdd + '\n' + methodIndent + content.substring(closeBracePos);
89
+ };
90
+
91
+ // Copy template file to Android project with content transformation
92
+ export const copyTemplateFile = (
93
+ expoConfig: ExportedConfigWithProps<ApplicationProjectFile>,
94
+ filename: string,
95
+ classPackage: string,
96
+ patchContent: (content: string) => string,
97
+ ): void => {
98
+ const projectRoot = expoConfig.modRequest.projectRoot;
99
+ const mainSourceDir = path.join(projectRoot, 'android/app/src/main/java');
100
+ const packagePath = path.join(mainSourceDir, classPackage.replace(/\./g, '/'));
101
+
102
+ try {
103
+ FileManagement.mkdir(packagePath, { recursive: true });
104
+ const sourcePath = path.join(getAndroidNativeFilesPath(), filename);
105
+ const content = patchContent(FileManagement.readFile(sourcePath));
106
+ const destinationPath = path.join(packagePath, filename);
107
+ FileManagement.writeFile(destinationPath, content);
108
+ } catch (error) {
109
+ console.warn(`Failed to copy ${filename} to Android project:`, error);
110
+ throw error;
111
+ }
112
+ };
@@ -0,0 +1,53 @@
1
+ import type { CustomerIOPluginOptionsIOS, NativeSDKConfig, RichPushConfig } from '../types/cio-types';
2
+
3
+ /**
4
+ * Merges config values with env values for backward compatibility.
5
+ * If env is provided, it takes precedence. If nativeConfig is provided but env is not,
6
+ * nativeConfig values are used. This prioritizes existing env configuration for backward compatibility.
7
+ */
8
+ function mergeConfigWithEnvValues(
9
+ props: CustomerIOPluginOptionsIOS,
10
+ nativeConfig?: NativeSDKConfig
11
+ ): RichPushConfig | undefined {
12
+ const nativeCdpApiKey = nativeConfig?.cdpApiKey;
13
+ const nativeRegion = nativeConfig?.region;
14
+
15
+ const envConfig = props.pushNotification?.env;
16
+ const envCdpApiKey = envConfig?.cdpApiKey;
17
+ const envRegion = envConfig?.region;
18
+
19
+ // Check for conflicts between env and nativeConfig
20
+ if (nativeCdpApiKey && envCdpApiKey) {
21
+ if (nativeCdpApiKey !== envCdpApiKey || nativeRegion?.toLowerCase() !== envRegion?.toLowerCase()) {
22
+ const errorMessage = `Configuration conflict: 'config' and 'ios.pushNotification.env' values must match when both are provided.\n` +
23
+ ` config.cdpApiKey: "${nativeCdpApiKey}"\n` +
24
+ ` env.cdpApiKey: "${envCdpApiKey}"\n` +
25
+ ` config.region: "${nativeRegion}"\n` +
26
+ ` env.region: "${envRegion}"`;
27
+
28
+ console.error(errorMessage);
29
+ throw new Error(errorMessage);
30
+ }
31
+
32
+ // Values match - warn about redundant configuration
33
+ console.warn(
34
+ `Both 'config' and 'ios.pushNotification.env' are provided with matching values. ` +
35
+ `Consider removing 'ios.pushNotification.env' since 'config' is already specified.`
36
+ );
37
+ }
38
+
39
+ // Return config (values are guaranteed to be the same if both exist)
40
+ const cdpApiKey = nativeCdpApiKey || envCdpApiKey;
41
+ const region = nativeRegion || envRegion;
42
+
43
+ if (cdpApiKey) {
44
+ return {
45
+ cdpApiKey,
46
+ region,
47
+ };
48
+ }
49
+
50
+ return undefined;
51
+ }
52
+
53
+ export { mergeConfigWithEnvValues };
@@ -0,0 +1,46 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ const findPluginPackageRoot = (): string => {
5
+ const finder = require('find-package-json');
6
+ const f = finder(__dirname);
7
+ const root = f.next().filename;
8
+ return path.dirname(root);
9
+ };
10
+
11
+ const pluginPackageRoot = findPluginPackageRoot();
12
+
13
+ // Returns path to plugin's native template files directory
14
+ export const getNativeFilesPath = (): string => {
15
+ return path.join(
16
+ pluginPackageRoot,
17
+ 'plugin/src/helpers/native-files/'
18
+ );
19
+ };
20
+
21
+ // Returns path to plugin's Android native template files
22
+ export const getAndroidNativeFilesPath = (): string => {
23
+ return path.join(getNativeFilesPath(), 'android');
24
+ };
25
+
26
+ // Returns path to plugin's iOS native template files
27
+ export const getIosNativeFilesPath = (): string => {
28
+ return path.join(getNativeFilesPath(), 'ios');
29
+ };
30
+
31
+ // Reads the version of the plugin from its `package.json` and returns it as a string.
32
+ export const getPluginVersion = (): string => {
33
+ const packageJsonPath = path.resolve(
34
+ pluginPackageRoot,
35
+ 'package.json'
36
+ );
37
+ if (!fs.existsSync(packageJsonPath)) {
38
+ throw new Error(`package.json not found at ${packageJsonPath}`);
39
+ }
40
+
41
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
42
+ if (!packageJson.version) {
43
+ throw new Error(`"version" field is missing in ${packageJsonPath}`);
44
+ }
45
+ return packageJson.version;
46
+ };
@@ -0,0 +1,54 @@
1
+ import type { NativeSDKConfig } from '../types/cio-types';
2
+
3
+ function validateRequired(value: unknown, fieldName: string, context: string): void {
4
+ if (value === undefined || value === null) {
5
+ throw new Error(`${context}: ${fieldName} is required, received: ${value}`);
6
+ }
7
+ }
8
+
9
+ function validateString(value: unknown, fieldName: string, context: string): void {
10
+ if (value !== undefined && (typeof value !== 'string' || value.trim() === '')) {
11
+ throw new Error(`${context}: ${fieldName} must be a non-empty string, received: ${typeof value === 'string' ? `"${value}"` : value}`);
12
+ }
13
+ }
14
+
15
+ function validateBoolean(value: unknown, fieldName: string, context: string): void {
16
+ if (value !== undefined && typeof value !== 'boolean') {
17
+ throw new Error(`${context}: ${fieldName} must be a boolean, received: ${value}`);
18
+ }
19
+ }
20
+
21
+ function validateEnum<T extends string>(
22
+ value: unknown,
23
+ fieldName: string,
24
+ allowedValues: readonly T[],
25
+ context: string
26
+ ): void {
27
+ if (value === undefined) return;
28
+
29
+ validateString(value, fieldName, context);
30
+
31
+ const lowerValue = (value as string).toLowerCase();
32
+ const lowerAllowedValues = allowedValues.map(v => v.toLowerCase());
33
+ if (!lowerAllowedValues.includes(lowerValue)) {
34
+ const valuesStr = allowedValues.map(v => `"${v}"`).join(', ');
35
+ throw new Error(`${context}: ${fieldName} must be one of ${valuesStr}, received: ${value}`);
36
+ }
37
+ }
38
+
39
+ function validateNativeSDKConfig(config: NativeSDKConfig): void {
40
+ const context = 'NativeSDKConfig';
41
+
42
+ validateRequired(config.cdpApiKey, 'cdpApiKey', context);
43
+ validateString(config.cdpApiKey, 'cdpApiKey', context);
44
+
45
+ validateEnum(config.region, 'region', ['US', 'EU'] as const, context);
46
+ validateEnum(config.screenViewUse, 'screenViewUse', ['all', 'inapp'] as const, context);
47
+ validateEnum(config.logLevel, 'logLevel', ['none', 'error', 'info', 'debug'] as const, context);
48
+ validateBoolean(config.autoTrackDeviceAttributes, 'autoTrackDeviceAttributes', context);
49
+ validateBoolean(config.trackApplicationLifecycleEvents, 'trackApplicationLifecycleEvents', context);
50
+ validateString(config.siteId, 'siteId', context);
51
+ validateString(config.migrationSiteId, 'migrationSiteId', context);
52
+ }
53
+
54
+ export { validateNativeSDKConfig };
@@ -0,0 +1,74 @@
1
+ import type { XcodeProject } from "@expo/config-plugins";
2
+ import path from 'path';
3
+ import { FileManagement } from "../helpers/utils/fileManagement";
4
+
5
+ /**
6
+ * Gets an existing CustomerIO group or creates a new one in the Xcode project
7
+ * @param xcodeProject The Xcode project instance
8
+ * @param projectName The iOS project name
9
+ * @returns The CustomerIO group reference
10
+ */
11
+ export function getOrCreateCustomerIOGroup(
12
+ xcodeProject: XcodeProject,
13
+ projectName: string,
14
+ ): XcodeProject['pbxCreateGroup'] {
15
+ // Check if CustomerIO group already exists
16
+ let customerIOGroup = xcodeProject.pbxGroupByName('CustomerIO');
17
+ if (customerIOGroup) {
18
+ return customerIOGroup;
19
+ }
20
+
21
+ // Create new CustomerIO group and add it to the project
22
+ customerIOGroup = xcodeProject.pbxCreateGroup('CustomerIO');
23
+ const projectGroupKey = xcodeProject.findPBXGroupKey({ name: projectName });
24
+ xcodeProject.addToPbxGroup(customerIOGroup, projectGroupKey);
25
+ return customerIOGroup;
26
+ }
27
+
28
+ /**
29
+ * Copies template file to iOS project, applies transformations, and registers with Xcode
30
+ * @param params.xcodeProject Xcode project instance
31
+ * @param params.iosProjectRoot iOS project root path
32
+ * @param params.projectName iOS project name
33
+ * @param params.sourceFilePath Source template file path
34
+ * @param params.targetFileName Target file name
35
+ * @param params.transform Content transformation function
36
+ * @param params.customerIOGroup CustomerIO group (auto-created if not provided)
37
+ * @returns Destination file path
38
+ */
39
+ export function copyFileToXcode({
40
+ xcodeProject,
41
+ iosProjectRoot,
42
+ projectName,
43
+ sourceFilePath,
44
+ targetFileName,
45
+ transform,
46
+ customerIOGroup = getOrCreateCustomerIOGroup(xcodeProject, projectName),
47
+ }: {
48
+ xcodeProject: XcodeProject;
49
+ iosProjectRoot: string;
50
+ projectName: string;
51
+ sourceFilePath: string;
52
+ targetFileName: string;
53
+ transform: (content: string) => string;
54
+ customerIOGroup?: XcodeProject['pbxCreateGroup'];
55
+ }): string {
56
+ // Construct the full destination path within the iOS project directory
57
+ const destinationPath = path.join(
58
+ iosProjectRoot,
59
+ projectName,
60
+ targetFileName
61
+ );
62
+
63
+ try {
64
+ // Read template, apply transformations, and write to project
65
+ const content = transform(FileManagement.readFile(sourceFilePath));
66
+ FileManagement.writeFile(destinationPath, content);
67
+ // Register file with Xcode project
68
+ xcodeProject.addSourceFile(`${projectName}/${targetFileName}`, null, customerIOGroup);
69
+ return destinationPath;
70
+ } catch (error) {
71
+ console.warn(`Failed to add ${targetFileName} to Xcode project:`, error);
72
+ throw error;
73
+ }
74
+ }
@@ -1,2 +0,0 @@
1
- "use strict";
2
- //# sourceMappingURL=globals.d.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":[],"sources":["globals.d.ts"],"sourcesContent":["declare module 'xcode' {\n interface xcode {\n project(projPath: string): any;\n }\n\n const xcode: xcode;\n export default xcode;\n}\n"],"mappings":"","ignoreList":[]}
@@ -1,26 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.getPluginVersion = void 0;
7
- var _fs = _interopRequireDefault(require("fs"));
8
- var _path = _interopRequireDefault(require("path"));
9
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
- /**
11
- * Reads the version of the plugin from its `package.json` and returns it as a string.
12
- */
13
- const getPluginVersion = () => {
14
- // Always resolves relative to the utility file's location
15
- const packageJsonPath = _path.default.resolve(__dirname, '../../../../../package.json');
16
- if (!_fs.default.existsSync(packageJsonPath)) {
17
- throw new Error(`package.json not found at ${packageJsonPath}`);
18
- }
19
- const packageJson = JSON.parse(_fs.default.readFileSync(packageJsonPath, 'utf8'));
20
- if (!packageJson.version) {
21
- throw new Error(`"version" field is missing in ${packageJsonPath}`);
22
- }
23
- return packageJson.version;
24
- };
25
- exports.getPluginVersion = getPluginVersion;
26
- //# sourceMappingURL=pluginUtils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["_fs","_interopRequireDefault","require","_path","e","__esModule","default","getPluginVersion","packageJsonPath","path","resolve","__dirname","fs","existsSync","Error","packageJson","JSON","parse","readFileSync","version","exports"],"sources":["pluginUtils.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\n/**\n * Reads the version of the plugin from its `package.json` and returns it as a string.\n */\nexport const getPluginVersion = (): string => {\n // Always resolves relative to the utility file's location\n const packageJsonPath = path.resolve(__dirname, '../../../../../package.json');\n\n if (!fs.existsSync(packageJsonPath)) {\n throw new Error(`package.json not found at ${packageJsonPath}`);\n }\n\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));\n\n if (!packageJson.version) {\n throw new Error(`\"version\" field is missing in ${packageJsonPath}`);\n }\n\n return packageJson.version;\n};\n"],"mappings":";;;;;;AAAA,IAAAA,GAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,KAAA,GAAAF,sBAAA,CAAAC,OAAA;AAAwB,SAAAD,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAExB;AACA;AACA;AACO,MAAMG,gBAAgB,GAAGA,CAAA,KAAc;EAC5C;EACA,MAAMC,eAAe,GAAGC,aAAI,CAACC,OAAO,CAACC,SAAS,EAAE,6BAA6B,CAAC;EAE9E,IAAI,CAACC,WAAE,CAACC,UAAU,CAACL,eAAe,CAAC,EAAE;IACnC,MAAM,IAAIM,KAAK,CAAC,6BAA6BN,eAAe,EAAE,CAAC;EACjE;EAEA,MAAMO,WAAW,GAAGC,IAAI,CAACC,KAAK,CAACL,WAAE,CAACM,YAAY,CAACV,eAAe,EAAE,MAAM,CAAC,CAAC;EAExE,IAAI,CAACO,WAAW,CAACI,OAAO,EAAE;IACxB,MAAM,IAAIL,KAAK,CAAC,iCAAiCN,eAAe,EAAE,CAAC;EACrE;EAEA,OAAOO,WAAW,CAACI,OAAO;AAC5B,CAAC;AAACC,OAAA,CAAAb,gBAAA,GAAAA,gBAAA","ignoreList":[]}
@@ -1,2 +0,0 @@
1
-
2
- //# sourceMappingURL=globals.d.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":[],"sources":["globals.d.ts"],"sourcesContent":["declare module 'xcode' {\n interface xcode {\n project(projPath: string): any;\n }\n\n const xcode: xcode;\n export default xcode;\n}\n"],"mappings":"","ignoreList":[]}
@@ -1,19 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
-
4
- /**
5
- * Reads the version of the plugin from its `package.json` and returns it as a string.
6
- */
7
- export const getPluginVersion = () => {
8
- // Always resolves relative to the utility file's location
9
- const packageJsonPath = path.resolve(__dirname, '../../../../../package.json');
10
- if (!fs.existsSync(packageJsonPath)) {
11
- throw new Error(`package.json not found at ${packageJsonPath}`);
12
- }
13
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
14
- if (!packageJson.version) {
15
- throw new Error(`"version" field is missing in ${packageJsonPath}`);
16
- }
17
- return packageJson.version;
18
- };
19
- //# sourceMappingURL=pluginUtils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["fs","path","getPluginVersion","packageJsonPath","resolve","__dirname","existsSync","Error","packageJson","JSON","parse","readFileSync","version"],"sources":["pluginUtils.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\n/**\n * Reads the version of the plugin from its `package.json` and returns it as a string.\n */\nexport const getPluginVersion = (): string => {\n // Always resolves relative to the utility file's location\n const packageJsonPath = path.resolve(__dirname, '../../../../../package.json');\n\n if (!fs.existsSync(packageJsonPath)) {\n throw new Error(`package.json not found at ${packageJsonPath}`);\n }\n\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));\n\n if (!packageJson.version) {\n throw new Error(`\"version\" field is missing in ${packageJsonPath}`);\n }\n\n return packageJson.version;\n};\n"],"mappings":"AAAA,OAAOA,EAAE,MAAM,IAAI;AACnB,OAAOC,IAAI,MAAM,MAAM;;AAEvB;AACA;AACA;AACA,OAAO,MAAMC,gBAAgB,GAAGA,CAAA,KAAc;EAC5C;EACA,MAAMC,eAAe,GAAGF,IAAI,CAACG,OAAO,CAACC,SAAS,EAAE,6BAA6B,CAAC;EAE9E,IAAI,CAACL,EAAE,CAACM,UAAU,CAACH,eAAe,CAAC,EAAE;IACnC,MAAM,IAAII,KAAK,CAAC,6BAA6BJ,eAAe,EAAE,CAAC;EACjE;EAEA,MAAMK,WAAW,GAAGC,IAAI,CAACC,KAAK,CAACV,EAAE,CAACW,YAAY,CAACR,eAAe,EAAE,MAAM,CAAC,CAAC;EAExE,IAAI,CAACK,WAAW,CAACI,OAAO,EAAE;IACxB,MAAM,IAAIL,KAAK,CAAC,iCAAiCJ,eAAe,EAAE,CAAC;EACrE;EAEA,OAAOK,WAAW,CAACI,OAAO;AAC5B,CAAC","ignoreList":[]}
@@ -1,4 +0,0 @@
1
- /**
2
- * Reads the version of the plugin from its `package.json` and returns it as a string.
3
- */
4
- export declare const getPluginVersion: () => string;
@@ -1,8 +0,0 @@
1
- declare module 'xcode' {
2
- interface xcode {
3
- project(projPath: string): any;
4
- }
5
-
6
- const xcode: xcode;
7
- export default xcode;
8
- }
@@ -1,22 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
-
4
- /**
5
- * Reads the version of the plugin from its `package.json` and returns it as a string.
6
- */
7
- export const getPluginVersion = (): string => {
8
- // Always resolves relative to the utility file's location
9
- const packageJsonPath = path.resolve(__dirname, '../../../../../package.json');
10
-
11
- if (!fs.existsSync(packageJsonPath)) {
12
- throw new Error(`package.json not found at ${packageJsonPath}`);
13
- }
14
-
15
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
16
-
17
- if (!packageJson.version) {
18
- throw new Error(`"version" field is missing in ${packageJsonPath}`);
19
- }
20
-
21
- return packageJson.version;
22
- };