@otakit/capacitor-updater 2.1.2 → 2.3.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.
- package/README.md +142 -4
- package/android/src/main/java/com/otakit/updater/BundleCrypto.java +81 -0
- package/android/src/main/java/com/otakit/updater/BundleStore.java +26 -0
- package/android/src/main/java/com/otakit/updater/DeltaAssembler.java +416 -0
- package/android/src/main/java/com/otakit/updater/HashUtils.java +16 -0
- package/android/src/main/java/com/otakit/updater/ManifestClient.java +136 -4
- package/android/src/main/java/com/otakit/updater/ManifestVerifier.java +43 -0
- package/android/src/main/java/com/otakit/updater/UpdaterPlugin.java +457 -20
- package/dist/esm/definitions.d.ts +104 -4
- package/dist/esm/definitions.d.ts.map +1 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +7 -1
- package/dist/esm/web.d.ts.map +1 -1
- package/dist/esm/web.js +25 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +31 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +31 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/UpdaterPlugin/BundleCrypto.swift +91 -0
- package/ios/Sources/UpdaterPlugin/BundleStore.swift +30 -0
- package/ios/Sources/UpdaterPlugin/DeltaAssembler.swift +316 -0
- package/ios/Sources/UpdaterPlugin/ManifestClient.swift +98 -6
- package/ios/Sources/UpdaterPlugin/ManifestVerifier.swift +29 -0
- package/ios/Sources/UpdaterPlugin/UpdaterPlugin.swift +422 -21
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PluginListenerHandle } from '@capacitor/core';
|
|
1
2
|
/**
|
|
2
3
|
* Bundle status enum.
|
|
3
4
|
*/
|
|
@@ -36,14 +37,22 @@ export interface LatestVersion {
|
|
|
36
37
|
version: string;
|
|
37
38
|
/** Native compatibility lane for this update. */
|
|
38
39
|
runtimeVersion?: string;
|
|
39
|
-
/**
|
|
40
|
-
url
|
|
41
|
-
/** SHA-256 checksum */
|
|
40
|
+
/** Bundle download URL. Present for the 'zip' strategy; absent for 'deltas'. */
|
|
41
|
+
url?: string;
|
|
42
|
+
/** SHA-256 checksum: the zip hash for 'zip', the canonical filesHash for 'deltas'. */
|
|
42
43
|
sha256: string;
|
|
43
|
-
/** Bundle size in bytes */
|
|
44
|
+
/** Bundle size in bytes (total decompressed size for 'deltas') */
|
|
44
45
|
size: number;
|
|
45
46
|
/** Release history ID associated with this manifest */
|
|
46
47
|
releaseId: string;
|
|
48
|
+
/** Update strategy this manifest was published with. Defaults to 'zip'. */
|
|
49
|
+
strategy?: 'zip' | 'deltas';
|
|
50
|
+
/**
|
|
51
|
+
* True when the release is marked force-immediate: automatic flows apply
|
|
52
|
+
* and reload it on the next lifecycle event regardless of shadow or
|
|
53
|
+
* apply-staged policies. Manual API behavior is unchanged.
|
|
54
|
+
*/
|
|
55
|
+
forceImmediate?: boolean;
|
|
47
56
|
}
|
|
48
57
|
export interface OtaKitState {
|
|
49
58
|
current: BundleInfo;
|
|
@@ -56,6 +65,12 @@ export interface OtaKitManifestKey {
|
|
|
56
65
|
kid: string;
|
|
57
66
|
key: string;
|
|
58
67
|
}
|
|
68
|
+
export interface OtaKitBundleKey {
|
|
69
|
+
/** Key ID: first 16 hex chars of sha256(key). Printed by `otakit generate-encryption-key`. */
|
|
70
|
+
kid: string;
|
|
71
|
+
/** 256-bit AES key, base64. Inject from an env var at build time; do not commit. */
|
|
72
|
+
key: string;
|
|
73
|
+
}
|
|
59
74
|
export interface CheckNoUpdateResult {
|
|
60
75
|
kind: 'no_update';
|
|
61
76
|
}
|
|
@@ -76,6 +91,46 @@ export interface DownloadStagedResult {
|
|
|
76
91
|
bundle: BundleInfo;
|
|
77
92
|
}
|
|
78
93
|
export type DownloadResult = DownloadNoUpdateResult | DownloadStagedResult;
|
|
94
|
+
export interface ChannelInfo {
|
|
95
|
+
/** The effective release channel, or null for the base channel. */
|
|
96
|
+
channel: string | null;
|
|
97
|
+
/** Where the effective channel comes from: a runtime override or static config. */
|
|
98
|
+
source: 'override' | 'config';
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Payload for the `updateStaged` event: a bundle was downloaded, verified,
|
|
102
|
+
* and staged, ready to apply.
|
|
103
|
+
*/
|
|
104
|
+
export interface UpdateStagedEvent {
|
|
105
|
+
bundle: BundleInfo;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Payload for the `updateApplied` event: a newly activated bundle was
|
|
109
|
+
* confirmed healthy via notifyAppReady().
|
|
110
|
+
*/
|
|
111
|
+
export interface UpdateAppliedEvent {
|
|
112
|
+
bundle: BundleInfo;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Payload for the `downloadFailed` and `rollback` events.
|
|
116
|
+
*/
|
|
117
|
+
export interface UpdateFailedEvent {
|
|
118
|
+
version: string;
|
|
119
|
+
runtimeVersion?: string;
|
|
120
|
+
releaseId?: string;
|
|
121
|
+
channel?: string;
|
|
122
|
+
/** Stable failure reason, e.g. "hash_mismatch", "insufficient_disk_space", "notify_timeout". */
|
|
123
|
+
reason: string;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Update lifecycle events emitted by the plugin.
|
|
127
|
+
*
|
|
128
|
+
* Events fire only while the app process is alive. Reconcile with
|
|
129
|
+
* getState() and getLastFailure() on startup for anything that happened
|
|
130
|
+
* while no listener was attached (e.g. a bundle staged in a previous
|
|
131
|
+
* session, or a startup rollback).
|
|
132
|
+
*/
|
|
133
|
+
export type OtaKitEventName = 'updateAvailable' | 'updateStaged' | 'updateApplied' | 'downloadFailed' | 'rollback';
|
|
79
134
|
/**
|
|
80
135
|
* Plugin configuration for capacitor.config.ts.
|
|
81
136
|
*/
|
|
@@ -109,6 +164,11 @@ export interface OtaKitConfig {
|
|
|
109
164
|
cdnUrl?: string;
|
|
110
165
|
/** Custom manifest verification keys for self-hosted or custom trust. */
|
|
111
166
|
manifestKeys?: OtaKitManifestKey[];
|
|
167
|
+
/**
|
|
168
|
+
* Bundle decryption keys for end-to-end encrypted bundles.
|
|
169
|
+
* Array to allow rotation: ship old + new keys together during a transition.
|
|
170
|
+
*/
|
|
171
|
+
bundleKeys?: OtaKitBundleKey[];
|
|
112
172
|
/** Allow HTTP only for localhost development. Defaults to false. */
|
|
113
173
|
allowInsecureUrls?: boolean;
|
|
114
174
|
}
|
|
@@ -153,6 +213,40 @@ export interface OtaKitPlugin {
|
|
|
153
213
|
* Returns null if no failure has occurred.
|
|
154
214
|
*/
|
|
155
215
|
getLastFailure(): Promise<BundleInfo | null>;
|
|
216
|
+
/**
|
|
217
|
+
* Override the release channel at runtime (e.g. a "Join beta" toggle).
|
|
218
|
+
* Pass null to clear the override and return to the configured channel.
|
|
219
|
+
*
|
|
220
|
+
* The override is persisted across launches and takes effect on the next
|
|
221
|
+
* check/download/automatic cycle — it does not trigger anything by itself.
|
|
222
|
+
* Rejects invalid channel names without persisting.
|
|
223
|
+
*/
|
|
224
|
+
setChannel(options: {
|
|
225
|
+
channel: string | null;
|
|
226
|
+
}): Promise<void>;
|
|
227
|
+
/**
|
|
228
|
+
* Get the effective release channel and where it comes from
|
|
229
|
+
* (a runtime override or the static plugin config).
|
|
230
|
+
*/
|
|
231
|
+
getChannel(): Promise<ChannelInfo>;
|
|
232
|
+
/**
|
|
233
|
+
* Subscribe to update lifecycle events.
|
|
234
|
+
*
|
|
235
|
+
* Events fire only while the app is running. On startup, reconcile with
|
|
236
|
+
* getState() (anything staged while not listening) and getLastFailure()
|
|
237
|
+
* (startup rollbacks happen before JS boots and cannot reach a listener).
|
|
238
|
+
* apply() reloads the WebView and destroys the JS context, so attach
|
|
239
|
+
* `updateApplied` / `rollback` listeners early in app startup.
|
|
240
|
+
*/
|
|
241
|
+
addListener(eventName: 'updateAvailable', listenerFunc: (latest: LatestVersion) => void): Promise<PluginListenerHandle>;
|
|
242
|
+
addListener(eventName: 'updateStaged', listenerFunc: (event: UpdateStagedEvent) => void): Promise<PluginListenerHandle>;
|
|
243
|
+
addListener(eventName: 'updateApplied', listenerFunc: (event: UpdateAppliedEvent) => void): Promise<PluginListenerHandle>;
|
|
244
|
+
addListener(eventName: 'downloadFailed', listenerFunc: (event: UpdateFailedEvent) => void): Promise<PluginListenerHandle>;
|
|
245
|
+
addListener(eventName: 'rollback', listenerFunc: (event: UpdateFailedEvent) => void): Promise<PluginListenerHandle>;
|
|
246
|
+
/**
|
|
247
|
+
* Remove all registered event listeners.
|
|
248
|
+
*/
|
|
249
|
+
removeAllListeners(): Promise<void>;
|
|
156
250
|
}
|
|
157
251
|
export interface OtaKitBridgePlugin {
|
|
158
252
|
getState(): Promise<OtaKitState>;
|
|
@@ -162,5 +256,11 @@ export interface OtaKitBridgePlugin {
|
|
|
162
256
|
update(): Promise<void>;
|
|
163
257
|
notifyAppReady(): Promise<void>;
|
|
164
258
|
getLastFailure(): Promise<BundleInfo | null>;
|
|
259
|
+
setChannel(options: {
|
|
260
|
+
channel: string | null;
|
|
261
|
+
}): Promise<void>;
|
|
262
|
+
getChannel(): Promise<ChannelInfo>;
|
|
263
|
+
addListener(eventName: OtaKitEventName, listenerFunc: (event: unknown) => void): Promise<PluginListenerHandle>;
|
|
264
|
+
removeAllListeners(): Promise<void>;
|
|
165
265
|
}
|
|
166
266
|
//# sourceMappingURL=definitions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,YAAY;IACtB,+BAA+B;IAC/B,OAAO,YAAY;IACnB,iDAAiD;IACjD,OAAO,YAAY;IACnB,mCAAmC;IACnC,KAAK,UAAU;IACf,wBAAwB;IACxB,OAAO,YAAY;IACnB,yDAAyD;IACzD,KAAK,UAAU;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,+BAA+B;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mCAAmC;IACnC,MAAM,EAAE,YAAY,CAAC;IACrB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,
|
|
1
|
+
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,CAAN,IAAY,YAWX;AAXD,WAAY,YAAY;IACtB,+BAA+B;IAC/B,mCAAmB,CAAA;IACnB,iDAAiD;IACjD,mCAAmB,CAAA;IACnB,mCAAmC;IACnC,+BAAe,CAAA;IACf,wBAAwB;IACxB,mCAAmB,CAAA;IACnB,yDAAyD;IACzD,+BAAe,CAAA;AACjB,CAAC,EAXW,YAAY,KAAZ,YAAY,QAWvB"}
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAsB,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAsB,YAAY,EAA+B,MAAM,eAAe,CAAC;AAqBnG,QAAA,MAAM,MAAM,EAAE,YAgBb,CAAC;AAEF,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,CAAC"}
|
package/dist/esm/index.js
CHANGED
|
@@ -23,6 +23,12 @@ const OtaKit = {
|
|
|
23
23
|
update: () => NativeOtaKit.update(),
|
|
24
24
|
notifyAppReady: () => NativeOtaKit.notifyAppReady(),
|
|
25
25
|
getLastFailure: async () => normalizeNullable(await NativeOtaKit.getLastFailure()),
|
|
26
|
+
setChannel: (options) => NativeOtaKit.setChannel(options),
|
|
27
|
+
getChannel: () => NativeOtaKit.getChannel(),
|
|
28
|
+
// NativeOtaKit is the registerPlugin proxy, which implements Capacitor's
|
|
29
|
+
// listener API; this plain-object wrapper must forward it explicitly.
|
|
30
|
+
addListener: ((eventName, listenerFunc) => NativeOtaKit.addListener(eventName, listenerFunc)),
|
|
31
|
+
removeAllListeners: () => NativeOtaKit.removeAllListeners(),
|
|
26
32
|
};
|
|
27
33
|
export * from './definitions';
|
|
28
34
|
export { OtaKit };
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,YAAY,GAAG,cAAc,CAAqB,QAAQ,EAAE;IAChE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;CAC1D,CAAC,CAAC;AAEH;;GAEG;AACH,SAAS,aAAa,CAAC,GAAY;IACjC,OAAO,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAClF,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAI,KAAQ;IACpC,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AAC7C,CAAC;AAED,MAAM,MAAM,GAAiB;IAC3B,QAAQ,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE;IACvC,KAAK,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE;IACjC,QAAQ,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE;IACvC,KAAK,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE;IACjC,MAAM,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE;IACnC,cAAc,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE;IACnD,cAAc,EAAE,KAAK,IAAgC,EAAE,CACrD,iBAAiB,CAAC,MAAM,YAAY,CAAC,cAAc,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,YAAY,GAAG,cAAc,CAAqB,QAAQ,EAAE;IAChE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;CAC1D,CAAC,CAAC;AAEH;;GAEG;AACH,SAAS,aAAa,CAAC,GAAY;IACjC,OAAO,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAClF,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAI,KAAQ;IACpC,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AAC7C,CAAC;AAED,MAAM,MAAM,GAAiB;IAC3B,QAAQ,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE;IACvC,KAAK,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE;IACjC,QAAQ,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE;IACvC,KAAK,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE;IACjC,MAAM,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE;IACnC,cAAc,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE;IACnD,cAAc,EAAE,KAAK,IAAgC,EAAE,CACrD,iBAAiB,CAAC,MAAM,YAAY,CAAC,cAAc,EAAE,CAAC;IACxD,UAAU,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC;IACzD,UAAU,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE;IAC3C,yEAAyE;IACzE,sEAAsE;IACtE,WAAW,EAAE,CAAC,CAAC,SAA0B,EAAE,YAAsC,EAAE,EAAE,CACnF,YAAY,CAAC,WAAW,CAAC,SAAS,EAAE,YAAY,CAAC,CAAgC;IACnF,kBAAkB,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,kBAAkB,EAAE;CAC5D,CAAC;AAEF,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,CAAC"}
|
package/dist/esm/web.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WebPlugin } from '@capacitor/core';
|
|
2
|
-
import type { OtaKitBridgePlugin, BundleInfo, CheckResult, DownloadResult, OtaKitState } from './definitions';
|
|
2
|
+
import type { OtaKitBridgePlugin, BundleInfo, ChannelInfo, CheckResult, DownloadResult, OtaKitState } from './definitions';
|
|
3
3
|
/**
|
|
4
4
|
* Web implementation of the native bridge.
|
|
5
5
|
* Most methods are no-ops since OTA updates don't apply to web.
|
|
@@ -13,5 +13,11 @@ export declare class OtaKitWeb extends WebPlugin implements OtaKitBridgePlugin {
|
|
|
13
13
|
update(): Promise<void>;
|
|
14
14
|
notifyAppReady(): Promise<void>;
|
|
15
15
|
getLastFailure(): Promise<BundleInfo | null>;
|
|
16
|
+
private static readonly OVERRIDE_CHANNEL_KEY;
|
|
17
|
+
private static isValidChannelName;
|
|
18
|
+
setChannel(options: {
|
|
19
|
+
channel: string | null;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
getChannel(): Promise<ChannelInfo>;
|
|
16
22
|
}
|
|
17
23
|
//# sourceMappingURL=web.d.ts.map
|
package/dist/esm/web.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EACV,kBAAkB,EAClB,UAAU,EAEV,WAAW,EACX,cAAc,EACd,WAAW,EACZ,MAAM,eAAe,CAAC;AAEvB;;;GAGG;AACH,qBAAa,SAAU,SAAQ,SAAU,YAAW,kBAAkB;IACpE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAI7B;IAEI,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC;IAShC,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;IAK7B,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;IAKnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,cAAc,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EACV,kBAAkB,EAClB,UAAU,EAEV,WAAW,EACX,WAAW,EACX,cAAc,EACd,WAAW,EACZ,MAAM,eAAe,CAAC;AAEvB;;;GAGG;AACH,qBAAa,SAAU,SAAQ,SAAU,YAAW,kBAAkB;IACpE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAI7B;IAEI,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC;IAShC,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;IAK7B,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;IAKnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,cAAc,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAIlD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAA6B;IAEzE,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAQ3B,UAAU,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAa9D,UAAU,IAAI,OAAO,CAAC,WAAW,CAAC;CAOzC"}
|
package/dist/esm/web.js
CHANGED
|
@@ -38,5 +38,30 @@ export class OtaKitWeb extends WebPlugin {
|
|
|
38
38
|
async getLastFailure() {
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
41
|
+
static OVERRIDE_CHANNEL_KEY = 'otakit_override_channel';
|
|
42
|
+
static isValidChannelName(name) {
|
|
43
|
+
if (!/^[A-Za-z0-9._-]{1,64}$/.test(name) || name.includes('..') || name === '.') {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const lower = name.toLowerCase();
|
|
47
|
+
return lower !== 'base' && lower !== 'default';
|
|
48
|
+
}
|
|
49
|
+
async setChannel(options) {
|
|
50
|
+
if (options.channel === null) {
|
|
51
|
+
window.localStorage.removeItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!OtaKitWeb.isValidChannelName(options.channel)) {
|
|
55
|
+
throw new Error(`Invalid channel name '${options.channel}': use 1-64 letters, numbers, '.', '_' or '-' (reserved names: base, default)`);
|
|
56
|
+
}
|
|
57
|
+
window.localStorage.setItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY, options.channel);
|
|
58
|
+
}
|
|
59
|
+
async getChannel() {
|
|
60
|
+
const override = window.localStorage.getItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY);
|
|
61
|
+
if (override !== null) {
|
|
62
|
+
return { channel: override, source: 'override' };
|
|
63
|
+
}
|
|
64
|
+
return { channel: null, source: 'config' };
|
|
65
|
+
}
|
|
41
66
|
}
|
|
42
67
|
//# sourceMappingURL=web.js.map
|
package/dist/esm/web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAY5C;;;GAGG;AACH,MAAM,OAAO,SAAU,SAAQ,SAAS;IACrB,cAAc,GAAe;QAC5C,EAAE,EAAE,SAAS;QACb,OAAO,EAAE,OAAO;QAChB,MAAM,EAAE,SAAyB;KAClC,CAAC;IAEF,KAAK,CAAC,QAAQ;QACZ,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,cAAc;YAC5B,QAAQ,EAAE,IAAI,CAAC,cAAc;YAC7B,MAAM,EAAE,IAAI;YACZ,cAAc,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO;SAC5C,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,KAAK;QACT,OAAO,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QACvD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,OAAO,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;QAC1D,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,OAAO,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,uEAAuE;IACzE,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,MAAM,CAAU,oBAAoB,GAAG,yBAAyB,CAAC;IAEjE,MAAM,CAAC,kBAAkB,CAAC,IAAY;QAC5C,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YAChF,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,OAAO,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,SAAS,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,OAAmC;QAClD,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAC7B,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;YAC/D,OAAO;QACT,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACnD,MAAM,IAAI,KAAK,CACb,yBAAyB,OAAO,CAAC,OAAO,+EAA+E,CACxH,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,oBAAoB,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/E,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QAC7E,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;QACnD,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAC7C,CAAC"}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -43,6 +43,12 @@ const OtaKit = {
|
|
|
43
43
|
update: () => NativeOtaKit.update(),
|
|
44
44
|
notifyAppReady: () => NativeOtaKit.notifyAppReady(),
|
|
45
45
|
getLastFailure: async () => normalizeNullable(await NativeOtaKit.getLastFailure()),
|
|
46
|
+
setChannel: (options) => NativeOtaKit.setChannel(options),
|
|
47
|
+
getChannel: () => NativeOtaKit.getChannel(),
|
|
48
|
+
// NativeOtaKit is the registerPlugin proxy, which implements Capacitor's
|
|
49
|
+
// listener API; this plain-object wrapper must forward it explicitly.
|
|
50
|
+
addListener: ((eventName, listenerFunc) => NativeOtaKit.addListener(eventName, listenerFunc)),
|
|
51
|
+
removeAllListeners: () => NativeOtaKit.removeAllListeners(),
|
|
46
52
|
};
|
|
47
53
|
|
|
48
54
|
/**
|
|
@@ -84,6 +90,31 @@ class OtaKitWeb extends core.WebPlugin {
|
|
|
84
90
|
async getLastFailure() {
|
|
85
91
|
return null;
|
|
86
92
|
}
|
|
93
|
+
static OVERRIDE_CHANNEL_KEY = 'otakit_override_channel';
|
|
94
|
+
static isValidChannelName(name) {
|
|
95
|
+
if (!/^[A-Za-z0-9._-]{1,64}$/.test(name) || name.includes('..') || name === '.') {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const lower = name.toLowerCase();
|
|
99
|
+
return lower !== 'base' && lower !== 'default';
|
|
100
|
+
}
|
|
101
|
+
async setChannel(options) {
|
|
102
|
+
if (options.channel === null) {
|
|
103
|
+
window.localStorage.removeItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (!OtaKitWeb.isValidChannelName(options.channel)) {
|
|
107
|
+
throw new Error(`Invalid channel name '${options.channel}': use 1-64 letters, numbers, '.', '_' or '-' (reserved names: base, default)`);
|
|
108
|
+
}
|
|
109
|
+
window.localStorage.setItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY, options.channel);
|
|
110
|
+
}
|
|
111
|
+
async getChannel() {
|
|
112
|
+
const override = window.localStorage.getItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY);
|
|
113
|
+
if (override !== null) {
|
|
114
|
+
return { channel: override, source: 'override' };
|
|
115
|
+
}
|
|
116
|
+
return { channel: null, source: 'config' };
|
|
117
|
+
}
|
|
87
118
|
}
|
|
88
119
|
|
|
89
120
|
var web = /*#__PURE__*/Object.freeze({
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["/**\n * Bundle status enum.\n */\nexport var BundleStatus;\n(function (BundleStatus) {\n /** Factory-installed bundle */\n BundleStatus[\"BUILTIN\"] = \"builtin\";\n /** Downloaded and staged, awaiting activation */\n BundleStatus[\"PENDING\"] = \"pending\";\n /** Active but not yet confirmed */\n BundleStatus[\"TRIAL\"] = \"trial\";\n /** Confirmed working */\n BundleStatus[\"SUCCESS\"] = \"success\";\n /** Failed (hash mismatch, extraction error, rollback) */\n BundleStatus[\"ERROR\"] = \"error\";\n})(BundleStatus || (BundleStatus = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeOtaKit = registerPlugin('OtaKit', {\n web: () => import('./web').then((m) => new m.OtaKitWeb()),\n});\n/**\n * Check if result is an empty object (iOS returns {} instead of null)\n */\nfunction isEmptyObject(obj) {\n return obj !== null && typeof obj === 'object' && Object.keys(obj).length === 0;\n}\n/**\n * Wrapped plugin that normalizes null returns from native code.\n * iOS call.resolve() without arguments returns {} instead of null.\n */\nfunction normalizeNullable(value) {\n return isEmptyObject(value) ? null : value;\n}\nconst OtaKit = {\n getState: () => NativeOtaKit.getState(),\n check: () => NativeOtaKit.check(),\n download: () => NativeOtaKit.download(),\n apply: () => NativeOtaKit.apply(),\n update: () => NativeOtaKit.update(),\n notifyAppReady: () => NativeOtaKit.notifyAppReady(),\n getLastFailure: async () => normalizeNullable(await NativeOtaKit.getLastFailure()),\n};\nexport * from './definitions';\nexport { OtaKit };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\n/**\n * Web implementation of the native bridge.\n * Most methods are no-ops since OTA updates don't apply to web.\n */\nexport class OtaKitWeb extends WebPlugin {\n BUILTIN_BUNDLE = {\n id: 'builtin',\n version: '0.0.0',\n status: 'builtin',\n };\n async getState() {\n return {\n current: this.BUILTIN_BUNDLE,\n fallback: this.BUILTIN_BUNDLE,\n staged: null,\n builtinVersion: this.BUILTIN_BUNDLE.version,\n };\n }\n async check() {\n console.warn('OtaKit.check() is not supported on web');\n return { kind: 'no_update' };\n }\n async download() {\n console.warn('OtaKit.download() is not supported on web');\n return { kind: 'no_update' };\n }\n async apply() {\n console.warn('OtaKit.apply() is not supported on web');\n throw new Error('OtaKit.apply() is not supported on web');\n }\n async update() {\n await this.download();\n }\n async notifyAppReady() {\n // No-op on web, but don't warn - apps should call this unconditionally\n }\n async getLastFailure() {\n return null;\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["BundleStatus","registerPlugin","WebPlugin"],"mappings":";;;;AAAA;AACA;AACA;AACWA;AACX,CAAC,UAAU,YAAY,EAAE;AACzB;AACA,IAAI,YAAY,CAAC,SAAS,CAAC,GAAG,SAAS;AACvC;AACA,IAAI,YAAY,CAAC,SAAS,CAAC,GAAG,SAAS;AACvC;AACA,IAAI,YAAY,CAAC,OAAO,CAAC,GAAG,OAAO;AACnC;AACA,IAAI,YAAY,CAAC,SAAS,CAAC,GAAG,SAAS;AACvC;AACA,IAAI,YAAY,CAAC,OAAO,CAAC,GAAG,OAAO;AACnC,CAAC,EAAEA,oBAAY,KAAKA,oBAAY,GAAG,EAAE,CAAC,CAAC;;ACdvC,MAAM,YAAY,GAAGC,mBAAc,CAAC,QAAQ,EAAE;AAC9C,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;AAC7D,CAAC,CAAC;AACF;AACA;AACA;AACA,SAAS,aAAa,CAAC,GAAG,EAAE;AAC5B,IAAI,OAAO,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC;AACnF;AACA;AACA;AACA;AACA;AACA,SAAS,iBAAiB,CAAC,KAAK,EAAE;AAClC,IAAI,OAAO,aAAa,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,KAAK;AAC9C;AACK,MAAC,MAAM,GAAG;AACf,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC,QAAQ,EAAE;AAC3C,IAAI,KAAK,EAAE,MAAM,YAAY,CAAC,KAAK,EAAE;AACrC,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC,QAAQ,EAAE;AAC3C,IAAI,KAAK,EAAE,MAAM,YAAY,CAAC,KAAK,EAAE;AACrC,IAAI,MAAM,EAAE,MAAM,YAAY,CAAC,MAAM,EAAE;AACvC,IAAI,cAAc,EAAE,MAAM,YAAY,CAAC,cAAc,EAAE;AACvD,IAAI,cAAc,EAAE,YAAY,iBAAiB,CAAC,MAAM,YAAY,CAAC,cAAc,EAAE,CAAC;AACtF;;
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["/**\n * Bundle status enum.\n */\nexport var BundleStatus;\n(function (BundleStatus) {\n /** Factory-installed bundle */\n BundleStatus[\"BUILTIN\"] = \"builtin\";\n /** Downloaded and staged, awaiting activation */\n BundleStatus[\"PENDING\"] = \"pending\";\n /** Active but not yet confirmed */\n BundleStatus[\"TRIAL\"] = \"trial\";\n /** Confirmed working */\n BundleStatus[\"SUCCESS\"] = \"success\";\n /** Failed (hash mismatch, extraction error, rollback) */\n BundleStatus[\"ERROR\"] = \"error\";\n})(BundleStatus || (BundleStatus = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeOtaKit = registerPlugin('OtaKit', {\n web: () => import('./web').then((m) => new m.OtaKitWeb()),\n});\n/**\n * Check if result is an empty object (iOS returns {} instead of null)\n */\nfunction isEmptyObject(obj) {\n return obj !== null && typeof obj === 'object' && Object.keys(obj).length === 0;\n}\n/**\n * Wrapped plugin that normalizes null returns from native code.\n * iOS call.resolve() without arguments returns {} instead of null.\n */\nfunction normalizeNullable(value) {\n return isEmptyObject(value) ? null : value;\n}\nconst OtaKit = {\n getState: () => NativeOtaKit.getState(),\n check: () => NativeOtaKit.check(),\n download: () => NativeOtaKit.download(),\n apply: () => NativeOtaKit.apply(),\n update: () => NativeOtaKit.update(),\n notifyAppReady: () => NativeOtaKit.notifyAppReady(),\n getLastFailure: async () => normalizeNullable(await NativeOtaKit.getLastFailure()),\n setChannel: (options) => NativeOtaKit.setChannel(options),\n getChannel: () => NativeOtaKit.getChannel(),\n // NativeOtaKit is the registerPlugin proxy, which implements Capacitor's\n // listener API; this plain-object wrapper must forward it explicitly.\n addListener: ((eventName, listenerFunc) => NativeOtaKit.addListener(eventName, listenerFunc)),\n removeAllListeners: () => NativeOtaKit.removeAllListeners(),\n};\nexport * from './definitions';\nexport { OtaKit };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\n/**\n * Web implementation of the native bridge.\n * Most methods are no-ops since OTA updates don't apply to web.\n */\nexport class OtaKitWeb extends WebPlugin {\n BUILTIN_BUNDLE = {\n id: 'builtin',\n version: '0.0.0',\n status: 'builtin',\n };\n async getState() {\n return {\n current: this.BUILTIN_BUNDLE,\n fallback: this.BUILTIN_BUNDLE,\n staged: null,\n builtinVersion: this.BUILTIN_BUNDLE.version,\n };\n }\n async check() {\n console.warn('OtaKit.check() is not supported on web');\n return { kind: 'no_update' };\n }\n async download() {\n console.warn('OtaKit.download() is not supported on web');\n return { kind: 'no_update' };\n }\n async apply() {\n console.warn('OtaKit.apply() is not supported on web');\n throw new Error('OtaKit.apply() is not supported on web');\n }\n async update() {\n await this.download();\n }\n async notifyAppReady() {\n // No-op on web, but don't warn - apps should call this unconditionally\n }\n async getLastFailure() {\n return null;\n }\n static OVERRIDE_CHANNEL_KEY = 'otakit_override_channel';\n static isValidChannelName(name) {\n if (!/^[A-Za-z0-9._-]{1,64}$/.test(name) || name.includes('..') || name === '.') {\n return false;\n }\n const lower = name.toLowerCase();\n return lower !== 'base' && lower !== 'default';\n }\n async setChannel(options) {\n if (options.channel === null) {\n window.localStorage.removeItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY);\n return;\n }\n if (!OtaKitWeb.isValidChannelName(options.channel)) {\n throw new Error(`Invalid channel name '${options.channel}': use 1-64 letters, numbers, '.', '_' or '-' (reserved names: base, default)`);\n }\n window.localStorage.setItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY, options.channel);\n }\n async getChannel() {\n const override = window.localStorage.getItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY);\n if (override !== null) {\n return { channel: override, source: 'override' };\n }\n return { channel: null, source: 'config' };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["BundleStatus","registerPlugin","WebPlugin"],"mappings":"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"}
|
package/dist/plugin.js
CHANGED
|
@@ -42,6 +42,12 @@ var capacitorUpdater = (function (exports, core) {
|
|
|
42
42
|
update: () => NativeOtaKit.update(),
|
|
43
43
|
notifyAppReady: () => NativeOtaKit.notifyAppReady(),
|
|
44
44
|
getLastFailure: async () => normalizeNullable(await NativeOtaKit.getLastFailure()),
|
|
45
|
+
setChannel: (options) => NativeOtaKit.setChannel(options),
|
|
46
|
+
getChannel: () => NativeOtaKit.getChannel(),
|
|
47
|
+
// NativeOtaKit is the registerPlugin proxy, which implements Capacitor's
|
|
48
|
+
// listener API; this plain-object wrapper must forward it explicitly.
|
|
49
|
+
addListener: ((eventName, listenerFunc) => NativeOtaKit.addListener(eventName, listenerFunc)),
|
|
50
|
+
removeAllListeners: () => NativeOtaKit.removeAllListeners(),
|
|
45
51
|
};
|
|
46
52
|
|
|
47
53
|
/**
|
|
@@ -83,6 +89,31 @@ var capacitorUpdater = (function (exports, core) {
|
|
|
83
89
|
async getLastFailure() {
|
|
84
90
|
return null;
|
|
85
91
|
}
|
|
92
|
+
static OVERRIDE_CHANNEL_KEY = 'otakit_override_channel';
|
|
93
|
+
static isValidChannelName(name) {
|
|
94
|
+
if (!/^[A-Za-z0-9._-]{1,64}$/.test(name) || name.includes('..') || name === '.') {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const lower = name.toLowerCase();
|
|
98
|
+
return lower !== 'base' && lower !== 'default';
|
|
99
|
+
}
|
|
100
|
+
async setChannel(options) {
|
|
101
|
+
if (options.channel === null) {
|
|
102
|
+
window.localStorage.removeItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (!OtaKitWeb.isValidChannelName(options.channel)) {
|
|
106
|
+
throw new Error(`Invalid channel name '${options.channel}': use 1-64 letters, numbers, '.', '_' or '-' (reserved names: base, default)`);
|
|
107
|
+
}
|
|
108
|
+
window.localStorage.setItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY, options.channel);
|
|
109
|
+
}
|
|
110
|
+
async getChannel() {
|
|
111
|
+
const override = window.localStorage.getItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY);
|
|
112
|
+
if (override !== null) {
|
|
113
|
+
return { channel: override, source: 'override' };
|
|
114
|
+
}
|
|
115
|
+
return { channel: null, source: 'config' };
|
|
116
|
+
}
|
|
86
117
|
}
|
|
87
118
|
|
|
88
119
|
var web = /*#__PURE__*/Object.freeze({
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["/**\n * Bundle status enum.\n */\nexport var BundleStatus;\n(function (BundleStatus) {\n /** Factory-installed bundle */\n BundleStatus[\"BUILTIN\"] = \"builtin\";\n /** Downloaded and staged, awaiting activation */\n BundleStatus[\"PENDING\"] = \"pending\";\n /** Active but not yet confirmed */\n BundleStatus[\"TRIAL\"] = \"trial\";\n /** Confirmed working */\n BundleStatus[\"SUCCESS\"] = \"success\";\n /** Failed (hash mismatch, extraction error, rollback) */\n BundleStatus[\"ERROR\"] = \"error\";\n})(BundleStatus || (BundleStatus = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeOtaKit = registerPlugin('OtaKit', {\n web: () => import('./web').then((m) => new m.OtaKitWeb()),\n});\n/**\n * Check if result is an empty object (iOS returns {} instead of null)\n */\nfunction isEmptyObject(obj) {\n return obj !== null && typeof obj === 'object' && Object.keys(obj).length === 0;\n}\n/**\n * Wrapped plugin that normalizes null returns from native code.\n * iOS call.resolve() without arguments returns {} instead of null.\n */\nfunction normalizeNullable(value) {\n return isEmptyObject(value) ? null : value;\n}\nconst OtaKit = {\n getState: () => NativeOtaKit.getState(),\n check: () => NativeOtaKit.check(),\n download: () => NativeOtaKit.download(),\n apply: () => NativeOtaKit.apply(),\n update: () => NativeOtaKit.update(),\n notifyAppReady: () => NativeOtaKit.notifyAppReady(),\n getLastFailure: async () => normalizeNullable(await NativeOtaKit.getLastFailure()),\n};\nexport * from './definitions';\nexport { OtaKit };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\n/**\n * Web implementation of the native bridge.\n * Most methods are no-ops since OTA updates don't apply to web.\n */\nexport class OtaKitWeb extends WebPlugin {\n BUILTIN_BUNDLE = {\n id: 'builtin',\n version: '0.0.0',\n status: 'builtin',\n };\n async getState() {\n return {\n current: this.BUILTIN_BUNDLE,\n fallback: this.BUILTIN_BUNDLE,\n staged: null,\n builtinVersion: this.BUILTIN_BUNDLE.version,\n };\n }\n async check() {\n console.warn('OtaKit.check() is not supported on web');\n return { kind: 'no_update' };\n }\n async download() {\n console.warn('OtaKit.download() is not supported on web');\n return { kind: 'no_update' };\n }\n async apply() {\n console.warn('OtaKit.apply() is not supported on web');\n throw new Error('OtaKit.apply() is not supported on web');\n }\n async update() {\n await this.download();\n }\n async notifyAppReady() {\n // No-op on web, but don't warn - apps should call this unconditionally\n }\n async getLastFailure() {\n return null;\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["BundleStatus","registerPlugin","WebPlugin"],"mappings":";;;IAAA;IACA;IACA;AACWA;IACX,CAAC,UAAU,YAAY,EAAE;IACzB;IACA,IAAI,YAAY,CAAC,SAAS,CAAC,GAAG,SAAS;IACvC;IACA,IAAI,YAAY,CAAC,SAAS,CAAC,GAAG,SAAS;IACvC;IACA,IAAI,YAAY,CAAC,OAAO,CAAC,GAAG,OAAO;IACnC;IACA,IAAI,YAAY,CAAC,SAAS,CAAC,GAAG,SAAS;IACvC;IACA,IAAI,YAAY,CAAC,OAAO,CAAC,GAAG,OAAO;IACnC,CAAC,EAAEA,oBAAY,KAAKA,oBAAY,GAAG,EAAE,CAAC,CAAC;;ICdvC,MAAM,YAAY,GAAGC,mBAAc,CAAC,QAAQ,EAAE;IAC9C,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;IAC7D,CAAC,CAAC;IACF;IACA;IACA;IACA,SAAS,aAAa,CAAC,GAAG,EAAE;IAC5B,IAAI,OAAO,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC;IACnF;IACA;IACA;IACA;IACA;IACA,SAAS,iBAAiB,CAAC,KAAK,EAAE;IAClC,IAAI,OAAO,aAAa,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,KAAK;IAC9C;AACK,UAAC,MAAM,GAAG;IACf,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC,QAAQ,EAAE;IAC3C,IAAI,KAAK,EAAE,MAAM,YAAY,CAAC,KAAK,EAAE;IACrC,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC,QAAQ,EAAE;IAC3C,IAAI,KAAK,EAAE,MAAM,YAAY,CAAC,KAAK,EAAE;IACrC,IAAI,MAAM,EAAE,MAAM,YAAY,CAAC,MAAM,EAAE;IACvC,IAAI,cAAc,EAAE,MAAM,YAAY,CAAC,cAAc,EAAE;IACvD,IAAI,cAAc,EAAE,YAAY,iBAAiB,CAAC,MAAM,YAAY,CAAC,cAAc,EAAE,CAAC;IACtF;;
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["/**\n * Bundle status enum.\n */\nexport var BundleStatus;\n(function (BundleStatus) {\n /** Factory-installed bundle */\n BundleStatus[\"BUILTIN\"] = \"builtin\";\n /** Downloaded and staged, awaiting activation */\n BundleStatus[\"PENDING\"] = \"pending\";\n /** Active but not yet confirmed */\n BundleStatus[\"TRIAL\"] = \"trial\";\n /** Confirmed working */\n BundleStatus[\"SUCCESS\"] = \"success\";\n /** Failed (hash mismatch, extraction error, rollback) */\n BundleStatus[\"ERROR\"] = \"error\";\n})(BundleStatus || (BundleStatus = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeOtaKit = registerPlugin('OtaKit', {\n web: () => import('./web').then((m) => new m.OtaKitWeb()),\n});\n/**\n * Check if result is an empty object (iOS returns {} instead of null)\n */\nfunction isEmptyObject(obj) {\n return obj !== null && typeof obj === 'object' && Object.keys(obj).length === 0;\n}\n/**\n * Wrapped plugin that normalizes null returns from native code.\n * iOS call.resolve() without arguments returns {} instead of null.\n */\nfunction normalizeNullable(value) {\n return isEmptyObject(value) ? null : value;\n}\nconst OtaKit = {\n getState: () => NativeOtaKit.getState(),\n check: () => NativeOtaKit.check(),\n download: () => NativeOtaKit.download(),\n apply: () => NativeOtaKit.apply(),\n update: () => NativeOtaKit.update(),\n notifyAppReady: () => NativeOtaKit.notifyAppReady(),\n getLastFailure: async () => normalizeNullable(await NativeOtaKit.getLastFailure()),\n setChannel: (options) => NativeOtaKit.setChannel(options),\n getChannel: () => NativeOtaKit.getChannel(),\n // NativeOtaKit is the registerPlugin proxy, which implements Capacitor's\n // listener API; this plain-object wrapper must forward it explicitly.\n addListener: ((eventName, listenerFunc) => NativeOtaKit.addListener(eventName, listenerFunc)),\n removeAllListeners: () => NativeOtaKit.removeAllListeners(),\n};\nexport * from './definitions';\nexport { OtaKit };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\n/**\n * Web implementation of the native bridge.\n * Most methods are no-ops since OTA updates don't apply to web.\n */\nexport class OtaKitWeb extends WebPlugin {\n BUILTIN_BUNDLE = {\n id: 'builtin',\n version: '0.0.0',\n status: 'builtin',\n };\n async getState() {\n return {\n current: this.BUILTIN_BUNDLE,\n fallback: this.BUILTIN_BUNDLE,\n staged: null,\n builtinVersion: this.BUILTIN_BUNDLE.version,\n };\n }\n async check() {\n console.warn('OtaKit.check() is not supported on web');\n return { kind: 'no_update' };\n }\n async download() {\n console.warn('OtaKit.download() is not supported on web');\n return { kind: 'no_update' };\n }\n async apply() {\n console.warn('OtaKit.apply() is not supported on web');\n throw new Error('OtaKit.apply() is not supported on web');\n }\n async update() {\n await this.download();\n }\n async notifyAppReady() {\n // No-op on web, but don't warn - apps should call this unconditionally\n }\n async getLastFailure() {\n return null;\n }\n static OVERRIDE_CHANNEL_KEY = 'otakit_override_channel';\n static isValidChannelName(name) {\n if (!/^[A-Za-z0-9._-]{1,64}$/.test(name) || name.includes('..') || name === '.') {\n return false;\n }\n const lower = name.toLowerCase();\n return lower !== 'base' && lower !== 'default';\n }\n async setChannel(options) {\n if (options.channel === null) {\n window.localStorage.removeItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY);\n return;\n }\n if (!OtaKitWeb.isValidChannelName(options.channel)) {\n throw new Error(`Invalid channel name '${options.channel}': use 1-64 letters, numbers, '.', '_' or '-' (reserved names: base, default)`);\n }\n window.localStorage.setItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY, options.channel);\n }\n async getChannel() {\n const override = window.localStorage.getItem(OtaKitWeb.OVERRIDE_CHANNEL_KEY);\n if (override !== null) {\n return { channel: override, source: 'override' };\n }\n return { channel: null, source: 'config' };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["BundleStatus","registerPlugin","WebPlugin"],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import CryptoKit
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
enum BundleCryptoError: Error, LocalizedError {
|
|
5
|
+
case noMatchingKey(String)
|
|
6
|
+
case invalidParameter(String)
|
|
7
|
+
case decryptionFailed(String)
|
|
8
|
+
|
|
9
|
+
var errorDescription: String? {
|
|
10
|
+
switch self {
|
|
11
|
+
case let .noMatchingKey(kid):
|
|
12
|
+
return "no matching bundle key (kid \(kid))"
|
|
13
|
+
case let .invalidParameter(name):
|
|
14
|
+
return "invalid bundle encryption parameter: \(name)"
|
|
15
|
+
case let .decryptionFailed(detail):
|
|
16
|
+
return "bundle decryption failed: \(detail)"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// AES-256-GCM bundle decryption.
|
|
22
|
+
///
|
|
23
|
+
/// The CLI encrypts the zip with a random per-bundle DEK and wraps the DEK
|
|
24
|
+
/// under the app KEK; both ciphertexts carry the 16-byte GCM tag appended.
|
|
25
|
+
enum BundleCrypto {
|
|
26
|
+
private static let tagLength = 16
|
|
27
|
+
private static let keyLength = 32
|
|
28
|
+
|
|
29
|
+
static func unwrapDek(
|
|
30
|
+
kek: Data,
|
|
31
|
+
wrapNonceB64: String,
|
|
32
|
+
wrappedDekB64: String
|
|
33
|
+
) throws -> Data {
|
|
34
|
+
guard kek.count == keyLength else {
|
|
35
|
+
throw BundleCryptoError.invalidParameter("bundle key length")
|
|
36
|
+
}
|
|
37
|
+
guard let wrapNonce = Data(base64Encoded: wrapNonceB64) else {
|
|
38
|
+
throw BundleCryptoError.invalidParameter("wrapNonce")
|
|
39
|
+
}
|
|
40
|
+
guard let wrappedDek = Data(base64Encoded: wrappedDekB64),
|
|
41
|
+
wrappedDek.count == keyLength + tagLength else {
|
|
42
|
+
throw BundleCryptoError.invalidParameter("wrappedDek")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let dek: Data
|
|
46
|
+
do {
|
|
47
|
+
let sealedBox = try AES.GCM.SealedBox(
|
|
48
|
+
nonce: AES.GCM.Nonce(data: wrapNonce),
|
|
49
|
+
ciphertext: wrappedDek.prefix(keyLength),
|
|
50
|
+
tag: wrappedDek.suffix(tagLength)
|
|
51
|
+
)
|
|
52
|
+
dek = try AES.GCM.open(sealedBox, using: SymmetricKey(data: kek))
|
|
53
|
+
} catch {
|
|
54
|
+
throw BundleCryptoError.decryptionFailed("DEK unwrap: \(error.localizedDescription)")
|
|
55
|
+
}
|
|
56
|
+
guard dek.count == keyLength else {
|
|
57
|
+
throw BundleCryptoError.decryptionFailed("unwrapped DEK has unexpected length")
|
|
58
|
+
}
|
|
59
|
+
return dek
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static func decryptFile(
|
|
63
|
+
dek: Data,
|
|
64
|
+
nonceB64: String,
|
|
65
|
+
input: URL,
|
|
66
|
+
output: URL
|
|
67
|
+
) throws {
|
|
68
|
+
guard let nonce = Data(base64Encoded: nonceB64) else {
|
|
69
|
+
throw BundleCryptoError.invalidParameter("nonce")
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let ciphertextWithTag = try Data(contentsOf: input)
|
|
73
|
+
guard ciphertextWithTag.count > tagLength else {
|
|
74
|
+
throw BundleCryptoError.invalidParameter("ciphertext too short")
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
do {
|
|
78
|
+
let sealedBox = try AES.GCM.SealedBox(
|
|
79
|
+
nonce: AES.GCM.Nonce(data: nonce),
|
|
80
|
+
ciphertext: ciphertextWithTag.prefix(ciphertextWithTag.count - tagLength),
|
|
81
|
+
tag: ciphertextWithTag.suffix(tagLength)
|
|
82
|
+
)
|
|
83
|
+
let plaintext = try AES.GCM.open(sealedBox, using: SymmetricKey(data: dek))
|
|
84
|
+
try plaintext.write(to: output, options: .atomic)
|
|
85
|
+
} catch let error as BundleCryptoError {
|
|
86
|
+
throw error
|
|
87
|
+
} catch {
|
|
88
|
+
throw BundleCryptoError.decryptionFailed(error.localizedDescription)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -7,6 +7,7 @@ final class BundleStore {
|
|
|
7
7
|
static let stagedBundleId = "otakit_staged_bundle_id"
|
|
8
8
|
static let lastFailedBundleInfo = "otakit_last_failed_bundle_info"
|
|
9
9
|
static let lastResolvedRuntimeKey = "otakit_last_resolved_runtime_key"
|
|
10
|
+
static let overrideChannel = "otakit_override_channel"
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
private let defaults = UserDefaults.standard
|
|
@@ -41,6 +42,23 @@ final class BundleStore {
|
|
|
41
42
|
return directory
|
|
42
43
|
}()
|
|
43
44
|
|
|
45
|
+
/// Content-addressed file cache for the deltas strategy (`otakit_files/<sha256>`).
|
|
46
|
+
private(set) lazy var filesCacheDirectory: URL = {
|
|
47
|
+
let appSupport = fileManager.urls(
|
|
48
|
+
for: .applicationSupportDirectory,
|
|
49
|
+
in: .userDomainMask
|
|
50
|
+
).first!
|
|
51
|
+
let directory = appSupport.appendingPathComponent(
|
|
52
|
+
"otakit_files",
|
|
53
|
+
isDirectory: true
|
|
54
|
+
)
|
|
55
|
+
try? fileManager.createDirectory(
|
|
56
|
+
at: directory,
|
|
57
|
+
withIntermediateDirectories: true
|
|
58
|
+
)
|
|
59
|
+
return directory
|
|
60
|
+
}()
|
|
61
|
+
|
|
44
62
|
var builtinVersion: String {
|
|
45
63
|
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0"
|
|
46
64
|
}
|
|
@@ -206,6 +224,18 @@ final class BundleStore {
|
|
|
206
224
|
return try? decoder.decode(BundleInfo.self, from: data)
|
|
207
225
|
}
|
|
208
226
|
|
|
227
|
+
func getOverrideChannel() -> String? {
|
|
228
|
+
defaults.string(forKey: Keys.overrideChannel)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
func setOverrideChannel(_ channel: String?) {
|
|
232
|
+
if let channel {
|
|
233
|
+
defaults.set(channel, forKey: Keys.overrideChannel)
|
|
234
|
+
} else {
|
|
235
|
+
defaults.removeObject(forKey: Keys.overrideChannel)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
209
239
|
func getLastResolvedRuntimeKey() -> String? {
|
|
210
240
|
defaults.string(forKey: Keys.lastResolvedRuntimeKey)
|
|
211
241
|
}
|