@codeimplants/version-control 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/.turbo/cache/27fad7e4fc35ffd5-meta.json +1 -0
  2. package/.turbo/cache/27fad7e4fc35ffd5.tar.zst +0 -0
  3. package/.turbo/cache/3a784e60ef4fd5a6-meta.json +1 -0
  4. package/.turbo/cache/3a784e60ef4fd5a6.tar.zst +0 -0
  5. package/.turbo/cache/5ff842ce2cdfa953-meta.json +1 -0
  6. package/.turbo/cache/5ff842ce2cdfa953.tar.zst +0 -0
  7. package/.turbo/cache/60a231c552bcee53-meta.json +1 -0
  8. package/.turbo/cache/60a231c552bcee53.tar.zst +0 -0
  9. package/.turbo/cache/6c7d12bf8eda1401-meta.json +1 -0
  10. package/.turbo/cache/6c7d12bf8eda1401.tar.zst +0 -0
  11. package/.turbo/cache/b1169bf3a222dd96-meta.json +1 -0
  12. package/.turbo/cache/b1169bf3a222dd96.tar.zst +0 -0
  13. package/.turbo/cache/bef44a5355c4c0bf-meta.json +1 -0
  14. package/.turbo/cache/bef44a5355c4c0bf.tar.zst +0 -0
  15. package/.turbo/cookies/3.cookie +0 -0
  16. package/.turbo/cookies/4.cookie +0 -0
  17. package/.turbo/cookies/5.cookie +0 -0
  18. package/.turbo/cookies/6.cookie +0 -0
  19. package/.turbo/cookies/7.cookie +0 -0
  20. package/.turbo/daemon/16cf72aee83bd4e9-turbo.log.2026-01-29 +1 -0
  21. package/.turbo/daemon/16cf72aee83bd4e9-turbo.log.2026-01-30 +2 -0
  22. package/dist/client.d.ts +3 -0
  23. package/dist/client.d.ts.map +1 -0
  24. package/dist/client.js +22 -0
  25. package/dist/comparator.d.ts +3 -0
  26. package/dist/comparator.d.ts.map +1 -0
  27. package/dist/comparator.js +17 -0
  28. package/dist/detector.d.ts +5 -0
  29. package/dist/detector.d.ts.map +1 -0
  30. package/dist/detector.js +65 -0
  31. package/dist/engine.d.ts +3 -0
  32. package/dist/engine.d.ts.map +1 -0
  33. package/dist/engine.js +33 -0
  34. package/dist/index.d.ts +3 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +2 -0
  37. package/dist/sdk.d.ts +8 -0
  38. package/dist/sdk.d.ts.map +1 -0
  39. package/dist/sdk.js +58 -0
  40. package/dist/types.d.ts +30 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/types.js +1 -0
  43. package/package.json +14 -0
  44. package/src/client.ts +26 -0
  45. package/src/comparator.ts +18 -0
  46. package/src/detector.ts +67 -0
  47. package/src/engine.ts +39 -0
  48. package/src/index.ts +2 -0
  49. package/src/sdk.ts +65 -0
  50. package/src/types.ts +39 -0
  51. package/tsconfig.json +17 -0
  52. package/turbo.json +13 -0
