native-update 1.4.9 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. package/Readme.md +35 -9
  2. package/android/manifest-additions.xml +28 -0
  3. package/android/src/main/AndroidManifest.xml +3 -7
  4. package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundNotificationManager.kt +21 -13
  5. package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdatePlugin.kt +15 -0
  6. package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdateWorker.kt +23 -7
  7. package/android/src/main/java/com/aoneahsan/nativeupdate/LiveUpdatePlugin.kt +346 -31
  8. package/android/src/main/java/com/aoneahsan/nativeupdate/NativeUpdatePlugin.kt +32 -34
  9. package/android/src/main/java/com/aoneahsan/nativeupdate/NotificationActionReceiver.kt +10 -1
  10. package/android/src/main/java/com/aoneahsan/nativeupdate/SecurityManager.kt +18 -18
  11. package/cli/AGENTS.md +43 -0
  12. package/cli/CLAUDE.md +65 -0
  13. package/cli/commands/backend-create.js +56 -15
  14. package/cli/commands/bundle-create.js +39 -7
  15. package/cli/commands/monitor.js +72 -51
  16. package/cli/index.js +10 -24
  17. package/cli/package.json +11 -3
  18. package/dist/esm/app-review/app-review-manager.js +0 -1
  19. package/dist/esm/app-review/index.js +0 -1
  20. package/dist/esm/app-review/platform-review-handler.js +0 -1
  21. package/dist/esm/app-review/review-conditions-checker.js +0 -1
  22. package/dist/esm/app-review/review-rate-limiter.js +0 -1
  23. package/dist/esm/app-review/types.js +0 -1
  24. package/dist/esm/app-update/app-update-checker.js +0 -1
  25. package/dist/esm/app-update/app-update-installer.d.ts +1 -0
  26. package/dist/esm/app-update/app-update-installer.js +19 -8
  27. package/dist/esm/app-update/app-update-manager.js +0 -1
  28. package/dist/esm/app-update/app-update-notifier.js +0 -1
  29. package/dist/esm/app-update/index.js +0 -1
  30. package/dist/esm/app-update/platform-app-update.js +12 -3
  31. package/dist/esm/app-update/types.js +0 -1
  32. package/dist/esm/background-update/background-scheduler.js +0 -1
  33. package/dist/esm/background-update/index.js +0 -1
  34. package/dist/esm/background-update/notification-manager.js +0 -1
  35. package/dist/esm/config/support.js +0 -1
  36. package/dist/esm/core/analytics.d.ts +13 -0
  37. package/dist/esm/core/analytics.js +15 -1
  38. package/dist/esm/core/cache-manager.js +0 -1
  39. package/dist/esm/core/config.d.ts +6 -30
  40. package/dist/esm/core/config.js +1 -8
  41. package/dist/esm/core/errors.d.ts +1 -0
  42. package/dist/esm/core/errors.js +1 -1
  43. package/dist/esm/core/event-emitter.d.ts +0 -3
  44. package/dist/esm/core/event-emitter.js +8 -3
  45. package/dist/esm/core/logger.js +0 -1
  46. package/dist/esm/core/performance.js +0 -1
  47. package/dist/esm/core/plugin-manager.js +3 -1
  48. package/dist/esm/core/security.d.ts +22 -3
  49. package/dist/esm/core/security.js +46 -7
  50. package/dist/esm/definitions.d.ts +13 -40
  51. package/dist/esm/definitions.js +0 -1
  52. package/dist/esm/index.d.ts +3 -4
  53. package/dist/esm/index.js +3 -3
  54. package/dist/esm/live-update/bundle-manager.d.ts +8 -0
  55. package/dist/esm/live-update/bundle-manager.js +20 -1
  56. package/dist/esm/live-update/certificate-pinning.js +0 -1
  57. package/dist/esm/live-update/delta-processor.d.ts +1 -1
  58. package/dist/esm/live-update/delta-processor.js +3 -1
  59. package/dist/esm/live-update/download-manager.d.ts +36 -5
  60. package/dist/esm/live-update/download-manager.js +70 -24
  61. package/dist/esm/live-update/rollout-checker.d.ts +1 -1
  62. package/dist/esm/live-update/rollout-checker.js +5 -6
  63. package/dist/esm/live-update/update-manager.d.ts +12 -1
  64. package/dist/esm/live-update/update-manager.js +38 -11
  65. package/dist/esm/live-update/version-manager.d.ts +9 -12
  66. package/dist/esm/live-update/version-manager.js +40 -68
  67. package/dist/esm/plugin.js +82 -89
  68. package/dist/esm/security/crypto.js +11 -7
  69. package/dist/esm/security/validator.js +0 -1
  70. package/dist/esm/types/rollout.d.ts +85 -0
  71. package/dist/esm/types/rollout.js +25 -0
  72. package/dist/esm/web.d.ts +26 -1
  73. package/dist/esm/web.js +214 -75
  74. package/dist/plugin.cjs.js +1 -2
  75. package/dist/plugin.esm.js +1 -2
  76. package/dist/plugin.js +2 -3
  77. package/docs/AGENTS.md +38 -0
  78. package/docs/CHANGELOG.md +275 -0
  79. package/docs/CLAUDE.md +101 -0
  80. package/docs/MANUAL-TASKS.md +17 -0
  81. package/docs/MARKETING_WEBSITE_TRACKER.md +18 -2
  82. package/docs/MIGRATION.md +170 -0
  83. package/docs/PACKAGES.md +167 -0
  84. package/docs/PERFORMANCE.md +110 -0
  85. package/docs/PROJECT_COMPLETION_TRACKER.md +4 -35
  86. package/docs/README.md +12 -2
  87. package/docs/REMAINING_FEATURES.md +123 -76
  88. package/docs/ROADMAP.md +200 -120
  89. package/docs/SECURITY-IF-REPO-PUBLIC.md +232 -0
  90. package/docs/TESTING_REQUIREMENTS.md +1 -112
  91. package/docs/ai-knowledge-base/00-project-profile.md +38 -0
  92. package/docs/ai-knowledge-base/01-features.md +46 -0
  93. package/docs/ai-knowledge-base/02-routes.md +72 -0
  94. package/docs/ai-knowledge-base/03-tech-stack.md +57 -0
  95. package/docs/ai-knowledge-base/README.md +23 -0
  96. package/docs/analytics-tracking.md +37 -0
  97. package/docs/capacitor-official-plugins.md +46 -0
  98. package/docs/capawesome-plugins.md +29 -0
  99. package/docs/deployment/HOSTINGER_DEPLOY.md +370 -0
  100. package/docs/deployment/PRODUCTION-DEPLOY-CHECKLIST.md +184 -0
  101. package/docs/docs-site/plan.md +201 -0
  102. package/docs/docs-site/scope.md +127 -0
  103. package/docs/docs-site/tracker.json +235 -0
  104. package/docs/error-handling-tracking.md +37 -0
  105. package/docs/features/laravel-nova-backend/ASSESSMENT-SUMMARY.md +96 -0
  106. package/docs/features/laravel-nova-backend/IMPLEMENTATION-PLAN.md +504 -0
  107. package/docs/features/laravel-nova-backend/progress-tracker.json +184 -0
  108. package/docs/guides/testing-guide.md +15 -41
  109. package/docs/package-audit.md +41 -0
  110. package/docs/play-store/ASO-METADATA.md +5 -0
  111. package/docs/play-store/DATA-SAFETY.md +5 -0
  112. package/docs/play-store/DECLARATIONS.md +5 -0
  113. package/docs/play-store/README.md +27 -0
  114. package/docs/play-store/RELEASE-NOTES.md +99 -0
  115. package/docs/play-store/RELEASE-STEPS.md +6 -0
  116. package/docs/play-store/STORE-LISTING.md +7 -0
  117. package/docs/play-store/SUBMISSION-CHECKLIST.md +5 -0
  118. package/docs/production-readiness.md +1 -1
  119. package/docs/project-audit/2026-05-16/00-FINALIZATION-PLAN.md +123 -0
  120. package/docs/project-audit/2026-05-16/01-BATCH-PLAN.md +227 -0
  121. package/docs/project-audit/2026-05-16/02-AUDIT-FINDINGS.md +144 -0
  122. package/docs/project-audit/2026-05-16/sessions/batch-01.md +42 -0
  123. package/docs/project-audit/2026-05-16/sessions/batch-02.md +74 -0
  124. package/docs/project-audit/2026-05-16/sessions/batch-03.md +71 -0
  125. package/docs/project-audit/2026-05-16/sessions/batch-04.md +88 -0
  126. package/docs/project-audit/2026-05-16/sessions/batch-05.md +63 -0
  127. package/docs/project-audit/2026-05-16/sessions/batch-06.md +103 -0
  128. package/docs/project-audit/2026-05-16/sessions/batch-07.md +61 -0
  129. package/docs/project-audit/2026-05-16/sessions/batch-08.md +101 -0
  130. package/docs/project-audit/2026-05-16/sessions/batch-09.md +105 -0
  131. package/docs/project-audit/2026-05-16/sessions/batch-10.md +102 -0
  132. package/docs/project-audit/AUDIT-HISTORY.md +74 -0
  133. package/docs/project-audit/BATCH-PLAN.md +168 -0
  134. package/docs/project-audit/MARKETING-WEBSITE-FINDINGS-2026-05-11.md +73 -0
  135. package/docs/project-audit/PRODUCTION-READINESS-AUDIT-2026-05-08.md +275 -0
  136. package/docs/project-finalization/00-tracker.json +60 -0
  137. package/docs/project-knowledge-base/01-system-overview.md +227 -0
  138. package/docs/project-knowledge-base/02-routes-pages-forms-users.md +352 -0
  139. package/docs/project-knowledge-base/03-tech-stack-modules-services.md +379 -0
  140. package/docs/project-knowledge-base/04-data-models-integrations.md +244 -0
  141. package/docs/project-knowledge-base/05-docs-corpus-inventory.md +256 -0
  142. package/docs/project-knowledge-base/06-operations-testing-legal-content.md +208 -0
  143. package/docs/project-knowledge-base/README.md +103 -0
  144. package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-05-19.md +81 -0
  145. package/docs/rules-tracker.md +102 -0
  146. package/docs/security/PRE-LAUNCH-PENTEST-CHECKLIST.md +116 -0
  147. package/docs/seo-aeo-rules.json +3043 -0
  148. package/docs/social-media-marketing/README.md +81 -0
  149. package/docs/social-media-marketing/posts-batch-01.md +613 -0
  150. package/docs/social-media-marketing/posts-batch-02.md +528 -0
  151. package/docs/social-media-marketing/whatsapp-messages-batch-01.md +458 -0
  152. package/docs/social-media-marketing/whatsapp-status-batch-01.md +355 -0
  153. package/docs/tracking/finalization-2026-05-16-tracker.json +346 -0
  154. package/docs/tracking/full-audit-tracker.json +121 -0
  155. package/docs/tracking/production-readiness-audit-tracker.json +429 -0
  156. package/docs/tracking/seo-checklist-tracker.json +333 -0
  157. package/ios/Plugin/BackgroundUpdate/BackgroundUpdatePlugin.swift +66 -9
  158. package/ios/Plugin/LiveUpdate/LiveUpdatePlugin.swift +306 -52
  159. package/ios/Plugin/NativeUpdatePlugin.swift +22 -9
  160. package/ios/Plugin/Security/SecurityManager.swift +13 -14
  161. package/package.json +37 -38
  162. package/cli/node_modules/.yarn-integrity +0 -16
  163. package/cli/node_modules/commander/LICENSE +0 -22
  164. package/cli/node_modules/commander/Readme.md +0 -1148
  165. package/cli/node_modules/commander/esm.mjs +0 -16
  166. package/cli/node_modules/commander/index.js +0 -26
  167. package/cli/node_modules/commander/lib/argument.js +0 -145
  168. package/cli/node_modules/commander/lib/command.js +0 -2179
  169. package/cli/node_modules/commander/lib/error.js +0 -43
  170. package/cli/node_modules/commander/lib/help.js +0 -462
  171. package/cli/node_modules/commander/lib/option.js +0 -329
  172. package/cli/node_modules/commander/lib/suggestSimilar.js +0 -100
  173. package/cli/node_modules/commander/package-support.json +0 -16
  174. package/cli/node_modules/commander/package.json +0 -80
  175. package/cli/node_modules/commander/typings/esm.d.mts +0 -3
  176. package/cli/node_modules/commander/typings/index.d.ts +0 -884
  177. package/cli/yarn.lock +0 -8
  178. package/dist/esm/__tests__/bundle-manager.test.d.ts +0 -1
  179. package/dist/esm/__tests__/bundle-manager.test.js +0 -151
  180. package/dist/esm/__tests__/bundle-manager.test.js.map +0 -1
  181. package/dist/esm/__tests__/config.test.d.ts +0 -1
  182. package/dist/esm/__tests__/config.test.js +0 -70
  183. package/dist/esm/__tests__/config.test.js.map +0 -1
  184. package/dist/esm/__tests__/delta-processor.test.d.ts +0 -1
  185. package/dist/esm/__tests__/delta-processor.test.js +0 -77
  186. package/dist/esm/__tests__/delta-processor.test.js.map +0 -1
  187. package/dist/esm/__tests__/firestore-schema.test.d.ts +0 -1
  188. package/dist/esm/__tests__/firestore-schema.test.js +0 -74
  189. package/dist/esm/__tests__/firestore-schema.test.js.map +0 -1
  190. package/dist/esm/__tests__/integration.test.d.ts +0 -1
  191. package/dist/esm/__tests__/integration.test.js +0 -78
  192. package/dist/esm/__tests__/integration.test.js.map +0 -1
  193. package/dist/esm/__tests__/manifest-reader.test.d.ts +0 -1
  194. package/dist/esm/__tests__/manifest-reader.test.js +0 -271
  195. package/dist/esm/__tests__/manifest-reader.test.js.map +0 -1
  196. package/dist/esm/__tests__/rollout-checker.test.d.ts +0 -1
  197. package/dist/esm/__tests__/rollout-checker.test.js +0 -210
  198. package/dist/esm/__tests__/rollout-checker.test.js.map +0 -1
  199. package/dist/esm/__tests__/security.test.d.ts +0 -1
  200. package/dist/esm/__tests__/security.test.js +0 -54
  201. package/dist/esm/__tests__/security.test.js.map +0 -1
  202. package/dist/esm/__tests__/version-manager.test.d.ts +0 -1
  203. package/dist/esm/__tests__/version-manager.test.js +0 -45
  204. package/dist/esm/__tests__/version-manager.test.js.map +0 -1
  205. package/dist/esm/app-review/app-review-manager.js.map +0 -1
  206. package/dist/esm/app-review/index.js.map +0 -1
  207. package/dist/esm/app-review/platform-review-handler.js.map +0 -1
  208. package/dist/esm/app-review/review-conditions-checker.js.map +0 -1
  209. package/dist/esm/app-review/review-rate-limiter.js.map +0 -1
  210. package/dist/esm/app-review/types.js.map +0 -1
  211. package/dist/esm/app-update/app-update-checker.js.map +0 -1
  212. package/dist/esm/app-update/app-update-installer.js.map +0 -1
  213. package/dist/esm/app-update/app-update-manager.js.map +0 -1
  214. package/dist/esm/app-update/app-update-notifier.js.map +0 -1
  215. package/dist/esm/app-update/index.js.map +0 -1
  216. package/dist/esm/app-update/platform-app-update.js.map +0 -1
  217. package/dist/esm/app-update/types.js.map +0 -1
  218. package/dist/esm/background-update/background-scheduler.js.map +0 -1
  219. package/dist/esm/background-update/index.js.map +0 -1
  220. package/dist/esm/background-update/notification-manager.js.map +0 -1
  221. package/dist/esm/config/support.js.map +0 -1
  222. package/dist/esm/core/analytics.js.map +0 -1
  223. package/dist/esm/core/cache-manager.js.map +0 -1
  224. package/dist/esm/core/config.js.map +0 -1
  225. package/dist/esm/core/errors.js.map +0 -1
  226. package/dist/esm/core/event-emitter.js.map +0 -1
  227. package/dist/esm/core/logger.js.map +0 -1
  228. package/dist/esm/core/performance.js.map +0 -1
  229. package/dist/esm/core/plugin-manager.js.map +0 -1
  230. package/dist/esm/core/security.js.map +0 -1
  231. package/dist/esm/definitions.js.map +0 -1
  232. package/dist/esm/firestore/firestore-client.d.ts +0 -109
  233. package/dist/esm/firestore/firestore-client.js +0 -264
  234. package/dist/esm/firestore/firestore-client.js.map +0 -1
  235. package/dist/esm/firestore/index.d.ts +0 -11
  236. package/dist/esm/firestore/index.js +0 -11
  237. package/dist/esm/firestore/index.js.map +0 -1
  238. package/dist/esm/firestore/manifest-reader.d.ts +0 -87
  239. package/dist/esm/firestore/manifest-reader.js +0 -294
  240. package/dist/esm/firestore/manifest-reader.js.map +0 -1
  241. package/dist/esm/firestore/schema.d.ts +0 -507
  242. package/dist/esm/firestore/schema.js +0 -73
  243. package/dist/esm/firestore/schema.js.map +0 -1
  244. package/dist/esm/index.js.map +0 -1
  245. package/dist/esm/live-update/bundle-manager.js.map +0 -1
  246. package/dist/esm/live-update/certificate-pinning.js.map +0 -1
  247. package/dist/esm/live-update/delta-processor.js.map +0 -1
  248. package/dist/esm/live-update/download-manager.js.map +0 -1
  249. package/dist/esm/live-update/rollout-checker.js.map +0 -1
  250. package/dist/esm/live-update/update-manager.js.map +0 -1
  251. package/dist/esm/live-update/version-manager.js.map +0 -1
  252. package/dist/esm/plugin.js.map +0 -1
  253. package/dist/esm/security/crypto.js.map +0 -1
  254. package/dist/esm/security/validator.js.map +0 -1
  255. package/dist/esm/web.js.map +0 -1
  256. package/dist/plugin.cjs.js.map +0 -1
  257. package/dist/plugin.esm.js.map +0 -1
  258. package/dist/plugin.js.map +0 -1
  259. package/docs/FIREBASE_INTEGRATION_TRACKER.md +0 -321
  260. package/docs/FIREBASE_QUERIES_AND_INDEXES_AUDIT.md +0 -221
  261. package/docs/examples/firebase-backend-example.md +0 -27
  262. package/docs/guides/no-cost-backend-implementation-plan.md +0 -77
  263. package/docs/guides/no-cost-firestore-google-drive-backend.md +0 -60
  264. package/docs/play-console-rejection-rules.json +0 -428
  265. /package/docs/{MARKETING_WEBSITE_PLAN.md → archive/MARKETING_WEBSITE_PLAN.md} +0 -0
