native-update 1.3.0 → 1.3.2
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/Readme.md +5 -5
- package/cli/index.js +5 -6
- package/cli/node_modules/.yarn-integrity +16 -0
- package/cli/node_modules/commander/LICENSE +22 -0
- package/cli/node_modules/commander/Readme.md +1148 -0
- package/cli/node_modules/commander/esm.mjs +16 -0
- package/cli/node_modules/commander/index.js +26 -0
- package/cli/node_modules/commander/lib/argument.js +145 -0
- package/cli/node_modules/commander/lib/command.js +2179 -0
- package/cli/node_modules/commander/lib/error.js +43 -0
- package/cli/node_modules/commander/lib/help.js +462 -0
- package/cli/node_modules/commander/lib/option.js +329 -0
- package/cli/node_modules/commander/lib/suggestSimilar.js +100 -0
- package/cli/node_modules/commander/package-support.json +16 -0
- package/cli/node_modules/commander/package.json +80 -0
- package/cli/node_modules/commander/typings/esm.d.mts +3 -0
- package/cli/node_modules/commander/typings/index.d.ts +884 -0
- package/cli/yarn.lock +8 -0
- package/dist/esm/__tests__/delta-processor.test.d.ts +1 -0
- package/dist/esm/__tests__/delta-processor.test.js +77 -0
- package/dist/esm/__tests__/delta-processor.test.js.map +1 -0
- package/dist/esm/__tests__/firestore-schema.test.d.ts +1 -0
- package/dist/esm/__tests__/firestore-schema.test.js +74 -0
- package/dist/esm/__tests__/firestore-schema.test.js.map +1 -0
- package/dist/esm/__tests__/manifest-reader.test.d.ts +1 -0
- package/dist/esm/__tests__/manifest-reader.test.js +271 -0
- package/dist/esm/__tests__/manifest-reader.test.js.map +1 -0
- package/dist/esm/__tests__/rollout-checker.test.d.ts +1 -0
- package/dist/esm/__tests__/rollout-checker.test.js +210 -0
- package/dist/esm/__tests__/rollout-checker.test.js.map +1 -0
- package/dist/esm/core/config.d.ts +26 -0
- package/dist/esm/core/config.js +6 -0
- package/dist/esm/core/config.js.map +1 -1
- package/dist/esm/firestore/firestore-client.d.ts +109 -0
- package/dist/esm/firestore/firestore-client.js +260 -0
- package/dist/esm/firestore/firestore-client.js.map +1 -0
- package/dist/esm/firestore/index.d.ts +11 -0
- package/dist/esm/firestore/index.js +11 -0
- package/dist/esm/firestore/index.js.map +1 -0
- package/dist/esm/firestore/manifest-reader.d.ts +87 -0
- package/dist/esm/firestore/manifest-reader.js +294 -0
- package/dist/esm/firestore/manifest-reader.js.map +1 -0
- package/dist/esm/firestore/schema.d.ts +504 -0
- package/dist/esm/firestore/schema.js +69 -0
- package/dist/esm/firestore/schema.js.map +1 -0
- package/dist/esm/live-update/delta-processor.d.ts +94 -0
- package/dist/esm/live-update/delta-processor.js +212 -0
- package/dist/esm/live-update/delta-processor.js.map +1 -0
- package/dist/esm/live-update/rollout-checker.d.ts +86 -0
- package/dist/esm/live-update/rollout-checker.js +305 -0
- package/dist/esm/live-update/rollout-checker.js.map +1 -0
- package/dist/esm/live-update/version-manager.d.ts +12 -0
- package/dist/esm/live-update/version-manager.js +67 -0
- package/dist/esm/live-update/version-manager.js.map +1 -1
- package/dist/plugin.cjs.js +1 -1
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.esm.js +1 -1
- package/dist/plugin.esm.js.map +1 -1
- package/dist/plugin.js +1 -1
- package/dist/plugin.js.map +1 -1
- package/docs/QUICK_START.md +3 -3
- package/docs/README.md +4 -4
- package/docs/api/API.md +4 -3
- package/docs/getting-started/installation.md +2 -2
- package/docs/play-console-rejection-rules.json +428 -0
- package/package.json +20 -18
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delta Processor
|
|
3
|
+
*
|
|
4
|
+
* Handles delta/patch-based updates for reduced download sizes.
|
|
5
|
+
* Uses a simple file-level patching strategy that works in JavaScript.
|
|
6
|
+
*
|
|
7
|
+
* For production, a WASM-based bsdiff/bspatch implementation can be added
|
|
8
|
+
* for better performance on binary files.
|
|
9
|
+
*/
|
|
10
|
+
import { Logger } from '../core/logger';
|
|
11
|
+
import { ConfigManager } from '../core/config';
|
|
12
|
+
import { DownloadError, ErrorCode, ValidationError } from '../core/errors';
|
|
13
|
+
/**
|
|
14
|
+
* Delta processor for applying patch updates
|
|
15
|
+
*/
|
|
16
|
+
export class DeltaProcessor {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.logger = Logger.getInstance();
|
|
19
|
+
this.configManager = ConfigManager.getInstance();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Download update with delta support
|
|
23
|
+
* Falls back to full download if delta fails
|
|
24
|
+
*/
|
|
25
|
+
async downloadWithDelta(updateInfo, currentBundleData, onProgress) {
|
|
26
|
+
var _a;
|
|
27
|
+
const startTime = Date.now();
|
|
28
|
+
// Check if delta is available and we have current bundle
|
|
29
|
+
if (this.configManager.get('enableDeltaUpdates') &&
|
|
30
|
+
((_a = updateInfo.delta) === null || _a === void 0 ? void 0 : _a.available) &&
|
|
31
|
+
updateInfo.delta.patchUrl &&
|
|
32
|
+
currentBundleData) {
|
|
33
|
+
try {
|
|
34
|
+
this.logger.info('Attempting delta update', {
|
|
35
|
+
patchSize: updateInfo.delta.patchSize,
|
|
36
|
+
fullSize: updateInfo.size,
|
|
37
|
+
});
|
|
38
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress(10);
|
|
39
|
+
// Download delta patch
|
|
40
|
+
const patchData = await this.downloadPatch(updateInfo.delta.patchUrl);
|
|
41
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress(50);
|
|
42
|
+
// Apply delta patch
|
|
43
|
+
const patchedBundle = await this.applyPatch(currentBundleData, patchData, updateInfo.delta.targetChecksum || updateInfo.checksum);
|
|
44
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress(90);
|
|
45
|
+
// Verify result
|
|
46
|
+
const resultChecksum = await this.calculateChecksum(patchedBundle);
|
|
47
|
+
if (resultChecksum !== (updateInfo.delta.targetChecksum || updateInfo.checksum)) {
|
|
48
|
+
throw new ValidationError(ErrorCode.CHECKSUM_MISMATCH, 'Delta patch result checksum mismatch');
|
|
49
|
+
}
|
|
50
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress(100);
|
|
51
|
+
const duration = Date.now() - startTime;
|
|
52
|
+
this.logger.info('Delta update successful', {
|
|
53
|
+
downloadSize: patchData.byteLength,
|
|
54
|
+
resultSize: patchedBundle.byteLength,
|
|
55
|
+
savings: `${Math.round((1 - patchData.byteLength / (updateInfo.size || patchedBundle.byteLength)) * 100)}%`,
|
|
56
|
+
duration,
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
usedDelta: true,
|
|
60
|
+
bundleData: patchedBundle,
|
|
61
|
+
checksum: resultChecksum,
|
|
62
|
+
downloadSize: patchData.byteLength,
|
|
63
|
+
duration,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
this.logger.warn('Delta update failed, falling back to full download', { error });
|
|
68
|
+
// Fall through to full download
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Full download
|
|
72
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress(10);
|
|
73
|
+
if (!updateInfo.bundleUrl) {
|
|
74
|
+
throw new DownloadError(ErrorCode.DOWNLOAD_FAILED, 'No bundle URL provided');
|
|
75
|
+
}
|
|
76
|
+
const fullBundle = await this.downloadFull(updateInfo.bundleUrl, onProgress);
|
|
77
|
+
const duration = Date.now() - startTime;
|
|
78
|
+
const checksum = await this.calculateChecksum(fullBundle);
|
|
79
|
+
// Verify checksum
|
|
80
|
+
if (updateInfo.checksum && checksum !== updateInfo.checksum) {
|
|
81
|
+
throw new ValidationError(ErrorCode.CHECKSUM_MISMATCH, 'Full bundle checksum mismatch');
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
usedDelta: false,
|
|
85
|
+
bundleData: fullBundle,
|
|
86
|
+
checksum,
|
|
87
|
+
downloadSize: fullBundle.byteLength,
|
|
88
|
+
duration,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Download a delta patch file
|
|
93
|
+
*/
|
|
94
|
+
async downloadPatch(url) {
|
|
95
|
+
const response = await fetch(url);
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
throw new DownloadError(ErrorCode.DOWNLOAD_FAILED, `Failed to download delta patch: ${response.status}`);
|
|
98
|
+
}
|
|
99
|
+
return response.arrayBuffer();
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Download full bundle
|
|
103
|
+
*/
|
|
104
|
+
async downloadFull(url, onProgress) {
|
|
105
|
+
const response = await fetch(url);
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
throw new DownloadError(ErrorCode.DOWNLOAD_FAILED, `Failed to download bundle: ${response.status}`);
|
|
108
|
+
}
|
|
109
|
+
// If we can track progress
|
|
110
|
+
const contentLength = response.headers.get('content-length');
|
|
111
|
+
const totalBytes = contentLength ? parseInt(contentLength, 10) : 0;
|
|
112
|
+
if (totalBytes && response.body) {
|
|
113
|
+
const reader = response.body.getReader();
|
|
114
|
+
const chunks = [];
|
|
115
|
+
let receivedBytes = 0;
|
|
116
|
+
while (true) {
|
|
117
|
+
const { done, value } = await reader.read();
|
|
118
|
+
if (done)
|
|
119
|
+
break;
|
|
120
|
+
chunks.push(value);
|
|
121
|
+
receivedBytes += value.length;
|
|
122
|
+
if (onProgress) {
|
|
123
|
+
const percent = Math.round((receivedBytes / totalBytes) * 90) + 10;
|
|
124
|
+
onProgress(Math.min(percent, 99));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress(100);
|
|
128
|
+
// Combine chunks
|
|
129
|
+
const result = new Uint8Array(receivedBytes);
|
|
130
|
+
let offset = 0;
|
|
131
|
+
for (const chunk of chunks) {
|
|
132
|
+
result.set(chunk, offset);
|
|
133
|
+
offset += chunk.length;
|
|
134
|
+
}
|
|
135
|
+
return result.buffer;
|
|
136
|
+
}
|
|
137
|
+
// Simple download without progress
|
|
138
|
+
const data = await response.arrayBuffer();
|
|
139
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress(100);
|
|
140
|
+
return data;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Apply a delta patch to create new bundle
|
|
144
|
+
*
|
|
145
|
+
* This is a simplified patch format using JSON metadata + file-level changes.
|
|
146
|
+
* For production binary patching, a WASM bspatch implementation would be used.
|
|
147
|
+
*/
|
|
148
|
+
async applyPatch(currentBundle, patchData, _expectedChecksum) {
|
|
149
|
+
try {
|
|
150
|
+
// Try to parse as JSON patch (file-level patching)
|
|
151
|
+
const patchText = new TextDecoder().decode(patchData);
|
|
152
|
+
const patch = JSON.parse(patchText);
|
|
153
|
+
if (patch.type === 'file-level') {
|
|
154
|
+
return this.applyFileLevelPatch(currentBundle, patch);
|
|
155
|
+
}
|
|
156
|
+
// Binary patch format
|
|
157
|
+
return this.applyBinaryPatch(currentBundle, patchData);
|
|
158
|
+
}
|
|
159
|
+
catch (_a) {
|
|
160
|
+
// Treat as binary patch
|
|
161
|
+
return this.applyBinaryPatch(currentBundle, patchData);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Apply file-level patch (JSON format)
|
|
166
|
+
*/
|
|
167
|
+
async applyFileLevelPatch(_currentBundle, _patch) {
|
|
168
|
+
// This would require JSZip on the client side
|
|
169
|
+
// For now, throw an error to fall back to full download
|
|
170
|
+
this.logger.debug('File-level patch not supported in plugin, falling back');
|
|
171
|
+
throw new Error('File-level patch not supported in plugin');
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Apply binary patch (bsdiff format)
|
|
175
|
+
*
|
|
176
|
+
* NOTE: This is a placeholder. For production, use WASM-compiled bspatch.
|
|
177
|
+
*/
|
|
178
|
+
async applyBinaryPatch(_currentBundle, _patchData) {
|
|
179
|
+
// Binary patching requires WASM implementation
|
|
180
|
+
// For now, throw an error to fall back to full download
|
|
181
|
+
this.logger.debug('Binary patch requires WASM module, falling back');
|
|
182
|
+
throw new Error('Binary patch not supported without WASM module');
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Calculate SHA-256 checksum
|
|
186
|
+
*/
|
|
187
|
+
async calculateChecksum(data) {
|
|
188
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
189
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
190
|
+
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Verify checksum matches
|
|
194
|
+
*/
|
|
195
|
+
async verifyChecksum(data, expectedChecksum) {
|
|
196
|
+
const actualChecksum = await this.calculateChecksum(data);
|
|
197
|
+
return actualChecksum.toLowerCase() === expectedChecksum.toLowerCase();
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Check if delta updates are enabled
|
|
201
|
+
*/
|
|
202
|
+
isDeltaEnabled() {
|
|
203
|
+
return this.configManager.get('enableDeltaUpdates');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Create delta processor instance
|
|
208
|
+
*/
|
|
209
|
+
export function createDeltaProcessor() {
|
|
210
|
+
return new DeltaProcessor();
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=delta-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delta-processor.js","sourceRoot":"","sources":["../../../src/live-update/delta-processor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rollout Checker
|
|
3
|
+
*
|
|
4
|
+
* Client-side rollout eligibility checking for staged deployments.
|
|
5
|
+
* Uses deterministic hashing for consistent rollout decisions.
|
|
6
|
+
*/
|
|
7
|
+
import type { RolloutConfig, DeviceInfo } from '../firestore/schema';
|
|
8
|
+
/**
|
|
9
|
+
* Rollout eligibility result
|
|
10
|
+
*/
|
|
11
|
+
export interface RolloutEligibilityResult {
|
|
12
|
+
/** Whether device is eligible for the update */
|
|
13
|
+
eligible: boolean;
|
|
14
|
+
/** Reason for the eligibility decision */
|
|
15
|
+
reason: string;
|
|
16
|
+
/** Device's calculated percentile (0-100) */
|
|
17
|
+
devicePercentile?: number;
|
|
18
|
+
/** Current rollout percentage */
|
|
19
|
+
currentPercentage?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Rollout checker for determining update eligibility
|
|
23
|
+
*/
|
|
24
|
+
export declare class RolloutChecker {
|
|
25
|
+
private readonly logger;
|
|
26
|
+
private readonly configManager;
|
|
27
|
+
private deviceInfoCache;
|
|
28
|
+
constructor();
|
|
29
|
+
/**
|
|
30
|
+
* Check if device is eligible for update based on rollout config
|
|
31
|
+
*/
|
|
32
|
+
checkEligibility(rollout: RolloutConfig, deviceInfo: DeviceInfo): Promise<RolloutEligibilityResult>;
|
|
33
|
+
/**
|
|
34
|
+
* Check time window for rollout
|
|
35
|
+
*/
|
|
36
|
+
private checkTimeWindow;
|
|
37
|
+
/**
|
|
38
|
+
* Check scheduled start time
|
|
39
|
+
*/
|
|
40
|
+
private checkScheduled;
|
|
41
|
+
/**
|
|
42
|
+
* Check target segments
|
|
43
|
+
*/
|
|
44
|
+
private checkSegments;
|
|
45
|
+
/**
|
|
46
|
+
* Calculate current rollout percentage based on schedule
|
|
47
|
+
*/
|
|
48
|
+
private calculateCurrentPercentage;
|
|
49
|
+
/**
|
|
50
|
+
* Get deterministic percentile for device ID
|
|
51
|
+
* Uses SHA-256 hash for uniform distribution
|
|
52
|
+
*/
|
|
53
|
+
getDevicePercentile(deviceId: string): Promise<number>;
|
|
54
|
+
/**
|
|
55
|
+
* Hash a string using SHA-256
|
|
56
|
+
*/
|
|
57
|
+
private hashString;
|
|
58
|
+
/**
|
|
59
|
+
* Convert hash to percentile (0-100)
|
|
60
|
+
*/
|
|
61
|
+
private hashToPercentile;
|
|
62
|
+
/**
|
|
63
|
+
* Compare semantic versions
|
|
64
|
+
*/
|
|
65
|
+
private compareVersions;
|
|
66
|
+
/**
|
|
67
|
+
* Set cached device info
|
|
68
|
+
*/
|
|
69
|
+
setDeviceInfo(deviceInfo: DeviceInfo): void;
|
|
70
|
+
/**
|
|
71
|
+
* Get cached device info
|
|
72
|
+
*/
|
|
73
|
+
getDeviceInfo(): DeviceInfo | null;
|
|
74
|
+
/**
|
|
75
|
+
* Check if staged rollouts are enabled
|
|
76
|
+
*/
|
|
77
|
+
isEnabled(): boolean;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create rollout checker instance
|
|
81
|
+
*/
|
|
82
|
+
export declare function createRolloutChecker(): RolloutChecker;
|
|
83
|
+
/**
|
|
84
|
+
* Collect device information for rollout eligibility
|
|
85
|
+
*/
|
|
86
|
+
export declare function collectDeviceInfo(): Promise<DeviceInfo>;
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rollout Checker
|
|
3
|
+
*
|
|
4
|
+
* Client-side rollout eligibility checking for staged deployments.
|
|
5
|
+
* Uses deterministic hashing for consistent rollout decisions.
|
|
6
|
+
*/
|
|
7
|
+
import { timestampToDate } from '../firestore/schema';
|
|
8
|
+
import { Logger } from '../core/logger';
|
|
9
|
+
import { ConfigManager } from '../core/config';
|
|
10
|
+
/**
|
|
11
|
+
* Rollout checker for determining update eligibility
|
|
12
|
+
*/
|
|
13
|
+
export class RolloutChecker {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.deviceInfoCache = null;
|
|
16
|
+
this.logger = Logger.getInstance();
|
|
17
|
+
this.configManager = ConfigManager.getInstance();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if device is eligible for update based on rollout config
|
|
21
|
+
*/
|
|
22
|
+
async checkEligibility(rollout, deviceInfo) {
|
|
23
|
+
var _a;
|
|
24
|
+
// If rollouts are disabled in config, always eligible
|
|
25
|
+
if (!this.configManager.get('enableStagedRollouts')) {
|
|
26
|
+
return { eligible: true, reason: 'Staged rollouts disabled in config' };
|
|
27
|
+
}
|
|
28
|
+
// If rollout is not enabled, all devices are eligible
|
|
29
|
+
if (!rollout.enabled) {
|
|
30
|
+
return { eligible: true, reason: 'Rollout not enabled, all devices eligible' };
|
|
31
|
+
}
|
|
32
|
+
// Check time window
|
|
33
|
+
const timeCheck = this.checkTimeWindow(rollout);
|
|
34
|
+
if (!timeCheck.eligible) {
|
|
35
|
+
return timeCheck;
|
|
36
|
+
}
|
|
37
|
+
// Check scheduled rollout
|
|
38
|
+
if (((_a = rollout.schedule) === null || _a === void 0 ? void 0 : _a.type) === 'scheduled' && rollout.schedule.scheduledTime) {
|
|
39
|
+
const scheduledCheck = this.checkScheduled(rollout.schedule.scheduledTime);
|
|
40
|
+
if (!scheduledCheck.eligible) {
|
|
41
|
+
return scheduledCheck;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Check target segments
|
|
45
|
+
if (rollout.targetSegments) {
|
|
46
|
+
const segmentCheck = this.checkSegments(rollout.targetSegments, deviceInfo);
|
|
47
|
+
if (!segmentCheck.eligible) {
|
|
48
|
+
return segmentCheck;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Calculate current rollout percentage
|
|
52
|
+
const currentPercentage = this.calculateCurrentPercentage(rollout);
|
|
53
|
+
// Get device percentile
|
|
54
|
+
const devicePercentile = await this.getDevicePercentile(deviceInfo.deviceId);
|
|
55
|
+
// Check if device is in rollout percentage
|
|
56
|
+
if (devicePercentile > currentPercentage) {
|
|
57
|
+
return {
|
|
58
|
+
eligible: false,
|
|
59
|
+
reason: `Device percentile ${devicePercentile.toFixed(1)}% exceeds rollout ${currentPercentage}%`,
|
|
60
|
+
devicePercentile,
|
|
61
|
+
currentPercentage,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
this.logger.debug('Device eligible for update', {
|
|
65
|
+
devicePercentile,
|
|
66
|
+
currentPercentage,
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
eligible: true,
|
|
70
|
+
reason: 'Device eligible for update',
|
|
71
|
+
devicePercentile,
|
|
72
|
+
currentPercentage,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check time window for rollout
|
|
77
|
+
*/
|
|
78
|
+
checkTimeWindow(rollout) {
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
const startTime = timestampToDate(rollout.startTime).getTime();
|
|
81
|
+
if (now < startTime) {
|
|
82
|
+
return {
|
|
83
|
+
eligible: false,
|
|
84
|
+
reason: `Rollout not started yet. Starts at ${new Date(startTime).toISOString()}`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (rollout.endTime) {
|
|
88
|
+
const endTime = timestampToDate(rollout.endTime).getTime();
|
|
89
|
+
if (now > endTime) {
|
|
90
|
+
return {
|
|
91
|
+
eligible: false,
|
|
92
|
+
reason: `Rollout ended at ${new Date(endTime).toISOString()}`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { eligible: true, reason: 'Within time window' };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Check scheduled start time
|
|
100
|
+
*/
|
|
101
|
+
checkScheduled(scheduledTime) {
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
const scheduled = timestampToDate(scheduledTime).getTime();
|
|
104
|
+
if (now < scheduled) {
|
|
105
|
+
return {
|
|
106
|
+
eligible: false,
|
|
107
|
+
reason: `Scheduled for ${new Date(scheduled).toISOString()}`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return { eligible: true, reason: 'Scheduled time passed' };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Check target segments
|
|
114
|
+
*/
|
|
115
|
+
checkSegments(segments, deviceInfo) {
|
|
116
|
+
// Platform check
|
|
117
|
+
if (segments.platforms && segments.platforms.length > 0) {
|
|
118
|
+
if (!segments.platforms.includes(deviceInfo.platform)) {
|
|
119
|
+
return {
|
|
120
|
+
eligible: false,
|
|
121
|
+
reason: `Platform ${deviceInfo.platform} not in target list: ${segments.platforms.join(', ')}`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// App version checks
|
|
126
|
+
if (segments.minAppVersion) {
|
|
127
|
+
if (this.compareVersions(deviceInfo.appVersion, segments.minAppVersion) < 0) {
|
|
128
|
+
return {
|
|
129
|
+
eligible: false,
|
|
130
|
+
reason: `App version ${deviceInfo.appVersion} below minimum ${segments.minAppVersion}`,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (segments.maxAppVersion) {
|
|
135
|
+
if (this.compareVersions(deviceInfo.appVersion, segments.maxAppVersion) > 0) {
|
|
136
|
+
return {
|
|
137
|
+
eligible: false,
|
|
138
|
+
reason: `App version ${deviceInfo.appVersion} above maximum ${segments.maxAppVersion}`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Device ID whitelist
|
|
143
|
+
if (segments.deviceIds && segments.deviceIds.length > 0) {
|
|
144
|
+
if (!segments.deviceIds.includes(deviceInfo.deviceId)) {
|
|
145
|
+
return {
|
|
146
|
+
eligible: false,
|
|
147
|
+
reason: 'Device not in whitelist',
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Region check
|
|
152
|
+
if (segments.regions && segments.regions.length > 0) {
|
|
153
|
+
if (!deviceInfo.region || !segments.regions.includes(deviceInfo.region)) {
|
|
154
|
+
return {
|
|
155
|
+
eligible: false,
|
|
156
|
+
reason: `Region ${deviceInfo.region || 'unknown'} not in target list: ${segments.regions.join(', ')}`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return { eligible: true, reason: 'Segment checks passed' };
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Calculate current rollout percentage based on schedule
|
|
164
|
+
*/
|
|
165
|
+
calculateCurrentPercentage(rollout) {
|
|
166
|
+
if (!rollout.schedule || rollout.schedule.type !== 'gradual') {
|
|
167
|
+
return rollout.percentage;
|
|
168
|
+
}
|
|
169
|
+
const { gradualSteps, gradualInterval } = rollout.schedule;
|
|
170
|
+
if (!gradualSteps || !gradualInterval) {
|
|
171
|
+
return rollout.percentage;
|
|
172
|
+
}
|
|
173
|
+
const startTime = timestampToDate(rollout.startTime).getTime();
|
|
174
|
+
const elapsedHours = (Date.now() - startTime) / (1000 * 60 * 60);
|
|
175
|
+
const stepIndex = Math.floor(elapsedHours / gradualInterval);
|
|
176
|
+
if (stepIndex >= gradualSteps.length) {
|
|
177
|
+
return gradualSteps[gradualSteps.length - 1];
|
|
178
|
+
}
|
|
179
|
+
return gradualSteps[stepIndex];
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get deterministic percentile for device ID
|
|
183
|
+
* Uses SHA-256 hash for uniform distribution
|
|
184
|
+
*/
|
|
185
|
+
async getDevicePercentile(deviceId) {
|
|
186
|
+
const hash = await this.hashString(deviceId);
|
|
187
|
+
return this.hashToPercentile(hash);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Hash a string using SHA-256
|
|
191
|
+
*/
|
|
192
|
+
async hashString(input) {
|
|
193
|
+
const encoder = new TextEncoder();
|
|
194
|
+
const data = encoder.encode(input);
|
|
195
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
196
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
197
|
+
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Convert hash to percentile (0-100)
|
|
201
|
+
*/
|
|
202
|
+
hashToPercentile(hash) {
|
|
203
|
+
const hashInt = parseInt(hash.substring(0, 8), 16);
|
|
204
|
+
const maxInt = parseInt('ffffffff', 16);
|
|
205
|
+
return (hashInt / maxInt) * 100;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Compare semantic versions
|
|
209
|
+
*/
|
|
210
|
+
compareVersions(v1, v2) {
|
|
211
|
+
const cleanV1 = v1.split('-')[0];
|
|
212
|
+
const cleanV2 = v2.split('-')[0];
|
|
213
|
+
const parts1 = cleanV1.split('.').map(Number);
|
|
214
|
+
const parts2 = cleanV2.split('.').map(Number);
|
|
215
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
216
|
+
const p1 = parts1[i] || 0;
|
|
217
|
+
const p2 = parts2[i] || 0;
|
|
218
|
+
if (p1 > p2)
|
|
219
|
+
return 1;
|
|
220
|
+
if (p1 < p2)
|
|
221
|
+
return -1;
|
|
222
|
+
}
|
|
223
|
+
return 0;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Set cached device info
|
|
227
|
+
*/
|
|
228
|
+
setDeviceInfo(deviceInfo) {
|
|
229
|
+
this.deviceInfoCache = deviceInfo;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Get cached device info
|
|
233
|
+
*/
|
|
234
|
+
getDeviceInfo() {
|
|
235
|
+
return this.deviceInfoCache;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Check if staged rollouts are enabled
|
|
239
|
+
*/
|
|
240
|
+
isEnabled() {
|
|
241
|
+
return this.configManager.get('enableStagedRollouts');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Create rollout checker instance
|
|
246
|
+
*/
|
|
247
|
+
export function createRolloutChecker() {
|
|
248
|
+
return new RolloutChecker();
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Collect device information for rollout eligibility
|
|
252
|
+
*/
|
|
253
|
+
export async function collectDeviceInfo() {
|
|
254
|
+
// Try to get Capacitor device info if available
|
|
255
|
+
let platform = 'web';
|
|
256
|
+
let osVersion = '';
|
|
257
|
+
try {
|
|
258
|
+
const { Device } = await import('@capacitor/device');
|
|
259
|
+
const info = await Device.getInfo();
|
|
260
|
+
platform = info.platform;
|
|
261
|
+
osVersion = info.osVersion;
|
|
262
|
+
}
|
|
263
|
+
catch (_a) {
|
|
264
|
+
// Running in web or Device plugin not available
|
|
265
|
+
platform = 'web';
|
|
266
|
+
osVersion = navigator.userAgent;
|
|
267
|
+
}
|
|
268
|
+
// Get or generate device ID
|
|
269
|
+
let deviceId = '';
|
|
270
|
+
try {
|
|
271
|
+
const { Preferences } = await import('@capacitor/preferences');
|
|
272
|
+
const { value } = await Preferences.get({ key: 'native_update_device_id' });
|
|
273
|
+
if (value) {
|
|
274
|
+
deviceId = value;
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
deviceId = crypto.randomUUID();
|
|
278
|
+
await Preferences.set({ key: 'native_update_device_id', value: deviceId });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
catch (_b) {
|
|
282
|
+
// Generate a session-based ID
|
|
283
|
+
deviceId = `web_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
284
|
+
}
|
|
285
|
+
// Get app version
|
|
286
|
+
let appVersion = '1.0.0';
|
|
287
|
+
try {
|
|
288
|
+
const { App } = await import('@capacitor/app');
|
|
289
|
+
const info = await App.getInfo();
|
|
290
|
+
appVersion = info.version;
|
|
291
|
+
}
|
|
292
|
+
catch (_c) {
|
|
293
|
+
// Use default version
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
deviceId,
|
|
297
|
+
platform,
|
|
298
|
+
appVersion,
|
|
299
|
+
bundleVersion: '1.0.0', // Will be updated by the plugin
|
|
300
|
+
osVersion,
|
|
301
|
+
locale: navigator.language,
|
|
302
|
+
region: undefined, // Could be determined via geolocation or timezone
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=rollout-checker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollout-checker.js","sourceRoot":"","sources":["../../../src/live-update/rollout-checker.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { LatestVersion } from '../definitions';
|
|
2
|
+
import type { DeviceInfo, UpdateCheckResponse } from '../firestore/schema';
|
|
2
3
|
/**
|
|
3
4
|
* Manages version checking and comparison
|
|
4
5
|
*/
|
|
@@ -10,6 +11,8 @@ export declare class VersionManager {
|
|
|
10
11
|
private readonly securityValidator;
|
|
11
12
|
private preferences;
|
|
12
13
|
private memoryCache;
|
|
14
|
+
private firestoreClient;
|
|
15
|
+
private manifestReader;
|
|
13
16
|
constructor();
|
|
14
17
|
/**
|
|
15
18
|
* Compare two semantic versions
|
|
@@ -27,6 +30,15 @@ export declare class VersionManager {
|
|
|
27
30
|
* Initialize the version manager
|
|
28
31
|
*/
|
|
29
32
|
initialize(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Check for updates from Firestore backend
|
|
35
|
+
* Uses ManifestReader for efficient single-document reads
|
|
36
|
+
*/
|
|
37
|
+
checkForUpdatesFromFirestore(currentVersion: string, deviceInfo: DeviceInfo): Promise<UpdateCheckResponse | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Get the appropriate update checker based on backend type
|
|
40
|
+
*/
|
|
41
|
+
checkForUpdatesAuto(currentVersion: string, deviceInfo?: DeviceInfo): Promise<LatestVersion | UpdateCheckResponse | null>;
|
|
30
42
|
/**
|
|
31
43
|
* Check for latest version from server
|
|
32
44
|
*/
|