@sigx/lynx-updates 0.6.1

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 (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +162 -0
  3. package/android/com/sigx/updates/UpdateDownloader.kt +154 -0
  4. package/android/com/sigx/updates/UpdateStore.kt +367 -0
  5. package/android/com/sigx/updates/UpdatesActivityHook.kt +25 -0
  6. package/android/com/sigx/updates/UpdatesBundleResolver.kt +18 -0
  7. package/android/com/sigx/updates/UpdatesEventBus.kt +54 -0
  8. package/android/com/sigx/updates/UpdatesLifecyclePublisher.kt +42 -0
  9. package/android/com/sigx/updates/UpdatesModule.kt +235 -0
  10. package/dist/controller.d.ts +31 -0
  11. package/dist/controller.d.ts.map +1 -0
  12. package/dist/controller.js +344 -0
  13. package/dist/controller.js.map +1 -0
  14. package/dist/events.d.ts +18 -0
  15. package/dist/events.d.ts.map +1 -0
  16. package/dist/events.js +61 -0
  17. package/dist/events.js.map +1 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +5 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/native.d.ts +41 -0
  23. package/dist/native.d.ts.map +1 -0
  24. package/dist/native.js +161 -0
  25. package/dist/native.js.map +1 -0
  26. package/dist/provider/static-manifest.d.ts +66 -0
  27. package/dist/provider/static-manifest.d.ts.map +1 -0
  28. package/dist/provider/static-manifest.js +173 -0
  29. package/dist/provider/static-manifest.js.map +1 -0
  30. package/dist/state.d.ts +23 -0
  31. package/dist/state.d.ts.map +1 -0
  32. package/dist/state.js +73 -0
  33. package/dist/state.js.map +1 -0
  34. package/dist/types.d.ts +203 -0
  35. package/dist/types.d.ts.map +1 -0
  36. package/dist/types.js +12 -0
  37. package/dist/types.js.map +1 -0
  38. package/dist/updates.d.ts +45 -0
  39. package/dist/updates.d.ts.map +1 -0
  40. package/dist/updates.js +67 -0
  41. package/dist/updates.js.map +1 -0
  42. package/dist/use-updates.d.ts +16 -0
  43. package/dist/use-updates.d.ts.map +1 -0
  44. package/dist/use-updates.js +29 -0
  45. package/dist/use-updates.js.map +1 -0
  46. package/ios/UpdateDownloader.swift +152 -0
  47. package/ios/UpdateStore.swift +286 -0
  48. package/ios/UpdatesBundleResolver.swift +15 -0
  49. package/ios/UpdatesEventBus.swift +59 -0
  50. package/ios/UpdatesLifecyclePublisher.swift +48 -0
  51. package/ios/UpdatesModule.swift +178 -0
  52. package/package.json +59 -0
  53. package/signalx-module.json +35 -0
@@ -0,0 +1,235 @@
1
+ package com.sigx.updates
2
+
3
+ import android.content.Context
4
+ import android.os.Handler
5
+ import android.os.Looper
6
+ import android.util.Log
7
+ import com.lynx.jsbridge.LynxMethod
8
+ import com.lynx.jsbridge.LynxModule
9
+ import com.lynx.react.bridge.Callback
10
+ import com.lynx.react.bridge.JavaOnlyMap
11
+ import com.lynx.react.bridge.ReadableMap
12
+ import com.lynx.tasm.TemplateData
13
+ import org.json.JSONObject
14
+ import java.io.File
15
+ import java.util.concurrent.Executors
16
+
17
+ /**
18
+ * JS bridge for OTA updates.
19
+ * JS usage: NativeModules.Updates.downloadUpdate({...}, callback)
20
+ *
21
+ * Downloads run on a single-thread executor (single-flight is also enforced
22
+ * in [UpdateDownloader]); everything else is cheap file/state work.
23
+ */
24
+ class UpdatesModule(context: Context) : LynxModule(context) {
25
+
26
+ companion object {
27
+ private const val TAG = "SigxUpdates"
28
+ private val executor = Executors.newSingleThreadExecutor { r ->
29
+ Thread(r, "sigx-updates").apply { isDaemon = true }
30
+ }
31
+ private val mainHandler = Handler(Looper.getMainLooper())
32
+ }
33
+
34
+ private fun error(message: String, code: String? = null): JavaOnlyMap {
35
+ val map = JavaOnlyMap()
36
+ map.putString("error", message)
37
+ if (code != null) map.putString("code", code)
38
+ return map
39
+ }
40
+
41
+ private fun ok(): JavaOnlyMap {
42
+ val map = JavaOnlyMap()
43
+ map.putBoolean("ok", true)
44
+ return map
45
+ }
46
+
47
+ @LynxMethod
48
+ fun getInstalledRuntimeVersion(): String {
49
+ return UpdateStore.installedRuntimeVersion(mContext)
50
+ }
51
+
52
+ @LynxMethod
53
+ fun getPlatform(): String = "android"
54
+
55
+ @LynxMethod
56
+ fun getCurrentUpdate(callback: Callback?) {
57
+ try {
58
+ val map = JavaOnlyMap()
59
+ val updateId = UpdateStore.launchedUpdateId
60
+ if (updateId != null) {
61
+ map.putString("updateId", updateId)
62
+ map.putBoolean("isEmbedded", false)
63
+ val metaFile = UpdateStore.updateJsonFile(mContext, updateId)
64
+ if (metaFile.exists()) {
65
+ try {
66
+ val meta = JSONObject(metaFile.readText())
67
+ map.putString("version", meta.optString("version"))
68
+ } catch (_: Exception) { /* metadata is best-effort */ }
69
+ }
70
+ } else {
71
+ map.putBoolean("isEmbedded", true)
72
+ }
73
+ map.putString("runtimeVersion", UpdateStore.installedRuntimeVersion(mContext))
74
+ // Store-shipped app version — providers receive it as
75
+ // UpdateCheckContext.embeddedVersion.
76
+ val versionName = runCatching {
77
+ mContext.packageManager.getPackageInfo(mContext.packageName, 0).versionName
78
+ }.getOrNull()
79
+ map.putString("embeddedVersion", versionName ?: "")
80
+ map.putBoolean("isFirstLaunchAfterUpdate", UpdateStore.isFirstLaunchAfterUpdate)
81
+ map.putBoolean("didRollBack", UpdateStore.didRollBack)
82
+ UpdateStore.rolledBackUpdateId?.let { map.putString("rolledBackUpdateId", it) }
83
+ callback?.invoke(map)
84
+ } catch (e: Exception) {
85
+ callback?.invoke(error(e.message ?: "getCurrentUpdate failed"))
86
+ }
87
+ }
88
+
89
+ @LynxMethod
90
+ fun getState(callback: Callback?) {
91
+ try {
92
+ val state = UpdateStore.readState(mContext)
93
+ val map = JavaOnlyMap()
94
+ if (state != null) {
95
+ map.putString("currentUpdateId", state.currentUpdateId ?: "")
96
+ map.putString("previousUpdateId", state.previousUpdateId ?: "")
97
+ map.putString("pendingUpdateId", state.pendingUpdateId ?: "")
98
+ map.putInt("pendingLaunchAttempts", state.pendingLaunchAttempts)
99
+ map.putInt("maxLaunchAttempts", state.maxLaunchAttempts)
100
+ map.putString("lastRollbackUpdateId", state.lastRollbackUpdateId ?: "")
101
+ map.putString("lastRollbackReason", state.lastRollbackReason ?: "")
102
+ }
103
+ map.putString("runningUpdateId", UpdateStore.launchedUpdateId ?: "")
104
+ callback?.invoke(map)
105
+ } catch (e: Exception) {
106
+ callback?.invoke(error(e.message ?: "getState failed"))
107
+ }
108
+ }
109
+
110
+ @LynxMethod
111
+ fun downloadUpdate(params: ReadableMap?, callback: Callback?) {
112
+ val url = params?.getString("url")
113
+ val sha256 = params?.getString("sha256")
114
+ val updateId = params?.getString("updateId")
115
+ val runtimeVersion = params?.getString("runtimeVersion")
116
+ val manifestJson = try { params?.getString("manifestJson") } catch (_: Exception) { null }
117
+ if (url.isNullOrEmpty() || sha256.isNullOrEmpty() || updateId.isNullOrEmpty()) {
118
+ callback?.invoke(error("url, sha256 and updateId are required"))
119
+ return
120
+ }
121
+
122
+ // Refuse incompatible bundles at the door — defense in depth on top
123
+ // of the JS-side check.
124
+ val installed = UpdateStore.installedRuntimeVersion(mContext)
125
+ if (!runtimeVersion.isNullOrEmpty() && runtimeVersion != installed) {
126
+ callback?.invoke(error(
127
+ "Update requires runtime $runtimeVersion but binary is $installed",
128
+ "E_RUNTIME_MISMATCH",
129
+ ))
130
+ return
131
+ }
132
+
133
+ val headers = mutableMapOf<String, String>()
134
+ try {
135
+ val headerMap = params.getMap("headers")
136
+ if (headerMap != null) {
137
+ val it = headerMap.keySetIterator()
138
+ while (it.hasNextKey()) {
139
+ val key = it.nextKey()
140
+ headerMap.getString(key)?.let { v -> headers[key] = v }
141
+ }
142
+ }
143
+ } catch (_: Exception) { /* headers optional */ }
144
+
145
+ executor.execute {
146
+ val result = UpdateDownloader.download(
147
+ mContext, url, sha256, updateId, headers, manifestJson ?: "{}")
148
+ if (result == null) {
149
+ callback?.invoke(ok())
150
+ } else {
151
+ val code = when {
152
+ result.startsWith("E_DOWNLOAD_IN_PROGRESS") -> "E_DOWNLOAD_IN_PROGRESS"
153
+ result.startsWith("E_HASH_MISMATCH") -> "hash-mismatch"
154
+ else -> null
155
+ }
156
+ callback?.invoke(error(result, code))
157
+ }
158
+ }
159
+ }
160
+
161
+ @LynxMethod
162
+ fun applyOnNextLaunch(updateId: String?, callback: Callback?) {
163
+ if (updateId.isNullOrEmpty()) {
164
+ callback?.invoke(error("updateId is required"))
165
+ return
166
+ }
167
+ val result = UpdateStore.stagePending(mContext, updateId)
168
+ callback?.invoke(if (result == null) ok() else error(result))
169
+ }
170
+
171
+ @LynxMethod
172
+ fun applyNow(updateId: String?, callback: Callback?) {
173
+ if (updateId.isNullOrEmpty()) {
174
+ callback?.invoke(error("updateId is required"))
175
+ return
176
+ }
177
+ val bundle = UpdateStore.bundleFile(mContext, updateId)
178
+ if (!bundle.exists()) {
179
+ callback?.invoke(error("Update $updateId is not on disk"))
180
+ return
181
+ }
182
+ val view = UpdateStore.currentView()
183
+ if (view == null) {
184
+ callback?.invoke(error("No LynxView attached — cannot reload in place", "E_NO_VIEW"))
185
+ return
186
+ }
187
+ // Stage first so a crash mid-reload still gets crash-guarded rollback
188
+ // (the reload bypasses the startup resolver).
189
+ UpdateStore.stagePending(mContext, updateId)
190
+ UpdateStore.recordReloadAttempt(mContext, updateId)
191
+ mainHandler.post {
192
+ try {
193
+ val bytes = File(bundle.absolutePath).readBytes()
194
+ view.reloadAndInit()
195
+ view.renderTemplateWithBaseUrl(bytes, TemplateData.empty(), "file://${bundle.absolutePath}")
196
+ // JS context is replaced — the callback never reaches the old
197
+ // context on success, which is the documented contract.
198
+ } catch (e: Exception) {
199
+ Log.e(TAG, "applyNow reload failed: ${e.message}")
200
+ callback?.invoke(error(e.message ?: "Reload failed", "apply-failed"))
201
+ }
202
+ }
203
+ }
204
+
205
+ @LynxMethod
206
+ fun markReady(callback: Callback?) {
207
+ try {
208
+ UpdateStore.markReady(mContext)
209
+ callback?.invoke(ok())
210
+ } catch (e: Exception) {
211
+ callback?.invoke(error(e.message ?: "markReady failed"))
212
+ }
213
+ }
214
+
215
+ @LynxMethod
216
+ fun setRollbackOptions(params: ReadableMap?, callback: Callback?) {
217
+ try {
218
+ val max = params?.getInt("maxFailedLaunches") ?: 2
219
+ UpdateStore.setMaxLaunchAttempts(mContext, max)
220
+ callback?.invoke(ok())
221
+ } catch (e: Exception) {
222
+ callback?.invoke(error(e.message ?: "setRollbackOptions failed"))
223
+ }
224
+ }
225
+
226
+ @LynxMethod
227
+ fun clearUpdates(callback: Callback?) {
228
+ try {
229
+ UpdateStore.clearAll(mContext)
230
+ callback?.invoke(ok())
231
+ } catch (e: Exception) {
232
+ callback?.invoke(error(e.message ?: "clearUpdates failed"))
233
+ }
234
+ }
235
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The updates state machine + mode orchestration. Modes are a thin strategy
3
+ * layer over the same primitives the public API exposes:
4
+ *
5
+ * silent → check + download on launch/foreground; stop at 'ready'
6
+ * immediate → silent, then apply() as soon as 'ready'
7
+ * manual → nothing automatic
8
+ *
9
+ * Mandatory updates (manifest.mandatory && honorMandatory) override every
10
+ * mode: state.mandatory blocks the UI, the download is forced (even in
11
+ * manual) and the update auto-applies once ready — the user is blocked
12
+ * anyway, so restarting immediately strictly beats staying blocked.
13
+ */
14
+ import { type UpdateCheckResult, type UpdateManifest, type UpdatesConfig } from './types.js';
15
+ /** @internal */
16
+ export declare function configure(raw: UpdatesConfig): void;
17
+ /** @internal */
18
+ export declare function checkForUpdate(): Promise<UpdateCheckResult>;
19
+ /** @internal */
20
+ export declare function download(manifest?: UpdateManifest): Promise<void>;
21
+ /** @internal */
22
+ export declare function apply(): Promise<void>;
23
+ /** Check, then (per mode policy) download and maybe apply. @internal */
24
+ export declare function checkAndMaybeDownload(): Promise<void>;
25
+ /** @internal */
26
+ export declare function markReady(): Promise<void>;
27
+ /** @internal */
28
+ export declare function clearUpdates(): Promise<void>;
29
+ /** Test-only: reset module-level config. @internal */
30
+ export declare function __resetForTests(): void;
31
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAmBH,OAAO,EAIH,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAEnB,KAAK,aAAa,EACrB,MAAM,YAAY,CAAC;AAkCpB,gBAAgB;AAChB,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA2DlD;AA2CD,gBAAgB;AAChB,wBAAsB,cAAc,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAkEjE;AAED,gBAAgB;AAChB,wBAAsB,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA2DvE;AAED,gBAAgB;AAChB,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAqB3C;AAYD,wEAAwE;AACxE,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAyB3D;AAED,gBAAgB;AAChB,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED,gBAAgB;AAChB,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAElD;AAED,sDAAsD;AACtD,wBAAgB,eAAe,IAAI,IAAI,CAStC"}
@@ -0,0 +1,344 @@
1
+ /**
2
+ * The updates state machine + mode orchestration. Modes are a thin strategy
3
+ * layer over the same primitives the public API exposes:
4
+ *
5
+ * silent → check + download on launch/foreground; stop at 'ready'
6
+ * immediate → silent, then apply() as soon as 'ready'
7
+ * manual → nothing automatic
8
+ *
9
+ * Mandatory updates (manifest.mandatory && honorMandatory) override every
10
+ * mode: state.mandatory blocks the UI, the download is forced (even in
11
+ * manual) and the update auto-applies once ready — the user is blocked
12
+ * anyway, so restarting immediately strictly beats staying blocked.
13
+ */
14
+ import { createLogger } from '@sigx/lynx-core';
15
+ import { addNativeUpdatesListener } from './events.js';
16
+ import { bakedChannel, getCurrentUpdate, getInstalledRuntimeVersion, getPlatform, nativeApplyNow, nativeApplyOnNextLaunch, nativeAvailable, nativeClearUpdates, nativeDownload, nativeMarkReady, nativeSetRollbackOptions, } from './native.js';
17
+ import { StaticManifestProvider } from './provider/static-manifest.js';
18
+ import { emit, store } from './state.js';
19
+ import { UpdatesError, } from './types.js';
20
+ const log = createLogger('updates');
21
+ let config = null;
22
+ let unsubscribeNative = null;
23
+ let warnedUnavailable = false;
24
+ let checkInFlight = null;
25
+ let downloadInFlight = null;
26
+ let downloadInFlightId = null;
27
+ let bootstrapScheduled = false;
28
+ function requireConfig() {
29
+ if (!config) {
30
+ throw new UpdatesError('not-configured', 'Call Updates.configure() before using the Updates API');
31
+ }
32
+ return config;
33
+ }
34
+ function warnUnavailableOnce() {
35
+ if (warnedUnavailable)
36
+ return;
37
+ warnedUnavailable = true;
38
+ log.warn('Updates native module not available — OTA updates are a no-op in this environment');
39
+ }
40
+ /** @internal */
41
+ export function configure(raw) {
42
+ const provider = 'checkForUpdate' in raw.provider
43
+ ? raw.provider
44
+ : new StaticManifestProvider(raw.provider);
45
+ config = {
46
+ provider,
47
+ channel: raw.channel ?? bakedChannel(),
48
+ mode: raw.mode ?? 'silent',
49
+ checkOn: raw.checkOn ?? ['launch'],
50
+ honorMandatory: raw.honorMandatory !== false,
51
+ autoMarkReady: raw.autoMarkReady !== false,
52
+ };
53
+ if (!nativeAvailable()) {
54
+ warnUnavailableOnce();
55
+ return;
56
+ }
57
+ // Native event channel: download progress + foreground re-checks.
58
+ unsubscribeNative?.();
59
+ unsubscribeNative = addNativeUpdatesListener((event) => {
60
+ if (event.kind === 'progress') {
61
+ // Late events (delivered after the download settled) must not
62
+ // resurrect progress state outside 'downloading'.
63
+ if (store.status !== 'downloading')
64
+ return;
65
+ const progress = { receivedBytes: event.receivedBytes, totalBytes: event.totalBytes };
66
+ store.progress = progress;
67
+ emit({ type: 'downloadProgress', progress });
68
+ return;
69
+ }
70
+ if (event.kind === 'foreground' && config?.mode !== 'manual' && config?.checkOn.includes('foreground')) {
71
+ // Don't interleave with an installation already in flight — a
72
+ // re-check would flip status to 'checking' mid-download and hide
73
+ // progress UI. The next foreground (or the post-apply launch)
74
+ // picks it up.
75
+ if (downloadInFlight || store.status === 'downloading' || store.status === 'applying') {
76
+ return;
77
+ }
78
+ void checkAndMaybeDownload();
79
+ }
80
+ });
81
+ if (raw.rollback?.maxFailedLaunches !== undefined) {
82
+ nativeSetRollbackOptions(raw.rollback.maxFailedLaunches).catch((err) => {
83
+ log.warn('setRollbackOptions failed:', err);
84
+ });
85
+ }
86
+ // Defer everything else off the boot path — never block first paint.
87
+ // Latched: re-configuring updates the config but must not re-run
88
+ // markReady / the launch check (that's the documented idempotence).
89
+ if (!bootstrapScheduled) {
90
+ bootstrapScheduled = true;
91
+ setTimeout(() => {
92
+ void bootstrap();
93
+ }, 0);
94
+ }
95
+ }
96
+ async function bootstrap() {
97
+ const cfg = requireConfig();
98
+ // Surface what we're running + rollback outcome from the resolver.
99
+ try {
100
+ const running = await getCurrentUpdate();
101
+ store.currentlyRunning = running;
102
+ if (running.didRollBack) {
103
+ emit({ type: 'rolledBack', fromUpdateId: running.rolledBackUpdateId ?? 'unknown' });
104
+ }
105
+ }
106
+ catch (err) {
107
+ log.warn('getCurrentUpdate failed:', err);
108
+ }
109
+ // Health signal — the pending update commits once JS is alive. Apps
110
+ // that gate on their own readiness set autoMarkReady: false and call
111
+ // Updates.markReady() themselves.
112
+ if (cfg.autoMarkReady) {
113
+ try {
114
+ await nativeMarkReady();
115
+ }
116
+ catch (err) {
117
+ log.warn('markReady failed:', err);
118
+ }
119
+ }
120
+ if (cfg.mode !== 'manual' && cfg.checkOn.includes('launch')) {
121
+ await checkAndMaybeDownload();
122
+ }
123
+ }
124
+ function buildContext(cfg) {
125
+ const running = store.currentlyRunning;
126
+ return {
127
+ platform: getPlatform(),
128
+ runtimeVersion: getInstalledRuntimeVersion(),
129
+ currentUpdateId: running.updateId,
130
+ embeddedVersion: running.embeddedVersion,
131
+ channel: cfg.channel,
132
+ };
133
+ }
134
+ /** @internal */
135
+ export async function checkForUpdate() {
136
+ const cfg = requireConfig();
137
+ if (checkInFlight)
138
+ return checkInFlight;
139
+ checkInFlight = (async () => {
140
+ store.status = 'checking';
141
+ store.error = null;
142
+ store.progress = null;
143
+ emit({ type: 'checkStarted' });
144
+ try {
145
+ const ctx = buildContext(cfg);
146
+ let result = await cfg.provider.checkForUpdate(ctx);
147
+ // Core re-validates the provider's answer: a manifest whose
148
+ // runtimeVersion doesn't match this binary is never installable,
149
+ // whatever the provider claims.
150
+ if (result.type === 'update-available' &&
151
+ result.manifest.runtimeVersion !== ctx.runtimeVersion) {
152
+ result = { type: 'incompatible', manifest: result.manifest };
153
+ }
154
+ switch (result.type) {
155
+ case 'up-to-date':
156
+ store.status = 'up-to-date';
157
+ store.manifest = null;
158
+ store.mandatory = false;
159
+ emit({ type: 'upToDate' });
160
+ break;
161
+ case 'incompatible':
162
+ store.status = 'incompatible';
163
+ store.manifest = result.manifest;
164
+ store.mandatory = false;
165
+ emit({ type: 'incompatibleUpdate', manifest: result.manifest });
166
+ break;
167
+ case 'update-available':
168
+ store.status = 'available';
169
+ store.manifest = result.manifest;
170
+ emit({ type: 'updateAvailable', manifest: result.manifest });
171
+ if (result.manifest.mandatory && cfg.honorMandatory) {
172
+ store.mandatory = true;
173
+ // Mandatory overrides every mode (including manual):
174
+ // the UI is blocked, so install immediately. Fire and
175
+ // forget — progress/errors surface via state/events.
176
+ void runMandatoryPipeline(result.manifest);
177
+ }
178
+ else {
179
+ // The blocking flag tracks the CURRENT best update —
180
+ // a previously-seen mandatory update that has since
181
+ // been superseded must not keep the UI blocked.
182
+ store.mandatory = false;
183
+ }
184
+ break;
185
+ }
186
+ return result;
187
+ }
188
+ catch (err) {
189
+ const error = err instanceof UpdatesError
190
+ ? err
191
+ : new UpdatesError('check-failed', `${err?.message ?? err}`);
192
+ store.status = 'error';
193
+ store.error = error;
194
+ emit({ type: 'error', error });
195
+ throw error;
196
+ }
197
+ finally {
198
+ checkInFlight = null;
199
+ }
200
+ })();
201
+ return checkInFlight;
202
+ }
203
+ /** @internal */
204
+ export async function download(manifest) {
205
+ const cfg = requireConfig();
206
+ const target = manifest ?? store.manifest;
207
+ if (!target) {
208
+ throw new UpdatesError('download-failed', 'No update to download — call checkForUpdate() first');
209
+ }
210
+ if (downloadInFlight) {
211
+ // Same update → join the in-flight download. A DIFFERENT update must
212
+ // not silently resolve against the wrong bytes.
213
+ if (downloadInFlightId === target.id)
214
+ return downloadInFlight;
215
+ throw new UpdatesError('download-in-progress', `Download of update ${downloadInFlightId} is in progress — cannot start ${target.id}`);
216
+ }
217
+ // Validate BEFORE arming the single-flight latch — a throw past this
218
+ // point would leave downloadInFlight set forever.
219
+ const ctx = buildContext(cfg);
220
+ if (target.runtimeVersion !== ctx.runtimeVersion) {
221
+ throw new UpdatesError('runtime-mismatch', `Update ${target.id} requires runtime ${target.runtimeVersion}; this binary is ${ctx.runtimeVersion} — a store release is needed`);
222
+ }
223
+ downloadInFlightId = target.id;
224
+ downloadInFlight = (async () => {
225
+ store.status = 'downloading';
226
+ store.manifest = target;
227
+ store.progress = { receivedBytes: 0, totalBytes: null };
228
+ store.error = null;
229
+ emit({ type: 'downloadStarted', manifest: target });
230
+ try {
231
+ const spec = cfg.provider.resolveDownload
232
+ ? await cfg.provider.resolveDownload(target, ctx)
233
+ : { url: target.bundleUrl, sha256: target.sha256 };
234
+ await nativeDownload(spec, target.id, target.runtimeVersion, JSON.stringify(target));
235
+ // Default activation policy: the staged bundle loads on the next
236
+ // cold launch. apply() upgrades that to an immediate reload.
237
+ await nativeApplyOnNextLaunch(target.id);
238
+ store.status = 'ready';
239
+ store.progress = null;
240
+ emit({ type: 'updateReady', manifest: target });
241
+ }
242
+ catch (err) {
243
+ const error = err instanceof UpdatesError
244
+ ? err
245
+ : new UpdatesError('download-failed', `${err?.message ?? err}`);
246
+ store.status = 'error';
247
+ store.progress = null;
248
+ store.error = error;
249
+ emit({ type: 'error', error });
250
+ throw error;
251
+ }
252
+ finally {
253
+ downloadInFlight = null;
254
+ downloadInFlightId = null;
255
+ }
256
+ })();
257
+ return downloadInFlight;
258
+ }
259
+ /** @internal */
260
+ export async function apply() {
261
+ requireConfig();
262
+ const target = store.manifest;
263
+ if (!target || store.status !== 'ready') {
264
+ throw new UpdatesError('apply-failed', 'No downloaded update to apply — download() must complete first');
265
+ }
266
+ store.status = 'applying';
267
+ emit({ type: 'applying' });
268
+ try {
269
+ // On success the JS context is replaced — this only returns on failure.
270
+ await nativeApplyNow(target.id);
271
+ }
272
+ catch (err) {
273
+ const error = err instanceof UpdatesError
274
+ ? err
275
+ : new UpdatesError('apply-failed', `${err?.message ?? err}`);
276
+ // The bundle stays staged for next launch, so 'ready' remains true.
277
+ store.status = 'ready';
278
+ store.error = error;
279
+ emit({ type: 'error', error });
280
+ throw error;
281
+ }
282
+ }
283
+ /** Forced install for mandatory updates — runs in every mode. */
284
+ async function runMandatoryPipeline(target) {
285
+ try {
286
+ await download(target);
287
+ await apply();
288
+ }
289
+ catch {
290
+ // surfaced via state/events; UpdateGate offers retry
291
+ }
292
+ }
293
+ /** Check, then (per mode policy) download and maybe apply. @internal */
294
+ export async function checkAndMaybeDownload() {
295
+ const cfg = requireConfig();
296
+ let result;
297
+ try {
298
+ result = await checkForUpdate();
299
+ }
300
+ catch {
301
+ return; // surfaced via state/events already
302
+ }
303
+ if (result.type !== 'update-available')
304
+ return;
305
+ // Mandatory updates were already dispatched inside checkForUpdate.
306
+ if (result.manifest.mandatory && cfg.honorMandatory)
307
+ return;
308
+ if (cfg.mode === 'manual')
309
+ return;
310
+ try {
311
+ await download(result.manifest);
312
+ }
313
+ catch {
314
+ return;
315
+ }
316
+ if (cfg.mode === 'immediate') {
317
+ try {
318
+ await apply();
319
+ }
320
+ catch {
321
+ // surfaced via state/events; staged for next launch regardless
322
+ }
323
+ }
324
+ }
325
+ /** @internal */
326
+ export async function markReady() {
327
+ await nativeMarkReady();
328
+ }
329
+ /** @internal */
330
+ export async function clearUpdates() {
331
+ await nativeClearUpdates();
332
+ }
333
+ /** Test-only: reset module-level config. @internal */
334
+ export function __resetForTests() {
335
+ config = null;
336
+ unsubscribeNative?.();
337
+ unsubscribeNative = null;
338
+ warnedUnavailable = false;
339
+ checkInFlight = null;
340
+ downloadInFlight = null;
341
+ downloadInFlightId = null;
342
+ bootstrapScheduled = false;
343
+ }
344
+ //# sourceMappingURL=controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.js","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Native → JS event channel for the Updates module (same GlobalEventEmitter
3
+ * pattern as `@sigx/lynx-background`'s `__sigxBackgroundFire`).
4
+ *
5
+ * Wire shape:
6
+ * { kind: 'progress', receivedBytes: number, totalBytes: number | null }
7
+ * { kind: 'foreground' }
8
+ */
9
+ export type NativeUpdatesEvent = {
10
+ kind: 'progress';
11
+ receivedBytes: number;
12
+ totalBytes: number | null;
13
+ } | {
14
+ kind: 'foreground';
15
+ };
16
+ export declare function addNativeUpdatesListener(cb: (event: NativeUpdatesEvent) => void): () => void;
17
+ export declare const __EVENT_CHANNEL_FOR_TESTS = "__sigxUpdatesEvent";
18
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,kBAAkB,GACxB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,CAAC;AAwC7B,wBAAgB,wBAAwB,CACpC,EAAE,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,GACxC,MAAM,IAAI,CAiBZ;AAED,eAAO,MAAM,yBAAyB,uBAAgB,CAAC"}