package/cli/AGENTS.md ADDED
@@ -0,0 +1,43 @@
1
+ # AGENTS.md - CLI Tool
2
+
3
+ Agent instructions for the native-update CLI tool.
4
+
5
+ ## 🔴 3-Day Freshness Rule
6
+ Check and update this file at least every 3 days. See root AGENTS.md.
7
+
8
+ ## Package Manager Hierarchy: nvm → npm (global) → yarn (local) (IRON-SOLID)
9
+
10
+ Three tiers, each tool ONLY for its tier — for the best, most reproducible dev results:
11
+ - **`nvm`** → install/update Node.js (which bundles `npm`): `nvm install --lts`. Use nvm to get/update `npm` itself.
12
+ - **`npm`** → ALL global packages: `npm install -g yarn` (install yarn globally if missing) + `npm install -g <pkg>` (every other global CLI).
13
+ - **`yarn`** → ALL local project work: `yarn`, `yarn add <pkg>`, `yarn add -D <pkg>` inside the project.
14
+
15
+ ❌ NEVER use `npm`/`pnpm` for LOCAL installs. NEVER use `pnpm` at all. ✅ Only `yarn.lock` in the project — delete `package-lock.json` and `pnpm-lock.yaml`.
16
+
17
+ ## Scope
18
+
19
+ Node.js CLI for release management, bundle operations, and configuration. Distributed as part of the npm package.
20
+
21
+ ## Key Rules
22
+
23
+ 1. **ESM module**: Uses `"type": "module"` — all imports must use ESM syntax
24
+ 2. **Interactive prompts**: Use `prompts` library for user input
25
+ 3. **Error messages**: Clear, actionable error messages without exposing internals
26
+ 4. **Exit codes**: Use proper exit codes (0 success, 1 error)
27
+ 5. **Help text**: Every command must have `--help` documentation
28
+
29
+ ## When Adding CLI Commands
30
+
31
+ 1. Create command file in `commands/`
32
+ 2. Register in `index.js`
33
+ 3. Update `docs/CLI_REFERENCE.md`
34
+ 4. Add usage example to relevant docs
35
+
36
+ ---
37
+
38
+ ## Gitignore Hygiene (IRON-SOLID)
39
+ `.gitignore` stays current with the project structure — ignore only recoverable artifacts (build/`dist`/`www`/`node_modules`/logs/caches/IDE), never lose source. Custom rules always present: `*.ignore.*`, `project-record-ignore/`. This is a **PRIVATE** repo -> `.env`/secrets/keystores ARE tracked in git. Capacitor: track `android`/`ios` source, ignore native build.
40
+ Full rule + private/public protocol: `~/.claude/rules/project-config.md`.
41
+ Gitignore Last Verified: 2026-06-24
42
+
43
+ **Last Updated**: 2026-04-25
package/cli/CLAUDE.md ADDED
@@ -0,0 +1,65 @@
1
+ # Native Update - CLI
2
+
3
+ Command-line interface for native-update release management and bundle operations.
4
+
5
+ ## 🔴 3-Day Freshness Rule
6
+ Check and update this file at least every 3 days. See root CLAUDE.md.
7
+
8
+ ## Package Manager Hierarchy: nvm → npm (global) → yarn (local) (IRON-SOLID)
9
+
10
+ Three tiers, each tool ONLY for its tier — for the best, most reproducible dev results:
11
+ - **`nvm`** → install/update Node.js (which bundles `npm`): `nvm install --lts`. Use nvm to get/update `npm` itself.
12
+ - **`npm`** → ALL global packages: `npm install -g yarn` (install yarn globally if missing) + `npm install -g <pkg>` (every other global CLI).
13
+ - **`yarn`** → ALL local project work: `yarn`, `yarn add <pkg>`, `yarn add -D <pkg>` inside the project.
14
+
15
+ ❌ NEVER use `npm`/`pnpm` for LOCAL installs. NEVER use `pnpm` at all. ✅ Only `yarn.lock` in the project — delete `package-lock.json` and `pnpm-lock.yaml`.
16
+
17
+ ## Structure
18
+
19
+ | File/Folder | Purpose |
20
+ |-------------|---------|
21
+ | `index.js` | CLI entry point (~10KB) |
22
+ | `commands/` | Command implementations |
23
+ | `package.json` | CLI dependencies |
24
+
25
+ ## Dependencies
26
+ - `commander` — Argument parsing
27
+ - `chalk` — Colored terminal output
28
+ - `ora` — Loading spinners
29
+ - `archiver` — ZIP creation
30
+ - `prompts` — Interactive prompts
31
+
32
+ ## Usage
33
+
34
+ ```bash
35
+ # Via npx
36
+ npx native-update <command>
37
+
38
+ # Or install globally
39
+ npm install -g native-update
40
+ native-update <command>
41
+ ```
42
+
43
+ ## Commands
44
+
45
+ The CLI provides commands for:
46
+ - Bundle management (create, verify, sign)
47
+ - Update publishing (upload, activate, rollback)
48
+ - Configuration management
49
+ - Release management
50
+
51
+ ## Module System
52
+ - ESM (`"type": "module"` in package.json)
53
+ - Uses `commander` for CLI framework
54
+
55
+ ## FilesHub Rule (inherited)
56
+ All file storage uses FilesHub API. See root CLAUDE.md.
57
+
58
+ ---
59
+
60
+ ## Gitignore Hygiene (IRON-SOLID)
61
+ `.gitignore` stays current with the project structure — ignore only recoverable artifacts (build/`dist`/`www`/`node_modules`/logs/caches/IDE), never lose source. Custom rules always present: `*.ignore.*`, `project-record-ignore/`. This is a **PRIVATE** repo -> `.env`/secrets/keystores ARE tracked in git. Capacitor: track `android`/`ios` source, ignore native build.
62
+ Full rule + private/public protocol: `~/.claude/rules/project-config.md`.
63
+ Gitignore Last Verified: 2026-06-24
64
+
65
+ **Last Updated**: 2026-04-25
@@ -8,6 +8,46 @@ import ora from 'ora';
8
8
  const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = dirname(__filename);
