@codeimplants/version-control 1.0.0 â 1.0.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 +344 -0
- package/package.json +22 -1
- package/.turbo/cache/27fad7e4fc35ffd5-meta.json +0 -1
- package/.turbo/cache/27fad7e4fc35ffd5.tar.zst +0 -0
- package/.turbo/cache/3a784e60ef4fd5a6-meta.json +0 -1
- package/.turbo/cache/3a784e60ef4fd5a6.tar.zst +0 -0
- package/.turbo/cache/5ff842ce2cdfa953-meta.json +0 -1
- package/.turbo/cache/5ff842ce2cdfa953.tar.zst +0 -0
- package/.turbo/cache/60a231c552bcee53-meta.json +0 -1
- package/.turbo/cache/60a231c552bcee53.tar.zst +0 -0
- package/.turbo/cache/6c7d12bf8eda1401-meta.json +0 -1
- package/.turbo/cache/6c7d12bf8eda1401.tar.zst +0 -0
- package/.turbo/cache/b1169bf3a222dd96-meta.json +0 -1
- package/.turbo/cache/b1169bf3a222dd96.tar.zst +0 -0
- package/.turbo/cache/bef44a5355c4c0bf-meta.json +0 -1
- package/.turbo/cache/bef44a5355c4c0bf.tar.zst +0 -0
- package/.turbo/cookies/3.cookie +0 -0
- package/.turbo/cookies/4.cookie +0 -0
- package/.turbo/cookies/5.cookie +0 -0
- package/.turbo/cookies/6.cookie +0 -0
- package/.turbo/cookies/7.cookie +0 -0
- package/.turbo/daemon/16cf72aee83bd4e9-turbo.log.2026-01-29 +0 -1
- package/.turbo/daemon/16cf72aee83bd4e9-turbo.log.2026-01-30 +0 -2
package/README.md
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
# Version Control SDK
|
|
2
|
+
|
|
3
|
+
A lightweight, backend-driven SDK that enables remote control of app version behavior including soft updates, force updates, and maintenance mode for mobile and web applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ð **Soft Updates** - Notify users about new versions with optional updates
|
|
8
|
+
- ⥠**Force Updates** - Enforce minimum version requirements
|
|
9
|
+
- ð§ **Maintenance Mode** - Put your app in maintenance mode remotely
|
|
10
|
+
- ð **Kill Switch** - Emergency app disable capability
|
|
11
|
+
- ðą **Cross-Platform** - Works on iOS, Android, and Web
|
|
12
|
+
- ðŊ **Auto-Detection** - Automatically detects platform, version, and app ID
|
|
13
|
+
- ð **Backend-Driven** - All decisions made server-side for instant updates
|
|
14
|
+
- ðŠķ **Lightweight** - Minimal dependencies and footprint
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @codeimplants/version-control
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or with yarn:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
yarn add @codeimplants/version-control
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### Simple Usage
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { VersionSDK } from "@codeimplants/version-control";
|
|
34
|
+
|
|
35
|
+
// Quick check with minimal config
|
|
36
|
+
const decision = await VersionSDK.check(
|
|
37
|
+
"https://api.yourapp.com",
|
|
38
|
+
"your-api-key",
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// Handle the decision
|
|
42
|
+
switch (decision.action) {
|
|
43
|
+
case "FORCE_UPDATE":
|
|
44
|
+
// Show force update dialog
|
|
45
|
+
window.location.href = decision.storeUrl;
|
|
46
|
+
break;
|
|
47
|
+
case "SOFT_UPDATE":
|
|
48
|
+
// Show optional update dialog
|
|
49
|
+
showUpdatePrompt(decision.message, decision.storeUrl);
|
|
50
|
+
break;
|
|
51
|
+
case "MAINTENANCE":
|
|
52
|
+
// Show maintenance screen
|
|
53
|
+
showMaintenanceScreen(decision.message);
|
|
54
|
+
break;
|
|
55
|
+
case "KILL_SWITCH":
|
|
56
|
+
// App is disabled
|
|
57
|
+
showKillSwitchScreen(decision.message);
|
|
58
|
+
break;
|
|
59
|
+
case "NONE":
|
|
60
|
+
// Continue normally
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Advanced Usage
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { VersionSDK, VCConfig } from "@codeimplants/version-control";
|
|
69
|
+
|
|
70
|
+
const config: VCConfig = {
|
|
71
|
+
backendUrl: "https://api.yourapp.com",
|
|
72
|
+
apiKey: "your-api-key",
|
|
73
|
+
appId: "com.yourapp.mobile", // Optional: auto-detected
|
|
74
|
+
platform: "ios", // Optional: auto-detected
|
|
75
|
+
version: "2.1.0", // Optional: auto-detected
|
|
76
|
+
timeout: 8000, // Optional: default 8000ms
|
|
77
|
+
debug: true, // Optional: enable logging
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const sdk = VersionSDK.init(config);
|
|
81
|
+
const decision = await sdk.checkVersion();
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Auto-Detection
|
|
85
|
+
|
|
86
|
+
The SDK automatically detects the following if not provided:
|
|
87
|
+
|
|
88
|
+
### Platform Detection
|
|
89
|
+
|
|
90
|
+
- **Capacitor**: Detects iOS/Android via Capacitor.getPlatform()
|
|
91
|
+
- **React Native**: Detects via navigator.product
|
|
92
|
+
- **User Agent**: Falls back to UA string parsing
|
|
93
|
+
- **Web**: Default fallback
|
|
94
|
+
|
|
95
|
+
### Version Detection
|
|
96
|
+
|
|
97
|
+
Checks in order:
|
|
98
|
+
|
|
99
|
+
1. Capacitor App plugin (`Capacitor.Plugins.App.getInfo()`)
|
|
100
|
+
2. Global variables: `APP_VERSION`, `__VERSION__`, `VERSION`, `packageVersion`
|
|
101
|
+
3. Fallback: `"1.0.0"`
|
|
102
|
+
|
|
103
|
+
### App ID Detection
|
|
104
|
+
|
|
105
|
+
Checks in order:
|
|
106
|
+
|
|
107
|
+
1. Capacitor App plugin bundle ID
|
|
108
|
+
2. Global variables: `APP_ID`, `__APP_ID__`, `BUNDLE_ID`, `PACKAGE_NAME`
|
|
109
|
+
3. Fallback: `"unknown.app"`
|
|
110
|
+
|
|
111
|
+
## Configuration
|
|
112
|
+
|
|
113
|
+
### VCConfig
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
interface VCConfig {
|
|
117
|
+
backendUrl: string; // Your backend API URL (required)
|
|
118
|
+
apiKey?: string; // API key for authentication
|
|
119
|
+
appId?: string; // App identifier (auto-detected if not provided)
|
|
120
|
+
platform?: Platform; // 'android' | 'ios' | 'web' | 'all'
|
|
121
|
+
version?: string; // Current app version (auto-detected if not provided)
|
|
122
|
+
timeout?: number; // Request timeout in ms (default: 8000)
|
|
123
|
+
debug?: boolean; // Enable debug logging
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Response Types
|
|
128
|
+
|
|
129
|
+
### VCDecision
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
interface VCDecision {
|
|
133
|
+
action: VCAction; // The action to take
|
|
134
|
+
message?: string; // User-facing message
|
|
135
|
+
storeUrl?: string; // App store URL for updates
|
|
136
|
+
minVersion?: string; // Minimum required version (for FORCE_UPDATE)
|
|
137
|
+
latestVersion?: string; // Latest available version
|
|
138
|
+
raw?: any; // Raw backend response
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Actions
|
|
143
|
+
|
|
144
|
+
- **`NONE`** - No action required, app is up to date
|
|
145
|
+
- **`SOFT_UPDATE`** - Update available but optional
|
|
146
|
+
- **`FORCE_UPDATE`** - Update required to continue
|
|
147
|
+
- **`MAINTENANCE`** - App is in maintenance mode
|
|
148
|
+
- **`KILL_SWITCH`** - App is disabled
|
|
149
|
+
- **`BLOCKED`** - Access blocked for this version/platform
|
|
150
|
+
|
|
151
|
+
## Backend Integration
|
|
152
|
+
|
|
153
|
+
The SDK expects your backend endpoint to accept POST requests at `/sdk/version/check`:
|
|
154
|
+
|
|
155
|
+
### Request Format
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
{
|
|
159
|
+
"appId": "com.yourapp.mobile",
|
|
160
|
+
"platform": "ios",
|
|
161
|
+
"currentVersion": "2.0.0",
|
|
162
|
+
"environment": "prod"
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Response Format
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
{
|
|
170
|
+
"status": "FORCE_UPDATE", // VCAction
|
|
171
|
+
"message": "Please update to continue",
|
|
172
|
+
"title": "Update Required",
|
|
173
|
+
"minVersion": "2.1.0",
|
|
174
|
+
"latestVersion": "2.3.0",
|
|
175
|
+
"storeUrl": "https://apps.apple.com/..."
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Examples
|
|
180
|
+
|
|
181
|
+
### React Example
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import React, { useEffect, useState } from 'react';
|
|
185
|
+
import { VersionSDK, VCDecision } from '@codeimplants/version-control';
|
|
186
|
+
|
|
187
|
+
function App() {
|
|
188
|
+
const [versionCheck, setVersionCheck] = useState<VCDecision | null>(null);
|
|
189
|
+
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
async function checkVersion() {
|
|
192
|
+
const decision = await VersionSDK.check(
|
|
193
|
+
'https://api.yourapp.com',
|
|
194
|
+
'your-api-key'
|
|
195
|
+
);
|
|
196
|
+
setVersionCheck(decision);
|
|
197
|
+
}
|
|
198
|
+
checkVersion();
|
|
199
|
+
}, []);
|
|
200
|
+
|
|
201
|
+
if (versionCheck?.action === 'FORCE_UPDATE') {
|
|
202
|
+
return (
|
|
203
|
+
<div className="update-required">
|
|
204
|
+
<h1>Update Required</h1>
|
|
205
|
+
<p>{versionCheck.message}</p>
|
|
206
|
+
<button onClick={() => window.location.href = versionCheck.storeUrl}>
|
|
207
|
+
Update Now
|
|
208
|
+
</button>
|
|
209
|
+
</div>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (versionCheck?.action === 'MAINTENANCE') {
|
|
214
|
+
return (
|
|
215
|
+
<div className="maintenance">
|
|
216
|
+
<h1>Maintenance Mode</h1>
|
|
217
|
+
<p>{versionCheck.message}</p>
|
|
218
|
+
</div>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return <YourApp />;
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Capacitor/Ionic Example
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { VersionSDK } from "@codeimplants/version-control";
|
|
230
|
+
import { AlertController } from "@ionic/angular";
|
|
231
|
+
|
|
232
|
+
export class AppComponent {
|
|
233
|
+
constructor(private alertController: AlertController) {}
|
|
234
|
+
|
|
235
|
+
async ngOnInit() {
|
|
236
|
+
const decision = await VersionSDK.check(
|
|
237
|
+
"https://api.yourapp.com",
|
|
238
|
+
"your-api-key",
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
if (decision.action === "SOFT_UPDATE") {
|
|
242
|
+
const alert = await this.alertController.create({
|
|
243
|
+
header: "Update Available",
|
|
244
|
+
message: decision.message,
|
|
245
|
+
buttons: [
|
|
246
|
+
{ text: "Later", role: "cancel" },
|
|
247
|
+
{
|
|
248
|
+
text: "Update",
|
|
249
|
+
handler: () => {
|
|
250
|
+
window.open(decision.storeUrl, "_system");
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
});
|
|
255
|
+
await alert.present();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### React Native Example
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
import { VersionSDK } from "@codeimplants/version-control";
|
|
265
|
+
import { Alert, Linking } from "react-native";
|
|
266
|
+
|
|
267
|
+
async function checkAppVersion() {
|
|
268
|
+
const decision = await VersionSDK.check(
|
|
269
|
+
"https://api.yourapp.com",
|
|
270
|
+
"your-api-key",
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
if (decision.action === "FORCE_UPDATE") {
|
|
274
|
+
Alert.alert(
|
|
275
|
+
"Update Required",
|
|
276
|
+
decision.message,
|
|
277
|
+
[
|
|
278
|
+
{
|
|
279
|
+
text: "Update Now",
|
|
280
|
+
onPress: () => Linking.openURL(decision.storeUrl),
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
{ cancelable: false },
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Version Comparison
|
|
290
|
+
|
|
291
|
+
The SDK uses semantic versioning (semver) comparison:
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// Examples of version comparisons
|
|
295
|
+
"1.0.0" < "1.0.1"; // true
|
|
296
|
+
"1.0.0" < "1.1.0"; // true
|
|
297
|
+
"2.0.0" > "1.9.9"; // true
|
|
298
|
+
"1.0" === "1.0.0"; // true (normalized)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Error Handling
|
|
302
|
+
|
|
303
|
+
The SDK gracefully handles errors and returns `{ action: "NONE" }` if:
|
|
304
|
+
|
|
305
|
+
- Network request fails
|
|
306
|
+
- Backend is unreachable
|
|
307
|
+
- Request times out
|
|
308
|
+
- Invalid response format
|
|
309
|
+
|
|
310
|
+
Enable debug mode to see detailed error logs:
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
const config = {
|
|
314
|
+
backendUrl: "https://api.yourapp.com",
|
|
315
|
+
debug: true, // Logs all requests and responses
|
|
316
|
+
};
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Best Practices
|
|
320
|
+
|
|
321
|
+
1. **Check on App Launch** - Always check version status when the app starts
|
|
322
|
+
2. **Cache Decisions** - Cache the decision for a reasonable time (e.g., 1 hour)
|
|
323
|
+
3. **Handle Offline** - Gracefully handle offline scenarios (SDK returns `NONE`)
|
|
324
|
+
4. **User Experience** - Don't interrupt critical user flows with update prompts
|
|
325
|
+
5. **Test Thoroughly** - Test all action types before deploying rules
|
|
326
|
+
6. **Gradual Rollouts** - Use backend rules to gradually roll out forced updates
|
|
327
|
+
|
|
328
|
+
## Platform Support
|
|
329
|
+
|
|
330
|
+
- â
iOS (Native & Web)
|
|
331
|
+
- â
Android (Native & Web)
|
|
332
|
+
- â
Web Browsers
|
|
333
|
+
- â
Capacitor
|
|
334
|
+
- â
React Native
|
|
335
|
+
- â
Ionic
|
|
336
|
+
- â
Cordova
|
|
337
|
+
|
|
338
|
+
## TypeScript
|
|
339
|
+
|
|
340
|
+
The SDK is written in TypeScript and includes full type definitions.
|
|
341
|
+
|
|
342
|
+
## License
|
|
343
|
+
|
|
344
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codeimplants/version-control",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "A lightweight cross-platform SDK to remotely control app version behavior such as soft update, force update, and maintenance mode using backend-driven rules, without enforcing UI.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"version-control",
|
|
7
|
+
"app-version",
|
|
8
|
+
"mobile-versioning",
|
|
9
|
+
"force-update",
|
|
10
|
+
"soft-update",
|
|
11
|
+
"maintenance-mode",
|
|
12
|
+
"capacitor",
|
|
13
|
+
"ionic",
|
|
14
|
+
"react-native",
|
|
15
|
+
"react",
|
|
16
|
+
"angular",
|
|
17
|
+
"android",
|
|
18
|
+
"ios",
|
|
19
|
+
"mobile-sdk",
|
|
20
|
+
"update-management",
|
|
21
|
+
"feature-control",
|
|
22
|
+
"remote-config",
|
|
23
|
+
"sdk"
|
|
24
|
+
],
|
|
4
25
|
"main": "dist/index.js",
|
|
5
26
|
"types": "dist/index.d.ts",
|
|
6
27
|
"scripts": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"hash":"27fad7e4fc35ffd5","duration":4627}
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"hash":"3a784e60ef4fd5a6","duration":1325}
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"hash":"5ff842ce2cdfa953","duration":1270}
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"hash":"60a231c552bcee53","duration":2326}
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"hash":"6c7d12bf8eda1401","duration":1164}
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"hash":"b1169bf3a222dd96","duration":2680}
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"hash":"bef44a5355c4c0bf","duration":2233}
|
|
Binary file
|
package/.turbo/cookies/3.cookie
DELETED
|
File without changes
|
package/.turbo/cookies/4.cookie
DELETED
|
File without changes
|
package/.turbo/cookies/5.cookie
DELETED
|
File without changes
|
package/.turbo/cookies/6.cookie
DELETED
|
File without changes
|
package/.turbo/cookies/7.cookie
DELETED
|
File without changes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2026-01-29T08:29:08.349183Z WARN daemon_server: turborepo_lib::commands::daemon: daemon already running
|