flet-box 1.0.3 → 1.0.5

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 (172) hide show
  1. package/README.md +10 -0
  2. package/bin/cli.js +15 -0
  3. package/bin/commands/checkApi.js +761 -0
  4. package/bin/commands/create.js +77 -5
  5. package/bin/utils/dtsProps.js +191 -0
  6. package/bin/utils/moduleExports.js +165 -0
  7. package/bin/utils/neutralise.js +64 -0
  8. package/bin/utils/templates.js +366 -27
  9. package/docs/CONTRIBUTING.md +156 -0
  10. package/docs/README.md +68 -0
  11. package/docs/arquitectura.md +164 -0
  12. package/docs/cli/README.md +542 -0
  13. package/docs/guides/README.md +33 -0
  14. package/docs/guides/app-templates.md +197 -0
  15. package/docs/guides/compatibility.md +95 -0
  16. package/docs/guides/embedding.md +284 -0
  17. package/docs/guides/first-app.md +208 -0
  18. package/docs/guides/frontend-services.md +171 -0
  19. package/docs/guides/minification-and-protection.md +260 -0
  20. package/docs/guides/mobile-and-platforms.md +439 -0
  21. package/docs/guides/online-updates.md +196 -0
  22. package/docs/guides/router.md +282 -0
  23. package/docs/guides/state.md +283 -0
  24. package/docs/guides/syntax-philosophy.md +150 -0
  25. package/docs/guides/utilities.md +241 -0
  26. package/docs/guides/widget-structure.md +163 -0
  27. package/docs/server/README.md +68 -0
  28. package/docs/server/START_HERE.md +121 -0
  29. package/docs/server/api.md +225 -0
  30. package/docs/server/data-services.md +300 -0
  31. package/docs/server/deployment.md +113 -0
  32. package/docs/server/security.md +196 -0
  33. package/docs/services/README.md +79 -0
  34. package/docs/services/RamStore.md +84 -0
  35. package/docs/services/Session.md +84 -0
  36. package/docs/services/Storage.md +88 -0
  37. package/docs/services/http.md +86 -0
  38. package/docs/tools/README.md +138 -0
  39. package/docs/tools/animation.md +87 -0
  40. package/docs/tools/async-ids-log.md +80 -0
  41. package/docs/tools/device.md +132 -0
  42. package/docs/tools/introspection.md +67 -0
  43. package/docs/tools/lists.md +96 -0
  44. package/docs/tools/markdown.md +79 -0
  45. package/docs/tools/reactivity.md +288 -0
  46. package/docs/tools/state.md +103 -0
  47. package/docs/tools/styling.md +92 -0
  48. package/docs/tools/text-and-time.md +89 -0
  49. package/docs/tools/theme.md +102 -0
  50. package/docs/tools/validation.md +75 -0
  51. package/docs/tools/visual-effects.md +84 -0
  52. package/docs/widget/Accordion.md +144 -0
  53. package/docs/widget/AdMob.md +144 -0
  54. package/docs/widget/AdSense.md +133 -0
  55. package/docs/widget/AdaptiveScaffold.md +105 -0
  56. package/docs/widget/AlertDialog.md +103 -0
  57. package/docs/widget/AnimatedBox.md +130 -0
  58. package/docs/widget/AnimatedText.md +123 -0
  59. package/docs/widget/AppBar.md +118 -0
  60. package/docs/widget/Audio.md +135 -0
  61. package/docs/widget/Avatar.md +119 -0
  62. package/docs/widget/Badge.md +128 -0
  63. package/docs/widget/BottomNavigation.md +116 -0
  64. package/docs/widget/BottomSheet.md +125 -0
  65. package/docs/widget/Button.md +137 -0
  66. package/docs/widget/COMMON_PROPS.md +150 -0
  67. package/docs/widget/Card.md +125 -0
  68. package/docs/widget/Carousel.md +151 -0
  69. package/docs/widget/Chart.md +151 -0
  70. package/docs/widget/Checkbox.md +118 -0
  71. package/docs/widget/Chip.md +126 -0
  72. package/docs/widget/CircularBar.md +168 -0
  73. package/docs/widget/CircularChart.md +164 -0
  74. package/docs/widget/CodeViewer.md +130 -0
  75. package/docs/widget/CollapsibleSideBar.md +112 -0
  76. package/docs/widget/Column.md +129 -0
  77. package/docs/widget/Container.md +136 -0
  78. package/docs/widget/DataTable.md +150 -0
  79. package/docs/widget/Divider.md +109 -0
  80. package/docs/widget/DraggBox.md +129 -0
  81. package/docs/widget/Drawer.md +135 -0
  82. package/docs/widget/DrawerItem.md +98 -0
  83. package/docs/widget/Dropdown.md +139 -0
  84. package/docs/widget/DroppBox.md +152 -0
  85. package/docs/widget/FloatingActionButton.md +120 -0
  86. package/docs/widget/GridView.md +140 -0
  87. package/docs/widget/Icon.md +117 -0
  88. package/docs/widget/Image.md +125 -0
  89. package/docs/widget/Input.md +151 -0
  90. package/docs/widget/Inspector.md +92 -0
  91. package/docs/widget/InstallButton.md +128 -0
  92. package/docs/widget/ListTile.md +132 -0
  93. package/docs/widget/ListView.md +138 -0
  94. package/docs/widget/Markdown.md +175 -0
  95. package/docs/widget/MatrixRain.md +122 -0
  96. package/docs/widget/Modal.md +129 -0
  97. package/docs/widget/Pagination.md +135 -0
  98. package/docs/widget/ParallaxBox.md +128 -0
  99. package/docs/widget/ProgressBar.md +130 -0
  100. package/docs/widget/QRCode.md +117 -0
  101. package/docs/widget/README.md +188 -0
  102. package/docs/widget/Radio.md +123 -0
  103. package/docs/widget/Rating.md +131 -0
  104. package/docs/widget/Row.md +131 -0
  105. package/docs/widget/START_HERE.md +146 -0
  106. package/docs/widget/Scaffold.md +152 -0
  107. package/docs/widget/Skeleton.md +130 -0
  108. package/docs/widget/Slider.md +146 -0
  109. package/docs/widget/SnackBar.md +102 -0
  110. package/docs/widget/Stack.md +128 -0
  111. package/docs/widget/Stepper.md +145 -0
  112. package/docs/widget/Switch.md +118 -0
  113. package/docs/widget/Tabs.md +124 -0
  114. package/docs/widget/Text.md +133 -0
  115. package/docs/widget/Tooltip.md +145 -0
  116. package/docs/widget/TreeView.md +176 -0
  117. package/docs/widget/Video.md +127 -0
  118. package/docs/widget/_template.md +61 -0
  119. package/package.json +4 -2
  120. package/src/animations/AnimatedBox.js +32 -1
  121. package/src/animations/AnimatedText.js +7 -3
  122. package/src/animations/MatrixRain.js +37 -12
  123. package/src/animations/ParallaxBox.js +16 -3
  124. package/src/animations/animate.js +187 -58
  125. package/src/animations/index.js +1 -0
  126. package/src/core/runApp.js +13 -1
  127. package/src/create-index.sh +1 -1
  128. package/src/index.d.ts +227 -91
  129. package/src/index.js +1 -0
  130. package/src/navigations/AdaptiveScaffold.js +11 -2
  131. package/src/navigations/AppBar.js +8 -2
  132. package/src/navigations/BottomNavigation.js +24 -5
  133. package/src/navigations/DrawerItem.js +16 -3
  134. package/src/navigations/Scaffold.js +8 -1
  135. package/src/navigations/Tabs.js +40 -8
  136. package/src/tools/dimensions.js +11 -3
  137. package/src/tools/useState.js +139 -101
  138. package/src/utils/getWidgetProps.js +5 -3
  139. package/src/widget-factory/assignProps.js +30 -11
  140. package/src/widget-factory/processProps.js +17 -5
  141. package/src/widget-factory/reactiveValue.js +172 -0
  142. package/src/widget-factory/translateProps.js +777 -901
  143. package/src/widget-factory/warnings.js +15 -0
  144. package/src/widget-factory/widgetFactory.js +27 -0
  145. package/src/widgets/Accordion.js +6 -5
  146. package/src/widgets/AlertDialog.js +7 -5
  147. package/src/widgets/Audio.js +30 -8
  148. package/src/widgets/BottomSheet.js +24 -1
  149. package/src/widgets/Chart.js +4 -1
  150. package/src/widgets/Checkbox.js +16 -8
  151. package/src/widgets/CircularBar.js +12 -1
  152. package/src/widgets/CircularChart.js +4 -1
  153. package/src/widgets/CodeViewer.js +26 -8
  154. package/src/widgets/DataTable.js +11 -2
  155. package/src/widgets/Dropdown.js +5 -1
  156. package/src/widgets/FloatingActionButton.js +4 -7
  157. package/src/widgets/Image.js +7 -2
  158. package/src/widgets/Input.js +119 -6
  159. package/src/widgets/Inspector.js +16 -10
  160. package/src/widgets/ListView.js +80 -30
  161. package/src/widgets/Markdown.js +53 -8
  162. package/src/widgets/Modal.js +24 -1
  163. package/src/widgets/Pagination.js +6 -3
  164. package/src/widgets/ProgressBar.js +16 -13
  165. package/src/widgets/QRCode.js +9 -2
  166. package/src/widgets/Radio.js +11 -8
  167. package/src/widgets/Slider.js +26 -4
  168. package/src/widgets/SnackBar.js +20 -2
  169. package/src/widgets/Stack.js +36 -3
  170. package/src/widgets/Stepper.js +4 -3
  171. package/src/widgets/Switch.js +8 -5
  172. package/src/widgets/TreeView.js +3 -2
