native-update 1.4.5 → 1.4.7
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.
- package/AI-INTEGRATION-GUIDE.md +4 -4
- package/Readme.md +33 -33
- package/dist/plugin.js +1 -1
- package/docs/APP_REVIEW_GUIDE.md +2 -2
- package/docs/BUNDLE_SIGNING.md +1 -1
- package/docs/LIVE_UPDATES_GUIDE.md +4 -4
- package/docs/MIGRATION.md +1 -1
- package/docs/NATIVE_UPDATES_GUIDE.md +4 -4
- package/docs/QUICK_START.md +5 -5
- package/docs/README.md +23 -23
- package/docs/api/app-update-api.md +1 -1
- package/docs/cli-reference.md +1 -1
- package/docs/examples/advanced-scenarios.md +3 -3
- package/docs/examples/basic-usage.md +4 -4
- package/docs/features/app-reviews.md +4 -4
- package/docs/features/app-updates.md +4 -4
- package/docs/features/live-updates.md +3 -3
- package/docs/getting-started/configuration.md +2 -2
- package/docs/getting-started/installation.md +4 -4
- package/docs/getting-started/quick-start.md +6 -6
- package/docs/guides/BACKEND_TEMPLATES_GUIDE.md +3 -3
- package/docs/guides/admin-panel.md +3 -3
- package/docs/guides/channel-management.md +4 -4
- package/docs/guides/dashboard-guide.md +5 -5
- package/docs/guides/end-to-end-workflow.md +4 -4
- package/docs/guides/key-management.md +3 -3
- package/docs/guides/migration-from-codepush.md +3 -3
- package/docs/guides/security-best-practices.md +1 -1
- package/docs/guides/troubleshooting.md +3 -3
- package/docs/tracking/capacitor-rollout-note-2026-03-01.md +21 -0
- package/package.json +1 -1
package/AI-INTEGRATION-GUIDE.md
CHANGED
|
@@ -283,8 +283,8 @@ async function handlePurchaseComplete() {
|
|
|
283
283
|
|
|
284
284
|
## Links
|
|
285
285
|
|
|
286
|
-
- [Full Documentation](
|
|
287
|
-
- [API Reference](
|
|
286
|
+
- [Full Documentation](https://nativeupdate.aoneahsan.com/docs)
|
|
287
|
+
- [API Reference](https://nativeupdate.aoneahsan.com/docs/api/API)
|
|
288
288
|
- [Example Apps](./example-apps/)
|
|
289
|
-
- [CLI Reference](
|
|
290
|
-
- [Security Guide](
|
|
289
|
+
- [CLI Reference](https://nativeupdate.aoneahsan.com/docs/cli-reference)
|
|
290
|
+
- [Security Guide](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices)
|
package/Readme.md
CHANGED
|
@@ -15,49 +15,49 @@
|
|
|
15
15
|
|
|
16
16
|
## 📚 Documentation
|
|
17
17
|
|
|
18
|
-
- **[AI Integration Guide](
|
|
18
|
+
- **[AI Integration Guide](https://nativeupdate.aoneahsan.com/docs/AI-INTEGRATION-GUIDE)** - Quick reference for AI development agents (Claude, Cursor, Copilot)
|
|
19
19
|
|
|
20
20
|
### Getting Started
|
|
21
21
|
|
|
22
|
-
- **[Installation Guide](
|
|
23
|
-
- **[Quick Start Guide](
|
|
24
|
-
- **[Configuration Guide](
|
|
25
|
-
- **[End-to-End Workflow](
|
|
22
|
+
- **[Installation Guide](https://nativeupdate.aoneahsan.com/docs/getting-started/installation)** - Step-by-step installation instructions
|
|
23
|
+
- **[Quick Start Guide](https://nativeupdate.aoneahsan.com/docs/getting-started/quick-start)** - Get up and running in minutes
|
|
24
|
+
- **[Configuration Guide](https://nativeupdate.aoneahsan.com/docs/getting-started/configuration)** - Detailed configuration options
|
|
25
|
+
- **[End-to-End Workflow](https://nativeupdate.aoneahsan.com/docs/guides/end-to-end-workflow)** - Complete setup to production guide
|
|
26
26
|
|
|
27
27
|
### Dashboard & Management
|
|
28
28
|
|
|
29
|
-
- **[Dashboard Guide](
|
|
30
|
-
- **[Channel Management](
|
|
31
|
-
- **[Admin Panel](
|
|
29
|
+
- **[Dashboard Guide](https://nativeupdate.aoneahsan.com/docs/guides/dashboard-guide)** - Complete dashboard user guide
|
|
30
|
+
- **[Channel Management](https://nativeupdate.aoneahsan.com/docs/guides/channel-management)** - Dev/staging/production channels
|
|
31
|
+
- **[Admin Panel](https://nativeupdate.aoneahsan.com/docs/guides/admin-panel)** - Super-admin documentation
|
|
32
32
|
|
|
33
33
|
### Features Documentation
|
|
34
34
|
|
|
35
|
-
- **[Live Updates (OTA)](
|
|
36
|
-
- **[App Updates](
|
|
37
|
-
- **[App Reviews](
|
|
38
|
-
- **[Background Updates](
|
|
35
|
+
- **[Live Updates (OTA)](https://nativeupdate.aoneahsan.com/docs/features/live-updates)** - Deploy web updates instantly
|
|
36
|
+
- **[App Updates](https://nativeupdate.aoneahsan.com/docs/features/app-updates)** - Native app store update management
|
|
37
|
+
- **[App Reviews](https://nativeupdate.aoneahsan.com/docs/features/app-reviews)** - In-app review integration
|
|
38
|
+
- **[Background Updates](https://nativeupdate.aoneahsan.com/docs/background-updates)** - Background update management
|
|
39
39
|
|
|
40
40
|
### Guides & Best Practices
|
|
41
41
|
|
|
42
|
-
- **[Security Best Practices](
|
|
43
|
-
- **[Deployment Guide](
|
|
44
|
-
- **[Migration Guide](
|
|
45
|
-
- **[Production Readiness](
|
|
46
|
-
- **[Bundle Signing](
|
|
47
|
-
- **[Troubleshooting](
|
|
42
|
+
- **[Security Best Practices](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices)** - Implement secure updates
|
|
43
|
+
- **[Deployment Guide](https://nativeupdate.aoneahsan.com/docs/guides/deployment-guide)** - Deployment procedures
|
|
44
|
+
- **[Migration Guide](https://nativeupdate.aoneahsan.com/docs/MIGRATION)** - Migrate from other solutions
|
|
45
|
+
- **[Production Readiness](https://nativeupdate.aoneahsan.com/docs/production-readiness)** - Production deployment checklist
|
|
46
|
+
- **[Bundle Signing](https://nativeupdate.aoneahsan.com/docs/BUNDLE_SIGNING)** - Cryptographic signing guide
|
|
47
|
+
- **[Troubleshooting](https://nativeupdate.aoneahsan.com/docs/guides/troubleshooting)** - Common issues and solutions
|
|
48
48
|
|
|
49
49
|
### API Reference
|
|
50
50
|
|
|
51
|
-
- **[Live Update API](
|
|
52
|
-
- **[App Update API](
|
|
53
|
-
- **[App Review API](
|
|
54
|
-
- **[Events API](
|
|
55
|
-
- **[CLI Reference](
|
|
51
|
+
- **[Live Update API](https://nativeupdate.aoneahsan.com/docs/api/live-update-api)** - Complete API for OTA updates
|
|
52
|
+
- **[App Update API](https://nativeupdate.aoneahsan.com/docs/api/app-update-api)** - Native app update methods
|
|
53
|
+
- **[App Review API](https://nativeupdate.aoneahsan.com/docs/api/app-review-api)** - Review request methods
|
|
54
|
+
- **[Events API](https://nativeupdate.aoneahsan.com/docs/api/events-api)** - Event listeners and handlers
|
|
55
|
+
- **[CLI Reference](https://nativeupdate.aoneahsan.com/docs/cli-reference)** - Command-line tools documentation
|
|
56
56
|
|
|
57
57
|
### Examples
|
|
58
58
|
|
|
59
|
-
- **[Basic Usage](
|
|
60
|
-
- **[Advanced Scenarios](
|
|
59
|
+
- **[Basic Usage](https://nativeupdate.aoneahsan.com/docs/examples/basic-usage)** - Simple implementation examples
|
|
60
|
+
- **[Advanced Scenarios](https://nativeupdate.aoneahsan.com/docs/examples/advanced-scenarios)** - Complex use cases
|
|
61
61
|
|
|
62
62
|
---
|
|
63
63
|
|
|
@@ -89,7 +89,7 @@ Native Update supports three deployment channels:
|
|
|
89
89
|
| **Staging** | QA/Beta testing | Configurable | 1 hour |
|
|
90
90
|
| **Production** | Live users | No (consent) | Daily |
|
|
91
91
|
|
|
92
|
-
See [Channel Management Guide](
|
|
92
|
+
See [Channel Management Guide](https://nativeupdate.aoneahsan.com/docs/guides/channel-management) for detailed configuration.
|
|
93
93
|
|
|
94
94
|
---
|
|
95
95
|
|
|
@@ -395,7 +395,7 @@ npx native-update backend create express --with-admin
|
|
|
395
395
|
- Generate keys: `npx native-update keys generate --type rsa --size 4096`
|
|
396
396
|
- Supports RSA (2048/4096) and EC (256/384) keys
|
|
397
397
|
- Creates timestamped key pairs with proper permissions
|
|
398
|
-
- See [Key Management Guide](
|
|
398
|
+
- See [Key Management Guide](https://nativeupdate.aoneahsan.com/docs/guides/key-management) for detailed instructions
|
|
399
399
|
|
|
400
400
|
✅ **Backend Templates**
|
|
401
401
|
- Express.js: `npx native-update backend create express --with-admin`
|
|
@@ -410,7 +410,7 @@ npx native-update backend create express --with-admin
|
|
|
410
410
|
✅ **Migration Tools**
|
|
411
411
|
- From CodePush: `npx native-update migrate --from codepush`
|
|
412
412
|
|
|
413
|
-
See [CLI Reference](
|
|
413
|
+
See [CLI Reference](https://nativeupdate.aoneahsan.com/docs/cli-reference) for complete documentation.
|
|
414
414
|
|
|
415
415
|
## 🏗️ Development Status
|
|
416
416
|
|
|
@@ -459,12 +459,12 @@ See [CLI Reference](./docs/cli-reference.md) for complete documentation.
|
|
|
459
459
|
## 🚀 Getting Started with Development
|
|
460
460
|
|
|
461
461
|
1. **Understand the Architecture**:
|
|
462
|
-
- Review the documentation
|
|
462
|
+
- Review the documentation at `https://nativeupdate.aoneahsan.com/docs`
|
|
463
463
|
- Study the TypeScript interfaces in `/src/definitions.ts`
|
|
464
|
-
- Check the [ROADMAP.md](
|
|
464
|
+
- Check the [ROADMAP.md](https://nativeupdate.aoneahsan.com/docs/ROADMAP) for development priorities
|
|
465
465
|
|
|
466
466
|
2. **Build Required Infrastructure**:
|
|
467
|
-
- Set up an update server (see [server requirements](
|
|
467
|
+
- Set up an update server (see [server requirements](https://nativeupdate.aoneahsan.com/docs/server-requirements))
|
|
468
468
|
- Implement bundle storage solution
|
|
469
469
|
- Create signing infrastructure
|
|
470
470
|
|
|
@@ -494,8 +494,8 @@ This package is **open-source** and created by **Ahsan Mahmood** for the develop
|
|
|
494
494
|
|
|
495
495
|
- **[NPM Package](https://www.npmjs.com/package/native-update)** - Package and versions
|
|
496
496
|
- **[Website](https://nativeupdate.aoneahsan.com)** - Official website and dashboard
|
|
497
|
-
- **[Documentation](
|
|
498
|
-
- **[Examples](
|
|
497
|
+
- **[Documentation](https://nativeupdate.aoneahsan.com/docs)** - Comprehensive documentation
|
|
498
|
+
- **[Examples](https://nativeupdate.aoneahsan.com/docs/examples/basic-usage)** - Real-world usage examples
|
|
499
499
|
|
|
500
500
|
### Professional Support
|
|
501
501
|
|
package/dist/plugin.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/*! Native Update Plugin v1.4.
|
|
1
|
+
/*! Native Update Plugin v1.4.7 | MIT License */
|
|
2
2
|
!function(e,t,r,a){var i,n,s,o,l,c,d,u,h,g,p,f;!function(e){e.APP_UPDATE="app_update",e.LIVE_UPDATE="live_update",e.BOTH="both"}(i||(i={})),function(e){e.MIN="min",e.LOW="low",e.DEFAULT="default",e.HIGH="high",e.MAX="max"}(n||(n={})),function(e){e.IMMEDIATE="immediate",e.BACKGROUND="background",e.MANUAL="manual"}(s||(s={})),function(e){e.IMMEDIATE="immediate",e.ON_NEXT_RESTART="on_next_restart",e.ON_NEXT_RESUME="on_next_resume"}(o||(o={})),function(e){e.IMMEDIATE="immediate",e.ON_NEXT_RESTART="on_next_restart",e.ON_NEXT_RESUME="on_next_resume"}(l||(l={})),function(e){e.SHA256="SHA-256",e.SHA512="SHA-512"}(c||(c={})),function(e){e.UP_TO_DATE="UP_TO_DATE",e.UPDATE_AVAILABLE="UPDATE_AVAILABLE",e.UPDATE_INSTALLED="UPDATE_INSTALLED",e.ERROR="ERROR"}(d||(d={})),function(e){e.PENDING="PENDING",e.DOWNLOADING="DOWNLOADING",e.READY="READY",e.ACTIVE="ACTIVE",e.FAILED="FAILED"}(u||(u={})),function(e){e.UNKNOWN="UNKNOWN",e.PENDING="PENDING",e.DOWNLOADING="DOWNLOADING",e.DOWNLOADED="DOWNLOADED",e.INSTALLING="INSTALLING",e.INSTALLED="INSTALLED",e.FAILED="FAILED",e.CANCELED="CANCELED"}(h||(h={})),function(e){e.NETWORK_ERROR="NETWORK_ERROR",e.SERVER_ERROR="SERVER_ERROR",e.TIMEOUT_ERROR="TIMEOUT_ERROR",e.DOWNLOAD_ERROR="DOWNLOAD_ERROR",e.STORAGE_ERROR="STORAGE_ERROR",e.SIZE_LIMIT_EXCEEDED="SIZE_LIMIT_EXCEEDED",e.VERIFICATION_ERROR="VERIFICATION_ERROR",e.CHECKSUM_ERROR="CHECKSUM_ERROR",e.SIGNATURE_ERROR="SIGNATURE_ERROR",e.INSECURE_URL="INSECURE_URL",e.INVALID_CERTIFICATE="INVALID_CERTIFICATE",e.PATH_TRAVERSAL="PATH_TRAVERSAL",e.INSTALL_ERROR="INSTALL_ERROR",e.ROLLBACK_ERROR="ROLLBACK_ERROR",e.VERSION_MISMATCH="VERSION_MISMATCH",e.PERMISSION_DENIED="PERMISSION_DENIED",e.UPDATE_NOT_AVAILABLE="UPDATE_NOT_AVAILABLE",e.UPDATE_IN_PROGRESS="UPDATE_IN_PROGRESS",e.UPDATE_CANCELLED="UPDATE_CANCELLED",e.PLATFORM_NOT_SUPPORTED="PLATFORM_NOT_SUPPORTED",e.REVIEW_NOT_SUPPORTED="REVIEW_NOT_SUPPORTED",e.QUOTA_EXCEEDED="QUOTA_EXCEEDED",e.CONDITIONS_NOT_MET="CONDITIONS_NOT_MET",e.INVALID_CONFIG="INVALID_CONFIG",e.NOT_CONFIGURED="NOT_CONFIGURED",e.UNKNOWN_ERROR="UNKNOWN_ERROR"}(g||(g={}));class m{constructor(){this.config=this.getDefaultConfig()}static getInstance(){return m.instance||(m.instance=new m),m.instance}getDefaultConfig(){return{filesystem:null,preferences:null,baseUrl:"",allowedHosts:[],maxBundleSize:104857600,downloadTimeout:3e4,retryAttempts:3,retryDelay:1e3,enableSignatureValidation:!0,publicKey:"",cacheExpiration:864e5,enableLogging:!1,serverUrl:"",channel:"production",appId:"",autoCheck:!0,autoUpdate:!1,updateStrategy:"background",requireSignature:!0,checksumAlgorithm:"SHA-256",checkInterval:864e5,security:{enforceHttps:!0,validateInputs:!0,secureStorage:!0,logSecurityEvents:!1},promptAfterPositiveEvents:!1,maxPromptsPerVersion:1,minimumDaysSinceLastPrompt:7,isPremiumUser:!1,appStoreId:"",iosAppId:"",packageName:"",webReviewUrl:"",minimumVersion:"1.0.0",backendType:"http",firestore:null,enableDeltaUpdates:!0,enableStagedRollouts:!0,enableEncryption:!1,encryptionKey:"",encryptionSalt:""}}configure(e){this.config=Object.assign(Object.assign({},this.config),e),this.validateConfig()}validateConfig(){if(this.config.baseUrl&&!this.config.baseUrl.startsWith("https://"))throw new Error("baseUrl must use HTTPS protocol for security");if(this.config.maxBundleSize<=0)throw new Error("maxBundleSize must be greater than 0");if(this.config.downloadTimeout<=0)throw new Error("downloadTimeout must be greater than 0");if(this.config.retryAttempts<0)throw new Error("retryAttempts must be non-negative");if(this.config.retryDelay<0)throw new Error("retryDelay must be non-negative")}get(e){return this.config[e]}set(e,t){this.config[e]=t}getAll(){return Object.assign({},this.config)}isConfigured(){return!(!this.config.filesystem||!this.config.preferences)}}e.LogLevel=void 0,(p=e.LogLevel||(e.LogLevel={}))[p.DEBUG=0]="DEBUG",p[p.INFO=1]="INFO",p[p.WARN=2]="WARN",p[p.ERROR=3]="ERROR";class w{constructor(e){this.configManager=m.getInstance(),this.context=e||"NativeUpdate"}static getInstance(){return w.instance||(w.instance=new w),w.instance}shouldLog(){return this.configManager.get("enableLogging")}sanitize(e){if("string"==typeof e){let t=e;return t=t.replace(/\/[^\s]+\/([\w.-]+)$/g,"/<path>/$1"),t=t.replace(/https?:\/\/[^:]+:[^@]+@/g,"https://***:***@"),t=t.replace(/[a-zA-Z0-9]{32,}/g,"<redacted>"),t}if("object"==typeof e&&null!==e){if(Array.isArray(e))return e.map(e=>this.sanitize(e));{const t={},r=e;for(const e in r)t[e]=e.toLowerCase().includes("key")||e.toLowerCase().includes("secret")||e.toLowerCase().includes("password")||e.toLowerCase().includes("token")?"<redacted>":this.sanitize(r[e]);return t}}return e}log(t,r){this.logWithLevel(e.LogLevel.INFO,t,r)}logWithLevel(t,r,a){if(!this.shouldLog())return;const i=(new Date).toISOString(),n=a?this.sanitize(a):void 0,s={timestamp:i,level:e.LogLevel[t],context:this.context,message:r};switch(void 0!==n&&(s.data=n),t){case e.LogLevel.DEBUG:console.debug(`[${this.context}]`,s);break;case e.LogLevel.INFO:console.info(`[${this.context}]`,s);break;case e.LogLevel.WARN:console.warn(`[${this.context}]`,s);break;case e.LogLevel.ERROR:console.error(`[${this.context}]`,s)}}debug(t,r){this.logWithLevel(e.LogLevel.DEBUG,t,r)}info(t,r){this.logWithLevel(e.LogLevel.INFO,t,r)}warn(t,r){this.logWithLevel(e.LogLevel.WARN,t,r)}error(t,r){const a=r instanceof Error?{name:r.name,message:r.message,stack:r.stack}:r;this.logWithLevel(e.LogLevel.ERROR,t,a)}}e.ErrorCode=void 0,(f=e.ErrorCode||(e.ErrorCode={})).NOT_CONFIGURED="NOT_CONFIGURED",f.INVALID_CONFIG="INVALID_CONFIG",f.MISSING_DEPENDENCY="MISSING_DEPENDENCY",f.NETWORK_ERROR="NETWORK_ERROR",f.SERVER_ERROR="SERVER_ERROR",f.DOWNLOAD_FAILED="DOWNLOAD_FAILED",f.DOWNLOAD_TIMEOUT="DOWNLOAD_TIMEOUT",f.INVALID_URL="INVALID_URL",f.UNAUTHORIZED_HOST="UNAUTHORIZED_HOST",f.BUNDLE_TOO_LARGE="BUNDLE_TOO_LARGE",f.CHECKSUM_MISMATCH="CHECKSUM_MISMATCH",f.SIGNATURE_INVALID="SIGNATURE_INVALID",f.VERSION_DOWNGRADE="VERSION_DOWNGRADE",f.INVALID_BUNDLE_FORMAT="INVALID_BUNDLE_FORMAT",f.VALIDATION_ERROR="VALIDATION_ERROR",f.STORAGE_FULL="STORAGE_FULL",f.FILE_NOT_FOUND="FILE_NOT_FOUND",f.PERMISSION_DENIED="PERMISSION_DENIED",f.UPDATE_FAILED="UPDATE_FAILED",f.ROLLBACK_FAILED="ROLLBACK_FAILED",f.BUNDLE_NOT_READY="BUNDLE_NOT_READY",f.UPDATE_NOT_AVAILABLE="UPDATE_NOT_AVAILABLE",f.PLATFORM_NOT_SUPPORTED="PLATFORM_NOT_SUPPORTED",f.NATIVE_ERROR="NATIVE_ERROR";class y extends Error{constructor(e,t,r,a){super(t),this.code=e,this.message=t,this.details=r,this.originalError=a,this.name="NativeUpdateError",Object.setPrototypeOf(this,y.prototype)}toJSON(){return{name:this.name,code:this.code,message:this.message,details:this.details,stack:this.stack}}}class E extends y{constructor(e,t,r,a){super(e,t,r,a),this.name="DownloadError"}}class A extends y{constructor(e,t,r){super(e,t,r),this.name="ValidationError"}}class v extends y{constructor(e,t,r,a){super(e,t,r,a),this.name="StorageError"}}class I extends y{constructor(e,t,r,a){super(e,t,r,a),this.name="UpdateError"}}class D{constructor(){this.configManager=m.getInstance(),this.logger=w.getInstance()}static getInstance(){return D.instance||(D.instance=new D),D.instance}static validateUrl(e){try{return"https:"===new URL(e).protocol}catch(e){return!1}}static validateChecksum(e){return/^[a-f0-9]{64}$/i.test(e)}static sanitizeInput(e){return e?e.replace(/<[^>]*>/g,"").replace(/[^\w\s/.-]/g,""):""}static validateBundleSize(e){return e>0&&e<=104857600}async calculateChecksum(e){const t=await crypto.subtle.digest("SHA-256",e);return Array.from(new Uint8Array(t)).map(e=>e.toString(16).padStart(2,"0")).join("")}async verifyChecksum(e,t){if(!t)return this.logger.warn("No checksum provided for verification"),!0;const r=await this.calculateChecksum(e),a=r===t.toLowerCase();return a||this.logger.error("Checksum verification failed",{expected:t,actual:r}),a}async validateChecksum(e,t){return this.verifyChecksum(e,t)}async verifySignature(t,r){if(!this.configManager.get("enableSignatureValidation"))return!0;const a=this.configManager.get("publicKey");if(!a)throw new A(e.ErrorCode.SIGNATURE_INVALID,"Public key not configured for signature validation");try{const e=await crypto.subtle.importKey("spki",this.pemToArrayBuffer(a),{name:"RSA-PSS",hash:"SHA-256"},!1,["verify"]),i=await crypto.subtle.verify({name:"RSA-PSS",saltLength:32},e,this.base64ToArrayBuffer(r),t);return i||this.logger.error("Signature verification failed"),i}catch(e){return this.logger.error("Signature verification error",e),!1}}pemToArrayBuffer(e){const t=e.replace(/-----BEGIN PUBLIC KEY-----/g,"").replace(/-----END PUBLIC KEY-----/g,"").replace(/\s/g,"");return this.base64ToArrayBuffer(t)}base64ToArrayBuffer(e){const t=atob(e),r=new Uint8Array(t.length);for(let e=0;e<t.length;e++)r[e]=t.charCodeAt(e);return r.buffer}sanitizePath(e){return e.split("/").filter(e=>".."!==e&&"."!==e).join("/").replace(/^\/+/,"")}validateBundleId(t){if(!t||"string"!=typeof t)throw new A(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Bundle ID must be a non-empty string");if(!/^[a-zA-Z0-9\-_.]+$/.test(t))throw new A(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Bundle ID contains invalid characters");if(t.length>100)throw new A(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Bundle ID is too long (max 100 characters)")}validateVersion(t){if(!t||"string"!=typeof t)throw new A(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Version must be a non-empty string");if(!/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/.test(t))throw new A(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Version must follow semantic versioning format (e.g., 1.2.3)")}isVersionDowngrade(e,t){const r=this.parseVersion(e),a=this.parseVersion(t);return a.major<r.major||!(a.major>r.major)&&(a.minor<r.minor||!(a.minor>r.minor)&&a.patch<r.patch)}parseVersion(e){const t=e.split("-")[0].split(".");return{major:parseInt(t[0],10)||0,minor:parseInt(t[1],10)||0,patch:parseInt(t[2],10)||0}}validateUrl(t){if(!t||"string"!=typeof t)throw new A(e.ErrorCode.INVALID_URL,"URL must be a non-empty string");let r;try{r=new URL(t)}catch(t){throw new A(e.ErrorCode.INVALID_URL,"Invalid URL format")}if("https:"!==r.protocol)throw new A(e.ErrorCode.INVALID_URL,"Only HTTPS URLs are allowed");const a=this.configManager.get("allowedHosts");if(a.length>0&&!a.includes(r.hostname))throw new A(e.ErrorCode.UNAUTHORIZED_HOST,`Host ${r.hostname} is not in the allowed hosts list`);if([/^localhost$/i,/^127\./,/^10\./,/^172\.(1[6-9]|2[0-9]|3[0-1])\./,/^192\.168\./,/^::1$/,/^fc00:/i,/^fe80:/i].some(e=>e.test(r.hostname)))throw new A(e.ErrorCode.UNAUTHORIZED_HOST,"Private/local addresses are not allowed")}validateFileSize(t){if("number"!=typeof t||t<0)throw new A(e.ErrorCode.INVALID_BUNDLE_FORMAT,"File size must be a non-negative number");const r=this.configManager.get("maxBundleSize");if(t>r)throw new A(e.ErrorCode.BUNDLE_TOO_LARGE,`File size ${t} exceeds maximum allowed size of ${r} bytes`)}generateSecureId(){const e=new Uint8Array(16);return crypto.getRandomValues(e),Array.from(e,e=>e.toString(16).padStart(2,"0")).join("")}async validateCertificatePin(e,t){const r=this.configManager.certificatePins;if(!r||!Array.isArray(r)||0===r.length)return!0;const a=r.filter(t=>t.hostname===e);if(0===a.length)return!0;const i=await this.calculateCertificateHash(t),n=a.some(e=>e.sha256===i);return n||this.logger.error("Certificate pinning validation failed",{hostname:e,expectedPins:a.map(e=>e.sha256),actualHash:i}),n}async calculateCertificateHash(e){const t=(new TextEncoder).encode(e),r=await crypto.subtle.digest("SHA-256",t),a=Array.from(new Uint8Array(r));return"sha256/"+btoa(String.fromCharCode(...a))}validateMetadata(t){if(t&&"object"!=typeof t)throw new A(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Metadata must be an object");if(JSON.stringify(t||{}).length>10240)throw new A(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Metadata is too large (max 10KB)")}}class b{constructor(){this.STORAGE_KEY="capacitor_native_update_bundles",this.ACTIVE_BUNDLE_KEY="capacitor_native_update_active",this.preferences=null,this.cache=new Map,this.cacheExpiry=0,this.logger=w.getInstance(),this.configManager=m.getInstance()}async initialize(){if(this.preferences=this.configManager.get("preferences"),!this.preferences)throw new v(e.ErrorCode.MISSING_DEPENDENCY,"Preferences not configured. Please configure the plugin first.");await this.loadCache()}async loadCache(){if(!(Date.now()<this.cacheExpiry))try{const{value:e}=await this.preferences.get({key:this.STORAGE_KEY});if(e){const t=JSON.parse(e);this.cache.clear(),t.forEach(e=>this.cache.set(e.bundleId,e))}this.cacheExpiry=Date.now()+5e3}catch(e){this.logger.error("Failed to load bundles from storage",e),this.cache.clear()}}async saveCache(){try{const e=Array.from(this.cache.values());await this.preferences.set({key:this.STORAGE_KEY,value:JSON.stringify(e)}),this.logger.debug("Saved bundles to storage",{count:e.length})}catch(t){throw new v(e.ErrorCode.STORAGE_FULL,"Failed to save bundles to storage",void 0,t)}}async saveBundleInfo(e){this.validateBundleInfo(e),this.cache.set(e.bundleId,e),await this.saveCache(),this.logger.info("Bundle saved",{bundleId:e.bundleId,version:e.version})}validateBundleInfo(t){if(!t.bundleId||"string"!=typeof t.bundleId)throw new v(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Invalid bundle ID");if(!t.version||"string"!=typeof t.version)throw new v(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Invalid bundle version");if(!t.path||"string"!=typeof t.path)throw new v(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Invalid bundle path");if("number"!=typeof t.size||t.size<0)throw new v(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Invalid bundle size")}async getAllBundles(){return await this.loadCache(),Array.from(this.cache.values())}async getBundle(t){if(!t)throw new v(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Bundle ID is required");return await this.loadCache(),this.cache.get(t)||null}async deleteBundle(t){if(!t)throw new v(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Bundle ID is required");await this.loadCache(),this.cache.get(t)?(this.cache.delete(t),await this.saveCache(),await this.getActiveBundleId()===t&&await this.clearActiveBundle(),this.logger.info("Bundle deleted",{bundleId:t})):this.logger.warn("Attempted to delete non-existent bundle",{bundleId:t})}async getActiveBundle(){const e=await this.getActiveBundleId();return e?this.getBundle(e):null}async setActiveBundle(t){if(!t)throw new v(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Bundle ID is required");const r=await this.getBundle(t);if(!r)throw new v(e.ErrorCode.FILE_NOT_FOUND,`Bundle ${t} not found`);const a=await this.getActiveBundle();a&&a.bundleId!==t&&(a.status="READY",await this.saveBundleInfo(a)),r.status="ACTIVE",await this.saveBundleInfo(r),await this.preferences.set({key:this.ACTIVE_BUNDLE_KEY,value:t}),this.logger.info("Active bundle set",{bundleId:t,version:r.version})}async getActiveBundleId(){try{const{value:e}=await this.preferences.get({key:this.ACTIVE_BUNDLE_KEY});return e}catch(e){return this.logger.error("Failed to get active bundle ID",e),null}}async clearActiveBundle(){await this.preferences.remove({key:this.ACTIVE_BUNDLE_KEY}),this.logger.info("Active bundle cleared")}async clearAllBundles(){await this.preferences.remove({key:this.STORAGE_KEY}),await this.preferences.remove({key:this.ACTIVE_BUNDLE_KEY}),this.cache.clear(),this.cacheExpiry=0,this.logger.info("All bundles cleared")}async cleanupOldBundles(t){if(t<1)throw new v(e.ErrorCode.INVALID_CONFIG,"Keep count must be at least 1");const r=await this.getAllBundles(),a=await this.getActiveBundleId(),i=r.sort((e,t)=>t.downloadTime-e.downloadTime),n=new Set;a&&n.add(a);let s=n.size;for(const e of i){if(s>=t)break;n.has(e.bundleId)||(n.add(e.bundleId),s++)}let o=0;for(const e of r)n.has(e.bundleId)||(await this.deleteBundle(e.bundleId),o++);o>0&&this.logger.info("Cleaned up old bundles",{deleted:o,kept:s})}async getBundlesOlderThan(t){if(t<0)throw new v(e.ErrorCode.INVALID_CONFIG,"Timestamp must be non-negative");return(await this.getAllBundles()).filter(e=>e.downloadTime<t)}async markBundleAsVerified(t){const r=await this.getBundle(t);if(!r)throw new v(e.ErrorCode.FILE_NOT_FOUND,`Bundle ${t} not found`);r.verified=!0,await this.saveBundleInfo(r),this.logger.info("Bundle marked as verified",{bundleId:t})}async getTotalStorageUsed(){return(await this.getAllBundles()).reduce((e,t)=>e+t.size,0)}async isStorageLimitExceeded(e=0){const t=await this.getTotalStorageUsed();let r=3*this.configManager.get("maxBundleSize");try{if("storage"in navigator&&"estimate"in navigator.storage){const e=await navigator.storage.estimate();e.quota&&(r=Math.max(r,e.quota-104857600))}}catch(e){this.logger.warn("Storage API not available for quota check, using config limit")}return t+e>r}createDefaultBundle(){return{bundleId:"default",version:"1.0.0",path:"/",downloadTime:Date.now(),size:0,status:"ACTIVE",checksum:"",verified:!0}}async cleanExpiredBundles(){const e=this.configManager.get("cacheExpiration"),t=Date.now()-e,r=await this.getBundlesOlderThan(t);for(const e of r){const t=await this.getActiveBundleId();e.bundleId!==t&&await this.deleteBundle(e.bundleId)}}}class N{constructor(){this.activeDownloads=new Map,this.filesystem=null,this.logger=w.getInstance(),this.configManager=m.getInstance()}async initialize(){if(this.filesystem=this.configManager.get("filesystem"),!this.filesystem)throw new E(e.ErrorCode.MISSING_DEPENDENCY,"Filesystem not configured. Please configure the plugin first.")}validateUrl(t){try{const r=new URL(t);if("https:"!==r.protocol)throw new A(e.ErrorCode.INVALID_URL,"Only HTTPS URLs are allowed for security reasons");const a=this.configManager.get("allowedHosts");if(a.length>0&&!a.includes(r.hostname))throw new A(e.ErrorCode.UNAUTHORIZED_HOST,`Host ${r.hostname} is not in the allowed hosts list`)}catch(t){if(t instanceof A)throw t;throw new A(e.ErrorCode.INVALID_URL,"Invalid URL format")}}async download(t,r,a){if(this.validateUrl(t),!r)throw new A(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Bundle ID is required");if(this.activeDownloads.has(r))throw new E(e.ErrorCode.DOWNLOAD_FAILED,`Download already in progress for bundle ${r}`);const i=new AbortController,n={controller:i,startTime:Date.now()};this.activeDownloads.set(r,n);try{const s=this.configManager.get("downloadTimeout"),o=setTimeout(()=>i.abort(),s),l={"Cache-Control":"no-cache",Accept:"application/octet-stream, application/zip"};n.resumePosition&&n.resumePosition>0&&(l.Range=`bytes=${n.resumePosition}-`);const c=await fetch(t,{signal:i.signal,headers:l});if(clearTimeout(o),!c.ok)throw new E(e.ErrorCode.DOWNLOAD_FAILED,`Download failed: ${c.status} ${c.statusText}`,{status:c.status,statusText:c.statusText});const d=c.headers.get("content-type");if(d&&!this.isValidContentType(d))throw new A(e.ErrorCode.INVALID_BUNDLE_FORMAT,`Invalid content type: ${d}`);const u=c.headers.get("content-length"),h=u?parseInt(u,10):0;if(h>this.configManager.get("maxBundleSize"))throw new A(e.ErrorCode.BUNDLE_TOO_LARGE,`Bundle size ${h} exceeds maximum allowed size`);if(!h||!c.body){const e=await c.blob();return this.validateBlobSize(e),e}const g=c.body.getReader(),p=[];let f=0;for(;;){const{done:t,value:i}=await g.read();if(t)break;if(p.push(i),f+=i.length,f>this.configManager.get("maxBundleSize"))throw new A(e.ErrorCode.BUNDLE_TOO_LARGE,"Download size exceeds maximum allowed size");if(a){const e=n.resumePosition||0,t=e+f,i=e+h;a({percent:Math.round(t/i*100),bytesDownloaded:t,totalBytes:i,bundleId:r})}}const m=new Blob(p);return this.validateBlobSize(m),this.logger.info("Download completed",{bundleId:r,size:m.size,duration:Date.now()-n.startTime}),m}catch(t){if(t instanceof Error&&"AbortError"===t.name){const r=Date.now()-n.startTime>=this.configManager.get("downloadTimeout");throw new E(r?e.ErrorCode.DOWNLOAD_TIMEOUT:e.ErrorCode.DOWNLOAD_FAILED,r?"Download timed out":"Download cancelled",void 0,t)}throw t}finally{this.activeDownloads.delete(r)}}async resumeDownload(e,t,r,a){const i=this.activeDownloads.get(t);i&&(i.resumePosition=r.size);try{const i=await this.download(e,t,a);return new Blob([r,i])}catch(e){throw this.activeDownloads.delete(t),e}}async canResume(e){try{return"bytes"===(await fetch(e,{method:"HEAD"})).headers.get("Accept-Ranges")}catch(e){return!1}}isValidContentType(e){return["application/octet-stream","application/zip","application/x-zip-compressed","application/x-zip"].some(t=>e.includes(t))}validateBlobSize(t){if(0===t.size)throw new A(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Downloaded file is empty");if(t.size>this.configManager.get("maxBundleSize"))throw new A(e.ErrorCode.BUNDLE_TOO_LARGE,`File size ${t.size} exceeds maximum allowed size`)}cancelDownload(e){const t=this.activeDownloads.get(e);t&&(t.controller.abort(),this.activeDownloads.delete(e),this.logger.info("Download cancelled",{bundleId:e}))}cancelAllDownloads(){for(const e of this.activeDownloads.values())e.controller.abort();const e=this.activeDownloads.size;this.activeDownloads.clear(),e>0&&this.logger.info("All downloads cancelled",{count:e})}isDownloading(e){return this.activeDownloads.has(e)}getActiveDownloadCount(){return this.activeDownloads.size}async downloadWithRetry(t,r,a){const i=this.configManager.get("retryAttempts"),n=this.configManager.get("retryDelay");let s=null;for(let e=0;e<i;e++)try{if(e>0){const t=Math.min(n*Math.pow(2,e-1),3e4);await new Promise(e=>setTimeout(e,t)),this.logger.debug("Retrying download",{bundleId:r,attempt:e,delay:t})}return await this.download(t,r,a)}catch(t){if(s=t,t instanceof A||t instanceof Error&&"AbortError"===t.name)throw t;this.logger.warn(`Download attempt ${e+1} failed`,{bundleId:r,error:t})}throw new E(e.ErrorCode.DOWNLOAD_FAILED,"Download failed after all retries",{attempts:i},s||void 0)}async blobToArrayBuffer(e){return e.arrayBuffer()}async saveBlob(t,a){if(!this.filesystem)throw new E(e.ErrorCode.MISSING_DEPENDENCY,"Filesystem not initialized");const i=await this.blobToArrayBuffer(a),n=btoa(String.fromCharCode(...new Uint8Array(i))),s=`bundles/${t}/bundle.zip`;return await this.filesystem.writeFile({path:s,data:n,directory:r.Directory.Data,recursive:!0}),this.logger.debug("Bundle saved to filesystem",{bundleId:t,path:s,size:a.size}),s}async loadBlob(t){if(!this.filesystem)throw new E(e.ErrorCode.MISSING_DEPENDENCY,"Filesystem not initialized");try{const e=`bundles/${t}/bundle.zip`,a=await this.filesystem.readFile({path:e,directory:r.Directory.Data}),i=atob(a.data),n=new Uint8Array(i.length);for(let e=0;e<i.length;e++)n[e]=i.charCodeAt(e);return new Blob([n],{type:"application/zip"})}catch(e){return this.logger.debug("Failed to load bundle from filesystem",{bundleId:t,error:e}),null}}async deleteBlob(t){if(!this.filesystem)throw new E(e.ErrorCode.MISSING_DEPENDENCY,"Filesystem not initialized");try{const e=`bundles/${t}`;await this.filesystem.rmdir({path:e,directory:r.Directory.Data,recursive:!0}),this.logger.debug("Bundle deleted from filesystem",{bundleId:t})}catch(e){this.logger.warn("Failed to delete bundle from filesystem",{bundleId:t,error:e})}}}function C(e){const t=Math.floor(e.nanoseconds/1e6);return new Date(1e3*e.seconds+t)}const U="manifests";function R(e,t){return`${e}_${t}`}class _{constructor(e){var t;this.cache=new Map,this.config={projectId:e.projectId,databaseId:e.databaseId||"(default)",appId:e.appId,channel:e.channel,cacheDuration:e.cacheDuration||3e5,enableOffline:null===(t=e.enableOffline)||void 0===t||t},this.logger=w.getInstance()}static getInstance(e){if(!_.instance&&e&&(_.instance=new _(e)),!_.instance)throw new Error("FirestoreClient not initialized. Call with config first.");return _.instance}static resetInstance(){_.instance=null}getBaseUrl(){return`https://firestore.googleapis.com/v1/projects/${this.config.projectId}/databases/${this.config.databaseId}/documents`}async getDocument(e,t){const r=`${e}/${t}`,a=this.getFromCache(r);if(null!==a)return this.logger.debug("Firestore cache hit",{collection:e,documentId:t}),a;const i=`${this.getBaseUrl()}/${e}/${t}`;try{this.logger.debug("Fetching Firestore document",{url:i});const a=await fetch(i,{method:"GET",headers:{"Content-Type":"application/json"}});if(404===a.status)return this.logger.debug("Firestore document not found",{collection:e,documentId:t}),null;if(!a.ok)throw new Error(`Firestore error: ${a.status} ${a.statusText}`);const n=await a.json(),s=this.parseDocument(n);return this.setCache(r,s),s}catch(e){this.logger.error("Firestore fetch error",e);const t=this.getFromCache(r,!0);if(null!==t)return this.logger.warn("Using stale cache due to fetch error"),t;throw e}}async getManifest(){const e=R(this.config.appId,this.config.channel);return this.getDocument(U,e)}async getManifestFor(e,t){const r=R(e,t);return this.getDocument(U,r)}parseDocument(e){return this.parseValue({mapValue:{fields:e.fields}})}parseValue(e){if("stringValue"in e)return e.stringValue;if("integerValue"in e)return parseInt(e.integerValue,10);if("doubleValue"in e)return e.doubleValue;if("booleanValue"in e)return e.booleanValue;if("nullValue"in e)return null;if("timestampValue"in e)return this.parseTimestamp(e.timestampValue);if("mapValue"in e){const t={},r=e.mapValue.fields||{};for(const[e,a]of Object.entries(r))t[e]=this.parseValue(a);return t}return"arrayValue"in e?(e.arrayValue.values||[]).map(e=>this.parseValue(e)):null}parseTimestamp(e){const t=new Date(e);return{seconds:Math.floor(t.getTime()/1e3),nanoseconds:t.getTime()%1e3*1e6}}getFromCache(e,t=!1){const r=this.cache.get(e);return r&&(Date.now()<r.expiresAt||t)?r.data:null}setCache(e,t){const r=Date.now();this.cache.set(e,{data:t,timestamp:r,expiresAt:r+this.config.cacheDuration})}clearCache(){this.cache.clear(),this.logger.debug("Firestore cache cleared")}clearCacheEntry(e,t){this.cache.delete(`${e}/${t}`)}getCacheStats(){return{size:this.cache.size,keys:Array.from(this.cache.keys())}}getConfig(){return Object.assign({},this.config)}setChannel(e){this.config.channel=e,this.clearCache()}isConfigured(){return!!(this.config.projectId&&this.config.appId&&this.config.channel)}}_.instance=null;class L{constructor(e){this.firestoreClient=e,this.logger=w.getInstance()}async checkForUpdates(e){const{currentVersion:t,deviceInfo:r,checkDeltas:a=!0}=e;try{e.forceRefresh&&this.firestoreClient.clearCache();const i=await this.firestoreClient.getManifest();if(!i)return this.logger.debug("No manifest found"),{updateAvailable:!1};if(!this.isNewerVersion(t,i.current.version))return{updateAvailable:!1,version:i.current.version};const n=await this.checkRolloutEligibility(i.rollout,r);if(!n.eligible)return{updateAvailable:!0,version:i.current.version,rolloutEligible:!1,rolloutReason:n.reason};const s={updateAvailable:!0,version:i.current.version,bundleUrl:i.current.bundleUrl,minNativeVersion:i.current.minNativeVersion,releaseNotes:i.current.releaseNotes,signature:i.current.signature,checksum:i.current.checksum,mandatory:i.current.mandatory,size:i.current.size,rolloutEligible:!0};if(a&&i.deltas){const e=i.deltas[t];e?(s.delta={available:!0,patchUrl:e.patchUrl,patchSize:e.patchSize,patchChecksum:e.patchChecksum,targetChecksum:e.targetChecksum},this.logger.debug("Delta update available",{from:t,to:i.current.version,patchSize:e.patchSize})):s.delta={available:!1}}return s}catch(e){throw this.logger.error("Error checking for updates",e),e}}async checkRolloutEligibility(e,t){var r,a;if(!e.enabled)return{eligible:!0,reason:"Rollout not enabled, all devices eligible"};const i=Date.now();if(i<C(e.startTime).getTime())return{eligible:!1,reason:"Rollout not started yet"};if(e.endTime&&i>C(e.endTime).getTime())return{eligible:!1,reason:"Rollout ended"};if("scheduled"===(null===(r=e.schedule)||void 0===r?void 0:r.type)&&e.schedule.scheduledTime&&i<C(e.schedule.scheduledTime).getTime())return{eligible:!1,reason:"Scheduled for later"};if(e.targetSegments){const r=this.checkSegments(e.targetSegments,t);if(!r.eligible)return r}let n=e.percentage;"gradual"===(null===(a=e.schedule)||void 0===a?void 0:a.type)&&(n=this.calculateGradualPercentage(e));const s=await this.getDevicePercentile(t.deviceId);return s>n?{eligible:!1,reason:`Device percentile ${s.toFixed(1)}% > rollout ${n}%`}:{eligible:!0,reason:"All checks passed"}}checkSegments(e,t){return e.platforms&&e.platforms.length>0&&!e.platforms.includes(t.platform)?{eligible:!1,reason:`Platform ${t.platform} not targeted`}:e.minAppVersion&&this.compareVersions(t.appVersion,e.minAppVersion)<0?{eligible:!1,reason:`App version below minimum ${e.minAppVersion}`}:e.maxAppVersion&&this.compareVersions(t.appVersion,e.maxAppVersion)>0?{eligible:!1,reason:`App version above maximum ${e.maxAppVersion}`}:e.deviceIds&&e.deviceIds.length>0&&!e.deviceIds.includes(t.deviceId)?{eligible:!1,reason:"Device not in whitelist"}:!(e.regions&&e.regions.length>0)||t.region&&e.regions.includes(t.region)?{eligible:!0,reason:"Segment checks passed"}:{eligible:!1,reason:`Region ${t.region||"unknown"} not targeted`}}calculateGradualPercentage(e){var t,r;if(!(null===(t=e.schedule)||void 0===t?void 0:t.gradualSteps)||!(null===(r=e.schedule)||void 0===r?void 0:r.gradualInterval))return e.percentage;const a=C(e.startTime).getTime(),i=(Date.now()-a)/36e5,n=Math.floor(i/e.schedule.gradualInterval),s=e.schedule.gradualSteps;return n>=s.length?s[s.length-1]:s[n]}async getDevicePercentile(e){const t=await this.hashString(e);return this.hashToPercentile(t)}async hashString(e){const t=(new TextEncoder).encode(e),r=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(r)).map(e=>e.toString(16).padStart(2,"0")).join("")}hashToPercentile(e){return parseInt(e.substring(0,8),16)/parseInt("ffffffff",16)*100}compareVersions(e,t){const r=e.split("-")[0],a=t.split("-")[0],i=r.split(".").map(Number),n=a.split(".").map(Number);for(let e=0;e<Math.max(i.length,n.length);e++){const t=i[e]||0,r=n[e]||0;if(t>r)return 1;if(t<r)return-1}const s=e.includes("-"),o=t.includes("-");return s&&!o?-1:!s&&o?1:0}isNewerVersion(e,t){return this.compareVersions(e,t)<0}async getManifest(){return this.firestoreClient.getManifest()}async getManifestFor(e,t){return this.firestoreClient.getManifestFor(e,t)}clearCache(){this.firestoreClient.clearCache()}}class S{constructor(){this.VERSION_CHECK_CACHE_KEY="capacitor_native_update_version_cache",this.CACHE_DURATION=3e5,this.preferences=null,this.memoryCache=new Map,this.firestoreClient=null,this.manifestReader=null,this.logger=w.getInstance(),this.configManager=m.getInstance(),this.securityValidator=D.getInstance()}static compareVersions(e,t){try{const[r,a]=e.split("-"),[i,n]=t.split("-"),s=r.split(".").map(Number),o=i.split(".").map(Number);for(let e=0;e<3;e++){const t=s[e]||0,r=o[e]||0;if(t>r)return 1;if(t<r)return-1}return a&&!n?-1:!a&&n?1:a&&n?a.localeCompare(n):0}catch(r){return e===t?0:e>t?1:-1}}static isValidVersion(e){return/^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(e)}static shouldUpdate(e,t,r){return!(r&&S.compareVersions(e,r)<0)&&S.compareVersions(e,t)<0}async initialize(){if(this.preferences=this.configManager.get("preferences"),!this.preferences)throw new A(e.ErrorCode.MISSING_DEPENDENCY,"Preferences not configured. Please configure the plugin first.");if("firestore"===this.configManager.get("backendType")){const t=this.configManager.get("firestore");if(!t)throw new A(e.ErrorCode.INVALID_CONFIG,"Firestore configuration required when using firestore backend");this.firestoreClient=new _(t),this.manifestReader=new L(this.firestoreClient),this.logger.debug("Firestore backend initialized")}}async checkForUpdatesFromFirestore(t,r){if(!this.manifestReader)throw new A(e.ErrorCode.INVALID_CONFIG,"Firestore backend not initialized. Call initialize() first or configure firestore backend.");this.securityValidator.validateVersion(t);const a={currentVersion:t,deviceInfo:r,checkDeltas:this.configManager.get("enableDeltaUpdates")};try{const e=await this.manifestReader.checkForUpdates(a);return e&&this.logger.info("Firestore update check completed",{currentVersion:t,latestVersion:e.version,updateAvailable:e.updateAvailable,eligible:e.rolloutEligible}),e}catch(e){return this.logger.error("Failed to check for updates from Firestore",e),null}}async checkForUpdatesAuto(t,r){if("firestore"===(this.configManager.get("backendType")||"http")){if(!r)throw new A(e.ErrorCode.INVALID_CONFIG,"Device info is required for Firestore backend");return this.checkForUpdatesFromFirestore(t,r)}const a=this.configManager.get("serverUrl"),i=this.configManager.get("channel")||"production",n=this.configManager.get("appId");if(!a||!n)throw new A(e.ErrorCode.INVALID_CONFIG,"Server URL and App ID are required for HTTP backend");return this.checkForUpdates(a,i,t,n)}async checkForUpdates(t,r,a,i){if(this.securityValidator.validateUrl(t),this.securityValidator.validateVersion(a),!r||!i)throw new A(e.ErrorCode.INVALID_CONFIG,"Channel and appId are required");const n=`${r}-${i}`,s=await this.getCachedVersionInfo(n);if(s&&s.channel===r&&Date.now()-s.timestamp<this.CACHE_DURATION)return this.logger.debug("Returning cached version info",{channel:r,version:s.data.version}),s.data;try{const s=new URL(`${t}/check`);s.searchParams.append("channel",r),s.searchParams.append("version",a),s.searchParams.append("appId",i),s.searchParams.append("platform","web");const o=await fetch(s.toString(),{method:"GET",headers:{"Content-Type":"application/json","X-App-Version":a,"X-App-Id":i},signal:AbortSignal.timeout(this.configManager.get("downloadTimeout"))});if(!o.ok)throw new Error(`Version check failed: ${o.status}`);const l=await o.json();if(!l.version)throw new A(e.ErrorCode.INVALID_BUNDLE_FORMAT,"No version in server response");return this.securityValidator.validateVersion(l.version),l.bundleUrl&&this.securityValidator.validateUrl(l.bundleUrl),l.minAppVersion&&this.securityValidator.validateVersion(l.minAppVersion),await this.cacheVersionInfo(n,r,l),this.logger.info("Version check completed",{channel:r,currentVersion:a,latestVersion:l.version,updateAvailable:this.isNewerVersion(l.version,a)}),l}catch(e){return this.logger.error("Failed to check for updates",e),null}}isNewerVersion(e,t){try{const r=this.parseVersion(e),a=this.parseVersion(t);return r.major!==a.major?r.major>a.major:r.minor!==a.minor?r.minor>a.minor:r.patch!==a.patch?r.patch>a.patch:!(r.prerelease&&!a.prerelease||(r.prerelease||!a.prerelease)&&(!r.prerelease||!a.prerelease||!(r.prerelease>a.prerelease)))}catch(r){return this.logger.error("Failed to compare versions",{version1:e,version2:t,error:r}),!1}}isUpdateMandatory(e,t){if(!t)return!1;try{return this.securityValidator.validateVersion(e),this.securityValidator.validateVersion(t),!this.isNewerVersion(e,t)&&e!==t}catch(e){return this.logger.error("Failed to check mandatory update",e),!1}}parseVersion(t){const r=t.match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/);if(!r)throw new A(e.ErrorCode.INVALID_BUNDLE_FORMAT,"Invalid version format");return{major:parseInt(r[1],10),minor:parseInt(r[2],10),patch:parseInt(r[3],10),prerelease:r[4],build:r[5]}}buildVersionString(e){let t=`${e.major}.${e.minor}.${e.patch}`;return e.prerelease&&(t+=`-${e.prerelease}`),e.build&&(t+=`+${e.build}`),t}isCompatibleWithNativeVersion(e,t,r){if(!r)return!0;try{const a=r[e];return!a||(this.securityValidator.validateVersion(t),this.securityValidator.validateVersion(a),!this.isNewerVersion(a,t))}catch(e){return this.logger.error("Failed to check compatibility",e),!1}}async getCachedVersionInfo(e){const t=this.memoryCache.get(e);if(t&&Date.now()-t.timestamp<this.CACHE_DURATION)return t;try{const{value:t}=await this.preferences.get({key:this.VERSION_CHECK_CACHE_KEY});if(!t)return null;const r=JSON.parse(t)[e];if(r&&Date.now()-r.timestamp<this.CACHE_DURATION)return this.memoryCache.set(e,r),r}catch(e){this.logger.debug("Failed to load cached version info",e)}return null}async cacheVersionInfo(e,t,r){const a={channel:t,data:r,timestamp:Date.now()};this.memoryCache.set(e,a);try{const{value:t}=await this.preferences.get({key:this.VERSION_CHECK_CACHE_KEY}),r=t?JSON.parse(t):{},i=Date.now();for(const e in r)i-r[e].timestamp>2*this.CACHE_DURATION&&delete r[e];r[e]=a,await this.preferences.set({key:this.VERSION_CHECK_CACHE_KEY,value:JSON.stringify(r)})}catch(e){this.logger.warn("Failed to cache version info",e)}}async clearVersionCache(){this.memoryCache.clear();try{await this.preferences.remove({key:this.VERSION_CHECK_CACHE_KEY})}catch(e){this.logger.warn("Failed to clear version cache",e)}}shouldBlockDowngrade(e,t){try{return this.securityValidator.isVersionDowngrade(e,t)}catch(e){return this.logger.error("Failed to check downgrade",e),!0}}}class O{constructor(e){this.cachedAppInfo=null,this.CACHE_DURATION=6e4,this.config=e,this.logger=new w("AppUpdateChecker")}async checkServerVersion(e){if(!this.config.updateUrl)return{};try{const e=await this.getCurrentVersion(),t=new URL(`${this.config.updateUrl}/app-version`);t.searchParams.append("platform",this.getPlatform()),t.searchParams.append("current",e),this.config.channel&&t.searchParams.append("channel",this.config.channel);const r=await fetch(t.toString(),{method:"GET",headers:{Accept:"application/json","X-App-Version":e,"X-App-Platform":this.getPlatform()}});if(!r.ok)throw new Error(`Server returned ${r.status}`);const a=await r.json();return{availableVersion:a.version,updatePriority:a.priority,releaseNotes:a.releaseNotes,updateSize:a.size,updateURL:a.downloadUrl}}catch(e){return this.logger.error("Failed to check server version",e),{}}}compareVersions(e,t){const r=e.split(".").map(Number),a=t.split(".").map(Number);for(let e=0;e<Math.max(r.length,a.length);e++){const t=r[e]||0,i=a[e]||0;if(t>i)return 1;if(t<i)return-1}return 0}isUpdateRequired(e,t,r){return this.compareVersions(e,t)<0||!!(r&&this.compareVersions(e,r)<0)}determineUpdatePriority(e,t){const[r,a]=e.split(".").map(Number);return r>0?5:a>0&&t&&t>30?4:a>0?3:1}async getCurrentVersion(){if(this.cachedAppInfo&&Date.now()-this.cachedAppInfo.timestamp<this.CACHE_DURATION)return this.cachedAppInfo.version;try{const{App:e}=await Promise.resolve().then(function(){return K}),t=await e.getInfo();return this.cachedAppInfo={version:t.version,build:t.build,name:t.name,id:t.id,timestamp:Date.now()},this.logger.debug("Got app info from Capacitor",{version:t.version,build:t.build}),t.version}catch(e){this.logger.debug("Failed to get app info from Capacitor, using fallback",e);const t=this.config.appVersion;if("string"==typeof t)return t;if("undefined"!=typeof window){const e=window;if("string"==typeof e.APP_VERSION)return e.APP_VERSION}return"1.0.0"}}async getCurrentBuild(){if(this.cachedAppInfo&&Date.now()-this.cachedAppInfo.timestamp<this.CACHE_DURATION)return this.cachedAppInfo.build;try{const{App:e}=await Promise.resolve().then(function(){return K}),t=await e.getInfo();return this.cachedAppInfo={version:t.version,build:t.build,name:t.name,id:t.id,timestamp:Date.now()},t.build}catch(e){return"1"}}async getAppInfo(){if(this.cachedAppInfo&&Date.now()-this.cachedAppInfo.timestamp<this.CACHE_DURATION)return this.cachedAppInfo;try{const{App:e}=await Promise.resolve().then(function(){return K}),t=await e.getInfo();return this.cachedAppInfo={version:t.version,build:t.build,name:t.name,id:t.id,timestamp:Date.now()},this.cachedAppInfo}catch(e){return{version:"1.0.0",build:"1",name:"App",id:"com.app",timestamp:Date.now()}}}clearCache(){this.cachedAppInfo=null}getPlatform(){if("undefined"!=typeof window){const e=window.navigator.userAgent;if(/android/i.test(e))return"android";if(/iPad|iPhone|iPod/.test(e))return"ios"}return"web"}}var T;!function(e){e[e.UNKNOWN=0]="UNKNOWN",e[e.PENDING=1]="PENDING",e[e.DOWNLOADING=2]="DOWNLOADING",e[e.INSTALLING=3]="INSTALLING",e[e.INSTALLED=4]="INSTALLED",e[e.FAILED=5]="FAILED",e[e.CANCELED=6]="CANCELED",e[e.DOWNLOADED=11]="DOWNLOADED"}(T||(T={}));class M{constructor(e){this.logger=new w("AppUpdateInstaller"),this.currentState={installStatus:T.UNKNOWN,packageName:"",availableVersion:""}}async startImmediateUpdate(){if(this.logger.log("Starting immediate update installation"),this.updateState(T.PENDING),this.isAndroid())this.logger.log("Triggering Android immediate update");else{if(!this.isIOS())throw new Error("Immediate updates not supported on web platform");this.logger.log("Opening iOS App Store for update")}}async startFlexibleUpdate(){if(this.logger.log("Starting flexible update download"),this.updateState(T.DOWNLOADING),this.isWeb())this.simulateFlexibleUpdate();else{if(!this.isAndroid())throw new Error("Flexible updates not supported on iOS");this.logger.log("Starting Android flexible update")}}async completeFlexibleUpdate(){if(this.logger.log("Completing flexible update installation"),this.currentState.installStatus!==T.DOWNLOADED)throw new Error("Update not ready for installation");this.updateState(T.INSTALLING),this.isAndroid()?this.logger.log("Completing Android update installation"):setTimeout(()=>{this.updateState(T.INSTALLED)},1e3)}async cancelUpdate(){this.logger.log("Cancelling update"),this.currentState.installStatus===T.DOWNLOADING&&this.updateState(T.CANCELED)}async getInstallState(){return Object.assign({},this.currentState)}onProgress(e){this.progressCallback=e}updateState(e,t){this.currentState.installStatus=e,void 0!==t&&(this.currentState.installErrorCode=t),this.logger.log("Update state changed",this.currentState)}simulateFlexibleUpdate(){let e=0;const t=52428800,r=1048576,a=setInterval(()=>{e+=r,e>=t&&(e=t,clearInterval(a),this.updateState(T.DOWNLOADED));const i={bytesDownloaded:e,totalBytesToDownload:t,percentComplete:Math.round(e/t*100),downloadSpeed:r,estimatedTime:Math.ceil((t-e)/r)};this.progressCallback&&this.progressCallback(i)},1e3)}isAndroid(){return"undefined"!=typeof window&&/android/i.test(window.navigator.userAgent)}isIOS(){return"undefined"!=typeof window&&/iPad|iPhone|iPod/.test(window.navigator.userAgent)}isWeb(){return!this.isAndroid()&&!this.isIOS()}}class V{constructor(e){this.config=e,this.logger=new w("PlatformAppUpdate"),this.platform=t.Capacitor.getPlatform()}async checkForUpdate(e){this.logger.log("Checking for platform update: "+this.platform);const t=await this.getVersionInfo(),r={updateAvailable:!1,currentVersion:t.currentVersion,availableVersion:t.currentVersion};if("android"===this.platform)return r;if("ios"===this.platform)return r;if(this.config.webUpdateUrl)try{const e=await fetch(this.config.webUpdateUrl),a=await e.json();a.version&&a.version!==t.currentVersion&&(r.updateAvailable=!0,r.availableVersion=a.version,r.releaseNotes=a.releaseNotes,r.updateURL=a.downloadUrl)}catch(e){this.logger.error("Failed to check web update",e)}return r}async getVersionInfo(){return{currentVersion:"1.0.0",buildNumber:"1",packageName:"com.example.app",platform:this.platform,minimumVersion:this.config.minimumVersion}}async getAppStoreUrl(){const e=this.platform;let t="";if("ios"===e){const e=this.config.appStoreId||this.config.iosAppId;if(!e)throw new Error("App Store ID not configured");t=`https://apps.apple.com/app/id${e}`}else t="android"===e?`https://play.google.com/store/apps/details?id=${this.config.packageName||(await this.getVersionInfo()).packageName}`:this.config.webUpdateUrl||window.location.origin;return{url:t,platform:e}}async openUrl(e){if("undefined"==typeof window||!window.open)throw new Error("Cannot open URL on this platform");window.open(e,"_blank")}isUpdateSupported(){return"android"===this.platform||"ios"!==this.platform}getUpdateCapabilities(){const e={immediateUpdate:!1,flexibleUpdate:!1,backgroundDownload:!1,inAppReview:!1};return"android"===this.platform?(e.immediateUpdate=!0,e.flexibleUpdate=!0,e.backgroundDownload=!0,e.inAppReview=!0):"ios"===this.platform&&(e.inAppReview=!0),e}}class B{constructor(e){this.listeners=new Map,this.config=e,this.logger=new w("AppUpdateManager"),this.checker=new O(e),this.installer=new M(e),this.platformUpdate=new V(e)}async checkAppUpdate(e){try{this.logger.log("Checking for app updates",e);const t=await this.platformUpdate.checkForUpdate(e);if(!t.updateAvailable&&this.config.updateUrl){const r=await this.checker.checkServerVersion(e);return this.mergeUpdateInfo(t,r)}return t}catch(e){throw this.logger.error("Failed to check app update",e),e}}async startImmediateUpdate(){try{this.logger.log("Starting immediate update");const e=await this.checkAppUpdate();if(!e.immediateUpdateAllowed)throw new Error("Immediate update not allowed");await this.installer.startImmediateUpdate(),this.emit("appUpdateStateChanged",{installStatus:1,packageName:this.config.packageName||"",availableVersion:e.availableVersion||""})}catch(e){throw this.logger.error("Failed to start immediate update",e),e}}async startFlexibleUpdate(){try{this.logger.log("Starting flexible update");const e=await this.checkAppUpdate();if(!e.flexibleUpdateAllowed)throw new Error("Flexible update not allowed");await this.installer.startFlexibleUpdate(),this.installer.onProgress(e=>{this.emit("appUpdateProgress",e)}),this.emit("appUpdateStateChanged",{installStatus:2,packageName:this.config.packageName||"",availableVersion:e.availableVersion||""})}catch(e){throw this.logger.error("Failed to start flexible update",e),e}}async completeFlexibleUpdate(){try{this.logger.log("Completing flexible update"),await this.installer.completeFlexibleUpdate(),this.emit("appUpdateStateChanged",{installStatus:3,packageName:this.config.packageName||"",availableVersion:""})}catch(e){throw this.logger.error("Failed to complete flexible update",e),e}}async getVersionInfo(){try{return this.logger.log("Getting version info"),await this.platformUpdate.getVersionInfo()}catch(e){throw this.logger.error("Failed to get version info",e),e}}async isMinimumVersionMet(){try{this.logger.log("Checking minimum version");const e=await this.getVersionInfo(),t=this.config.minimumVersion||"0.0.0",r=this.checker.compareVersions(e.currentVersion,t)>=0;return{isMet:r,currentVersion:e.currentVersion,minimumVersion:t,updateRequired:!r&&!0===this.config.enforceMinVersion}}catch(e){throw this.logger.error("Failed to check minimum version",e),e}}async getAppUpdateInfo(){return this.checkAppUpdate()}async performImmediateUpdate(){return this.startImmediateUpdate()}async openAppStore(e){try{this.logger.log("Opening app store");const e=await this.getAppStoreUrl();await this.platformUpdate.openUrl(e.url)}catch(e){throw this.logger.error("Failed to open app store",e),e}}async getAppStoreUrl(){try{return this.logger.log("Getting app store URL"),await this.platformUpdate.getAppStoreUrl()}catch(e){throw this.logger.error("Failed to get app store URL",e),e}}async getUpdateInstallState(){try{return this.logger.log("Getting update install state"),await this.installer.getInstallState()}catch(e){throw this.logger.error("Failed to get update install state",e),e}}addListener(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),{remove:async()=>{const r=this.listeners.get(e);r&&r.delete(t)}}}async removeAllListeners(e){e?this.listeners.delete(e):this.listeners.clear()}emit(e,t){const r=this.listeners.get(e);r&&r.forEach(r=>{try{r(t)}catch(t){this.logger.error(`Error in ${e} listener`,t)}})}mergeUpdateInfo(e,t){return Object.assign(Object.assign(Object.assign({},e),t),{updateAvailable:e.updateAvailable||!!t.availableVersion,availableVersion:t.availableVersion||e.availableVersion})}}class k{constructor(){this.config=null,this.status={enabled:!1,isRunning:!1,checkCount:0,failureCount:0},this.deps={},this.logger=w.getInstance(),this.configManager=m.getInstance()}setDependencies(e){this.deps=e}configure(e){this.config=e,this.status.enabled=e.enabled}getStatus(){return Object.assign({},this.status)}async performCheck(){var e;if(!(null===(e=this.config)||void 0===e?void 0:e.enabled))return{success:!1,updatesFound:!1,notificationSent:!1,error:{code:g.INVALID_CONFIG,message:"Background updates not enabled"}};this.status.isRunning=!0,this.status.checkCount++;try{const e=await this.checkForUpdates();return this.status.lastCheckTime=Date.now(),this.status.isRunning=!1,this.status.lastError=void 0,e}catch(e){return this.status.failureCount++,this.status.isRunning=!1,this.status.lastError={code:g.UNKNOWN_ERROR,message:e instanceof Error?e.message:"Unknown error"},{success:!1,updatesFound:!1,notificationSent:!1,error:this.status.lastError}}}async checkForUpdates(){var e,t,r,a,n,s;const o=[];let l,c;((null===(e=this.config)||void 0===e?void 0:e.updateTypes.includes(i.APP_UPDATE))||(null===(t=this.config)||void 0===t?void 0:t.updateTypes.includes(i.BOTH)))&&o.push(this.checkAppUpdate()),((null===(r=this.config)||void 0===r?void 0:r.updateTypes.includes(i.LIVE_UPDATE))||(null===(a=this.config)||void 0===a?void 0:a.updateTypes.includes(i.BOTH)))&&o.push(this.checkLiveUpdate());const d=await Promise.allSettled(o);"fulfilled"===(null===(n=d[0])||void 0===n?void 0:n.status)&&(l=d[0].value),"fulfilled"===(null===(s=d[1])||void 0===s?void 0:s.status)&&(c=d[1].value);const u=(null==l?void 0:l.updateAvailable)||(null==c?void 0:c.available)||!1;let h=!1;return u&&(h=await this.sendNotification(l,c)),{success:!0,updatesFound:u,appUpdate:l,liveUpdate:c,notificationSent:h}}async checkAppUpdate(){if(this.deps.appUpdateManager)try{return await this.deps.appUpdateManager.getAppUpdateInfo()}catch(e){this.logger.error("Failed to check app update via manager",e)}const e=this.configManager.get("baseUrl")||this.configManager.get("serverUrl");if(e)try{const t=await this.getCurrentAppVersion(),r=await fetch(`${e}/app-version`,{method:"GET",headers:{"Content-Type":"application/json","X-Current-Version":t,"X-Platform":this.getPlatform()}});if(r.ok){const e=await r.json(),a=e.version||e.latestVersion,i=a&&this.compareVersions(t,a)<0;return{updateAvailable:i,currentVersion:t,availableVersion:i?a:void 0,updatePriority:e.priority}}}catch(e){this.logger.error("Failed to check app update via server",e)}return{updateAvailable:!1,currentVersion:await this.getCurrentAppVersion()}}async checkLiveUpdate(){let e="1.0.0";if(this.deps.bundleManager)try{const t=await this.deps.bundleManager.getActiveBundle();t&&(e=t.version)}catch(e){}if(this.deps.versionManager)try{const t=this.configManager.get("serverUrl"),r=this.configManager.get("channel")||"production",a=this.configManager.get("appId");if(t&&a){const i=await this.deps.versionManager.checkForUpdates(t,r,e,a);if(i)return i}}catch(e){this.logger.error("Failed to check live update via VersionManager",e)}const t=this.configManager.get("baseUrl")||this.configManager.get("serverUrl");if(t)try{const r=await fetch(`${t}/updates/latest`,{method:"GET",headers:{"Content-Type":"application/json","X-Current-Version":e}});if(r.ok){const t=await r.json(),a=t.version||t.latestVersion,i=a&&this.compareVersions(e,a)<0;return{available:i,version:a,url:i?t.url:void 0,notes:t.notes||t.releaseNotes,size:t.size,checksum:t.checksum}}}catch(e){this.logger.error("Failed to check live update via server",e)}return{available:!1}}async sendNotification(e,t){var r;if(!(null===(r=this.config)||void 0===r?void 0:r.notificationPreferences))return!1;const a=this.config.notificationPreferences;let i=a.title||"Update Available",n=a.description||"";if((null==e?void 0:e.updateAvailable)?(i="App Update Available",n=`Version ${e.availableVersion} is ready to install.`):(null==t?void 0:t.available)&&(i="New Update Available",n=`Version ${t.version} is ready to download. ${t.notes||""}`),"undefined"!=typeof window&&"Notification"in window)try{if("granted"===Notification.permission)return new Notification(i,{body:n}),!0}catch(e){this.logger.error("Failed to send notification",e)}return this.logger.info("Background update notification",{title:i,body:n}),!1}async getCurrentAppVersion(){try{const{App:e}=await Promise.resolve().then(function(){return K});return(await e.getInfo()).version}catch(e){return"1.0.0"}}getPlatform(){if("undefined"!=typeof window){const e=window.navigator.userAgent;if(/android/i.test(e))return"android";if(/iPad|iPhone|iPod/.test(e))return"ios"}return"web"}calculateNextCheckTime(){var e;return Date.now()+((null===(e=this.config)||void 0===e?void 0:e.checkInterval)||864e5)}shouldRespectBatteryOptimization(){var e,t;return null===(t=null===(e=this.config)||void 0===e?void 0:e.respectBatteryOptimization)||void 0===t||t}isNetworkConditionMet(){return"undefined"==typeof navigator||!("onLine"in navigator)||navigator.onLine}isBatteryLevelSufficient(){return!0}compareVersions(e,t){const r=e.split(".").map(Number),a=t.split(".").map(Number);for(let e=0;e<Math.max(r.length,a.length);e++){const t=r[e]||0,i=a[e]||0;if(t>i)return 1;if(t<i)return-1}return 0}}class F{constructor(){this.listeners=new Map}static getInstance(){return F.instance||(F.instance=new F),F.instance}addListener(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>{const r=this.listeners.get(e);r&&(r.delete(t),0===r.size&&this.listeners.delete(e))}}emit(e,t){const r=this.listeners.get(e);r&&r.forEach(r=>{try{r(t)}catch(t){console.error(`Error in event listener for ${e}:`,t)}})}removeListeners(e){this.listeners.delete(e)}removeAllListeners(){this.listeners.clear()}listenerCount(e){var t;return(null===(t=this.listeners.get(e))||void 0===t?void 0:t.size)||0}eventNames(){return Array.from(this.listeners.keys())}}class P{constructor(){this.bundleManager=null,this.downloadManager=null,this.versionManager=null,this.appUpdateManager=null,this.backgroundScheduler=null,this.initialized=!1,this.configManager=m.getInstance(),this.logger=w.getInstance(),this.securityValidator=D.getInstance(),this.eventEmitter=F.getInstance()}static getInstance(){return P.instance||(P.instance=new P),P.instance}async initialize(e){var t,i;if(this.initialized)this.logger.warn("Plugin already initialized");else try{this.configManager.configure(e),e.filesystem||(e.filesystem=r.Filesystem),e.preferences||(e.preferences=a.Preferences),this.bundleManager=new b,await this.bundleManager.initialize(),this.downloadManager=new N,await this.downloadManager.initialize(),this.versionManager=new S,await this.versionManager.initialize(),this.appUpdateManager=new B({serverUrl:e.serverUrl||e.baseUrl||"",channel:e.channel||"production",autoCheck:null===(t=e.autoCheck)||void 0===t||t,autoUpdate:null!==(i=e.autoUpdate)&&void 0!==i&&i,updateStrategy:e.updateStrategy,publicKey:e.publicKey,requireSignature:e.requireSignature,checksumAlgorithm:e.checksumAlgorithm,checkInterval:e.checkInterval,security:e.security}),this.setupAppUpdateEventBridge(),this.backgroundScheduler=new k,this.backgroundScheduler.setDependencies({appUpdateManager:this.appUpdateManager,versionManager:this.versionManager,bundleManager:this.bundleManager}),this.initialized=!0,this.logger.info("Plugin initialized successfully")}catch(e){throw this.logger.error("Failed to initialize plugin",e),e}}isInitialized(){return this.initialized&&this.configManager.isConfigured()}ensureInitialized(){if(!this.isInitialized())throw new y(e.ErrorCode.NOT_CONFIGURED,"Plugin not initialized. Please call initialize() first.")}getBundleManager(){return this.ensureInitialized(),this.bundleManager}getDownloadManager(){return this.ensureInitialized(),this.downloadManager}getVersionManager(){return this.ensureInitialized(),this.versionManager}getConfigManager(){return this.configManager}getLogger(){return this.logger}getSecurityValidator(){return this.securityValidator}getAppUpdateManager(){return this.ensureInitialized(),this.appUpdateManager}getEventEmitter(){return this.eventEmitter}getBackgroundScheduler(){return this.ensureInitialized(),this.backgroundScheduler}async reset(){this.logger.info("Resetting plugin state"),this.bundleManager&&await this.bundleManager.clearAllBundles(),this.versionManager&&await this.versionManager.clearVersionCache(),this.downloadManager&&this.downloadManager.cancelAllDownloads(),this.bundleManager=null,this.downloadManager=null,this.versionManager=null,this.appUpdateManager=null,this.backgroundScheduler=null,this.initialized=!1,this.eventEmitter.removeAllListeners(),this.logger.info("Plugin reset complete")}async cleanup(){this.logger.info("Cleaning up plugin resources"),this.downloadManager&&this.downloadManager.cancelAllDownloads(),this.bundleManager&&await this.bundleManager.cleanExpiredBundles(),this.logger.info("Cleanup complete")}setupAppUpdateEventBridge(){this.appUpdateManager&&(this.appUpdateManager.addListener("appUpdateStateChanged",e=>{this.eventEmitter.emit("appUpdateStateChanged",e)}),this.appUpdateManager.addListener("appUpdateProgress",e=>{this.eventEmitter.emit("appUpdateProgress",e)}))}}const z={algorithm:"AES-GCM",keyLength:256,ivLength:12,tagLength:128};class x{static async calculateChecksum(e){const t=new TextEncoder,r="string"==typeof e?t.encode(e):e,a=await crypto.subtle.digest("SHA-256",r);return Array.from(new Uint8Array(a)).map(e=>e.toString(16).padStart(2,"0")).join("")}static async verifySignature(e,t,r){try{const a=this.base64ToArrayBuffer(t),i=this.base64ToArrayBuffer(r),n=await crypto.subtle.importKey("spki",i,{name:"RSA-PSS",hash:"SHA-256"},!1,["verify"]),s=new TextEncoder,o="string"==typeof e?s.encode(e):e;return await crypto.subtle.verify({name:"RSA-PSS",saltLength:32},n,a,o)}catch(e){return console.error("Signature verification failed:",e),!1}}static base64ToArrayBuffer(e){const t=atob(e),r=new Uint8Array(t.length);for(let e=0;e<t.length;e++)r[e]=t.charCodeAt(e);return r.buffer}static generateNonce(e=16){const t=new Uint8Array(e);return crypto.getRandomValues(t),Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}static isValidChecksum(e,t="SHA-256"){return e.length===("SHA-256"===t?64:128)&&/^[a-f0-9]+$/i.test(e)}static async deriveKey(e,t,r=256){const a=(new TextEncoder).encode(e),i=await crypto.subtle.importKey("raw",a,"PBKDF2",!1,["deriveKey"]);return crypto.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:1e5,hash:"SHA-256"},i,{name:"AES-GCM",length:r},!1,["encrypt","decrypt"])}static async importKey(e,t=256){const r="string"==typeof e?this.base64ToArrayBuffer(e):e;return crypto.subtle.importKey("raw",r,{name:"AES-GCM",length:t},!1,["encrypt","decrypt"])}static async encrypt(e,t,r={}){const a=Object.assign(Object.assign({},z),r),i=crypto.getRandomValues(new Uint8Array(a.ivLength));let n;n=e instanceof Uint8Array?e:new Uint8Array(e);const s=await crypto.subtle.encrypt({name:"AES-GCM",iv:i,tagLength:a.tagLength},t,n),o=new Uint8Array(i),l=new ArrayBuffer(o.length);return new Uint8Array(l).set(o),{iv:this.arrayBufferToBase64(l),data:this.arrayBufferToBase64(s),algorithm:`AES-${a.keyLength}-GCM`}}static async decrypt(e,t,r={}){const a=Object.assign(Object.assign({},z),r),i=new Uint8Array(this.base64ToArrayBuffer(e.iv)),n=this.base64ToArrayBuffer(e.data);return crypto.subtle.decrypt({name:"AES-GCM",iv:i,tagLength:a.tagLength},t,n)}static async encryptBundle(e,t,r){const a=r?new Uint8Array(r):crypto.getRandomValues(new Uint8Array(16)),i=a.buffer.slice(a.byteOffset,a.byteOffset+a.byteLength),n=await this.deriveKey(t,i);return{encrypted:await this.encrypt(e,n),salt:this.arrayBufferToBase64(i)}}static async decryptBundle(e,t,r){const a=this.base64ToArrayBuffer(r),i=await this.deriveKey(t,a);return this.decrypt(e,i)}static isEncryptedBundle(e){return"object"==typeof e&&null!==e&&"iv"in e&&"data"in e&&"algorithm"in e&&"string"==typeof e.iv&&"string"==typeof e.data&&"string"==typeof e.algorithm}static generateEncryptionKey(e=256){const t=crypto.getRandomValues(new Uint8Array(e/8));return this.arrayBufferToBase64(t.buffer)}static generateSalt(e=16){const t=crypto.getRandomValues(new Uint8Array(e));return this.arrayBufferToBase64(t.buffer)}static arrayBufferToBase64(e){const t=new Uint8Array(e);let r="";for(let e=0;e<t.length;e++)r+=String.fromCharCode(t[e]);return btoa(r)}}class G{constructor(){this.initialized=!1,this.windowEventListeners=new Map,this.pluginManager=P.getInstance()}async initialize(e){await this.pluginManager.initialize(e),this.initialized=!0,this.setupWindowEventBridge()}isInitialized(){return this.initialized&&this.pluginManager.isInitialized()}async reset(){await this.pluginManager.reset()}async cleanup(){await this.pluginManager.cleanup()}async configure(e){var t;let r;r="config"in e&&"object"==typeof e.config?e.config:{baseUrl:null===(t=e.liveUpdate)||void 0===t?void 0:t.serverUrl},this.initialized?this.pluginManager.getConfigManager().configure(r):await this.initialize(r)}async getSecurityInfo(){return{enforceHttps:!0,certificatePinning:{enabled:!1,pins:[]},validateInputs:!0,secureStorage:!0}}async sync(e){const t=this.pluginManager.getBundleManager(),r=this.pluginManager.getConfigManager(),a=this.pluginManager.getDownloadManager(),i=this.pluginManager.getEventEmitter();try{const n=await t.getActiveBundle(),s=(null==n?void 0:n.version)||"1.0.0",o=r.get("baseUrl");if(!o)return{status:d.UP_TO_DATE,version:s};const l=(null==e?void 0:e.channel)||r.get("channel")||"production";let c=null;try{const e=await fetch(`${o}/updates/latest`,{method:"GET",headers:{"Content-Type":"application/json","X-Current-Version":s,"X-Channel":l}});if(e.ok){const t=await e.json(),r=t.version||t.latestVersion;r&&this.compareVersions(s,r)<0&&(c={version:r,url:t.url||t.downloadUrl,mandatory:t.mandatory,notes:t.notes||t.releaseNotes,checksum:t.checksum,signature:t.signature})}}catch(e){return{status:d.UP_TO_DATE,version:s}}if(!c)return{status:d.UP_TO_DATE,version:s};if("immediate"===(null==e?void 0:e.updateMode)||void 0===(null==e?void 0:e.updateMode)){i.emit("updateStateChanged",{status:u.DOWNLOADING,bundleId:c.version,version:c.version});const e=e=>{i.emit("downloadProgress",e)},r=await a.downloadWithRetry(c.url,c.version,e);let n=await r.arrayBuffer();n=await this.decryptBundleIfNeeded(n),await this.validateBundleData(n,c.checksum,c.signature);const s=new Blob([n],{type:r.type}),o=await a.saveBlob(c.version,s),l={bundleId:c.version,version:c.version,path:o,downloadTime:Date.now(),size:s.size,status:u.READY,checksum:c.checksum||"",verified:!0};return await t.saveBundleInfo(l),await t.setActiveBundle(c.version),i.emit("updateStateChanged",{status:u.ACTIVE,bundleId:c.version,version:c.version}),{status:d.UPDATE_INSTALLED,version:c.version,description:c.notes,mandatory:c.mandatory}}return{status:d.UPDATE_AVAILABLE,version:c.version,description:c.notes,mandatory:c.mandatory}}catch(e){return{status:d.ERROR,error:{code:g.UNKNOWN_ERROR,message:e instanceof Error?e.message:"Sync failed"}}}}async download(e){const t=this.pluginManager.getDownloadManager(),r=this.pluginManager.getBundleManager(),a=await t.downloadWithRetry(e.url,e.version),i=await t.saveBlob(e.version,a),n={bundleId:e.version,version:e.version,path:i,downloadTime:Date.now(),size:a.size,status:u.READY,checksum:e.checksum,signature:e.signature,verified:!1};return await r.saveBundleInfo(n),n}async set(e){const t=this.pluginManager.getBundleManager();await t.setActiveBundle(e.bundleId)}async reload(){"undefined"!=typeof window&&window.location.reload()}async current(){const t=this.pluginManager.getBundleManager(),r=await t.getActiveBundle();if(!r)throw new y(e.ErrorCode.FILE_NOT_FOUND,"No active bundle found");return r}async list(){return this.pluginManager.getBundleManager().getAllBundles()}async delete(e){const t=this.pluginManager.getBundleManager();if(e.bundleId)await t.deleteBundle(e.bundleId);else if(void 0!==e.keepVersions){const r=(await t.getAllBundles()).sort((e,t)=>t.downloadTime-e.downloadTime);for(let a=e.keepVersions;a<r.length;a++)await t.deleteBundle(r[a].bundleId)}}async notifyAppReady(){const e=this.pluginManager.getBundleManager(),t=await e.getActiveBundle();t&&(t.status=u.ACTIVE,await e.saveBundleInfo(t))}async getLatest(){return{available:!1}}async setChannel(e){const t=this.pluginManager.getConfigManager().get("preferences");t&&await t.set({key:"update_channel",value:e})}async setUpdateUrl(e){this.pluginManager.getConfigManager().configure({baseUrl:e})}async validateUpdate(e){const t=this.pluginManager.getSecurityValidator();try{const r=await t.validateChecksum(new ArrayBuffer(0),e.checksum);return{isValid:r,details:{checksumValid:r,signatureValid:!0,sizeValid:!0,versionValid:!0}}}catch(e){return{isValid:!1,error:e instanceof Error?e.message:"Validation failed"}}}async checkForUpdate(){const t=this.pluginManager.getBundleManager(),r=this.pluginManager.getConfigManager();try{const e=await t.getActiveBundle(),a=(null==e?void 0:e.version)||"1.0.0",i=r.get("baseUrl");if(!i)return{available:!1,currentVersion:a};try{const e=await fetch(`${i}/updates/latest`,{method:"GET",headers:{"Content-Type":"application/json","X-Current-Version":a}});if(!e.ok)return{available:!1,currentVersion:a};const t=await e.json(),r=t.version||t.latestVersion,n=this.compareVersions(a,r)<0;return{available:n,currentVersion:a,latestVersion:n?r:void 0,url:n?t.url:void 0,mandatory:t.mandatory,notes:t.notes||t.releaseNotes,size:t.size,checksum:t.checksum,signature:t.signature}}catch(e){return{available:!1,currentVersion:a}}}catch(t){throw new y(e.ErrorCode.NETWORK_ERROR,t instanceof Error?t.message:"Failed to check for updates")}}async downloadUpdate(t){const r=this.pluginManager.getDownloadManager(),a=this.pluginManager.getBundleManager(),i=this.pluginManager.getEventEmitter();let n;if(!((null==t?void 0:t.url)&&(null==t?void 0:t.version)||(n=await this.checkForUpdate(),n.available&&n.url)))throw new y(e.ErrorCode.UPDATE_NOT_AVAILABLE,"No update available to download");const s=(null==t?void 0:t.url)||(null==n?void 0:n.url)||"",o=(null==t?void 0:t.version)||(null==n?void 0:n.latestVersion)||"",l=(null==t?void 0:t.checksum)||(null==n?void 0:n.checksum)||"",c=(null==t?void 0:t.signature)||(null==n?void 0:n.signature)||"";if(!s||!o)throw new y(e.ErrorCode.INVALID_CONFIG,"URL and version are required for download");const d=await r.downloadWithRetry(s,o,e=>{i.emit("downloadProgress",e),(null==t?void 0:t.onProgress)&&t.onProgress(e)});let h=await d.arrayBuffer();h=await this.decryptBundleIfNeeded(h),await this.validateBundleData(h,l,c||void 0);const g=new Blob([h],{type:d.type}),p={bundleId:o,version:o,path:await r.saveBlob(o,g),downloadTime:Date.now(),size:g.size,status:u.READY,checksum:l,verified:!0};return await a.saveBundleInfo(p),p}async applyUpdate(t){const r=this.pluginManager.getBundleManager();let a=t;if(!a){const t=(await r.getAllBundles()).filter(e=>e.status===u.READY).sort((e,t)=>t.downloadTime-e.downloadTime);if(0===t.length)throw new y(e.ErrorCode.FILE_NOT_FOUND,"No ready bundle found to apply");a=t[0].bundleId}const i=await r.getBundle(a);if(!i)throw new y(e.ErrorCode.FILE_NOT_FOUND,`Bundle ${a} not found`);await this.set(i),await this.reload()}async cancelDownload(e){this.pluginManager.getDownloadManager().cancelDownload(e)}async cancelAllDownloads(){this.pluginManager.getDownloadManager().cancelAllDownloads()}async isDownloading(e){return this.pluginManager.getDownloadManager().isDownloading(e)}async getActiveDownloadCount(){return this.pluginManager.getDownloadManager().getActiveDownloadCount()}compareVersions(e,t){const r=e.split(".").map(Number),a=t.split(".").map(Number);for(let e=0;e<Math.max(r.length,a.length);e++){const t=r[e]||0,i=a[e]||0;if(t<i)return-1;if(t>i)return 1}return 0}async decryptBundleIfNeeded(t){const r=this.pluginManager.getConfigManager(),a=r.get("enableEncryption"),i=r.get("encryptionKey"),n=r.get("encryptionSalt");if(!a)return t;if(!i)throw new y(e.ErrorCode.INVALID_CONFIG,"Encryption is enabled but encryptionKey is not configured");if(!n)throw new y(e.ErrorCode.INVALID_CONFIG,"Encryption is enabled but encryptionSalt is not configured");try{const r=(new TextDecoder).decode(t),a=JSON.parse(r);if(!x.isEncryptedBundle(a))throw new y(e.ErrorCode.VALIDATION_ERROR,"Encryption is enabled but bundle is not encrypted");return await x.decryptBundle(a,i,n)}catch(t){if(t instanceof y)throw t;throw new y(e.ErrorCode.VALIDATION_ERROR,`Failed to decrypt bundle: ${t instanceof Error?t.message:"Unknown error"}`)}}async validateBundleData(t,r,a){const i=this.pluginManager.getSecurityValidator(),n=this.pluginManager.getConfigManager();if(r&&!await i.verifyChecksum(t,r))throw new y(e.ErrorCode.CHECKSUM_MISMATCH,"Bundle checksum verification failed");const s=n.get("requireSignature"),o=n.get("publicKey");if(s){if(!o)throw new y(e.ErrorCode.INVALID_CONFIG,"Signature verification required but publicKey is not configured");if(!a)throw new y(e.ErrorCode.SIGNATURE_INVALID,"Signature verification required but no signature provided");if(!await i.verifySignature(t,a))throw new y(e.ErrorCode.SIGNATURE_INVALID,"Bundle signature verification failed")}return!0}async getAppUpdateInfo(){return this.pluginManager.getAppUpdateManager().getAppUpdateInfo()}async performImmediateUpdate(){return this.pluginManager.getAppUpdateManager().performImmediateUpdate()}async startFlexibleUpdate(){return this.pluginManager.getAppUpdateManager().startFlexibleUpdate()}async completeFlexibleUpdate(){return this.pluginManager.getAppUpdateManager().completeFlexibleUpdate()}async openAppStore(e){return this.pluginManager.getAppUpdateManager().openAppStore(e)}async requestReview(){return{displayed:!1,error:"Reviews are not supported on web"}}async canRequestReview(){return{canRequest:!1,reason:"Reviews are not supported on web"}}async enableBackgroundUpdates(e){this.pluginManager.getBackgroundScheduler().configure(e);const t=this.pluginManager.getConfigManager().get("preferences");t&&await t.set({key:"background_update_config",value:JSON.stringify(e)})}async disableBackgroundUpdates(){this.pluginManager.getBackgroundScheduler().configure({enabled:!1,checkInterval:0,updateTypes:[]});const e=this.pluginManager.getConfigManager().get("preferences");e&&await e.remove({key:"background_update_config"})}async getBackgroundUpdateStatus(){return this.pluginManager.getBackgroundScheduler().getStatus()}async scheduleBackgroundCheck(t){throw new y(e.ErrorCode.PLATFORM_NOT_SUPPORTED,"Background updates are not supported on web")}async triggerBackgroundCheck(){return this.pluginManager.getBackgroundScheduler().performCheck()}async setNotificationPreferences(e){const t=this.pluginManager.getConfigManager().get("preferences");t&&await t.set({key:"notification_preferences",value:JSON.stringify(e)})}async getNotificationPermissions(){return{granted:!1,canRequest:!1}}async requestNotificationPermissions(){return!1}async addListener(e,t){const r=this.pluginManager.getEventEmitter().addListener(e,t);return{remove:async()=>{r()}}}async removeAllListeners(){this.pluginManager.getEventEmitter().removeAllListeners()}setupWindowEventBridge(){const e=this.pluginManager.getEventEmitter();["appUpdateAvailable","appUpdateProgress","appUpdateReady","appUpdateFailed","appUpdateNotificationClicked","appUpdateInstallClicked"].forEach(t=>{const r=r=>{e.emit(t,r.detail)};window.addEventListener(t,r),this.windowEventListeners.set(t,r)})}}const $=t.registerPlugin("NativeUpdate",{web:()=>new G}),H=t.registerPlugin("App",{web:()=>Promise.resolve().then(function(){return W}).then(e=>new e.AppWeb)});var K=Object.freeze({__proto__:null,App:H}),W=Object.freeze({__proto__:null,AppWeb:class extends t.WebPlugin{constructor(){super(),this.handleVisibilityChange=()=>{const e={isActive:!0!==document.hidden};this.notifyListeners("appStateChange",e),document.hidden?this.notifyListeners("pause",null):this.notifyListeners("resume",null)},document.addEventListener("visibilitychange",this.handleVisibilityChange,!1)}exitApp(){throw this.unimplemented("Not implemented on web.")}async getInfo(){throw this.unimplemented("Not implemented on web.")}async getLaunchUrl(){return{url:""}}async getState(){return{isActive:!0!==document.hidden}}async minimizeApp(){throw this.unimplemented("Not implemented on web.")}async toggleBackButtonHandler(){throw this.unimplemented("Not implemented on web.")}}});e.BundleManager=b,e.CacheManager=class{constructor(){this.filesystem=null,this.memoryCache=new Map,this.CACHE_DIR="cache",this.logger=w.getInstance(),this.configManager=m.getInstance()}async initialize(){if(this.filesystem=this.configManager.get("filesystem"),!this.filesystem)throw new Error("Filesystem not configured");try{await this.filesystem.mkdir({path:this.CACHE_DIR,directory:r.Directory.Data,recursive:!0})}catch(e){this.logger.debug("Cache directory may already exist",e)}await this.cleanExpiredCache()}async set(e,t,r){const a=Date.now()+(r||this.configManager.get("cacheExpiration")),i={data:t,timestamp:Date.now(),expiry:a};this.memoryCache.set(e,i),this.shouldPersist(t)&&await this.persistToFile(e,i),this.logger.debug("Cache entry set",{key:e,expiry:new Date(a)})}async get(e){const t=this.memoryCache.get(e);if(t){if(Date.now()<t.expiry)return t.data;this.memoryCache.delete(e)}const r=await this.loadFromFile(e);if(r){if(Date.now()<r.expiry)return this.memoryCache.set(e,r),r.data;await this.removeFile(e)}return null}async has(e){return null!==await this.get(e)}async remove(e){this.memoryCache.delete(e),await this.removeFile(e),this.logger.debug("Cache entry removed",{key:e})}async clear(){this.memoryCache.clear();try{await this.filesystem.rmdir({path:this.CACHE_DIR,directory:r.Directory.Data,recursive:!0}),await this.filesystem.mkdir({path:this.CACHE_DIR,directory:r.Directory.Data,recursive:!0})}catch(e){this.logger.warn("Failed to clear cache directory",e)}this.logger.info("Cache cleared")}async cleanExpiredCache(){const e=Date.now();let t=0;for(const[r,a]of this.memoryCache)e>=a.expiry&&(this.memoryCache.delete(r),t++);try{const a=await this.filesystem.readdir({path:this.CACHE_DIR,directory:r.Directory.Data});for(const r of a.files){const a=r.name.replace(".json",""),i=await this.loadFromFile(a);(!i||e>=i.expiry)&&(await this.removeFile(a),t++)}}catch(e){this.logger.debug("Failed to clean filesystem cache",e)}t>0&&this.logger.info("Cleaned expired cache entries",{count:t})}async getStats(){let e=0,t=0;try{const a=await this.filesystem.readdir({path:this.CACHE_DIR,directory:r.Directory.Data});e=a.files.length;for(const e of a.files)t+=(await this.filesystem.stat({path:`${this.CACHE_DIR}/${e.name}`,directory:r.Directory.Data})).size||0}catch(e){this.logger.debug("Failed to get cache stats",e)}return{memoryEntries:this.memoryCache.size,fileEntries:e,totalSize:t}}async cacheBundleMetadata(e){const t=`bundle_meta_${e.bundleId}`;await this.set(t,e,864e5)}async getCachedBundleMetadata(e){return this.get(`bundle_meta_${e}`)}shouldPersist(e){return"object"==typeof e||"string"==typeof e&&e.length>1024}async persistToFile(e,t){if(this.filesystem)try{const a=`${this.CACHE_DIR}/${e}.json`,i=JSON.stringify(t);await this.filesystem.writeFile({path:a,data:i,directory:r.Directory.Data,encoding:r.Encoding.UTF8})}catch(t){this.logger.warn("Failed to persist cache to file",{key:e,error:t})}}async loadFromFile(e){if(!this.filesystem)return null;try{const t=`${this.CACHE_DIR}/${e}.json`,a=await this.filesystem.readFile({path:t,directory:r.Directory.Data,encoding:r.Encoding.UTF8});return JSON.parse(a.data)}catch(e){return null}}async removeFile(e){if(this.filesystem)try{const t=`${this.CACHE_DIR}/${e}.json`;await this.filesystem.deleteFile({path:t,directory:r.Directory.Data})}catch(t){this.logger.debug("Failed to remove cache file",{key:e,error:t})}}},e.ConfigManager=m,e.ConfigurationError=class extends y{constructor(t,r){super(e.ErrorCode.INVALID_CONFIG,t,r),this.name="ConfigurationError"}},e.CryptoUtils=x,e.DownloadError=E,e.DownloadManager=N,e.Logger=w,e.NativeUpdate=$,e.NativeUpdateError=y,e.PluginManager=P,e.SecurityValidator=D,e.StorageError=v,e.UpdateErrorClass=I,e.UpdateManager=class{constructor(){this.filesystem=null,this.updateInProgress=!1,this.currentState=null,this.pluginManager=P.getInstance(),this.securityValidator=D.getInstance()}async initialize(){if(this.filesystem=this.pluginManager.getConfigManager().get("filesystem"),!this.filesystem)throw new I(e.ErrorCode.MISSING_DEPENDENCY,"Filesystem not configured")}async applyUpdate(t,r){if(this.updateInProgress)throw new I(e.ErrorCode.UPDATE_FAILED,"Another update is already in progress");const a=this.pluginManager.getLogger(),i=this.pluginManager.getBundleManager();try{this.updateInProgress=!0,a.info("Starting bundle update",{bundleId:t});const n=await i.getBundle(t);if(!n)throw new I(e.ErrorCode.FILE_NOT_FOUND,`Bundle ${t} not found`);if("READY"!==n.status&&"ACTIVE"!==n.status)throw new I(e.ErrorCode.BUNDLE_NOT_READY,`Bundle ${t} is not ready for installation`);const s=await i.getActiveBundle();this.currentState={currentBundle:s,newBundle:n,backupPath:null,startTime:Date.now()},await this.validateUpdate(s,n,r),s&&"default"!==s.bundleId&&(this.currentState.backupPath=await this.createBackup(s)),await this.performUpdate(n),await this.verifyUpdate(n),await i.setActiveBundle(t),(null==r?void 0:r.cleanupOldBundles)&&await i.cleanupOldBundles(r.keepBundleCount||3),a.info("Bundle update completed successfully",{bundleId:t,version:n.version,duration:Date.now()-this.currentState.startTime}),this.currentState=null}catch(e){throw a.error("Bundle update failed",e),this.currentState&&await this.rollback(),e}finally{this.updateInProgress=!1}}async validateUpdate(t,r,a){const i=this.pluginManager.getLogger(),n=this.pluginManager.getVersionManager();if(t&&!(null==a?void 0:a.allowDowngrade)&&n.shouldBlockDowngrade(t.version,r.version))throw new A(e.ErrorCode.VERSION_DOWNGRADE,`Cannot downgrade from ${t.version} to ${r.version}`);if(!r.verified){i.warn("Bundle not verified, verifying now",{bundleId:r.bundleId});const t=this.pluginManager.getDownloadManager(),a=await t.loadBlob(r.bundleId);if(!a)throw new I(e.ErrorCode.FILE_NOT_FOUND,"Bundle data not found");const n=await a.arrayBuffer();if(!await this.securityValidator.verifyChecksum(n,r.checksum))throw new A(e.ErrorCode.CHECKSUM_MISMATCH,"Bundle checksum verification failed");if(r.signature&&!await this.securityValidator.verifySignature(n,r.signature))throw new A(e.ErrorCode.SIGNATURE_INVALID,"Bundle signature verification failed");await this.pluginManager.getBundleManager().markBundleAsVerified(r.bundleId)}i.debug("Bundle validation passed",{bundleId:r.bundleId})}async createBackup(t){const a=`backups/${t.bundleId}_${Date.now()}`,i=this.pluginManager.getLogger();try{return await this.filesystem.mkdir({path:a,directory:r.Directory.Data,recursive:!0}),await this.filesystem.copy({from:t.path,to:a,directory:r.Directory.Data}),i.info("Backup created",{bundleId:t.bundleId,backupPath:a}),a}catch(t){throw i.error("Failed to create backup",t),new I(e.ErrorCode.UPDATE_FAILED,"Failed to create backup",void 0,t)}}async performUpdate(t){const a=this.pluginManager.getLogger();try{const e=`active/${t.bundleId}`;await this.filesystem.mkdir({path:e,directory:r.Directory.Data,recursive:!0}),await this.filesystem.copy({from:t.path,to:e,directory:r.Directory.Data}),t.path=e,a.debug("Bundle files installed",{bundleId:t.bundleId,targetPath:e})}catch(t){throw new I(e.ErrorCode.UPDATE_FAILED,"Failed to install bundle files",void 0,t)}}async verifyUpdate(t){try{const e=`${t.path}/index.html`;await this.filesystem.stat({path:e,directory:r.Directory.Data})}catch(t){throw new I(e.ErrorCode.UPDATE_FAILED,"Bundle verification failed after installation",void 0,t)}}async rollback(){var t;if(!this.currentState)throw new I(e.ErrorCode.ROLLBACK_FAILED,"No update state to rollback");const a=this.pluginManager.getLogger();a.warn("Starting rollback",{from:this.currentState.newBundle.bundleId,to:(null===(t=this.currentState.currentBundle)||void 0===t?void 0:t.bundleId)||"default"});try{const e=this.pluginManager.getBundleManager();if(this.currentState.backupPath&&this.currentState.currentBundle){const t=`active/${this.currentState.currentBundle.bundleId}`;await this.filesystem.copy({from:this.currentState.backupPath,to:t,directory:r.Directory.Data}),this.currentState.currentBundle.path=t,await e.saveBundleInfo(this.currentState.currentBundle)}this.currentState.currentBundle?await e.setActiveBundle(this.currentState.currentBundle.bundleId):await e.clearActiveBundle(),a.info("Rollback completed successfully")}catch(t){throw a.error("Rollback failed",t),new I(e.ErrorCode.ROLLBACK_FAILED,"Failed to rollback update",void 0,t)}finally{if(this.currentState.backupPath)try{await this.filesystem.rmdir({path:this.currentState.backupPath,directory:r.Directory.Data,recursive:!0})}catch(e){a.warn("Failed to clean up backup",e)}}}getUpdateProgress(){var e,t;return{inProgress:this.updateInProgress,bundleId:null===(e=this.currentState)||void 0===e?void 0:e.newBundle.bundleId,startTime:null===(t=this.currentState)||void 0===t?void 0:t.startTime}}async cancelUpdate(){this.updateInProgress&&this.currentState&&(this.pluginManager.getLogger().warn("Cancelling update",{bundleId:this.currentState.newBundle.bundleId}),await this.rollback(),this.updateInProgress=!1,this.currentState=null)}},e.ValidationError=A,e.VersionManager=S}({},capacitorExports,capacitorFilesystem,capacitorPreferences);
|
|
3
3
|
//# sourceMappingURL=plugin.js.map
|
package/docs/APP_REVIEW_GUIDE.md
CHANGED
|
@@ -763,6 +763,6 @@ Key takeaways for implementing app reviews:
|
|
|
763
763
|
|
|
764
764
|
## Next Steps
|
|
765
765
|
|
|
766
|
-
- Review the [Quick Start Guide](
|
|
767
|
-
- Check the [API Reference](
|
|
766
|
+
- Review the [Quick Start Guide](https://nativeupdate.aoneahsan.com/docs/QUICK_START)
|
|
767
|
+
- Check the [API Reference](https://nativeupdate.aoneahsan.com/docs/api/app-review-api)
|
|
768
768
|
- See example implementation in the `/example` directory
|
package/docs/BUNDLE_SIGNING.md
CHANGED
|
@@ -30,7 +30,7 @@ This creates:
|
|
|
30
30
|
- `public-{timestamp}.pem` - Include in your app
|
|
31
31
|
- Proper file permissions (600) are set automatically
|
|
32
32
|
|
|
33
|
-
For detailed key management instructions, see the [Key Management Guide](
|
|
33
|
+
For detailed key management instructions, see the [Key Management Guide](https://nativeupdate.aoneahsan.com/docs/guides/key-management).
|
|
34
34
|
|
|
35
35
|
### 2. Secure Private Key
|
|
36
36
|
|
|
@@ -669,7 +669,7 @@ class UpdateHealthCheck {
|
|
|
669
669
|
|
|
670
670
|
## Next Steps
|
|
671
671
|
|
|
672
|
-
- Read the [Native App Updates Guide](
|
|
673
|
-
- Learn about [App Review Integration](
|
|
674
|
-
- Check out [Security Best Practices](
|
|
675
|
-
- See [Bundle Signing Documentation](
|
|
672
|
+
- Read the [Native App Updates Guide](https://nativeupdate.aoneahsan.com/docs/NATIVE_UPDATES_GUIDE)
|
|
673
|
+
- Learn about [App Review Integration](https://nativeupdate.aoneahsan.com/docs/APP_REVIEW_GUIDE)
|
|
674
|
+
- Check out [Security Best Practices](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices)
|
|
675
|
+
- See [Bundle Signing Documentation](https://nativeupdate.aoneahsan.com/docs/BUNDLE_SIGNING)
|
package/docs/MIGRATION.md
CHANGED
|
@@ -188,5 +188,5 @@ await NativeUpdate.configure(config);
|
|
|
188
188
|
### Getting Help
|
|
189
189
|
|
|
190
190
|
- Check our example app in the `/example` directory for implementation patterns
|
|
191
|
-
- Review the [API documentation](
|
|
191
|
+
- Review the [API documentation](https://nativeupdate.aoneahsan.com/docs/api/live-update-api)
|
|
192
192
|
- File issues on GitHub for migration problems
|
|
@@ -689,7 +689,7 @@ if (!config.appStoreId) {
|
|
|
689
689
|
|
|
690
690
|
## Next Steps
|
|
691
691
|
|
|
692
|
-
- Learn about [Live Updates (OTA)](
|
|
693
|
-
- Implement [App Review Features](
|
|
694
|
-
- Review [Security Guidelines](
|
|
695
|
-
- Check [API Reference](
|
|
692
|
+
- Learn about [Live Updates (OTA)](https://nativeupdate.aoneahsan.com/docs/LIVE_UPDATES_GUIDE)
|
|
693
|
+
- Implement [App Review Features](https://nativeupdate.aoneahsan.com/docs/APP_REVIEW_GUIDE)
|
|
694
|
+
- Review [Security Guidelines](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices)
|
|
695
|
+
- Check [API Reference](https://nativeupdate.aoneahsan.com/docs/api/app-update-api)
|
package/docs/QUICK_START.md
CHANGED
|
@@ -556,11 +556,11 @@ Now that you have the basics working:
|
|
|
556
556
|
- Rollback capabilities
|
|
557
557
|
|
|
558
558
|
3. **Deep Dive**:
|
|
559
|
-
- [Live Updates Guide](
|
|
560
|
-
- [Native Updates Guide](
|
|
561
|
-
- [App Review Guide](
|
|
562
|
-
- [Security Guide](
|
|
563
|
-
- [API Reference](
|
|
559
|
+
- [Live Updates Guide](https://nativeupdate.aoneahsan.com/docs/LIVE_UPDATES_GUIDE) - Complete OTA implementation
|
|
560
|
+
- [Native Updates Guide](https://nativeupdate.aoneahsan.com/docs/NATIVE_UPDATES_GUIDE) - Platform-specific details
|
|
561
|
+
- [App Review Guide](https://nativeupdate.aoneahsan.com/docs/APP_REVIEW_GUIDE) - Maximize review rates
|
|
562
|
+
- [Security Guide](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices) - Best practices
|
|
563
|
+
- [API Reference](https://nativeupdate.aoneahsan.com/docs/api/live-update-api) - All methods and options
|
|
564
564
|
|
|
565
565
|
## Troubleshooting
|
|
566
566
|
|
package/docs/README.md
CHANGED
|
@@ -21,47 +21,47 @@ Created by **Ahsan Mahmood** and open-sourced for the developer community, this
|
|
|
21
21
|
|
|
22
22
|
### Getting Started
|
|
23
23
|
|
|
24
|
-
- [**Installation**](
|
|
25
|
-
- [**Quick Start**](
|
|
26
|
-
- [**Configuration**](
|
|
27
|
-
- [**End-to-End Workflow**](
|
|
24
|
+
- [**Installation**](https://nativeupdate.aoneahsan.com/docs/getting-started/installation) - How to install and set up the plugin
|
|
25
|
+
- [**Quick Start**](https://nativeupdate.aoneahsan.com/docs/getting-started/quick-start) - Get up and running in minutes
|
|
26
|
+
- [**Configuration**](https://nativeupdate.aoneahsan.com/docs/getting-started/configuration) - Detailed configuration options
|
|
27
|
+
- [**End-to-End Workflow**](https://nativeupdate.aoneahsan.com/docs/guides/end-to-end-workflow) - Complete setup to production guide
|
|
28
28
|
|
|
29
29
|
### Dashboard & Management
|
|
30
30
|
|
|
31
|
-
- [**Dashboard Guide**](
|
|
32
|
-
- [**Channel Management**](
|
|
33
|
-
- [**Admin Panel**](
|
|
31
|
+
- [**Dashboard Guide**](https://nativeupdate.aoneahsan.com/docs/guides/dashboard-guide) - Complete dashboard user guide
|
|
32
|
+
- [**Channel Management**](https://nativeupdate.aoneahsan.com/docs/guides/channel-management) - Production/staging/development channels
|
|
33
|
+
- [**Admin Panel**](https://nativeupdate.aoneahsan.com/docs/guides/admin-panel) - Super-admin documentation
|
|
34
34
|
|
|
35
35
|
### Features
|
|
36
36
|
|
|
37
|
-
- [**Live Updates**](
|
|
38
|
-
- [**App Updates**](
|
|
39
|
-
- [**App Reviews**](
|
|
37
|
+
- [**Live Updates**](https://nativeupdate.aoneahsan.com/docs/features/live-updates) - Deploy JavaScript/HTML/CSS updates instantly
|
|
38
|
+
- [**App Updates**](https://nativeupdate.aoneahsan.com/docs/features/app-updates) - Native app store update management
|
|
39
|
+
- [**App Reviews**](https://nativeupdate.aoneahsan.com/docs/features/app-reviews) - In-app review requests
|
|
40
40
|
|
|
41
41
|
### Guides
|
|
42
42
|
|
|
43
|
-
- [**Security Best Practices**](
|
|
44
|
-
- [**Key Management**](
|
|
45
|
-
- [**Deployment Guide**](
|
|
46
|
-
- [**Testing Guide**](
|
|
47
|
-
- [**Troubleshooting**](
|
|
48
|
-
- [**Migration from CodePush**](
|
|
43
|
+
- [**Security Best Practices**](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices) - Implement secure updates
|
|
44
|
+
- [**Key Management**](https://nativeupdate.aoneahsan.com/docs/guides/key-management) - Generate and manage signing keys
|
|
45
|
+
- [**Deployment Guide**](https://nativeupdate.aoneahsan.com/docs/guides/deployment-guide) - Deploy to production
|
|
46
|
+
- [**Testing Guide**](https://nativeupdate.aoneahsan.com/docs/guides/testing-guide) - Testing your update implementation
|
|
47
|
+
- [**Troubleshooting**](https://nativeupdate.aoneahsan.com/docs/guides/troubleshooting) - Common issues and solutions
|
|
48
|
+
- [**Migration from CodePush**](https://nativeupdate.aoneahsan.com/docs/guides/migration-from-codepush) - Migrate from CodePush
|
|
49
49
|
|
|
50
50
|
### API Reference
|
|
51
51
|
|
|
52
|
-
- [**Live Update API**](
|
|
53
|
-
- [**App Update API**](
|
|
54
|
-
- [**App Review API**](
|
|
55
|
-
- [**Events API**](
|
|
52
|
+
- [**Live Update API**](https://nativeupdate.aoneahsan.com/docs/api/live-update-api) - Complete API for OTA updates
|
|
53
|
+
- [**App Update API**](https://nativeupdate.aoneahsan.com/docs/api/app-update-api) - Native app update methods
|
|
54
|
+
- [**App Review API**](https://nativeupdate.aoneahsan.com/docs/api/app-review-api) - Review request methods
|
|
55
|
+
- [**Events API**](https://nativeupdate.aoneahsan.com/docs/api/events-api) - Event listeners and handlers
|
|
56
56
|
|
|
57
57
|
### Examples
|
|
58
58
|
|
|
59
|
-
- [**Basic Usage**](
|
|
60
|
-
- [**Advanced Scenarios**](
|
|
59
|
+
- [**Basic Usage**](https://nativeupdate.aoneahsan.com/docs/examples/basic-usage) - Simple implementation examples
|
|
60
|
+
- [**Advanced Scenarios**](https://nativeupdate.aoneahsan.com/docs/examples/advanced-scenarios) - Complex use cases and framework integrations
|
|
61
61
|
|
|
62
62
|
### Production
|
|
63
63
|
|
|
64
|
-
- [**Production Readiness**](
|
|
64
|
+
- [**Production Readiness**](https://nativeupdate.aoneahsan.com/docs/production-readiness) - Checklist and best practices for production deployment
|
|
65
65
|
|
|
66
66
|
## 🚀 Quick Links
|
|
67
67
|
|
|
@@ -73,7 +73,7 @@ await NativeUpdate.openAppStore({
|
|
|
73
73
|
|
|
74
74
|
## Events
|
|
75
75
|
|
|
76
|
-
App update events are available for monitoring the native app update process. See the [Events API Reference](
|
|
76
|
+
App update events are available for monitoring the native app update process. See the [Events API Reference](https://nativeupdate.aoneahsan.com/docs/api/events-api#app-update-events) for complete documentation of the following events:
|
|
77
77
|
|
|
78
78
|
- `appUpdateStateChanged` - Fired when update state changes
|
|
79
79
|
- `appUpdateProgress` - Monitor download progress for flexible updates
|
package/docs/cli-reference.md
CHANGED
|
@@ -104,7 +104,7 @@ npx native-update keys generate --output ./my-keys --type rsa --size 4096
|
|
|
104
104
|
- NEVER commit private keys to version control
|
|
105
105
|
- Store private keys in secure locations with restricted access
|
|
106
106
|
- Use environment variables or key management services in production
|
|
107
|
-
- See [Key Management Guide](
|
|
107
|
+
- See [Key Management Guide](https://nativeupdate.aoneahsan.com/docs/guides/key-management) for best practices
|
|
108
108
|
|
|
109
109
|
### Backend Templates
|
|
110
110
|
|
|
@@ -389,6 +389,6 @@ class UpdateMetrics {
|
|
|
389
389
|
|
|
390
390
|
## Next Steps
|
|
391
391
|
|
|
392
|
-
- Review [Basic Usage](
|
|
393
|
-
- See the [API Reference](
|
|
394
|
-
- Check [Basic Usage](
|
|
392
|
+
- Review [Basic Usage](https://nativeupdate.aoneahsan.com/docs/examples/basic-usage) for framework-specific implementations
|
|
393
|
+
- See the [API Reference](https://nativeupdate.aoneahsan.com/docs/api/live-update-api) for detailed method documentation
|
|
394
|
+
- Check [Basic Usage](https://nativeupdate.aoneahsan.com/docs/examples/basic-usage) for simpler examples
|
|
@@ -200,7 +200,7 @@ function cleanup() {
|
|
|
200
200
|
|
|
201
201
|
## Next Steps
|
|
202
202
|
|
|
203
|
-
- See [Advanced Scenarios](
|
|
204
|
-
- Read the [Live Update API Reference](
|
|
205
|
-
- Read the [App Update API Reference](
|
|
206
|
-
- Read the [App Review API Reference](
|
|
203
|
+
- See [Advanced Scenarios](https://nativeupdate.aoneahsan.com/docs/examples/advanced-scenarios) for more complex use cases
|
|
204
|
+
- Read the [Live Update API Reference](https://nativeupdate.aoneahsan.com/docs/api/live-update-api) for complete live update methods
|
|
205
|
+
- Read the [App Update API Reference](https://nativeupdate.aoneahsan.com/docs/api/app-update-api) for native update methods
|
|
206
|
+
- Read the [App Review API Reference](https://nativeupdate.aoneahsan.com/docs/api/app-review-api) for review request methods
|
|
@@ -965,10 +965,10 @@ async function monitorReviewSuccess() {
|
|
|
965
965
|
|
|
966
966
|
## Next Steps
|
|
967
967
|
|
|
968
|
-
- Implement [Security Best Practices](
|
|
969
|
-
- Review the [API Reference](
|
|
970
|
-
- Configure [Live Updates](
|
|
971
|
-
- Review [API Reference](
|
|
968
|
+
- Implement [Security Best Practices](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices)
|
|
969
|
+
- Review the [API Reference](https://nativeupdate.aoneahsan.com/docs/api/app-review-api)
|
|
970
|
+
- Configure [Live Updates](https://nativeupdate.aoneahsan.com/docs/features/live-updates)
|
|
971
|
+
- Review [API Reference](https://nativeupdate.aoneahsan.com/docs/api/app-review-api)
|
|
972
972
|
|
|
973
973
|
---
|
|
974
974
|
|
|
@@ -775,10 +775,10 @@ async function trackUpdateMetrics(event: string, data: any) {
|
|
|
775
775
|
|
|
776
776
|
## Next Steps
|
|
777
777
|
|
|
778
|
-
- Configure [App Reviews](
|
|
779
|
-
- Implement [Security Best Practices](
|
|
780
|
-
- Review [Testing Guide](
|
|
781
|
-
- Review [API Reference](
|
|
778
|
+
- Configure [App Reviews](https://nativeupdate.aoneahsan.com/docs/features/app-reviews) for user feedback
|
|
779
|
+
- Implement [Security Best Practices](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices)
|
|
780
|
+
- Review [Testing Guide](https://nativeupdate.aoneahsan.com/docs/guides/testing-guide)
|
|
781
|
+
- Review [API Reference](https://nativeupdate.aoneahsan.com/docs/api/app-update-api)
|
|
782
782
|
|
|
783
783
|
---
|
|
784
784
|
|
|
@@ -633,9 +633,9 @@ async function loadFeature(featureName: string) {
|
|
|
633
633
|
## Next Steps
|
|
634
634
|
|
|
635
635
|
- Set up your update server (see backend-template folder)
|
|
636
|
-
- Implement [Security Best Practices](
|
|
637
|
-
- Configure [App Updates](
|
|
638
|
-
- Explore [API Reference](
|
|
636
|
+
- Implement [Security Best Practices](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices)
|
|
637
|
+
- Configure [App Updates](https://nativeupdate.aoneahsan.com/docs/features/app-updates) for native changes
|
|
638
|
+
- Explore [API Reference](https://nativeupdate.aoneahsan.com/docs/api/live-update-api)
|
|
639
639
|
|
|
640
640
|
---
|
|
641
641
|
|
|
@@ -460,9 +460,9 @@ try {
|
|
|
460
460
|
|
|
461
461
|
## Next Steps
|
|
462
462
|
|
|
463
|
-
- Implement [Security Best Practices](
|
|
463
|
+
- Implement [Security Best Practices](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices)
|
|
464
464
|
- Set up your update server (see backend-template folder)
|
|
465
|
-
- Explore [Advanced Features](
|
|
465
|
+
- Explore [Advanced Features](https://nativeupdate.aoneahsan.com/docs/features/live-updates)
|
|
466
466
|
|
|
467
467
|
---
|
|
468
468
|
|
|
@@ -282,16 +282,16 @@ import type {
|
|
|
282
282
|
|
|
283
283
|
After successful installation:
|
|
284
284
|
|
|
285
|
-
1. Read the [Quick Start Guide](
|
|
286
|
-
2. Configure the plugin with your [update server settings](
|
|
287
|
-
3. Implement [security best practices](
|
|
285
|
+
1. Read the [Quick Start Guide](https://nativeupdate.aoneahsan.com/docs/getting-started/quick-start) for basic usage
|
|
286
|
+
2. Configure the plugin with your [update server settings](https://nativeupdate.aoneahsan.com/docs/getting-started/configuration)
|
|
287
|
+
3. Implement [security best practices](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices)
|
|
288
288
|
4. Set up your update server (see backend-template folder)
|
|
289
289
|
|
|
290
290
|
## Support
|
|
291
291
|
|
|
292
292
|
If you encounter any issues during installation:
|
|
293
293
|
|
|
294
|
-
- Check our [Testing Guide](
|
|
294
|
+
- Check our [Testing Guide](https://nativeupdate.aoneahsan.com/docs/guides/testing-guide)
|
|
295
295
|
- Visit [nativeupdate.aoneahsan.com/contact](https://nativeupdate.aoneahsan.com/contact) for support
|
|
296
296
|
- Email aoneahsan@gmail.com with detailed information about your setup
|
|
297
297
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
> - **Version Management**: Handle versioning, channels, and rollback mechanisms
|
|
11
11
|
> - **Analytics & Monitoring**: Track update success rates and handle failures
|
|
12
12
|
>
|
|
13
|
-
> **This is NOT a complete solution** - it's a foundation that requires substantial additional development. See our [Server Requirements](
|
|
13
|
+
> **This is NOT a complete solution** - it's a foundation that requires substantial additional development. See our [Server Requirements](https://nativeupdate.aoneahsan.com/docs/server-requirements) guide for detailed backend implementation requirements.
|
|
14
14
|
|
|
15
15
|
Get up and running with Capacitor Native Update in just a few minutes! This guide covers the most common use cases.
|
|
16
16
|
|
|
@@ -366,7 +366,7 @@ yarn dev
|
|
|
366
366
|
|
|
367
367
|
### Creating and Deploying Updates
|
|
368
368
|
|
|
369
|
-
1. **Generate signing keys** (see [Key Management Guide](
|
|
369
|
+
1. **Generate signing keys** (see [Key Management Guide](https://nativeupdate.aoneahsan.com/docs/guides/key-management) for details):
|
|
370
370
|
```bash
|
|
371
371
|
npx native-update keys generate --type rsa --size 4096
|
|
372
372
|
```
|
|
@@ -401,10 +401,10 @@ npx native-update monitor --server http://localhost:3000
|
|
|
401
401
|
Now that you have the basics working:
|
|
402
402
|
|
|
403
403
|
1. Deploy your backend to production
|
|
404
|
-
2. Implement [Security Best Practices](
|
|
405
|
-
3. Configure [Advanced Options](
|
|
406
|
-
4. Explore [API Reference](
|
|
407
|
-
5. See [CLI Reference](
|
|
404
|
+
2. Implement [Security Best Practices](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices)
|
|
405
|
+
3. Configure [Advanced Options](https://nativeupdate.aoneahsan.com/docs/getting-started/configuration)
|
|
406
|
+
4. Explore [API Reference](https://nativeupdate.aoneahsan.com/docs/api/live-update-api) for all available methods
|
|
407
|
+
5. See [CLI Reference](https://nativeupdate.aoneahsan.com/docs/cli-reference) for all available commands
|
|
408
408
|
|
|
409
409
|
## Quick Reference
|
|
410
410
|
|
|
@@ -178,6 +178,6 @@ SIGNING_PUBLIC_KEY_PATH=./keys/public.pem
|
|
|
178
178
|
|
|
179
179
|
## Related Documentation
|
|
180
180
|
|
|
181
|
-
- [Deployment Guide](
|
|
182
|
-
- [Security Best Practices](
|
|
183
|
-
- [Key Management](
|
|
181
|
+
- [Deployment Guide](https://nativeupdate.aoneahsan.com/docs/guides/deployment-guide) - Full deployment instructions
|
|
182
|
+
- [Security Best Practices](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices) - Security recommendations
|
|
183
|
+
- [Key Management](https://nativeupdate.aoneahsan.com/docs/guides/key-management) - Managing signing keys
|
|
@@ -263,6 +263,6 @@ Planned admin features:
|
|
|
263
263
|
|
|
264
264
|
## Related Documentation
|
|
265
265
|
|
|
266
|
-
- [Dashboard Guide](
|
|
267
|
-
- [Channel Management](
|
|
268
|
-
- [Security Best Practices](
|
|
266
|
+
- [Dashboard Guide](https://nativeupdate.aoneahsan.com/docs/guides/dashboard-guide) - Regular user dashboard
|
|
267
|
+
- [Channel Management](https://nativeupdate.aoneahsan.com/docs/guides/channel-management) - Managing update channels
|
|
268
|
+
- [Security Best Practices](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices) - Security guidelines
|
|
@@ -337,7 +337,7 @@ Users see release notes before updating. Make them:
|
|
|
337
337
|
|
|
338
338
|
## Related Documentation
|
|
339
339
|
|
|
340
|
-
- [Quick Start Guide](
|
|
341
|
-
- [Configuration Guide](
|
|
342
|
-
- [Deployment Guide](
|
|
343
|
-
- [Production Readiness](
|
|
340
|
+
- [Quick Start Guide](https://nativeupdate.aoneahsan.com/docs/getting-started/quick-start)
|
|
341
|
+
- [Configuration Guide](https://nativeupdate.aoneahsan.com/docs/getting-started/configuration)
|
|
342
|
+
- [Deployment Guide](https://nativeupdate.aoneahsan.com/docs/guides/deployment-guide)
|
|
343
|
+
- [Production Readiness](https://nativeupdate.aoneahsan.com/docs/production-readiness)
|
|
@@ -354,12 +354,12 @@ Use this URL in your app configuration.
|
|
|
354
354
|
## Support
|
|
355
355
|
|
|
356
356
|
- **Email**: aoneahsan@gmail.com
|
|
357
|
-
- **Documentation**: [docs folder](
|
|
357
|
+
- **Documentation**: [docs folder](https://nativeupdate.aoneahsan.com/docs)
|
|
358
358
|
- **Website**: [nativeupdate.aoneahsan.com](https://nativeupdate.aoneahsan.com)
|
|
359
359
|
|
|
360
360
|
## Related Documentation
|
|
361
361
|
|
|
362
|
-
- [Channel Management](
|
|
363
|
-
- [Quick Start Guide](
|
|
364
|
-
- [Configuration Guide](
|
|
365
|
-
- [End-to-End Workflow](
|
|
362
|
+
- [Channel Management](https://nativeupdate.aoneahsan.com/docs/guides/channel-management)
|
|
363
|
+
- [Quick Start Guide](https://nativeupdate.aoneahsan.com/docs/getting-started/quick-start)
|
|
364
|
+
- [Configuration Guide](https://nativeupdate.aoneahsan.com/docs/getting-started/configuration)
|
|
365
|
+
- [End-to-End Workflow](https://nativeupdate.aoneahsan.com/docs/guides/end-to-end-workflow)
|
|
@@ -479,10 +479,10 @@ export default App;
|
|
|
479
479
|
|
|
480
480
|
## Next Steps
|
|
481
481
|
|
|
482
|
-
- [Channel Management](
|
|
483
|
-
- [Dashboard Guide](
|
|
484
|
-
- [Security Best Practices](
|
|
485
|
-
- [API Reference](
|
|
482
|
+
- [Channel Management](https://nativeupdate.aoneahsan.com/docs/guides/channel-management) - Advanced channel configuration
|
|
483
|
+
- [Dashboard Guide](https://nativeupdate.aoneahsan.com/docs/guides/dashboard-guide) - Full dashboard documentation
|
|
484
|
+
- [Security Best Practices](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices) - Secure your updates
|
|
485
|
+
- [API Reference](https://nativeupdate.aoneahsan.com/docs/api/API) - Complete API documentation
|
|
486
486
|
|
|
487
487
|
## Support
|
|
488
488
|
|
|
@@ -275,9 +275,9 @@ cat intermediate.crt root.crt > chain.pem
|
|
|
275
275
|
|
|
276
276
|
## Next Steps
|
|
277
277
|
|
|
278
|
-
- Review [Security Best Practices](
|
|
279
|
-
- Implement [Bundle Signing](
|
|
280
|
-
- Set up [Deployment Guide](
|
|
278
|
+
- Review [Security Best Practices](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices)
|
|
279
|
+
- Implement [Bundle Signing](https://nativeupdate.aoneahsan.com/docs/BUNDLE_SIGNING)
|
|
280
|
+
- Set up [Deployment Guide](https://nativeupdate.aoneahsan.com/docs/guides/deployment-guide) for production deployment
|
|
281
281
|
|
|
282
282
|
---
|
|
283
283
|
|
|
@@ -140,6 +140,6 @@ npx native-update bundle sign bundle.zip --key private-key.pem
|
|
|
140
140
|
|
|
141
141
|
## Need Help?
|
|
142
142
|
|
|
143
|
-
- See [Testing Guide](
|
|
144
|
-
- Check [Server Requirements](
|
|
145
|
-
- Review [Security Best Practices](
|
|
143
|
+
- See [Testing Guide](https://nativeupdate.aoneahsan.com/docs/guides/testing-guide)
|
|
144
|
+
- Check [Server Requirements](https://nativeupdate.aoneahsan.com/docs/server-requirements)
|
|
145
|
+
- Review [Security Best Practices](https://nativeupdate.aoneahsan.com/docs/guides/security-best-practices)
|
|
@@ -1047,7 +1047,7 @@ const productionSecurityConfig = {
|
|
|
1047
1047
|
|
|
1048
1048
|
## Next Steps
|
|
1049
1049
|
|
|
1050
|
-
- Review [Production Readiness](
|
|
1050
|
+
- Review [Production Readiness](https://nativeupdate.aoneahsan.com/docs/production-readiness) checklist
|
|
1051
1051
|
- Implement proper monitoring and analytics
|
|
1052
1052
|
- Set up incident response procedures
|
|
1053
1053
|
- Configure update server security (see backend-template folder)
|
|
@@ -446,9 +446,9 @@ async function testAPI() {
|
|
|
446
446
|
If you can't resolve your issue:
|
|
447
447
|
|
|
448
448
|
1. **Check existing documentation**
|
|
449
|
-
- [Dashboard Guide](
|
|
450
|
-
- [Channel Management](
|
|
451
|
-
- [API Reference](
|
|
449
|
+
- [Dashboard Guide](https://nativeupdate.aoneahsan.com/docs/guides/dashboard-guide)
|
|
450
|
+
- [Channel Management](https://nativeupdate.aoneahsan.com/docs/guides/channel-management)
|
|
451
|
+
- [API Reference](https://nativeupdate.aoneahsan.com/docs/api/API)
|
|
452
452
|
|
|
453
453
|
2. **Gather information**
|
|
454
454
|
- Plugin version
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Capacitor + Capawesome + Trapeze Rollout Note (2026-03-01)
|
|
2
|
+
|
|
3
|
+
- Rollout target: nested `website/` app.
|
|
4
|
+
- App identity used from project context:
|
|
5
|
+
- App ID / bundle: `com.aoneahsan.nativeupdate`
|
|
6
|
+
- Host domain: `nativeupdate.aoneahsan.com`
|
|
7
|
+
- Implemented:
|
|
8
|
+
- Added `website/capacitor.config.ts`
|
|
9
|
+
- Added `website/apps-config.yaml`
|
|
10
|
+
- Added native platforms: `website/android/`, `website/ios/`
|
|
11
|
+
- Added scripts: `typecheck`, `cap:*`, `sync:apps-config`, `mobile:sync`
|
|
12
|
+
- Added deps: `@capacitor/android`, `@capacitor/ios`, `@trapezedev/configure`
|
|
13
|
+
- Updated `website/eslint.config.js` ignores for `android/**` and `ios/**`
|
|
14
|
+
- Verification:
|
|
15
|
+
- `cd website && yarn typecheck`
|
|
16
|
+
- `cd website && yarn lint`
|
|
17
|
+
- `cd website && yarn build`
|
|
18
|
+
- `cd website && npx cap add android`
|
|
19
|
+
- `cd website && npx cap add ios`
|
|
20
|
+
- `cd website && yarn mobile:sync`
|
|
21
|
+
- Result: pass.
|