strata-storage 1.5.0 โ†’ 1.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 (38) hide show
  1. package/dist/README.md +116 -0
  2. package/dist/adapters/capacitor/FilesystemAdapter.js +4 -4
  3. package/dist/adapters/capacitor/PreferencesAdapter.js +4 -4
  4. package/dist/adapters/capacitor/SecureAdapter.js +4 -4
  5. package/dist/adapters/capacitor/SqliteAdapter.js +4 -4
  6. package/dist/adapters/capacitor/index.js +4 -4
  7. package/dist/adapters/web/CacheAdapter.js +3 -3
  8. package/dist/adapters/web/CookieAdapter.js +2 -2
  9. package/dist/adapters/web/IndexedDBAdapter.js +3 -3
  10. package/dist/adapters/web/LocalStorageAdapter.js +3 -3
  11. package/dist/adapters/web/MemoryAdapter.js +2 -2
  12. package/dist/adapters/web/SessionStorageAdapter.js +1 -1
  13. package/dist/adapters/web/index.js +6 -6
  14. package/dist/android/src/main/java/com/strata/storage/EncryptedStorage.java +65 -0
  15. package/dist/android/src/main/java/com/strata/storage/SQLiteStorage.java +147 -0
  16. package/dist/android/src/main/java/com/strata/storage/SharedPreferencesStorage.java +74 -0
  17. package/dist/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +256 -0
  18. package/dist/core/AdapterRegistry.js +1 -1
  19. package/dist/core/BaseAdapter.js +3 -3
  20. package/dist/core/StorageStrategy.js +1 -0
  21. package/dist/core/Strata.js +17 -17
  22. package/dist/features/compression.js +1 -1
  23. package/dist/features/encryption.d.ts.map +1 -1
  24. package/dist/features/encryption.js +6 -5
  25. package/dist/features/sync.js +1 -1
  26. package/dist/features/ttl.js +1 -1
  27. package/dist/index.js +24 -24
  28. package/dist/ios/Plugin/KeychainStorage.swift +87 -0
  29. package/dist/ios/Plugin/SQLiteStorage.swift +167 -0
  30. package/dist/ios/Plugin/StrataStoragePlugin.swift +204 -0
  31. package/dist/ios/Plugin/UserDefaultsStorage.swift +44 -0
  32. package/dist/package.json +14 -4
  33. package/dist/plugin/index.js +2 -2
  34. package/package.json +8 -26
  35. package/scripts/build.js +119 -14
  36. package/scripts/cli.js +6 -2
  37. package/scripts/configure.js +7 -3
  38. package/scripts/postinstall.js +6 -2