10
10
 
11
+ /**
12
+ * Banner injected at the top of every generated server-side file. The
13
+ * generator emits SCAFFOLDS, not turn-key production code: bundle
14
+ * lookup, persistent storage, signature verification, and rollout
15
+ * gating are all left as `TODO[SCAFFOLD-IMPLEMENT-BEFORE-DEPLOY]`
16
+ * markers because the right answer depends on the host's storage +
17
+ * DB choices. Fully working references:
18
+ * - example-apps/node-express (minimal HTTP contract)
19
+ * - backend/ (Laravel + Nova production app)
20
+ */
21
+ const SCAFFOLD_BANNER = `/**
22
+ * ⚠️ SCAFFOLD — IMPLEMENT BEFORE DEPLOYING ⚠️
23
+ *
24
+ * This file was generated by \`native-update backend-create\` as a
25
+ * STARTING POINT, not a deploy-ready service. Every block tagged
26
+ * \`TODO[SCAFFOLD-IMPLEMENT-BEFORE-DEPLOY]\` MUST be replaced with
27
+ * real persistence + storage + signature verification before this
28
+ * runs in production.
29
+ *
30
+ * Working references:
31
+ * • example-apps/node-express — minimal HTTP-contract reference
32
+ * • backend/ (Laravel + Nova) — full SaaS implementation
33
+ *
34
+ * Deploying this file as-is will respond with hard-coded sample
35
+ * payloads and silently drop uploaded bundles.
36
+ */
37
+ `;
38
+
39
+ const POST_GENERATION_WARNING = [
40
+ '',
41
+ chalk.yellow.bold('⚠️ SCAFFOLD — NOT PRODUCTION READY ⚠️'),
42
+ chalk.gray('This template ships with placeholder bundle lookup + storage logic.'),
43
+ chalk.gray('Search the generated code for `TODO[SCAFFOLD-IMPLEMENT-BEFORE-DEPLOY]`'),
44
+ chalk.gray('and replace each block with your real DB / storage / signing logic.'),
45
+ chalk.gray('See:'),
46
+ chalk.gray(' • example-apps/node-express (minimal HTTP-contract reference)'),
47
+ chalk.gray(' • backend/ (Laravel + Nova) (full SaaS implementation)'),
48
+ '',
49
+ ].join('\n');
50
+
11
51
  export async function createBackend(type, options) {
12
52
  const spinner = ora(`Creating ${type} backend template...`).start();
13
53
 
@@ -40,14 +80,15 @@ export async function createBackend(type, options) {
40
80
  throw new Error(`Unknown backend type: ${type}`);
41
81
  }
42
82
 
43
- spinner.succeed(`${type} backend created successfully!`);
44
-
45
- console.log('');
83
+ spinner.succeed(`${type} backend scaffold created!`);
84
+
85
+ console.log(POST_GENERATION_WARNING);
46
86
  console.log(chalk.bold('Next steps:'));
47
87
  console.log(chalk.gray(` 1. cd ${options.output}`));
48
88
  console.log(chalk.gray(` 2. npm install`));
49
89
  console.log(chalk.gray(` 3. Configure your environment variables`));
50
- console.log(chalk.gray(` 4. npm run dev`));
90
+ console.log(chalk.gray(` 4. Replace every TODO[SCAFFOLD-IMPLEMENT-BEFORE-DEPLOY] block`));
91
+ console.log(chalk.gray(` 5. npm run dev`));
51
92
 
52
93
  } catch (error) {
53
94
  spinner.fail(`Failed to create backend: ${error.message}`);
@@ -90,7 +131,7 @@ async function createExpressBackend(outputDir, options) {
90
131
  );
91
132
 
92
133
  // Create server.js
93
- const serverCode = `import express from 'express';
134
+ const serverCode = `${SCAFFOLD_BANNER}import express from 'express';
94
135
  import cors from 'cors';
95
136
  import dotenv from 'dotenv';
96
137
  import { router as bundleRouter } from './routes/bundles.js';
@@ -141,7 +182,7 @@ router.get('/latest', async (req, res) => {
141
182
  try {
142
183
  const { appId, version, channel = 'production' } = req.query;
143
184
 
144
- // TODO: Implement bundle lookup logic
185
+ // TODO[SCAFFOLD-IMPLEMENT-BEFORE-DEPLOY]: replace with real bundle lookup (DB query against your apps + builds tables)
145
186
  // This is a simplified example
146
187
  const bundle = {
147
188
  version: '1.0.1',
@@ -173,8 +214,8 @@ router.post('/upload', upload.single('bundle'), async (req, res) => {
173
214
  const fileBuffer = await fs.readFile(file.path);
174
215
  const checksum = crypto.createHash('sha256').update(fileBuffer).digest('hex');
175
216
 
176
- // TODO: Store bundle metadata in database
177
- // TODO: Move file to permanent storage
217
+ // TODO[SCAFFOLD-IMPLEMENT-BEFORE-DEPLOY]: persist build metadata (channel, version, checksum, signature, fileshub_public_id) — see backend/ Laravel migrations for the canonical shape
218
+ // TODO[SCAFFOLD-IMPLEMENT-BEFORE-DEPLOY]: upload to FilesHub (preferred) or your CDN; multer drops to /uploads/ which is ephemeral
178
219
 
179
220
  res.json({
180
221
  success: true,
@@ -268,7 +309,7 @@ async function createVercelBackend(outputDir, options) {
268
309
  );
269
310
 
270
311
  // Create bundles API endpoint
271
- const bundlesApi = `export default async function handler(req, res) {
312
+ const bundlesApi = `${SCAFFOLD_BANNER}export default async function handler(req, res) {
272
313
  // Enable CORS
273
314
  res.setHeader('Access-Control-Allow-Credentials', true);
274
315
  res.setHeader('Access-Control-Allow-Origin', '*');
@@ -287,7 +328,7 @@ async function createVercelBackend(outputDir, options) {
287
328
  // Get latest bundle
288
329
  const { appId, version, channel = 'production' } = req.query;
289
330
 
290
- // TODO: Implement bundle lookup from your storage solution
331
+ // TODO[SCAFFOLD-IMPLEMENT-BEFORE-DEPLOY]: replace with real bundle lookup against your KV / DB store
291
332
  // This is a simplified example
292
333
  const bundle = {
293
334
  version: '1.0.1',
@@ -306,8 +347,8 @@ async function createVercelBackend(outputDir, options) {
306
347
  // Handle bundle upload
307
348
  const { version, channel, metadata } = req.body;
308
349
 
309
- // TODO: Handle file upload to storage (Vercel Blob, S3, etc.)
310
- // TODO: Save metadata to database (Vercel KV, external DB, etc.)
350
+ // TODO[SCAFFOLD-IMPLEMENT-BEFORE-DEPLOY]: upload to Vercel Blob / S3 / FilesHub; verify signature against your signing key registry
351
+ // TODO[SCAFFOLD-IMPLEMENT-BEFORE-DEPLOY]: persist build metadata to Vercel KV / Postgres / your DB of choice
311
352
 
312
353
  return res.status(200).json({
313
354
  success: true,
@@ -448,7 +489,7 @@ async function createFirebaseBackend(outputDir, options) {
448
489
  );
449
490
 
450
491
  // Create index.js
451
- const indexJs = `import { onRequest } from 'firebase-functions/v2/https';
492
+ const indexJs = `${SCAFFOLD_BANNER}import { onRequest } from 'firebase-functions/v2/https';
452
493
  import * as admin from 'firebase-admin';
453
494
  import express from 'express';
454
495
  import cors from 'cors';
@@ -490,8 +531,8 @@ app.post('/api/bundles/upload', async (req, res) => {
490
531
  try {
491
532
  const { version, channel, metadata } = req.body;
492
533
 
493
- // TODO: Handle file upload to Cloud Storage
494
- // TODO: Save metadata to Firestore
534
+ // TODO[SCAFFOLD-IMPLEMENT-BEFORE-DEPLOY]: upload to Cloud Storage; verify signature; enforce monotonic version per channel
535
+ // TODO[SCAFFOLD-IMPLEMENT-BEFORE-DEPLOY]: write to Firestore; remember Firestore rules must be locked down (no public writes)
495
536
 
496
537
  res.json({ success: true, version });
497
538
  } catch (error) {
@@ -3,13 +3,15 @@ import fs from 'fs/promises';
3
3
  import path from 'path';
4
4
  import crypto from 'crypto';
5
5
  import archiver from 'archiver';
6
- import { createWriteStream } from 'fs';
6
+ import { createWriteStream, createReadStream } from 'fs';
7
7
  import { fileURLToPath } from 'url';
8
8
  import { dirname, join } from 'path';
9
9
 
10
10
  const __filename = fileURLToPath(import.meta.url);
11
11
  const __dirname = dirname(__filename);
12
12
 
13
+ const NEVER_BUNDLE = ['node_modules', '.git', '.env', '.env.local', '.env.production'];
14
+
13
15
  export async function createBundle(webDir, options) {
14
16
  console.log(chalk.blue('🔨 Creating update bundle...'));
15
17
 
@@ -28,6 +30,18 @@ export async function createBundle(webDir, options) {
28
30
  throw new Error(`No index.html found in ${webDir}. Is this a valid web build directory?`);
29
31
  }
30
32
 
33
+ // Refuse to bundle a project root by mistake — `node_modules`, `.git`,
34
+ // and `.env*` should never end up in an OTA bundle. Catches the common
35
+ // mistake of pointing at the project root instead of `dist/` or `www/`.
36
+ const entries = await fs.readdir(webDir);
37
+ const dangerous = entries.filter((e) => NEVER_BUNDLE.includes(e));
38
+ if (dangerous.length > 0) {
39
+ throw new Error(
40
+ `Refusing to bundle: ${webDir} contains ${dangerous.join(', ')}. ` +
41
+ `Point at your built web output (e.g. ./dist or ./www), not the project root.`
42
+ );
43
+ }
44
+
31
45
  // Create output directory
32
46
  const outputDir = path.resolve(options.output);
33
47
  await fs.mkdir(outputDir, { recursive: true });
@@ -44,13 +58,24 @@ export async function createBundle(webDir, options) {
44
58
  }
45
59
  }
46
60
 
61
+ // Parse --metadata separately so we can give a clearer error than
62
+ // a bare "Unexpected token" if the user passes invalid JSON.
63
+ let extraMetadata = {};
64
+ if (options.metadata) {
65
+ try {
66
+ extraMetadata = JSON.parse(options.metadata);
67
+ } catch (e) {
68
+ throw new Error(`Invalid JSON in --metadata: ${e.message}`);
69
+ }
70
+ }
71
+
47
72
  // Create bundle metadata
48
73
  const metadata = {
49
74
  version,
50
75
  channel: options.channel,
51
76
  created: new Date().toISOString(),
52
77
  platform: 'web',
53
- ...(options.metadata ? JSON.parse(options.metadata) : {})
78
+ ...extraMetadata,
54
79
  };
55
80
 
56
81
  const bundleId = `${version}-${Date.now()}`;
@@ -81,13 +106,20 @@ export async function createBundle(webDir, options) {
81
106
  await archive.finalize();
82
107
  await archivePromise;
83
108
 
84
- // Calculate checksum
85
- const fileBuffer = await fs.readFile(bundlePath);
86
- const checksum = crypto.createHash('sha256').update(fileBuffer).digest('hex');
109
+ // Stream the bundle through the SHA-256 hasher so a 100MB+ bundle
110
+ // doesn't get slurped into RAM all at once. Size comes from fs.stat.
111
+ const bundleStat = await fs.stat(bundlePath);
112
+ const checksum = await new Promise((resolve, reject) => {
113
+ const hash = crypto.createHash('sha256');
114
+ createReadStream(bundlePath)
115
+ .on('data', (chunk) => hash.update(chunk))
116
+ .on('end', () => resolve(hash.digest('hex')))
117
+ .on('error', reject);
118
+ });
87
119
 
88
120
  // Update metadata with file info
89
121
  metadata.checksum = checksum;
90
- metadata.size = fileBuffer.length;
122
+ metadata.size = bundleStat.size;
91
123
  metadata.filename = bundleFileName;
92
124
 
93
125
  // Save metadata
@@ -97,7 +129,7 @@ export async function createBundle(webDir, options) {
97
129
  console.log('');
98
130
  console.log(chalk.bold('Bundle Details:'));
99
131
  console.log(chalk.gray(` File: ${bundlePath}`));
100
- console.log(chalk.gray(` Size: ${(fileBuffer.length / 1024 / 1024).toFixed(2)} MB`));
132
+ console.log(chalk.gray(` Size: ${(bundleStat.size / 1024 / 1024).toFixed(2)} MB`));
101
133
  console.log(chalk.gray(` Checksum: ${checksum}`));
102
134
  console.log(chalk.gray(` Metadata: ${metadataPath}`));
103
135
  console.log('');
@@ -1,6 +1,9 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
 
4
+ const POLL_MS = 5000;
5
+ const MAX_CONSECUTIVE_FAILURES = 5;
6
+
4
7
  export async function monitor(options) {
5
8
  if (!options.server) {
6
9
  console.error(chalk.red('Error: --server URL is required'));
@@ -12,57 +15,75 @@ export async function monitor(options) {
12
15
  console.log('');
13
16
 
14
17
  const spinner = ora('Fetching update statistics...').start();
18
+ let intervalId = null;
19
+ let consecutiveFailures = 0;
20
+ let stopping = false;
21
+
22
+ const stop = (code = 0) => {
23
+ if (stopping) return;
24
+ stopping = true;
25
+ if (intervalId) clearInterval(intervalId);
26
+ if (spinner.isSpinning) spinner.stop();
27
+ process.exit(code);
28
+ };
29
+
30
+ process.on('SIGINT', () => stop(0));
31
+ process.on('SIGTERM', () => stop(0));
32
+
33
+ const headers = options.key ? { Authorization: `Bearer ${options.key}` } : {};
34
+
35
+ const tick = async () => {
36
+ try {
37
+ const response = await fetch(`${options.server}/api/stats`, { headers });
38
+ if (!response.ok) throw new Error(`Server returned ${response.status}`);
39
+
40
+ const stats = await response.json();
41
+ consecutiveFailures = 0;
42
+
43
+ spinner.stop();
44
+ console.clear();
45
+ console.log(chalk.blue(`📊 Update Monitor - ${new Date().toLocaleTimeString()}`));
46
+ console.log(chalk.gray('─'.repeat(50)));
47
+ console.log('');
15
48
 
16
- try {
17
- // Poll server for stats
18
- setInterval(async () => {
19
- try {
20
- const headers = options.key ? { 'Authorization': `Bearer ${options.key}` } : {};
21
-
22
- const response = await fetch(`${options.server}/api/stats`, { headers });
23
-
24
- if (!response.ok) {
25
- throw new Error(`Server returned ${response.status}`);
26
- }
27
-
28
- const stats = await response.json();
29
-
30
- spinner.stop();
31
- console.clear();
32
- console.log(chalk.blue(`📊 Update Monitor - ${new Date().toLocaleTimeString()}`));
33
- console.log(chalk.gray('─'.repeat(50)));
34
- console.log('');
35
-
36
- console.log(chalk.bold('Current Version:'));
37
- console.log(chalk.gray(` Latest: ${stats.latestVersion || 'N/A'}`));
38
- console.log(chalk.gray(` Channel: ${stats.channel || 'production'}`));
39
- console.log('');
40
-
41
- console.log(chalk.bold('Download Statistics:'));
42
- console.log(chalk.gray(` Total Downloads: ${stats.totalDownloads || 0}`));
43
- console.log(chalk.gray(` Downloads Today: ${stats.downloadsToday || 0}`));
44
- console.log(chalk.gray(` Active Installs: ${stats.activeInstalls || 0}`));
45
- console.log('');
46
-
47
- if (stats.recentActivity) {
48
- console.log(chalk.bold('Recent Activity:'));
49
- stats.recentActivity.forEach(activity => {
50
- console.log(chalk.gray(` ${activity.time} - ${activity.action} (${activity.version})`));
51
- });
52
- }
53
-
54
- console.log('');
55
- console.log(chalk.gray('Press Ctrl+C to stop'));
56
-
57
- spinner.start('Updating...');
58
- } catch (error) {
59
- spinner.fail(`Failed to fetch stats: ${error.message}`);
60
- spinner.start('Retrying...');
49
+ console.log(chalk.bold('Current Version:'));
50
+ console.log(chalk.gray(` Latest: ${stats.latestVersion || 'N/A'}`));
51
+ console.log(chalk.gray(` Channel: ${stats.channel || 'production'}`));
52
+ console.log('');
53
+
54
+ console.log(chalk.bold('Download Statistics:'));
55
+ console.log(chalk.gray(` Total Downloads: ${stats.totalDownloads || 0}`));
56
+ console.log(chalk.gray(` Downloads Today: ${stats.downloadsToday || 0}`));
57
+ console.log(chalk.gray(` Active Installs: ${stats.activeInstalls || 0}`));
58
+ console.log('');
59
+
60
+ if (stats.recentActivity) {
61
+ console.log(chalk.bold('Recent Activity:'));
62
+ stats.recentActivity.forEach((activity) => {
63
+ console.log(chalk.gray(` ${activity.time} - ${activity.action} (${activity.version})`));
64
+ });
61
65
  }
62
- }, 5000); // Update every 5 seconds
63
66
 
64
- } catch (error) {
65
- spinner.fail(`Monitor failed: ${error.message}`);
66
- process.exit(1);
67
- }
68
- }
67
+ console.log('');
68
+ console.log(chalk.gray('Press Ctrl+C to stop'));
69
+
70
+ spinner.start('Updating...');
71
+ } catch (error) {
72
+ consecutiveFailures += 1;
73
+ spinner.fail(
74
+ `Failed to fetch stats (${consecutiveFailures}/${MAX_CONSECUTIVE_FAILURES}): ${error.message}`,
75
+ );
76
+ if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
77
+ console.error(
78
+ chalk.red(`\nGiving up after ${MAX_CONSECUTIVE_FAILURES} consecutive failures.`),
79
+ );
80
+ stop(1);
81
+ return;
82
+ }
83
+ spinner.start('Retrying...');
84
+ }
85
+ };
86
+
87
+ await tick();
88
+ intervalId = setInterval(tick, POLL_MS);
89
+ }
package/cli/index.js CHANGED
@@ -188,29 +188,6 @@ ${chalk.bold('Examples:')}
188
188
  await createBackend(type, options);
189
189
  });
190
190
 
191
- // Config Commands
192
- const configCmd = program
193
- .command('config')
194
- .description('Configuration commands');
195
-
196
- configCmd
197
- .command('check')
198
- .description('Validate your native-update configuration')
199
- .action(async () => {
200
- const { checkConfig } = await import('./commands/config-check.js');
201
- await checkConfig();
202
- });
203
-
204
- // Migration Commands
205
- program
206
- .command('migrate')
207
- .description('Migrate from other OTA solutions')
208
- .option('--from <solution>', 'Source solution (codepush, appflow)', 'codepush')
209
- .action(async (options) => {
210
- const { migrate } = await import('./commands/migrate.js');
211
- await migrate(options);
212
- });
213
-
214
191
  // Monitoring Commands
215
192
  program
216
193
  .command('monitor')
@@ -264,6 +241,15 @@ program.exitOverride();
264
241
  try {
265
242
  await program.parseAsync(process.argv);
266
243
  } catch (error) {
244
+ // With exitOverride(), Commander throws for normal help/version output too
245
+ // (exitCode 0). Those are successful displays, not errors — exit quietly.
246
+ if (
247
+ error.code === 'commander.helpDisplayed' ||
248
+ error.code === 'commander.help' ||
249
+ error.code === 'commander.version'
250
+ ) {
251
+ process.exit(0);
252
+ }
267
253
  if (error.code === 'commander.missingArgument') {
268
254
  console.error(chalk.red(`Error: ${error.message}`));
269
255
  } else if (error.code === 'commander.unknownCommand') {
@@ -272,5 +258,5 @@ try {
272
258
  } else {
273
259
  console.error(chalk.red(`Error: ${error.message}`));
274
260
  }
275
- process.exit(1);
261
+ process.exit(error.exitCode ?? 1);
276
262
  }
package/cli/package.json CHANGED
@@ -1,12 +1,20 @@
1
1
  {
2
2
  "name": "native-update-cli",
3
- "version": "1.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "CLI for Native Update",
5
5
  "type": "module",
6
+ "engines": {
7
+ "node": ">=24.16.0"
8
+ },
6
9
  "bin": {
7
10
  "native-update": "./index.js"
8
11
  },
9
12
  "dependencies": {
10
- "commander": "^11.0.0"
13
+ "archiver": "^8.0.0",
14
+ "chalk": "^5.6.2",
15
+ "commander": "^15.0.0",
16
+ "express": "^5.2.1",
17
+ "ora": "^9.4.0",
18
+ "prompts": "^2.4.2"
11
19
  }
12
- }
20
+ }
@@ -192,4 +192,3 @@ export class AppReviewManager {
192
192
  }
193
193
  }
194
194
  }
195
- //# sourceMappingURL=app-review-manager.js.map
@@ -3,4 +3,3 @@ export { ReviewConditionsChecker } from './review-conditions-checker';
3
3
  export { ReviewRateLimiter } from './review-rate-limiter';
4
4
  export { PlatformReviewHandler } from './platform-review-handler';
5
5
  export * from './types';
6
- //# sourceMappingURL=index.js.map
@@ -135,4 +135,3 @@ export class PlatformReviewHandler {
135
135
  return true;
136
136
  }
137
137
  }
138
- //# sourceMappingURL=platform-review-handler.js.map
@@ -152,4 +152,3 @@ export class ReviewConditionsChecker {
152
152
  }
153
153
  }
154
154
  }
155
- //# sourceMappingURL=review-conditions-checker.js.map
@@ -161,4 +161,3 @@ export class ReviewRateLimiter {
161
161
  return now;
162
162
  }
163
163
  }
164
- //# sourceMappingURL=review-rate-limiter.js.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=types.js.map
@@ -206,4 +206,3 @@ export class AppUpdateChecker {
206
206
  return 'web';
207
207
  }
208
208
  }
209
- //# sourceMappingURL=app-update-checker.js.map
@@ -4,6 +4,7 @@ export declare class AppUpdateInstaller {
4
4
  private logger;
5
5
  private progressCallback?;
6
6
  private currentState;
7
+ private flexibleUpdateInterval;
7
8
  constructor(_: PluginConfig);
8
9
  startImmediateUpdate(): Promise<void>;
9
10
  startFlexibleUpdate(): Promise<void>;