@@ -5,6 +5,18 @@ import { isTokenExpired } from '../utils/jwt'
55import { isDemoMode , isDemoSessionActive } from '../utils/demoMode'
66import * as tokenStorage from '../utils/tokenStorage'
77import { usePerformanceMark } from '../composables/usePerformanceMark'
8+ import { useFeatureFlagStore } from '../store/featureFlagStore'
9+ import type { FeatureFlags } from '../types/feature-flags'
10+
11+ // Augment vue-router's RouteMeta so that `requiresFlag` is type-safe throughout.
12+ declare module 'vue-router' {
13+ interface RouteMeta {
14+ public ?: boolean
15+ requiresShell ?: boolean
16+ automationSurface ?: string
17+ requiresFlag ?: keyof FeatureFlags
18+ }
19+ }
820
921// Lazy-loaded route components — keeps initial bundle small and speeds up
1022// first-paint for login/register (the only eagerly-loaded views).
@@ -92,25 +104,25 @@ const router = createRouter({
92104 path : '/workspace/activity' ,
93105 name : 'workspace-activity' ,
94106 component : ActivityView ,
95- meta : { requiresShell : true } ,
107+ meta : { requiresShell : true , requiresFlag : 'newActivity' } ,
96108 } ,
97109 {
98110 path : '/workspace/activity/board/:boardId' ,
99111 name : 'workspace-activity-board' ,
100112 component : ActivityView ,
101- meta : { requiresShell : true } ,
113+ meta : { requiresShell : true , requiresFlag : 'newActivity' } ,
102114 } ,
103115 {
104116 path : '/workspace/activity/entity/:entityType/:entityId' ,
105117 name : 'workspace-activity-entity' ,
106118 component : ActivityView ,
107- meta : { requiresShell : true } ,
119+ meta : { requiresShell : true , requiresFlag : 'newActivity' } ,
108120 } ,
109121 {
110122 path : '/workspace/activity/user' ,
111123 name : 'workspace-activity-user' ,
112124 component : ActivityView ,
113- meta : { requiresShell : true } ,
125+ meta : { requiresShell : true , requiresFlag : 'newActivity' } ,
114126 } ,
115127 {
116128 path : '/workspace/activity/user/:userId' ,
@@ -130,13 +142,13 @@ const router = createRouter({
130142 path : '/workspace/automations/queue' ,
131143 name : 'workspace-automations-queue' ,
132144 component : AutomationQueueView ,
133- meta : { requiresShell : true , automationSurface : 'queue' } ,
145+ meta : { requiresShell : true , automationSurface : 'queue' , requiresFlag : 'newAutomation' } ,
134146 } ,
135147 {
136148 path : '/workspace/review' ,
137149 name : 'workspace-review' ,
138150 component : ReviewView ,
139- meta : { requiresShell : true , automationSurface : 'review' } ,
151+ meta : { requiresShell : true , automationSurface : 'review' , requiresFlag : 'newAutomation' } ,
140152 } ,
141153 {
142154 path : '/workspace/automations/proposals' ,
@@ -150,35 +162,35 @@ const router = createRouter({
150162 path : '/workspace/automations/chat' ,
151163 name : 'workspace-automations-chat' ,
152164 component : AutomationChatView ,
153- meta : { requiresShell : true } ,
165+ meta : { requiresShell : true , requiresFlag : 'newAutomation' } ,
154166 } ,
155167
156168 // Ops routes
157169 {
158170 path : '/workspace/ops/cli' ,
159171 name : 'workspace-ops-cli' ,
160172 component : OpsConsoleView ,
161- meta : { requiresShell : true } ,
173+ meta : { requiresShell : true , requiresFlag : 'newOps' } ,
162174 } ,
163175 {
164176 path : '/workspace/ops/endpoints' ,
165177 name : 'workspace-ops-endpoints' ,
166178 component : OpsConsoleView ,
167- meta : { requiresShell : true } ,
179+ meta : { requiresShell : true , requiresFlag : 'newOps' } ,
168180 } ,
169181 {
170182 path : '/workspace/ops/logs' ,
171183 name : 'workspace-ops-logs' ,
172184 component : OpsConsoleView ,
173- meta : { requiresShell : true } ,
185+ meta : { requiresShell : true , requiresFlag : 'newOps' } ,
174186 } ,
175187
176188 // Settings routes
177189 {
178190 path : '/workspace/settings/profile' ,
179191 name : 'workspace-settings-profile' ,
180192 component : ProfileSettingsView ,
181- meta : { requiresShell : true } ,
193+ meta : { requiresShell : true , requiresFlag : 'newAuth' } ,
182194 } ,
183195 {
184196 path : '/workspace/settings/access/:boardId?' ,
@@ -187,7 +199,7 @@ const router = createRouter({
187199 props : ( route ) => ( {
188200 boardId : typeof route . params . boardId === 'string' ? route . params . boardId : null ,
189201 } ) ,
190- meta : { requiresShell : true } ,
202+ meta : { requiresShell : true , requiresFlag : 'newAccess' } ,
191203 } ,
192204 {
193205 path : '/workspace/settings/export-import' ,
@@ -207,7 +219,7 @@ const router = createRouter({
207219 path : '/workspace/archive' ,
208220 name : 'workspace-archive' ,
209221 component : ArchiveView ,
210- meta : { requiresShell : true } ,
222+ meta : { requiresShell : true , requiresFlag : 'newArchive' } ,
211223 } ,
212224 {
213225 path : '/workspace/inbox' ,
@@ -227,7 +239,7 @@ const router = createRouter({
227239// Route-transition performance instrumentation
228240const routePerf = usePerformanceMark ( 'route-transition' )
229241
230- // Navigation guard for auth
242+ // Navigation guard for auth and feature flags
231243router . beforeEach ( ( to ) => {
232244 routePerf . start ( )
233245
@@ -248,6 +260,19 @@ router.beforeEach((to) => {
248260 if ( isPublic && hasValidSession && ( to . path === '/login' || to . path === '/register' ) ) {
249261 return { path : '/workspace/home' }
250262 }
263+
264+ // Feature-flag gate: block direct URL access to flagged routes when the flag
265+ // is disabled. The store is read synchronously from localStorage on first
266+ // access so the guard works correctly on hard refresh before App.vue mounts.
267+ const requiredFlag = to . meta . requiresFlag
268+ if ( requiredFlag !== undefined ) {
269+ const featureFlags = useFeatureFlagStore ( )
270+ // Restore from localStorage in case App.vue hasn't mounted yet (direct nav).
271+ featureFlags . restore ( )
272+ if ( ! featureFlags . isEnabled ( requiredFlag ) ) {
273+ return { path : '/workspace/home' }
274+ }
275+ }
251276} )
252277
253278router . afterEach ( ( ) => {
0 commit comments