@@ -0,0 +1,439 @@
1
+ # Build FletBox for mobile and other platforms
2
+
3
+ > **Level: Advanced.** This page is for shipping — skip it on a first read. It follows [minification and protection](minification-and-protection.md) in the book's reading path.
4
+
5
+ FletBox applications can be built for the web and then packaged for mobile with Capacitor. The same frontend code can become a website, PWA, Android app, or iOS app.
6
+
7
+ ## The general flow
8
+
9
+ ```text
10
+ FletBox source
11
+ |
12
+ | npm run build
13
+ v
14
+ Web build / dist
15
+ |
16
+ | Capacitor sync
17
+ v
18
+ Android project iOS project
19
+ | |
20
+ APK / AAB App Store app
21
+ ```
22
+
23
+ Capacitor packages the compiled web application inside a native shell. You can add native Capacitor plugins later when the app needs device features.
24
+
25
+ ## Requirements
26
+
27
+ ### For every platform
28
+
29
+ - Node.js 18 or newer.
30
+ - npm.
31
+ - A working FletBox project.
32
+ - A production build that creates the directory configured as Capacitor's `webDir`.
33
+
34
+ ### For Android
35
+
36
+ - Android Studio.
37
+ - Android SDK.
38
+ - Java/JDK compatible with the Android Gradle project.
39
+ - An emulator or physical Android device for testing.
40
+
41
+ ### For iOS
42
+
43
+ - macOS.
44
+ - Xcode.
45
+ - CocoaPods when required by installed plugins.
46
+ - An Apple developer account for device distribution and App Store publishing.
47
+
48
+ ## Prepare the web build
49
+
50
+ From the FletBox project:
51
+
52
+ ```bash
53
+ npm install
54
+ npm run build
55
+ ```
56
+
57
+ Check that the build output exists before syncing Capacitor:
58
+
59
+ ```text
60
+ dist/
61
+ ├── index.html
62
+ └── src/
63
+ └── app.js
64
+ ```
65
+
66
+ Your Capacitor configuration must point `webDir` to that output directory, commonly `dist`:
67
+
68
+ ```json
69
+ {
70
+ "appId": "com.example.myapp",
71
+ "appName": "My FletBox App",
72
+ "webDir": "dist"
73
+ }
74
+ ```
75
+
76
+ The exact configuration file may be `capacitor.config.ts`, `capacitor.config.json`, or another format depending on how Capacitor was initialized.
77
+
78
+ ## Install Capacitor
79
+
80
+ ```bash
81
+ npm install @capacitor/core @capacitor/cli
82
+ ```
83
+
84
+ Initialize Capacitor:
85
+
86
+ ```bash
87
+ npx cap init
88
+ ```
89
+
90
+ The command asks for:
91
+
92
+ - App name.
93
+ - App package id, such as `com.example.myapp`.
94
+ - Web output directory.
95
+
96
+ Use a stable package id. Changing it later can create a different application in app stores.
97
+
98
+ ## Android
99
+
100
+ Projects created with `flet-box create` already include Capacitor's Android
101
+ configuration and commands for adding the platform, building the web assets,
102
+ and generating launcher icons from `assets/logo.png`. See the [CLI Android
103
+ workflow](../cli/README.md#create-an-android-app).
104
+
105
+ To build a debug APK from one of these generated projects, run
106
+ `flet-box build android`. It initializes the Android platform on first use,
107
+ synchronizes the web assets and launcher icons, and compiles the APK.
108
+ It installs missing npm project dependencies and prints the absolute APK path.
109
+ Android Studio, Java, and the Android SDK remain system prerequisites and must
110
+ be installed separately.
111
+
112
+ ### Add Android to the project
113
+
114
+ ```bash
115
+ npm install @capacitor/android
116
+ npx cap add android
117
+ npx cap sync android
118
+ ```
119
+
120
+ Run the web build before each sync when frontend files changed:
121
+
122
+ ```bash
123
+ npm run build
124
+ npx cap sync android
125
+ ```
126
+
127
+ ### Open Android Studio
128
+
129
+ ```bash
130
+ npx cap open android
131
+ ```
132
+
133
+ If Android Studio is installed in a custom location on macOS:
134
+
135
+ ```bash
136
+ export CAPACITOR_ANDROID_STUDIO_PATH="/Applications/Android Studio.app"
137
+ npx cap open android
138
+ ```
139
+
140
+ Remove the custom override when it is no longer needed:
141
+
142
+ ```bash
143
+ unset CAPACITOR_ANDROID_STUDIO_PATH
144
+ ```
145
+
146
+ ### Run on an emulator or device
147
+
148
+ You can open the project in Android Studio and use the Run button, or use Capacitor's command when a device is configured:
149
+
150
+ ```bash
151
+ npx cap run android
152
+ ```
153
+
154
+ For a physical device, enable developer mode and USB debugging on the device.
155
+
156
+ ### Build a debug APK
157
+
158
+ From the project root:
159
+
160
+ ```bash
161
+ npm run build
162
+ npx cap sync android
163
+ cd android
164
+ ./gradlew assembleDebug --warning-mode all
165
+ ```
166
+
167
+ On macOS, the generated debug APK is normally located at:
168
+
169
+ ```text
170
+ android/app/build/outputs/apk/debug/app-debug.apk
171
+ ```
172
+
173
+ The debug APK is for local testing and should not be published as the production release.
174
+
175
+ ### Build a release APK
176
+
177
+ ```bash
178
+ cd android
179
+ ./gradlew assembleRelease
180
+ ```
181
+
182
+ A release build must be signed before distribution. Configure a release signing key in the Android project and keep the keystore outside source control.
183
+
184
+ ### Build an Android App Bundle
185
+
186
+ Google Play normally expects an Android App Bundle:
187
+
188
+ ```bash
189
+ cd android
190
+ ./gradlew bundleRelease
191
+ ```
192
+
193
+ The output is normally located at:
194
+
195
+ ```text
196
+ android/app/build/outputs/bundle/release/app-release.aab
197
+ ```
198
+
199
+ ## iOS
200
+
201
+ Projects created with `flet-box create` include Capacitor's iOS platform and
202
+ scripts. On macOS, install the full Xcode app, an iOS simulator runtime, and
203
+ CocoaPods (`sudo gem install cocoapods` and `pod setup`). Then run
204
+ `flet-box build ios` to initialize the platform on first use, synchronize the
205
+ web assets, regenerate the app icon, launch images and launch screen from
206
+ `assets/logo.png`, and build an iOS Simulator `.app`. The command prints the
207
+ absolute app path, normally
208
+ `ios/build/Build/Products/Debug-iphonesimulator/App.app`, which installs with
209
+ `xcrun simctl install booted <path>`.
210
+
211
+ For real devices, `flet-box build ios --archive` writes
212
+ `ios/build/App.xcarchive` for the `iphoneos` SDK. The archive is unsigned: open
213
+ it in Xcode with a signing team to export an IPA.
214
+
215
+ The Command Line Tools are not enough. `xcodebuild` is only a stub there and
216
+ there is no simulator runtime, so the build command refuses to start and prints
217
+ the `xcode-select -s /Applications/Xcode.app/Contents/Developer` fix instead of
218
+ failing later inside Xcode. CocoaPods is checked too, because Capacitor links
219
+ its native layer with Pods.
220
+
221
+ ### Capacitor origin
222
+
223
+ Both platforms serve the app from a local HTTPS origin
224
+ (`https://localhost`), configured with `androidScheme` and `iosScheme` in
225
+ `capacitor.config.json`. A remote API has to allow that origin in its CORS
226
+ configuration.
227
+
228
+ ### Add iOS to the project
229
+
230
+ ```bash
231
+ npm install @capacitor/ios
232
+ npx cap add ios
233
+ npx cap sync ios
234
+ ```
235
+
236
+ After frontend changes:
237
+
238
+ ```bash
239
+ npm run build
240
+ npx cap sync ios
241
+ ```
242
+
243
+ ### Open Xcode
244
+
245
+ ```bash
246
+ npx cap open ios
247
+ ```
248
+
249
+ Select a simulator or connected device in Xcode, configure the signing team, and run the application.
250
+
251
+ For App Store distribution, configure:
252
+
253
+ - Bundle identifier.
254
+ - Signing team.
255
+ - Development and distribution certificates.
256
+ - Provisioning profile.
257
+ - App icons and launch assets.
258
+ - Privacy descriptions for any native plugins.
259
+
260
+ ## Capacitor assets
261
+
262
+ Install the asset helper:
263
+
264
+ ```bash
265
+ npm install -D @capacitor/assets
266
+ ```
267
+
268
+ Prepare source icons according to the Capacitor assets requirements, then generate platform assets with the relevant Capacitor assets command for your project version.
269
+
270
+ Review the generated icons in Android Studio and Xcode before publishing.
271
+
272
+ ## Update Capacitor
273
+
274
+ Update the core packages together:
275
+
276
+ ```bash
277
+ npm install @capacitor/core@latest @capacitor/cli@latest @capacitor/android@latest
278
+ ```
279
+
280
+ For iOS projects, include the iOS package too:
281
+
282
+ ```bash
283
+ npm install @capacitor/ios@latest
284
+ ```
285
+
286
+ Then synchronize native projects:
287
+
288
+ ```bash
289
+ npx cap sync android
290
+ npx cap sync ios
291
+ ```
292
+
293
+ Read the Capacitor migration notes before updating a major version.
294
+
295
+ ## macOS permission fixes
296
+
297
+ If Gradle cannot execute on macOS:
298
+
299
+ ```bash
300
+ cd android
301
+ chmod +x gradlew
302
+ xattr -c gradlew
303
+ ```
304
+
305
+ Then retry:
306
+
307
+ ```bash
308
+ ./gradlew assembleDebug --warning-mode all
309
+ ```
310
+
311
+ `chmod` makes the script executable. `xattr -c` removes macOS quarantine metadata from that local script. Only do this for project files you trust.
312
+
313
+ ## PWA alternative
314
+
315
+ A FletBox app can also be installed as a Progressive Web App without Capacitor:
316
+
317
+ ```javascript
318
+ import { installPWA, isPWAInstalled } from "flet-box";
319
+
320
+ if (!isPWAInstalled()) {
321
+ installPWA();
322
+ }
323
+ ```
324
+
325
+ A PWA needs:
326
+
327
+ - A valid web manifest.
328
+ - A registered service worker.
329
+ - HTTPS in production, except for localhost.
330
+ - Icons in the expected sizes.
331
+
332
+ PWA installation depends on browser and platform support.
333
+
334
+ ## Desktop options
335
+
336
+ Capacitor targets Android and iOS. For desktop packaging, consider a desktop shell such as Electron or Tauri around the same `dist` output.
337
+
338
+ The general idea remains:
339
+
340
+ ```text
341
+ npm run build
342
+ package dist with the desktop tool
343
+ ```
344
+
345
+ Desktop packaging requires its own application configuration, signing, and distribution process.
346
+
347
+ ## Updating the app after code changes
348
+
349
+ Use this cycle:
350
+
351
+ ```bash
352
+ npm run build
353
+ npx cap sync android
354
+ npx cap open android
355
+ ```
356
+
357
+ For iOS, replace the Android command:
358
+
359
+ ```bash
360
+ npm run build
361
+ npx cap sync ios
362
+ npx cap open ios
363
+ ```
364
+
365
+ `cap sync` copies the current web build and updates native dependencies. It does not replace the need to rebuild the web application first.
366
+
367
+ ## Troubleshooting
368
+
369
+ ### Capacitor cannot find the web directory
370
+
371
+ Check that:
372
+
373
+ 1. `npm run build` completed successfully.
374
+ 2. The output directory exists.
375
+ 3. `webDir` points to that directory.
376
+
377
+ ### Android Studio does not open
378
+
379
+ Set the path temporarily:
380
+
381
+ ```bash
382
+ export CAPACITOR_ANDROID_STUDIO_PATH="/Applications/Android Studio.app"
383
+ npx cap open android
384
+ ```
385
+
386
+ ### Gradle says permission denied
387
+
388
+ ```bash
389
+ cd android
390
+ chmod +x gradlew
391
+ xattr -c gradlew
392
+ ```
393
+
394
+ ### The native app shows an old version
395
+
396
+ Rebuild and synchronize:
397
+
398
+ ```bash
399
+ npm run build
400
+ npx cap sync android
401
+ ```
402
+
403
+ Then rebuild from Android Studio or Gradle.
404
+
405
+ ### The APK installs but the screen is blank
406
+
407
+ Check:
408
+
409
+ - The `webDir` configuration.
410
+ - That `dist/index.html` exists.
411
+ - That asset paths work from the packaged app.
412
+ - Browser console logs through Android Studio or Safari Web Inspector.
413
+ - That the app does not depend on a development server URL.
414
+
415
+ ### The app works in the browser but not on the device
416
+
417
+ Check absolute URLs, CORS, secure connections, permissions, and native plugin configuration. A mobile WebView is not identical to a desktop browser.
418
+
419
+ ## Release checklist
420
+
421
+ - Build from a clean working tree.
422
+ - Update the app version.
423
+ - Run `npm run build`.
424
+ - Run `npx cap sync`.
425
+ - Test on a real device.
426
+ - Verify icons, splash screen, orientation, and deep links.
427
+ - Configure release signing.
428
+ - Remove development URLs and debug credentials.
429
+ - Confirm API endpoints use HTTPS.
430
+ - Generate an APK for direct testing or an AAB for Google Play.
431
+ - Keep signing keys and certificates out of Git.
432
+
433
+ ---
434
+
435
+ ## Continue reading
436
+
437
+ - **Previous:** [Minify and protect a FletBox build](minification-and-protection.md)
438
+ - **Next:** [Compatibility and support](compatibility.md) — Appendix C
439
+ - **Index:** [Guides index](README.md) · [The FletBox Book](../README.md)
@@ -0,0 +1,196 @@
1
+ # Ship updates without the store (OTA / online updates)
2
+
3
+ > **Level: Advanced.** This page is for shipping — skip it on a first read. It follows [Add FletBox to an existing page](embedding.md) in the book's reading path.
4
+
5
+ FletBox apps are 100% web at their core: the Android/iOS build is a thin native shell (Capacitor) around a web bundle. That means the **web layer can update itself over the air**: you publish the app to the store **once**, and every later change to your UI/logic reaches installed apps the next time they open — no new store submission, no user download of the whole app.
6
+
7
+ This page explains the whole pipeline: what `flet-box release` does, what the `config.json` manifest contains, what the running app does with it, and — critically — **what OTA can and cannot change**.
8
+
9
+ ## The pipeline at a glance
10
+
11
+ ```text
12
+ you edit source code
13
+ |
14
+ | flet-box release (bumps version, rebuilds www/, git tag v1.0.1)
15
+ v
16
+ git tag v1.0.1 pushed to GitHub
17
+ |
18
+ | GitHub Actions (on: tags v*)
19
+ v
20
+ www/ deployed to GitHub Pages <- the ONLY place an update is published
21
+ |
22
+ | app opens (web or installed mobile app)
23
+ v
24
+ loader.js fetches <update.url>/config.json
25
+ |
26
+ | hash differs from the running bundle?
27
+ +-- no -> run the bundle already on the device
28
+ +-- yes -> show "Update available" dialog
29
+ | Accept
30
+ v
31
+ download bundle, verify sha256, store it, run it
32
+ ```
33
+
34
+ Three invariants make this safe:
35
+
36
+ 1. **Updates are published only by release tags.** Ordinary `git push` runs CI (so breakage is caught) but never publishes, so a half-finished test build can never reach your users.
37
+ 2. **The app verifies integrity.** A downloaded bundle is executed only if its sha256 matches the hash in `config.json`.
38
+ 3. **The app always launches.** Offline, a wrong URL, a hash mismatch, or a declined dialog all fall back to the bundle already on the device. OTA can never brick the app.
39
+
40
+ ## Cutting a release
41
+
42
+ Work on your code freely and build preview bundles as often as you like — **the version number does not move** on a normal build. When the code is stable and you want it to reach users:
43
+
44
+ ```bash
45
+ flet-box release # bump PATCH (1.0.0 -> 1.0.1), rebuild, commit, tag v1.0.1
46
+ flet-box release --minor # bump MINOR (1.0.0 -> 1.1.0)
47
+ flet-box release --major # bump MAJOR (1.0.0 -> 2.0.0)
48
+ flet-box release --push # also push the commit and the tag to origin
49
+ ```
50
+
51
+ `flet-box release` does, in order:
52
+
53
+ 1. Refuses to run if the working tree has uncommitted changes (the tag must capture exactly what you release).
54
+ 2. Bumps the version in `package.json` using the odometer scheme below.
55
+ 3. Rebuilds the production bundle (`www/`), which regenerates `www/config.json` with the new version and the sha256 of the new bundle.
56
+ 4. Commits the version bump and creates the annotated tag `vX.Y.Z`.
57
+ 5. Optionally pushes (`--push`), which triggers the CI deploy.
58
+
59
+ If the build fails, the version bump is rolled back so a failed release leaves no trace.
60
+
61
+ ### Odometer versioning
62
+
63
+ Versions are `MAJOR.MINOR.PATCH` where **MAJOR grows without bound** and **MINOR/PATCH are single digits (0–9)** that carry like an odometer:
64
+
65
+ ```text
66
+ 1.0.8 -> 1.0.9 -> 1.1.0 -> ... -> 1.9.9 -> 2.0.0 -> ... -> 9.9.9 -> 10.0.0
67
+ ```
68
+
69
+ When a position passes 9 it resets to 0 and carries into the position on its left. MAJOR never caps, so you can never run out of versions.
70
+
71
+ ## The `config.json` manifest
72
+
73
+ Every bundle ships a small manifest at `www/config.json`. This is what a running app fetches (a ~200-byte check, not the whole bundle):
74
+
75
+ ```json
76
+ {
77
+ "version": "1.0.1",
78
+ "hash": "494ca992aecf2de1e32680eae56405ec42baaa9b455248a4999df2edb76ad0b9",
79
+ "builtAt": "2026-10-11T19:53:24.524Z",
80
+ "bundle": "src/app.js",
81
+ "size": 36132,
82
+ "minNativeVersion": "0.0.0",
83
+ "update": {
84
+ "url": "https://kuko53348.github.io/my-app/",
85
+ "mode": "prompt",
86
+ "channel": "stable",
87
+ "mandatory": false,
88
+ "notes": "Fixed the login timeout."
89
+ }
90
+ }
91
+ ```
92
+
93
+ | Field | Meaning |
94
+ | --- | --- |
95
+ | `version` | Human-readable label from `package.json` (shown in the dialog). |
96
+ | `hash` | sha256 of the bundle. **This is what drives the update decision**, not the version. |
97
+ | `bundle` | Path of the bundle relative to the update URL. |
98
+ | `size` | Bundle size in bytes (shown in the dialog as KB). |
99
+ | `minNativeVersion` | Oldest native shell this bundle supports. Older shells are told to update from the store instead. |
100
+ | `update.url` | Where `config.json` and the bundle are published (your GitHub Pages site). |
101
+ | `update.mode` | `prompt` (ask the user, default) or `auto` (download silently). |
102
+ | `update.mandatory` | `true` hides the "Later" button so the update cannot be skipped. |
103
+ | `update.notes` | Release notes shown in the dialog. |
104
+ | `update.channel` | Label for rollout channels (e.g. `stable`, `beta`). |
105
+
106
+ ### Where the update URL comes from
107
+
108
+ `update.url` is resolved automatically, in this order:
109
+
110
+ 1. `update.url` in `fletbox.config.json` at your project root (explicit wins).
111
+ 2. Derived from the `repository` field of `package.json`, mapped to GitHub Pages: `https://<user>.github.io/<repo>/`.
112
+ 3. Derived from the **current project's** git remote `origin` (when it is a GitHub URL). This means `flet-box createBundle` always points at the project it is built in, never at the flet-box framework installed as a dependency.
113
+ 4. If none of these exist, OTA is **disabled** and the bundle simply runs what shipped in it.
114
+
115
+ To configure it by hand, create `fletbox.config.json`:
116
+
117
+ ```json
118
+ {
119
+ "update": {
120
+ "url": "https://kuko53348.github.io/my-app/",
121
+ "mode": "prompt",
122
+ "mandatory": false,
123
+ "notes": "",
124
+ "minNativeVersion": "0.0.0",
125
+ "channel": "stable"
126
+ }
127
+ }
128
+ ```
129
+
130
+ ## What the app does on launch
131
+
132
+ `www/loader.js` (wired in automatically as the module entry of every production bundle) runs **before** your app:
133
+
134
+ 1. Reads the shipped `config.json` to learn the update URL and the hash of the bundle on the device.
135
+ 2. Fetches the remote `config.json` with `cache: no-store` and a short timeout.
136
+ 3. If the remote hash equals the running hash, it logs "up to date" and starts the app immediately.
137
+ 4. If they differ, it either downloads silently (`mode: "auto"` or `mandatory`) or shows the **Update available** dialog with the version change, your release notes, and the download size, offering **Later** / **Update now**.
138
+ 5. On accept it downloads the bundle, recomputes its sha256, and only runs it if the hash matches. The bundle is then kept in Cache Storage so the next launch uses it even offline.
139
+
140
+ The dialog is plain HTML/CSS rendered by the loader itself, so it appears before the framework (and your UI) loads.
141
+
142
+ ## What OTA can and cannot change
143
+
144
+ OTA replaces the **web layer only** — the files inside `www/`.
145
+
146
+ | Updates over the air | Needs a new store submission |
147
+ | --- | --- |
148
+ | UI, screens, widgets, styles | New native plugins / Capacitor version |
149
+ | App logic and routing | New Android/iOS permissions |
150
+ | Text, images, fonts, assets in `www/` | Changes to `AndroidManifest.xml` / `Info.plist` |
151
+ | Bug fixes and new JS features | Native code or signing changes |
152
+ | Anything inside the bundle | Anything that changes the app's identity or purpose |
153
+
154
+ Rule of thumb: if the change lives in `src/` (your FletBox code) it ships OTA; if it lives in the native shell it goes through the store. The `minNativeVersion` field exists for the boundary case where a new web bundle needs a newer shell: older shells skip the OTA and you direct users to the store update.
155
+
156
+ ## Store policy (read this before relying on OTA)
157
+
158
+ Both stores permit over-the-air updates of **interpreted code** (JavaScript in a WebView counts), with conditions. This is a summary, not legal advice — check the current policy text before shipping.
159
+
160
+ - **Google Play** (Device and Network Abuse policy): apps may not download and execute code that changes the app's behavior outside a virtual machine/interpreter. Code running in an interpreter (your JS in the Capacitor WebView) **is** allowed. You may add features and fix bugs OTA; you may not push new APKs or escalate permissions OTA.
161
+ - **Apple (App Review Guideline 2.5.2)**: interpreted code may be updated OTA only if it does not change the **primary purpose** of the app. Patches, minor features, and assets are fine; turning a catalog app into a payment app (or anything that changes what the app fundamentally is) requires review.
162
+
163
+ Because of these rules, keep OTA updates within the app's existing purpose, and never use OTA to request new permissions or swap the native shell.
164
+
165
+ ## Offline behaviour and the service worker
166
+
167
+ The generated service worker uses a hybrid strategy so OTA and offline coexist:
168
+
169
+ - **Updatable shell** (`index.html`, `loader.js`, `config.json`, `src/app.js`): network-first. Online users always get fresh bytes, so a new release is picked up immediately and the worker never serves a stale `app.js`. Offline, the last cached copy is used.
170
+ - **Static assets** (fonts, icons, images, css): cache-first, since they are immutable per release.
171
+ - **Cross-origin requests** (your remote `config.json` and bundle) are left to the network and are never cached by the worker.
172
+
173
+ Downloaded OTA bundles live in a **separate** cache (`fletbox-ota`) owned by the loader, so a service-worker update never wipes an installed OTA bundle.
174
+
175
+ ## Requirements and troubleshooting
176
+
177
+ Requirements: a **public** GitHub repository, GitHub Pages enabled (Settings → Pages → Source = "GitHub Actions"), and the workflow from `flet-box create` (it already deploys `www/` on `v*` tags). GitHub Pages and Actions are free for public repos.
178
+
179
+ | Symptom | Likely cause |
180
+ | --- | --- |
181
+ | Build prints "THIS APP DOES NOT SUPPORT OTA UPDATES" | No public GitHub repo is reachable from the project (no `origin` remote, a non-GitHub remote, or nothing configured). The app still works — it runs the bundle it shipped with. Create the repo, push it, and rebuild. |
182
+ | No dialog, app runs old code | `update.url` missing/incorrect, or the tag was never pushed (CI never published). |
183
+ | `hash mismatch` in console | The published `config.json` and bundle are from different builds. Re-run `flet-box release` so both are published together. |
184
+ | Dialog appears but download fails | The Pages site is not reachable/CORS-blocked from the app origin. Pages serves with permissive headers; check you are not behind a proxy that strips them. |
185
+ | Update applies but reverts on next launch | Cache Storage unavailable (private browsing). The bundle runs once but cannot persist. |
186
+ | Want to force a clean state | Clear site data for the app origin (this removes the OTA cache and localStorage entry). |
187
+
188
+ Because the decision is hash-based, two builds of identical source produce the same hash and no spurious update is offered; only a real content change triggers the dialog.
189
+
190
+ ---
191
+
192
+ ## Continue reading
193
+
194
+ - **Previous:** [Add FletBox to an existing page](embedding.md)
195
+ - **Next:** none — you reached the end of the book.
196
+ - **Index:** [Guides index](README.md) · [The FletBox Book](../README.md)