native-update 2.0.0 → 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.
- package/Readme.md +28 -14
- package/android/manifest-additions.xml +28 -0
- package/android/src/main/AndroidManifest.xml +3 -7
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundNotificationManager.kt +21 -13
- package/android/src/main/java/com/aoneahsan/nativeupdate/LiveUpdatePlugin.kt +196 -29
- package/android/src/main/java/com/aoneahsan/nativeupdate/NativeUpdatePlugin.kt +21 -36
- package/cli/AGENTS.md +15 -1
- package/cli/CLAUDE.md +15 -1
- package/cli/commands/backend-create.js +56 -15
- package/cli/commands/bundle-create.js +39 -7
- package/cli/commands/monitor.js +72 -51
- package/cli/index.js +10 -24
- package/cli/package.json +11 -3
- package/dist/esm/app-review/app-review-manager.js +0 -1
- package/dist/esm/app-review/index.js +0 -1
- package/dist/esm/app-review/platform-review-handler.js +0 -1
- package/dist/esm/app-review/review-conditions-checker.js +0 -1
- package/dist/esm/app-review/review-rate-limiter.js +0 -1
- package/dist/esm/app-review/types.js +0 -1
- package/dist/esm/app-update/app-update-checker.js +0 -1
- package/dist/esm/app-update/app-update-installer.d.ts +1 -0
- package/dist/esm/app-update/app-update-installer.js +19 -8
- package/dist/esm/app-update/app-update-manager.js +0 -1
- package/dist/esm/app-update/app-update-notifier.js +0 -1
- package/dist/esm/app-update/index.js +0 -1
- package/dist/esm/app-update/platform-app-update.js +12 -3
- package/dist/esm/app-update/types.js +0 -1
- package/dist/esm/background-update/background-scheduler.js +0 -1
- package/dist/esm/background-update/index.js +0 -1
- package/dist/esm/background-update/notification-manager.js +0 -1
- package/dist/esm/config/support.js +0 -1
- package/dist/esm/core/analytics.d.ts +13 -0
- package/dist/esm/core/analytics.js +15 -1
- package/dist/esm/core/cache-manager.js +0 -1
- package/dist/esm/core/config.d.ts +0 -15
- package/dist/esm/core/config.js +0 -4
- package/dist/esm/core/errors.d.ts +1 -0
- package/dist/esm/core/errors.js +1 -1
- package/dist/esm/core/event-emitter.d.ts +0 -3
- package/dist/esm/core/event-emitter.js +8 -3
- package/dist/esm/core/logger.js +0 -1
- package/dist/esm/core/performance.js +0 -1
- package/dist/esm/core/plugin-manager.js +3 -1
- package/dist/esm/core/security.d.ts +11 -0
- package/dist/esm/core/security.js +27 -1
- package/dist/esm/definitions.d.ts +0 -11
- package/dist/esm/definitions.js +0 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +3 -1
- package/dist/esm/live-update/bundle-manager.d.ts +8 -0
- package/dist/esm/live-update/bundle-manager.js +20 -1
- package/dist/esm/live-update/certificate-pinning.js +0 -1
- package/dist/esm/live-update/delta-processor.d.ts +1 -1
- package/dist/esm/live-update/delta-processor.js +3 -1
- package/dist/esm/live-update/download-manager.js +9 -2
- package/dist/esm/live-update/rollout-checker.d.ts +1 -1
- package/dist/esm/live-update/rollout-checker.js +5 -6
- package/dist/esm/live-update/update-manager.js +0 -1
- package/dist/esm/live-update/version-manager.d.ts +0 -12
- package/dist/esm/live-update/version-manager.js +0 -68
- package/dist/esm/plugin.js +69 -43
- package/dist/esm/security/crypto.js +11 -7
- package/dist/esm/security/validator.js +0 -1
- package/dist/esm/types/rollout.d.ts +85 -0
- package/dist/esm/types/rollout.js +25 -0
- package/dist/esm/web.d.ts +8 -0
- package/dist/esm/web.js +148 -54
- package/dist/plugin.cjs.js +1 -2
- package/dist/plugin.esm.js +1 -2
- package/dist/plugin.js +2 -3
- package/docs/CHANGELOG.md +124 -0
- package/docs/CLAUDE.md +1 -1
- package/docs/MANUAL-TASKS.md +17 -0
- package/docs/MARKETING_WEBSITE_TRACKER.md +18 -2
- package/docs/MIGRATION.md +83 -0
- package/docs/PACKAGES.md +167 -0
- package/docs/PERFORMANCE.md +110 -0
- package/docs/PROJECT_COMPLETION_TRACKER.md +4 -35
- package/docs/README.md +0 -1
- package/docs/REMAINING_FEATURES.md +123 -76
- package/docs/ROADMAP.md +200 -120
- package/docs/SECURITY-IF-REPO-PUBLIC.md +232 -0
- package/docs/TESTING_REQUIREMENTS.md +1 -112
- package/docs/ai-knowledge-base/00-project-profile.md +38 -0
- package/docs/ai-knowledge-base/01-features.md +46 -0
- package/docs/ai-knowledge-base/02-routes.md +72 -0
- package/docs/ai-knowledge-base/03-tech-stack.md +57 -0
- package/docs/ai-knowledge-base/README.md +23 -0
- package/docs/analytics-tracking.md +37 -0
- package/docs/capacitor-official-plugins.md +46 -0
- package/docs/capawesome-plugins.md +29 -0
- package/docs/deployment/HOSTINGER_DEPLOY.md +75 -34
- package/docs/deployment/PRODUCTION-DEPLOY-CHECKLIST.md +184 -0
- package/docs/docs-site/plan.md +201 -0
- package/docs/docs-site/scope.md +127 -0
- package/docs/docs-site/tracker.json +235 -0
- package/docs/error-handling-tracking.md +37 -0
- package/docs/guides/testing-guide.md +15 -41
- package/docs/package-audit.md +41 -0
- package/docs/play-store/ASO-METADATA.md +5 -0
- package/docs/play-store/DATA-SAFETY.md +5 -0
- package/docs/play-store/DECLARATIONS.md +5 -0
- package/docs/play-store/README.md +27 -0
- package/docs/play-store/RELEASE-NOTES.md +99 -0
- package/docs/play-store/RELEASE-STEPS.md +6 -0
- package/docs/play-store/STORE-LISTING.md +7 -0
- package/docs/play-store/SUBMISSION-CHECKLIST.md +5 -0
- package/docs/production-readiness.md +1 -1
- package/docs/project-audit/2026-05-16/00-FINALIZATION-PLAN.md +123 -0
- package/docs/project-audit/2026-05-16/01-BATCH-PLAN.md +227 -0
- package/docs/project-audit/2026-05-16/02-AUDIT-FINDINGS.md +144 -0
- package/docs/project-audit/2026-05-16/sessions/batch-01.md +42 -0
- package/docs/project-audit/2026-05-16/sessions/batch-02.md +74 -0
- package/docs/project-audit/2026-05-16/sessions/batch-03.md +71 -0
- package/docs/project-audit/2026-05-16/sessions/batch-04.md +88 -0
- package/docs/project-audit/2026-05-16/sessions/batch-05.md +63 -0
- package/docs/project-audit/2026-05-16/sessions/batch-06.md +103 -0
- package/docs/project-audit/2026-05-16/sessions/batch-07.md +61 -0
- package/docs/project-audit/2026-05-16/sessions/batch-08.md +101 -0
- package/docs/project-audit/2026-05-16/sessions/batch-09.md +105 -0
- package/docs/project-audit/2026-05-16/sessions/batch-10.md +102 -0
- package/docs/project-audit/AUDIT-HISTORY.md +74 -0
- package/docs/project-audit/BATCH-PLAN.md +168 -0
- package/docs/project-audit/MARKETING-WEBSITE-FINDINGS-2026-05-11.md +73 -0
- package/docs/project-audit/PRODUCTION-READINESS-AUDIT-2026-05-08.md +275 -0
- package/docs/project-finalization/00-tracker.json +60 -0
- package/docs/project-knowledge-base/01-system-overview.md +39 -30
- package/docs/project-knowledge-base/02-routes-pages-forms-users.md +87 -81
- package/docs/project-knowledge-base/03-tech-stack-modules-services.md +140 -108
- package/docs/project-knowledge-base/04-data-models-integrations.md +191 -254
- package/docs/project-knowledge-base/05-docs-corpus-inventory.md +83 -20
- package/docs/project-knowledge-base/06-operations-testing-legal-content.md +67 -29
- package/docs/project-knowledge-base/README.md +27 -14
- package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-05-19.md +81 -0
- package/docs/rules-tracker.md +102 -0
- package/docs/security/PRE-LAUNCH-PENTEST-CHECKLIST.md +116 -0
- package/docs/social-media-marketing/README.md +81 -0
- package/docs/social-media-marketing/posts-batch-01.md +613 -0
- package/docs/social-media-marketing/posts-batch-02.md +528 -0
- package/docs/social-media-marketing/whatsapp-messages-batch-01.md +458 -0
- package/docs/social-media-marketing/whatsapp-status-batch-01.md +355 -0
- package/docs/tracking/finalization-2026-05-16-tracker.json +346 -0
- package/docs/tracking/full-audit-tracker.json +121 -0
- package/docs/tracking/production-readiness-audit-tracker.json +429 -0
- package/ios/Plugin/BackgroundUpdate/BackgroundUpdatePlugin.swift +16 -3
- package/ios/Plugin/LiveUpdate/LiveUpdatePlugin.swift +71 -47
- package/ios/Plugin/NativeUpdatePlugin.swift +14 -9
- package/package.json +29 -29
- package/cli/node_modules/.yarn-integrity +0 -16
- package/cli/node_modules/commander/LICENSE +0 -22
- package/cli/node_modules/commander/Readme.md +0 -1148
- package/cli/node_modules/commander/esm.mjs +0 -16
- package/cli/node_modules/commander/index.js +0 -26
- package/cli/node_modules/commander/lib/argument.js +0 -145
- package/cli/node_modules/commander/lib/command.js +0 -2179
- package/cli/node_modules/commander/lib/error.js +0 -43
- package/cli/node_modules/commander/lib/help.js +0 -462
- package/cli/node_modules/commander/lib/option.js +0 -329
- package/cli/node_modules/commander/lib/suggestSimilar.js +0 -100
- package/cli/node_modules/commander/package-support.json +0 -16
- package/cli/node_modules/commander/package.json +0 -80
- package/cli/node_modules/commander/typings/esm.d.mts +0 -3
- package/cli/node_modules/commander/typings/index.d.ts +0 -884
- package/cli/yarn.lock +0 -8
- package/dist/esm/__tests__/bundle-manager.test.d.ts +0 -1
- package/dist/esm/__tests__/bundle-manager.test.js +0 -151
- package/dist/esm/__tests__/bundle-manager.test.js.map +0 -1
- package/dist/esm/__tests__/config.test.d.ts +0 -1
- package/dist/esm/__tests__/config.test.js +0 -70
- package/dist/esm/__tests__/config.test.js.map +0 -1
- package/dist/esm/__tests__/delta-processor.test.d.ts +0 -1
- package/dist/esm/__tests__/delta-processor.test.js +0 -77
- package/dist/esm/__tests__/delta-processor.test.js.map +0 -1
- package/dist/esm/__tests__/firestore-schema.test.d.ts +0 -1
- package/dist/esm/__tests__/firestore-schema.test.js +0 -74
- package/dist/esm/__tests__/firestore-schema.test.js.map +0 -1
- package/dist/esm/__tests__/integration.test.d.ts +0 -1
- package/dist/esm/__tests__/integration.test.js +0 -78
- package/dist/esm/__tests__/integration.test.js.map +0 -1
- package/dist/esm/__tests__/manifest-reader.test.d.ts +0 -1
- package/dist/esm/__tests__/manifest-reader.test.js +0 -271
- package/dist/esm/__tests__/manifest-reader.test.js.map +0 -1
- package/dist/esm/__tests__/rollout-checker.test.d.ts +0 -1
- package/dist/esm/__tests__/rollout-checker.test.js +0 -210
- package/dist/esm/__tests__/rollout-checker.test.js.map +0 -1
- package/dist/esm/__tests__/security-enforcement.test.d.ts +0 -1
- package/dist/esm/__tests__/security-enforcement.test.js +0 -95
- package/dist/esm/__tests__/security-enforcement.test.js.map +0 -1
- package/dist/esm/__tests__/security.test.d.ts +0 -1
- package/dist/esm/__tests__/security.test.js +0 -54
- package/dist/esm/__tests__/security.test.js.map +0 -1
- package/dist/esm/__tests__/version-manager.test.d.ts +0 -1
- package/dist/esm/__tests__/version-manager.test.js +0 -45
- package/dist/esm/__tests__/version-manager.test.js.map +0 -1
- package/dist/esm/app-review/app-review-manager.js.map +0 -1
- package/dist/esm/app-review/index.js.map +0 -1
- package/dist/esm/app-review/platform-review-handler.js.map +0 -1
- package/dist/esm/app-review/review-conditions-checker.js.map +0 -1
- package/dist/esm/app-review/review-rate-limiter.js.map +0 -1
- package/dist/esm/app-review/types.js.map +0 -1
- package/dist/esm/app-update/app-update-checker.js.map +0 -1
- package/dist/esm/app-update/app-update-installer.js.map +0 -1
- package/dist/esm/app-update/app-update-manager.js.map +0 -1
- package/dist/esm/app-update/app-update-notifier.js.map +0 -1
- package/dist/esm/app-update/index.js.map +0 -1
- package/dist/esm/app-update/platform-app-update.js.map +0 -1
- package/dist/esm/app-update/types.js.map +0 -1
- package/dist/esm/background-update/background-scheduler.js.map +0 -1
- package/dist/esm/background-update/index.js.map +0 -1
- package/dist/esm/background-update/notification-manager.js.map +0 -1
- package/dist/esm/config/support.js.map +0 -1
- package/dist/esm/core/analytics.js.map +0 -1
- package/dist/esm/core/cache-manager.js.map +0 -1
- package/dist/esm/core/config.js.map +0 -1
- package/dist/esm/core/errors.js.map +0 -1
- package/dist/esm/core/event-emitter.js.map +0 -1
- package/dist/esm/core/logger.js.map +0 -1
- package/dist/esm/core/performance.js.map +0 -1
- package/dist/esm/core/plugin-manager.js.map +0 -1
- package/dist/esm/core/security.js.map +0 -1
- package/dist/esm/definitions.js.map +0 -1
- package/dist/esm/firestore/firestore-client.d.ts +0 -109
- package/dist/esm/firestore/firestore-client.js +0 -264
- package/dist/esm/firestore/firestore-client.js.map +0 -1
- package/dist/esm/firestore/index.d.ts +0 -11
- package/dist/esm/firestore/index.js +0 -11
- package/dist/esm/firestore/index.js.map +0 -1
- package/dist/esm/firestore/manifest-reader.d.ts +0 -87
- package/dist/esm/firestore/manifest-reader.js +0 -294
- package/dist/esm/firestore/manifest-reader.js.map +0 -1
- package/dist/esm/firestore/schema.d.ts +0 -507
- package/dist/esm/firestore/schema.js +0 -73
- package/dist/esm/firestore/schema.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/live-update/bundle-manager.js.map +0 -1
- package/dist/esm/live-update/certificate-pinning.js.map +0 -1
- package/dist/esm/live-update/delta-processor.js.map +0 -1
- package/dist/esm/live-update/download-manager.js.map +0 -1
- package/dist/esm/live-update/rollout-checker.js.map +0 -1
- package/dist/esm/live-update/update-manager.js.map +0 -1
- package/dist/esm/live-update/version-manager.js.map +0 -1
- package/dist/esm/plugin.js.map +0 -1
- package/dist/esm/security/crypto.js.map +0 -1
- package/dist/esm/security/validator.js.map +0 -1
- package/dist/esm/web.js.map +0 -1
- package/dist/plugin.cjs.js.map +0 -1
- package/dist/plugin.esm.js.map +0 -1
- package/dist/plugin.js.map +0 -1
- package/docs/FIREBASE_INTEGRATION_TRACKER.md +0 -321
- package/docs/FIREBASE_QUERIES_AND_INDEXES_AUDIT.md +0 -221
- package/docs/examples/firebase-backend-example.md +0 -27
- package/docs/features/laravel-nova-backend/credentials.ignore.md +0 -34
- package/docs/guides/no-cost-backend-implementation-plan.md +0 -77
- package/docs/guides/no-cost-firestore-google-drive-backend.md +0 -60
- package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-03-16.md +0 -454
- package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-03-24.md +0 -66
- package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-03-25.md +0 -67
- /package/docs/{MARKETING_WEBSITE_PLAN.md → archive/MARKETING_WEBSITE_PLAN.md} +0 -0
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Metadata
|
|
4
4
|
|
|
5
|
-
- **Reference Date:** 2026-
|
|
6
|
-
- **Last Updated:** 2026-
|
|
5
|
+
- **Reference Date:** 2026-05-18
|
|
6
|
+
- **Last Updated:** 2026-05-18
|
|
7
7
|
|
|
8
8
|
## Project Identity
|
|
9
9
|
|
|
10
10
|
- **Name:** Native Update
|
|
11
11
|
- **Package:** `native-update`
|
|
12
|
-
- **Type:** Open-source Capacitor plugin plus update management platform
|
|
12
|
+
- **Type:** Open-source Capacitor plugin plus self-hostable update management platform
|
|
13
13
|
- **Primary Creator:** Ahsan Mahmood
|
|
14
14
|
- **Primary Contact:** aoneahsan@gmail.com
|
|
15
15
|
- **License:** MIT
|
|
@@ -28,21 +28,21 @@ The plugin provides the runtime capabilities inside a Capacitor app:
|
|
|
28
28
|
- app update checks and prompts
|
|
29
29
|
- in-app review requests
|
|
30
30
|
- background update scheduling
|
|
31
|
-
- security validation logic
|
|
32
|
-
-
|
|
31
|
+
- security validation logic (SHA-256 checksums + RSA-SHA256 signatures)
|
|
32
|
+
- HTTP-based manifest reading from any user-controlled backend
|
|
33
33
|
|
|
34
34
|
### 2. Dashboard Layer
|
|
35
35
|
|
|
36
36
|
The website provides a management surface for:
|
|
37
37
|
|
|
38
|
-
- user auth
|
|
38
|
+
- user auth (Google OAuth via Laravel Socialite + Sanctum bearer tokens)
|
|
39
39
|
- app creation and app management
|
|
40
|
-
- build uploads
|
|
40
|
+
- build uploads (browser → Laravel → Google Drive)
|
|
41
41
|
- rollout visibility and control
|
|
42
42
|
- analytics review
|
|
43
43
|
- Google Drive connection and storage management
|
|
44
44
|
- configuration generation
|
|
45
|
-
- admin-only oversight pages
|
|
45
|
+
- admin-only oversight pages (Spatie permission-gated)
|
|
46
46
|
|
|
47
47
|
### 3. Docs Layer
|
|
48
48
|
|
|
@@ -52,17 +52,19 @@ The docs explain:
|
|
|
52
52
|
- features and APIs
|
|
53
53
|
- examples and advanced scenarios
|
|
54
54
|
- security, deployment, testing, and troubleshooting
|
|
55
|
-
- migration from other approaches
|
|
55
|
+
- migration from other approaches (CodePush, Capacitor Live Updates)
|
|
56
56
|
- planning, tracking, roadmap, audit, and report history
|
|
57
57
|
|
|
58
|
+
In addition to the in-repo `/docs` folder, a public Docusaurus site lives at the sibling repo `native-update-docs` and is deployed to Firebase Hosting (hosting-only — no Firebase backend).
|
|
59
|
+
|
|
58
60
|
### 4. Tooling Layer
|
|
59
61
|
|
|
60
62
|
The project includes:
|
|
61
63
|
|
|
62
|
-
- CLI commands
|
|
64
|
+
- CLI commands (`init`, `bundle-create`, `bundle-sign`, `bundle-verify`, `keys-generate`, `backend-create`, `server-start`, `monitor`)
|
|
63
65
|
- bundle creation/signing/verification utilities
|
|
64
|
-
- backend templates
|
|
65
|
-
- local development
|
|
66
|
+
- backend scaffolding templates
|
|
67
|
+
- local development server
|
|
66
68
|
|
|
67
69
|
## What The Product Solves
|
|
68
70
|
|
|
@@ -82,7 +84,7 @@ Native Update consolidates those concerns into one project with a common mental
|
|
|
82
84
|
### Live / OTA Updates
|
|
83
85
|
|
|
84
86
|
- Deliver JavaScript, HTML, and CSS changes without app store review
|
|
85
|
-
- Support update channels
|
|
87
|
+
- Support update channels (production / staging / development)
|
|
86
88
|
- Manage version checks and bundle downloads
|
|
87
89
|
- Support checksum and signature validation
|
|
88
90
|
- Enable rollback-aware update flows
|
|
@@ -92,35 +94,36 @@ Native Update consolidates those concerns into one project with a common mental
|
|
|
92
94
|
|
|
93
95
|
- Check for newer native binary versions
|
|
94
96
|
- Support immediate and flexible update strategies
|
|
95
|
-
- Integrate with Android
|
|
97
|
+
- Integrate with Android Play Core update + iOS App Store update behavior
|
|
96
98
|
|
|
97
99
|
### In-App Reviews
|
|
98
100
|
|
|
99
|
-
- Request native reviews
|
|
101
|
+
- Request native reviews via Play Review API + StoreKit `SKStoreReviewController`
|
|
100
102
|
- respect timing/rate limiting logic
|
|
101
103
|
- support user-experience-aware prompting
|
|
102
104
|
|
|
103
105
|
### Background Updates
|
|
104
106
|
|
|
105
|
-
- Schedule checks while app is not active
|
|
107
|
+
- Schedule checks while app is not active (Android WorkManager, iOS BGTaskScheduler)
|
|
106
108
|
- support native notification behavior
|
|
107
109
|
- provide background configuration hooks
|
|
108
110
|
|
|
109
111
|
### Security
|
|
110
112
|
|
|
111
|
-
- signing
|
|
112
|
-
- checksum verification
|
|
113
|
+
- RSA-SHA256 signing of bundles
|
|
114
|
+
- SHA-256 checksum verification
|
|
113
115
|
- HTTPS-first posture
|
|
114
116
|
- certificate pinning support
|
|
115
117
|
- validation helpers
|
|
118
|
+
- fail-closed on integrity violations
|
|
116
119
|
|
|
117
120
|
### Release Operations
|
|
118
121
|
|
|
119
|
-
- upload build ZIPs
|
|
122
|
+
- upload build ZIPs through the dashboard
|
|
120
123
|
- generate file manifests
|
|
121
124
|
- publish app-facing manifests
|
|
122
125
|
- configure rollout percentages
|
|
123
|
-
- manage app/channel/build metadata
|
|
126
|
+
- manage app/channel/build metadata via Nova admin or REST API
|
|
124
127
|
|
|
125
128
|
## Current Product Surfaces
|
|
126
129
|
|
|
@@ -131,6 +134,7 @@ Native Update consolidates those concerns into one project with a common mental
|
|
|
131
134
|
- `/pricing`
|
|
132
135
|
- `/docs`
|
|
133
136
|
- `/docs/*`
|
|
137
|
+
- `/examples`
|
|
134
138
|
- `/about`
|
|
135
139
|
- `/contact`
|
|
136
140
|
- `/privacy`
|
|
@@ -143,8 +147,9 @@ Native Update consolidates those concerns into one project with a common mental
|
|
|
143
147
|
|
|
144
148
|
### Auth Pages
|
|
145
149
|
|
|
146
|
-
- `/login`
|
|
147
|
-
- `/signup`
|
|
150
|
+
- `/login` (Google sign-in via Laravel Socialite redirect)
|
|
151
|
+
- `/signup` (same flow as `/login`; account is created on first callback)
|
|
152
|
+
- `/auth/callback` (SPA receives the Sanctum token from the Laravel OAuth callback)
|
|
148
153
|
|
|
149
154
|
### Protected Dashboard
|
|
150
155
|
|
|
@@ -166,6 +171,7 @@ Native Update consolidates those concerns into one project with a common mental
|
|
|
166
171
|
- `/admin/users`
|
|
167
172
|
- `/admin/apps`
|
|
168
173
|
- `/admin/builds`
|
|
174
|
+
- Laravel Nova admin lives at `/nova` on the backend host (separate UI; same MySQL store)
|
|
169
175
|
|
|
170
176
|
## User Types
|
|
171
177
|
|
|
@@ -175,15 +181,15 @@ Can browse marketing, docs, legal, contact, sitemap, and feed pages.
|
|
|
175
181
|
|
|
176
182
|
### Authenticated User
|
|
177
183
|
|
|
178
|
-
|
|
184
|
+
Holds a Sanctum personal access token, has a row in the MySQL `users` table keyed by `google_id`, and the `user` role from `spatie/laravel-permission`. Can manage their own apps, builds, Drive connection, settings, and release operations.
|
|
179
185
|
|
|
180
186
|
### Admin User
|
|
181
187
|
|
|
182
|
-
|
|
188
|
+
A signed-in user assigned the `admin` or `super-admin` role in MySQL via `spatie/laravel-permission`. Super-admins bypass all `can:` middleware checks via a `Gate::before` hook. Admin assignment happens via `php artisan native-update:role-assign <email> <role>` or automatically when the email is in the `ADMIN_EMAILS` env list.
|
|
183
189
|
|
|
184
190
|
### Mobile App Client
|
|
185
191
|
|
|
186
|
-
Not a dashboard user, but an app-side runtime consumer of manifests, update metadata, and plugin APIs.
|
|
192
|
+
Not a dashboard user, but an app-side runtime consumer of manifests, update metadata, and plugin APIs. Authenticates with the mobile API plane via a per-app API key (separate from Sanctum).
|
|
187
193
|
|
|
188
194
|
### Developer / Maintainer
|
|
189
195
|
|
|
@@ -192,22 +198,24 @@ Works in the codebase, docs, CLI, native code, example apps, and release infrast
|
|
|
192
198
|
## Access Model
|
|
193
199
|
|
|
194
200
|
- Public routes are open.
|
|
195
|
-
- Dashboard routes are wrapped by `ProtectedRoute
|
|
196
|
-
- Admin routes are wrapped by `AdminRoute
|
|
197
|
-
- Google sign-in is the
|
|
198
|
-
- Admin
|
|
201
|
+
- Dashboard routes are wrapped by `ProtectedRoute` on the SPA AND protected by `auth:sanctum` middleware on every Laravel `/api/dashboard/*` route.
|
|
202
|
+
- Admin routes are wrapped by `AdminRoute` on the SPA AND protected by `can:<permission>` middleware on every Laravel `/api/admin/*` route.
|
|
203
|
+
- Google sign-in (via Laravel Socialite) is the only auth flow.
|
|
204
|
+
- Admin privilege comes from Spatie role assignment, not a `users.is_admin` boolean.
|
|
199
205
|
|
|
200
206
|
## Product Positioning Notes
|
|
201
207
|
|
|
202
208
|
- The repo positions Native Update as more than a plugin; it is a platform direction.
|
|
203
209
|
- The website presents a complete update solution with dashboard and docs.
|
|
204
210
|
- The repo also contains older historical docs from pre-production phases. These should be treated as timeline artifacts, not always the newest truth.
|
|
211
|
+
- All mentions of "Firebase Auth", "Firestore", or "Firebase backend" in older docs are historical — the architecture switched to Laravel + Sanctum on 2026-05-11.
|
|
205
212
|
|
|
206
213
|
## Recommended Decision Assumptions For AI Tools
|
|
207
214
|
|
|
208
215
|
- Treat the project as a cross-platform mobile infrastructure product.
|
|
209
216
|
- Treat the website as both a marketing site and operations dashboard.
|
|
210
|
-
- Treat
|
|
217
|
+
- Treat the Laravel backend at `backend/` as the canonical persistence + auth layer for the dashboard.
|
|
218
|
+
- Treat Google Drive as the canonical storage destination for uploaded bundle ZIPs; tokens are encrypted server-side by Laravel.
|
|
211
219
|
- Treat the dashboard as owner/operator tooling, not end-user mobile UI.
|
|
212
220
|
- Treat security, rollout control, and documentation quality as important project values.
|
|
213
221
|
|
|
@@ -216,3 +224,4 @@ Works in the codebase, docs, CLI, native code, example apps, and release infrast
|
|
|
216
224
|
- The project contains current docs plus historical reports and planning files.
|
|
217
225
|
- Some documents disagree on production readiness because they were written at different times.
|
|
218
226
|
- For technical state, prefer current source code, route definitions, type definitions, package files, and newer roadmap/report files.
|
|
227
|
+
- The "no-cost Firestore + Google Drive backend" architecture was deliberately abandoned in v3.0.0 (2026-05-04). Any document still describing it is historical-only.
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## Metadata
|
|
4
4
|
|
|
5
|
-
- **Reference Date:** 2026-
|
|
6
|
-
- **Last Updated:** 2026-
|
|
5
|
+
- **Reference Date:** 2026-05-18
|
|
6
|
+
- **Last Updated:** 2026-05-18
|
|
7
7
|
|
|
8
8
|
## Route Inventory
|
|
9
9
|
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
| `/pricing` | PricingPage | Public | Pricing/beta/commercial positioning page |
|
|
17
17
|
| `/docs` | DocsPage | Public | Docs hub and guided docs entry |
|
|
18
18
|
| `/docs/*` | DocsMarkdownPage | Public | Render markdown docs pages from public docs set |
|
|
19
|
+
| `/examples` | ExamplesPage | Public | Code snippets + sample-app links |
|
|
19
20
|
| `/about` | AboutPage | Public | Mission, project story, differentiators, creator profile |
|
|
20
21
|
| `/contact` | ContactPage | Public | Contact methods, support info, FAQ |
|
|
21
22
|
| `/privacy` | PrivacyPage | Public | Privacy policy |
|
|
@@ -30,8 +31,10 @@
|
|
|
30
31
|
|
|
31
32
|
| Route | Page | Access | Purpose |
|
|
32
33
|
|------|------|--------|---------|
|
|
33
|
-
| `/login` | LoginPage | Public | Sign in with Google |
|
|
34
|
-
| `/signup` | SignupPage | Public |
|
|
34
|
+
| `/login` | LoginPage | Public | Sign in with Google (redirects to Laravel Socialite) |
|
|
35
|
+
| `/signup` | SignupPage | Public | Same OAuth flow as `/login`; account auto-created on first callback |
|
|
36
|
+
| `/auth/callback` | CallbackPage | Public | Receives the Sanctum token from Laravel's OAuth callback and persists it |
|
|
37
|
+
| `/oauth/google-drive/callback` | DriveCallbackPage | Public | Receives Drive OAuth code and posts it to the Laravel backend |
|
|
35
38
|
|
|
36
39
|
### Protected Dashboard Routes
|
|
37
40
|
|
|
@@ -42,37 +45,45 @@
|
|
|
42
45
|
| `/dashboard/apps/:appId` | AppDetailPage | Authenticated user | Per-app details, builds, channels, and rollout state |
|
|
43
46
|
| `/dashboard/builds` | BuildsPage | Authenticated user | Browse/filter builds |
|
|
44
47
|
| `/dashboard/builds/:buildId` | BuildDetailPage | Authenticated user | Detailed build record view |
|
|
45
|
-
| `/dashboard/upload` | UploadPage | Authenticated user | Publish ZIP bundle
|
|
48
|
+
| `/dashboard/upload` | UploadPage | Authenticated user | Publish ZIP bundle, write manifest |
|
|
46
49
|
| `/dashboard/drive` | GoogleDrivePage | Authenticated user | Connect/disconnect Drive and review storage footprint |
|
|
47
50
|
| `/dashboard/config` | ConfigPage | Authenticated user | Show recommended backend/config integration guidance |
|
|
48
|
-
| `/dashboard/settings` | SettingsPage | Authenticated user | Account preferences,
|
|
51
|
+
| `/dashboard/settings` | SettingsPage | Authenticated user | Account preferences, account deletion |
|
|
49
52
|
| `/dashboard/rollouts` | RolloutsPage | Authenticated user | Manage rollout percentages and pause/resume rollouts |
|
|
50
53
|
| `/dashboard/analytics` | AnalyticsPage | Authenticated user | View aggregated update analytics |
|
|
51
54
|
|
|
52
|
-
### Admin Routes
|
|
55
|
+
### Admin Routes (SPA)
|
|
53
56
|
|
|
54
57
|
| Route | Page | Access | Purpose |
|
|
55
58
|
|------|------|--------|---------|
|
|
56
|
-
| `/admin` | AdminOverview |
|
|
57
|
-
| `/admin/users` | AdminUsersPage |
|
|
58
|
-
| `/admin/apps` | AdminAppsPage |
|
|
59
|
-
| `/admin/builds` | AdminBuildsPage |
|
|
59
|
+
| `/admin` | AdminOverview | `admin` or `super-admin` role | Admin overview metrics |
|
|
60
|
+
| `/admin/users` | AdminUsersPage | `admin` or `super-admin` role | Search/browse user accounts |
|
|
61
|
+
| `/admin/apps` | AdminAppsPage | `admin` or `super-admin` role | Search/browse all apps |
|
|
62
|
+
| `/admin/builds` | AdminBuildsPage | `admin` or `super-admin` role | Search/filter all builds |
|
|
63
|
+
|
|
64
|
+
### Admin Routes (Nova)
|
|
65
|
+
|
|
66
|
+
Laravel Nova 5 lives at `/nova` on the backend host. Same MySQL store, separate UI. Resources include User, App, ApiKey, SigningKey, Build, Subscription, AnalyticsEvent. Custom Nova actions exist for rotating signing keys, promoting builds, etc.
|
|
60
67
|
|
|
61
68
|
## Access and Guard Behavior
|
|
62
69
|
|
|
63
|
-
### ProtectedRoute
|
|
70
|
+
### ProtectedRoute (SPA)
|
|
71
|
+
|
|
72
|
+
- Requires a Sanctum token stored in `@capacitor/preferences` (native) or `localStorage` (web)
|
|
73
|
+
- The 401-handling axios interceptor clears the token and event-broadcasts a logout, redirecting to `/login`
|
|
74
|
+
- Token is sent as a `Bearer` header on every `/api/dashboard/*` and `/api/admin/*` request
|
|
64
75
|
|
|
65
|
-
|
|
66
|
-
- Computes email verification from Firebase auth state or Google provider context
|
|
67
|
-
- Redirects unauthenticated users to `/login`
|
|
68
|
-
- If email verification is required and not satisfied, redirects to `/verify-email`
|
|
76
|
+
### AdminRoute (SPA)
|
|
69
77
|
|
|
70
|
-
|
|
78
|
+
- Requires a Sanctum token AND the SPA-side cached `me.roles` to include `admin` or `super-admin`
|
|
79
|
+
- Backend enforcement: every Laravel admin route also carries `auth:sanctum` + `can:<permission>` middleware; an unauthorized request returns 403 regardless of SPA state
|
|
71
80
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
81
|
+
### Backend gate model
|
|
82
|
+
|
|
83
|
+
- `auth:sanctum` middleware on `/api/dashboard/*` and `/api/admin/*`
|
|
84
|
+
- `can:<permission>` middleware on every dashboard/admin route (per-permission, not per-role)
|
|
85
|
+
- `Gate::before` returns `true` for any user with the `super-admin` role (full bypass)
|
|
86
|
+
- First sign-in assigns the `user` role; emails in `ADMIN_EMAILS` env auto-assign `super-admin`
|
|
76
87
|
|
|
77
88
|
## User Types and Permissions
|
|
78
89
|
|
|
@@ -83,19 +94,23 @@
|
|
|
83
94
|
|
|
84
95
|
### Signed-In Product User
|
|
85
96
|
|
|
86
|
-
- Signs in via Google
|
|
87
|
-
- has a
|
|
97
|
+
- Signs in via Google (Laravel Socialite redirect)
|
|
98
|
+
- has a MySQL `users` row keyed by `google_id`
|
|
99
|
+
- holds a Sanctum personal access token (default 7-day TTL, configurable via `SANCTUM_TOKEN_EXPIRATION`)
|
|
100
|
+
- carries the `user` role by default
|
|
88
101
|
- can manage their own apps/builds/settings
|
|
89
|
-
- can connect Google Drive
|
|
102
|
+
- can connect Google Drive (server-side OAuth — tokens encrypted at rest on the User row)
|
|
90
103
|
|
|
91
104
|
### Admin User
|
|
92
105
|
|
|
93
|
-
- Same as signed-in user plus `
|
|
94
|
-
- can access admin
|
|
106
|
+
- Same as signed-in user plus `admin` or `super-admin` role
|
|
107
|
+
- can access SPA admin pages and Nova admin
|
|
108
|
+
- super-admin bypasses every `can:` gate
|
|
95
109
|
|
|
96
110
|
### App Runtime Consumer
|
|
97
111
|
|
|
98
112
|
- Uses plugin APIs and manifest endpoints
|
|
113
|
+
- authenticates with a per-app API key (separate `ValidateApiKey` middleware on `/api/v1/*`)
|
|
99
114
|
- not a website operator account
|
|
100
115
|
|
|
101
116
|
## Page-Level Notes
|
|
@@ -129,10 +144,15 @@
|
|
|
129
144
|
- DocsPage acts as a rich docs landing surface
|
|
130
145
|
- DocsMarkdownPage renders markdown docs pages from the public docs payload
|
|
131
146
|
|
|
147
|
+
### ExamplesPage
|
|
148
|
+
|
|
149
|
+
- Hero + quick-start code snippets (OTA basic, React hook, background, app-store, self-hosted backend pattern)
|
|
150
|
+
- Two canonical example-app cards (React + Capacitor, Node.js + Express)
|
|
151
|
+
|
|
132
152
|
### SitemapPage and FeedPage
|
|
133
153
|
|
|
134
154
|
- Search/filter style discovery experiences
|
|
135
|
-
- use
|
|
155
|
+
- use Input fields for route/content lookup
|
|
136
156
|
- intended as human-friendly content navigation surfaces
|
|
137
157
|
|
|
138
158
|
### DashboardOverview
|
|
@@ -143,8 +163,8 @@
|
|
|
143
163
|
### AppsPage
|
|
144
164
|
|
|
145
165
|
- Central app management page
|
|
146
|
-
- create/edit/delete dialog workflows
|
|
147
|
-
- stores package id, description, platforms, and generates API key on creation
|
|
166
|
+
- create/edit/delete dialog workflows (URL-state driven: `?formMode`, `?editId`, `?deleteId`)
|
|
167
|
+
- stores package id, description, platforms, and generates an API key on creation
|
|
148
168
|
|
|
149
169
|
### AppDetailPage
|
|
150
170
|
|
|
@@ -161,27 +181,26 @@
|
|
|
161
181
|
### UploadPage
|
|
162
182
|
|
|
163
183
|
- Core publish UI
|
|
164
|
-
- requires Google Drive connection
|
|
165
|
-
-
|
|
166
|
-
-
|
|
184
|
+
- requires Google Drive connection (server-side OAuth must be completed)
|
|
185
|
+
- multipart-POST ZIP to Laravel `/api/dashboard/apps/{app}/builds`
|
|
186
|
+
- Laravel computes SHA-256, signs with the app's signing key, stages in user's Drive, persists Build row in MySQL
|
|
167
187
|
|
|
168
188
|
### GoogleDrivePage
|
|
169
189
|
|
|
170
|
-
-
|
|
190
|
+
- starts the Drive OAuth flow (`/api/dashboard/google-drive/auth-url` → full-window Google redirect → `/oauth/google-drive/callback` → POST code to backend)
|
|
171
191
|
- shows Drive connection status, connected email, folder list, file count, and total size
|
|
172
192
|
|
|
173
193
|
### ConfigPage
|
|
174
194
|
|
|
175
195
|
- instructional/configuration surface
|
|
176
|
-
- presents recommended
|
|
196
|
+
- presents the recommended path: dashboard publishes through the Laravel backend; plugin polls the Laravel manifest endpoint
|
|
177
197
|
|
|
178
198
|
### SettingsPage
|
|
179
199
|
|
|
180
200
|
- account profile details
|
|
181
|
-
- preferences
|
|
182
|
-
- appearance
|
|
183
|
-
-
|
|
184
|
-
- account deletion confirmation flow
|
|
201
|
+
- preferences (`users.preferences` JSON column via `/api/dashboard/preferences`)
|
|
202
|
+
- appearance/theme controls
|
|
203
|
+
- account deletion confirmation flow (Sanctum tokens revoked, MySQL row soft-deleted, encrypted Drive tokens cleared)
|
|
185
204
|
|
|
186
205
|
### RolloutsPage
|
|
187
206
|
|
|
@@ -193,7 +212,7 @@
|
|
|
193
212
|
### AnalyticsPage
|
|
194
213
|
|
|
195
214
|
- filters by app and channel
|
|
196
|
-
- aggregates analytics
|
|
215
|
+
- aggregates analytics data across time range from `analytics_events` table
|
|
197
216
|
- intended for download/install/error/rollback analysis
|
|
198
217
|
|
|
199
218
|
### Admin Pages
|
|
@@ -202,18 +221,19 @@
|
|
|
202
221
|
- AdminUsersPage supports user search/filtering
|
|
203
222
|
- AdminAppsPage supports app search/filtering
|
|
204
223
|
- AdminBuildsPage supports build search/filtering by multiple fields
|
|
224
|
+
- Full admin tooling (resource editors, custom actions) lives in Nova at `/nova`
|
|
205
225
|
|
|
206
226
|
## Form and Input Inventory
|
|
207
227
|
|
|
208
228
|
### LoginPage
|
|
209
229
|
|
|
210
|
-
- **Action:** Google sign-in button
|
|
230
|
+
- **Action:** Google sign-in button → SPA navigates to `${VITE_API_URL}/auth/google/redirect`
|
|
211
231
|
- **Inputs:** No text input fields
|
|
212
|
-
- **Errors handled:** popup
|
|
232
|
+
- **Errors handled:** popup blocked (unlikely with full-window redirect), generic OAuth failure displayed on `/auth/callback`
|
|
213
233
|
|
|
214
234
|
### SignupPage
|
|
215
235
|
|
|
216
|
-
- **Action:** Google
|
|
236
|
+
- **Action:** Same Google flow as LoginPage; backend auto-provisions the User row + assigns `user` role
|
|
217
237
|
- **Inputs:** No text input fields
|
|
218
238
|
- **Purpose:** fast account creation for beta onboarding
|
|
219
239
|
|
|
@@ -228,16 +248,15 @@
|
|
|
228
248
|
- app name required, minimum length
|
|
229
249
|
- package ID required and regex-validated
|
|
230
250
|
- at least one platform required
|
|
231
|
-
- **
|
|
232
|
-
-
|
|
233
|
-
- generates API key
|
|
234
|
-
-
|
|
235
|
-
- increments user `appsCount`
|
|
251
|
+
- **Backend side effects on create (Laravel `AppController` + `UpdateBuildRequest`):**
|
|
252
|
+
- inserts row in `apps` table (RBAC-gated by `can:apps.create`)
|
|
253
|
+
- generates an API key, inserts row in `api_keys` table
|
|
254
|
+
- increments user `apps_count`
|
|
236
255
|
|
|
237
256
|
### AppsPage Delete Dialog
|
|
238
257
|
|
|
239
258
|
- **Purpose:** destructive app deletion
|
|
240
|
-
- **
|
|
259
|
+
- **Backend side effects:** soft-deletes app row, revokes related API keys, decrements user `apps_count`, optionally archives builds
|
|
241
260
|
|
|
242
261
|
### UploadPage Publish Form
|
|
243
262
|
|
|
@@ -249,21 +268,18 @@
|
|
|
249
268
|
- version input
|
|
250
269
|
- build number input
|
|
251
270
|
- release notes textarea
|
|
252
|
-
- **Validation:**
|
|
253
|
-
-
|
|
254
|
-
- version required
|
|
255
|
-
-
|
|
256
|
-
- ZIP file required
|
|
257
|
-
- Drive connection required
|
|
271
|
+
- **Validation (server-side `UpdateBuildRequest` FormRequest):**
|
|
272
|
+
- app_id required + ownership-checked
|
|
273
|
+
- version required + semver-validated
|
|
274
|
+
- build_number required + numeric
|
|
275
|
+
- ZIP file required + max-size enforced
|
|
276
|
+
- Drive connection required (backend rejects otherwise)
|
|
258
277
|
- **Flow:**
|
|
259
|
-
-
|
|
260
|
-
-
|
|
261
|
-
-
|
|
262
|
-
-
|
|
263
|
-
-
|
|
264
|
-
- write build doc
|
|
265
|
-
- update Firestore manifest docs
|
|
266
|
-
- navigate to app detail
|
|
278
|
+
- browser multipart POST → Laravel `/api/dashboard/apps/{app}/builds`
|
|
279
|
+
- Laravel validates, computes SHA-256, signs with RSA-SHA256 using the app's signing key
|
|
280
|
+
- Laravel uploads the ZIP to the user's Drive folder (server-side OAuth tokens)
|
|
281
|
+
- Laravel writes the Build row, updates the App's active manifest pointer
|
|
282
|
+
- SPA navigates to app detail
|
|
267
283
|
|
|
268
284
|
### SettingsPage Preferences
|
|
269
285
|
|
|
@@ -272,31 +288,19 @@
|
|
|
272
288
|
- update notifications toggle
|
|
273
289
|
- theme select
|
|
274
290
|
- language select
|
|
275
|
-
- **Action:**
|
|
276
|
-
|
|
277
|
-
### SettingsPage Change Password
|
|
278
|
-
|
|
279
|
-
- **Fields:**
|
|
280
|
-
- current password
|
|
281
|
-
- new password
|
|
282
|
-
- confirm new password
|
|
283
|
-
- **Validation:**
|
|
284
|
-
- required fields
|
|
285
|
-
- minimum new password length
|
|
286
|
-
- confirmation match
|
|
287
|
-
- **Special note:** only shown for email/password provider, not Google-only users
|
|
291
|
+
- **Action:** PATCH `/api/dashboard/preferences` → updates `users.preferences` JSON column
|
|
288
292
|
|
|
289
293
|
### SettingsPage Delete Account
|
|
290
294
|
|
|
291
295
|
- **Field:** confirmation input requiring exact `DELETE`
|
|
292
|
-
- **Action:**
|
|
296
|
+
- **Action:** DELETE `/api/dashboard/me` → revokes Sanctum tokens, soft-deletes user row, clears encrypted Drive tokens, cascades to apps/builds
|
|
293
297
|
|
|
294
298
|
### RolloutsPage Edit Modal
|
|
295
299
|
|
|
296
300
|
- **Fields:**
|
|
297
301
|
- rollout percentage numeric input
|
|
298
302
|
- rollout percentage range slider
|
|
299
|
-
- **Action:**
|
|
303
|
+
- **Action:** PATCH `/api/dashboard/builds/{build}/rollout` → updates rollout config on the Build row
|
|
300
304
|
|
|
301
305
|
### BuildsPage Filters
|
|
302
306
|
|
|
@@ -305,12 +309,13 @@
|
|
|
305
309
|
- channel filter
|
|
306
310
|
- platform filter
|
|
307
311
|
- status filter
|
|
312
|
+
- URL-state driven (`?q=`, `?app=`, `?channel=`, `?platform=`, `?status=`, `?page=`)
|
|
308
313
|
|
|
309
314
|
### AdminBuildsPage Filters
|
|
310
315
|
|
|
311
316
|
- search input
|
|
312
317
|
- app filter
|
|
313
|
-
- user filter
|
|
318
|
+
- user filter
|
|
314
319
|
- channel/status/platform filters
|
|
315
320
|
|
|
316
321
|
### AdminUsersPage Filters
|
|
@@ -339,8 +344,9 @@
|
|
|
339
344
|
## Important UX and Product Notes
|
|
340
345
|
|
|
341
346
|
- Dashboard is clearly owner/operator tooling, not consumer UI
|
|
342
|
-
- Google sign-in is
|
|
343
|
-
- Google Drive is central to the
|
|
347
|
+
- Google sign-in is the only auth path
|
|
348
|
+
- Google Drive is central to the publishing workflow (server-side OAuth, not browser SDK)
|
|
344
349
|
- App creation is a prerequisite for upload
|
|
345
350
|
- Drive connection is a prerequisite for upload
|
|
346
|
-
- Rollouts are edited
|
|
351
|
+
- Rollouts are edited on the Build row, not from a separate dedicated rollout service
|
|
352
|
+
- All user-visible state that should survive refresh (modal open, edit/delete dialogs, filters, tabs) is encoded in URL search params via the shared `useUrlState` hooks
|