@@ -0,0 +1 @@
1
+ {"hash":"27fad7e4fc35ffd5","duration":4627}
@@ -0,0 +1 @@
1
+ {"hash":"3a784e60ef4fd5a6","duration":1325}
@@ -0,0 +1 @@
1
+ {"hash":"5ff842ce2cdfa953","duration":1270}
@@ -0,0 +1 @@
1
+ {"hash":"60a231c552bcee53","duration":2326}
@@ -0,0 +1 @@
1
+ {"hash":"6c7d12bf8eda1401","duration":1164}
@@ -0,0 +1 @@
1
+ {"hash":"b1169bf3a222dd96","duration":2680}
@@ -0,0 +1 @@
1
+ {"hash":"bef44a5355c4c0bf","duration":2233}
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1 @@
1
+ 2026-01-29T08:29:08.349183Z WARN daemon_server: turborepo_lib::commands::daemon: daemon already running
@@ -0,0 +1,2 @@
1
+ 2026-01-30T13:27:33.409085Z WARN daemon_server: turborepo_lib::commands::daemon: daemon already running
2
+ 2026-01-30T19:18:48.539522Z WARN daemon_server: turborepo_lib::commands::daemon: daemon already running
@@ -0,0 +1,3 @@
1
+ import { VCConfig } from "./types";
2
+ export declare function fetchRules(config: VCConfig): Promise<any>;
3
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,wBAAsB,UAAU,CAAC,MAAM,EAAE,QAAQ,gBAuBhD"}
package/dist/client.js ADDED
@@ -0,0 +1,22 @@
1
+ export async function fetchRules(config) {
2
+ const controller = new AbortController();
3
+ const id = setTimeout(() => controller.abort(), config.timeout || 8000);
4
+ const res = await fetch(`${config.backendUrl}/sdk/version/check`, {
5
+ method: "POST",
6
+ headers: {
7
+ "Content-Type": "application/json",
8
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {})
9
+ },
10
+ body: JSON.stringify({
11
+ appId: config.appId || "unknown",
12
+ platform: config.platform || "web",
13
+ currentVersion: config.version || "1.0.0",
14
+ environment: "prod" // Default to prod
15
+ }),
16
+ signal: controller.signal
17
+ });
18
+ clearTimeout(id);
19
+ if (!res.ok)
20
+ throw new Error("SDK_BACKEND_ERROR");
21
+ return res.json();
22
+ }
@@ -0,0 +1,3 @@
1
+ export declare function normalize(v: string): number[];
2
+ export declare function compareVersions(a: string, b: string): number;
3
+ //# sourceMappingURL=comparator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comparator.d.ts","sourceRoot":"","sources":["../src/comparator.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAE7C;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAa5D"}
@@ -0,0 +1,17 @@
1
+ export function normalize(v) {
2
+ return v.split(".").map(n => parseInt(n || "0", 10));
3
+ }
4
+ export function compareVersions(a, b) {
5
+ const av = normalize(a);
6
+ const bv = normalize(b);
7
+ const len = Math.max(av.length, bv.length);
8
+ for (let i = 0; i < len; i++) {
9
+ const x = av[i] || 0;
10
+ const y = bv[i] || 0;
11
+ if (x > y)
12
+ return 1;
13
+ if (x < y)
14
+ return -1;
15
+ }
16
+ return 0;
17
+ }
@@ -0,0 +1,5 @@
1
+ import { Platform } from "./types";
2
+ export declare function detectPlatform(): Promise<Platform>;
3
+ export declare function detectVersion(): Promise<string>;
4
+ export declare function detectAppId(): Promise<string>;
5
+ //# sourceMappingURL=detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../src/detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,wBAAsB,cAAc,IAAI,OAAO,CAAC,QAAQ,CAAC,CAoBxD;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAoBrD;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAoBnD"}
@@ -0,0 +1,65 @@
1
+ export async function detectPlatform() {
2
+ var _a, _b;
3
+ if (typeof window === "undefined")
4
+ return "web";
5
+ const win = window;
6
+ // Check for Capacitor
7
+ if ((_a = win.Capacitor) === null || _a === void 0 ? void 0 : _a.getPlatform) {
8
+ const platform = win.Capacitor.getPlatform();
9
+ if (platform === "android" || platform === "ios")
10
+ return platform;
11
+ }
12
+ if (((_b = win.navigator) === null || _b === void 0 ? void 0 : _b.product) === "ReactNative") {
13
+ return "all";
14
+ }
15
+ const ua = navigator.userAgent.toLowerCase();
16
+ if (ua.includes("android"))
17
+ return "android";
18
+ if (ua.includes("iphone") || ua.includes("ipad") || ua.includes("ipod"))
19
+ return "ios";
20
+ return "web";
21
+ }
22
+ export async function detectVersion() {
23
+ var _a, _b;
24
+ const win = window;
25
+ if (typeof window === "undefined")
26
+ return "1.0.0";
27
+ if ((_b = (_a = win.Capacitor) === null || _a === void 0 ? void 0 : _a.Plugins) === null || _b === void 0 ? void 0 : _b.App) {
28
+ try {
29
+ const info = await win.Capacitor.Plugins.App.getInfo();
30
+ if (info.version)
31
+ return info.version;
32
+ }
33
+ catch (e) {
34
+ // ignore
35
+ }
36
+ }
37
+ const versionKeys = ["APP_VERSION", "__VERSION__", "VERSION", "packageVersion"];
38
+ for (const key of versionKeys) {
39
+ if (win[key])
40
+ return win[key];
41
+ }
42
+ return "1.0.0";
43
+ }
44
+ export async function detectAppId() {
45
+ var _a, _b;
46
+ const win = window;
47
+ if (typeof window === "undefined")
48
+ return "unknown.app";
49
+ if ((_b = (_a = win.Capacitor) === null || _a === void 0 ? void 0 : _a.Plugins) === null || _b === void 0 ? void 0 : _b.App) {
50
+ try {
51
+ const info = await win.Capacitor.Plugins.App.getInfo();
52
+ if (info.id)
53
+ return info.id;
54
+ }
55
+ catch (e) {
56
+ // ignore
57
+ }
58
+ }
59
+ const idKeys = ["APP_ID", "__APP_ID__", "BUNDLE_ID", "PACKAGE_NAME"];
60
+ for (const key of idKeys) {
61
+ if (win[key])
62
+ return win[key];
63
+ }
64
+ return "unknown.app";
65
+ }
@@ -0,0 +1,3 @@
1
+ import { BackendRule, VCDecision } from "./types";
2
+ export declare function evaluate(rule: BackendRule, currentVersion: string): VCDecision;
3
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGlD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,GAAG,UAAU,CAmC9E"}
package/dist/engine.js ADDED
@@ -0,0 +1,33 @@
1
+ import { compareVersions } from "./comparator";
2
+ export function evaluate(rule, currentVersion) {
3
+ if (rule.killSwitch) {
4
+ return { action: "KILL_SWITCH", message: rule.message, raw: rule };
5
+ }
6
+ if (rule.maintenance) {
7
+ return { action: "MAINTENANCE", message: rule.message, raw: rule };
8
+ }
9
+ if (rule.forceUpdate && rule.minVersion) {
10
+ if (compareVersions(currentVersion, rule.minVersion) < 0) {
11
+ return {
12
+ action: "FORCE_UPDATE",
13
+ message: rule.message,
14
+ storeUrl: rule.storeUrl,
15
+ minVersion: rule.minVersion,
16
+ latestVersion: rule.latestVersion,
17
+ raw: rule
18
+ };
19
+ }
20
+ }
21
+ if (rule.softUpdate && rule.latestVersion) {
22
+ if (compareVersions(currentVersion, rule.latestVersion) < 0) {
23
+ return {
24
+ action: "SOFT_UPDATE",
25
+ message: rule.message,
26
+ storeUrl: rule.storeUrl,
27
+ latestVersion: rule.latestVersion,
28
+ raw: rule
29
+ };
30
+ }
31
+ }
32
+ return { action: "NONE", raw: rule };
33
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./types";
2
+ export * from "./sdk";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./types";
2
+ export * from "./sdk";
package/dist/sdk.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { VCConfig, VCDecision } from "./types";
2
+ export declare class VersionSDK {
3
+ private config;
4
+ static init(config: VCConfig | string, apiKey?: string): VersionSDK;
5
+ static check(config: VCConfig | string, apiKey?: string): Promise<VCDecision>;
6
+ checkVersion(): Promise<VCDecision>;
7
+ }
8
+ //# sourceMappingURL=sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAK/C,qBAAa,UAAU;IACnB,OAAO,CAAC,MAAM,CAAY;IAE1B,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;WAczC,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAI7E,YAAY,IAAI,OAAO,CAAC,UAAU,CAAC;CAsC5C"}
package/dist/sdk.js ADDED
@@ -0,0 +1,58 @@
1
+ import { fetchRules } from "./client";
2
+ import { detectAppId, detectPlatform, detectVersion } from "./detector";
3
+ export class VersionSDK {
4
+ static init(config, apiKey) {
5
+ const sdk = new VersionSDK();
6
+ if (typeof config === "string") {
7
+ sdk.config = {
8
+ backendUrl: config,
9
+ apiKey: apiKey,
10
+ debug: false
11
+ };
12
+ }
13
+ else {
14
+ sdk.config = config;
15
+ }
16
+ return sdk;
17
+ }
18
+ static async check(config, apiKey) {
19
+ return VersionSDK.init(config, apiKey).checkVersion();
20
+ }
21
+ async checkVersion() {
22
+ try {
23
+ if (!this.config.platform)
24
+ this.config.platform = await detectPlatform();
25
+ if (!this.config.version)
26
+ this.config.version = await detectVersion();
27
+ if (!this.config.appId)
28
+ this.config.appId = await detectAppId();
29
+ if (this.config.debug) {
30
+ console.log("[VC-SDK] Checking version for:", {
31
+ appId: this.config.appId,
32
+ platform: this.config.platform,
33
+ version: this.config.version
34
+ });
35
+ }
36
+ const response = await fetchRules(this.config);
37
+ if (this.config.debug) {
38
+ console.log("[VC-SDK] Backend Response:", response);
39
+ }
40
+ // The backend now returns the evaluated decision directly
41
+ const decision = {
42
+ action: response.status || "NONE",
43
+ message: response.message || response.title,
44
+ storeUrl: response.storeUrl,
45
+ minVersion: response.minVersion,
46
+ latestVersion: response.latestVersion,
47
+ raw: response
48
+ };
49
+ return decision;
50
+ }
51
+ catch (e) {
52
+ if (this.config.debug) {
53
+ console.error("[VC-SDK] Error:", e);
54
+ }
55
+ return { action: "NONE" };
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,30 @@
1
+ export type Platform = "android" | "ios" | "web" | "all";
2
+ export type VCAction = "NONE" | "SOFT_UPDATE" | "FORCE_UPDATE" | "MAINTENANCE" | "KILL_SWITCH" | "BLOCKED";
3
+ export interface VCConfig {
4
+ backendUrl: string;
5
+ apiKey?: string;
6
+ appId?: string;
7
+ platform?: Platform;
8
+ version?: string;
9
+ timeout?: number;
10
+ debug?: boolean;
11
+ }
12
+ export interface BackendRule {
13
+ minVersion?: string;
14
+ latestVersion?: string;
15
+ softUpdate?: boolean;
16
+ forceUpdate?: boolean;
17
+ maintenance?: boolean;
18
+ killSwitch?: boolean;
19
+ message?: string;
20
+ storeUrl?: string;
21
+ }
22
+ export interface VCDecision {
23
+ action: VCAction;
24
+ message?: string;
25
+ storeUrl?: string;
26
+ minVersion?: string;
27
+ latestVersion?: string;
28
+ raw?: any;
29
+ }
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAEzD,MAAM,MAAM,QAAQ,GACd,MAAM,GACN,aAAa,GACb,cAAc,GACd,aAAa,GACb,aAAa,GACb,SAAS,CAAC;AAEhB,MAAM,WAAW,QAAQ;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACvB,MAAM,EAAE,QAAQ,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,GAAG,CAAC;CACb"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@codeimplants/version-control",
3
+ "version": "1.0.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "scripts": {
7
+ "build": "tsc",
8
+ "publish:pkg": "pnpm build && pnpm publish --access public"
9
+ },
10
+ "dependencies": {},
11
+ "devDependencies": {
12
+ "typescript": "^5.4.0"
13
+ }
14
+ }
package/src/client.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { VCConfig } from "./types";
2
+
3
+ export async function fetchRules(config: VCConfig) {
4
+ const controller = new AbortController();
5
+ const id = setTimeout(() => controller.abort(), config.timeout || 8000);
6
+
7
+ const res = await fetch(`${config.backendUrl}/sdk/version/check`, {
8
+ method: "POST",
9
+ headers: {
10
+ "Content-Type": "application/json",
11
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {})
12
+ },
13
+ body: JSON.stringify({
14
+ appId: config.appId || "unknown",
15
+ platform: config.platform || "web",
16
+ currentVersion: config.version || "1.0.0",
17
+ environment: "prod" // Default to prod
18
+ }),
19
+ signal: controller.signal
20
+ });
21
+
22
+ clearTimeout(id);
23
+
24
+ if (!res.ok) throw new Error("SDK_BACKEND_ERROR");
25
+ return res.json();
26
+ }
@@ -0,0 +1,18 @@
1
+ export function normalize(v: string): number[] {
2
+ return v.split(".").map(n => parseInt(n || "0", 10));
3
+ }
4
+
5
+ export function compareVersions(a: string, b: string): number {
6
+ const av = normalize(a);
7
+ const bv = normalize(b);
8
+ const len = Math.max(av.length, bv.length);
9
+
10
+
11
+ for (let i = 0; i < len; i++) {
12
+ const x = av[i] || 0;
13
+ const y = bv[i] || 0;
14
+ if (x > y) return 1;
15
+ if (x < y) return -1;
16
+ }
17
+ return 0;
18
+ }
@@ -0,0 +1,67 @@
1
+ import { Platform } from "./types";
2
+
3
+ export async function detectPlatform(): Promise<Platform> {
4
+ if (typeof window === "undefined") return "web";
5
+
6
+ const win = window as any;
7
+
8
+ // Check for Capacitor
9
+ if (win.Capacitor?.getPlatform) {
10
+ const platform = win.Capacitor.getPlatform();
11
+ if (platform === "android" || platform === "ios") return platform as Platform;
12
+ }
13
+
14
+ if (win.navigator?.product === "ReactNative") {
15
+ return "all";
16
+ }
17
+
18
+ const ua = navigator.userAgent.toLowerCase();
19
+ if (ua.includes("android")) return "android";
20
+ if (ua.includes("iphone") || ua.includes("ipad") || ua.includes("ipod")) return "ios";
21
+
22
+ return "web";
23
+ }
24
+
25
+ export async function detectVersion(): Promise<string> {
26
+ const win = window as any;
27
+
28
+ if (typeof window === "undefined") return "1.0.0";
29
+
30
+ if (win.Capacitor?.Plugins?.App) {
31
+ try {
32
+ const info = await win.Capacitor.Plugins.App.getInfo();
33
+ if (info.version) return info.version;
34
+ } catch (e) {
35
+ // ignore
36
+ }
37
+ }
38
+
39
+ const versionKeys = ["APP_VERSION", "__VERSION__", "VERSION", "packageVersion"];
40
+ for (const key of versionKeys) {
41
+ if (win[key]) return win[key];
42
+ }
43
+
44
+ return "1.0.0";
45
+ }
46
+
47
+ export async function detectAppId(): Promise<string> {
48
+ const win = window as any;
49
+
50
+ if (typeof window === "undefined") return "unknown.app";
51
+
52
+ if (win.Capacitor?.Plugins?.App) {
53
+ try {
54
+ const info = await win.Capacitor.Plugins.App.getInfo();
55
+ if (info.id) return info.id;
56
+ } catch (e) {
57
+ // ignore
58
+ }
59
+ }
60
+
61
+ const idKeys = ["APP_ID", "__APP_ID__", "BUNDLE_ID", "PACKAGE_NAME"];
62
+ for (const key of idKeys) {
63
+ if (win[key]) return win[key];
64
+ }
65
+
66
+ return "unknown.app";
67
+ }
package/src/engine.ts ADDED
@@ -0,0 +1,39 @@
1
+ import { BackendRule, VCDecision } from "./types";
2
+ import { compareVersions } from "./comparator";
3
+
4
+ export function evaluate(rule: BackendRule, currentVersion: string): VCDecision {
5
+ if (rule.killSwitch) {
6
+ return { action: "KILL_SWITCH", message: rule.message, raw: rule };
7
+ }
8
+
9
+ if (rule.maintenance) {
10
+ return { action: "MAINTENANCE", message: rule.message, raw: rule };
11
+ }
12
+
13
+ if (rule.forceUpdate && rule.minVersion) {
14
+ if (compareVersions(currentVersion, rule.minVersion) < 0) {
15
+ return {
16
+ action: "FORCE_UPDATE",
17
+ message: rule.message,
18
+ storeUrl: rule.storeUrl,
19
+ minVersion: rule.minVersion,
20
+ latestVersion: rule.latestVersion,
21
+ raw: rule
22
+ };
23
+ }
24
+ }
25
+
26
+ if (rule.softUpdate && rule.latestVersion) {
27
+ if (compareVersions(currentVersion, rule.latestVersion) < 0) {
28
+ return {
29
+ action: "SOFT_UPDATE",
30
+ message: rule.message,
31
+ storeUrl: rule.storeUrl,
32
+ latestVersion: rule.latestVersion,
33
+ raw: rule
34
+ };
35
+ }
36
+ }
37
+
38
+ return { action: "NONE", raw: rule };
39
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./types";
2
+ export * from "./sdk";
package/src/sdk.ts ADDED
@@ -0,0 +1,65 @@
1
+ import { VCConfig, VCDecision } from "./types";
2
+ import { fetchRules } from "./client";
3
+ import { detectAppId, detectPlatform, detectVersion } from "./detector";
4
+
5
+
6
+ export class VersionSDK {
7
+ private config!: VCConfig;
8
+
9
+ static init(config: VCConfig | string, apiKey?: string) {
10
+ const sdk = new VersionSDK();
11
+ if (typeof config === "string") {
12
+ sdk.config = {
13
+ backendUrl: config,
14
+ apiKey: apiKey,
15
+ debug: false
16
+ };
17
+ } else {
18
+ sdk.config = config;
19
+ }
20
+ return sdk;
21
+ }
22
+
23
+ static async check(config: VCConfig | string, apiKey?: string): Promise<VCDecision> {
24
+ return VersionSDK.init(config, apiKey).checkVersion();
25
+ }
26
+
27
+ async checkVersion(): Promise<VCDecision> {
28
+ try {
29
+ if (!this.config.platform) this.config.platform = await detectPlatform();
30
+ if (!this.config.version) this.config.version = await detectVersion();
31
+ if (!this.config.appId) this.config.appId = await detectAppId();
32
+
33
+ if (this.config.debug) {
34
+ console.log("[VC-SDK] Checking version for:", {
35
+ appId: this.config.appId,
36
+ platform: this.config.platform,
37
+ version: this.config.version
38
+ });
39
+ }
40
+
41
+ const response = await fetchRules(this.config);
42
+
43
+ if (this.config.debug) {
44
+ console.log("[VC-SDK] Backend Response:", response);
45
+ }
46
+
47
+ // The backend now returns the evaluated decision directly
48
+ const decision: VCDecision = {
49
+ action: response.status || "NONE",
50
+ message: response.message || response.title,
51
+ storeUrl: response.storeUrl,
52
+ minVersion: response.minVersion,
53
+ latestVersion: response.latestVersion,
54
+ raw: response
55
+ };
56
+
57
+ return decision;
58
+ } catch (e) {
59
+ if (this.config.debug) {
60
+ console.error("[VC-SDK] Error:", e);
61
+ }
62
+ return { action: "NONE" };
63
+ }
64
+ }
65
+ }
package/src/types.ts ADDED
@@ -0,0 +1,39 @@
1
+ export type Platform = "android" | "ios" | "web" | "all";
2
+
3
+ export type VCAction =
4
+ | "NONE"
5
+ | "SOFT_UPDATE"
6
+ | "FORCE_UPDATE"
7
+ | "MAINTENANCE"
8
+ | "KILL_SWITCH"
9
+ | "BLOCKED";
10
+
11
+ export interface VCConfig {
12
+ backendUrl: string;
13
+ apiKey?: string;
14
+ appId?: string;
15
+ platform?: Platform;
16
+ version?: string;
17
+ timeout?: number;
18
+ debug?: boolean;
19
+ }
20
+
21
+ export interface BackendRule {
22
+ minVersion?: string;
23
+ latestVersion?: string;
24
+ softUpdate?: boolean;
25
+ forceUpdate?: boolean;
26
+ maintenance?: boolean;
27
+ killSwitch?: boolean;
28
+ message?: string;
29
+ storeUrl?: string;
30
+ }
31
+
32
+ export interface VCDecision {
33
+ action: VCAction;
34
+ message?: string;
35
+ storeUrl?: string;
36
+ minVersion?: string;
37
+ latestVersion?: string;
38
+ raw?: any;
39
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2019",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Node",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "declaration": true,
9
+ "declarationMap": true,
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true
13
+ },
14
+ "include": [
15
+ "src/**/*"
16
+ ]
17
+ }
package/turbo.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://turbo.build/schema.json",
3
+ "tasks": {
4
+ "build": {
5
+ "dependsOn": [
6
+ "^build"
7
+ ],
8
+ "outputs": [
9
+ "dist/**"
10
+ ]
11
+ }
12
+ }
13
+ }