@@ -0,0 +1,167 @@
1
+ import Foundation
2
+ import SQLite3
3
+
4
+ @objc public class SQLiteStorage: NSObject {
5
+ private var db: OpaquePointer?
6
+ private let dbName: String
7
+ private let tableName = "strata_storage"
8
+
9
+ @objc public init(dbName: String = "strata.db") {
10
+ self.dbName = dbName
11
+ super.init()
12
+ openDatabase()
13
+ createTable()
14
+ }
15
+
16
+ deinit {
17
+ closeDatabase()
18
+ }
19
+
20
+ private func openDatabase() {
21
+ let fileURL = try! FileManager.default
22
+ .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
23
+ .appendingPathComponent(dbName)
24
+
25
+ if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
26
+ print("Unable to open database")
27
+ }
28
+ }
29
+
30
+ private func closeDatabase() {
31
+ sqlite3_close(db)
32
+ }
33
+
34
+ private func createTable() {
35
+ let createTableString = """
36
+ CREATE TABLE IF NOT EXISTS \(tableName) (
37
+ key TEXT PRIMARY KEY NOT NULL,
38
+ value BLOB NOT NULL,
39
+ created INTEGER NOT NULL,
40
+ updated INTEGER NOT NULL,
41
+ expires INTEGER,
42
+ tags TEXT,
43
+ metadata TEXT
44
+ );
45
+ """
46
+
47
+ var createTableStatement: OpaquePointer?
48
+ if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
49
+ if sqlite3_step(createTableStatement) == SQLITE_DONE {
50
+ print("Storage table created.")
51
+ }
52
+ }
53
+ sqlite3_finalize(createTableStatement)
54
+ }
55
+
56
+ @objc public func set(key: String, value: Data, expires: Int64? = nil, tags: [String]? = nil, metadata: [String: Any]? = nil) -> Bool {
57
+ let now = Int64(Date().timeIntervalSince1970 * 1000)
58
+ let tagsJson = tags != nil ? try? JSONSerialization.data(withJSONObject: tags!, options: []) : nil
59
+ let metadataJson = metadata != nil ? try? JSONSerialization.data(withJSONObject: metadata!, options: []) : nil
60
+
61
+ let insertSQL = """
62
+ INSERT OR REPLACE INTO \(tableName)
63
+ (key, value, created, updated, expires, tags, metadata)
64
+ VALUES (?, ?, ?, ?, ?, ?, ?)
65
+ """
66
+
67
+ var statement: OpaquePointer?
68
+ let result = sqlite3_prepare_v2(db, insertSQL, -1, &statement, nil) == SQLITE_OK &&
69
+ sqlite3_bind_text(statement, 1, key, -1, nil) == SQLITE_OK &&
70
+ sqlite3_bind_blob(statement, 2, (value as NSData).bytes, Int32(value.count), nil) == SQLITE_OK &&
71
+ sqlite3_bind_int64(statement, 3, now) == SQLITE_OK &&
72
+ sqlite3_bind_int64(statement, 4, now) == SQLITE_OK &&
73
+ (expires != nil ? sqlite3_bind_int64(statement, 5, expires!) : sqlite3_bind_null(statement, 5)) == SQLITE_OK &&
74
+ (tagsJson != nil ? sqlite3_bind_blob(statement, 6, (tagsJson! as NSData).bytes, Int32(tagsJson!.count), nil) : sqlite3_bind_null(statement, 6)) == SQLITE_OK &&
75
+ (metadataJson != nil ? sqlite3_bind_blob(statement, 7, (metadataJson! as NSData).bytes, Int32(metadataJson!.count), nil) : sqlite3_bind_null(statement, 7)) == SQLITE_OK &&
76
+ sqlite3_step(statement) == SQLITE_DONE
77
+
78
+ sqlite3_finalize(statement)
79
+ return result
80
+ }
81
+
82
+ @objc public func get(key: String) -> [String: Any]? {
83
+ let querySQL = "SELECT * FROM \(tableName) WHERE key = ? LIMIT 1"
84
+ var statement: OpaquePointer?
85
+
86
+ guard sqlite3_prepare_v2(db, querySQL, -1, &statement, nil) == SQLITE_OK,
87
+ sqlite3_bind_text(statement, 1, key, -1, nil) == SQLITE_OK else {
88
+ sqlite3_finalize(statement)
89
+ return nil
90
+ }
91
+
92
+ var result: [String: Any]?
93
+ if sqlite3_step(statement) == SQLITE_ROW {
94
+ let valueData = Data(bytes: sqlite3_column_blob(statement, 1), count: Int(sqlite3_column_bytes(statement, 1)))
95
+ let created = sqlite3_column_int64(statement, 2)
96
+ let updated = sqlite3_column_int64(statement, 3)
97
+
98
+ result = [
99
+ "key": key,
100
+ "value": valueData,
101
+ "created": created,
102
+ "updated": updated
103
+ ]
104
+
105
+ if sqlite3_column_type(statement, 4) != SQLITE_NULL {
106
+ result!["expires"] = sqlite3_column_int64(statement, 4)
107
+ }
108
+
109
+ if sqlite3_column_type(statement, 5) != SQLITE_NULL {
110
+ let tagsData = Data(bytes: sqlite3_column_blob(statement, 5), count: Int(sqlite3_column_bytes(statement, 5)))
111
+ if let tags = try? JSONSerialization.jsonObject(with: tagsData, options: []) {
112
+ result!["tags"] = tags
113
+ }
114
+ }
115
+
116
+ if sqlite3_column_type(statement, 6) != SQLITE_NULL {
117
+ let metadataData = Data(bytes: sqlite3_column_blob(statement, 6), count: Int(sqlite3_column_bytes(statement, 6)))
118
+ if let metadata = try? JSONSerialization.jsonObject(with: metadataData, options: []) {
119
+ result!["metadata"] = metadata
120
+ }
121
+ }
122
+ }
123
+
124
+ sqlite3_finalize(statement)
125
+ return result
126
+ }
127
+
128
+ @objc public func remove(key: String) -> Bool {
129
+ let deleteSQL = "DELETE FROM \(tableName) WHERE key = ?"
130
+ var statement: OpaquePointer?
131
+
132
+ let result = sqlite3_prepare_v2(db, deleteSQL, -1, &statement, nil) == SQLITE_OK &&
133
+ sqlite3_bind_text(statement, 1, key, -1, nil) == SQLITE_OK &&
134
+ sqlite3_step(statement) == SQLITE_DONE
135
+
136
+ sqlite3_finalize(statement)
137
+ return result
138
+ }
139
+
140
+ @objc public func clear() -> Bool {
141
+ let deleteSQL = "DELETE FROM \(tableName)"
142
+ var statement: OpaquePointer?
143
+
144
+ let result = sqlite3_prepare_v2(db, deleteSQL, -1, &statement, nil) == SQLITE_OK &&
145
+ sqlite3_step(statement) == SQLITE_DONE
146
+
147
+ sqlite3_finalize(statement)
148
+ return result
149
+ }
150
+
151
+ @objc public func keys() -> [String] {
152
+ let querySQL = "SELECT key FROM \(tableName)"
153
+ var statement: OpaquePointer?
154
+ var keys: [String] = []
155
+
156
+ if sqlite3_prepare_v2(db, querySQL, -1, &statement, nil) == SQLITE_OK {
157
+ while sqlite3_step(statement) == SQLITE_ROW {
158
+ if let key = sqlite3_column_text(statement, 0) {
159
+ keys.append(String(cString: key))
160
+ }
161
+ }
162
+ }
163
+
164
+ sqlite3_finalize(statement)
165
+ return keys
166
+ }
167
+ }
@@ -0,0 +1,204 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ /**
5
+ * Main Capacitor plugin for Strata Storage
6
+ * Coordinates between different storage types on iOS
7
+ */
8
+ @objc(StrataStoragePlugin)
9
+ public class StrataStoragePlugin: CAPPlugin {
10
+ private let userDefaultsStorage = UserDefaultsStorage()
11
+ private let keychainStorage = KeychainStorage()
12
+ private let sqliteStorage = SQLiteStorage()
13
+
14
+ /**
15
+ * Check if storage is available
16
+ */
17
+ @objc func isAvailable(_ call: CAPPluginCall) {
18
+ call.resolve([
19
+ "available": true,
20
+ "platform": "ios",
21
+ "adapters": [
22
+ "preferences": true,
23
+ "secure": true,
24
+ "sqlite": true,
25
+ "filesystem": true
26
+ ]
27
+ ])
28
+ }
29
+
30
+ /**
31
+ * Get value from storage
32
+ */
33
+ @objc func get(_ call: CAPPluginCall) {
34
+ guard let key = call.getString("key") else {
35
+ call.reject("Key is required")
36
+ return
37
+ }
38
+
39
+ let storage = call.getString("storage") ?? "preferences"
40
+
41
+ do {
42
+ let value: Any?
43
+
44
+ switch storage {
45
+ case "secure":
46
+ value = try keychainStorage.get(key: key)
47
+ case "sqlite":
48
+ value = try sqliteStorage.get(key: key)
49
+ case "preferences":
50
+ fallthrough
51
+ default:
52
+ value = userDefaultsStorage.get(key: key)
53
+ }
54
+
55
+ call.resolve([
56
+ "value": value ?? NSNull()
57
+ ])
58
+ } catch {
59
+ call.reject("Failed to get value", error.localizedDescription)
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Set value in storage
65
+ */
66
+ @objc func set(_ call: CAPPluginCall) {
67
+ guard let key = call.getString("key") else {
68
+ call.reject("Key is required")
69
+ return
70
+ }
71
+
72
+ let value = call.getValue("value") ?? NSNull()
73
+ let storage = call.getString("storage") ?? "preferences"
74
+
75
+ do {
76
+ switch storage {
77
+ case "secure":
78
+ try keychainStorage.set(key: key, value: value)
79
+ case "sqlite":
80
+ try sqliteStorage.set(key: key, value: value)
81
+ case "preferences":
82
+ fallthrough
83
+ default:
84
+ userDefaultsStorage.set(key: key, value: value)
85
+ }
86
+
87
+ call.resolve()
88
+ } catch {
89
+ call.reject("Failed to set value", error.localizedDescription)
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Remove value from storage
95
+ */
96
+ @objc func remove(_ call: CAPPluginCall) {
97
+ guard let key = call.getString("key") else {
98
+ call.reject("Key is required")
99
+ return
100
+ }
101
+
102
+ let storage = call.getString("storage") ?? "preferences"
103
+
104
+ do {
105
+ switch storage {
106
+ case "secure":
107
+ try keychainStorage.remove(key: key)
108
+ case "sqlite":
109
+ try sqliteStorage.remove(key: key)
110
+ case "preferences":
111
+ fallthrough
112
+ default:
113
+ userDefaultsStorage.remove(key: key)
114
+ }
115
+
116
+ call.resolve()
117
+ } catch {
118
+ call.reject("Failed to remove value", error.localizedDescription)
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Clear storage
124
+ */
125
+ @objc func clear(_ call: CAPPluginCall) {
126
+ let storage = call.getString("storage") ?? "preferences"
127
+ let prefix = call.getString("prefix")
128
+
129
+ do {
130
+ switch storage {
131
+ case "secure":
132
+ try keychainStorage.clear(prefix: prefix)
133
+ case "sqlite":
134
+ try sqliteStorage.clear(prefix: prefix)
135
+ case "preferences":
136
+ fallthrough
137
+ default:
138
+ userDefaultsStorage.clear(prefix: prefix)
139
+ }
140
+
141
+ call.resolve()
142
+ } catch {
143
+ call.reject("Failed to clear storage", error.localizedDescription)
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Get all keys from storage
149
+ */
150
+ @objc func keys(_ call: CAPPluginCall) {
151
+ let storage = call.getString("storage") ?? "preferences"
152
+ let pattern = call.getString("pattern")
153
+
154
+ do {
155
+ let keys: [String]
156
+
157
+ switch storage {
158
+ case "secure":
159
+ keys = try keychainStorage.keys(pattern: pattern)
160
+ case "sqlite":
161
+ keys = try sqliteStorage.keys(pattern: pattern)
162
+ case "preferences":
163
+ fallthrough
164
+ default:
165
+ keys = userDefaultsStorage.keys(pattern: pattern)
166
+ }
167
+
168
+ call.resolve([
169
+ "keys": keys
170
+ ])
171
+ } catch {
172
+ call.reject("Failed to get keys", error.localizedDescription)
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Get storage size information
178
+ */
179
+ @objc func size(_ call: CAPPluginCall) {
180
+ let storage = call.getString("storage") ?? "preferences"
181
+
182
+ do {
183
+ let sizeInfo: (total: Int, count: Int)
184
+
185
+ switch storage {
186
+ case "secure":
187
+ sizeInfo = try keychainStorage.size()
188
+ case "sqlite":
189
+ sizeInfo = try sqliteStorage.size()
190
+ case "preferences":
191
+ fallthrough
192
+ default:
193
+ sizeInfo = userDefaultsStorage.size()
194
+ }
195
+
196
+ call.resolve([
197
+ "total": sizeInfo.total,
198
+ "count": sizeInfo.count
199
+ ])
200
+ } catch {
201
+ call.reject("Failed to get size", error.localizedDescription)
202
+ }
203
+ }
204
+ }
@@ -0,0 +1,44 @@
1
+ import Foundation
2
+
3
+ @objc public class UserDefaultsStorage: NSObject {
4
+ private let suiteName: String?
5
+ private var userDefaults: UserDefaults
6
+
7
+ @objc public init(suiteName: String? = nil) {
8
+ self.suiteName = suiteName
9
+ self.userDefaults = suiteName != nil ? UserDefaults(suiteName: suiteName)! : UserDefaults.standard
10
+ super.init()
11
+ }
12
+
13
+ @objc public func set(key: String, value: Any) -> Bool {
14
+ userDefaults.set(value, forKey: key)
15
+ return userDefaults.synchronize()
16
+ }
17
+
18
+ @objc public func get(key: String) -> Any? {
19
+ return userDefaults.object(forKey: key)
20
+ }
21
+
22
+ @objc public func remove(key: String) -> Bool {
23
+ userDefaults.removeObject(forKey: key)
24
+ return userDefaults.synchronize()
25
+ }
26
+
27
+ @objc public func clear() -> Bool {
28
+ if let suiteName = suiteName {
29
+ UserDefaults(suiteName: suiteName)?.removePersistentDomain(forName: suiteName)
30
+ } else {
31
+ let domain = Bundle.main.bundleIdentifier!
32
+ userDefaults.removePersistentDomain(forName: domain)
33
+ }
34
+ return userDefaults.synchronize()
35
+ }
36
+
37
+ @objc public func keys() -> [String] {
38
+ return Array(userDefaults.dictionaryRepresentation().keys)
39
+ }
40
+
41
+ @objc public func has(key: String) -> Bool {
42
+ return userDefaults.object(forKey: key) != nil
43
+ }
44
+ }
package/dist/package.json CHANGED
@@ -1,10 +1,16 @@
1
1
  {
2
2
  "name": "strata-storage",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Zero-dependency universal storage plugin providing a unified API for all storage operations across web, Android, and iOS platforms",
5
- "main": "index.js",
6
- "module": "index.esm.js",
7
- "types": "index.d.ts",
5
+ "type": "module",
6
+ "main": "./index.js",
7
+ "types": "./index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./index.d.ts",
11
+ "default": "./index.js"
12
+ }
13
+ },
8
14
  "author": "Ahsan Mahmood",
9
15
  "license": "MIT",
10
16
  "repository": {
@@ -56,5 +62,9 @@
56
62
  "android": {
57
63
  "src": "android"
58
64
  }
65
+ },
66
+ "sideEffects": false,
67
+ "engines": {
68
+ "node": ">=18.0.0"
59
69
  }
60
70
  }
@@ -20,8 +20,8 @@ if (typeof window !== 'undefined') {
20
20
  const cap = window.Capacitor;
21
21
  if (cap && cap.registerPlugin) {
22
22
  StrataStorage = cap.registerPlugin('StrataStorage', {
23
- web: () => import('./web').then((m) => new m.StrataStorageWeb()),
23
+ web: () => import("./web.js").then((m) => new m.StrataStorageWeb()),
24
24
  });
25
25
  }
26
26
  }
27
- export * from './definitions';
27
+ export * from "./definitions.js";
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "strata-storage",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Zero-dependency universal storage plugin providing a unified API for all storage operations across web, Android, and iOS platforms",
5
+ "type": "module",
5
6
  "main": "dist/index.js",
6
- "module": "dist/index.esm.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "files": [
9
9
  "dist/",
@@ -17,32 +17,14 @@
17
17
  },
18
18
  "exports": {
19
19
  ".": {
20
- "import": "./dist/index.esm.js",
21
- "require": "./dist/index.js",
22
- "types": "./dist/index.d.ts"
23
- },
24
- "./react": {
25
- "import": "./dist/integrations/react/index.esm.js",
26
- "require": "./dist/integrations/react/index.js",
27
- "types": "./dist/integrations/react/index.d.ts"
28
- },
29
- "./vue": {
30
- "import": "./dist/integrations/vue/index.esm.js",
31
- "require": "./dist/integrations/vue/index.js",
32
- "types": "./dist/integrations/vue/index.d.ts"
33
- },
34
- "./angular": {
35
- "import": "./dist/integrations/angular/index.esm.js",
36
- "require": "./dist/integrations/angular/index.js",
37
- "types": "./dist/integrations/angular/index.d.ts"
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
38
22
  }
39
23
  },
40
24
  "scripts": {
41
- "build": "yarn clean && yarn build:web && yarn build:types",
42
- "build:web": "node scripts/build.js",
43
- "build:types": "tsc --emitDeclarationOnly",
25
+ "build": "node scripts/build.js",
44
26
  "clean": "rm -rf dist/",
45
- "lint": "eslint 'src/**/*.{ts,tsx}' --ignore-pattern 'src/integrations/**/*' --fix",
27
+ "lint": "eslint 'src/**/*.{ts,tsx}' --fix",
46
28
  "typecheck": "tsc --noEmit",
47
29
  "test": "vitest",
48
30
  "test:coverage": "vitest --coverage",
@@ -99,7 +81,7 @@
99
81
  }
100
82
  },
101
83
  "devDependencies": {
102
- "@eslint/js": "^9.32.0",
84
+ "@eslint/js": "^10.0.0",
103
85
  "@types/node": "^24.1.0",
104
86
  "@types/react": "^19.1.9",
105
87
  "@typescript-eslint/eslint-plugin": "^8.38.0",
@@ -109,7 +91,7 @@
109
91
  "eslint-config-prettier": "^10.1.8",
110
92
  "eslint-plugin-prettier": "^5.5.3",
111
93
  "prettier": "^3.6.2",
112
- "typescript": "^5.8.3",
94
+ "typescript": "^5.9.2",
113
95
  "typescript-eslint": "^8.38.0",
114
96
  "vitest": "^3.2.4"
115
97
  },
package/scripts/build.js CHANGED
@@ -1,39 +1,140 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs');
4
- const path = require('path');
5
- const { execSync } = require('child_process');
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { execSync } from 'child_process';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
6
10
 
7
11
  const rootDir = path.join(__dirname, '..');
8
12
  const srcDir = path.join(rootDir, 'src');
9
13
  const distDir = path.join(rootDir, 'dist');
10
14
 
11
- console.log('๐Ÿ”จ Building Strata Storage...');
15
+ console.log('๐Ÿ”จ Building Strata Storage (ESM)...');
12
16
 
13
- // Ensure dist directory exists
14
- if (!fs.existsSync(distDir)) {
15
- fs.mkdirSync(distDir, { recursive: true });
17
+ // Clean dist directory
18
+ console.log('๐Ÿงน Cleaning dist directory...');
19
+ if (fs.existsSync(distDir)) {
20
+ fs.rmSync(distDir, { recursive: true, force: true });
16
21
  }
17
22
 
18
- // Compile TypeScript
19
- console.log('๐Ÿ“ฆ Compiling TypeScript...');
23
+ // Ensure dist directory exists
24
+ fs.mkdirSync(distDir, { recursive: true });
25
+
26
+ // Compile TypeScript to ESM
27
+ console.log('๐Ÿ“ฆ Building ES Modules...');
20
28
  try {
21
29
  execSync('npx tsc', { stdio: 'inherit', cwd: rootDir });
22
30
  } catch (error) {
23
- console.error('โŒ TypeScript compilation failed');
31
+ console.error('โŒ Build failed');
24
32
  process.exit(1);
25
33
  }
26
34
 
27
- // Copy package.json fields for publishing
35
+ // Fix path aliases in all built files
36
+ console.log('๐Ÿ”ง Fixing path aliases...');
37
+ const fixPathAliases = (dir) => {
38
+ const files = fs.readdirSync(dir);
39
+
40
+ files.forEach(file => {
41
+ const filePath = path.join(dir, file);
42
+ const stat = fs.statSync(filePath);
43
+
44
+ if (stat.isDirectory()) {
45
+ fixPathAliases(filePath);
46
+ } else if (file.endsWith('.js')) {
47
+ let content = fs.readFileSync(filePath, 'utf8');
48
+
49
+ // Calculate depth from dist root
50
+ const relativeFromDist = path.relative(distDir, path.dirname(filePath));
51
+ const depth = relativeFromDist ? relativeFromDist.split(path.sep).length : 0;
52
+ const pathToRoot = depth > 0 ? '../'.repeat(depth) : './';
53
+
54
+ // Fix all @/ imports with proper aliasing
55
+ content = content
56
+ .replace(/from\s+["']@\/core\//g, `from "${pathToRoot}core/`)
57
+ .replace(/from\s+["']@\/adapters\//g, `from "${pathToRoot}adapters/`)
58
+ .replace(/from\s+["']@\/features\//g, `from "${pathToRoot}features/`)
59
+ .replace(/from\s+["']@\/utils\//g, `from "${pathToRoot}utils/`)
60
+ .replace(/from\s+["']@\/plugin\//g, `from "${pathToRoot}plugin/`)
61
+ .replace(/from\s+["']@\/types\//g, `from "${pathToRoot}types/`)
62
+ .replace(/from\s+["']@\/utils["']/g, `from "${pathToRoot}utils/index.js"`)
63
+ .replace(/from\s+["']@\/plugin["']/g, `from "${pathToRoot}plugin/index.js"`)
64
+ .replace(/import\(["']@\/core\//g, `import("${pathToRoot}core/`)
65
+ .replace(/import\(["']@\/adapters\//g, `import("${pathToRoot}adapters/`)
66
+ .replace(/import\(["']@\/features\//g, `import("${pathToRoot}features/`)
67
+ .replace(/import\(["']@\/utils\//g, `import("${pathToRoot}utils/`)
68
+ .replace(/import\(["']@\/plugin\//g, `import("${pathToRoot}plugin/`)
69
+ .replace(/import\(["']@\/types\//g, `import("${pathToRoot}types/`)
70
+ .replace(/import\(["']@\/utils["']\)/g, `import("${pathToRoot}utils/index.js")`)
71
+ .replace(/import\(["']@\/plugin["']\)/g, `import("${pathToRoot}plugin/index.js")`);
72
+
73
+ // Also fix any remaining short imports that should point to index.js
74
+ content = content
75
+ .replace(/from\s+["']\.\/utils["']/g, `from "./utils/index.js"`)
76
+ .replace(/from\s+["']\.\/plugin["']/g, `from "./plugin/index.js"`);
77
+
78
+ // Add .js extension to relative imports
79
+ content = content
80
+ .replace(/from\s+["'](\.[^"']+)["']/g, (match, path) => {
81
+ if (!path.endsWith('.js') && !path.endsWith('.json')) {
82
+ return `from "${path}.js"`;
83
+ }
84
+ return match;
85
+ })
86
+ .replace(/import\(["'](\.[^"']+)["']\)/g, (match, path) => {
87
+ if (!path.endsWith('.js') && !path.endsWith('.json')) {
88
+ return `import("${path}.js")`;
89
+ }
90
+ return match;
91
+ });
92
+
93
+ fs.writeFileSync(filePath, content);
94
+ }
95
+ });
96
+ };
97
+
98
+ fixPathAliases(distDir);
99
+
100
+ // Copy native directories if they exist
101
+ console.log('๐Ÿ“ฑ Copying native code...');
102
+ const nativeDirs = ['android', 'ios'];
103
+ nativeDirs.forEach(dir => {
104
+ const srcPath = path.join(rootDir, dir);
105
+ const destPath = path.join(distDir, dir);
106
+ if (fs.existsSync(srcPath)) {
107
+ fs.cpSync(srcPath, destPath, { recursive: true });
108
+ }
109
+ });
110
+
111
+ // Copy other files
112
+ console.log('๐Ÿ“„ Copying additional files...');
113
+ const filesToCopy = ['README.md', 'LICENSE'];
114
+ filesToCopy.forEach(file => {
115
+ const srcPath = path.join(rootDir, file);
116
+ const destPath = path.join(distDir, file);
117
+ if (fs.existsSync(srcPath)) {
118
+ fs.copyFileSync(srcPath, destPath);
119
+ }
120
+ });
121
+
122
+ // Create package.json for distribution
28
123
  console.log('๐Ÿ“‹ Preparing package metadata...');
29
124
  const packageJson = JSON.parse(fs.readFileSync(path.join(rootDir, 'package.json'), 'utf8'));
30
125
  const distPackageJson = {
31
126
  name: packageJson.name,
32
127
  version: packageJson.version,
33
128
  description: packageJson.description,
34
- main: 'index.js',
35
- module: 'index.esm.js',
36
- types: 'index.d.ts',
129
+ type: 'module',
130
+ main: './index.js',
131
+ types: './index.d.ts',
132
+ exports: {
133
+ '.': {
134
+ types: './index.d.ts',
135
+ default: './index.js'
136
+ }
137
+ },
37
138
  author: packageJson.author,
38
139
  license: packageJson.license,
39
140
  repository: packageJson.repository,
@@ -41,6 +142,10 @@ const distPackageJson = {
41
142
  peerDependencies: packageJson.peerDependencies,
42
143
  peerDependenciesMeta: packageJson.peerDependenciesMeta,
43
144
  capacitor: packageJson.capacitor,
145
+ sideEffects: false,
146
+ engines: {
147
+ node: '>=18.0.0'
148
+ }
44
149
  };
45
150
 
46
151
  fs.writeFileSync(